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