From cc5a6d978144bda90220bd10866c4fd908d07546 Mon Sep 17 00:00:00 2001
From: Rui Ueyama <ruiu@cs.stanford.edu>
Date: Sat, 3 Aug 2019 15:02:08 +0900
Subject: [PATCH] Improve error message

Now, chibicc can print out an error message with an error location
like this:

  $ ./chibicc 1+foo
  1+foo
    ^ expected a number
---
 main.c | 40 ++++++++++++++++++++++++++++++++++------
 1 file changed, 34 insertions(+), 6 deletions(-)

diff --git a/main.c b/main.c
index 5cf2bee..cefd99d 100644
--- a/main.c
+++ b/main.c
@@ -21,6 +21,9 @@ struct Token {
   int len;        // Token length
 };
 
+// Input string
+static char *current_input;
+
 // Reports an error and exit.
 static void error(char *fmt, ...) {
   va_list ap;
@@ -30,6 +33,29 @@ static void error(char *fmt, ...) {
   exit(1);
 }
 
+// Reports an error location and exit.
+static void verror_at(char *loc, char *fmt, va_list ap) {
+  int pos = loc - current_input;
+  fprintf(stderr, "%s\n", current_input);
+  fprintf(stderr, "%*s", pos, ""); // print pos spaces.
+  fprintf(stderr, "^ ");
+  vfprintf(stderr, fmt, ap);
+  fprintf(stderr, "\n");
+  exit(1);
+}
+
+static void error_at(char *loc, char *fmt, ...) {
+  va_list ap;
+  va_start(ap, fmt);
+  verror_at(loc, fmt, ap);
+}
+
+static void error_tok(Token *tok, char *fmt, ...) {
+  va_list ap;
+  va_start(ap, fmt);
+  verror_at(tok->loc, fmt, ap);
+}
+
 // Consumes the current token if it matches `s`.
 static bool equal(Token *tok, char *op) {
   return memcmp(tok->loc, op, tok->len) == 0 && op[tok->len] == '\0';
@@ -38,14 +64,14 @@ static bool equal(Token *tok, char *op) {
 // Ensure that the current token is `s`.
 static Token *skip(Token *tok, char *s) {
   if (!equal(tok, s))
-    error("expected '%s'", s);
+    error_tok(tok, "expected '%s'", s);
   return tok->next;
 }
 
 // Ensure that the current token is TK_NUM.
 static int get_number(Token *tok) {
   if (tok->kind != TK_NUM)
-    error("expected a number");
+    error_tok(tok, "expected a number");
   return tok->val;
 }
 
@@ -58,8 +84,9 @@ static Token *new_token(TokenKind kind, char *start, char *end) {
   return tok;
 }
 
-// Tokenize `p` and returns new tokens.
-static Token *tokenize(char *p) {
+// Tokenize `current_input` and returns new tokens.
+static Token *tokenize(void) {
+  char *p = current_input;
   Token head = {};
   Token *cur = &head;
 
@@ -86,7 +113,7 @@ static Token *tokenize(char *p) {
       continue;
     }
 
-    error("invalid token");
+    error_at(p, "invalid token");
   }
 
   cur = cur->next = new_token(TK_EOF, p, p);
@@ -97,7 +124,8 @@ int main(int argc, char **argv) {
   if (argc != 2)
     error("%s: invalid number of arguments", argv[0]);
 
-  Token *tok = tokenize(argv[1]);
+  current_input = argv[1];
+  Token *tok = tokenize();
 
   printf("  .globl main\n");
   printf("main:\n");
-- 
GitLab