]> mj.ucw.cz Git - paperjam.git/blobdiff - parse.cc
TODO: a4r paper
[paperjam.git] / parse.cc
index 5158eeb72fa3031295f7b752d34d319d15b2da31..d1573c88e84561bca9540231218f2dc441e5440a 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;
     }
 
@@ -78,7 +66,8 @@ static token_type get_next_token()
       while (*in_pos >= 'A' && *in_pos <= 'Z' ||
             *in_pos >= 'a' && *in_pos <= 'z' ||
             *in_pos >= '0' && *in_pos <= '9' ||
-            *in_pos == '_')
+            *in_pos == '_' ||
+            *in_pos == '-')
        token += *in_pos++;
       return TOK_IDENT;
     }
@@ -89,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++;
        }
@@ -125,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);
     }
 }
 
@@ -213,7 +202,7 @@ static double parse_dimen(const arg_def *adef)
 {
   token_type t = next_token();
   if (t != TOK_NUMBER)
-    parse_error("Parameter %s must be a dimension", adef->name);
+    err("Argument %s must be a dimension", adef->name);
   double tmp = token_num;
 
   t = next_token();
@@ -224,12 +213,12 @@ static double parse_dimen(const arg_def *adef)
          return_token();
          return 0;
        }
-      parse_error("Parameter %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)
@@ -240,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);
@@ -250,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;
 
@@ -267,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;
            }
 
@@ -308,35 +297,49 @@ static void parse_args(cmd *c)
       t = next_token();
       if (t == TOK_CLOSE_PAREN)
         break;
+
+      while (next_pos < num_args && !(adefs[next_pos].type & AT_POSITIONAL))
+       next_pos++;
+
       uint argi = 0;
       bool has_value = false;
+
       if (t == TOK_IDENT)
        {
          while (adefs[argi].name && token != adefs[argi].name)
            argi++;
-         if (!adefs[argi].name)
-           parse_error("Command %s has no parameter %s", cdef->name, token.c_str());
-         if (c->args.count(token))
-           parse_error("Parameter %s given multiple times", token.c_str());
-         t = next_token();
-         if (t == TOK_EQUAL)
-           has_value = true;
+         if (adefs[argi].name)
+           {
+             if (c->args.count(token))
+               err("Argument %s given multiple times", token.c_str());
+             t = next_token();
+             if (t == TOK_EQUAL)
+               has_value = true;
+             else
+               return_token();
+             saw_named = true;
+           }
+         else if (next_pos < num_args && (adefs[next_pos].type & AT_TYPE_MASK) == AT_STRING)
+           {
+             // Shortcut syntax: positional arguments of string type can be specified
+             // as bare identifiers if they do not collide with names or other arguments.
+             return_token();
+             argi = next_pos++;
+             has_value = true;
+           }
          else
-           return_token();
-         saw_named = true;
+           err("Command %s has no argument %s", cdef->name, token.c_str());
        }
       else if (saw_named)
-       parse_error("Positional parameters must precede named ones");
-      else
+       err("Positional arguments must precede named ones");
+      else if (next_pos < num_args)
        {
          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);
          argi = next_pos++;
          has_value = true;
        }
+      else
+       err("Too many positional arguments for command %s", cdef->name);
 
       const arg_def *adef = &adefs[argi];
       uint type = adef->type & AT_TYPE_MASK;
@@ -347,20 +350,20 @@ static void parse_args(cmd *c)
            {
            case AT_STRING:
              t = next_token();
-             if (t != TOK_STRING)
-               parse_error("Parameter %s must be a string", adef->name);
+             if (t != TOK_STRING && t != TOK_IDENT)
+               err("Argument %s must be a string or identifier", adef->name);
              val = new arg_string(token);
              break;
            case AT_INT:
              t = next_token();
              if (t != TOK_NUMBER || !token_is_int())
-               parse_error("Parameter %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("Parameter %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:
@@ -369,7 +372,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("Parameter %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:
@@ -381,7 +384,7 @@ static void parse_args(cmd *c)
          if (type == AT_SWITCH)
            val = new arg_int(1);
          else
-           parse_error("Parameter %s must have a value", adef->name);
+           err("Argument %s must have a value", adef->name);
        }
 
       c->args[adef->name] = val;
@@ -390,12 +393,12 @@ static void parse_args(cmd *c)
       if (t == TOK_CLOSE_PAREN)
        break;
       if (t != TOK_COMMA)
-       parse_error("Comma expected after parameter %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 a parameter %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)
@@ -434,7 +437,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;
@@ -445,11 +448,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;
 }
@@ -474,7 +477,14 @@ static void instantiate(list<cmd *> &cmds)
 {
   for (auto c: cmds)
     {
-      c->exec = c->def->constructor(c);
+      try
+       {
+         c->exec = c->def->constructor(c);
+       }
+      catch (exception &e)
+       {
+         die("Error in %s: %s", c->def->name, e.what());
+       }
       if (c->pipe)
        {
          for (auto pb: c->pipe->branches)
@@ -485,12 +495,22 @@ static void instantiate(list<cmd *> &cmds)
 
 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");
 
-  debug_cmds(cmds);
+  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);
   instantiate(cmds);
 }
 
@@ -499,23 +519,25 @@ void parser_help()
   for (int i=0; cmd_table[i].name; i++)
     {
       const cmd_def *def = &cmd_table[i];
-      printf("%s\n", def->name);
+      printf("%s - %s\n", def->name, def->help);
 
       const arg_def *arg = def->arg_defs;
       static const char * const type_names[] = {
-       "string", "int", "double", "dimen", "switch"
+       "string", "int", "real", "dimen", "switch"
       };
       while (arg->name)
        {
-         printf("\t%s (%s)%s%s\n",
+         char a[32];
+         snprintf(a, sizeof(a), "%s%s=%s<%s>",
+           (arg->type & AT_POSITIONAL) ? "[" : "",
            arg->name,
-           type_names[arg->type & AT_TYPE_MASK],
-           (arg->type & AT_MANDATORY) ? " [mandatory]" : "",
-           (arg->type & AT_POSITIONAL) ? " [positional]" : "");
+           (arg->type & AT_POSITIONAL) ? "]" : "",
+           type_names[arg->type & AT_TYPE_MASK]);
+         printf("   %-20s %s\n", a, arg->help);
          arg++;
        }
 
        if (def->has_pipeline)
-         printf("\t{ pipeline }\n");
+         printf("   { pipeline }\n");
     }
 }