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