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