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