Pulleyscript: remove declaration of yylex()
[steamworks] / src / pulley / pulleyscript / pulley.yacc
1
2 %{
3
4 #include "parser.h"
5 #include "variable.h"
6 #include "condition.h"
7 #include "generator.h"
8 #include "driver.h"
9
10 %}
11
12 %union {
13         int intval;
14         float fltval;
15         char *strval;
16         struct {
17                 size_t len;
18                 uint8_t *data;
19         } binval;
20         varnum_t varnum;
21 }
22
23 %{
24
25 // int yylex (YYSTYPE *yylval_param, yyscan_t yyscanner);
26 void yyerror (struct parser *,char *);
27 #define YYLEX_PARAM &yylval, prs->lexer
28
29 %}
30
31 %token NEWLINE INDENT COMMENT
32 %token GEN_FROM DRIVE_TO
33 %token OPEN CLOSE BRA KET
34 %token STAR PLUS COMMA COLON AT
35 %token CMP_EQ CMP_NE CMP_LT CMP_LE CMP_GT CMP_GE
36 %token LOG_NOT LOG_AND LOG_OR
37 %token ATTRTYPE ATTRNAME ATTROPT VARIABLE SILENCER DRIVERNAME PARAMETER
38 %token STRING BLOB INTEGER FLOAT
39
40 %type<varnum> INTEGER FLOAT STRING BLOB DRIVERNAME
41 %type<varnum> VARIABLE PARAMETER optvalue value const varnm dnvar bindrdnpart
42 %type<intval> filtcmp
43
44 %left COMMA
45
46 /* The parser is made re-entrant, with parameters for the various tables
47  * that will store the semantics for later analysis.
48  * Kludge: Bison seems to extract the last identifier for lex-param only,
49  * so we've defined one to take that place
50  */
51 %pure-parser
52 %lex-param { YYSTYPE *yylval_param, yyscan_t prs_arrow_scanner }
53 //HUH?// %define api.puri
54 %parse-param { struct parser *prs }
55
56 %{
57 #define prs_arrow_scanner prs->lexer
58 %}
59
60 /* Define a switching start symbol, and hav yylex() prefix the input stream
61  * with a suitable start condition switch; this makes for flexibility in the
62  * syntax scanned with bison.  The global bison_switch directs yylex() to
63  * deliver these special starting tags.  The syntax below requires it, but
64  * will only accept it at the beginning of the parsing process.
65  */
66 %start switch
67 %token START_SCRIPT START_LINE START_GENERATOR START_CONDITION START_DRIVEROUT
68
69 %verbose
70
71 %{
72
73 // The following code implements a stack, together with a virtual production
74 // rule _pushV that does the opposite of _popV.
75 //
76 // This may not be the best option; we might also return bitsets of variables
77 // from the various non-terminals in the grammar, set their %type and use $i.
78 // The vital matter in that case is allocation and reuse of the variable sets.
79
80 // _tosV is the top of the variable stack
81 bitset_t *_tosV (struct parser *prs) {
82         if (prs->varstack_sp < 0) {
83                 fatal_error ("Cannot share top of empty stack");
84         }
85         return prs->varstack [prs->varstack_sp];
86 }
87
88 void _pushV (struct parser *prs) {
89         prs->varstack_sp++;
90         if (prs->varstack_sp >= PARSER_VARIABLE_STACK_ENTRIES) {
91                 fatal_error ("Parser ran out of variable stack levels");
92         }
93         bitset_empty (prs->varstack [prs->varstack_sp]);
94 }
95
96 // _popV retrieves the top element of the variable stack
97 bitset_t *_popV (struct parser *prs) {
98         if (prs->varstack_sp <= 0) {
99                 fatal_error ("Attempt to pop top element of variable stack");
100         }
101         return prs->varstack [prs->varstack_sp--];
102 }
103
104 // _clrV wipes the variable stack
105 void _clrV (struct parser *prs) {
106         prs->varstack_sp = 0;
107         bitset_empty (prs->varstack [0]);
108 }
109
110 %}
111
112 %%
113
114 /* The definition of a (fake) construct that pushes that variable stack.
115  * This is used to separate contexts of variables within a line.  The new
116  * top-of-stack accumulates new variables, and starts empty after _pushV().
117  */
118 _pushV: { _pushV (prs); }
119
120 /* When initiated by the pulley_parser_XXX() functions, they will be setup
121  * to parse either a single line, possibly even of a particular type, or a
122  * sequence of lines informally known as a script.  The switch implements
123  * this behaviour, in unison with a bit of startup code in the lexer that
124  * inserts the special START_xxx symbols at the head of the lexeme stream.
125  */
126 switch: START_SCRIPT script
127 switch: START_LINE line
128 switch: START_GENERATOR line_generator
129 switch: START_CONDITION line_condition
130 switch: START_DRIVEROUT line_driverout
131
132 script: /* %empty */
133 script: script line NEWLINE
134 script: script error NEWLINE
135
136 line: line_generator | line_condition | line_driverout | line_empty
137
138 /* Empty lines constitute acceptable grammar withing scripts, or if the line
139  * type has not been specified.  It does not make any semantical modifications.
140  */
141 line_empty:
142
143 /* Generator lines contain variables that are bound to the environment, they
144  * extract information from a DN-type variable and they have annotations that
145  * can define guard variables and a line weight.
146  */
147 line_generator: error GEN_FROM dnvar annotations { _clrV (prs); }
148 line_generator: binding GEN_FROM _pushV dnvar annotations {
149         bitset_t *guards = _popV (prs);
150         varnum_t dnvar = $4;
151         bitset_t *bindvars = _tosV (prs);
152         gennum_t gennew = gen_new (prs->gentab, dnvar);
153         bitset_iter_t lvi;
154         hash_t linehash;
155 printf ("FOUND %d bound variables in generator line\n", bitset_count (bindvars));
156         bitset_iterator_init (&lvi, bindvars);
157         while (bitset_iterator_next_one (&lvi, NULL)) {
158                 varnum_t varnum = bitset_iterator_bitnum (&lvi);
159                 var_used_in_generator (prs->vartab, varnum, gennew);
160                 gen_add_variable (prs->gentab, gennew, varnum);
161         }
162         if (! bitset_isempty (guards)) {
163                 //TODO// Process guards (but first discover what they mean!)
164                 fprintf (stderr, "WARNING: Ignoring explicit guards to generator, unsure about semantics...\n");
165         }
166         gen_set_weight (prs->gentab, gennew, prs->weight_set? prs->weight: 250.0);
167         hash_curline (&prs->scanhashbuf, &linehash);
168         gen_set_hash (prs->gentab, gennew, linehash);
169         _clrV (prs);
170 }
171
172 /* Condition lines simply mention a filter and annotations.  As part of their
173  * most valuable impact on semantics, they cause variable partitions; but
174  * this is not currently worked out.  Instead, sets of variables used in
175  * conditions are setup for later processing with cndtab_drive_partitions().
176  * Annotations may set a weight and could introduce more variables as guards
177  * that will also be taken into account during partitioning.
178  */
179 line_condition: filter annotations {
180         bitset_t *filtervars = _tosV (prs);     // Includes explicit guards!
181         bitset_iter_t fvi;
182         bitset_iterator_init (&fvi, filtervars);
183         hash_t linehash;
184         while (bitset_iterator_next_one (&fvi, NULL)) {
185                 varnum_t varnum = bitset_iterator_bitnum (&fvi);
186                 var_used_in_condition (prs->vartab, varnum, prs->newcnd);
187                 cnd_needvar (prs->cndtab, prs->newcnd, varnum);
188         }
189         cnd_set_weight (prs->cndtab, prs->newcnd, prs->weight_set? prs->weight: 0.02);
190         hash_curline (&prs->scanhashbuf, &linehash);
191         cnd_set_hash (prs->cndtab, prs->newcnd, linehash);
192         _clrV (prs);
193         prs->newcnd = CNDNUM_BAD;
194 }
195
196 /* Driver output lines specify a number of variables that will be sent out,
197  * they name an output driver module as well as constant settings for named
198  * parameters that this driver module may expect.  The constant settings are
199  * provided while configuring the driver module; the variables will be supplied
200  * when actually transmitting data through the driver module.
201  * Processing of the line's information is done as soon as it is recognised,
202  * to save internal structures beyond the sets that we wish to maintain.
203  * Although a weight could be defined, it hardly has an impact on ordering;
204  * although guards could be specified, it barely seems interesting to do so.
205  */
206 //USELESS???// line_driveout: error DRIVE_TO DRIVERNAME OPEN parmlist CLOSE annotations { _clrV (); }
207 //USELESS???// line_driveout: error CLOSE annotations { _clrV (); }
208 line_driverout: drvout_vallist_s DRIVE_TO {
209         bitset_t *varout = _tosV (prs);
210         bitset_iter_t voi;
211         bitset_iterator_init (&voi, varout);
212         while (bitset_iterator_next_one (&voi, NULL)) {
213                 varnum_t varnum = bitset_iterator_bitnum (&voi);
214         }
215         _clrV (prs);
216 } DRIVERNAME {
217         char *module = var_get_name (prs->vartab, $4);
218         drv_set_module (prs->drvtab, prs->newdrv, module);
219         bitset_t *driver = _tosV (prs);
220         _clrV (prs);
221 } OPEN parmlist CLOSE {
222         bitset_t *params = _tosV (prs);
223         _clrV (prs);
224 } annotations {
225         bitset_t *guards = _tosV (prs);
226         bitset_iter_t gvi;
227         bitset_iterator_init (&gvi, guards);
228         while (bitset_iterator_next_one (&gvi, NULL)) {
229                 varnum_t varnum = bitset_iterator_bitnum (&gvi);
230                 var_used_in_driverout (prs->vartab, varnum, prs->newdrv);
231                 drv_add_guardvar (prs->drvtab, prs->newdrv, varnum);
232         }
233         drv_set_weight (prs->drvtab, prs->newdrv, prs->weight_set? prs->weight: 3.0);
234         hash_t linehash;
235         hash_curline (&prs->scanhashbuf, &linehash);
236         drv_set_hash (prs->drvtab, prs->newdrv, linehash);
237         _clrV (prs);
238         prs->newdrv = DRVNUM_BAD;
239 }
240
241 annotations: annotation1 annotation2
242 annotation1: /* %empty */
243 annotation1: BRA varlist KET
244 annotation2: /* %empty */ { prs->weight_set = false; }
245 annotation2: STAR FLOAT { prs->weight_set = true; prs->weight = /*TODO*/ 0.1; }
246
247 binding: bindatr
248 binding: bindatr COMMA bindsteps
249 binding: bindsteps
250 bindsteps: bindstep
251 bindsteps: bindsteps COMMA bindstep
252 bindstep: AT dnvar {
253         bitset_set (_tosV (prs), $2);
254 }
255 bindstep: bindrdn
256 bindrdn: bindrdnpart
257 bindrdn: bindrdn PLUS bindrdnpart
258 bindrdnpart: ATTRTYPE CMP_EQ optvalue {
259         // The optvalue may be a SILENCER
260         if ($3 != VARNUM_BAD) {
261                 bitset_set (_tosV (prs), $3);
262         }
263 }
264 bindatr: atmatch
265 bindatr: bindatr PLUS atmatch
266 atmatch: ATTRTYPE COLON optvalue {
267         // The optvalue may be a SILENCER
268         if ($3 != VARNUM_BAD) {
269                 bitset_set (_tosV (prs), $3);
270         }
271 }
272
273 filter: OPEN {
274         if (prs->newcnd == CNDNUM_BAD) {
275                 prs->newcnd = cnd_new (prs->cndtab);
276         }
277 } filtbody CLOSE
278 filter: OPEN error CLOSE
279 filtbody: filtbase
280 filtbody: LOG_NOT filter {
281         cnd_pushop (prs->cndtab, prs->newcnd, CND_NOT);
282 }
283 filtbody: LOG_AND {
284         cnd_pushop (prs->cndtab, prs->newcnd, CND_SEQ_START);
285 } filters {
286         cnd_pushop (prs->cndtab, prs->newcnd, CND_AND);
287 }
288 filtbody: LOG_OR  {
289         cnd_pushop (prs->cndtab, prs->newcnd, CND_SEQ_START);
290 } filters {
291         cnd_pushop (prs->cndtab, prs->newcnd, CND_OR);
292 }
293 filters: /* %empty */
294 filters: filters filter
295 filtcmp:  CMP_EQ { $$ = CND_EQ; }
296         | CMP_NE { $$ = CND_NE; }
297         | CMP_LT { $$ = CND_LT; }
298         | CMP_LE { $$ = CND_LE; }
299         | CMP_GT { $$ = CND_GT; }
300         | CMP_GE { $$ = CND_GE; }
301 filtbase: varnm filtcmp value {
302         var_used_in_condition (prs->vartab, $1, prs->newcnd);
303         var_used_in_condition (prs->vartab, $3, prs->newcnd);
304         cnd_needvar (prs->cndtab, prs->newcnd, $1);
305         cnd_needvar (prs->cndtab, prs->newcnd, $3);
306         /* TODO: Type checking? Operator applicability checking? */
307         cnd_pushvar (prs->cndtab, prs->newcnd, $1);
308         cnd_pushvar (prs->cndtab, prs->newcnd, $3);
309         cnd_pushop  (prs->cndtab, prs->newcnd, $2);
310 }
311
312 // value_s: value
313 // value_s: BRA valuelist KET
314 // valuelist: value
315 // valuelist: valuelist COMMA value
316 optvalue: value { $$ = $1; }
317 optvalue: SILENCER { $$ = VARNUM_BAD; }
318 value: varnm | const
319 dnvar: varnm
320 drvout_vallist_s: drvout_vallist_s COMMA drvout_vallist_s
321 drvout_vallist_s: value {
322         if (prs->newdrv == DRVNUM_BAD) {
323                 prs->newdrv = drv_new (prs->drvtab);
324         }
325         drv_output_variable (prs->drvtab, prs->newdrv, $1);
326         var_used_in_driverout (prs->vartab, $1, prs->newdrv);
327 }
328 drvout_vallist_s: BRA {
329         drv_output_list_begin (prs->drvtab, prs->newdrv);
330 } drvout_vallist KET {
331         drv_output_list_end (prs->drvtab, prs->newdrv);
332 }
333 drvout_vallist: drvout_vallist COMMA drvout_vallist
334 drvout_vallist: value {
335         if (prs->newdrv == DRVNUM_BAD) {
336                 prs->newdrv = drv_new (prs->drvtab);
337         }
338         drv_output_variable (prs->drvtab, prs->newdrv, $1);
339         var_used_in_driverout (prs->vartab, $1, prs->newdrv);
340 }
341 varlist: varnm
342 varlist: varlist COMMA varnm
343 varnm: VARIABLE
344 const: STRING | INTEGER | FLOAT | BLOB
345 parmlist: parmlist COMMA parm
346 parmlist: parm
347 parm: PARAMETER CMP_EQ const {
348         char *paramname = var_get_name (prs->vartab, $1);
349         drv_setup_param (prs->drvtab, prs->newdrv, paramname, $3);
350 }
351
352 %%
353