281b5f8323f839eaf48c2d26dfe7f4ef80506ca4
[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 *npval(pvaltype type, int first_line, int last_line,
31         int first_column, int last_column);
32 static void linku1(pval *head, pval *tail);
33
34 void reset_parencount(yyscan_t yyscanner);
35 void reset_semicount(yyscan_t yyscanner);
36 void reset_argcount(yyscan_t yyscanner );
37
38 #define YYLEX_PARAM ((struct parse_io *)parseio)->scanner
39 #define YYERROR_VERBOSE 1
40
41 extern char *my_file;
42 #ifdef AAL_ARGCHECK
43 int ael_is_funcname(char *name);
44 #endif
45 static char *ael_token_subst(char *mess);
46
47 %}
48
49
50 %union {
51         char *str;
52         struct pval *pval;
53 }
54
55 %{
56         /* declaring these AFTER the union makes things a lot simpler! */
57 void yyerror(YYLTYPE *locp, struct parse_io *parseio, char const *s);
58 int ael_yylex (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , void * yyscanner);
59
60 %}
61
62
63 %token KW_CONTEXT LC RC LP RP SEMI EQ COMMA COLON AMPER BAR AT
64 %token KW_MACRO KW_GLOBALS KW_IGNOREPAT KW_SWITCH KW_IF KW_IFTIME KW_ELSE KW_RANDOM KW_ABSTRACT
65 %token EXTENMARK KW_GOTO KW_JUMP KW_RETURN KW_BREAK KW_CONTINUE KW_REGEXTEN KW_HINT
66 %token KW_FOR KW_WHILE KW_CASE KW_PATTERN KW_DEFAULT KW_CATCH KW_SWITCHES KW_ESWITCHES
67 %token KW_INCLUDES
68
69 %token <str> word
70
71 %type <pval>includes
72 %type <pval>includeslist
73 %type <pval>switchlist
74 %type <pval>eswitches
75 %type <pval>switches
76 %type <pval>macro_statement
77 %type <pval>macro_statements
78 %type <pval>case_statement
79 %type <pval>case_statements
80 %type <pval>eval_arglist
81 %type <pval>application_call
82 %type <pval>application_call_head
83 %type <pval>macro_call
84 %type <pval>target jumptarget
85 %type <pval>statement
86 %type <pval>switch_head
87 %type <str>word_list goto_word
88 %type <str>word3_list
89 %type <str>includedname
90 %type <pval>if_head
91 %type <pval>random_head
92 %type <pval>iftime_head
93 %type <pval>statements
94 %type <pval>extension
95 %type <pval>ignorepat
96 %type <pval>element
97 %type <pval>elements
98 %type <pval>arglist
99 %type <pval>global_statement
100 %type <pval>global_statements
101 %type <pval>globals
102 %type <pval>macro
103 %type <pval>context
104 %type <pval>object
105 %type <pval>objects
106 %type <pval>file
107
108 /* OPTIONS */
109 %locations
110 %pure-parser
111 %name-prefix="ael_yy"
112 /* the following option does two things:
113     it adds the locp arg to the yyerror
114     and it adds the NULL to the yyerrr arg list, and calls yyerror with NULL for that arg.
115     You can't get the locp arg without the NULL arg, don't ask me why. */
116 %parse-param {struct parse_io *parseio}
117 /* 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
118    the default action to shift will attach the else to the preceeding if. */
119 %expect 5
120 %error-verbose
121 %destructor { if (yymsg[0] != 'C') {destroy_pval($$); prev_word=0;} else {printf("Cleanup destructor called for pvals\n");} } includes includeslist switchlist eswitches switches macro_statement macro_statements case_statement case_statements eval_arglist application_call
122                                 application_call_head macro_call target jumptarget statement switch_head if_head random_head iftime_head statements extension ignorepat element
123                                 elements arglist global_statement global_statements globals macro context object objects
124 %destructor { free($$);}  word word_list goto_word word3_list includedname
125
126
127 %%
128
129 file : objects  { $$ = parseio->pval = $1; }
130         ;
131
132 objects : object {$$=$1;}
133         | objects object
134                 {
135                         if ( $1 && $2 ) {
136                                 $$=$1;
137                                 linku1($$,$2);
138                         } else if ( $1 ) {
139                                 $$=$1;
140                         } else if ( $2 ) {
141                                 $$=$2;
142                         }
143                 }
144         | objects error {$$=$1;}
145         ;
146
147 object : context {$$=$1;}
148         | macro {$$=$1;}
149         | globals {$$=$1;}
150         | SEMI  {$$=0;/* allow older docs to be read */}
151         ;
152
153 context : KW_CONTEXT word LC elements RC {
154                 $$ = npval(PV_CONTEXT, @1.first_line, @5.last_line,
155                         @1.first_column, @5.last_column);
156                 $$->u1.str = $2;
157                 $$->u2.statements = $4; }
158         | KW_CONTEXT word LC RC /* empty context OK */ {
159                 $$ = npval(PV_CONTEXT, @1.first_line, @4.last_line,
160                         @1.first_column, @4.last_column);
161                 $$->u1.str = $2; }
162         | KW_CONTEXT KW_DEFAULT LC elements RC {
163                 $$ = npval(PV_CONTEXT, @1.first_line, @5.last_line,
164                         @1.first_column, @5.last_column);
165                 $$->u1.str = strdup("default");
166                 $$->u2.statements = $4; }
167         | KW_CONTEXT KW_DEFAULT LC RC /* empty context OK */ {
168                 $$ = npval(PV_CONTEXT, @1.first_line, @4.last_line,
169                         @1.first_column, @4.last_column);
170                 $$->u1.str = strdup("default"); }
171         | KW_ABSTRACT KW_CONTEXT word LC elements RC {
172                 $$ = npval(PV_CONTEXT, @1.first_line, @6.last_line,
173                         @1.first_column, @6.last_column);
174                 $$->u1.str = $3;
175                 $$->u2.statements = $5;
176                 $$->u3.abstract = 1; }
177         | KW_ABSTRACT KW_CONTEXT word LC RC /* empty context OK */ {
178                 $$ = npval(PV_CONTEXT, @1.first_line, @5.last_line,
179                         @1.first_column, @5.last_column);
180                 $$->u1.str = $3;
181                 $$->u3.abstract = 1; }
182         | KW_ABSTRACT KW_CONTEXT KW_DEFAULT LC elements RC  {
183                 $$ = npval(PV_CONTEXT, @1.first_line, @6.last_line,
184                         @1.first_column, @6.last_column);
185                 $$->u1.str = strdup("default");
186                 $$->u2.statements = $5;
187                 $$->u3.abstract = 1; }
188         | KW_ABSTRACT KW_CONTEXT KW_DEFAULT LC RC /* empty context OK */ {
189                 $$ = npval(PV_CONTEXT, @1.first_line, @5.last_line,
190                         @1.first_column, @5.last_column);
191                 $$->u1.str = strdup("default");
192                 $$->u3.abstract = 1; }
193         ;
194
195 macro : KW_MACRO word LP arglist RP LC macro_statements RC {
196                 $$=npval(PV_MACRO,@1.first_line,@8.last_line, @1.first_column, @8.last_column);
197                 $$->u1.str = $2; $$->u2.arglist = $4; $$->u3.macro_statements = $7; }
198         | KW_MACRO word LP arglist RP LC  RC {
199                 $$=npval(PV_MACRO,@1.first_line,@7.last_line, @1.first_column, @7.last_column);
200                 $$->u1.str = $2; $$->u2.arglist = $4; }
201         | KW_MACRO word LP RP LC macro_statements RC {
202                 $$=npval(PV_MACRO,@1.first_line,@7.last_line, @1.first_column, @7.last_column);
203                 $$->u1.str = $2; $$->u3.macro_statements = $6; }
204         | KW_MACRO word LP RP LC  RC {
205                 $$=npval(PV_MACRO,@1.first_line,@6.last_line, @1.first_column, @6.last_column);
206                 $$->u1.str = $2; /* pretty empty! */ }
207         ;
208
209 globals : KW_GLOBALS LC global_statements RC {
210                 $$=npval(PV_GLOBALS,@1.first_line,@4.last_line, @1.first_column, @4.last_column);
211                 $$->u1.statements = $3;}
212         | KW_GLOBALS LC RC /* empty global is OK */ {
213                 $$=npval(PV_GLOBALS,@1.first_line,@3.last_line, @1.first_column, @3.last_column);
214                 /* and that's all */ }
215         ;
216
217 global_statements : global_statement {$$=$1;}
218         | global_statements global_statement {$$=$1; linku1($$,$2);}
219         | global_statements error {$$=$1;}
220         ;
221
222 global_statement : word EQ { reset_semicount(parseio->scanner); }  word SEMI {
223                 $$=npval(PV_VARDEC,@1.first_line,@5.last_line, @1.first_column, @5.last_column);
224                 $$->u1.str = $1;
225                 $$->u2.val = $4; }
226         ;
227
228 arglist : word {
229                 $$= npval(PV_WORD,@1.first_line,@1.last_line, @1.first_column, @1.last_column);
230                 $$->u1.str = $1; }
231         | arglist COMMA word {
232                 pval *z = npval(PV_WORD,@1.first_line,@3.last_line, @1.first_column, @3.last_column);
233                 z->u1.str = $3;
234                 $$=$1;
235                 linku1($$,z); }
236         | arglist error {$$=$1;}
237         ;
238
239 elements : element { $$=$1;}
240         | error {$$=0;}
241         | elements element { if ( $1 && $2 ) {$$=$1; linku1($$,$2);}
242                                 else if ( $1 ) {$$=$1;}
243                                 else if ( $2 ) {$$=$2;} }
244         | elements error   { $$=$1;}
245         ;
246
247 element : extension {$$=$1;}
248         | includes {$$=$1;}
249         | switches {$$=$1;}
250         | eswitches {$$=$1;}
251         | ignorepat {$$=$1;}
252         | word EQ { reset_semicount(parseio->scanner); } word SEMI {
253                 $$=npval(PV_VARDEC,@1.first_line,@5.last_line, @1.first_column, @5.last_column);
254                 $$->u1.str = $1;
255                 $$->u2.val = $4; }
256         | word error {free($1); $$=0;}
257         | SEMI  {$$=0;/* allow older docs to be read */}
258         ;
259
260 ignorepat : KW_IGNOREPAT EXTENMARK word SEMI {
261                 $$=npval(PV_IGNOREPAT,@1.first_line,@4.last_line, @1.first_column, @4.last_column);
262                 $$->u1.str = $3;}
263         ;
264
265 extension : word EXTENMARK statement {
266                 $$ = npval(PV_EXTENSION,@1.first_line,@3.last_line, @1.first_column, @3.last_column);
267                 $$->u1.str = $1;
268                 $$->u2.statements = $3; }
269         | KW_REGEXTEN word EXTENMARK statement {
270                 $$ = npval(PV_EXTENSION,@1.first_line,@4.last_line, @1.first_column, @4.last_column);
271                 $$->u1.str = $2;
272                 $$->u2.statements = $4;
273                 $$->u4.regexten=1;}
274         | KW_HINT LP word3_list RP word EXTENMARK statement {
275                 $$ = npval(PV_EXTENSION,@1.first_line,@7.last_line, @1.first_column, @7.last_column);
276                 $$->u1.str = $5;
277                 $$->u2.statements = $7;
278                 $$->u3.hints = $3;}
279         | KW_REGEXTEN KW_HINT LP word3_list RP word EXTENMARK statement {
280                 $$ = npval(PV_EXTENSION,@1.first_line,@8.last_line, @1.first_column, @8.last_column);
281                 $$->u1.str = $6;
282                 $$->u2.statements = $8;
283                 $$->u4.regexten=1;
284                 $$->u3.hints = $4;}
285
286         ;
287
288 statements : statement {$$=$1;}
289         | statements statement {if ( $1 && $2 ) {$$=$1; linku1($$,$2);}
290                                                  else if ( $1 ) {$$=$1;}
291                                                  else if ( $2 ) {$$=$2;} }
292         | statements error {$$=$1;}
293         ;
294
295 if_head : KW_IF LP { reset_parencount(parseio->scanner); }  word_list RP {
296                 $$= npval(PV_IF,@1.first_line,@5.last_line, @1.first_column, @5.last_column);
297                 $$->u1.str = $4; }
298         ;
299
300 random_head : KW_RANDOM LP { reset_parencount(parseio->scanner); } word_list RP {
301                 $$= npval(PV_RANDOM,@1.first_line,@5.last_line, @1.first_column, @5.last_column);
302                 $$->u1.str=$4;}
303         ;
304
305 iftime_head : KW_IFTIME LP word3_list COLON word3_list COLON word3_list
306                 BAR word3_list BAR word3_list BAR word3_list RP {
307                 $$= npval(PV_IFTIME,@1.first_line,@5.last_line, @1.first_column, @5.last_column);
308                 $$->u1.list = npval(PV_WORD,@3.first_line,@3.last_line, @3.first_column, @3.last_column);
309                 asprintf(&($$->u1.list->u1.str), "%s:%s:%s", $3, $5, $7);
310                 free($3);
311                 free($5);
312                 free($7);
313                 $$->u1.list->next = npval(PV_WORD,@9.first_line,@9.last_line, @9.first_column, @9.last_column);
314                 $$->u1.list->next->u1.str = $9;
315                 $$->u1.list->next->next = npval(PV_WORD,@11.first_line,@11.last_line, @11.first_column, @11.last_column);
316                 $$->u1.list->next->next->u1.str = $11;
317                 $$->u1.list->next->next->next = npval(PV_WORD,@13.first_line,@13.last_line, @13.first_column, @13.last_column);
318                 $$->u1.list->next->next->next->u1.str = $13;
319                 prev_word = 0;
320         }
321         | KW_IFTIME LP word BAR word3_list BAR word3_list BAR word3_list RP {
322                 $$= npval(PV_IFTIME,@1.first_line,@5.last_line, @1.first_column, @5.last_column);
323                 $$->u1.list = npval(PV_WORD,@3.first_line,@3.last_line, @3.first_column, @3.last_column);
324                 $$->u1.list->u1.str = $3;
325                 $$->u1.list->next = npval(PV_WORD,@5.first_line,@5.last_line, @5.first_column, @5.last_column);
326                 $$->u1.list->next->u1.str = $5;
327                 $$->u1.list->next->next = npval(PV_WORD,@7.first_line,@7.last_line, @7.first_column, @7.last_column);
328                 $$->u1.list->next->next->u1.str = $7;
329                 $$->u1.list->next->next->next = npval(PV_WORD,@9.first_line,@9.last_line, @9.first_column, @9.last_column);
330                 $$->u1.list->next->next->next->u1.str = $9;
331                 prev_word = 0;
332         }
333
334         ;
335
336 /* word_list is a hack to fix a problem with context switching between bison and flex;
337    by the time you register a new context with flex, you've already got a look-ahead token
338    from the old context, with no way to put it back and start afresh. So, we kludge this
339    and merge the words back together. */
340
341 word_list : word { $$ = $1;}
342         | word word {
343                 asprintf(&($$), "%s%s", $1, $2);
344                 free($1);
345                 free($2);
346                 prev_word = $$;}
347         ;
348 word3_list : word { $$ = $1;}
349         | word word {
350                 asprintf(&($$), "%s%s", $1, $2);
351                 free($1);
352                 free($2);
353                 prev_word = $$;}
354         | word word word {
355                 asprintf(&($$), "%s%s%s", $1, $2, $3);
356                 free($1);
357                 free($2);
358                 free($3);
359                 prev_word=$$;}
360         ;
361
362 goto_word : word { $$ = $1;}
363         | word word {
364                 asprintf(&($$), "%s%s", $1, $2);
365                 free($1);
366                 free($2);}
367         | word COLON word {
368                 asprintf(&($$), "%s:%s", $1, $3);
369                 free($1);
370                 free($3);}
371         ;
372
373 switch_head : KW_SWITCH LP { reset_parencount(parseio->scanner); } word RP  LC {
374                 $$=npval(PV_SWITCH,@1.first_line,@6.last_line, @1.first_column, @6.last_column);
375                 $$->u1.str = $4; }
376         ;
377
378 statement : LC statements RC {
379                 $$=npval(PV_STATEMENTBLOCK,@1.first_line,@3.last_line, @1.first_column, @3.last_column);
380                 $$->u1.list = $2; }
381         | word EQ {reset_semicount(parseio->scanner);} word SEMI {
382                 $$=npval(PV_VARDEC,@1.first_line,@5.last_line, @1.first_column, @5.last_column);
383                 $$->u1.str = $1;
384                 $$->u2.val = $4; }
385         | KW_GOTO target SEMI {
386                 $$=npval(PV_GOTO,@1.first_line,@3.last_line, @1.first_column, @3.last_column);
387                 $$->u1.list = $2;}
388         | KW_JUMP jumptarget SEMI {
389                 $$=npval(PV_GOTO,@1.first_line,@3.last_line, @1.first_column, @3.last_column);
390                 $$->u1.list = $2;}
391         | word COLON {
392                 $$=npval(PV_LABEL,@1.first_line,@2.last_line, @1.first_column, @2.last_column);
393                 $$->u1.str = $1; }
394         | KW_FOR LP {reset_semicount(parseio->scanner);} word SEMI
395                         {reset_semicount(parseio->scanner);} word SEMI
396                         {reset_parencount(parseio->scanner);} word RP statement {
397                 $$=npval(PV_FOR,@1.first_line,@12.last_line, @1.first_column, @12.last_column);
398                 $$->u1.for_init = $4;
399                 $$->u2.for_test=$7;
400                 $$->u3.for_inc = $10;
401                 $$->u4.for_statements = $12;}
402         | KW_WHILE LP {reset_parencount(parseio->scanner);} word RP statement {
403                 $$=npval(PV_WHILE,@1.first_line,@6.last_line, @1.first_column, @6.last_column);
404                 $$->u1.str = $4;
405                 $$->u2.statements = $6; }
406         | switch_head RC /* empty list OK */ {
407                 $$=$1;
408                 $$->endline = @2.last_line;
409                 $$->endcol = @2.last_column;}
410         | switch_head case_statements RC {
411                 $$=$1;
412                 $$->u2.statements = $2;
413                 $$->endline = @3.last_line;
414                 $$->endcol = @3.last_column;}
415         | AMPER macro_call SEMI {
416                 $$ = $2;
417                 $$->endline = @2.last_line;
418                 $$->endcol = @2.last_column;}
419         | application_call SEMI {
420                 $$ = $1;
421                 $$->endline = @2.last_line;
422                 $$->endcol = @2.last_column;}
423         | word SEMI {
424                 $$= npval(PV_APPLICATION_CALL,@1.first_line,@2.last_line, @1.first_column, @2.last_column);
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                 $$ = npval(PV_VARDEC,@1.first_line,@5.last_line, @1.first_column, @5.last_column);
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 = (char *)malloc(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 {
460                 $$ = npval(PV_BREAK,@1.first_line,@2.last_line, @1.first_column, @2.last_column);}
461         | KW_RETURN SEMI {
462                 $$ = npval(PV_RETURN,@1.first_line,@2.last_line, @1.first_column, @2.last_column);}
463         | KW_CONTINUE SEMI {
464                 $$ = npval(PV_CONTINUE,@1.first_line,@2.last_line, @1.first_column, @2.last_column);}
465         | random_head statement {
466                 $$=$1;
467                 $$->u2.statements = $2;
468                 $$->endline = @2.last_line;
469                 $$->endcol = @2.last_column;}
470         | random_head statement KW_ELSE statement {
471                 $$=$1;
472                 $$->u2.statements = $2;
473                 $$->endline = @2.last_line;
474                 $$->endcol = @2.last_column;
475                 $$->u3.else_statements = $4;}
476         | if_head statement {
477                 $$=$1;
478                 $$->u2.statements = $2;
479                 $$->endline = @2.last_line;
480                 $$->endcol = @2.last_column;}
481         | if_head statement KW_ELSE statement {
482                 $$=$1;
483                 $$->u2.statements = $2;
484                 $$->endline = @2.last_line;
485                 $$->endcol = @2.last_column;
486                 $$->u3.else_statements = $4;}
487         | iftime_head statement {
488                 $$=$1;
489                 $$->u2.statements = $2;
490                 $$->endline = @2.last_line;
491                 $$->endcol = @2.last_column;}
492         | iftime_head statement KW_ELSE statement {
493                 $$=$1;
494                 $$->u2.statements = $2;
495                 $$->endline = @2.last_line;
496                 $$->endcol = @2.last_column;
497                 $$->u3.else_statements = $4;}
498         | SEMI { $$=0; }
499         ;
500
501 target : goto_word { $$ = npval(PV_WORD,@1.first_line,@1.last_line, @1.first_column, @1.last_column); $$->u1.str = $1;}
502         | goto_word BAR goto_word {
503                 $$=npval(PV_WORD,@1.first_line,@1.last_line, @1.first_column, @1.last_column);
504                 $$->u1.str = $1;
505                 $$->next = npval(PV_WORD,@3.first_line,@3.last_line, @3.first_column, @3.last_column);
506                 $$->next->u1.str = $3;}
507         | goto_word COMMA goto_word {
508                 $$=npval(PV_WORD,@1.first_line,@1.last_line, @1.first_column, @1.last_column);
509                 $$->u1.str = $1;
510                 $$->next = npval(PV_WORD,@3.first_line,@3.last_line, @3.first_column, @3.last_column);
511                 $$->next->u1.str = $3;}
512         | goto_word BAR goto_word BAR goto_word {$$=npval(PV_WORD,@1.first_line,@1.last_line, @1.first_column, @1.last_column);
513                 $$->u1.str = $1; $$->next = npval(PV_WORD,@3.first_line,@3.last_line, @3.first_column, @3.last_column);
514                 $$->next->u1.str = $3;
515                 $$->next->next = npval(PV_WORD,@5.first_line,@5.last_line, @5.first_column, @5.last_column);
516                 $$->next->next->u1.str = $5; }
517         | goto_word COMMA goto_word COMMA goto_word {$$=npval(PV_WORD,@1.first_line,@1.last_line, @1.first_column, @1.last_column);
518                 $$->u1.str = $1; $$->next = npval(PV_WORD,@3.first_line,@3.last_line, @3.first_column, @3.last_column);
519                 $$->next->u1.str = $3;
520                 $$->next->next = npval(PV_WORD,@5.first_line,@5.last_line, @5.first_column, @5.last_column);
521                 $$->next->next->u1.str = $5; }
522         | KW_DEFAULT BAR goto_word BAR goto_word {$$=npval(PV_WORD,@1.first_line,@1.last_line, @1.first_column, @1.last_column);
523                 $$->u1.str = strdup("default"); $$->next = npval(PV_WORD,@3.first_line,@3.last_line, @3.first_column, @3.last_column);
524                 $$->next->u1.str = $3;
525                 $$->next->next = npval(PV_WORD,@5.first_line,@5.last_line, @5.first_column, @5.last_column);
526                 $$->next->next->u1.str = $5; }
527         | KW_DEFAULT COMMA goto_word COMMA goto_word {$$=npval(PV_WORD,@1.first_line,@1.last_line, @1.first_column, @1.last_column);
528                 $$->u1.str = strdup("default"); $$->next = npval(PV_WORD,@3.first_line,@3.last_line, @3.first_column, @3.last_column);
529                 $$->next->u1.str = $3;
530                 $$->next->next = npval(PV_WORD,@5.first_line,@5.last_line, @5.first_column, @5.last_column);
531                 $$->next->next->u1.str = $5; }
532         ;
533
534 jumptarget : goto_word {$$=npval(PV_WORD,@1.first_line,@1.last_line, @1.first_column, @1.last_column);
535                 $$->u1.str = $1; $$->next = npval(PV_WORD,@1.first_line,@1.last_line, @1.first_column, @1.last_column);
536                 $$->next->u1.str = strdup("1");}  /*  jump extension[,priority][@context] */
537         | goto_word COMMA goto_word {$$=npval(PV_WORD,@1.first_line,@1.last_line, @1.first_column, @1.last_column);
538                 $$->u1.str = $1; $$->next = npval(PV_WORD,@3.first_line,@3.last_line, @3.first_column, @3.last_column);
539                 $$->next->u1.str = $3;}
540         | goto_word COMMA word AT word {$$=npval(PV_WORD,@1.first_line,@1.last_line, @1.first_column, @1.last_column);
541                 $$->u1.str = $5; $$->next = npval(PV_WORD,@3.first_line,@3.last_line, @3.first_column, @3.last_column);
542                 $$->next->u1.str = $1;
543                 $$->next->next = npval(PV_WORD,@5.first_line,@5.last_line, @5.first_column, @5.last_column);
544                 $$->next->next->u1.str = $3; }
545         | goto_word AT goto_word {$$=npval(PV_WORD,@1.first_line,@1.last_line, @1.first_column, @1.last_column);
546                 $$->u1.str = $3; $$->next = npval(PV_WORD,@3.first_line,@3.last_line, @3.first_column, @3.last_column);
547                 $$->next->u1.str = $1;
548                 $$->next->next = npval(PV_WORD,@3.first_line,@3.last_line, @3.first_column, @3.last_column);
549                 $$->next->next->u1.str = strdup("1"); }
550         | goto_word COMMA word AT KW_DEFAULT {$$=npval(PV_WORD,@1.first_line,@1.last_line, @1.first_column, @1.last_column);
551                 $$->u1.str = strdup("default"); $$->next = npval(PV_WORD,@3.first_line,@3.last_line, @3.first_column, @3.last_column);
552                 $$->next->u1.str = $1;
553                 $$->next->next = npval(PV_WORD,@5.first_line,@5.last_line, @5.first_column, @5.last_column);
554                 $$->next->next->u1.str = $3; }
555         | goto_word AT KW_DEFAULT {$$=npval(PV_WORD,@1.first_line,@1.last_line, @1.first_column, @1.last_column);
556                 $$->u1.str = strdup("default"); $$->next = npval(PV_WORD,@3.first_line,@3.last_line, @3.first_column, @3.last_column);
557                 $$->next->u1.str = $1;
558                 $$->next->next = npval(PV_WORD,@3.first_line,@3.last_line, @3.first_column, @3.last_column);
559                 $$->next->next->u1.str = strdup("1"); }
560         ;
561
562 macro_call : word LP {reset_argcount(parseio->scanner);} eval_arglist RP
563                 {$$= npval(PV_MACRO_CALL,@1.first_line,@2.last_line, @1.first_column, @2.last_column);
564                 $$->u1.str = $1; $$->u2.arglist = $4;}
565         | word LP RP {$$= npval(PV_MACRO_CALL,@1.first_line,@3.last_line, @1.first_column, @3.last_column); $$->u1.str = $1; }
566         ;
567
568 application_call_head: word {reset_argcount(parseio->scanner);} LP  {
569                 if (strcasecmp($1,"goto") == 0) {
570                         $$= npval(PV_GOTO,@1.first_line,@3.last_line, @1.first_column, @3.last_column);
571                         free($1); /* won't be using this */
572                         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 );
573                 } else
574                         $$= npval(PV_APPLICATION_CALL,@1.first_line,@3.last_line, @1.first_column, @3.last_column);
575                 $$->u1.str = $1; }
576         ;
577
578 application_call : application_call_head eval_arglist RP {$$ = $1;
579                 if( $$->type == PV_GOTO )
580                         $$->u1.list = $2;
581                 else
582                         $$->u2.arglist = $2;
583                 $$->endline = @3.last_line; $$->endcol = @3.last_column;}
584         | application_call_head RP {$$=$1;$$->endline = @2.last_line; $$->endcol = @2.last_column;}
585         ;
586
587 eval_arglist :  word_list { $$= npval(PV_WORD,@1.first_line,@1.last_line, @1.first_column, @1.last_column); $$->u1.str = $1;}
588         | /*nothing! */   { $$= npval(PV_WORD,0/*@1.first_line*/,0/*@1.last_line*/,0/* @1.first_column*/, 0/*@1.last_column*/); $$->u1.str = strdup(""); }
589         | eval_arglist COMMA  word { pval *z = npval(PV_WORD,@3.first_line,@3.last_line, @3.first_column, @3.last_column); $$ = $1; linku1($1,z); z->u1.str = $3;}
590         | eval_arglist COMMA { pval *z = npval(PV_WORD,@2.first_line,@2.last_line, @2.first_column, @2.last_column); $$ = $1; linku1($1,z); z->u1.str = strdup("");}
591         ;
592
593 case_statements: case_statement {$$=$1;}
594         | case_statements case_statement { if ( $1 && $2 ) {$$=$1; linku1($$,$2);}
595                                                  else if ( $1 ) {$$=$1;}
596                                                  else if ( $2 ) {$$=$2;} }
597         ;
598
599 case_statement: KW_CASE word COLON statements {$$ = npval(PV_CASE,@1.first_line,@3.last_line, @1.first_column, @3.last_column); $$->u1.str = $2; $$->u2.statements = $4;}
600         | KW_DEFAULT COLON statements {$$ = npval(PV_DEFAULT,@1.first_line,@3.last_line, @1.first_column, @3.last_column); $$->u1.str = 0; $$->u2.statements = $3;}
601         | KW_PATTERN word COLON statements {$$ = npval(PV_PATTERN,@1.first_line,@3.last_line, @1.first_column, @3.last_column); $$->u1.str = $2; $$->u2.statements = $4;}
602         | KW_CASE word COLON {$$ = npval(PV_CASE,@1.first_line,@3.last_line, @1.first_column, @3.last_column); $$->u1.str = $2;}
603         | KW_DEFAULT COLON {$$ = npval(PV_DEFAULT,@1.first_line,@2.last_line, @1.first_column, @2.last_column); $$->u1.str = 0;}
604         | KW_PATTERN word COLON  {$$ = npval(PV_PATTERN,@1.first_line,@3.last_line, @1.first_column, @3.last_column); $$->u1.str = $2;}
605         ;
606
607 macro_statements: macro_statement {$$ = $1;}
608         | macro_statements macro_statement { if ( $1 && $2 ) {$$=$1; linku1($$,$2);}
609                                                  else if ( $1 ) {$$=$1;}
610                                                  else if ( $2 ) {$$=$2;} }
611         ;
612
613 macro_statement : statement {$$=$1;}
614         | KW_CATCH word LC statements RC {$$=npval(PV_CATCH,@1.first_line,@5.last_line, @1.first_column, @5.last_column); $$->u1.str = $2; $$->u2.statements = $4;}
615         ;
616
617 switches : KW_SWITCHES LC switchlist RC {$$= npval(PV_SWITCHES,@1.first_line,@4.last_line, @1.first_column, @4.last_column); $$->u1.list = $3; }
618         | KW_SWITCHES LC RC /* empty switch list OK */ {$$= npval(PV_SWITCHES,@1.first_line,@3.last_line, @1.first_column, @3.last_column);}
619         ;
620
621 eswitches : KW_ESWITCHES LC switchlist RC {$$= npval(PV_ESWITCHES,@1.first_line,@4.last_line, @1.first_column, @4.last_column); $$->u1.list = $3; }
622         | KW_ESWITCHES LC  RC /* empty switch list OK */ {$$= npval(PV_ESWITCHES,@1.first_line,@3.last_line, @1.first_column, @3.last_column); } /* if there's nothing to declare, why include it? */
623         ;
624
625 switchlist : word SEMI {$$=npval(PV_WORD,@1.first_line,@2.last_line, @1.first_column, @2.last_column); $$->u1.str = $1;}
626         | switchlist word SEMI {pval *z = npval(PV_WORD,@2.first_line,@3.last_line, @2.first_column, @3.last_column); $$=$1; z->u1.str = $2; linku1($$,z); }
627         | switchlist error {$$=$1;}
628         ;
629
630 includeslist : includedname SEMI {$$=npval(PV_WORD,@1.first_line,@2.last_line, @1.first_column, @2.last_column); $$->u1.str = $1;}
631         | includedname BAR word3_list COLON word3_list COLON word3_list BAR word3_list BAR word3_list BAR word3_list SEMI {
632                 $$=npval(PV_WORD,@1.first_line,@2.last_line, @1.first_column, @2.last_column);
633                 $$->u1.str = $1;
634                 $$->u2.arglist = npval(PV_WORD,@3.first_line,@7.last_line, @3.first_column, @7.last_column);
635                 asprintf( &($$->u2.arglist->u1.str), "%s:%s:%s", $3, $5, $7);
636                 free($3);
637                 free($5);
638                 free($7);
639                 $$->u2.arglist->next = npval(PV_WORD,@9.first_line,@9.last_line, @9.first_column, @9.last_column);
640                 $$->u2.arglist->next->u1.str = $9;
641                 $$->u2.arglist->next->next = npval(PV_WORD,@11.first_line,@11.last_line, @11.first_column, @11.last_column);
642                 $$->u2.arglist->next->next->u1.str = $11;
643                 $$->u2.arglist->next->next->next = npval(PV_WORD,@13.first_line,@13.last_line, @13.first_column, @13.last_column);
644                 $$->u2.arglist->next->next->next->u1.str = $13;
645                 prev_word=0;
646         }
647         | includedname BAR word BAR word3_list BAR word3_list BAR word3_list SEMI {
648                 $$=npval(PV_WORD,@1.first_line,@2.last_line, @1.first_column, @2.last_column);
649                 $$->u1.str = $1;
650                 $$->u2.arglist = npval(PV_WORD,@3.first_line,@3.last_line, @3.first_column, @3.last_column);
651                 $$->u2.arglist->u1.str = $3;
652                 $$->u2.arglist->next = npval(PV_WORD,@5.first_line,@5.last_line, @5.first_column, @5.last_column);
653                 $$->u2.arglist->next->u1.str = $5;
654                 $$->u2.arglist->next->next = npval(PV_WORD,@7.first_line,@7.last_line, @7.first_column, @7.last_column);
655                 $$->u2.arglist->next->next->u1.str = $7;
656                 $$->u2.arglist->next->next->next = npval(PV_WORD,@9.first_line,@9.last_line, @9.first_column, @9.last_column);
657                 $$->u2.arglist->next->next->next->u1.str = $9;
658                 prev_word=0;
659         }
660         | includeslist includedname SEMI {pval *z = npval(PV_WORD,@2.first_line,@3.last_line, @2.first_column, @3.last_column); $$=$1; z->u1.str = $2; linku1($$,z); }
661         | includeslist includedname BAR word3_list COLON word3_list COLON word3_list BAR word3_list BAR word3_list BAR word3_list SEMI {pval *z = npval(PV_WORD,@2.first_line,@3.last_line, @2.first_column, @3.last_column);
662                 $$=$1; z->u1.str = $2; linku1($$,z);
663                 z->u2.arglist = npval(PV_WORD,@4.first_line,@4.last_line, @4.first_column, @4.last_column);
664                 asprintf( &($$->u2.arglist->u1.str), "%s:%s:%s", $4, $6, $8);
665                 free($4);
666                 free($6);
667                 free($8);
668                 z->u2.arglist->next = npval(PV_WORD,@10.first_line,@10.last_line, @10.first_column, @10.last_column);
669                 z->u2.arglist->next->u1.str = $10;
670                 z->u2.arglist->next->next = npval(PV_WORD,@12.first_line,@12.last_line, @12.first_column, @12.last_column);
671                 z->u2.arglist->next->next->u1.str = $12;
672                 z->u2.arglist->next->next->next = npval(PV_WORD,@14.first_line,@14.last_line, @14.first_column, @14.last_column);
673                 z->u2.arglist->next->next->next->u1.str = $14;
674                 prev_word=0;
675         }
676         | includeslist includedname BAR word BAR word3_list BAR word3_list BAR word3_list SEMI
677                 {pval *z = npval(PV_WORD,@2.first_line,@2.last_line, @2.first_column, @3.last_column);
678                 $$=$1; z->u1.str = $2; linku1($$,z);
679                 z->u2.arglist = npval(PV_WORD,@4.first_line,@4.last_line, @4.first_column, @4.last_column);
680                 $$->u2.arglist->u1.str = $4;
681                 z->u2.arglist->next = npval(PV_WORD,@6.first_line,@6.last_line, @6.first_column, @6.last_column);
682                 z->u2.arglist->next->u1.str = $6;
683                 z->u2.arglist->next->next = npval(PV_WORD,@8.first_line,@8.last_line, @8.first_column, @8.last_column);
684                 z->u2.arglist->next->next->u1.str = $8;
685                 z->u2.arglist->next->next->next = npval(PV_WORD,@10.first_line,@10.last_line, @10.first_column, @10.last_column);
686                 z->u2.arglist->next->next->next->u1.str = $10;
687                 prev_word=0;
688         }
689         | includeslist error {$$=$1;}
690         ;
691
692 includedname : word { $$ = $1;}
693         | KW_DEFAULT {$$=strdup("default");}
694         ;
695
696 includes : KW_INCLUDES LC includeslist RC {
697                 $$= npval(PV_INCLUDES,@1.first_line,@4.last_line, @1.first_column, @4.last_column);
698                 $$->u1.list = $3;}
699         | KW_INCLUDES LC RC {
700                 $$= npval(PV_INCLUDES,@1.first_line,@3.last_line, @1.first_column, @3.last_column);}
701         ;
702
703
704 %%
705
706 static char *token_equivs1[] =
707 {
708         "AMPER",
709         "AT",
710         "BAR",
711         "COLON",
712         "COMMA",
713         "EQ",
714         "EXTENMARK",
715         "KW_BREAK",
716         "KW_CASE",
717         "KW_CATCH",
718         "KW_CONTEXT",
719         "KW_CONTINUE",
720         "KW_DEFAULT",
721         "KW_ELSE",
722         "KW_ESWITCHES",
723         "KW_FOR",
724         "KW_GLOBALS",
725         "KW_GOTO",
726         "KW_HINT",
727         "KW_IFTIME",
728         "KW_IF",
729         "KW_IGNOREPAT",
730         "KW_INCLUDES"
731         "KW_JUMP",
732         "KW_MACRO",
733         "KW_PATTERN",
734         "KW_REGEXTEN",
735         "KW_RETURN",
736         "KW_SWITCHES",
737         "KW_SWITCH",
738         "KW_WHILE",
739         "LC",
740         "LP",
741         "RC",
742         "RP",
743         "SEMI",
744 };
745
746 static char *token_equivs2[] =
747 {
748         "&",
749         "@",
750         "|",
751         ":",
752         ",",
753         "=",
754         "=>",
755         "break",
756         "case",
757         "catch",
758         "context",
759         "continue",
760         "default",
761         "else",
762         "eswitches",
763         "for",
764         "globals",
765         "goto",
766         "hint",
767         "ifTime",
768         "if",
769         "ignorepat",
770         "includes"
771         "jump",
772         "macro",
773         "pattern",
774         "regexten",
775         "return",
776         "switches",
777         "switch",
778         "while",
779         "{",
780         "(",
781         "}",
782         ")",
783         ";",
784 };
785
786
787 static char *ael_token_subst(char *mess)
788 {
789         /* calc a length, malloc, fill, and return; yyerror had better free it! */
790         int len=0,i;
791         char *p;
792         char *res, *s,*t;
793         int token_equivs_entries = sizeof(token_equivs1)/sizeof(char*);
794
795         for (p=mess; *p; p++) {
796                 for (i=0; i<token_equivs_entries; i++) {
797                         if ( strncmp(p,token_equivs1[i],strlen(token_equivs1[i])) == 0 )
798                         {
799                                 len+=strlen(token_equivs2[i])+2;
800                                 p += strlen(token_equivs1[i])-1;
801                                 break;
802                         }
803                 }
804                 len++;
805         }
806         res = (char*)malloc(len+1);
807         res[0] = 0;
808         s = res;
809         for (p=mess; *p;) {
810                 int found = 0;
811                 for (i=0; i<token_equivs_entries; i++) {
812                         if ( strncmp(p,token_equivs1[i],strlen(token_equivs1[i])) == 0 ) {
813                                 *s++ = '\'';
814                                 for (t=token_equivs2[i]; *t;) {
815                                         *s++ = *t++;
816                                 }
817                                 *s++ = '\'';
818                                 p += strlen(token_equivs1[i]);
819                                 found = 1;
820                                 break;
821                         }
822                 }
823                 if( !found )
824                         *s++ = *p++;
825         }
826         *s++ = 0;
827         return res;
828 }
829
830 void yyerror(YYLTYPE *locp, struct parse_io *parseio,  char const *s)
831 {
832         char *s2 = ael_token_subst((char *)s);
833         if (locp->first_line == locp->last_line) {
834                 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);
835         } else {
836                 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);
837         }
838         free(s2);
839         parseio->syntax_error_count++;
840 }
841
842 static struct pval *npval(pvaltype type, int first_line, int last_line,
843         int first_column, int last_column)
844 {
845         extern char *my_file;
846         pval *z = (pval *)calloc(sizeof(struct pval),1);
847         z->type = type;
848         z->startline = first_line;
849         z->endline = last_line;
850         z->startcol = first_column;
851         z->endcol = last_column;
852         z->filename = strdup(my_file);
853         return z;
854 }
855
856 /* append second element to the list in the first one */
857 static void linku1(pval *head, pval *tail)
858 {
859         if (!head->next) {
860                 head->next = tail;
861         } else {
862                 head->u1_last->next = tail;
863         }
864         head->u1_last = tail;
865 }
866