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