Merged revisions 79553 via svnmerge from
[asterisk/asterisk.git] / pbx / ael / ael.flex
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2006, Digium, Inc.
5  *
6  * Steve Murphy <murf@parsetree.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18 /*! \file
19  *
20  * \brief Flex scanner description of tokens used in AEL2 .
21  *
22  */
23
24 /*
25  * Start with flex options:
26  *
27  * %x describes the contexts we have: paren, semic and argg, plus INITIAL
28  */
29 %x paren semic argg  comment
30
31 /* prefix used for various globally-visible functions and variables.
32  * This renames also yywrap, but since we do not use it, we just
33  * add option noyywrap to remove it.
34  */
35 %option prefix="ael_yy"
36 %option noyywrap
37
38 /* batch gives a bit more performance if we are using it in
39  * a non-interactive mode. We probably don't care much.
40  */
41 %option batch
42
43 /* outfile is the filename to be used instead of lex.yy.c */
44 %option outfile="ael_lex.c"
45
46 /*
47  * These are not supported in flex 2.5.4, but we need them
48  * at the moment:
49  * reentrant produces a thread-safe parser. Not 100% sure that
50  * we require it, though.
51  * bison-bridge passes an additional yylval argument to yylex().
52  * bison-locations is probably not needed.
53  */
54 %option reentrant
55 %option bison-bridge
56 %option bison-locations
57
58 %{
59 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
60
61 #include <sys/types.h>
62 #include <sys/stat.h>
63 #include <unistd.h>
64
65 #include "asterisk/logger.h"
66 #include "asterisk/utils.h"
67 #include "ael/ael.tab.h"
68 #include "asterisk/ael_structs.h"
69
70 /*
71  * A stack to keep track of matching brackets ( [ { } ] )
72  */
73 static char pbcstack[400];      /* XXX missing size checks */
74 static int pbcpos = 0;
75 static void pbcpush(char x);
76 static int pbcpop(char x);
77
78 static int parencount = 0;
79
80 /*
81  * current line, column and filename, updated as we read the input.
82  */
83 static int my_lineno = 1;       /* current line in the source */
84 static int my_col = 1;          /* current column in the source */
85 char *my_file = 0;              /* used also in the bison code */
86 char *prev_word;                /* XXX document it */
87
88 #define MAX_INCLUDE_DEPTH 50
89
90 /*
91  * flex is not too smart, and generates global functions
92  * without prototypes so the compiler may complain.
93  * To avoid that, we declare the prototypes here,
94  * even though these functions are not used.
95  */
96 int ael_yyget_column  (yyscan_t yyscanner);
97 void ael_yyset_column (int  column_no , yyscan_t yyscanner);
98
99 int ael_yyparse (struct parse_io *);
100
101 /*
102  * A stack to process include files.
103  * As we switch into the new file we need to store the previous
104  * state to restore it later.
105  */
106 struct stackelement {
107         char *fname;
108         int lineno;
109         int colno;
110         YY_BUFFER_STATE bufstate;
111 };
112
113 static struct stackelement  include_stack[MAX_INCLUDE_DEPTH];
114 static int include_stack_index = 0;
115
116 /*
117  * if we use the @n feature of bison, we must supply the start/end
118  * location of tokens in the structure pointed by yylloc.
119  * Simple tokens are just assumed to be on the same line, so
120  * the line number is constant, and the column is incremented
121  * by the length of the token.
122  */
123 #ifdef FLEX_BETA        /* set for 2.5.33 */
124
125 /* compute the total number of lines and columns in the text
126  * passed as argument.
127  */
128 static void pbcwhere(const char *text, int *line, int *col )
129 {
130         int loc_line = *line;
131         int loc_col = *col;
132         char c;
133         while ( (c = *text++) ) {
134                 if ( c == '\t' ) {
135                         loc_col += 8 - (loc_col % 8);
136                 } else if ( c == '\n' ) {
137                         loc_line++;
138                         loc_col = 1;
139                 } else
140                         loc_col++;
141         }
142         *line = loc_line;
143         *col = loc_col;
144 }
145
146 #define STORE_POS do {                                                  \
147                 yylloc->first_line = yylloc->last_line = my_lineno;     \
148                 yylloc->first_column=my_col;                            \
149                 yylloc->last_column=my_col+yyleng-1;                    \
150                 my_col+=yyleng;                                         \
151         } while (0)
152
153 #define STORE_LOC do {                                  \
154                 yylloc->first_line = my_lineno;         \
155                 yylloc->first_column=my_col;            \
156                 pbcwhere(yytext, &my_lineno, &my_col);  \
157                 yylloc->last_line = my_lineno;          \
158                 yylloc->last_column = my_col - 1;       \
159         } while (0)
160 #else
161 #define STORE_POS
162 #define STORE_LOC
163 #endif
164 %}
165
166
167 NOPARENS        ([^()\[\]\{\}]|\\[()\[\]\{\}])*
168
169 NOARGG          ([^(),\{\}\[\]]|\\[,()\[\]\{\}])*
170
171 NOSEMIC         ([^;()\{\}\[\]]|\\[;()\[\]\{\}])*
172
173 %%
174
175 \{              { STORE_POS; return LC;}
176 \}              { STORE_POS; return RC;}
177 \(              { STORE_POS; return LP;}
178 \)              { STORE_POS; return RP;}
179 \;              { STORE_POS; return SEMI;}
180 \=              { STORE_POS; return EQ;}
181 \,              { STORE_POS; return COMMA;}
182 \:              { STORE_POS; return COLON;}
183 \&              { STORE_POS; return AMPER;}
184 \|              { STORE_POS; return BAR;}
185 \=\>            { STORE_POS; return EXTENMARK;}
186 \@              { STORE_POS; return AT;}
187 \/\/[^\n]*      {/*comment*/}
188 context         { STORE_POS; return KW_CONTEXT;}
189 abstract        { STORE_POS; return KW_ABSTRACT;}
190 macro           { STORE_POS; return KW_MACRO;};
191 globals         { STORE_POS; return KW_GLOBALS;}
192 local           { STORE_POS; return KW_LOCAL;}
193 ignorepat       { STORE_POS; return KW_IGNOREPAT;}
194 switch          { STORE_POS; return KW_SWITCH;}
195 if              { STORE_POS; return KW_IF;}
196 ifTime          { STORE_POS; return KW_IFTIME;}
197 random          { STORE_POS; return KW_RANDOM;}
198 regexten        { STORE_POS; return KW_REGEXTEN;}
199 hint            { STORE_POS; return KW_HINT;}
200 else            { STORE_POS; return KW_ELSE;}
201 goto            { STORE_POS; return KW_GOTO;}
202 jump            { STORE_POS; return KW_JUMP;}
203 return          { STORE_POS; return KW_RETURN;}
204 break           { STORE_POS; return KW_BREAK;}
205 continue        { STORE_POS; return KW_CONTINUE;}
206 for             { STORE_POS; return KW_FOR;}
207 while           { STORE_POS; return KW_WHILE;}
208 case            { STORE_POS; return KW_CASE;}
209 default         { STORE_POS; return KW_DEFAULT;}
210 pattern         { STORE_POS; return KW_PATTERN;}
211 catch           { STORE_POS; return KW_CATCH;}
212 switches        { STORE_POS; return KW_SWITCHES;}
213 eswitches       { STORE_POS; return KW_ESWITCHES;}
214 includes        { STORE_POS; return KW_INCLUDES;}
215 "/*"            { BEGIN(comment); my_col += 2; }
216
217 <comment>[^*\n]*        { my_col += yyleng; }
218 <comment>[^*\n]*\n      { ++my_lineno; my_col=1;}
219 <comment>"*"+[^*/\n]*   { my_col += yyleng; }
220 <comment>"*"+[^*/\n]*\n         { ++my_lineno; my_col=1;}
221 <comment>"*/"           { my_col += 2; BEGIN(INITIAL); }
222
223 \n              { my_lineno++; my_col = 1; }
224 [ ]+            { my_col += yyleng; }
225 [\t]+           { my_col += (yyleng*8)-(my_col%8); }
226
227 [-a-zA-Z0-9'"_/.\<\>\*\+!$#\[\]][-a-zA-Z0-9'"_/.!\*\+\<\>\{\}$#\[\]]*   {
228                 STORE_POS;
229                 yylval->str = strdup(yytext);
230                 prev_word = yylval->str;
231                 return word;
232         }
233
234
235
236         /*
237          * context used for arguments of if_head, random_head, switch_head,
238          * for (last statement), while (XXX why not iftime_head ?).
239          * End with the matching parentheses.
240          * A comma at the top level is valid here, unlike in argg where it
241          * is an argument separator so it must be returned as a token.
242          */
243 <paren>{NOPARENS}\)     {
244                 if ( pbcpop(')') ) {    /* error */
245                         STORE_LOC;
246                         ast_log(LOG_ERROR,"File=%s, line=%d, column=%d: Mismatched ')' in expression: %s !\n", my_file, my_lineno, my_col, yytext);
247                         BEGIN(0);
248                         yylval->str = strdup(yytext);
249                         prev_word = 0;
250                         return word;
251                 }
252                 parencount--;
253                 if ( parencount >= 0) {
254                         yymore();
255                 } else {
256                         STORE_LOC;
257                         yylval->str = strdup(yytext);
258                         yylval->str[yyleng-1] = '\0'; /* trim trailing ')' */
259                         unput(')');
260                         BEGIN(0);
261                         return word;
262                 }
263         }
264
265 <paren>{NOPARENS}[\(\[\{]       {
266                 char c = yytext[yyleng-1];
267                 if (c == '(')
268                         parencount++;
269                 pbcpush(c);
270                 yymore();
271         }
272
273 <paren>{NOPARENS}[\]\}] {
274                 char c = yytext[yyleng-1];
275                 if ( pbcpop(c))  { /* error */
276                         STORE_LOC;
277                         ast_log(LOG_ERROR,"File=%s, line=%d, column=%d: Mismatched '%c' in expression!\n",
278                                 my_file, my_lineno, my_col, c);
279                         BEGIN(0);
280                         yylval->str = strdup(yytext);
281                         return word;
282                 }
283                 yymore();
284         }
285
286
287         /*
288          * handlers for arguments to a macro or application calls.
289          * We enter this context when we find the initial '(' and
290          * stay here until we close all matching parentheses,
291          * and find the comma (argument separator) or the closing ')'
292          * of the (external) call, which happens when parencount == 0
293          * before the decrement.
294          */
295 <argg>{NOARGG}[\(\[\{]    {
296                 char c = yytext[yyleng-1];
297                 if (c == '(')
298                         parencount++;
299                 pbcpush(c);
300                 yymore();
301         }
302
303 <argg>{NOARGG}\)        {
304                 if ( pbcpop(')') ) { /* error */
305                         STORE_LOC;
306                         ast_log(LOG_ERROR,"File=%s, line=%d, column=%d: Mismatched ')' in expression!\n", my_file, my_lineno, my_col);
307                         BEGIN(0);
308                         yylval->str = strdup(yytext);
309                         return word;
310                 }
311
312                 parencount--;
313                 if( parencount >= 0){
314                         yymore();
315                 } else {
316                         STORE_LOC;
317                         BEGIN(0);
318                         if ( !strcmp(yytext, ")") )
319                                 return RP;
320                         yylval->str = strdup(yytext);
321                         yylval->str[yyleng-1] = '\0'; /* trim trailing ')' */
322                         unput(')');
323                         return word;
324                 }
325         }
326
327 <argg>{NOARGG}\,        {
328                 if( parencount != 0) { /* printf("Folding in a comma!\n"); */
329                         yymore();
330                 } else  {
331                         STORE_LOC;
332                         if( !strcmp(yytext,"," ) )
333                                 return COMMA;
334                         yylval->str = strdup(yytext);
335                         yylval->str[yyleng-1] = '\0';
336                         unput(',');
337                         return word;
338                 }
339         }
340
341 <argg>{NOARGG}[\]\}]    {
342                 char c = yytext[yyleng-1];
343                 if ( pbcpop(c) ) { /* error */
344                         STORE_LOC;
345                         ast_log(LOG_ERROR,"File=%s, line=%d, column=%d: Mismatched '%c' in expression!\n", my_file, my_lineno, my_col, c);
346                         BEGIN(0);
347                         yylval->str = strdup(yytext);
348                         return word;
349                 }
350                 yymore();
351         }
352
353         /*
354          * context used to find tokens in the right hand side of assignments,
355          * or in the first and second operand of a 'for'. As above, match
356          * commas and use ';' as a separator (hence return it as a separate token).
357          */
358 <semic>{NOSEMIC}[\(\[\{]        {
359                 char c = yytext[yyleng-1];
360                 yymore();
361                 pbcpush(c);
362         }
363
364 <semic>{NOSEMIC}[\)\]\}]        {
365                 char c = yytext[yyleng-1];
366                 if ( pbcpop(c) ) { /* error */
367                         STORE_LOC;
368                         ast_log(LOG_ERROR,"File=%s, line=%d, column=%d: Mismatched '%c' in expression!\n", my_file, my_lineno, my_col, c);
369                         BEGIN(0);
370                         yylval->str = strdup(yytext);
371                         return word;
372                 }
373                 yymore();
374         }
375
376 <semic>{NOSEMIC};       {
377                 STORE_LOC;
378                 yylval->str = strdup(yytext);
379                 yylval->str[yyleng-1] = '\0';
380                 unput(';');
381                 BEGIN(0);
382                 return word;
383         }
384
385 \#include[ \t]+\"[^\"]+\" {
386                 FILE *in1;
387                 char fnamebuf[1024],*p1,*p2;
388                 int error = 1;  /* don't use the file if set */
389                 p1 = strchr(yytext,'"');
390                 p2 = strrchr(yytext,'"');
391                 if ( include_stack_index >= MAX_INCLUDE_DEPTH ) {
392                         ast_log(LOG_ERROR,"File=%s, line=%d, column=%d: Includes nested too deeply! Wow!!! How did you do that?\n", my_file, my_lineno, my_col);
393                 } else if ( (int)(p2-p1) > sizeof(fnamebuf) - 1 ) {
394                         ast_log(LOG_ERROR,"File=%s, line=%d, column=%d: Filename is incredibly way too long (%d chars!). Inclusion ignored!\n", my_file, my_lineno, my_col, yyleng - 10);
395                 } else {
396                         int i;
397                         strncpy(fnamebuf, p1, p2-p1);
398                         fnamebuf[p2-p1] = 0;
399                         for (i=0; i<include_stack_index; i++) {
400                                 if ( !strcmp(fnamebuf,include_stack[i].fname )) {
401                                         ast_log(LOG_ERROR,"File=%s, line=%d, column=%d: Nice Try!!! But %s has already been included (perhaps by another file), and would cause an infinite loop of file inclusions!!! Include directive ignored\n",
402                                                 my_file, my_lineno, my_col, fnamebuf);
403                                         break;
404                                 }
405                         }
406                         if (i == include_stack_index)
407                                 error = 0;      /* we can use this file */
408                 }
409                 if ( !error ) { /* valid file name */
410                         *p2 = 0;
411                         /* relative vs. absolute */
412                         if (*(p1+1) != '/')
413                                 snprintf(fnamebuf, sizeof(fnamebuf), "%s/%s", ast_config_AST_CONFIG_DIR, p1 + 1);
414                         else
415 #if defined(STANDALONE) || defined(LOW_MEMORY) || defined(STANDALONE_AEL)
416                                 strncpy(fnamebuf, p1 + 1, sizeof(fnamebuf) - 1);
417 #else
418                                 ast_copy_string(fnamebuf, p1 + 1, sizeof(fnamebuf));
419 #endif
420                         in1 = fopen( fnamebuf, "r" );
421                         if ( ! in1 ) {
422                                 ast_log(LOG_ERROR,"File=%s, line=%d, column=%d: Couldn't find the include file: %s; ignoring the Include directive!\n", my_file, my_lineno, my_col, fnamebuf);
423                         } else {
424                                 char *buffer;
425                                 struct stat stats;
426                                 stat(fnamebuf, &stats);
427                                 buffer = (char*)malloc(stats.st_size+1);
428                                 fread(buffer, 1, stats.st_size, in1);
429                                 buffer[stats.st_size] = 0;
430                                 ast_log(LOG_NOTICE,"  --Read in included file %s, %d chars\n",fnamebuf, (int)stats.st_size);
431                                 fclose(in1);
432
433                                 include_stack[include_stack_index].fname = my_file;
434                                 my_file = strdup(fnamebuf);
435                                 include_stack[include_stack_index].lineno = my_lineno;
436                                 include_stack[include_stack_index].colno = my_col+yyleng;
437                                 include_stack[include_stack_index++].bufstate = YY_CURRENT_BUFFER;
438
439                                 yy_switch_to_buffer(ael_yy_scan_string (buffer ,yyscanner),yyscanner);
440                                 free(buffer);
441                                 my_lineno = 1;
442                                 my_col = 1;
443                                 BEGIN(INITIAL);
444                         }
445                 }
446         }
447
448 <<EOF>>         {
449                 if ( --include_stack_index < 0 ) {
450                         yyterminate();
451                 } else {
452                         free(my_file);
453                         yy_delete_buffer( YY_CURRENT_BUFFER, yyscanner );
454                         yy_switch_to_buffer(include_stack[include_stack_index].bufstate, yyscanner );
455                         my_lineno = include_stack[include_stack_index].lineno;
456                         my_col    = include_stack[include_stack_index].colno;
457                         my_file   = include_stack[include_stack_index].fname;
458                 }
459         }
460
461 %%
462
463 static void pbcpush(char x)
464 {
465         pbcstack[pbcpos++] = x;
466 }
467
468 static int pbcpop(char x)
469 {
470         if (   ( x == ')' && pbcstack[pbcpos-1] == '(' )
471                 || ( x == ']' && pbcstack[pbcpos-1] == '[' )
472                 || ( x == '}' && pbcstack[pbcpos-1] == '{' )) {
473                 pbcpos--;
474                 return 0;
475         }
476         return 1; /* error */
477 }
478
479 static int c_prevword(void)
480 {
481         char *c = prev_word;
482         if (c == NULL)
483                 return 0;
484         while ( *c ) {
485                 switch (*c) {
486                 case '{':
487                 case '[':
488                 case '(':
489                         pbcpush(*c);
490                         break;
491                 case '}':
492                 case ']':
493                 case ')':
494                         if (pbcpop(*c))
495                                 return 1;
496                         break;
497                 }
498                 c++;
499         }
500         return 0;
501 }
502
503
504 /*
505  * The following three functions, reset_*, are used in the bison
506  * code to switch context. As a consequence, we need to
507  * declare them global and add a prototype so that the
508  * compiler does not complain.
509  *
510  * NOTE: yyg is declared because it is used in the BEGIN macros,
511  * though that should be hidden as the macro changes
512  * depending on the flex options that we use - in particular,
513  * %reentrant changes the way the macro is declared;
514  * without %reentrant, BEGIN uses yystart instead of yyg
515  */
516
517 void reset_parencount(yyscan_t yyscanner );
518 void reset_parencount(yyscan_t yyscanner )
519 {
520         struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
521         parencount = 0;
522         pbcpos = 0;
523         pbcpush('(');   /* push '(' so the last pcbpop (parencount= -1) will succeed */
524         c_prevword();
525         BEGIN(paren);
526 }
527
528 void reset_semicount(yyscan_t yyscanner );
529 void reset_semicount(yyscan_t yyscanner )
530 {
531         struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
532         pbcpos = 0;
533         BEGIN(semic);
534 }
535
536 void reset_argcount(yyscan_t yyscanner );
537 void reset_argcount(yyscan_t yyscanner )
538 {
539         struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
540         parencount = 0;
541         pbcpos = 0;
542         pbcpush('(');   /* push '(' so the last pcbpop (parencount= -1) will succeed */
543         c_prevword();
544         BEGIN(argg);
545 }
546
547 /* used elsewhere, but some local vars */
548 struct pval *ael2_parse(char *filename, int *errors)
549 {
550         struct pval *pval;
551         struct parse_io *io;
552         char *buffer;
553         struct stat stats;
554         FILE *fin;
555
556         /* extern int ael_yydebug; */
557
558         io = calloc(sizeof(struct parse_io),1);
559         /* reset the global counters */
560         prev_word = 0;
561         my_lineno = 1;
562         include_stack_index=0;
563         my_col = 0;
564         /* ael_yydebug = 1; */
565         ael_yylex_init(&io->scanner);
566         fin = fopen(filename,"r");
567         if ( !fin ) {
568                 ast_log(LOG_ERROR,"File %s could not be opened\n", filename);
569                 *errors = 1;
570                 return 0;
571         }
572         my_file = strdup(filename);
573         stat(filename, &stats);
574         buffer = (char*)malloc(stats.st_size+2);
575         fread(buffer, 1, stats.st_size, fin);
576         buffer[stats.st_size]=0;
577         fclose(fin);
578
579         ael_yy_scan_string (buffer ,io->scanner);
580         ael_yyset_lineno(1 , io->scanner);
581
582         /* ael_yyset_in (fin , io->scanner);    OLD WAY */
583
584         ael_yyparse(io);
585
586
587         pval = io->pval;
588         *errors = io->syntax_error_count;
589
590         ael_yylex_destroy(io->scanner);
591         free(buffer);
592         free(io);
593
594         return pval;
595 }