fix a bug in computing line numbers
[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
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 #include <sys/types.h>
60 #include <sys/stat.h>
61 #include <unistd.h>
62
63 #include "asterisk.h"
64 #include "asterisk/logger.h"
65 #include "ael/ael.tab.h"
66 #include "asterisk/ael_structs.h"
67
68 /*
69  * A stack to keep track of matching brackets ( [ { } ] )
70  */
71 static char pbcstack[400];      /* XXX missing size checks */
72 static int pbcpos = 0;
73
74 static int parencount = 0;
75 static int commaout = 0;
76
77 /*
78  * current line, column and filename, updated as we read the input.
79  */
80 static int my_lineno = 1;       /* current line in the source */
81 static int my_col = 1;          /* current column in the source */
82 char *my_file = 0;              /* used also in the bison code */
83 char *prev_word;                /* XXX document it */
84
85 #define MAX_INCLUDE_DEPTH 50
86
87 /*
88  * flex is not too smart, and generates global functions
89  * without prototypes so the compiler may complain.
90  * To avoid that, we declare the prototypes here,
91  * even though these functions are not used.
92  */
93 int ael_yyget_column  (yyscan_t yyscanner);
94 void ael_yyset_column (int  column_no , yyscan_t yyscanner);
95
96 int ael_yyparse (struct parse_io *);
97 static void pbcpush(char x);
98 static int pbcpop(char x);
99
100 /*
101  * A stack to process include files.
102  * As we switch into the new file we need to store the previous
103  * state to restore it later.
104  */
105 struct stackelement {
106         char *fname;
107         int lineno;
108         int colno;
109         YY_BUFFER_STATE bufstate;
110 };
111
112 static struct stackelement  include_stack[MAX_INCLUDE_DEPTH];
113 static int include_stack_index = 0;
114
115 /*
116  * if we use the @n feature of bison, we must supply the start/end
117  * location of tokens in the structure pointed by yylloc.
118  * Simple tokens are just assumed to be on the same line, so
119  * the line number is constant, and the column is incremented
120  * by the length of the token.
121  */
122 #ifdef FLEX_BETA        /* set for 2.5.33 */
123
124 /* compute the total number of lines and columns in the text
125  * passed as argument.
126  */
127 static void pbcwhere(const char *text, int *line, int *col )
128 {
129         int loc_line = *line;
130         int loc_col = *col;
131         char c;
132         while ( (c = *text++) ) {
133                 if ( c == '\n' ) {
134                         loc_line++;
135                         loc_col = 0;
136                 }
137                 loc_col++;
138         }
139         *line = loc_line;
140         *col = loc_col;
141 }
142
143 #define STORE_POS do {                                                  \
144                 yylloc->first_line = yylloc->last_line = my_lineno;     \
145                 yylloc->first_column=my_col;                            \
146                 yylloc->last_column=my_col+yyleng-1;                    \
147                 my_col+=yyleng;                                         \
148         } while (0)
149
150 #define STORE_START do {                                \
151                 yylloc->first_line = my_lineno;         \
152                 yylloc->first_column=my_col;            \
153         } while (0)
154
155 #define STORE_END do {                                  \
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_START
163 #define STORE_END
164 #endif
165 %}
166
167
168 NOPARENS        [^()\[\]\{\}]*
169
170 NOARGG          [^(),\{\}\[\]]*
171
172 NOSEMIC         [^;()\{\}\[\]]*
173
174 %%
175
176 \{              { STORE_POS; return LC;}
177 \}              { STORE_POS; return RC;}
178 \(              { STORE_POS; return LP;}
179 \)              { STORE_POS; return RP;}
180 \;              { STORE_POS; return SEMI;}
181 \=              { STORE_POS; return EQ;}
182 \,              { STORE_POS; return COMMA;}
183 \:              { STORE_POS; return COLON;}
184 \&              { STORE_POS; return AMPER;}
185 \|              { STORE_POS; return BAR;}
186 \=\>            { STORE_POS; return EXTENMARK;}
187 \@              { STORE_POS; return AT;}
188 \/\/[^\n]*      {/*comment*/}
189 context         { STORE_POS; return KW_CONTEXT;}
190 abstract        { STORE_POS; return KW_ABSTRACT;}
191 macro           { STORE_POS; return KW_MACRO;};
192 globals         { STORE_POS; return KW_GLOBALS;}
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
216 \n              { my_lineno++; my_col = 1; }
217 [ ]+            { my_col += yyleng; }
218 [\t]+           { my_col += (yyleng*8)-(my_col%8); }
219
220 [-a-zA-Z0-9'"_/.\<\>\*\+!$#\[\]][-a-zA-Z0-9'"_/.!\*\+\<\>\{\}$#\[\]]*   {
221                 STORE_POS;
222                 yylval->str = strdup(yytext);
223                 prev_word = yylval->str;
224                 return word;
225         }
226
227
228
229
230 <paren>{NOPARENS}\)     {
231                 STORE_START;
232                 if ( pbcpop(')') ) {    /* error */
233                         STORE_END;
234                         ast_log(LOG_ERROR,"File=%s, line=%d, column=%d: Mismatched ')' in expression: %s !\n", my_file, my_lineno, my_col, yytext);
235                         BEGIN(0);
236                         yylval->str = strdup(yytext);
237                         prev_word = 0;
238                         return word;
239                 }
240                 parencount--;
241                 if ( parencount >= 0) {
242                         yymore();
243                 } else {
244                         STORE_END;
245                         yylval->str = strdup(yytext);
246                         *(yylval->str+strlen(yylval->str)-1)=0;
247                         /* printf("Got paren word %s\n", yylval->str); */
248                         unput(')');
249                         BEGIN(0);
250                         return word;
251                 }
252         }
253
254 <paren>{NOPARENS}[\(\[\{]       {
255                 char c = yytext[yyleng-1];
256                 // STORE_START;
257                 if (c == '(')
258                         parencount++;
259                 pbcpush(c);
260                 yymore();
261         }
262
263 <paren>{NOPARENS}[\]\}] {
264                 char c = yytext[yyleng-1];
265                 STORE_START;
266                 if ( pbcpop(c))  { /* error */
267                         STORE_END;
268                         ast_log(LOG_ERROR,"File=%s, line=%d, column=%d: Mismatched '%c' in expression!\n",
269                                 my_file, my_lineno, my_col, c);
270                         BEGIN(0);
271                         yylval->str = strdup(yytext);
272                         return word;
273                 }
274                 yymore();
275         }
276
277 <argg>{NOARGG}[\(\[\{]    {
278                 char c = yytext[yyleng-1];
279                 STORE_START;
280                 if (c == '(')
281                         parencount++;
282                 pbcpush(c);
283                 yymore();
284         }
285
286 <argg>{NOARGG}\)        {
287                 STORE_START;
288                 if ( pbcpop(')') ) { /* error */
289                         STORE_END;
290                         ast_log(LOG_ERROR,"File=%s, line=%d, column=%d: Mismatched ')' in expression!\n", my_file, my_lineno, my_col);
291                         BEGIN(0);
292                         yylval->str = strdup(yytext);
293                         return word;
294                 }
295
296                 parencount--;
297                 if( parencount >= 0){
298                         yymore();
299                 } else {
300                         STORE_END;
301                         yylval->str = strdup(yytext);
302                         if(yyleng > 1 )
303                                 *(yylval->str+yyleng-1)=0;
304                         BEGIN(0);
305                         if ( !strcmp(yylval->str,")") ) {
306                                 free(yylval->str);
307                                 yylval->str = 0;
308                                 my_col++; /* XXX why ? */
309                                 return RP;
310                         } else {
311                                 unput(')');
312                                 return word;
313                         }
314                 }
315         }
316
317 <argg>{NOARGG}\,        {
318                 if( parencount != 0) {
319                         /* printf("Folding in a comma!\n"); */
320                         yymore();
321                 } else  {
322                         STORE_START;
323                         STORE_END;
324                         if( !commaout ) {
325                                 if( !strcmp(yytext,"," ) ) {
326                                         commaout = 0;
327                                         my_col+=1;
328                                         return COMMA;
329                                 }
330                                 yylval->str = strdup(yytext);
331                                 /* printf("Got argg2 word %s\n", yylval->str); */
332                                 unput(',');
333                                 commaout = 1;
334                                 if (yyleng > 1 )
335                                         *(yylval->str+yyleng-1)=0;
336                                 return word;
337                         } else {
338                                 commaout = 0;
339                                 my_col+=1;
340                                 return COMMA;
341                         }
342                 }
343         }
344
345 <argg>{NOARGG}[\]\}]    {
346                 char c = yytext[yyleng-1];
347                 STORE_START;
348                 if ( pbcpop(c) ) { /* error */
349                         STORE_END;
350                         ast_log(LOG_ERROR,"File=%s, line=%d, column=%d: Mismatched '%c' in expression!\n", my_file, my_lineno, my_col, c);
351                         BEGIN(0);
352                         yylval->str = strdup(yytext);
353                         return word;
354                 }
355                 yymore();
356         }
357
358
359
360 <semic>{NOSEMIC}[\(\[\{]        {
361                 char c = yytext[yyleng-1];
362                 STORE_START;
363                 yymore();
364                 pbcpush(c);
365         }
366
367 <semic>{NOSEMIC}[\)\]\}]        {
368                 char c = yytext[yyleng-1];
369                 STORE_START;
370                 if ( pbcpop(c) ) { /* error */
371                         STORE_END;
372                         ast_log(LOG_ERROR,"File=%s, line=%d, column=%d: Mismatched '%c' in expression!\n", my_file, my_lineno, my_col, c);
373                         BEGIN(0);
374                         yylval->str = strdup(yytext);
375                         return word;
376                 }
377                 yymore();
378         }
379
380 <semic>{NOSEMIC};       {
381                 STORE_START;
382                 STORE_END;
383                 yylval->str = strdup(yytext);
384                 if(yyleng > 1)
385                         *(yylval->str+yyleng-1)=0;
386                 unput(';');
387                 BEGIN(0);
388                 return word;
389         }
390
391 \#include[ \t]+\"[^\"]+\" {
392                 FILE *in1;
393                 char fnamebuf[1024],*p1,*p2;
394                 int error = 1;  /* don't use the file if set */
395                 p1 = strchr(yytext,'"');
396                 p2 = strrchr(yytext,'"');
397                 if ( include_stack_index >= MAX_INCLUDE_DEPTH ) {
398                         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);
399                 } else if ( (int)(p2-p1) > sizeof(fnamebuf) - 1 ) {
400                         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);
401                 } else {
402                         int i;
403                         strncpy(fnamebuf, p1, p2-p1);
404                         fnamebuf[p2-p1] = 0;
405                         for (i=0; i<include_stack_index; i++) {
406                                 if ( !strcmp(fnamebuf,include_stack[i].fname )) {
407                                         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",
408                                                 my_file, my_lineno, my_col, fnamebuf);
409                                         break;
410                                 }
411                         }
412                         if (i == include_stack_index)
413                                 error = 0;      /* we can use this file */
414                 }
415                 if ( !error ) { /* valid file name */
416                         *p2 = 0;
417                         /* relative vs. absolute */
418                         if ( *(p1+1) != '/' ) {
419                                 /* XXX must check overflows */
420                                 strcpy(fnamebuf,ast_config_AST_CONFIG_DIR);
421                                 strcat(fnamebuf,"/");
422                                 strcat(fnamebuf,p1+1);
423                         } else
424                                 strcpy(fnamebuf,p1+1);
425                         in1 = fopen( fnamebuf, "r" );
426                         if ( ! in1 ) {
427                                 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);
428                         } else {
429                                 char *buffer;
430                                 struct stat stats;
431                                 stat(fnamebuf, &stats);
432                                 buffer = (char*)malloc(stats.st_size+1);
433                                 fread(buffer, 1, stats.st_size, in1);
434                                 buffer[stats.st_size] = 0;
435                                 ast_log(LOG_NOTICE,"  --Read in included file %s, %d chars\n",fnamebuf, (int)stats.st_size);
436                                 fclose(in1);
437
438                                 include_stack[include_stack_index].fname = my_file;
439                                 my_file = strdup(fnamebuf);
440                                 include_stack[include_stack_index].lineno = my_lineno;
441                                 include_stack[include_stack_index].colno = my_col+yyleng;
442                                 include_stack[include_stack_index++].bufstate = YY_CURRENT_BUFFER;
443
444                                 yy_switch_to_buffer(ael_yy_scan_string (buffer ,yyscanner),yyscanner);
445                                 free(buffer);
446                                 my_lineno = 1;
447                                 my_col = 1;
448                                 BEGIN(INITIAL);
449                         }
450                 }
451         }
452
453 <<EOF>>         {
454                 if ( --include_stack_index < 0 ) {
455                         yyterminate();
456                 } else {
457                         free(my_file);
458                         yy_delete_buffer( YY_CURRENT_BUFFER, yyscanner );
459                         yy_switch_to_buffer(include_stack[include_stack_index].bufstate, yyscanner );
460                         my_lineno = include_stack[include_stack_index].lineno;
461                         my_col    = include_stack[include_stack_index].colno;
462                         my_file   = include_stack[include_stack_index].fname;
463                 }
464         }
465
466 %%
467
468 static void pbcpush(char x)
469 {
470         pbcstack[pbcpos++] = x;
471 }
472
473 static int pbcpop(char x)
474 {
475         if (   ( x == ')' && pbcstack[pbcpos-1] == '(' )
476                 || ( x == ']' && pbcstack[pbcpos-1] == '[' )
477                 || ( x == '}' && pbcstack[pbcpos-1] == '{' )) {
478                 pbcpos--;
479                 return 0;
480         }
481         return 1; /* error */
482 }
483
484 static int c_prevword(void)
485 {
486         char *c = prev_word;
487         if (c == NULL)
488                 return 0;
489         while ( *c ) {
490                 switch (*c) {
491                 case '{':
492                 case '[':
493                 case '(':
494                         pbcpush(*c);
495                         break;
496                 case '}':
497                 case ']':
498                 case ')':
499                         if (pbcpop(*c))
500                                 return 1;
501                         break;
502                 }
503                 c++;
504         }
505         return 0;
506 }
507
508
509 /* used by the bison code */
510 void reset_parencount(yyscan_t yyscanner );
511 void reset_parencount(yyscan_t yyscanner )
512 {
513         struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
514         parencount = 0;
515         pbcpos = 0;
516         pbcpush('(');
517         c_prevword();
518         BEGIN(paren);
519 }
520
521 /* used by the bison code */
522 void reset_semicount(yyscan_t yyscanner );
523 void reset_semicount(yyscan_t yyscanner )
524 {
525         struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
526         pbcpos = 0;
527         BEGIN(semic);
528 }
529
530 /* used by the bison code */
531 void reset_argcount(yyscan_t yyscanner );
532 void reset_argcount(yyscan_t yyscanner )
533 {
534         struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
535         parencount = 0;
536         pbcpos = 0;
537         commaout = 0;
538         pbcpush('(');
539         c_prevword();
540         BEGIN(argg);
541 }
542
543 /* used elsewhere, but some local vars */
544 struct pval *ael2_parse(char *filename, int *errors)
545 {
546         struct pval *pval;
547         struct parse_io *io;
548         char *buffer;
549         struct stat stats;
550         FILE *fin;
551
552         /* extern int ael_yydebug; */
553
554         io = calloc(sizeof(struct parse_io),1);
555         /* reset the global counters */
556         prev_word = 0;
557         my_lineno = 1;
558         include_stack_index=0;
559         my_col = 0;
560         /* ael_yydebug = 1; */
561         ael_yylex_init(&io->scanner);
562         fin = fopen(filename,"r");
563         if ( !fin ) {
564                 ast_log(LOG_ERROR,"File %s could not be opened\n", filename);
565                 *errors = 1;
566                 return 0;
567         }
568         my_file = strdup(filename);
569         stat(filename, &stats);
570         buffer = (char*)malloc(stats.st_size+2);
571         fread(buffer, 1, stats.st_size, fin);
572         buffer[stats.st_size]=0;
573         fclose(fin);
574
575         ael_yy_scan_string (buffer ,io->scanner);
576         ael_yyset_lineno(1 , io->scanner);
577
578         /* ael_yyset_in (fin , io->scanner);    OLD WAY */
579
580         ael_yyparse(io);
581
582
583         pval = io->pval;
584         *errors = io->syntax_error_count;
585
586         ael_yylex_destroy(io->scanner);
587         free(buffer);
588         free(io);
589
590         return pval;
591 }