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