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