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