Merge Steve Murphy's (murf) complete re-implementation of AEL, which is now no longer...
[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 extern void reset_parencount(yyscan_t yyscanner);
31 extern void reset_semicount(yyscan_t yyscanner);
32 extern void reset_argcount(yyscan_t yyscanner );
33
34 #define YYLEX_PARAM ((struct parse_io *)parseio)->scanner
35 #define YYERROR_VERBOSE 1
36
37 extern char *my_file;
38 #ifdef AAL_ARGCHECK
39 int ael_is_funcname(char *name);
40 #endif
41  static char *ael_token_subst(char *mess);
42  extern char *prev_word;
43  
44 %}
45
46
47 %union {
48         char *str;
49         struct pval *pval;
50 }
51
52 %{
53         /* declaring these AFTER the union makes things a lot simpler! */
54 void yyerror(YYLTYPE *locp, struct parse_io *parseio, char const *s);
55 int ael_yylex (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , void * yyscanner);
56         
57 %}
58
59  
60 %token KW_CONTEXT LC RC LP RP SEMI EQ COMMA COLON AMPER BAR AT
61 %token KW_MACRO KW_GLOBALS KW_IGNOREPAT KW_SWITCH KW_IF KW_IFTIME KW_ELSE KW_RANDOM KW_ABSTRACT
62 %token EXTENMARK KW_GOTO KW_JUMP KW_RETURN KW_BREAK KW_CONTINUE KW_REGEXTEN KW_HINT
63 %token KW_FOR KW_WHILE KW_CASE KW_PATTERN KW_DEFAULT KW_CATCH KW_SWITCHES KW_ESWITCHES
64 %token KW_INCLUDES
65
66 %token <str> word
67
68 %type <pval>includes
69 %type <pval>includeslist
70 %type <pval>switchlist
71 %type <pval>eswitches
72 %type <pval>switches
73 %type <pval>macro_statement
74 %type <pval>macro_statements
75 %type <pval>case_statement
76 %type <pval>case_statements
77 %type <pval>eval_arglist
78 %type <pval>application_call
79 %type <pval>application_call_head
80 %type <pval>macro_call
81 %type <pval>target jumptarget
82 %type <pval>statement
83 %type <pval>switch_head
84 %type <str>word_list goto_word
85 %type <str>word3_list
86 %type <str>includedname
87 %type <pval>if_head
88 %type <pval>random_head
89 %type <pval>iftime_head
90 %type <pval>statements
91 %type <pval>extension
92 %type <pval>ignorepat
93 %type <pval>element
94 %type <pval>elements
95 %type <pval>arglist
96 %type <pval>global_statement
97 %type <pval>global_statements
98 %type <pval>globals
99 %type <pval>macro
100 %type <pval>context
101 %type <pval>object
102 %type <pval>objects 
103 %type <pval>file 
104
105 /* OPTIONS */
106 %locations
107 %pure-parser
108 %name-prefix="ael_yy"
109 /* the following option does two things: 
110     it adds the locp arg to the yyerror
111     and it adds the NULL to the yyerrr arg list, and calls yyerror with NULL for that arg.
112     You can't get the locp arg without the NULL arg, don't ask me why. */
113 %parse-param {struct parse_io *parseio}
114 /* 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 
115    the default action to shift will attach the else to the preceeding if. */
116 %expect 5
117 %error-verbose
118 %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 
119                                 application_call_head macro_call target jumptarget statement switch_head if_head random_head iftime_head statements extension ignorepat element
120                                 elements arglist global_statement global_statements globals macro context object objects
121 %destructor { free($$);}  word word_list goto_word word3_list includedname
122
123
124 %%
125
126 file : objects  { $$ = parseio->pval = $1; }
127         ;
128
129 objects : object {$$=$1;}
130         | objects object {if ( $1 && $2 ) {$$=$1; linku1($$,$2);} 
131                                                  else if ( $1 ) {$$=$1;}
132                                                  else if ( $2 ) {$$=$2;} }
133         | objects error {$$=$1;}
134         ;
135
136 object : context {$$=$1;}
137         | macro {$$=$1;}
138         | globals {$$=$1;}
139         | SEMI  {$$=0;/* allow older docs to be read */}
140         ;
141
142 context : KW_CONTEXT word LC elements RC {$$=npval(PV_CONTEXT,@1.first_line,@5.last_line, @1.first_column, @5.last_column); $$->u1.str = $2; $$->u2.statements = $4; }
143         | KW_CONTEXT word LC RC /* empty context OK */ {$$=npval(PV_CONTEXT,@1.first_line,@4.last_line, @1.first_column, @4.last_column); $$->u1.str = $2; }
144         | KW_CONTEXT KW_DEFAULT LC elements RC  {$$=npval(PV_CONTEXT,@1.first_line,@5.last_line, @1.first_column, @5.last_column); $$->u1.str = strdup("default"); $$->u2.statements = $4; }
145         | KW_CONTEXT KW_DEFAULT LC RC /* empty context OK */ {$$=npval(PV_CONTEXT,@1.first_line,@4.last_line, @1.first_column, @4.last_column); $$->u1.str = strdup("default"); }
146     | KW_ABSTRACT KW_CONTEXT word LC elements RC {$$=npval(PV_CONTEXT,@1.first_line,@6.last_line, @1.first_column, @6.last_column); $$->u1.str = $3; $$->u2.statements = $5;  $$->u3.abstract = 1;}
147         | KW_ABSTRACT KW_CONTEXT word LC RC /* empty context OK */ {$$=npval(PV_CONTEXT,@1.first_line,@5.last_line, @1.first_column, @5.last_column); $$->u1.str = $3; $$->u3.abstract = 1; }
148         | KW_ABSTRACT KW_CONTEXT KW_DEFAULT LC elements RC  {$$=npval(PV_CONTEXT,@1.first_line,@6.last_line, @1.first_column, @6.last_column); $$->u1.str = strdup("default"); $$->u2.statements = $5; $$->u3.abstract = 1; }
149         | KW_ABSTRACT KW_CONTEXT KW_DEFAULT LC RC /* empty context OK */ {$$=npval(PV_CONTEXT,@1.first_line,@5.last_line, @1.first_column, @5.last_column); $$->u1.str = strdup("default"); $$->u3.abstract = 1; }
150         ;
151
152 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); 
153                                                                                                                                          $$->u1.str = $2; $$->u2.arglist = $4; $$->u3.macro_statements = $7; }
154         | 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; }
155         | 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; }
156         | 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! */ }
157         ;
158
159 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;}
160         | 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 */ }
161         ;
162
163 global_statements : global_statement {$$=$1;}
164         | global_statements global_statement {$$=$1; linku1($$,$2);}
165         | global_statements error {$$=$1;}
166         ;
167
168 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; }
169         ;
170
171 arglist : word {$$= npval(PV_WORD,@1.first_line,@1.last_line, @1.first_column, @1.last_column); $$->u1.str = $1; }
172         | 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); }
173         | arglist error {$$=$1;}
174         ;
175
176 elements : element { $$=$1;}
177     | error {$$=0;}
178         | elements element { if ( $1 && $2 ) {$$=$1; linku1($$,$2);} 
179                                                  else if ( $1 ) {$$=$1;}
180                                                  else if ( $2 ) {$$=$2;} }
181         | elements error   { $$=$1;}
182         ;
183
184 element : extension {$$=$1;}
185         | includes {$$=$1;}
186         | switches {$$=$1;}
187         | eswitches {$$=$1;}
188         | ignorepat {$$=$1;}
189         | 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; }
190     | word error {free($1); $$=0;}
191         | SEMI  {$$=0;/* allow older docs to be read */}
192         ;
193
194 ignorepat : KW_IGNOREPAT EXTENMARK word SEMI { $$=npval(PV_IGNOREPAT,@1.first_line,@4.last_line, @1.first_column, @4.last_column); $$->u1.str = $3;}
195         ;
196
197 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; }
198                   | 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;}
199                   | 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;}
200                   | 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;}
201
202         ;
203
204 statements : statement {$$=$1;}
205         | statements statement {if ( $1 && $2 ) {$$=$1; linku1($$,$2);} 
206                                                  else if ( $1 ) {$$=$1;}
207                                                  else if ( $2 ) {$$=$2;} }
208         | statements error {$$=$1;}
209         ;
210
211 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; }
212                 ;
213
214 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;}
215                 ;
216
217 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); 
218                                         $$->u1.list = npval(PV_WORD,@3.first_line,@3.last_line, @3.first_column, @3.last_column); 
219                                         $$->u1.list->u1.str = (char*)malloc(strlen($3)+strlen($5)+strlen($7)+4);
220                                         strcpy($$->u1.list->u1.str,$3);
221                                         strcat($$->u1.list->u1.str,":");
222                                         strcat($$->u1.list->u1.str,$5);
223                                         strcat($$->u1.list->u1.str,":");
224                                         strcat($$->u1.list->u1.str,$7);
225                                         free($3);
226                                         free($5);
227                                         free($7);
228                                         $$->u1.list->next = npval(PV_WORD,@9.first_line,@9.last_line, @9.first_column, @9.last_column); 
229                                         $$->u1.list->next->u1.str = $9; 
230                                         $$->u1.list->next->next = npval(PV_WORD,@11.first_line,@11.last_line, @11.first_column, @11.last_column); 
231                                         $$->u1.list->next->next->u1.str = $11; 
232                                         $$->u1.list->next->next->next = npval(PV_WORD,@13.first_line,@13.last_line, @13.first_column, @13.last_column); 
233                                         $$->u1.list->next->next->next->u1.str = $13; 
234                                         prev_word = 0;
235                 }
236                 | 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); 
237                                         $$->u1.list = npval(PV_WORD,@3.first_line,@3.last_line, @3.first_column, @3.last_column); 
238                                         $$->u1.list->u1.str = $3;
239                                         $$->u1.list->next = npval(PV_WORD,@5.first_line,@5.last_line, @5.first_column, @5.last_column); 
240                                         $$->u1.list->next->u1.str = $5; 
241                                         $$->u1.list->next->next = npval(PV_WORD,@7.first_line,@7.last_line, @7.first_column, @7.last_column); 
242                                         $$->u1.list->next->next->u1.str = $7; 
243                                         $$->u1.list->next->next->next = npval(PV_WORD,@9.first_line,@9.last_line, @9.first_column, @9.last_column); 
244                                         $$->u1.list->next->next->next->u1.str = $9; 
245                                         prev_word = 0;
246                 }
247
248         ;
249
250 /* word_list is a hack to fix a problem with context switching between bison and flex;
251    by the time you register a new context with flex, you've already got a look-ahead token 
252    from the old context, with no way to put it back and start afresh. So, we kludge this
253    and merge the words back together. */
254
255 word_list : word { $$ = $1;}  
256         | word word { $$ = (char*)malloc(strlen($1)+strlen($2)+1); strcpy($$, $1); strcat($$, $2);  free($1); free($2);prev_word = $$;}
257         ;
258 word3_list : word { $$ = $1;}  
259         | word word { $$ = (char*)malloc(strlen($1)+strlen($2)+1); strcpy($$, $1); strcat($$, $2);  free($1); free($2);prev_word = $$;}
260         | 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=$$;}
261         ;
262
263 goto_word : word { $$ = $1;}
264         | word word { $$ = (char*)malloc(strlen($1)+strlen($2)+1); strcpy($$, $1); strcat($$, $2);  free($1); free($2);}
265         | word COLON word { $$ = (char*)malloc(strlen($1)+strlen($3)+2); strcpy($$, $1); strcat($$,":"); strcat($$, $3);  free($1); free($3);}
266         ;
267
268 switch_head : KW_SWITCH LP { reset_parencount(parseio->scanner); } word RP  LC 
269                                         {$$=npval(PV_SWITCH,@1.first_line,@6.last_line, @1.first_column, @6.last_column);
270                                                 $$->u1.str = $4; }
271         ;
272
273 statement : LC statements RC {$$=npval(PV_STATEMENTBLOCK,@1.first_line,@3.last_line, @1.first_column, @3.last_column); $$->u1.list = $2; }
274         | word EQ {reset_semicount(parseio->scanner);} word SEMI 
275                         {$$=npval(PV_VARDEC,@1.first_line,@5.last_line, @1.first_column, @5.last_column); 
276                                 $$->u1.str = $1; $$->u2.val = $4; }
277         | KW_GOTO target SEMI {$$=npval(PV_GOTO,@1.first_line,@3.last_line, @1.first_column, @3.last_column); $$->u1.list = $2;}
278     | KW_JUMP jumptarget SEMI {$$=npval(PV_GOTO,@1.first_line,@3.last_line, @1.first_column, @3.last_column); $$->u1.list = $2;}
279         | word COLON {$$=npval(PV_LABEL,@1.first_line,@2.last_line, @1.first_column, @2.last_column); $$->u1.str = $1; }
280         | KW_FOR LP {reset_semicount(parseio->scanner);} word SEMI
281                         {reset_semicount(parseio->scanner);} word SEMI
282                         {reset_parencount(parseio->scanner);} word RP statement 
283                                 { $$=npval(PV_FOR,@1.first_line,@12.last_line, @1.first_column, @12.last_column); 
284                                                 $$->u1.for_init = $4; $$->u2.for_test=$7; $$->u3.for_inc = $10; $$->u4.for_statements = $12;}
285         | KW_WHILE LP {reset_parencount(parseio->scanner);} word RP statement 
286                         {$$=npval(PV_WHILE,@1.first_line,@6.last_line, @1.first_column, @6.last_column); 
287                                         $$->u1.str = $4; $$->u2.statements = $6; }
288         | switch_head RC /* empty list OK */ {$$=$1;$$->endline = @2.last_line; $$->endcol = @2.last_column;}
289         | switch_head case_statements RC {$$=$1; $$->u2.statements = $2;$$->endline = @3.last_line; $$->endcol = @3.last_column;}
290         | AMPER macro_call SEMI {$$ = $2;$$->endline = @2.last_line; $$->endcol = @2.last_column;}
291         | application_call SEMI { $$ = $1;$$->endline = @2.last_line; $$->endcol = @2.last_column;}
292     | word SEMI { $$= npval(PV_APPLICATION_CALL,@1.first_line,@2.last_line, @1.first_column, @2.last_column); 
293                                                                                                                                                                                 $$->u1.str = $1;}
294         | application_call EQ {reset_semicount(parseio->scanner);} word SEMI {
295                           char *bufx;
296                                                   int tot=0;
297                                                   pval *pptr;
298                                                   
299                           $$ = npval(PV_VARDEC,@1.first_line,@5.last_line, @1.first_column, @5.last_column);
300                                                   $$->u2.val=$4;
301                                                   /* rebuild the original string-- this is not an app call, it's an unwrapped vardec, with a func call on the LHS */
302                           /* string to big to fit in the buffer? */
303                                                   tot+=strlen($1->u1.str);
304                                                   for(pptr=$1->u2.arglist;pptr;pptr=pptr->next) {
305                                                           tot+=strlen(pptr->u1.str);
306                                                           tot++; /* for a sep like a comma */
307                                                   }
308                                                   tot+=4; /* for safety */
309                                                   bufx = (char *)malloc(tot);
310                                                   strcpy(bufx,$1->u1.str);
311                                                   strcat(bufx,"(");
312                                                   for (pptr=$1->u2.arglist;pptr;pptr=pptr->next) {
313                                                           if ( pptr != $1->u2.arglist )
314                                                                   strcat(bufx,",");
315                                                           strcat(bufx,pptr->u1.str);
316                                                   }
317                                                   strcat(bufx,")");
318 #ifdef AAL_ARGCHECK
319                                                   if ( !ael_is_funcname($1->u1.str) )
320                               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", 
321                                                                           my_file, @1.first_line, @1.first_column, @1.last_column, $1->u1.str);
322 #endif
323                                                   $$->u1.str = bufx;
324                                                   destroy_pval($1); /* the app call it is not, get rid of that chain */
325                                                   prev_word = 0;
326                        }
327         | KW_BREAK SEMI { $$ = npval(PV_BREAK,@1.first_line,@2.last_line, @1.first_column, @2.last_column);}
328         | KW_RETURN SEMI {$$ = npval(PV_RETURN,@1.first_line,@2.last_line, @1.first_column, @2.last_column);}
329         | KW_CONTINUE SEMI {$$ = npval(PV_CONTINUE,@1.first_line,@2.last_line, @1.first_column, @2.last_column);}
330         | random_head statement {$$=$1; $$->u2.statements = $2;$$->endline = @2.last_line; $$->endcol = @2.last_column;}
331         | random_head statement KW_ELSE statement {$$=$1; $$->u2.statements = $2;$$->endline = @2.last_line; $$->endcol = @2.last_column; $$->u3.else_statements = $4;}
332         | if_head statement {$$=$1; $$->u2.statements = $2;$$->endline = @2.last_line; $$->endcol = @2.last_column;}
333         | if_head statement KW_ELSE statement {$$=$1; $$->u2.statements = $2;$$->endline = @2.last_line; $$->endcol = @2.last_column; $$->u3.else_statements = $4;}
334         | iftime_head statement {$$=$1; $$->u2.statements = $2;$$->endline = @2.last_line; $$->endcol = @2.last_column;}
335         | iftime_head statement KW_ELSE statement {$$=$1; $$->u2.statements = $2;$$->endline = @2.last_line; $$->endcol = @2.last_column; $$->u3.else_statements = $4;}
336         | SEMI { $$=0; }
337         ;
338
339 target : goto_word { $$ = npval(PV_WORD,@1.first_line,@1.last_line, @1.first_column, @1.last_column); $$->u1.str = $1;}
340         | goto_word BAR goto_word {$$=npval(PV_WORD,@1.first_line,@1.last_line, @1.first_column, @1.last_column); 
341                                         $$->u1.str = $1; $$->next = npval(PV_WORD,@3.first_line,@3.last_line, @3.first_column, @3.last_column); 
342                                         $$->next->u1.str = $3;}
343         | goto_word COMMA goto_word {$$=npval(PV_WORD,@1.first_line,@1.last_line, @1.first_column, @1.last_column); 
344                                         $$->u1.str = $1; $$->next = npval(PV_WORD,@3.first_line,@3.last_line, @3.first_column, @3.last_column); 
345                                         $$->next->u1.str = $3;}
346         | goto_word BAR goto_word BAR goto_word {$$=npval(PV_WORD,@1.first_line,@1.last_line, @1.first_column, @1.last_column); 
347                                         $$->u1.str = $1; $$->next = npval(PV_WORD,@3.first_line,@3.last_line, @3.first_column, @3.last_column); 
348                                         $$->next->u1.str = $3; 
349                                         $$->next->next = npval(PV_WORD,@5.first_line,@5.last_line, @5.first_column, @5.last_column); 
350                                         $$->next->next->u1.str = $5; }
351         | goto_word COMMA goto_word COMMA goto_word {$$=npval(PV_WORD,@1.first_line,@1.last_line, @1.first_column, @1.last_column); 
352                                         $$->u1.str = $1; $$->next = npval(PV_WORD,@3.first_line,@3.last_line, @3.first_column, @3.last_column); 
353                                         $$->next->u1.str = $3; 
354                                         $$->next->next = npval(PV_WORD,@5.first_line,@5.last_line, @5.first_column, @5.last_column); 
355                                         $$->next->next->u1.str = $5; }
356         | KW_DEFAULT BAR goto_word BAR goto_word {$$=npval(PV_WORD,@1.first_line,@1.last_line, @1.first_column, @1.last_column); 
357                                         $$->u1.str = strdup("default"); $$->next = npval(PV_WORD,@3.first_line,@3.last_line, @3.first_column, @3.last_column); 
358                                         $$->next->u1.str = $3; 
359                                         $$->next->next = npval(PV_WORD,@5.first_line,@5.last_line, @5.first_column, @5.last_column); 
360                                         $$->next->next->u1.str = $5; }
361         | KW_DEFAULT COMMA goto_word COMMA goto_word {$$=npval(PV_WORD,@1.first_line,@1.last_line, @1.first_column, @1.last_column); 
362                                         $$->u1.str = strdup("default"); $$->next = npval(PV_WORD,@3.first_line,@3.last_line, @3.first_column, @3.last_column); 
363                                         $$->next->u1.str = $3; 
364                                         $$->next->next = npval(PV_WORD,@5.first_line,@5.last_line, @5.first_column, @5.last_column); 
365                                         $$->next->next->u1.str = $5; }
366         ;
367
368 jumptarget : goto_word {$$=npval(PV_WORD,@1.first_line,@1.last_line, @1.first_column, @1.last_column); 
369                                         $$->u1.str = $1; $$->next = npval(PV_WORD,@1.first_line,@1.last_line, @1.first_column, @1.last_column); 
370                                         $$->next->u1.str = strdup("1");}  /*  jump extension[,priority][@context] */
371                 | goto_word COMMA goto_word {$$=npval(PV_WORD,@1.first_line,@1.last_line, @1.first_column, @1.last_column); 
372                                         $$->u1.str = $1; $$->next = npval(PV_WORD,@3.first_line,@3.last_line, @3.first_column, @3.last_column); 
373                                         $$->next->u1.str = $3;} 
374                 | goto_word COMMA word AT word {$$=npval(PV_WORD,@1.first_line,@1.last_line, @1.first_column, @1.last_column); 
375                                         $$->u1.str = $5; $$->next = npval(PV_WORD,@3.first_line,@3.last_line, @3.first_column, @3.last_column); 
376                                         $$->next->u1.str = $1; 
377                                         $$->next->next = npval(PV_WORD,@5.first_line,@5.last_line, @5.first_column, @5.last_column); 
378                                         $$->next->next->u1.str = $3; } 
379                 | goto_word AT goto_word {$$=npval(PV_WORD,@1.first_line,@1.last_line, @1.first_column, @1.last_column); 
380                                         $$->u1.str = $3; $$->next = npval(PV_WORD,@3.first_line,@3.last_line, @3.first_column, @3.last_column); 
381                                         $$->next->u1.str = $1; 
382                                         $$->next->next = npval(PV_WORD,@3.first_line,@3.last_line, @3.first_column, @3.last_column); 
383                                         $$->next->next->u1.str = strdup("1"); } 
384                 | goto_word COMMA word AT KW_DEFAULT {$$=npval(PV_WORD,@1.first_line,@1.last_line, @1.first_column, @1.last_column); 
385                                         $$->u1.str = strdup("default"); $$->next = npval(PV_WORD,@3.first_line,@3.last_line, @3.first_column, @3.last_column); 
386                                         $$->next->u1.str = $1; 
387                                         $$->next->next = npval(PV_WORD,@5.first_line,@5.last_line, @5.first_column, @5.last_column); 
388                                         $$->next->next->u1.str = $3; } 
389                 | goto_word AT KW_DEFAULT {$$=npval(PV_WORD,@1.first_line,@1.last_line, @1.first_column, @1.last_column); 
390                                         $$->u1.str = strdup("default"); $$->next = npval(PV_WORD,@3.first_line,@3.last_line, @3.first_column, @3.last_column); 
391                                         $$->next->u1.str = $1; 
392                                         $$->next->next = npval(PV_WORD,@3.first_line,@3.last_line, @3.first_column, @3.last_column); 
393                                         $$->next->next->u1.str = strdup("1"); } 
394                 ;
395
396 macro_call : word LP {reset_argcount(parseio->scanner);} eval_arglist RP 
397                         {$$= npval(PV_MACRO_CALL,@1.first_line,@2.last_line, @1.first_column, @2.last_column); 
398                         $$->u1.str = $1; $$->u2.arglist = $4;}
399         | word LP RP {$$= npval(PV_MACRO_CALL,@1.first_line,@3.last_line, @1.first_column, @3.last_column); $$->u1.str = $1; }
400         ;
401
402 application_call_head: word {reset_argcount(parseio->scanner);} LP  {if (strcasecmp($1,"goto") == 0) {
403                                                                                                                                                                                         $$= npval(PV_GOTO,@1.first_line,@3.last_line, @1.first_column, @3.last_column);
404                                                                                                                                                                                         free($1); /* won't be using this */
405                                                                                                                                                                                         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 );
406                                                                                                                                                                                 } else
407                                                                                                                                                                                         $$= npval(PV_APPLICATION_CALL,@1.first_line,@3.last_line, @1.first_column, @3.last_column); 
408                                                                                                                                                                                 $$->u1.str = $1; }
409         ;
410
411 application_call : application_call_head eval_arglist RP {$$ = $1;
412                 if( $$->type == PV_GOTO )
413                         $$->u1.list = $2;
414                 else
415                         $$->u2.arglist = $2;
416                 $$->endline = @3.last_line; $$->endcol = @3.last_column;}
417         | application_call_head RP {$$=$1;$$->endline = @2.last_line; $$->endcol = @2.last_column;}
418         ;
419
420 eval_arglist :  word_list { $$= npval(PV_WORD,@1.first_line,@1.last_line, @1.first_column, @1.last_column); $$->u1.str = $1;}
421         | /*nothing! */   { $$= npval(PV_WORD,0/*@1.first_line*/,0/*@1.last_line*/,0/* @1.first_column*/, 0/*@1.last_column*/); $$->u1.str = strdup(""); }
422         | 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;}
423         | 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("");}
424         ;
425
426 case_statements: case_statement {$$=$1;}
427         | case_statements case_statement { if ( $1 && $2 ) {$$=$1; linku1($$,$2);} 
428                                                  else if ( $1 ) {$$=$1;}
429                                                  else if ( $2 ) {$$=$2;} }
430         ;
431
432 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;}
433         | 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;}
434         | 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;}
435     | KW_CASE word COLON {$$ = npval(PV_CASE,@1.first_line,@3.last_line, @1.first_column, @3.last_column); $$->u1.str = $2;}
436         | KW_DEFAULT COLON {$$ = npval(PV_DEFAULT,@1.first_line,@2.last_line, @1.first_column, @2.last_column); $$->u1.str = 0;}
437         | KW_PATTERN word COLON  {$$ = npval(PV_PATTERN,@1.first_line,@3.last_line, @1.first_column, @3.last_column); $$->u1.str = $2;}
438         ;
439
440 macro_statements: macro_statement {$$ = $1;}
441         | macro_statements macro_statement { if ( $1 && $2 ) {$$=$1; linku1($$,$2);} 
442                                                  else if ( $1 ) {$$=$1;}
443                                                  else if ( $2 ) {$$=$2;} }
444         ;
445
446 macro_statement : statement {$$=$1;}
447         | 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;}
448         ;
449
450 switches : KW_SWITCHES LC switchlist RC {$$= npval(PV_SWITCHES,@1.first_line,@4.last_line, @1.first_column, @4.last_column); $$->u1.list = $3; }
451         | KW_SWITCHES LC RC /* empty switch list OK */ {$$= npval(PV_SWITCHES,@1.first_line,@3.last_line, @1.first_column, @3.last_column);}
452         ;
453
454 eswitches : KW_ESWITCHES LC switchlist RC {$$= npval(PV_ESWITCHES,@1.first_line,@4.last_line, @1.first_column, @4.last_column); $$->u1.list = $3; }
455         | 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? */
456         ;
457
458 switchlist : word SEMI {$$=npval(PV_WORD,@1.first_line,@2.last_line, @1.first_column, @2.last_column); $$->u1.str = $1;}
459         | 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); }
460         | switchlist error {$$=$1;}
461         ;
462
463 includeslist : includedname SEMI {$$=npval(PV_WORD,@1.first_line,@2.last_line, @1.first_column, @2.last_column); $$->u1.str = $1;}
464         | includedname BAR word3_list COLON word3_list COLON word3_list BAR word3_list BAR word3_list BAR word3_list SEMI {
465                     $$=npval(PV_WORD,@1.first_line,@2.last_line, @1.first_column, @2.last_column); 
466                     $$->u1.str = $1;
467                                         $$->u2.arglist = npval(PV_WORD,@3.first_line,@7.last_line, @3.first_column, @7.last_column); 
468                                         $$->u2.arglist->u1.str = (char*)malloc(strlen($3)+strlen($5)+strlen($7)+4);
469                                         strcpy($$->u2.arglist->u1.str,$3);
470                                         strcat($$->u2.arglist->u1.str,":");
471                                         strcat($$->u2.arglist->u1.str,$5);
472                                         strcat($$->u2.arglist->u1.str,":");
473                                         strcat($$->u2.arglist->u1.str,$7);
474                                         free($3);
475                                         free($5);
476                                         free($7);
477                                         $$->u2.arglist->next = npval(PV_WORD,@9.first_line,@9.last_line, @9.first_column, @9.last_column); 
478                                         $$->u2.arglist->next->u1.str = $9; 
479                                         $$->u2.arglist->next->next = npval(PV_WORD,@11.first_line,@11.last_line, @11.first_column, @11.last_column); 
480                                         $$->u2.arglist->next->next->u1.str = $11; 
481                                         $$->u2.arglist->next->next->next = npval(PV_WORD,@13.first_line,@13.last_line, @13.first_column, @13.last_column); 
482                                         $$->u2.arglist->next->next->next->u1.str = $13; 
483                                         prev_word=0;
484                         }
485         | includedname BAR word BAR word3_list BAR word3_list BAR word3_list SEMI {
486                     $$=npval(PV_WORD,@1.first_line,@2.last_line, @1.first_column, @2.last_column); 
487                     $$->u1.str = $1;
488                                         $$->u2.arglist = npval(PV_WORD,@3.first_line,@3.last_line, @3.first_column, @3.last_column); 
489                                         $$->u2.arglist->u1.str = $3;
490                                         $$->u2.arglist->next = npval(PV_WORD,@5.first_line,@5.last_line, @5.first_column, @5.last_column); 
491                                         $$->u2.arglist->next->u1.str = $5; 
492                                         $$->u2.arglist->next->next = npval(PV_WORD,@7.first_line,@7.last_line, @7.first_column, @7.last_column); 
493                                         $$->u2.arglist->next->next->u1.str = $7; 
494                                         $$->u2.arglist->next->next->next = npval(PV_WORD,@9.first_line,@9.last_line, @9.first_column, @9.last_column); 
495                                         $$->u2.arglist->next->next->next->u1.str = $9; 
496                                         prev_word=0;
497                         }
498         | 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); }
499         | 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); 
500                                         $$=$1; z->u1.str = $2; linku1($$,z);
501                                         z->u2.arglist = npval(PV_WORD,@4.first_line,@4.last_line, @4.first_column, @4.last_column); 
502                                         $$->u2.arglist->u1.str = (char*)malloc(strlen($4)+strlen($6)+strlen($8)+4);
503                                         strcpy($$->u2.arglist->u1.str,$4);
504                                         strcat($$->u2.arglist->u1.str,":");
505                                         strcat($$->u2.arglist->u1.str,$6);
506                                         strcat($$->u2.arglist->u1.str,":");
507                                         strcat($$->u2.arglist->u1.str,$8);
508                                         free($4);
509                                         free($6);
510                                         free($8);
511                                         z->u2.arglist->next = npval(PV_WORD,@10.first_line,@10.last_line, @10.first_column, @10.last_column); 
512                                         z->u2.arglist->next->u1.str = $10; 
513                                         z->u2.arglist->next->next = npval(PV_WORD,@12.first_line,@12.last_line, @12.first_column, @12.last_column); 
514                                         z->u2.arglist->next->next->u1.str = $12; 
515                                         z->u2.arglist->next->next->next = npval(PV_WORD,@14.first_line,@14.last_line, @14.first_column, @14.last_column); 
516                                         z->u2.arglist->next->next->next->u1.str = $14; 
517                                         prev_word=0;
518                         }
519         | includeslist includedname BAR word BAR word3_list BAR word3_list BAR word3_list SEMI
520                    {pval *z = npval(PV_WORD,@2.first_line,@2.last_line, @2.first_column, @3.last_column);
521                                         $$=$1; z->u1.str = $2; linku1($$,z);
522                                         z->u2.arglist = npval(PV_WORD,@4.first_line,@4.last_line, @4.first_column, @4.last_column);
523                                         $$->u2.arglist->u1.str = $4;
524                                         z->u2.arglist->next = npval(PV_WORD,@6.first_line,@6.last_line, @6.first_column, @6.last_column);
525                                         z->u2.arglist->next->u1.str = $6;
526                                         z->u2.arglist->next->next = npval(PV_WORD,@8.first_line,@8.last_line, @8.first_column, @8.last_column);
527                                         z->u2.arglist->next->next->u1.str = $8;
528                                         z->u2.arglist->next->next->next = npval(PV_WORD,@10.first_line,@10.last_line, @10.first_column, @10.last_column);
529                                         z->u2.arglist->next->next->next->u1.str = $10;
530                                         prev_word=0;
531                         }
532         | includeslist error {$$=$1;}
533         ;
534
535 includedname : word { $$ = $1;}
536                         | KW_DEFAULT {$$=strdup("default");}
537                         ;
538
539 includes : KW_INCLUDES LC includeslist RC {$$= npval(PV_INCLUDES,@1.first_line,@4.last_line, @1.first_column, @4.last_column); $$->u1.list = $3;}
540         | KW_INCLUDES LC RC  {$$= npval(PV_INCLUDES,@1.first_line,@3.last_line, @1.first_column, @3.last_column);}
541         ;
542
543
544 %%
545
546 static char *token_equivs1[] = 
547 {
548         "AMPER",
549         "AT",
550         "BAR",
551         "COLON",
552         "COMMA",
553         "EQ",
554         "EXTENMARK",
555         "KW_BREAK", 
556         "KW_CASE", 
557         "KW_CATCH", 
558         "KW_CONTEXT",
559         "KW_CONTINUE", 
560         "KW_DEFAULT", 
561         "KW_ELSE",
562         "KW_ESWITCHES",
563         "KW_FOR", 
564         "KW_GLOBALS",
565         "KW_GOTO",
566         "KW_HINT",
567         "KW_IFTIME",
568         "KW_IF",
569         "KW_IGNOREPAT",
570         "KW_INCLUDES"
571         "KW_JUMP",
572         "KW_MACRO",
573         "KW_PATTERN", 
574         "KW_REGEXTEN", 
575         "KW_RETURN", 
576         "KW_SWITCHES", 
577         "KW_SWITCH",
578         "KW_WHILE", 
579         "LC",
580         "LP",
581         "RC",
582         "RP",
583         "SEMI",
584 };
585
586 static char *token_equivs2[] = 
587 {
588         "&",
589         "@",
590         "|",
591         ":",
592         ",",
593         "=",
594         "=>",
595         "break", 
596         "case", 
597         "catch", 
598         "context",
599         "continue", 
600         "default", 
601         "else",
602         "eswitches",
603         "for", 
604         "globals",
605         "goto",
606         "hint",
607         "ifTime",
608         "if",
609         "ignorepat",
610         "includes"
611         "jump",
612         "macro",
613         "pattern", 
614         "regexten", 
615         "return", 
616         "switches", 
617         "switch",
618         "while", 
619         "{",
620         "(",
621         "}",
622         ")",
623         ";",
624 };
625
626
627 static char *ael_token_subst(char *mess)
628 {
629         /* calc a length, malloc, fill, and return; yyerror had better free it! */
630         int len=0,i;
631         char *p;
632         char *res, *s,*t;
633         int token_equivs_entries = sizeof(token_equivs1)/sizeof(char*);
634
635         for (p=mess; *p; p++) {
636                 for (i=0; i<token_equivs_entries; i++) {
637                         if ( strncmp(p,token_equivs1[i],strlen(token_equivs1[i])) == 0 )
638                         {
639                                 len+=strlen(token_equivs2[i])+2;
640                                 p += strlen(token_equivs1[i])-1;
641                                 break;
642                         }
643                 }
644                 len++;
645         }
646         res = (char*)malloc(len+1);
647         res[0] = 0;
648         s = res;
649         for (p=mess; *p;) {
650                 int found = 0;
651                 for (i=0; i<token_equivs_entries; i++) {
652                         if ( strncmp(p,token_equivs1[i],strlen(token_equivs1[i])) == 0 ) {
653                                 *s++ = '\'';
654                                 for (t=token_equivs2[i]; *t;) {
655                                         *s++ = *t++;
656                                 }
657                                 *s++ = '\'';
658                                 p += strlen(token_equivs1[i]);
659                                 found = 1;
660                                 break;
661                         }
662                 }
663                 if( !found )
664                         *s++ = *p++;
665         }
666         *s++ = 0;
667         return res;
668 }
669
670 void yyerror(YYLTYPE *locp, struct parse_io *parseio,  char const *s)
671 {
672         char *s2 = ael_token_subst((char *)s);
673         if (locp->first_line == locp->last_line) {
674                 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);
675         } else {
676                 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);
677         }
678         free(s2);
679         parseio->syntax_error_count++;
680 }
681
682 struct pval *npval(pvaltype type,int first_line, int last_line, int first_column, int last_column)
683 {
684         extern char *my_file;
685         pval *z = (pval *)calloc(sizeof(struct pval),1);
686         z->type = type;
687         z->startline = first_line;
688         z->endline = last_line;
689         z->startcol = first_column;
690         z->endcol = last_column;
691         z->filename = strdup(my_file);
692         return z;
693 }
694
695 void linku1(pval *head, pval *tail)
696 {
697         if (!head->next) {
698                 head->next = tail;
699                 head->u1_last = tail;
700         } else {
701                 head->u1_last->next = tail;
702                 head->u1_last = tail;
703         }
704 }
705