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