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