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