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