afcef4c50625e32dac5aa2bef4a7d8dd8fbffa19
[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 {$$=npval(PV_MACRO,@1.first_line,@8.last_line, @1.first_column, @8.last_column);
196                                                                                                                                          $$->u1.str = $2; $$->u2.arglist = $4; $$->u3.macro_statements = $7; }
197         | KW_MACRO word LP arglist RP LC  RC {$$=npval(PV_MACRO,@1.first_line,@7.last_line, @1.first_column, @7.last_column); $$->u1.str = $2; $$->u2.arglist = $4; }
198         | KW_MACRO word LP RP LC macro_statements RC {$$=npval(PV_MACRO,@1.first_line,@7.last_line, @1.first_column, @7.last_column); $$->u1.str = $2; $$->u3.macro_statements = $6; }
199         | KW_MACRO word LP RP LC  RC {$$=npval(PV_MACRO,@1.first_line,@6.last_line, @1.first_column, @6.last_column); $$->u1.str = $2; /* pretty empty! */ }
200         ;
201
202 globals : KW_GLOBALS LC global_statements RC {$$=npval(PV_GLOBALS,@1.first_line,@4.last_line, @1.first_column, @4.last_column); $$->u1.statements = $3;}
203         | KW_GLOBALS LC RC /* empty global is OK */ {$$=npval(PV_GLOBALS,@1.first_line,@3.last_line, @1.first_column, @3.last_column); /* and that's all */ }
204         ;
205
206 global_statements : global_statement {$$=$1;}
207         | global_statements global_statement {$$=$1; linku1($$,$2);}
208         | global_statements error {$$=$1;}
209         ;
210
211 global_statement : word EQ { reset_semicount(parseio->scanner); }  word SEMI {$$=npval(PV_VARDEC,@1.first_line,@5.last_line, @1.first_column, @5.last_column); $$->u1.str = $1;$$->u2.val = $4; }
212         ;
213
214 arglist : word {$$= npval(PV_WORD,@1.first_line,@1.last_line, @1.first_column, @1.last_column); $$->u1.str = $1; }
215         | arglist COMMA word {pval *z = npval(PV_WORD,@1.first_line,@3.last_line, @1.first_column, @3.last_column); z->u1.str = $3; $$=$1; linku1($$,z); }
216         | arglist error {$$=$1;}
217         ;
218
219 elements : element { $$=$1;}
220         | error {$$=0;}
221         | elements element { if ( $1 && $2 ) {$$=$1; linku1($$,$2);}
222                                 else if ( $1 ) {$$=$1;}
223                                 else if ( $2 ) {$$=$2;} }
224         | elements error   { $$=$1;}
225         ;
226
227 element : extension {$$=$1;}
228         | includes {$$=$1;}
229         | switches {$$=$1;}
230         | eswitches {$$=$1;}
231         | ignorepat {$$=$1;}
232         | word EQ { reset_semicount(parseio->scanner); } word SEMI {$$=npval(PV_VARDEC,@1.first_line,@5.last_line, @1.first_column, @5.last_column); $$->u1.str = $1;$$->u2.val = $4; }
233         | word error {free($1); $$=0;}
234         | SEMI  {$$=0;/* allow older docs to be read */}
235         ;
236
237 ignorepat : KW_IGNOREPAT EXTENMARK word SEMI { $$=npval(PV_IGNOREPAT,@1.first_line,@4.last_line, @1.first_column, @4.last_column); $$->u1.str = $3;}
238         ;
239
240 extension : word EXTENMARK statement {$$ = npval(PV_EXTENSION,@1.first_line,@3.last_line, @1.first_column, @3.last_column); $$->u1.str = $1; $$->u2.statements = $3; }
241                   | KW_REGEXTEN word EXTENMARK statement {$$ = npval(PV_EXTENSION,@1.first_line,@3.last_line, @1.first_column, @4.last_column); $$->u1.str = $2; $$->u2.statements = $4; $$->u4.regexten=1;}
242                   | KW_HINT LP word3_list RP word EXTENMARK statement {$$ = npval(PV_EXTENSION,@1.first_line,@7.last_line, @1.first_column, @7.last_column); $$->u1.str = $5; $$->u2.statements = $7; $$->u3.hints = $3;}
243                   | KW_REGEXTEN KW_HINT LP word3_list RP word EXTENMARK statement {$$ = npval(PV_EXTENSION,@1.first_line,@4.last_line, @1.first_column, @8.last_column); $$->u1.str = $6; $$->u2.statements = $8; $$->u4.regexten=1;$$->u3.hints = $4;}
244
245         ;
246
247 statements : statement {$$=$1;}
248         | statements statement {if ( $1 && $2 ) {$$=$1; linku1($$,$2);}
249                                                  else if ( $1 ) {$$=$1;}
250                                                  else if ( $2 ) {$$=$2;} }
251         | statements error {$$=$1;}
252         ;
253
254 if_head : KW_IF LP { reset_parencount(parseio->scanner); }  word_list RP { $$= npval(PV_IF,@1.first_line,@5.last_line, @1.first_column, @5.last_column); $$->u1.str = $4; }
255                 ;
256
257 random_head : KW_RANDOM LP { reset_parencount(parseio->scanner); } word_list RP { $$= npval(PV_RANDOM,@1.first_line,@5.last_line, @1.first_column, @5.last_column); $$->u1.str=$4;}
258                 ;
259
260 iftime_head : KW_IFTIME LP word3_list COLON word3_list COLON word3_list BAR word3_list BAR word3_list BAR word3_list RP { $$= npval(PV_IFTIME,@1.first_line,@5.last_line, @1.first_column, @5.last_column);
261                                         $$->u1.list = npval(PV_WORD,@3.first_line,@3.last_line, @3.first_column, @3.last_column);
262                                         $$->u1.list->u1.str = (char*)malloc(strlen($3)+strlen($5)+strlen($7)+4);
263                                         strcpy($$->u1.list->u1.str,$3);
264                                         strcat($$->u1.list->u1.str,":");
265                                         strcat($$->u1.list->u1.str,$5);
266                                         strcat($$->u1.list->u1.str,":");
267                                         strcat($$->u1.list->u1.str,$7);
268                                         free($3);
269                                         free($5);
270                                         free($7);
271                                         $$->u1.list->next = npval(PV_WORD,@9.first_line,@9.last_line, @9.first_column, @9.last_column);
272                                         $$->u1.list->next->u1.str = $9;
273                                         $$->u1.list->next->next = npval(PV_WORD,@11.first_line,@11.last_line, @11.first_column, @11.last_column);
274                                         $$->u1.list->next->next->u1.str = $11;
275                                         $$->u1.list->next->next->next = npval(PV_WORD,@13.first_line,@13.last_line, @13.first_column, @13.last_column);
276                                         $$->u1.list->next->next->next->u1.str = $13;
277                                         prev_word = 0;
278                 }
279                 | KW_IFTIME LP word BAR word3_list BAR word3_list BAR word3_list RP { $$= npval(PV_IFTIME,@1.first_line,@5.last_line, @1.first_column, @5.last_column);
280                                         $$->u1.list = npval(PV_WORD,@3.first_line,@3.last_line, @3.first_column, @3.last_column);
281                                         $$->u1.list->u1.str = $3;
282                                         $$->u1.list->next = npval(PV_WORD,@5.first_line,@5.last_line, @5.first_column, @5.last_column);
283                                         $$->u1.list->next->u1.str = $5;
284                                         $$->u1.list->next->next = npval(PV_WORD,@7.first_line,@7.last_line, @7.first_column, @7.last_column);
285                                         $$->u1.list->next->next->u1.str = $7;
286                                         $$->u1.list->next->next->next = npval(PV_WORD,@9.first_line,@9.last_line, @9.first_column, @9.last_column);
287                                         $$->u1.list->next->next->next->u1.str = $9;
288                                         prev_word = 0;
289                 }
290
291         ;
292
293 /* word_list is a hack to fix a problem with context switching between bison and flex;
294    by the time you register a new context with flex, you've already got a look-ahead token
295    from the old context, with no way to put it back and start afresh. So, we kludge this
296    and merge the words back together. */
297
298 word_list : word { $$ = $1;}
299         | word word { $$ = (char*)malloc(strlen($1)+strlen($2)+1); strcpy($$, $1); strcat($$, $2);  free($1); free($2);prev_word = $$;}
300         ;
301 word3_list : word { $$ = $1;}
302         | word word { $$ = (char*)malloc(strlen($1)+strlen($2)+1); strcpy($$, $1); strcat($$, $2);  free($1); free($2);prev_word = $$;}
303         | word word word { $$ = (char*)malloc(strlen($1)+strlen($2)+strlen($3)+1); strcpy($$, $1); strcat($$, $2);  strcat($$, $3);  free($1); free($2); free($3);prev_word=$$;}
304         ;
305
306 goto_word : word { $$ = $1;}
307         | word word { $$ = (char*)malloc(strlen($1)+strlen($2)+1); strcpy($$, $1); strcat($$, $2);  free($1); free($2);}
308         | word COLON word { $$ = (char*)malloc(strlen($1)+strlen($3)+2); strcpy($$, $1); strcat($$,":"); strcat($$, $3);  free($1); free($3);}
309         ;
310
311 switch_head : KW_SWITCH LP { reset_parencount(parseio->scanner); } word RP  LC
312                                         {$$=npval(PV_SWITCH,@1.first_line,@6.last_line, @1.first_column, @6.last_column);
313                                                 $$->u1.str = $4; }
314         ;
315
316 statement : LC statements RC {$$=npval(PV_STATEMENTBLOCK,@1.first_line,@3.last_line, @1.first_column, @3.last_column); $$->u1.list = $2; }
317         | word EQ {reset_semicount(parseio->scanner);} word SEMI
318                         {$$=npval(PV_VARDEC,@1.first_line,@5.last_line, @1.first_column, @5.last_column);
319                                 $$->u1.str = $1; $$->u2.val = $4; }
320         | KW_GOTO target SEMI {$$=npval(PV_GOTO,@1.first_line,@3.last_line, @1.first_column, @3.last_column); $$->u1.list = $2;}
321         | KW_JUMP jumptarget SEMI {$$=npval(PV_GOTO,@1.first_line,@3.last_line, @1.first_column, @3.last_column); $$->u1.list = $2;}
322         | word COLON {$$=npval(PV_LABEL,@1.first_line,@2.last_line, @1.first_column, @2.last_column); $$->u1.str = $1; }
323         | KW_FOR LP {reset_semicount(parseio->scanner);} word SEMI
324                         {reset_semicount(parseio->scanner);} word SEMI
325                         {reset_parencount(parseio->scanner);} word RP statement
326                                 { $$=npval(PV_FOR,@1.first_line,@12.last_line, @1.first_column, @12.last_column);
327                                                 $$->u1.for_init = $4; $$->u2.for_test=$7; $$->u3.for_inc = $10; $$->u4.for_statements = $12;}
328         | KW_WHILE LP {reset_parencount(parseio->scanner);} word RP statement
329                         {$$=npval(PV_WHILE,@1.first_line,@6.last_line, @1.first_column, @6.last_column);
330                                         $$->u1.str = $4; $$->u2.statements = $6; }
331         | switch_head RC /* empty list OK */ {$$=$1;$$->endline = @2.last_line; $$->endcol = @2.last_column;}
332         | switch_head case_statements RC {$$=$1; $$->u2.statements = $2;$$->endline = @3.last_line; $$->endcol = @3.last_column;}
333         | AMPER macro_call SEMI {$$ = $2;$$->endline = @2.last_line; $$->endcol = @2.last_column;}
334         | application_call SEMI { $$ = $1;$$->endline = @2.last_line; $$->endcol = @2.last_column;}
335         | word SEMI { $$= npval(PV_APPLICATION_CALL,@1.first_line,@2.last_line, @1.first_column, @2.last_column);
336                                                                                                                                                                                 $$->u1.str = $1;}
337         | application_call EQ {reset_semicount(parseio->scanner);} word SEMI {
338                           char *bufx;
339                                                   int tot=0;
340                                                   pval *pptr;
341
342                           $$ = npval(PV_VARDEC,@1.first_line,@5.last_line, @1.first_column, @5.last_column);
343                                                   $$->u2.val=$4;
344                                                   /* rebuild the original string-- this is not an app call, it's an unwrapped vardec, with a func call on the LHS */
345                           /* string to big to fit in the buffer? */
346                                                   tot+=strlen($1->u1.str);
347                                                   for(pptr=$1->u2.arglist;pptr;pptr=pptr->next) {
348                                                           tot+=strlen(pptr->u1.str);
349                                                           tot++; /* for a sep like a comma */
350                                                   }
351                                                   tot+=4; /* for safety */
352                                                   bufx = (char *)malloc(tot);
353                                                   strcpy(bufx,$1->u1.str);
354                                                   strcat(bufx,"(");
355                                                   for (pptr=$1->u2.arglist;pptr;pptr=pptr->next) {
356                                                           if ( pptr != $1->u2.arglist )
357                                                                   strcat(bufx,",");
358                                                           strcat(bufx,pptr->u1.str);
359                                                   }
360                                                   strcat(bufx,")");
361 #ifdef AAL_ARGCHECK
362                                                   if ( !ael_is_funcname($1->u1.str) )
363                               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",
364                                                                           my_file, @1.first_line, @1.first_column, @1.last_column, $1->u1.str);
365 #endif
366                                                   $$->u1.str = bufx;
367                                                   destroy_pval($1); /* the app call it is not, get rid of that chain */
368                                                   prev_word = 0;
369                        }
370         | KW_BREAK SEMI { $$ = npval(PV_BREAK,@1.first_line,@2.last_line, @1.first_column, @2.last_column);}
371         | KW_RETURN SEMI {$$ = npval(PV_RETURN,@1.first_line,@2.last_line, @1.first_column, @2.last_column);}
372         | KW_CONTINUE SEMI {$$ = npval(PV_CONTINUE,@1.first_line,@2.last_line, @1.first_column, @2.last_column);}
373         | random_head statement {$$=$1; $$->u2.statements = $2;$$->endline = @2.last_line; $$->endcol = @2.last_column;}
374         | random_head statement KW_ELSE statement {$$=$1; $$->u2.statements = $2;$$->endline = @2.last_line; $$->endcol = @2.last_column; $$->u3.else_statements = $4;}
375         | if_head statement {$$=$1; $$->u2.statements = $2;$$->endline = @2.last_line; $$->endcol = @2.last_column;}
376         | if_head statement KW_ELSE statement {$$=$1; $$->u2.statements = $2;$$->endline = @2.last_line; $$->endcol = @2.last_column; $$->u3.else_statements = $4;}
377         | iftime_head statement {$$=$1; $$->u2.statements = $2;$$->endline = @2.last_line; $$->endcol = @2.last_column;}
378         | iftime_head statement KW_ELSE statement {$$=$1; $$->u2.statements = $2;$$->endline = @2.last_line; $$->endcol = @2.last_column; $$->u3.else_statements = $4;}
379         | SEMI { $$=0; }
380         ;
381
382 target : goto_word { $$ = npval(PV_WORD,@1.first_line,@1.last_line, @1.first_column, @1.last_column); $$->u1.str = $1;}
383         | goto_word BAR goto_word {$$=npval(PV_WORD,@1.first_line,@1.last_line, @1.first_column, @1.last_column);
384                                         $$->u1.str = $1; $$->next = npval(PV_WORD,@3.first_line,@3.last_line, @3.first_column, @3.last_column);
385                                         $$->next->u1.str = $3;}
386         | goto_word COMMA goto_word {$$=npval(PV_WORD,@1.first_line,@1.last_line, @1.first_column, @1.last_column);
387                                         $$->u1.str = $1; $$->next = npval(PV_WORD,@3.first_line,@3.last_line, @3.first_column, @3.last_column);
388                                         $$->next->u1.str = $3;}
389         | goto_word BAR goto_word BAR goto_word {$$=npval(PV_WORD,@1.first_line,@1.last_line, @1.first_column, @1.last_column);
390                                         $$->u1.str = $1; $$->next = npval(PV_WORD,@3.first_line,@3.last_line, @3.first_column, @3.last_column);
391                                         $$->next->u1.str = $3;
392                                         $$->next->next = npval(PV_WORD,@5.first_line,@5.last_line, @5.first_column, @5.last_column);
393                                         $$->next->next->u1.str = $5; }
394         | goto_word COMMA goto_word COMMA goto_word {$$=npval(PV_WORD,@1.first_line,@1.last_line, @1.first_column, @1.last_column);
395                                         $$->u1.str = $1; $$->next = npval(PV_WORD,@3.first_line,@3.last_line, @3.first_column, @3.last_column);
396                                         $$->next->u1.str = $3;
397                                         $$->next->next = npval(PV_WORD,@5.first_line,@5.last_line, @5.first_column, @5.last_column);
398                                         $$->next->next->u1.str = $5; }
399         | KW_DEFAULT BAR goto_word BAR goto_word {$$=npval(PV_WORD,@1.first_line,@1.last_line, @1.first_column, @1.last_column);
400                                         $$->u1.str = strdup("default"); $$->next = npval(PV_WORD,@3.first_line,@3.last_line, @3.first_column, @3.last_column);
401                                         $$->next->u1.str = $3;
402                                         $$->next->next = npval(PV_WORD,@5.first_line,@5.last_line, @5.first_column, @5.last_column);
403                                         $$->next->next->u1.str = $5; }
404         | KW_DEFAULT COMMA goto_word COMMA goto_word {$$=npval(PV_WORD,@1.first_line,@1.last_line, @1.first_column, @1.last_column);
405                                         $$->u1.str = strdup("default"); $$->next = npval(PV_WORD,@3.first_line,@3.last_line, @3.first_column, @3.last_column);
406                                         $$->next->u1.str = $3;
407                                         $$->next->next = npval(PV_WORD,@5.first_line,@5.last_line, @5.first_column, @5.last_column);
408                                         $$->next->next->u1.str = $5; }
409         ;
410
411 jumptarget : goto_word {$$=npval(PV_WORD,@1.first_line,@1.last_line, @1.first_column, @1.last_column);
412                                         $$->u1.str = $1; $$->next = npval(PV_WORD,@1.first_line,@1.last_line, @1.first_column, @1.last_column);
413                                         $$->next->u1.str = strdup("1");}  /*  jump extension[,priority][@context] */
414                 | goto_word COMMA goto_word {$$=npval(PV_WORD,@1.first_line,@1.last_line, @1.first_column, @1.last_column);
415                                         $$->u1.str = $1; $$->next = npval(PV_WORD,@3.first_line,@3.last_line, @3.first_column, @3.last_column);
416                                         $$->next->u1.str = $3;}
417                 | goto_word COMMA word AT word {$$=npval(PV_WORD,@1.first_line,@1.last_line, @1.first_column, @1.last_column);
418                                         $$->u1.str = $5; $$->next = npval(PV_WORD,@3.first_line,@3.last_line, @3.first_column, @3.last_column);
419                                         $$->next->u1.str = $1;
420                                         $$->next->next = npval(PV_WORD,@5.first_line,@5.last_line, @5.first_column, @5.last_column);
421                                         $$->next->next->u1.str = $3; }
422                 | goto_word AT goto_word {$$=npval(PV_WORD,@1.first_line,@1.last_line, @1.first_column, @1.last_column);
423                                         $$->u1.str = $3; $$->next = npval(PV_WORD,@3.first_line,@3.last_line, @3.first_column, @3.last_column);
424                                         $$->next->u1.str = $1;
425                                         $$->next->next = npval(PV_WORD,@3.first_line,@3.last_line, @3.first_column, @3.last_column);
426                                         $$->next->next->u1.str = strdup("1"); }
427                 | goto_word COMMA word AT KW_DEFAULT {$$=npval(PV_WORD,@1.first_line,@1.last_line, @1.first_column, @1.last_column);
428                                         $$->u1.str = strdup("default"); $$->next = npval(PV_WORD,@3.first_line,@3.last_line, @3.first_column, @3.last_column);
429                                         $$->next->u1.str = $1;
430                                         $$->next->next = npval(PV_WORD,@5.first_line,@5.last_line, @5.first_column, @5.last_column);
431                                         $$->next->next->u1.str = $3; }
432                 | goto_word AT KW_DEFAULT {$$=npval(PV_WORD,@1.first_line,@1.last_line, @1.first_column, @1.last_column);
433                                         $$->u1.str = strdup("default"); $$->next = npval(PV_WORD,@3.first_line,@3.last_line, @3.first_column, @3.last_column);
434                                         $$->next->u1.str = $1;
435                                         $$->next->next = npval(PV_WORD,@3.first_line,@3.last_line, @3.first_column, @3.last_column);
436                                         $$->next->next->u1.str = strdup("1"); }
437                 ;
438
439 macro_call : word LP {reset_argcount(parseio->scanner);} eval_arglist RP
440                         {$$= npval(PV_MACRO_CALL,@1.first_line,@2.last_line, @1.first_column, @2.last_column);
441                         $$->u1.str = $1; $$->u2.arglist = $4;}
442         | word LP RP {$$= npval(PV_MACRO_CALL,@1.first_line,@3.last_line, @1.first_column, @3.last_column); $$->u1.str = $1; }
443         ;
444
445 application_call_head: word {reset_argcount(parseio->scanner);} LP  {if (strcasecmp($1,"goto") == 0) {
446                                                                                                                                                                                         $$= npval(PV_GOTO,@1.first_line,@3.last_line, @1.first_column, @3.last_column);
447                                                                                                                                                                                         free($1); /* won't be using this */
448                                                                                                                                                                                         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 );
449                                                                                                                                                                                 } else
450                                                                                                                                                                                         $$= npval(PV_APPLICATION_CALL,@1.first_line,@3.last_line, @1.first_column, @3.last_column);
451                                                                                                                                                                                 $$->u1.str = $1; }
452         ;
453
454 application_call : application_call_head eval_arglist RP {$$ = $1;
455                 if( $$->type == PV_GOTO )
456                         $$->u1.list = $2;
457                 else
458                         $$->u2.arglist = $2;
459                 $$->endline = @3.last_line; $$->endcol = @3.last_column;}
460         | application_call_head RP {$$=$1;$$->endline = @2.last_line; $$->endcol = @2.last_column;}
461         ;
462
463 eval_arglist :  word_list { $$= npval(PV_WORD,@1.first_line,@1.last_line, @1.first_column, @1.last_column); $$->u1.str = $1;}
464         | /*nothing! */   { $$= npval(PV_WORD,0/*@1.first_line*/,0/*@1.last_line*/,0/* @1.first_column*/, 0/*@1.last_column*/); $$->u1.str = strdup(""); }
465         | 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;}
466         | 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("");}
467         ;
468
469 case_statements: case_statement {$$=$1;}
470         | case_statements case_statement { if ( $1 && $2 ) {$$=$1; linku1($$,$2);}
471                                                  else if ( $1 ) {$$=$1;}
472                                                  else if ( $2 ) {$$=$2;} }
473         ;
474
475 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;}
476         | 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;}
477         | 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;}
478         | KW_CASE word COLON {$$ = npval(PV_CASE,@1.first_line,@3.last_line, @1.first_column, @3.last_column); $$->u1.str = $2;}
479         | KW_DEFAULT COLON {$$ = npval(PV_DEFAULT,@1.first_line,@2.last_line, @1.first_column, @2.last_column); $$->u1.str = 0;}
480         | KW_PATTERN word COLON  {$$ = npval(PV_PATTERN,@1.first_line,@3.last_line, @1.first_column, @3.last_column); $$->u1.str = $2;}
481         ;
482
483 macro_statements: macro_statement {$$ = $1;}
484         | macro_statements macro_statement { if ( $1 && $2 ) {$$=$1; linku1($$,$2);}
485                                                  else if ( $1 ) {$$=$1;}
486                                                  else if ( $2 ) {$$=$2;} }
487         ;
488
489 macro_statement : statement {$$=$1;}
490         | 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;}
491         ;
492
493 switches : KW_SWITCHES LC switchlist RC {$$= npval(PV_SWITCHES,@1.first_line,@4.last_line, @1.first_column, @4.last_column); $$->u1.list = $3; }
494         | KW_SWITCHES LC RC /* empty switch list OK */ {$$= npval(PV_SWITCHES,@1.first_line,@3.last_line, @1.first_column, @3.last_column);}
495         ;
496
497 eswitches : KW_ESWITCHES LC switchlist RC {$$= npval(PV_ESWITCHES,@1.first_line,@4.last_line, @1.first_column, @4.last_column); $$->u1.list = $3; }
498         | 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? */
499         ;
500
501 switchlist : word SEMI {$$=npval(PV_WORD,@1.first_line,@2.last_line, @1.first_column, @2.last_column); $$->u1.str = $1;}
502         | 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); }
503         | switchlist error {$$=$1;}
504         ;
505
506 includeslist : includedname SEMI {$$=npval(PV_WORD,@1.first_line,@2.last_line, @1.first_column, @2.last_column); $$->u1.str = $1;}
507         | includedname BAR word3_list COLON word3_list COLON word3_list BAR word3_list BAR word3_list BAR word3_list SEMI {
508                     $$=npval(PV_WORD,@1.first_line,@2.last_line, @1.first_column, @2.last_column);
509                     $$->u1.str = $1;
510                                         $$->u2.arglist = npval(PV_WORD,@3.first_line,@7.last_line, @3.first_column, @7.last_column);
511                                         $$->u2.arglist->u1.str = (char*)malloc(strlen($3)+strlen($5)+strlen($7)+4);
512                                         strcpy($$->u2.arglist->u1.str,$3);
513                                         strcat($$->u2.arglist->u1.str,":");
514                                         strcat($$->u2.arglist->u1.str,$5);
515                                         strcat($$->u2.arglist->u1.str,":");
516                                         strcat($$->u2.arglist->u1.str,$7);
517                                         free($3);
518                                         free($5);
519                                         free($7);
520                                         $$->u2.arglist->next = npval(PV_WORD,@9.first_line,@9.last_line, @9.first_column, @9.last_column);
521                                         $$->u2.arglist->next->u1.str = $9;
522                                         $$->u2.arglist->next->next = npval(PV_WORD,@11.first_line,@11.last_line, @11.first_column, @11.last_column);
523                                         $$->u2.arglist->next->next->u1.str = $11;
524                                         $$->u2.arglist->next->next->next = npval(PV_WORD,@13.first_line,@13.last_line, @13.first_column, @13.last_column);
525                                         $$->u2.arglist->next->next->next->u1.str = $13;
526                                         prev_word=0;
527                         }
528         | includedname BAR word BAR word3_list BAR word3_list BAR word3_list SEMI {
529                     $$=npval(PV_WORD,@1.first_line,@2.last_line, @1.first_column, @2.last_column);
530                     $$->u1.str = $1;
531                                         $$->u2.arglist = npval(PV_WORD,@3.first_line,@3.last_line, @3.first_column, @3.last_column);
532                                         $$->u2.arglist->u1.str = $3;
533                                         $$->u2.arglist->next = npval(PV_WORD,@5.first_line,@5.last_line, @5.first_column, @5.last_column);
534                                         $$->u2.arglist->next->u1.str = $5;
535                                         $$->u2.arglist->next->next = npval(PV_WORD,@7.first_line,@7.last_line, @7.first_column, @7.last_column);
536                                         $$->u2.arglist->next->next->u1.str = $7;
537                                         $$->u2.arglist->next->next->next = npval(PV_WORD,@9.first_line,@9.last_line, @9.first_column, @9.last_column);
538                                         $$->u2.arglist->next->next->next->u1.str = $9;
539                                         prev_word=0;
540                         }
541         | 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); }
542         | 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);
543                                         $$=$1; z->u1.str = $2; linku1($$,z);
544                                         z->u2.arglist = npval(PV_WORD,@4.first_line,@4.last_line, @4.first_column, @4.last_column);
545                                         $$->u2.arglist->u1.str = (char*)malloc(strlen($4)+strlen($6)+strlen($8)+4);
546                                         strcpy($$->u2.arglist->u1.str,$4);
547                                         strcat($$->u2.arglist->u1.str,":");
548                                         strcat($$->u2.arglist->u1.str,$6);
549                                         strcat($$->u2.arglist->u1.str,":");
550                                         strcat($$->u2.arglist->u1.str,$8);
551                                         free($4);
552                                         free($6);
553                                         free($8);
554                                         z->u2.arglist->next = npval(PV_WORD,@10.first_line,@10.last_line, @10.first_column, @10.last_column);
555                                         z->u2.arglist->next->u1.str = $10;
556                                         z->u2.arglist->next->next = npval(PV_WORD,@12.first_line,@12.last_line, @12.first_column, @12.last_column);
557                                         z->u2.arglist->next->next->u1.str = $12;
558                                         z->u2.arglist->next->next->next = npval(PV_WORD,@14.first_line,@14.last_line, @14.first_column, @14.last_column);
559                                         z->u2.arglist->next->next->next->u1.str = $14;
560                                         prev_word=0;
561                         }
562         | includeslist includedname BAR word BAR word3_list BAR word3_list BAR word3_list SEMI
563                    {pval *z = npval(PV_WORD,@2.first_line,@2.last_line, @2.first_column, @3.last_column);
564                                         $$=$1; z->u1.str = $2; linku1($$,z);
565                                         z->u2.arglist = npval(PV_WORD,@4.first_line,@4.last_line, @4.first_column, @4.last_column);
566                                         $$->u2.arglist->u1.str = $4;
567                                         z->u2.arglist->next = npval(PV_WORD,@6.first_line,@6.last_line, @6.first_column, @6.last_column);
568                                         z->u2.arglist->next->u1.str = $6;
569                                         z->u2.arglist->next->next = npval(PV_WORD,@8.first_line,@8.last_line, @8.first_column, @8.last_column);
570                                         z->u2.arglist->next->next->u1.str = $8;
571                                         z->u2.arglist->next->next->next = npval(PV_WORD,@10.first_line,@10.last_line, @10.first_column, @10.last_column);
572                                         z->u2.arglist->next->next->next->u1.str = $10;
573                                         prev_word=0;
574                         }
575         | includeslist error {$$=$1;}
576         ;
577
578 includedname : word { $$ = $1;}
579                         | KW_DEFAULT {$$=strdup("default");}
580                         ;
581
582 includes : KW_INCLUDES LC includeslist RC {$$= npval(PV_INCLUDES,@1.first_line,@4.last_line, @1.first_column, @4.last_column); $$->u1.list = $3;}
583         | KW_INCLUDES LC RC  {$$= npval(PV_INCLUDES,@1.first_line,@3.last_line, @1.first_column, @3.last_column);}
584         ;
585
586
587 %%
588
589 static char *token_equivs1[] =
590 {
591         "AMPER",
592         "AT",
593         "BAR",
594         "COLON",
595         "COMMA",
596         "EQ",
597         "EXTENMARK",
598         "KW_BREAK",
599         "KW_CASE",
600         "KW_CATCH",
601         "KW_CONTEXT",
602         "KW_CONTINUE",
603         "KW_DEFAULT",
604         "KW_ELSE",
605         "KW_ESWITCHES",
606         "KW_FOR",
607         "KW_GLOBALS",
608         "KW_GOTO",
609         "KW_HINT",
610         "KW_IFTIME",
611         "KW_IF",
612         "KW_IGNOREPAT",
613         "KW_INCLUDES"
614         "KW_JUMP",
615         "KW_MACRO",
616         "KW_PATTERN",
617         "KW_REGEXTEN",
618         "KW_RETURN",
619         "KW_SWITCHES",
620         "KW_SWITCH",
621         "KW_WHILE",
622         "LC",
623         "LP",
624         "RC",
625         "RP",
626         "SEMI",
627 };
628
629 static char *token_equivs2[] =
630 {
631         "&",
632         "@",
633         "|",
634         ":",
635         ",",
636         "=",
637         "=>",
638         "break",
639         "case",
640         "catch",
641         "context",
642         "continue",
643         "default",
644         "else",
645         "eswitches",
646         "for",
647         "globals",
648         "goto",
649         "hint",
650         "ifTime",
651         "if",
652         "ignorepat",
653         "includes"
654         "jump",
655         "macro",
656         "pattern",
657         "regexten",
658         "return",
659         "switches",
660         "switch",
661         "while",
662         "{",
663         "(",
664         "}",
665         ")",
666         ";",
667 };
668
669
670 static char *ael_token_subst(char *mess)
671 {
672         /* calc a length, malloc, fill, and return; yyerror had better free it! */
673         int len=0,i;
674         char *p;
675         char *res, *s,*t;
676         int token_equivs_entries = sizeof(token_equivs1)/sizeof(char*);
677
678         for (p=mess; *p; p++) {
679                 for (i=0; i<token_equivs_entries; i++) {
680                         if ( strncmp(p,token_equivs1[i],strlen(token_equivs1[i])) == 0 )
681                         {
682                                 len+=strlen(token_equivs2[i])+2;
683                                 p += strlen(token_equivs1[i])-1;
684                                 break;
685                         }
686                 }
687                 len++;
688         }
689         res = (char*)malloc(len+1);
690         res[0] = 0;
691         s = res;
692         for (p=mess; *p;) {
693                 int found = 0;
694                 for (i=0; i<token_equivs_entries; i++) {
695                         if ( strncmp(p,token_equivs1[i],strlen(token_equivs1[i])) == 0 ) {
696                                 *s++ = '\'';
697                                 for (t=token_equivs2[i]; *t;) {
698                                         *s++ = *t++;
699                                 }
700                                 *s++ = '\'';
701                                 p += strlen(token_equivs1[i]);
702                                 found = 1;
703                                 break;
704                         }
705                 }
706                 if( !found )
707                         *s++ = *p++;
708         }
709         *s++ = 0;
710         return res;
711 }
712
713 void yyerror(YYLTYPE *locp, struct parse_io *parseio,  char const *s)
714 {
715         char *s2 = ael_token_subst((char *)s);
716         if (locp->first_line == locp->last_line) {
717                 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);
718         } else {
719                 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);
720         }
721         free(s2);
722         parseio->syntax_error_count++;
723 }
724
725 static struct pval *npval(pvaltype type,int first_line, int last_line, int first_column, int last_column)
726 {
727         extern char *my_file;
728         pval *z = (pval *)calloc(sizeof(struct pval),1);
729         z->type = type;
730         z->startline = first_line;
731         z->endline = last_line;
732         z->startcol = first_column;
733         z->endcol = last_column;
734         z->filename = strdup(my_file);
735         return z;
736 }
737
738 /* append second element to the list in the first one */
739 static void linku1(pval *head, pval *tail)
740 {
741         if (!head->next) {
742                 head->next = tail;
743         } else {
744                 head->u1_last->next = tail;
745         }
746         head->u1_last = tail;
747 }
748