expressions used in if, while, switch are all of the same kind
[asterisk/asterisk.git] / pbx / ael / ael.y
1 %{
2 /*
3  * Asterisk -- An open source telephony toolkit.
4  *
5  * Copyright (C) 2006, Digium, Inc.
6  *
7  * Steve Murphy <murf@parsetree.com>
8  *
9  * See http://www.asterisk.org for more information about
10  * the Asterisk project. Please do not directly contact
11  * any of the maintainers of this project for assistance;
12  * the project provides a web site, mailing lists and IRC
13  * channels for your use.
14  *
15  * This program is free software, distributed under the terms of
16  * the GNU General Public License Version 2. See the LICENSE file
17  * at the top of the source tree.
18  */
19 /*! \file
20  *
21  * \brief Bison Grammar description of AEL2.
22  *
23  */
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include "asterisk/logger.h"
28 #include "asterisk/ael_structs.h"
29
30 static pval * linku1(pval *head, pval *tail);
31
32 void reset_parencount(yyscan_t yyscanner);
33 void reset_semicount(yyscan_t yyscanner);
34 void reset_argcount(yyscan_t yyscanner );
35
36 #define YYLEX_PARAM ((struct parse_io *)parseio)->scanner
37 #define YYERROR_VERBOSE 1
38
39 extern char *my_file;
40 #ifdef AAL_ARGCHECK
41 int ael_is_funcname(char *name);
42 #endif
43 static char *ael_token_subst(char *mess);
44
45 %}
46
47
48 %union {
49         int     intval;         /* integer value, typically flags */
50         char    *str;           /* strings */
51         struct pval *pval;      /* full objects */
52 }
53
54 %{
55         /* declaring these AFTER the union makes things a lot simpler! */
56 void yyerror(YYLTYPE *locp, struct parse_io *parseio, char const *s);
57 int ael_yylex (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , void * yyscanner);
58
59 /* create a new object with start-end marker */
60 static pval *npval(pvaltype type, int first_line, int last_line,
61         int first_column, int last_column);
62
63 /* create a new object with start-end marker, simplified interface.
64  * Must be declared here because YYLTYPE is not known before
65  */
66 static pval *npval2(pvaltype type, YYLTYPE *first, YYLTYPE *last);
67
68 /* another frontend for npval, this time for a string */
69 static pval *nword(char *string, YYLTYPE *pos);
70
71 /* update end position of an object, return the object */
72 static pval *update_last(pval *, YYLTYPE *);
73 %}
74
75
76 %token KW_CONTEXT LC RC LP RP SEMI EQ COMMA COLON AMPER BAR AT
77 %token KW_MACRO KW_GLOBALS KW_IGNOREPAT KW_SWITCH KW_IF KW_IFTIME KW_ELSE KW_RANDOM KW_ABSTRACT
78 %token EXTENMARK KW_GOTO KW_JUMP KW_RETURN KW_BREAK KW_CONTINUE KW_REGEXTEN KW_HINT
79 %token KW_FOR KW_WHILE KW_CASE KW_PATTERN KW_DEFAULT KW_CATCH KW_SWITCHES KW_ESWITCHES
80 %token KW_INCLUDES
81
82 %right BAR COMMA
83
84 %token <str> word
85
86 %type <pval>includes
87 %type <pval>includeslist
88 %type <pval>switchlist
89 %type <pval>eswitches
90 %type <pval>switches
91 %type <pval>macro_statement
92 %type <pval>macro_statements
93 %type <pval>case_statement
94 %type <pval>case_statements
95 %type <pval>eval_arglist
96 %type <pval>application_call
97 %type <pval>application_call_head
98 %type <pval>macro_call
99 %type <pval>target jumptarget
100 %type <pval>statement
101 %type <pval>switch_head
102
103 %type <pval>if_like_head
104 %type <pval>statements
105 %type <pval>extension
106 %type <pval>ignorepat
107 %type <pval>element
108 %type <pval>elements
109 %type <pval>arglist
110 %type <pval>global_statement
111 %type <pval>global_statements
112 %type <pval>globals
113 %type <pval>macro
114 %type <pval>context
115 %type <pval>object
116 %type <pval>objects
117 %type <pval>file
118 /* XXX lr changes */
119 %type <pval>opt_else
120 %type <pval>elements_block
121 %type <pval>switchlist_block
122 %type <pval>timespec
123 %type <pval>included_entry
124
125 %type <str>opt_word
126 %type <str>context_name
127 %type <str>timerange
128
129 %type <str>goto_word
130 %type <str>word_list
131 %type <str>word3_list
132 %type <str>test_expr
133
134 %type <intval>opt_abstract
135
136 /*
137  * OPTIONS
138  */
139
140 %locations      /* track source location using @n variables (yylloc in flex) */
141 %pure-parser    /* pass yylval and yylloc as arguments to yylex(). */
142 %name-prefix="ael_yy"
143 /*
144  * add an additional argument, parseio, to yyparse(),
145  * which is then accessible in the grammar actions
146  */
147 %parse-param {struct parse_io *parseio}
148
149 /* there will be two shift/reduce conflicts, they involve the if statement, where a single statement occurs not wrapped in curlies in the "true" section
150    the default action to shift will attach the else to the preceeding if. */
151 %expect 5
152 %error-verbose
153
154 /*
155  * declare destructors for objects.
156  * The former is for pval, the latter for strings.
157  * NOTE: we must not have a destructor for a 'file' object.
158  */
159 %destructor {
160                 destroy_pval($$);
161                 prev_word=0;
162         }       includes includeslist switchlist eswitches switches
163                 macro_statement macro_statements case_statement case_statements
164                 eval_arglist application_call application_call_head
165                 macro_call target jumptarget statement switch_head
166                 if_like_head statements extension
167                 ignorepat element elements arglist global_statement
168                 global_statements globals macro context object objects
169                 opt_else
170                 elements_block switchlist_block
171                 timespec included_entry
172
173 %destructor { free($$);}  word word_list goto_word word3_list opt_word context_name
174                 timerange
175                 test_expr
176
177
178 %%
179
180 file : objects  { $$ = parseio->pval = $1; }
181         ;
182
183 objects : object {$$=$1;}
184         | objects object { $$ = linku1($1, $2); }
185         | objects error {$$=$1;}
186         ;
187
188 object : context {$$=$1;}
189         | macro {$$=$1;}
190         | globals {$$=$1;}
191         | SEMI  {$$=0;/* allow older docs to be read */}
192         ;
193
194 context_name : word { $$ = $1; }
195         | KW_DEFAULT { $$ = strdup("default"); }
196         ;
197
198 context : opt_abstract KW_CONTEXT context_name elements_block {
199                 $$ = npval2(PV_CONTEXT, &@1, &@4);
200                 $$->u1.str = $3;
201                 $$->u2.statements = $4;
202                 $$->u3.abstract = $1; }
203         ;
204
205 /* optional "abstract" keyword  XXX there is no regression test for this */
206 opt_abstract: KW_ABSTRACT { $$ = 1; }
207         | /* nothing */ { $$ = 0; }
208         ;
209
210 macro : KW_MACRO word LP arglist RP LC macro_statements RC {
211                 $$ = npval2(PV_MACRO, &@1, &@8);
212                 $$->u1.str = $2; $$->u2.arglist = $4; $$->u3.macro_statements = $7; }
213         ;
214
215 globals : KW_GLOBALS LC global_statements RC {
216                 $$ = npval2(PV_GLOBALS, &@1, &@4);
217                 $$->u1.statements = $3;}
218         | KW_GLOBALS LC RC { /* empty globals is OK */
219                 $$ = npval2(PV_GLOBALS, &@1, &@3); }
220         ;
221
222 global_statements : global_statement {$$=$1;}
223         | global_statements global_statement {$$ = linku1($1, $2); }
224         | global_statements error {$$=$1;}
225         ;
226
227 global_statement : word EQ { reset_semicount(parseio->scanner); }  word SEMI {
228                 $$ = npval2(PV_VARDEC, &@1, &@5);
229                 $$->u1.str = $1;
230                 $$->u2.val = $4; }
231         ;
232
233 arglist : /* empty */ { $$ = NULL; }
234         | word { $$ = nword($1, &@1); }
235         | arglist COMMA word { $$ = linku1($1, nword($3, &@3)); }
236         | arglist error {$$=$1;}
237         ;
238
239 elements_block : LC RC  { $$ = NULL; }
240         | LC elements RC { $$ = $2; }
241         ;
242
243 elements : element { $$=$1;}
244         | error {$$=0;}
245         | elements element { $$ = linku1($1, $2); }
246         | elements error   { $$=$1;}
247         ;
248
249 element : extension {$$=$1;}
250         | includes {$$=$1;}
251         | switches {$$=$1;}
252         | eswitches {$$=$1;}
253         | ignorepat {$$=$1;}
254         | word EQ { reset_semicount(parseio->scanner); } word SEMI {
255                 $$ = npval2(PV_VARDEC, &@1, &@5);
256                 $$->u1.str = $1;
257                 $$->u2.val = $4; }
258         | word error {free($1); $$=0;}
259         | SEMI  {$$=0;/* allow older docs to be read */}
260         ;
261
262 ignorepat : KW_IGNOREPAT EXTENMARK word SEMI {
263                 $$ = npval2(PV_IGNOREPAT, &@1, &@4);
264                 $$->u1.str = $3;}
265         ;
266
267 extension : word EXTENMARK statement {
268                 $$ = npval2(PV_EXTENSION, &@1, &@3);
269                 $$->u1.str = $1;
270                 $$->u2.statements = $3; }
271         | KW_REGEXTEN word EXTENMARK statement {
272                 $$ = npval2(PV_EXTENSION, &@1, &@4);
273                 $$->u1.str = $2;
274                 $$->u2.statements = $4;
275                 $$->u4.regexten=1;}
276         | KW_HINT LP word3_list RP word EXTENMARK statement {
277                 $$ = npval2(PV_EXTENSION, &@1, &@7);
278                 $$->u1.str = $5;
279                 $$->u2.statements = $7;
280                 $$->u3.hints = $3;}
281         | KW_REGEXTEN KW_HINT LP word3_list RP word EXTENMARK statement {
282                 $$ = npval2(PV_EXTENSION, &@1, &@8);
283                 $$->u1.str = $6;
284                 $$->u2.statements = $8;
285                 $$->u4.regexten=1;
286                 $$->u3.hints = $4;}
287
288         ;
289
290 statements : statement {$$=$1;}
291         | statements statement { $$ = linku1($1, $2); }
292         | statements error {$$=$1;}
293         ;
294
295 /* hh:mm-hh:mm, due to the way the parser works we do not
296  * detect the '-' but only the ':' as separator
297  */
298 timerange: word3_list COLON word3_list COLON word3_list {
299                 asprintf(&$$, "%s:%s:%s", $1, $3, $5);
300                 free($1);
301                 free($3);
302                 free($5); }
303         | word { $$ = $1; }
304         ;
305
306 /* full time specification range|dow|*|* */
307 timespec : timerange BAR word3_list BAR word3_list BAR word3_list {
308                 $$ = nword($1, &@1);
309                 $$->u1.list = nword($3, &@3);
310                 $$->u1.list->next = nword($5, &@5);
311                 $$->u1.list->next->next = nword($7, &@7); }
312         ;
313
314 /* expression used in if, random, while, switch */
315 test_expr : LP { reset_parencount(parseio->scanner); }  word_list RP {
316                 $$ = $3; }
317         ;
318
319 /* 'if' like statements: if, iftime, random */
320 if_like_head : KW_IF test_expr {
321                 $$= npval2(PV_IF, &@1, &@2);
322                 $$->u1.str = $2; }
323         |  KW_RANDOM test_expr {
324                 $$ = npval2(PV_RANDOM, &@1, &@2);
325                 $$->u1.str=$2;}
326         | KW_IFTIME LP timespec RP {
327                 $$ = npval2(PV_IFTIME, &@1, &@4);
328                 $$->u1.list = $3;
329                 prev_word = 0; }
330         ;
331
332 /* word_list is a hack to fix a problem with context switching between bison and flex;
333    by the time you register a new context with flex, you've already got a look-ahead token
334    from the old context, with no way to put it back and start afresh. So, we kludge this
335    and merge the words back together. */
336
337 word_list : word { $$ = $1;}
338         | word word {
339                 asprintf(&($$), "%s%s", $1, $2);
340                 free($1);
341                 free($2);
342                 prev_word = $$;}
343         ;
344
345 word3_list : word { $$ = $1;}
346         | word word {
347                 asprintf(&($$), "%s%s", $1, $2);
348                 free($1);
349                 free($2);
350                 prev_word = $$;}
351         | word word word {
352                 asprintf(&($$), "%s%s%s", $1, $2, $3);
353                 free($1);
354                 free($2);
355                 free($3);
356                 prev_word=$$;}
357         ;
358
359 goto_word : word { $$ = $1;}
360         | word word {
361                 asprintf(&($$), "%s%s", $1, $2);
362                 free($1);
363                 free($2);}
364         | word COLON word {
365                 asprintf(&($$), "%s:%s", $1, $3);
366                 free($1);
367                 free($3);}
368         ;
369
370 switch_head : KW_SWITCH test_expr LC {
371                 $$ = npval2(PV_SWITCH, &@1, &@3);
372                 $$->u1.str = $2; }
373         ;
374
375 /*
376  * Definition of a statememt in our language
377  */
378 statement : LC statements RC {
379                 $$ = npval2(PV_STATEMENTBLOCK, &@1, &@3);
380                 $$->u1.list = $2; }
381         | word EQ {reset_semicount(parseio->scanner);} word SEMI {
382                 $$ = npval2(PV_VARDEC, &@1, &@5);
383                 $$->u1.str = $1;
384                 $$->u2.val = $4; }
385         | KW_GOTO target SEMI {
386                 $$ = npval2(PV_GOTO, &@1, &@3);
387                 $$->u1.list = $2;}
388         | KW_JUMP jumptarget SEMI {
389                 $$ = npval2(PV_GOTO, &@1, &@3);
390                 $$->u1.list = $2;}
391         | word COLON {
392                 $$ = npval2(PV_LABEL, &@1, &@2);
393                 $$->u1.str = $1; }
394         | KW_FOR LP {reset_semicount(parseio->scanner);} word SEMI
395                         {reset_semicount(parseio->scanner);} word SEMI
396                         {reset_parencount(parseio->scanner);} word RP statement { /* XXX word_list maybe ? */
397                 $$ = npval2(PV_FOR, &@1, &@12);
398                 $$->u1.for_init = $4;
399                 $$->u2.for_test=$7;
400                 $$->u3.for_inc = $10;
401                 $$->u4.for_statements = $12;}
402         | KW_WHILE test_expr statement {
403                 $$ = npval2(PV_WHILE, &@1, &@3);
404                 $$->u1.str = $2;
405                 $$->u2.statements = $3; }
406         | switch_head RC /* empty list OK */ {
407                 $$ = update_last($1, &@2); }
408         | switch_head case_statements RC {
409                 $$ = update_last($1, &@3);
410                 $$->u2.statements = $2;}
411         | AMPER macro_call SEMI {
412                 $$ = update_last($2, &@2); }
413         | application_call SEMI {
414                 $$ = update_last($1, &@2); }
415         | word SEMI {
416                 $$= npval2(PV_APPLICATION_CALL, &@1, &@2);
417                 $$->u1.str = $1;}
418         | application_call EQ {reset_semicount(parseio->scanner);} word SEMI {
419                 char *bufx;
420                 int tot=0;
421                 pval *pptr;
422                 $$ = npval2(PV_VARDEC, &@1, &@5);
423                 $$->u2.val=$4;
424                 /* rebuild the original string-- this is not an app call, it's an unwrapped vardec, with a func call on the LHS */
425                 /* string to big to fit in the buffer? */
426                 tot+=strlen($1->u1.str);
427                 for(pptr=$1->u2.arglist;pptr;pptr=pptr->next) {
428                         tot+=strlen(pptr->u1.str);
429                         tot++; /* for a sep like a comma */
430                 }
431                 tot+=4; /* for safety */
432                 bufx = calloc(1, tot);
433                 strcpy(bufx,$1->u1.str);
434                 strcat(bufx,"(");
435                 /* XXX need to advance the pointer or the loop is very inefficient */
436                 for (pptr=$1->u2.arglist;pptr;pptr=pptr->next) {
437                         if ( pptr != $1->u2.arglist )
438                                 strcat(bufx,",");
439                         strcat(bufx,pptr->u1.str);
440                 }
441                 strcat(bufx,")");
442 #ifdef AAL_ARGCHECK
443                 if ( !ael_is_funcname($1->u1.str) )
444                         ast_log(LOG_WARNING, "==== File: %s, Line %d, Cols: %d-%d: Function call? The name %s is not in my internal list of function names\n",
445                                 my_file, @1.first_line, @1.first_column, @1.last_column, $1->u1.str);
446 #endif
447                 $$->u1.str = bufx;
448                 destroy_pval($1); /* the app call it is not, get rid of that chain */
449                 prev_word = 0;
450         }
451         | KW_BREAK SEMI { $$ = npval2(PV_BREAK, &@1, &@2); }
452         | KW_RETURN SEMI { $$ = npval2(PV_RETURN, &@1, &@2); }
453         | KW_CONTINUE SEMI { $$ = npval2(PV_CONTINUE, &@1, &@2); }
454         | if_like_head statement opt_else {
455                 $$ = update_last($1, &@2);
456                 $$->u2.statements = $2;
457                 $$->u3.else_statements = $3;}
458         | SEMI { $$=0; }
459         ;
460
461 opt_else : KW_ELSE statement { $$ = $2; }
462         | { $$ = NULL ; }
463
464 /* XXX unused */
465 bar_or_comma: BAR | COMMA ;
466
467 target : goto_word { $$ = nword($1, &@1); }
468         | goto_word BAR goto_word {
469                 $$ = nword($1, &@1);
470                 $$->next = nword($3, &@3); }
471         | goto_word COMMA goto_word {
472                 $$ = nword($1, &@1);
473                 $$->next = nword($3, &@3); }
474         | goto_word BAR goto_word BAR goto_word {
475                 $$ = nword($1, &@1);
476                 $$->next = nword($3, &@3);
477                 $$->next->next = nword($5, &@5); }
478         | goto_word COMMA goto_word COMMA goto_word {
479                 $$ = nword($1, &@1);
480                 $$->next = nword($3, &@3);
481                 $$->next->next = nword($5, &@5); }
482         | KW_DEFAULT BAR goto_word BAR goto_word {
483                 $$ = nword(strdup("default"), &@1);
484                 $$->next = nword($3, &@3);
485                 $$->next->next = nword($5, &@5); }
486         | KW_DEFAULT COMMA goto_word COMMA goto_word {
487                 $$ = nword(strdup("default"), &@1);
488                 $$->next = nword($3, &@3);
489                 $$->next->next = nword($5, &@5); }
490         ;
491
492 /* XXX please document the form of jumptarget */
493 jumptarget : goto_word {
494                 $$ = nword($1, &@1);
495                 $$->next = nword(strdup("1"), &@1); }  /*  jump extension[,priority][@context] */
496         | goto_word COMMA goto_word {
497                 $$ = nword($1, &@1);
498                 $$->next = nword($3, &@3); }
499         | goto_word COMMA word AT word {        /* XXX they are stored in a different order */
500                 $$ = nword($5, &@5);
501                 $$->next = nword($1, &@1);
502                 $$->next->next = nword($3, &@3); }
503         | goto_word AT goto_word {
504                 $$ = nword($3, &@3);
505                 $$->next = nword($1, &@1);
506                 $$->next->next = nword(strdup("1"), &@3); }
507         | goto_word COMMA word AT KW_DEFAULT {
508                 $$ = nword(strdup("default"), &@1);
509                 $$->next = nword($1, &@1);
510                 $$->next->next = nword($3, &@3); }
511         | goto_word AT KW_DEFAULT {
512                 $$ = nword(strdup("default"), &@1);
513                 $$->next = nword($1, &@3);
514                 $$->next->next = nword( strdup("1"), &@3); }
515         ;
516
517 macro_call : word LP {reset_argcount(parseio->scanner);} eval_arglist RP {
518                 /* XXX original code had @2 but i think we need @5 */
519                 $$ = npval2(PV_MACRO_CALL, &@1, &@5);
520                 $$->u1.str = $1;
521                 $$->u2.arglist = $4;}
522         | word LP RP {
523                 $$= npval2(PV_MACRO_CALL, &@1, &@3);
524                 $$->u1.str = $1; }
525         ;
526
527 /* XXX application_call_head must be revised. Having 'word LP { ...'
528  * just as above should work fine, however it gives a different result.
529  */
530 application_call_head: word LP {reset_argcount(parseio->scanner);} {
531                 if (strcasecmp($1,"goto") == 0) {
532                         $$ = npval2(PV_GOTO, &@1, &@2);
533                         free($1); /* won't be using this */
534                         ast_log(LOG_WARNING, "==== File: %s, Line %d, Cols: %d-%d: Suggestion: Use the goto statement instead of the Goto() application call in AEL.\n", my_file, @1.first_line, @1.first_column, @1.last_column );
535                 } else {
536                         $$= npval2(PV_APPLICATION_CALL, &@1, &@2);
537                         $$->u1.str = $1;
538                 } }
539         ;
540
541 application_call : application_call_head eval_arglist RP {
542                 $$ = update_last($1, &@3);
543                 if( $$->type == PV_GOTO )
544                         $$->u1.list = $2;
545                 else
546                         $$->u2.arglist = $2;
547         }
548         | application_call_head RP { $$ = update_last($1, &@2); }
549         ;
550
551 opt_word : word { $$ = $1 }
552         | { $$ = strdup(""); }
553         ;
554
555 eval_arglist :  word_list { $$ = nword($1, &@1); }
556         | /*nothing! */   {
557                 $$= npval(PV_WORD,0/*@1.first_line*/,0/*@1.last_line*/,0/* @1.first_column*/, 0/*@1.last_column*/);
558                 $$->u1.str = strdup(""); }
559         | eval_arglist COMMA  opt_word { $$ = linku1($1, nword($3, &@3)); }
560         ;
561
562 case_statements: case_statement {$$=$1;}
563         | case_statements case_statement { $$ = linku1($1, $2); }
564         ;
565
566 case_statement: KW_CASE word COLON statements {
567                 $$ = npval2(PV_CASE, &@1, &@3); /* XXX 3 or 4 ? */
568                 $$->u1.str = $2;
569                 $$->u2.statements = $4;}
570         | KW_DEFAULT COLON statements {
571                 $$ = npval2(PV_DEFAULT, &@1, &@3);
572                 $$->u1.str = NULL;
573                 $$->u2.statements = $3;}
574         | KW_PATTERN word COLON statements {
575                 $$ = npval2(PV_PATTERN, &@1, &@4); /* XXX@3 or @4 ? */
576                 $$->u1.str = $2;
577                 $$->u2.statements = $4;}
578         | KW_CASE word COLON {
579                 $$ = npval2(PV_CASE, &@1, &@3);
580                 $$->u1.str = $2;}
581         | KW_DEFAULT COLON {
582                 $$ = npval2(PV_DEFAULT, &@1, &@2);
583                 $$->u1.str = NULL;}
584         | KW_PATTERN word COLON  {
585                 $$ = npval2(PV_PATTERN, &@1, &@3);
586                 $$->u1.str = $2;}
587         ;
588
589 macro_statements: /* empty */ { $$ = NULL; }
590         | macro_statement {$$ = $1;}
591         | macro_statements macro_statement { $$ = linku1($1, $2); }
592         ;
593
594 macro_statement : statement {$$=$1;}
595         | KW_CATCH word LC statements RC {
596                 $$ = npval2(PV_CATCH, &@1, &@5);
597                 $$->u1.str = $2;
598                 $$->u2.statements = $4;}
599         ;
600
601 switches : KW_SWITCHES switchlist_block {
602                 $$ = npval2(PV_SWITCHES, &@1, &@2);
603                 $$->u1.list = $2; }
604         ;
605
606 eswitches : KW_ESWITCHES switchlist_block {
607                 $$ = npval2(PV_ESWITCHES, &@1, &@2);
608                 $$->u1.list = $2; }
609         ;
610
611 switchlist_block : LC switchlist RC { $$ = $2; }
612         | LC RC { $$ = NULL; }
613         ;
614
615 switchlist : word SEMI { $$ = nword($1, &@1); }
616         | switchlist word SEMI { $$ = linku1($1, nword($2, &@2)); }
617         | switchlist error {$$=$1;}
618         ;
619
620 included_entry : context_name SEMI { $$ = nword($1, &@1); }
621         | context_name BAR timespec SEMI {
622                 $$ = nword($1, &@1);
623                 $$->u2.arglist = $3;
624                 prev_word=0; /* XXX sure ? */ }
625         ;
626
627 /* list of ';' separated context names followed by optional timespec */
628 includeslist : included_entry { $$ = $1; }
629         | includeslist included_entry { $$ = linku1($1, $2); }
630         | includeslist error {$$=$1;}
631         ;
632
633 includes : KW_INCLUDES LC includeslist RC {
634                 $$ = npval2(PV_INCLUDES, &@1, &@4);
635                 $$->u1.list = $3;}
636         | KW_INCLUDES LC RC {
637                 $$ = npval2(PV_INCLUDES, &@1, &@3);}
638         ;
639
640
641 %%
642
643 static char *token_equivs1[] =
644 {
645         "AMPER",
646         "AT",
647         "BAR",
648         "COLON",
649         "COMMA",
650         "EQ",
651         "EXTENMARK",
652         "KW_BREAK",
653         "KW_CASE",
654         "KW_CATCH",
655         "KW_CONTEXT",
656         "KW_CONTINUE",
657         "KW_DEFAULT",
658         "KW_ELSE",
659         "KW_ESWITCHES",
660         "KW_FOR",
661         "KW_GLOBALS",
662         "KW_GOTO",
663         "KW_HINT",
664         "KW_IFTIME",
665         "KW_IF",
666         "KW_IGNOREPAT",
667         "KW_INCLUDES"
668         "KW_JUMP",
669         "KW_MACRO",
670         "KW_PATTERN",
671         "KW_REGEXTEN",
672         "KW_RETURN",
673         "KW_SWITCHES",
674         "KW_SWITCH",
675         "KW_WHILE",
676         "LC",
677         "LP",
678         "RC",
679         "RP",
680         "SEMI",
681 };
682
683 static char *token_equivs2[] =
684 {
685         "&",
686         "@",
687         "|",
688         ":",
689         ",",
690         "=",
691         "=>",
692         "break",
693         "case",
694         "catch",
695         "context",
696         "continue",
697         "default",
698         "else",
699         "eswitches",
700         "for",
701         "globals",
702         "goto",
703         "hint",
704         "ifTime",
705         "if",
706         "ignorepat",
707         "includes"
708         "jump",
709         "macro",
710         "pattern",
711         "regexten",
712         "return",
713         "switches",
714         "switch",
715         "while",
716         "{",
717         "(",
718         "}",
719         ")",
720         ";",
721 };
722
723
724 static char *ael_token_subst(char *mess)
725 {
726         /* calc a length, malloc, fill, and return; yyerror had better free it! */
727         int len=0,i;
728         char *p;
729         char *res, *s,*t;
730         int token_equivs_entries = sizeof(token_equivs1)/sizeof(char*);
731
732         for (p=mess; *p; p++) {
733                 for (i=0; i<token_equivs_entries; i++) {
734                         if ( strncmp(p,token_equivs1[i],strlen(token_equivs1[i])) == 0 )
735                         {
736                                 len+=strlen(token_equivs2[i])+2;
737                                 p += strlen(token_equivs1[i])-1;
738                                 break;
739                         }
740                 }
741                 len++;
742         }
743         res = calloc(1, len+1);
744         res[0] = 0;
745         s = res;
746         for (p=mess; *p;) {
747                 int found = 0;
748                 for (i=0; i<token_equivs_entries; i++) {
749                         if ( strncmp(p,token_equivs1[i],strlen(token_equivs1[i])) == 0 ) {
750                                 *s++ = '\'';
751                                 for (t=token_equivs2[i]; *t;) {
752                                         *s++ = *t++;
753                                 }
754                                 *s++ = '\'';
755                                 p += strlen(token_equivs1[i]);
756                                 found = 1;
757                                 break;
758                         }
759                 }
760                 if( !found )
761                         *s++ = *p++;
762         }
763         *s++ = 0;
764         return res;
765 }
766
767 void yyerror(YYLTYPE *locp, struct parse_io *parseio,  char const *s)
768 {
769         char *s2 = ael_token_subst((char *)s);
770         if (locp->first_line == locp->last_line) {
771                 ast_log(LOG_ERROR, "==== File: %s, Line %d, Cols: %d-%d: Error: %s\n", my_file, locp->first_line, locp->first_column, locp->last_column, s2);
772         } else {
773                 ast_log(LOG_ERROR, "==== File: %s, Line %d Col %d  to Line %d Col %d: Error: %s\n", my_file, locp->first_line, locp->first_column, locp->last_line, locp->last_column, s2);
774         }
775         free(s2);
776         parseio->syntax_error_count++;
777 }
778
779 static struct pval *npval(pvaltype type, int first_line, int last_line,
780         int first_column, int last_column)
781 {
782         pval *z = calloc(1, sizeof(struct pval));
783         z->type = type;
784         z->startline = first_line;
785         z->endline = last_line;
786         z->startcol = first_column;
787         z->endcol = last_column;
788         z->filename = strdup(my_file);
789         return z;
790 }
791
792 static struct pval *npval2(pvaltype type, YYLTYPE *first, YYLTYPE *last)
793 {
794         return npval(type, first->first_line, last->last_line,
795                         first->first_column, last->last_column);
796 }
797
798 static struct pval *update_last(pval *obj, YYLTYPE *last)
799 {
800         obj->endline = last->last_line;
801         obj->endcol = last->last_column;
802         return obj;
803 }
804
805 /* frontend for npval to create a PV_WORD string from the given token */
806 static pval *nword(char *string, YYLTYPE *pos)
807 {
808         pval *p = npval2(PV_WORD, pos, pos);
809         if (p)
810                 p->u1.str = string;
811         return p;
812 }
813
814 /* append second element to the list in the first one */
815 static pval * linku1(pval *head, pval *tail)
816 {
817         if (!head)
818                 return tail;
819         if (tail) {
820                 if (!head->next) {
821                         head->next = tail;
822                 } else {
823                         head->u1_last->next = tail;
824                 }
825                 head->u1_last = tail;
826         }
827         return head;
828 }