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