effb44274d3d60a8c8f475f66f899a6d6e63f8bb
[steamworks] / src / pulley / pulleyscript / pulley.yacc
1
2 %{
3
4 #include <assert.h>
5
6 #include "parser.h"
7 #include "variable.h"
8 #include "condition.h"
9 #include "generator.h"
10 #include "driver.h"
11 #include "binding.h"
12
13 %}
14
15 %union {
16         int intval;
17         float fltval;
18         char *strval;
19         struct {
20                 size_t len;
21                 uint8_t *data;
22         } binval;
23         varnum_t varnum;
24 }
25
26 %{
27
28 // int yylex (YYSTYPE *yylval_param, yyscan_t yyscanner);
29 void yyerror (struct parser *,char *);
30 #define YYLEX_PARAM &yylval, prs->lexer
31
32 %}
33
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
42
43 %type<varnum> INTEGER FLOAT STRING BLOB DRIVERNAME ATTRTYPE
44 %type<varnum> VARIABLE PARAMETER optvalue value const varnm dnvar bindrdnpart
45 %type<intval> filtcmp
46
47 %left COMMA
48
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
53  */
54 %pure-parser
55 %lex-param { YYSTYPE *yylval_param, yyscan_t prs_arrow_scanner }
56 //HUH?// %define api.puri
57 %parse-param { struct parser *prs }
58
59 %{
60 #define prs_arrow_scanner prs->lexer
61 %}
62
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.
68  */
69 %start switch
70 %token START_SCRIPT START_LINE START_GENERATOR START_CONDITION START_DRIVEROUT
71
72 %verbose
73
74 %{
75
76 // The following code implements a stack, together with a virtual production
77 // rule _pushV that does the opposite of _popV.
78 //
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.
82
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");
87         }
88         return prs->varstack [prs->varstack_sp];
89 }
90
91 void _pushV (struct parser *prs) {
92         prs->varstack_sp++;
93         if (prs->varstack_sp >= PARSER_VARIABLE_STACK_ENTRIES) {
94                 fatal_error ("Parser ran out of variable stack levels");
95         }
96         bitset_empty (prs->varstack [prs->varstack_sp]);
97 }
98
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");
103         }
104         return prs->varstack [prs->varstack_sp--];
105 }
106
107 // _clrV wipes the variable stack
108 void _clrV (struct parser *prs) {
109         prs->varstack_sp = 0;
110         bitset_empty (prs->varstack [0]);
111 }
112
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.
119
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));
126         here--;
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) {
136                         case BNDO_ACT_DOWN:
137                         case BNDO_ACT_OBJECT:
138                         case BNDO_ACT_DONE:
139                                 // Uses no parameters
140                                 here += 1;
141                                 break;
142                         case BNDO_ACT_HAVE:
143                                 // Only uses $1
144                                 here += 1 + sizeof (varnum_t);
145                                 break;
146                         case BNDO_ACT_BIND:
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);
154                                         break;
155                                 }
156                                 // ...continue into BNDO_ACT_CMP...
157                         case BNDO_ACT_CMP:
158                                 // Uses both $1 and $2
159                                 here += 1 + 2 * sizeof (varnum_t);
160                                 break;
161                         }
162                 }
163         }
164 }
165
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;
174 }
175
176 // _clrO clears the action buffer.
177 void _clrO (struct parser *prs) {
178         prs->action_sp = sizeof (prs->action) -1;
179 }
180
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);
193
194         return binding;
195 }
196
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;
201         varnum_t binding;
202         int len = sizeof (hash_t);
203         uint8_t *ptr = (uint8_t *) &linehash;
204         memcpy (bndname, "bnd_", 4);
205         char *hexstr = bndname + 4;
206         while (len-- > 0) {
207                 sprintf (hexstr, "%02x", *ptr++);
208                 hexstr += 2;
209         }
210         binding = _store_binding (prs, bndname, &varval);
211 //DEBUG//
212 printf ("Binding %s V%d G%d created: >>> ", bndname, binding, g);
213 ptr = varval.typed_blob.str;
214 len = varval.typed_blob.len;
215 while (len-- > 0) {
216         printf ("%02x ", *ptr++);
217 }
218 printf ("<<<\n");
219
220         gen_set_binding(prs->gentab, g, binding);
221 }
222
223 %}
224
225 %%
226
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().
230  */
231 _pushV: { _pushV (prs); }
232
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.
238  */
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
244
245 script: /* %empty */
246 script: script line NEWLINE
247 script: script error NEWLINE
248
249 line: line_generator | line_condition | line_driverout | line_empty
250
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.
253  */
254 line_empty:
255
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.
259  */
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);
263         varnum_t dnvar = $4;
264         bitset_t *bindvars = _tosV (prs);
265         gennum_t gennew = gen_new (prs->gentab, dnvar);
266         bitset_iter_t lvi;
267         hash_t linehash;
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);
274         }
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");
278         }
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);
283         _clrV (prs);
284         _clrO (prs);
285 }
286
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.
293  */
294 line_condition: filter annotations {
295         bitset_t *filtervars = _tosV (prs);     // Includes explicit guards!
296         bitset_iter_t fvi;
297         bitset_iterator_init (&fvi, filtervars);
298         hash_t linehash;
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);
303         }
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);
307         _clrV (prs);
308         prs->newcnd = CNDNUM_BAD;
309 }
310
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.
320  */
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);
325         bitset_iter_t voi;
326         bitset_iterator_init (&voi, varout);
327         while (bitset_iterator_next_one (&voi, NULL)) {
328                 varnum_t varnum = bitset_iterator_bitnum (&voi);
329         }
330         _clrV (prs);
331 } DRIVERNAME {
332         char *module = var_get_name (prs->vartab, $4);
333         drv_set_module (prs->drvtab, prs->newdrv, module);
334         bitset_t *driver = _tosV (prs);
335         _clrV (prs);
336         _clrO (prs);
337 } OPEN parmlist CLOSE {
338         char bndname [7 + 2 * sizeof (hash_t) + 1];
339         struct var_value varval;
340         varnum_t binding;
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);
345         _clrV (prs);
346 } annotations {
347         bitset_t *guards = _tosV (prs);
348         bitset_iter_t gvi;
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);
354         }
355         drv_set_weight (prs->drvtab, prs->newdrv, prs->weight_set? prs->weight: 3.0);
356         hash_t linehash;
357         hash_curline (&prs->scanhashbuf, &linehash);
358         drv_set_hash (prs->drvtab, prs->newdrv, linehash);
359         _clrV (prs);
360         prs->newdrv = DRVNUM_BAD;
361 }
362
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; }
368
369 binding: bindatr { _actO (prs, BNDO_ACT_OBJECT); }
370 binding: bindatr { _actO (prs, BNDO_ACT_OBJECT); } COMMA bindsteps
371 binding: bindsteps
372 bindsteps: bindstep
373 bindsteps: bindsteps COMMA bindstep
374 bindstep: AT dnvar {
375         bitset_set (_tosV (prs), $2);
376         _varO (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
380 }
381 bindstep: bindrdn {
382         _actO (prs, BNDO_ACT_DOWN);
383 }
384 bindrdn: bindrdnpart
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
391                 _varO (prs, $1);
392                 _actO (prs, BNDO_SUBJ_RDN | BNDO_ACT_HAVE);
393         } else {
394                 uint8_t action;
395                  if (var_get_kind (prs->vartab, $3) == VARKIND_CONSTANT) {
396                         action = BNDO_SUBJ_RDN | BNDO_ACT_CMP;
397                 } else {
398                         action = BNDO_SUBJ_RDN | BNDO_ACT_BIND;
399                 }
400                 _varO (prs, $3);
401                 _varO (prs, $1);
402                 _actO (prs, action);
403                 bitset_set (_tosV (prs), $3);
404         }
405 }
406 bindatr: atmatch
407 bindatr: bindatr PLUS atmatch
408 atmatch: ATTRTYPE COLON optvalue {
409         if ($3 == VARNUM_BAD) {
410                 // SILENCER means just require having $1
411                 _varO (prs, $1);
412                 _actO (prs, BNDO_SUBJ_ATTR | BNDO_ACT_HAVE);
413         } else {
414                 uint8_t action;
415                 if (var_get_kind (prs->vartab, $3) == VARKIND_CONSTANT) {
416                         action = BNDO_SUBJ_ATTR | BNDO_ACT_CMP;
417                 } else {
418                         action = BNDO_SUBJ_ATTR | BNDO_ACT_BIND;
419                 }
420                 _varO (prs, $3);
421                 _varO (prs, $1);
422                 _actO (prs, action);
423                 bitset_set (_tosV (prs), $3);
424         }
425 }
426
427 filter: OPEN {
428         if (prs->newcnd == CNDNUM_BAD) {
429                 prs->newcnd = cnd_new (prs->cndtab);
430         }
431 } filtbody CLOSE
432 filter: OPEN error CLOSE
433 filtbody: filtbase
434 filtbody: LOG_NOT filter {
435         cnd_pushop (prs->cndtab, prs->newcnd, CND_NOT);
436 }
437 filtbody: LOG_AND {
438         cnd_pushop (prs->cndtab, prs->newcnd, CND_SEQ_START);
439 } filters {
440         cnd_pushop (prs->cndtab, prs->newcnd, CND_AND);
441 }
442 filtbody: LOG_OR  {
443         cnd_pushop (prs->cndtab, prs->newcnd, CND_SEQ_START);
444 } filters {
445         cnd_pushop (prs->cndtab, prs->newcnd, CND_OR);
446 }
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);
464 }
465
466 // value_s: value
467 // value_s: BRA valuelist KET
468 // valuelist: value
469 // valuelist: valuelist COMMA value
470 optvalue: value { $$ = $1; }
471 optvalue: SILENCER { $$ = VARNUM_BAD; }
472 value: varnm | const
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);
478         }
479         drv_output_variable (prs->drvtab, prs->newdrv, $1);
480         var_used_in_driverout (prs->vartab, $1, prs->newdrv);
481 }
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);
486 }
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);
491         }
492         drv_output_variable (prs->drvtab, prs->newdrv, $1);
493         var_used_in_driverout (prs->vartab, $1, prs->newdrv);
494 }
495 varlist: varnm
496 varlist: varlist COMMA varnm
497 varnm: VARIABLE
498 const: STRING | INTEGER | FLOAT | BLOB
499 parmlist: parmlist COMMA parm
500 parmlist: parm
501 parm: PARAMETER CMP_EQ const {
502         _varO (prs, $3);
503         _varO (prs, $1);
504         _actO (prs, BNDO_ACT_CMP);  /* Not really a comparison, but assignment */
505 }
506
507 %%
508