1d309cd76e983d45cf26925db956300675efe2ff
[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_head
104 %type <pval>random_head
105 %type <pval>iftime_head
106 %type <pval>statements
107 %type <pval>extension
108 %type <pval>ignorepat
109 %type <pval>element
110 %type <pval>elements
111 %type <pval>arglist
112 %type <pval>global_statement
113 %type <pval>global_statements
114 %type <pval>globals
115 %type <pval>macro
116 %type <pval>context
117 %type <pval>object
118 %type <pval>objects
119 %type <pval>file
120 /* XXX lr changes */
121 %type <pval>opt_else
122 %type <pval>elements_block
123 %type <pval>switchlist_block
124
125 %type <str>opt_word
126 %type <str>word_or_default
127
128 %type <str>goto_word
129 %type <str>word_list
130 %type <str>word3_list
131 %type <str>includedname
132
133 %type <intval>opt_abstract
134
135 /*
136  * OPTIONS
137  */
138
139 %locations      /* track source location using @n variables (yylloc in flex) */
140 %pure-parser    /* pass yylval and yylloc as arguments to yylex(). */
141 %name-prefix="ael_yy"
142 /*
143  * add an additional argument, parseio, to yyparse(),
144  * which is then accessible in the grammar actions
145  */
146 %parse-param {struct parse_io *parseio}
147
148 /* 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
149    the default action to shift will attach the else to the preceeding if. */
150 %expect 5
151 %error-verbose
152
153 /*
154  * declare destructors for objects.
155  * The former is for pval, the latter for strings.
156  * NOTE: we must not have a destructor for a 'file' object.
157  */
158 %destructor {
159                 destroy_pval($$);
160                 prev_word=0;
161         }       includes includeslist switchlist eswitches switches
162                 macro_statement macro_statements case_statement case_statements
163                 eval_arglist application_call application_call_head
164                 macro_call target jumptarget statement switch_head
165                 if_head random_head iftime_head statements extension
166                 ignorepat element elements arglist global_statement
167                 global_statements globals macro context object objects
168                 opt_else
169                 elements_block switchlist_block
170
171 %destructor { free($$);}  word word_list goto_word word3_list includedname opt_word word_or_default
172
173
174 %%
175
176 file : objects  { $$ = parseio->pval = $1; }
177         ;
178
179 objects : object {$$=$1;}
180         | objects object { $$ = linku1($1, $2); }
181         | objects error {$$=$1;}
182         ;
183
184 object : context {$$=$1;}
185         | macro {$$=$1;}
186         | globals {$$=$1;}
187         | SEMI  {$$=0;/* allow older docs to be read */}
188         ;
189
190 word_or_default : word { $$ = $1; }
191         | KW_DEFAULT { $$ = strdup("default"); }
192         ;
193
194 context : opt_abstract KW_CONTEXT word_or_default elements_block {
195                 $$ = npval2(PV_CONTEXT, &@1, &@4);
196                 $$->u1.str = $3;
197                 $$->u2.statements = $4;
198                 $$->u3.abstract = $1; }
199         ;
200
201 /* optional "abstract" keyword */
202 opt_abstract: KW_ABSTRACT { $$ = 1; }
203         | /* nothing */ { $$ = 0; }
204         ;
205
206 macro : KW_MACRO word LP arglist RP LC macro_statements RC {
207                 $$ = npval2(PV_MACRO, &@1, &@8);
208                 $$->u1.str = $2; $$->u2.arglist = $4; $$->u3.macro_statements = $7; }
209         | KW_MACRO word LP arglist RP LC  RC {
210                 $$ = npval2(PV_MACRO, &@1, &@7);
211                 $$->u1.str = $2; $$->u2.arglist = $4; }
212         | KW_MACRO word LP RP LC macro_statements RC {
213                 $$ = npval2(PV_MACRO, &@1, &@7);
214                 $$->u1.str = $2;
215                 $$->u3.macro_statements = $6; }
216         | KW_MACRO word LP RP LC  RC {
217                 $$ = npval2(PV_MACRO, &@1, &@6);
218                 $$->u1.str = $2; }
219         ;
220
221 globals : KW_GLOBALS LC global_statements RC {
222                 $$ = npval2(PV_GLOBALS, &@1, &@4);
223                 $$->u1.statements = $3;}
224         | KW_GLOBALS LC RC { /* empty globals is OK */
225                 $$ = npval2(PV_GLOBALS, &@1, &@3); }
226         ;
227
228 global_statements : global_statement {$$=$1;}
229         | global_statements global_statement {$$ = linku1($1, $2); }
230         | global_statements error {$$=$1;}
231         ;
232
233 global_statement : word EQ { reset_semicount(parseio->scanner); }  word SEMI {
234                 $$ = npval2(PV_VARDEC, &@1, &@5);
235                 $$->u1.str = $1;
236                 $$->u2.val = $4; }
237         ;
238
239 arglist : word { $$ = nword($1, &@1); }
240         | arglist COMMA word { $$ = linku1($1, nword($3, &@3)); }
241         | arglist error {$$=$1;}
242         ;
243
244 elements_block : LC RC  { $$ = NULL; }
245         | LC elements RC { $$ = $2; }
246         ;
247
248 elements : element { $$=$1;}
249         | error {$$=0;}
250         | elements element { $$ = linku1($1, $2); }
251         | elements error   { $$=$1;}
252         ;
253
254 element : extension {$$=$1;}
255         | includes {$$=$1;}
256         | switches {$$=$1;}
257         | eswitches {$$=$1;}
258         | ignorepat {$$=$1;}
259         | word EQ { reset_semicount(parseio->scanner); } word SEMI {
260                 $$ = npval2(PV_VARDEC, &@1, &@5);
261                 $$->u1.str = $1;
262                 $$->u2.val = $4; }
263         | word error {free($1); $$=0;}
264         | SEMI  {$$=0;/* allow older docs to be read */}
265         ;
266
267 ignorepat : KW_IGNOREPAT EXTENMARK word SEMI {
268                 $$ = npval2(PV_IGNOREPAT, &@1, &@4);
269                 $$->u1.str = $3;}
270         ;
271
272 extension : word EXTENMARK statement {
273                 $$ = npval2(PV_EXTENSION, &@1, &@3);
274                 $$->u1.str = $1;
275                 $$->u2.statements = $3; }
276         | KW_REGEXTEN word EXTENMARK statement {
277                 $$ = npval2(PV_EXTENSION, &@1, &@4);
278                 $$->u1.str = $2;
279                 $$->u2.statements = $4;
280                 $$->u4.regexten=1;}
281         | KW_HINT LP word3_list RP word EXTENMARK statement {
282                 $$ = npval2(PV_EXTENSION, &@1, &@7);
283                 $$->u1.str = $5;
284                 $$->u2.statements = $7;
285                 $$->u3.hints = $3;}
286         | KW_REGEXTEN KW_HINT LP word3_list RP word EXTENMARK statement {
287                 $$ = npval2(PV_EXTENSION, &@1, &@8);
288                 $$->u1.str = $6;
289                 $$->u2.statements = $8;
290                 $$->u4.regexten=1;
291                 $$->u3.hints = $4;}
292
293         ;
294
295 statements : statement {$$=$1;}
296         | statements statement { $$ = linku1($1, $2); }
297         | statements error {$$=$1;}
298         ;
299
300 if_head : KW_IF LP { reset_parencount(parseio->scanner); }  word_list RP {
301                 $$= npval2(PV_IF, &@1, &@5);
302                 $$->u1.str = $4; }
303         ;
304
305 random_head : KW_RANDOM LP { reset_parencount(parseio->scanner); } word_list RP {
306                 $$ = npval2(PV_RANDOM, &@1, &@5);
307                 $$->u1.str=$4;}
308         ;
309
310 iftime_head : KW_IFTIME LP word3_list COLON word3_list COLON word3_list
311                 BAR word3_list BAR word3_list BAR word3_list RP {
312                 $$ = npval2(PV_IFTIME, &@1, &@1);
313                 $$->u1.list = npval2(PV_WORD, &@3, &@7);
314                 asprintf(&($$->u1.list->u1.str), "%s:%s:%s", $3, $5, $7);
315                 free($3);
316                 free($5);
317                 free($7);
318                 $$->u1.list->next = nword($9, &@9);
319                 $$->u1.list->next->next = nword($11, &@11);
320                 $$->u1.list->next->next->next = nword($13, &@13);
321                 prev_word = 0;
322         }
323         | KW_IFTIME LP word BAR word3_list BAR word3_list BAR word3_list RP {
324                 $$ = npval2(PV_IFTIME, &@1, &@5); /* XXX @5 or greater ? */
325                 $$->u1.list = nword($3, &@3);
326                 $$->u1.list->next = nword($5, &@5);
327                 $$->u1.list->next->next = nword($7, &@7);
328                 $$->u1.list->next->next->next = nword($9, &@9);
329                 prev_word = 0;
330         }
331
332         ;
333
334 /* word_list is a hack to fix a problem with context switching between bison and flex;
335    by the time you register a new context with flex, you've already got a look-ahead token
336    from the old context, with no way to put it back and start afresh. So, we kludge this
337    and merge the words back together. */
338
339 word_list : word { $$ = $1;}
340         | word word {
341                 asprintf(&($$), "%s%s", $1, $2);
342                 free($1);
343                 free($2);
344                 prev_word = $$;}
345         ;
346
347 word3_list : word { $$ = $1;}
348         | word word {
349                 asprintf(&($$), "%s%s", $1, $2);
350                 free($1);
351                 free($2);
352                 prev_word = $$;}
353         | word word word {
354                 asprintf(&($$), "%s%s%s", $1, $2, $3);
355                 free($1);
356                 free($2);
357                 free($3);
358                 prev_word=$$;}
359         ;
360
361 goto_word : word { $$ = $1;}
362         | word word {
363                 asprintf(&($$), "%s%s", $1, $2);
364                 free($1);
365                 free($2);}
366         | word COLON word {
367                 asprintf(&($$), "%s:%s", $1, $3);
368                 free($1);
369                 free($3);}
370         ;
371
372 switch_head : KW_SWITCH LP { reset_parencount(parseio->scanner); } word RP  LC {
373                 $$ = npval2(PV_SWITCH, &@1, &@6);
374                 $$->u1.str = $4; }
375         ;
376
377 /*
378  * Definition of a statememt in our language
379  */
380 statement : LC statements RC {
381                 $$ = npval2(PV_STATEMENTBLOCK, &@1, &@3);
382                 $$->u1.list = $2; }
383         | word EQ {reset_semicount(parseio->scanner);} word SEMI {
384                 $$ = npval2(PV_VARDEC, &@1, &@5);
385                 $$->u1.str = $1;
386                 $$->u2.val = $4; }
387         | KW_GOTO target SEMI {
388                 $$ = npval2(PV_GOTO, &@1, &@3);
389                 $$->u1.list = $2;}
390         | KW_JUMP jumptarget SEMI {
391                 $$ = npval2(PV_GOTO, &@1, &@3);
392                 $$->u1.list = $2;}
393         | word COLON {
394                 $$ = npval2(PV_LABEL, &@1, &@2);
395                 $$->u1.str = $1; }
396         | KW_FOR LP {reset_semicount(parseio->scanner);} word SEMI
397                         {reset_semicount(parseio->scanner);} word SEMI
398                         {reset_parencount(parseio->scanner);} word RP statement {
399                 $$ = npval2(PV_FOR, &@1, &@12);
400                 $$->u1.for_init = $4;
401                 $$->u2.for_test=$7;
402                 $$->u3.for_inc = $10;
403                 $$->u4.for_statements = $12;}
404         | KW_WHILE LP {reset_parencount(parseio->scanner);} word RP statement {
405                 $$ = npval2(PV_WHILE, &@1, &@6);
406                 $$->u1.str = $4;
407                 $$->u2.statements = $6; }
408         | switch_head RC /* empty list OK */ {
409                 $$ = update_last($1, &@2); }
410         | switch_head case_statements RC {
411                 $$ = update_last($1, &@3);
412                 $$->u2.statements = $2;}
413         | AMPER macro_call SEMI {
414                 $$ = update_last($2, &@2); }
415         | application_call SEMI {
416                 $$ = update_last($1, &@2); }
417         | word SEMI {
418                 $$= npval2(PV_APPLICATION_CALL, &@1, &@2);
419                 $$->u1.str = $1;}
420         | application_call EQ {reset_semicount(parseio->scanner);} word SEMI {
421                 char *bufx;
422                 int tot=0;
423                 pval *pptr;
424                 $$ = npval2(PV_VARDEC, &@1, &@5);
425                 $$->u2.val=$4;
426                 /* rebuild the original string-- this is not an app call, it's an unwrapped vardec, with a func call on the LHS */
427                 /* string to big to fit in the buffer? */
428                 tot+=strlen($1->u1.str);
429                 for(pptr=$1->u2.arglist;pptr;pptr=pptr->next) {
430                         tot+=strlen(pptr->u1.str);
431                         tot++; /* for a sep like a comma */
432                 }
433                 tot+=4; /* for safety */
434                 bufx = calloc(1, tot);
435                 strcpy(bufx,$1->u1.str);
436                 strcat(bufx,"(");
437                 /* XXX need to advance the pointer or the loop is very inefficient */
438                 for (pptr=$1->u2.arglist;pptr;pptr=pptr->next) {
439                         if ( pptr != $1->u2.arglist )
440                                 strcat(bufx,",");
441                         strcat(bufx,pptr->u1.str);
442                 }
443                 strcat(bufx,")");
444 #ifdef AAL_ARGCHECK
445                 if ( !ael_is_funcname($1->u1.str) )
446                         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",
447                                 my_file, @1.first_line, @1.first_column, @1.last_column, $1->u1.str);
448 #endif
449                 $$->u1.str = bufx;
450                 destroy_pval($1); /* the app call it is not, get rid of that chain */
451                 prev_word = 0;
452         }
453         | KW_BREAK SEMI { $$ = npval2(PV_BREAK, &@1, &@2); }
454         | KW_RETURN SEMI { $$ = npval2(PV_RETURN, &@1, &@2); }
455         | KW_CONTINUE SEMI { $$ = npval2(PV_CONTINUE, &@1, &@2); }
456         | random_head statement opt_else {
457                 $$ = update_last($1, &@2); /* XXX probably @3... */
458                 $$->u2.statements = $2;
459                 $$->u3.else_statements = $3;}
460         | if_head statement opt_else {
461                 $$ = update_last($1, &@2); /* XXX probably @3... */
462                 $$->u2.statements = $2;
463                 $$->u3.else_statements = $3;}
464         | iftime_head statement opt_else {
465                 $$ = update_last($1, &@2); /* XXX probably @3... */
466                 $$->u2.statements = $2;
467                 $$->u3.else_statements = $3;}
468         | SEMI { $$=0; }
469         ;
470
471 opt_else : KW_ELSE statement { $$ = $2; }
472         | { $$ = NULL ; }
473
474 /* XXX unused */
475 bar_or_comma: BAR | COMMA ;
476
477 target : goto_word { $$ = nword($1, &@1); }
478         | goto_word BAR goto_word {
479                 $$ = nword($1, &@1);
480                 $$->next = nword($3, &@3); }
481         | goto_word COMMA goto_word {
482                 $$ = nword($1, &@1);
483                 $$->next = nword($3, &@3); }
484         | goto_word BAR goto_word BAR goto_word {
485                 $$ = nword($1, &@1);
486                 $$->next = nword($3, &@3);
487                 $$->next->next = nword($5, &@5); }
488         | goto_word COMMA goto_word COMMA goto_word {
489                 $$ = nword($1, &@1);
490                 $$->next = nword($3, &@3);
491                 $$->next->next = nword($5, &@5); }
492         | KW_DEFAULT BAR goto_word BAR goto_word {
493                 $$ = nword(strdup("default"), &@1);
494                 $$->next = nword($3, &@3);
495                 $$->next->next = nword($5, &@5); }
496         | KW_DEFAULT COMMA goto_word COMMA goto_word {
497                 $$ = nword(strdup("default"), &@1);
498                 $$->next = nword($3, &@3);
499                 $$->next->next = nword($5, &@5); }
500         ;
501
502 /* XXX please document the form of jumptarget */
503 jumptarget : goto_word {
504                 $$ = nword($1, &@1);
505                 $$->next = nword(strdup("1"), &@1); }  /*  jump extension[,priority][@context] */
506         | goto_word COMMA goto_word {
507                 $$ = nword($1, &@1);
508                 $$->next = nword($3, &@3); }
509         | goto_word COMMA word AT word {        /* XXX they are stored in a different order */
510                 $$ = nword($5, &@5);
511                 $$->next = nword($1, &@1);
512                 $$->next->next = nword($3, &@3); }
513         | goto_word AT goto_word {
514                 $$ = nword($3, &@3);
515                 $$->next = nword($1, &@1);
516                 $$->next->next = nword(strdup("1"), &@3); }
517         | goto_word COMMA word AT KW_DEFAULT {
518                 $$ = nword(strdup("default"), &@1);
519                 $$->next = nword($1, &@1);
520                 $$->next->next = nword($3, &@3); }
521         | goto_word AT KW_DEFAULT {
522                 $$ = nword(strdup("default"), &@1);
523                 $$->next = nword($1, &@3);
524                 $$->next->next = nword( strdup("1"), &@3); }
525         ;
526
527 macro_call : word LP {reset_argcount(parseio->scanner);} eval_arglist RP {
528                 /* XXX original code had @2 but i think we need @5 */
529                 $$ = npval2(PV_MACRO_CALL, &@1, &@5);
530                 $$->u1.str = $1;
531                 $$->u2.arglist = $4;}
532         | word LP RP {
533                 $$= npval2(PV_MACRO_CALL, &@1, &@3);
534                 $$->u1.str = $1; }
535         ;
536
537 /* XXX application_call_head must be revised. Having 'word LP { ...'
538  * just as above should work fine, however it gives a different result.
539  */
540 application_call_head: word LP {reset_argcount(parseio->scanner);} {
541                 if (strcasecmp($1,"goto") == 0) {
542                         $$ = npval2(PV_GOTO, &@1, &@2);
543                         free($1); /* won't be using this */
544                         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 );
545                 } else {
546                         $$= npval2(PV_APPLICATION_CALL, &@1, &@2);
547                         $$->u1.str = $1;
548                 } }
549         ;
550
551 application_call : application_call_head eval_arglist RP {
552                 $$ = update_last($1, &@3);
553                 if( $$->type == PV_GOTO )
554                         $$->u1.list = $2;
555                 else
556                         $$->u2.arglist = $2;
557         }
558         | application_call_head RP { $$ = update_last($1, &@2); }
559         ;
560
561 opt_word : word { $$ = $1 }
562         | { $$ = strdup(""); }
563         ;
564
565 eval_arglist :  word_list { $$ = nword($1, &@1); }
566         | /*nothing! */   {
567                 $$= npval(PV_WORD,0/*@1.first_line*/,0/*@1.last_line*/,0/* @1.first_column*/, 0/*@1.last_column*/);
568                 $$->u1.str = strdup(""); }
569         | eval_arglist COMMA  opt_word { $$ = linku1($1, nword($3, &@3)); }
570         ;
571
572 case_statements: case_statement {$$=$1;}
573         | case_statements case_statement { $$ = linku1($1, $2); }
574         ;
575
576 case_statement: KW_CASE word COLON statements {
577                 $$ = npval2(PV_CASE, &@1, &@3); /* XXX 3 or 4 ? */
578                 $$->u1.str = $2;
579                 $$->u2.statements = $4;}
580         | KW_DEFAULT COLON statements {
581                 $$ = npval2(PV_DEFAULT, &@1, &@3);
582                 $$->u1.str = NULL;
583                 $$->u2.statements = $3;}
584         | KW_PATTERN word COLON statements {
585                 $$ = npval2(PV_PATTERN, &@1, &@4); /* XXX@3 or @4 ? */
586                 $$->u1.str = $2;
587                 $$->u2.statements = $4;}
588         | KW_CASE word COLON {
589                 $$ = npval2(PV_CASE, &@1, &@3);
590                 $$->u1.str = $2;}
591         | KW_DEFAULT COLON {
592                 $$ = npval2(PV_DEFAULT, &@1, &@2);
593                 $$->u1.str = NULL;}
594         | KW_PATTERN word COLON  {
595                 $$ = npval2(PV_PATTERN, &@1, &@3);
596                 $$->u1.str = $2;}
597         ;
598
599 macro_statements: macro_statement {$$ = $1;}
600         | macro_statements macro_statement { $$ = linku1($1, $2); }
601         ;
602
603 macro_statement : statement {$$=$1;}
604         | KW_CATCH word LC statements RC {
605                 $$ = npval2(PV_CATCH, &@1, &@5);
606                 $$->u1.str = $2;
607                 $$->u2.statements = $4;}
608         ;
609
610 switches : KW_SWITCHES switchlist_block {
611                 $$ = npval2(PV_SWITCHES, &@1, &@2);
612                 $$->u1.list = $2; }
613         ;
614
615 eswitches : KW_ESWITCHES switchlist_block {
616                 $$ = npval2(PV_ESWITCHES, &@1, &@2);
617                 $$->u1.list = $2; }
618         ;
619
620 switchlist_block : LC switchlist RC { $$ = $2; }
621         | LC RC { $$ = NULL; }
622         ;
623
624 switchlist : word SEMI { $$ = nword($1, &@1); }
625         | switchlist word SEMI { $$ = linku1($1, nword($2, &@2)); }
626         | switchlist error {$$=$1;}
627         ;
628
629 includeslist : includedname SEMI { $$ = nword($1, &@1); }
630         | includedname BAR word3_list COLON word3_list COLON word3_list
631                         BAR word3_list BAR word3_list BAR word3_list SEMI {
632                 $$ = nword($1, &@1);
633                 $$->u2.arglist = npval2(PV_WORD, &@3, &@7);
634                 asprintf( &($$->u2.arglist->u1.str), "%s:%s:%s", $3, $5, $7);
635                 free($3);
636                 free($5);
637                 free($7);
638                 $$->u2.arglist->next = nword($9, &@9);
639                 $$->u2.arglist->next->next = nword($11, &@11);
640                 $$->u2.arglist->next->next->next = nword($13, &@13);
641                 prev_word=0;
642         }
643         | includedname BAR word BAR word3_list BAR word3_list BAR word3_list SEMI {
644                 $$ = nword($1, &@1);
645                 $$->u2.arglist = nword($3, &@3);
646                 $$->u2.arglist->next = nword($5, &@5);
647                 $$->u2.arglist->next->next = nword($7, &@7);
648                 $$->u2.arglist->next->next->next = nword($9, &@9);
649                 prev_word=0;
650         }
651         | includeslist includedname SEMI { $$ = linku1($1, nword($2, &@2)); }
652         | includeslist includedname BAR word3_list COLON word3_list COLON word3_list
653                         BAR word3_list BAR word3_list BAR word3_list SEMI {
654                 pval *z = nword($2, &@2);
655                 $$ = linku1($1, z);
656                 z->u2.arglist = npval2(PV_WORD, &@4, &@8);
657                 asprintf( &($$->u2.arglist->u1.str), "%s:%s:%s", $4, $6, $8);
658                 free($4);
659                 free($6);
660                 free($8);
661                 z->u2.arglist->next = nword($10, &@10);
662                 z->u2.arglist->next->next = nword($12, &@12);
663                 z->u2.arglist->next->next->next = nword($14, &@14);
664                 prev_word=0;
665         }
666         | includeslist includedname BAR word BAR word3_list BAR word3_list BAR word3_list SEMI {
667                 pval *z = npval2(PV_WORD, &@2, &@3);
668                 $$ = linku1($1, z);
669                 $$->u2.arglist->u1.str = $4;                    /* XXX maybe too early ? */
670                 z->u1.str = $2;
671                 z->u2.arglist = npval2(PV_WORD, &@4, &@4);      /* XXX is this correct ? */
672                 z->u2.arglist->next = nword($6, &@6);
673                 z->u2.arglist->next->next = nword($8, &@8);
674                 z->u2.arglist->next->next->next = nword($10, &@10);
675                 prev_word=0;
676         }
677         | includeslist error {$$=$1;}
678         ;
679
680 includedname : word { $$ = $1;}
681         | KW_DEFAULT {$$=strdup("default");}
682         ;
683
684 includes : KW_INCLUDES LC includeslist RC {
685                 $$ = npval2(PV_INCLUDES, &@1, &@4);
686                 $$->u1.list = $3;}
687         | KW_INCLUDES LC RC {
688                 $$ = npval2(PV_INCLUDES, &@1, &@3);}
689         ;
690
691
692 %%
693
694 static char *token_equivs1[] =
695 {
696         "AMPER",
697         "AT",
698         "BAR",
699         "COLON",
700         "COMMA",
701         "EQ",
702         "EXTENMARK",
703         "KW_BREAK",
704         "KW_CASE",
705         "KW_CATCH",
706         "KW_CONTEXT",
707         "KW_CONTINUE",
708         "KW_DEFAULT",
709         "KW_ELSE",
710         "KW_ESWITCHES",
711         "KW_FOR",
712         "KW_GLOBALS",
713         "KW_GOTO",
714         "KW_HINT",
715         "KW_IFTIME",
716         "KW_IF",
717         "KW_IGNOREPAT",
718         "KW_INCLUDES"
719         "KW_JUMP",
720         "KW_MACRO",
721         "KW_PATTERN",
722         "KW_REGEXTEN",
723         "KW_RETURN",
724         "KW_SWITCHES",
725         "KW_SWITCH",
726         "KW_WHILE",
727         "LC",
728         "LP",
729         "RC",
730         "RP",
731         "SEMI",
732 };
733
734 static char *token_equivs2[] =
735 {
736         "&",
737         "@",
738         "|",
739         ":",
740         ",",
741         "=",
742         "=>",
743         "break",
744         "case",
745         "catch",
746         "context",
747         "continue",
748         "default",
749         "else",
750         "eswitches",
751         "for",
752         "globals",
753         "goto",
754         "hint",
755         "ifTime",
756         "if",
757         "ignorepat",
758         "includes"
759         "jump",
760         "macro",
761         "pattern",
762         "regexten",
763         "return",
764         "switches",
765         "switch",
766         "while",
767         "{",
768         "(",
769         "}",
770         ")",
771         ";",
772 };
773
774
775 static char *ael_token_subst(char *mess)
776 {
777         /* calc a length, malloc, fill, and return; yyerror had better free it! */
778         int len=0,i;
779         char *p;
780         char *res, *s,*t;
781         int token_equivs_entries = sizeof(token_equivs1)/sizeof(char*);
782
783         for (p=mess; *p; p++) {
784                 for (i=0; i<token_equivs_entries; i++) {
785                         if ( strncmp(p,token_equivs1[i],strlen(token_equivs1[i])) == 0 )
786                         {
787                                 len+=strlen(token_equivs2[i])+2;
788                                 p += strlen(token_equivs1[i])-1;
789                                 break;
790                         }
791                 }
792                 len++;
793         }
794         res = calloc(1, len+1);
795         res[0] = 0;
796         s = res;
797         for (p=mess; *p;) {
798                 int found = 0;
799                 for (i=0; i<token_equivs_entries; i++) {
800                         if ( strncmp(p,token_equivs1[i],strlen(token_equivs1[i])) == 0 ) {
801                                 *s++ = '\'';
802                                 for (t=token_equivs2[i]; *t;) {
803                                         *s++ = *t++;
804                                 }
805                                 *s++ = '\'';
806                                 p += strlen(token_equivs1[i]);
807                                 found = 1;
808                                 break;
809                         }
810                 }
811                 if( !found )
812                         *s++ = *p++;
813         }
814         *s++ = 0;
815         return res;
816 }
817
818 void yyerror(YYLTYPE *locp, struct parse_io *parseio,  char const *s)
819 {
820         char *s2 = ael_token_subst((char *)s);
821         if (locp->first_line == locp->last_line) {
822                 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);
823         } else {
824                 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);
825         }
826         free(s2);
827         parseio->syntax_error_count++;
828 }
829
830 static struct pval *npval(pvaltype type, int first_line, int last_line,
831         int first_column, int last_column)
832 {
833         pval *z = calloc(1, sizeof(struct pval));
834         z->type = type;
835         z->startline = first_line;
836         z->endline = last_line;
837         z->startcol = first_column;
838         z->endcol = last_column;
839         z->filename = strdup(my_file);
840         return z;
841 }
842
843 static struct pval *npval2(pvaltype type, YYLTYPE *first, YYLTYPE *last)
844 {
845         return npval(type, first->first_line, last->last_line,
846                         first->first_column, last->last_column);
847 }
848
849 static struct pval *update_last(pval *obj, YYLTYPE *last)
850 {
851         obj->endline = last->last_line;
852         obj->endcol = last->last_column;
853         return obj;
854 }
855
856 /* frontend for npval to create a PV_WORD string from the given token */
857 static pval *nword(char *string, YYLTYPE *pos)
858 {
859         pval *p = npval2(PV_WORD, pos, pos);
860         if (p)
861                 p->u1.str = string;
862         return p;
863 }
864
865 /* append second element to the list in the first one */
866 static pval * linku1(pval *head, pval *tail)
867 {
868         if (!head)
869                 return tail;
870         if (tail) {
871                 if (!head->next) {
872                         head->next = tail;
873                 } else {
874                         head->u1_last->next = tail;
875                 }
876                 head->u1_last = tail;
877         }
878         return head;
879 }