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