28 // int yylex (YYSTYPE *yylval_param, yyscan_t yyscanner);
29 void yyerror (struct parser *,char *);
30 #define YYLEX_PARAM &yylval, prs->lexer
34 %token NEWLINE INDENT COMMENT
35 %token GEN_FROM DRIVE_TO
36 %token OPEN CLOSE BRA KET
37 %token STAR PLUS COMMA COLON AT
38 %token CMP_EQ CMP_NE CMP_LT CMP_LE CMP_GT CMP_GE
39 %token LOG_NOT LOG_AND LOG_OR
40 %token ATTRTYPE ATTRNAME ATTROPT VARIABLE SILENCER DRIVERNAME PARAMETER
41 %token STRING BLOB INTEGER FLOAT
43 %type<varnum> INTEGER FLOAT STRING BLOB DRIVERNAME ATTRTYPE
44 %type<varnum> VARIABLE PARAMETER optvalue value const varnm dnvar bindrdnpart
49 /* The parser is made re-entrant, with parameters for the various tables
50 * that will store the semantics for later analysis.
51 * Kludge: Bison seems to extract the last identifier for lex-param only,
52 * so we've defined one to take that place
55 %lex-param { YYSTYPE *yylval_param, yyscan_t prs_arrow_scanner }
56 //HUH?// %define api.puri
57 %parse-param { struct parser *prs }
60 #define prs_arrow_scanner prs->lexer
63 /* Define a switching start symbol, and hav yylex() prefix the input stream
64 * with a suitable start condition switch; this makes for flexibility in the
65 * syntax scanned with bison. The global bison_switch directs yylex() to
66 * deliver these special starting tags. The syntax below requires it, but
67 * will only accept it at the beginning of the parsing process.
70 %token START_SCRIPT START_LINE START_GENERATOR START_CONDITION START_DRIVEROUT
76 // The following code implements a stack, together with a virtual production
77 // rule _pushV that does the opposite of _popV.
79 // This may not be the best option; we might also return bitsets of variables
80 // from the various non-terminals in the grammar, set their %type and use $i.
81 // The vital matter in that case is allocation and reuse of the variable sets.
83 // _tosV is the top of the variable stack
84 bitset_t *_tosV (struct parser *prs) {
85 if (prs->varstack_sp < 0) {
86 fatal_error ("Cannot share top of empty stack");
88 return prs->varstack [prs->varstack_sp];
91 void _pushV (struct parser *prs) {
93 if (prs->varstack_sp >= PARSER_VARIABLE_STACK_ENTRIES) {
94 fatal_error ("Parser ran out of variable stack levels");
96 bitset_empty (prs->varstack [prs->varstack_sp]);
99 // _popV retrieves the top element of the variable stack
100 bitset_t *_popV (struct parser *prs) {
101 if (prs->varstack_sp <= 0) {
102 fatal_error ("Attempt to pop top element of variable stack");
104 return prs->varstack [prs->varstack_sp--];
107 // _clrV wipes the variable stack
108 void _clrV (struct parser *prs) {
109 prs->varstack_sp = 0;
110 bitset_empty (prs->varstack [0]);
113 // The following code implements a reverse bufffer, in which binding actions
114 // variable/constant numbers are collected. This is done before allocating
115 // memory for them and setting it up in a variable. The buffer starts with
116 // the BNDO_ACT_DONE installed and grows towards the beginning, minding its
117 // maximum size while at it. After it is cloned into the variable space,
118 // the buffer is reset to just BNDO_ACT_DONE.
120 // _actO prefixes an action code to the buffer
121 void _actO (struct parser *prs, uint8_t action) {
122 varnum_t rematch = VARNUM_BAD;
123 size_t here = prs->action_sp;
124 //TODO// Throw parser error instead
125 assert (here >= sizeof (action));
127 prs->action_sp = here;
128 prs->action [here] = action;
129 // Same variable bound in follow-up? Then make that a comparison!
130 if ((action & BNDO_ACT_MASK) == BNDO_ACT_BIND) {
131 here += 1 + sizeof (varnum_t);
132 rematch = * (varnum_t *) &prs->action [here];
133 here += 0 + sizeof (varnum_t);
134 while (here < sizeof (prs->action)) {
135 switch (prs->action [here] & BNDO_ACT_MASK) {
137 case BNDO_ACT_OBJECT:
139 // Uses no parameters
144 here += 1 + sizeof (varnum_t);
147 // Uses both $1 and $2
148 if (rematch == * (varnum_t *) &prs->action [here + 1 + sizeof (varnum_t)]) {
149 // BIND is overruled by the new one
150 prs->action [here] &= ~BNDO_ACT_MASK;
151 prs->action [here] |= BNDO_ACT_CMP;
152 // There will not be any more of these
153 here = sizeof (prs->action);
156 // ...continue into BNDO_ACT_CMP...
158 // Uses both $1 and $2
159 here += 1 + 2 * sizeof (varnum_t);
166 // _varO stores a variable number before the rest of the buffer.
167 // The variable number represents either a constant or a variable.
168 void _varO (struct parser *prs, varnum_t var) {
169 size_t actionbuf_freespace = prs->action_sp;
170 //TODO// Throw parser error instead
171 assert (actionbuf_freespace >= sizeof (var));
172 prs->action_sp -= sizeof (varnum_t);
173 * (varnum_t *) &prs->action [prs->action_sp] = var;
176 // _clrO clears the action buffer.
177 void _clrO (struct parser *prs) {
178 prs->action_sp = sizeof (prs->action) -1;
181 // Stores the action buffer as a variable called @p bndname,
182 // using the struct @p varval for intermediate storage.
183 // Returns the variable number of the stored action buffer.
184 varnum_t _store_binding (struct parser *prs, char *bndname, struct var_value *varval) {
185 varnum_t binding = var_have (prs->vartab, bndname, VARKIND_BINDING);
186 varval->type = VARTP_BLOB;
187 varval->typed_blob.len = sizeof (prs->action) - prs->action_sp;
188 varval->typed_blob.str = malloc (varval->typed_blob.len);
189 assert (varval->typed_blob.str != NULL);
190 memcpy (varval->typed_blob.str, &prs->action [prs->action_sp],
191 varval->typed_blob.len);
192 var_set_value (prs->vartab, binding, varval);
197 // _flushO stores the action buffer as bnd_<linehash>
198 void _flushO (struct parser *prs, hash_t linehash, gennum_t g) {
199 char bndname [4 + 2 * sizeof (hash_t) + 1];
200 struct var_value varval;
202 int len = sizeof (hash_t);
203 uint8_t *ptr = (uint8_t *) &linehash;
204 memcpy (bndname, "bnd_", 4);
205 char *hexstr = bndname + 4;
207 sprintf (hexstr, "%02x", *ptr++);
210 binding = _store_binding (prs, bndname, &varval);
212 printf ("Binding %s V%d G%d created: >>> ", bndname, binding, g);
213 ptr = varval.typed_blob.str;
214 len = varval.typed_blob.len;
216 printf ("%02x ", *ptr++);
220 gen_set_binding(prs->gentab, g, binding);
227 /* The definition of a (fake) construct that pushes that variable stack.
228 * This is used to separate contexts of variables within a line. The new
229 * top-of-stack accumulates new variables, and starts empty after _pushV().
231 _pushV: { _pushV (prs); }
233 /* When initiated by the pulley_parser_XXX() functions, they will be setup
234 * to parse either a single line, possibly even of a particular type, or a
235 * sequence of lines informally known as a script. The switch implements
236 * this behaviour, in unison with a bit of startup code in the lexer that
237 * inserts the special START_xxx symbols at the head of the lexeme stream.
239 switch: START_SCRIPT script
240 switch: START_LINE line
241 switch: START_GENERATOR line_generator
242 switch: START_CONDITION line_condition
243 switch: START_DRIVEROUT line_driverout
246 script: script line NEWLINE
247 script: script error NEWLINE
249 line: line_generator | line_condition | line_driverout | line_empty
251 /* Empty lines constitute acceptable grammar withing scripts, or if the line
252 * type has not been specified. It does not make any semantical modifications.
256 /* Generator lines contain variables that are bound to the environment, they
257 * extract information from a DN-type variable and they have annotations that
258 * can define guard variables and a line weight.
260 line_generator: error GEN_FROM dnvar annotations { _clrV (prs); _clrO (prs); }
261 line_generator: binding GEN_FROM _pushV dnvar annotations {
262 bitset_t *guards = _popV (prs);
264 bitset_t *bindvars = _tosV (prs);
265 gennum_t gennew = gen_new (prs->gentab, dnvar);
268 printf ("FOUND %d bound variables in generator line G%d\n", bitset_count (bindvars), gennew);
269 bitset_iterator_init (&lvi, bindvars);
270 while (bitset_iterator_next_one (&lvi, NULL)) {
271 varnum_t varnum = bitset_iterator_bitnum (&lvi);
272 var_used_in_generator (prs->vartab, varnum, gennew);
273 gen_add_variable (prs->gentab, gennew, varnum);
275 if (! bitset_isempty (guards)) {
276 //TODO// Process guards (but first discover what they mean!)
277 fprintf (stderr, "WARNING: Ignoring explicit guards to generator, unsure about semantics...\n");
279 gen_set_weight (prs->gentab, gennew, prs->weight_set? prs->weight: 250.0);
280 hash_curline (&prs->scanhashbuf, &linehash);
281 gen_set_hash (prs->gentab, gennew, linehash);
282 _flushO (prs, linehash, gennew);
287 /* Condition lines simply mention a filter and annotations. As part of their
288 * most valuable impact on semantics, they cause variable partitions; but
289 * this is not currently worked out. Instead, sets of variables used in
290 * conditions are setup for later processing with cndtab_drive_partitions().
291 * Annotations may set a weight and could introduce more variables as guards
292 * that will also be taken into account during partitioning.
294 line_condition: filter annotations {
295 bitset_t *filtervars = _tosV (prs); // Includes explicit guards!
297 bitset_iterator_init (&fvi, filtervars);
299 while (bitset_iterator_next_one (&fvi, NULL)) {
300 varnum_t varnum = bitset_iterator_bitnum (&fvi);
301 var_used_in_condition (prs->vartab, varnum, prs->newcnd);
302 cnd_needvar (prs->cndtab, prs->newcnd, varnum);
304 cnd_set_weight (prs->cndtab, prs->newcnd, prs->weight_set? prs->weight: 0.02);
305 hash_curline (&prs->scanhashbuf, &linehash);
306 cnd_set_hash (prs->cndtab, prs->newcnd, linehash);
308 prs->newcnd = CNDNUM_BAD;
311 /* Driver output lines specify a number of variables that will be sent out,
312 * they name an output driver module as well as constant settings for named
313 * parameters that this driver module may expect. The constant settings are
314 * provided while configuring the driver module; the variables will be supplied
315 * when actually transmitting data through the driver module.
316 * Processing of the line's information is done as soon as it is recognised,
317 * to save internal structures beyond the sets that we wish to maintain.
318 * Although a weight could be defined, it hardly has an impact on ordering;
319 * although guards could be specified, it barely seems interesting to do so.
321 //USELESS???// line_driveout: error DRIVE_TO DRIVERNAME OPEN parmlist CLOSE annotations { _clrV (); }
322 //USELESS???// line_driveout: error CLOSE annotations { _clrV (); }
323 line_driverout: drvout_vallist_s DRIVE_TO {
324 bitset_t *varout = _tosV (prs);
326 bitset_iterator_init (&voi, varout);
327 while (bitset_iterator_next_one (&voi, NULL)) {
328 varnum_t varnum = bitset_iterator_bitnum (&voi);
332 char *module = var_get_name (prs->vartab, $4);
333 drv_set_module (prs->drvtab, prs->newdrv, module);
334 bitset_t *driver = _tosV (prs);
337 } OPEN parmlist CLOSE {
338 char bndname [7 + 2 * sizeof (hash_t) + 1];
339 struct var_value varval;
341 snprintf (bndname, sizeof(bndname), "bnd_DRV%d", prs->newdrv);
342 binding = _store_binding (prs, bndname, &varval);
343 drv_set_module_parameters (prs->drvtab, prs->newdrv, binding);
344 bitset_t *params = _tosV (prs);
347 bitset_t *guards = _tosV (prs);
349 bitset_iterator_init (&gvi, guards);
350 while (bitset_iterator_next_one (&gvi, NULL)) {
351 varnum_t varnum = bitset_iterator_bitnum (&gvi);
352 var_used_in_driverout (prs->vartab, varnum, prs->newdrv);
353 drv_add_guardvar (prs->drvtab, prs->newdrv, varnum);
355 drv_set_weight (prs->drvtab, prs->newdrv, prs->weight_set? prs->weight: 3.0);
357 hash_curline (&prs->scanhashbuf, &linehash);
358 drv_set_hash (prs->drvtab, prs->newdrv, linehash);
360 prs->newdrv = DRVNUM_BAD;
363 annotations: annotation1 annotation2
364 annotation1: /* %empty */
365 annotation1: BRA varlist KET
366 annotation2: /* %empty */ { prs->weight_set = false; }
367 annotation2: STAR FLOAT { prs->weight_set = true; prs->weight = /*TODO*/ 0.1; }
369 binding: bindatr { _actO (prs, BNDO_ACT_OBJECT); }
370 binding: bindatr { _actO (prs, BNDO_ACT_OBJECT); } COMMA bindsteps
373 bindsteps: bindsteps COMMA bindstep
375 bitset_set (_tosV (prs), $2);
377 _varO (prs, VARNUM_BAD);
378 _actO (prs, BNDO_SUBJ_DN | BNDO_ACT_BIND);
379 // We do not move down at this point, we just harvest the DN
382 _actO (prs, BNDO_ACT_DOWN);
385 bindrdn: bindrdn PLUS bindrdnpart
386 bindrdnpart: ATTRTYPE CMP_EQ optvalue {
387 //TODO// Recognise special names, like DCList
388 //TODO// Alternatively, append dnvarmods ::= ? | + | *
389 if ($3 == VARNUM_BAD) {
390 // SILENCER means just require having $1
392 _actO (prs, BNDO_SUBJ_RDN | BNDO_ACT_HAVE);
395 if (var_get_kind (prs->vartab, $3) == VARKIND_CONSTANT) {
396 action = BNDO_SUBJ_RDN | BNDO_ACT_CMP;
398 action = BNDO_SUBJ_RDN | BNDO_ACT_BIND;
403 bitset_set (_tosV (prs), $3);
407 bindatr: bindatr PLUS atmatch
408 atmatch: ATTRTYPE COLON optvalue {
409 if ($3 == VARNUM_BAD) {
410 // SILENCER means just require having $1
412 _actO (prs, BNDO_SUBJ_ATTR | BNDO_ACT_HAVE);
415 if (var_get_kind (prs->vartab, $3) == VARKIND_CONSTANT) {
416 action = BNDO_SUBJ_ATTR | BNDO_ACT_CMP;
418 action = BNDO_SUBJ_ATTR | BNDO_ACT_BIND;
423 bitset_set (_tosV (prs), $3);
428 if (prs->newcnd == CNDNUM_BAD) {
429 prs->newcnd = cnd_new (prs->cndtab);
432 filter: OPEN error CLOSE
434 filtbody: LOG_NOT filter {
435 cnd_pushop (prs->cndtab, prs->newcnd, CND_NOT);
438 cnd_pushop (prs->cndtab, prs->newcnd, CND_SEQ_START);
440 cnd_pushop (prs->cndtab, prs->newcnd, CND_AND);
443 cnd_pushop (prs->cndtab, prs->newcnd, CND_SEQ_START);
445 cnd_pushop (prs->cndtab, prs->newcnd, CND_OR);
447 filters: /* %empty */
448 filters: filters filter
449 filtcmp: CMP_EQ { $$ = CND_EQ; }
450 | CMP_NE { $$ = CND_NE; }
451 | CMP_LT { $$ = CND_LT; }
452 | CMP_LE { $$ = CND_LE; }
453 | CMP_GT { $$ = CND_GT; }
454 | CMP_GE { $$ = CND_GE; }
455 filtbase: varnm filtcmp value {
456 var_used_in_condition (prs->vartab, $1, prs->newcnd);
457 var_used_in_condition (prs->vartab, $3, prs->newcnd);
458 cnd_needvar (prs->cndtab, prs->newcnd, $1);
459 cnd_needvar (prs->cndtab, prs->newcnd, $3);
460 /* TODO: Type checking? Operator applicability checking? */
461 cnd_pushvar (prs->cndtab, prs->newcnd, $1);
462 cnd_pushvar (prs->cndtab, prs->newcnd, $3);
463 cnd_pushop (prs->cndtab, prs->newcnd, $2);
467 // value_s: BRA valuelist KET
469 // valuelist: valuelist COMMA value
470 optvalue: value { $$ = $1; }
471 optvalue: SILENCER { $$ = VARNUM_BAD; }
473 dnvar: varnm { $$ = $1; }
474 drvout_vallist_s: drvout_vallist_s COMMA drvout_vallist_s
475 drvout_vallist_s: value {
476 if (prs->newdrv == DRVNUM_BAD) {
477 prs->newdrv = drv_new (prs->drvtab);
479 drv_output_variable (prs->drvtab, prs->newdrv, $1);
480 var_used_in_driverout (prs->vartab, $1, prs->newdrv);
482 drvout_vallist_s: BRA {
483 drv_output_list_begin (prs->drvtab, prs->newdrv);
484 } drvout_vallist KET {
485 drv_output_list_end (prs->drvtab, prs->newdrv);
487 drvout_vallist: drvout_vallist COMMA drvout_vallist
488 drvout_vallist: value {
489 if (prs->newdrv == DRVNUM_BAD) {
490 prs->newdrv = drv_new (prs->drvtab);
492 drv_output_variable (prs->drvtab, prs->newdrv, $1);
493 var_used_in_driverout (prs->vartab, $1, prs->newdrv);
496 varlist: varlist COMMA varnm
498 const: STRING | INTEGER | FLOAT | BLOB
499 parmlist: parmlist COMMA parm
501 parm: PARAMETER CMP_EQ const {
504 _actO (prs, BNDO_ACT_CMP); /* Not really a comparison, but assignment */