]> mj.ucw.cz Git - paperjam.git/commitdiff
Better reporting of parse errors
authorMartin Mares <mj@ucw.cz>
Sat, 7 Apr 2018 08:30:31 +0000 (10:30 +0200)
committerMartin Mares <mj@ucw.cz>
Sat, 7 Apr 2018 08:30:31 +0000 (10:30 +0200)
TODO
parse.cc

diff --git a/TODO b/TODO
index 48bcd544daa389e504c74ac42248086ff690d766..3228957da0591ecb1ae32673822a97d5f5a3eb85 100644 (file)
--- a/TODO
+++ b/TODO
@@ -2,7 +2,6 @@
 - What if an input page specifies /Rotate?
 - "-f" switch
 - Help
-- More precise parsing errors
 
 | # Position bbox on a new paper
 | paper("a4")
index 37ee377eaae4fae0d2dfdfefc20231a625c67897..aa840ba2af6cdda39bdb7382d84727c7aa8fe384 100644 (file)
--- a/parse.cc
+++ b/parse.cc
@@ -35,20 +35,8 @@ static token_type buffered_token = TOK_NONE;
 static string token;
 static double token_num;
 
-static void NONRET parse_error(const char *msg, ...);
 static void parse_commands(list<cmd *> &cmds);
 
-static void parse_error(const char *msg, ...)
-{
-  va_list args;
-  va_start(args, msg);
-  fprintf(stderr, "Parse error: ");
-  vfprintf(stderr, msg, args);
-  fprintf(stderr, "\n");
-  va_end(args);
-  exit(1);
-}
-
 static token_type get_next_token()
 {
   while (*in_pos == ' ' || *in_pos == '\t' || *in_pos == '\r' || *in_pos == '\n')
@@ -68,7 +56,7 @@ static token_type get_next_token()
       size_t end_pos;
       token_num = stod(token, &end_pos);
       if (end_pos < token.length())
-       parse_error("Invalid number %s", token.c_str());
+       err("Invalid number %s", token.c_str());
       return TOK_NUMBER;
     }
 
@@ -90,12 +78,12 @@ static token_type get_next_token()
       while (*in_pos != '"')
        {
          if (!*in_pos)
-           parse_error("Unterminated string");
+           err("Unterminated string");
          if (*in_pos == '\\')
            {
              in_pos++;
              if (*in_pos != '"' && *in_pos != '\\')
-               parse_error("Unrecognized escape sequence \\%c", *in_pos);
+               err("Unrecognized escape sequence \\%c", *in_pos);
            }
          token += *in_pos++;
        }
@@ -126,7 +114,7 @@ static token_type get_next_token()
          in_pos++;
          return TOK_DOTDOT;
        }
-      parse_error("Unrecognized character '%c'", c);
+      err("Unrecognized character '%c'", c);
     }
 }
 
@@ -214,7 +202,7 @@ static double parse_dimen(const arg_def *adef)
 {
   token_type t = next_token();
   if (t != TOK_NUMBER)
-    parse_error("Argument %s must be a dimension", adef->name);
+    err("Argument %s must be a dimension", adef->name);
   double tmp = token_num;
 
   t = next_token();
@@ -225,12 +213,12 @@ static double parse_dimen(const arg_def *adef)
          return_token();
          return 0;
        }
-      parse_error("Argument %s must have a unit", adef->name);
+      err("Argument %s must have a unit", adef->name);
     }
   for (uint i=0; units[i].name; i++)
     if (token == units[i].name)
       return tmp * units[i].multiplier;
-  parse_error("Unknown unit %s", token.c_str());
+  err("Unknown unit %s", token.c_str());
 }
 
 static void parse_pipeline(cmd *c)
@@ -241,7 +229,7 @@ static void parse_pipeline(cmd *c)
   while (peek_token() != TOK_CLOSE_BRACE)
     {
       if (pp->branches.size() && next_token() != TOK_COMMA)
-       parse_error("Comma expected between pipeline branches");
+       err("Comma expected between pipeline branches");
 
       pipeline_branch *pb = new pipeline_branch;
       pp->branches.push_back(pb);
@@ -251,15 +239,15 @@ static void parse_pipeline(cmd *c)
        {
          t = next_token();
          if (t == TOK_END)
-           parse_error("Missing close brace");
+           err("Missing close brace");
          if (t == TOK_CLOSE_BRACE || t == TOK_COLON || t == TOK_COMMA)
            break;
 
          pipeline_selector ps;
          if (t != TOK_NUMBER)
-           parse_error("Pipeline selectors must start with a number");
+           err("Pipeline selectors must start with a number");
          if (!token_is_int())
-           parse_error("Pipeline selectors must be integers");
+           err("Pipeline selectors must be integers");
          ps.from = (int) token_num;
          ps.to = ps.from;
 
@@ -268,9 +256,9 @@ static void parse_pipeline(cmd *c)
              next_token();
              t = next_token();
              if (t != TOK_NUMBER)
-               parse_error("Pipeline selectors must be numbers or ranges");
+               err("Pipeline selectors must be numbers or ranges");
              if (!token_is_int())
-               parse_error("Pipeline selectors must be integers");
+               err("Pipeline selectors must be integers");
              ps.to = (int) token_num;
            }
 
@@ -316,9 +304,9 @@ static void parse_args(cmd *c)
          while (adefs[argi].name && token != adefs[argi].name)
            argi++;
          if (!adefs[argi].name)
-           parse_error("Command %s has no argument %s", cdef->name, token.c_str());
+           err("Command %s has no argument %s", cdef->name, token.c_str());
          if (c->args.count(token))
-           parse_error("Argument %s given multiple times", token.c_str());
+           err("Argument %s given multiple times", token.c_str());
          t = next_token();
          if (t == TOK_EQUAL)
            has_value = true;
@@ -327,14 +315,14 @@ static void parse_args(cmd *c)
          saw_named = true;
        }
       else if (saw_named)
-       parse_error("Positional arguments must precede named ones");
+       err("Positional arguments must precede named ones");
       else
        {
          return_token();
          while (next_pos < num_args && !(adefs[next_pos].type & AT_POSITIONAL))
            next_pos++;
          if (next_pos >= num_args)
-           parse_error("Too many positional arguments for command %s", cdef->name);
+           err("Too many positional arguments for command %s", cdef->name);
          argi = next_pos++;
          has_value = true;
        }
@@ -349,19 +337,19 @@ static void parse_args(cmd *c)
            case AT_STRING:
              t = next_token();
              if (t != TOK_STRING)
-               parse_error("Argument %s must be a string", adef->name);
+               err("Argument %s must be a string", adef->name);
              val = new arg_string(token);
              break;
            case AT_INT:
              t = next_token();
              if (t != TOK_NUMBER || !token_is_int())
-               parse_error("Argument %s must be an integer", adef->name);
+               err("Argument %s must be an integer", adef->name);
              val = new arg_int((int) token_num);
              break;
            case AT_DOUBLE:
              t = next_token();
              if (t != TOK_NUMBER)
-               parse_error("Argument %s must be a number", adef->name);
+               err("Argument %s must be a number", adef->name);
              val = new arg_double(token_num);
              break;
            case AT_DIMEN:
@@ -370,7 +358,7 @@ static void parse_args(cmd *c)
            case AT_SWITCH:
              t = next_token();
              if (t != TOK_NUMBER || !token_is_int() || ((int) token_num != 0 && (int) token_num != 1))
-               parse_error("Argument %s must be a switch", adef->name);
+               err("Argument %s must be a switch", adef->name);
              val = new arg_int((int) token_num);
              break;
            default:
@@ -382,7 +370,7 @@ static void parse_args(cmd *c)
          if (type == AT_SWITCH)
            val = new arg_int(1);
          else
-           parse_error("Argument %s must have a value", adef->name);
+           err("Argument %s must have a value", adef->name);
        }
 
       c->args[adef->name] = val;
@@ -391,12 +379,12 @@ static void parse_args(cmd *c)
       if (t == TOK_CLOSE_PAREN)
        break;
       if (t != TOK_COMMA)
-       parse_error("Comma expected after argument %s", adef->name);
+       err("Comma expected after argument %s", adef->name);
     }
 
   for (uint i=0; i<num_args; i++)
     if ((adefs[i].type & AT_MANDATORY) && !c->args.count(adefs[i].name))
-      parse_error("Command %s is missing an argument %s", cdef->name, adefs[i].name);
+      err("Command %s is missing an argument %s", cdef->name, adefs[i].name);
 }
 
 static void debug_cmd(cmd *c, uint indent=0)
@@ -435,7 +423,7 @@ static cmd *parse_cmd()
   while (cdef->name && token != cdef->name)
     cdef++;
   if (!cdef->name)
-    parse_error("Unknown command %s", token.c_str());
+    err("Unknown command %s", token.c_str());
 
   cmd *c = new cmd;
   c->def = cdef;
@@ -446,11 +434,11 @@ static cmd *parse_cmd()
   if (peek_token() == TOK_OPEN_BRACE)
     {
       if (!cdef->has_pipeline)
-       parse_error("Command %s does not accept a pipeline", cdef->name);
+       err("Command %s does not accept a pipeline", cdef->name);
       parse_pipeline(c);
     }
   else if (cdef->has_pipeline)
-    parse_error("Command %s requires a pipeline", cdef->name);
+    err("Command %s requires a pipeline", cdef->name);
 
   return c;
 }
@@ -495,9 +483,17 @@ void parse(const char *in, list<cmd *> &cmds)
 {
   debug("### Parsing commands");
   in_pos = in;
-  parse_commands(cmds);
-  if (next_token() != TOK_END)
-    parse_error("Extra tokens after commands");
+
+  try
+    {
+      parse_commands(cmds);
+      if (next_token() != TOK_END)
+       err("Extra tokens after commands");
+    }
+  catch (exception &e)
+    {
+      die("Parse error: %s (at position %d)", e.what(), (int)(in_pos - in));
+    }
 
   if (debug_level > 1)
     debug_cmds(cmds);