27 static token_type this_token = TOK_NONE;
28 static token_type buffered_token = TOK_NONE;
30 static double token_num;
32 static void NONRET parse_error(const char *msg, ...);
33 static void parse_commands(list<cmd *> &cmds);
35 static void parse_error(const char *msg, ...)
39 fprintf(stderr, "Parse error: ");
40 vfprintf(stderr, msg, args);
41 fprintf(stderr, "\n");
46 static token_type get_next_token()
48 while (*in_pos == ' ' || *in_pos == '\t' || *in_pos == '\r' || *in_pos == '\n')
55 if (*in_pos >= '0' && *in_pos <= '9' ||
56 *in_pos == '-' && in_pos[1] >= '0' && in_pos[1] <= '9')
59 while (*in_pos >= '0' && *in_pos <= '9' || *in_pos == '.' && in_pos[1] != '.')
63 token_num = stod(token, &end_pos);
64 if (end_pos < token.length())
65 parse_error("Invalid number %s", token.c_str());
69 if (*in_pos >= 'A' && *in_pos <= 'Z' ||
70 *in_pos >= 'a' && *in_pos <= 'z')
72 while (*in_pos >= 'A' && *in_pos <= 'Z' ||
73 *in_pos >= 'a' && *in_pos <= 'z' ||
74 *in_pos >= '0' && *in_pos <= '9')
82 while (*in_pos != '"')
85 parse_error("Unterminated string");
89 if (*in_pos != '"' && *in_pos != '\\')
90 parse_error("Unrecognized escape sequence \\%c", *in_pos);
108 return TOK_OPEN_PAREN;
110 return TOK_CLOSE_PAREN;
112 return TOK_OPEN_BRACE;
114 return TOK_CLOSE_BRACE;
116 if (c == '.' && *in_pos == '.')
121 parse_error("Unrecognized character '%c'", c);
125 static token_type next_token()
127 if (buffered_token != TOK_NONE)
128 this_token = buffered_token;
130 this_token = get_next_token();
131 buffered_token = TOK_NONE;
135 static void return_token()
137 assert(this_token != TOK_NONE);
138 assert(buffered_token == TOK_NONE);
139 buffered_token = this_token;
140 this_token = TOK_NONE;
143 static token_type peek_token()
147 return buffered_token;
150 static bool token_is_int()
152 return token_num == (double)(int) token_num;
155 /*** Argument types ***/
157 class arg_int : public arg_val {
160 arg_int(int x) { val = x; }
161 bool given() { return true; }
162 int as_int(int def UNUSED) { return val; }
163 string dump() { return to_string(val); }
166 class arg_double : public arg_val {
169 arg_double(double x) { val = x; }
170 bool given() { return true; }
171 double as_double(double def UNUSED) { return val; }
172 string dump() { return to_string(val); }
175 class arg_string : public arg_val {
178 arg_string(string x) { val = x; }
179 bool given() { return true; }
180 string as_string(string def UNUSED) { return val; }
181 string dump() { return '"' + val + '"'; }
184 static arg_val null_arg;
195 static const unit units[] = {
205 static double parse_dimen(const arg_def *adef)
207 token_type t = next_token();
209 parse_error("Parameter %s must be a dimension", adef->name);
210 double tmp = token_num;
214 parse_error("Parameter %s must have a unit", adef->name);
215 for (uint i=0; units[i].name; i++)
216 if (token == units[i].name)
217 return tmp * units[i].multiplier;
218 parse_error("Unknown unit %s", token.c_str());
221 static void parse_pipeline(cmd *c)
223 pipeline *pp = new pipeline;
226 while (peek_token() != TOK_CLOSE_BRACE)
228 if (pp->branches.size() && next_token() != TOK_COMMA)
229 parse_error("Comma expected between pipeline branches");
231 pipeline_branch *pb = new pipeline_branch;
232 pp->branches.push_back(pb);
236 token_type t = next_token();
237 if (t == TOK_CLOSE_BRACE || t == TOK_END)
238 parse_error("Premature end of pipeline");
242 if (pb->selectors.size())
245 parse_error("Invalid pipeline selector");
247 if (t == TOK_CLOSE_BRACE || t == TOK_END)
248 parse_error("Premature end of pipeline");
251 pipeline_selector ps;
253 parse_error("Pipeline selectors must start with a number");
255 parse_error("Pipeline selectors must be integers");
256 ps.from = (int) token_num;
259 if (peek_token() == TOK_DOTDOT)
264 parse_error("Pipeline selectors must be numbers or ranges");
266 parse_error("Pipeline selectors must be integers");
267 ps.to = (int) token_num;
270 pb->selectors.push_back(ps);
273 parse_commands(pb->commands);
280 static void parse_args(cmd *c)
282 const cmd_def *cdef = c->def;
283 const arg_def *adefs = cdef->arg_defs;
285 while (adefs[num_args].name)
288 c->args.resize(num_args, &null_arg);
290 token_type t = next_token();
291 if (t != TOK_OPEN_PAREN)
297 bool saw_named = false;
305 while (adefs[argi].name && token != adefs[argi].name)
307 if (!adefs[argi].name)
308 parse_error("Command %s has no parameter %s", cdef->name, token.c_str());
309 if (c->args.at(argi)->given())
310 parse_error("Parameter %s given multiple times", token.c_str());
313 parse_error("Parameter name must be followed by '='");
317 parse_error("Positional parameters must precede named ones");
321 while (next_pos < num_args && !(adefs[next_pos].type & AT_POSITIONAL))
323 if (next_pos >= num_args)
324 parse_error("Too many positional arguments for command %s", cdef->name);
328 const arg_def *adef = &adefs[argi];
330 switch (adef->type & AT_TYPE_MASK)
335 parse_error("Parameter %s must be a string", adef->name);
336 val = new arg_string(token);
340 if (t != TOK_NUMBER || !token_is_int())
341 parse_error("Parameter %s must be an integer", adef->name);
342 val = new arg_int((int) token_num);
347 parse_error("Parameter %s must be a number", adef->name);
348 val = new arg_double(token_num);
351 val = new arg_double(parse_dimen(adef));
357 c->args.at(argi) = val;
360 if (t == TOK_CLOSE_PAREN)
363 parse_error("Comma expected after parameter %s", adef->name);
366 for (uint i=0; i<num_args; i++)
367 if ((adefs[i].type & AT_MANDATORY) && !c->args.at(i)->given())
368 parse_error("Command %s is missing a parameter %s", cdef->name, adefs[i].name);
371 static void debug_cmd(cmd *c, uint indent=0)
373 printf("%*sCommand %s\n", indent, "", c->def->name);
374 for (size_t i=0; i < c->args.size(); i++)
376 const arg_def *adef = &c->def->arg_defs[i];
377 string dump = c->args.at(i)->dump();
378 printf("%*sArg #%d: %s = %s\n", indent+4, "", (int) i, adef->name, dump.c_str());
382 printf("%*sPipeline:\n", indent+4, "");
383 for (auto pb: c->pipe->branches)
385 printf("%*sSelector:\n", indent+8, "");
386 for (auto ps: pb->selectors)
387 printf("%*s%d - %d\n", indent+12, "", ps.from, ps.to);
388 printf("%*sCommands:\n", indent+8, "");
389 for (auto cc: pb->commands)
390 debug_cmd(cc, indent+12);
395 static void debug_cmds(list<cmd *> &cmds)
401 static cmd *parse_cmd()
403 const cmd_def *cdef = cmd_table;
404 while (cdef->name && token != cdef->name)
407 parse_error("Unknown command %s", token.c_str());
415 if (peek_token() == TOK_OPEN_BRACE)
417 if (!cdef->has_pipeline)
418 parse_error("Command %s does not accept a pipeline", cdef->name);
421 else if (cdef->has_pipeline)
422 parse_error("Command %s requires a pipeline", cdef->name);
427 static void parse_commands(list<cmd *> &cmds)
431 token_type t = next_token();
438 cmd *c = parse_cmd();
443 static void instantiate(list<cmd *> &cmds)
447 c->exec = c->def->constructor(c);
450 for (auto pb: c->pipe->branches)
451 instantiate(pb->commands);
456 void parse(const char *in, list<cmd *> &cmds)
459 parse_commands(cmds);
460 if (next_token() != TOK_END)
461 parse_error("Extra tokens after commands");