Merge Steve Murphy's (murf) complete re-implementation of AEL, which is now no longer...
[asterisk/asterisk.git] / ast_expr2.fl
1 %{
2 /*
3  * Asterisk -- An open source telephony toolkit.
4  *
5  * Copyright (C) 1999 - 2006, Digium, Inc.
6  *
7  * Mark Spencer <markster@digium.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
20 /*! \file
21  *
22  * \brief Dialplan Expression Lexical Scanner
23  */
24
25 #include <sys/types.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <locale.h>
30 #include <ctype.h>
31 #if !defined(SOLARIS) && !defined(__CYGWIN__)
32 #include <err.h>
33 #else
34 #define quad_t int64_t
35 #endif
36 #include <errno.h>
37 #include <regex.h>
38 #include <limits.h>
39
40 #include "asterisk.h"
41
42 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
43
44 #include "asterisk/ast_expr.h"
45 #include "asterisk/logger.h"
46 #include "asterisk/strings.h"
47
48 enum valtype {
49         AST_EXPR_integer, AST_EXPR_numeric_string, AST_EXPR_string
50 } ;
51
52 struct val {
53         enum valtype type;
54         union {
55                 char *s;
56                 quad_t i;
57         } u;
58 } ;
59
60 #include "ast_expr2.h" /* the o/p of the bison on ast_expr2.y */
61
62 #define SET_COLUMNS yylloc_param->first_column = (int)(yyg->yytext_r - YY_CURRENT_BUFFER_LVALUE->yy_ch_buf);yylloc_param->last_column = yylloc_param->last_column + yyleng - 1; yylloc_param->first_line = yylloc_param->last_line = 1
63 #define SET_STRING yylval_param->val = (struct val *)calloc(sizeof(struct val),1); yylval_param->val->type = AST_EXPR_string; yylval_param->val->u.s = strdup(yytext);
64 #define SET_NUMERIC_STRING yylval_param->val = (struct val *)calloc(sizeof(struct val),1); yylval_param->val->type = AST_EXPR_numeric_string; yylval_param->val->u.s = strdup(yytext);
65
66 struct parse_io
67 {
68         char *string;
69         struct val *val;
70         yyscan_t scanner;
71 };
72  
73 void ast_yyset_column(int column_no, yyscan_t yyscanner);
74 int ast_yyget_column(yyscan_t yyscanner);
75 static int curlycount = 0;
76 static char *expr2_token_subst(char *mess);
77 %}
78
79 %option prefix="ast_yy"
80 %option batch
81 %option outfile="ast_expr2f.c"
82 %option reentrant
83 %option bison-bridge
84 %option bison-locations
85 %option noyywrap
86 %x var trail
87
88 %%
89
90 \|      { SET_COLUMNS; SET_STRING; return TOK_OR;}
91 \&      { SET_COLUMNS; SET_STRING; return TOK_AND;}
92 \=      { SET_COLUMNS; SET_STRING; return TOK_EQ;}
93 \|\|    { SET_COLUMNS; SET_STRING; return TOK_OR;}
94 \&\&    { SET_COLUMNS; SET_STRING; return TOK_AND;}
95 \=\=    { SET_COLUMNS; SET_STRING; return TOK_EQ;}
96 \=~     { SET_COLUMNS; SET_STRING; return TOK_EQTILDE;}
97 \>      { SET_COLUMNS; SET_STRING; return TOK_GT;}
98 \<      { SET_COLUMNS; SET_STRING; return TOK_LT;}
99 \>\=    { SET_COLUMNS; SET_STRING; return TOK_GE;}
100 \<\=    { SET_COLUMNS; SET_STRING; return TOK_LE;}
101 \!\=    { SET_COLUMNS; SET_STRING; return TOK_NE;}
102 \+      { SET_COLUMNS; SET_STRING; return TOK_PLUS;}
103 \-      { SET_COLUMNS; SET_STRING; return TOK_MINUS;}
104 \*      { SET_COLUMNS; SET_STRING; return TOK_MULT;}
105 \/      { SET_COLUMNS; SET_STRING; return TOK_DIV;}
106 \%      { SET_COLUMNS; SET_STRING; return TOK_MOD;}
107 \?      { SET_COLUMNS; SET_STRING; return TOK_COND;}
108 \!      { SET_COLUMNS; SET_STRING; return TOK_COMPL;}
109 \:      { SET_COLUMNS; SET_STRING; return TOK_COLON;}
110 \:\:    { SET_COLUMNS; SET_STRING; return TOK_COLONCOLON;}
111 \(      { SET_COLUMNS; SET_STRING; return TOK_LP;}
112 \)      { SET_COLUMNS; SET_STRING; return TOK_RP;}
113 \$\{   {/* gather the contents of ${} expressions, with trailing stuff, into a single TOKEN. They are much more complex now than they used to be */
114                        curlycount = 0; BEGIN(var); yymore();}
115
116 [       \r]             {}
117 \"[^"]*\"   {SET_COLUMNS; SET_STRING; return TOKEN;}
118
119 [\n]    {/* what to do with eol */}
120 [0-9]+          {   SET_COLUMNS;  /* the original behavior of the expression parser was to bring in numbers as a numeric string */
121                                 SET_NUMERIC_STRING;
122                                 return TOKEN;}
123
124 [a-zA-Z0-9,.';\\_^$#@]+ {SET_COLUMNS; SET_STRING; return TOKEN;}
125
126 <var>[^{}]*\}  {curlycount--; if(curlycount < 0){ BEGIN(trail);  yymore();} else {  yymore();}}
127 <var>[^{}]*\{  {curlycount++; yymore();  }
128 <trail>[^-\t\r \n$():?%/+=*<>!|&]* {BEGIN(0); SET_COLUMNS; SET_STRING; return TOKEN;}
129 <trail>[-\t\r \n$():?%/+=*<>!|&]        {char c = yytext[yyleng-1]; BEGIN(0); unput(c); SET_COLUMNS; SET_STRING; return TOKEN;}
130 <trail>\$\{            {curlycount = 0; BEGIN(var); yymore();  }
131 <trail><<EOF>>          {BEGIN(0); SET_COLUMNS; SET_STRING; return TOKEN; /*actually, if an expr is only a variable ref, this could happen a LOT */}
132
133 %%
134
135 /* I'm putting the interface routine to the whole parse here in the flexer input file
136    mainly because of all the flexer initialization that has to be done. Shouldn't matter
137    where it is, as long as it's somewhere. I didn't want to define a prototype for the
138    ast_yy_scan_string in the .y file, because then, I'd have to define YY_BUFFER_STATE there...
139         UGH! that would be inappropriate. */
140
141 int ast_yyparse(void *); /* need to/should define this prototype for the call to yyparse */
142 int ast_yyerror(const char *, YYLTYPE *, struct parse_io *); /* likewise */
143
144 int ast_expr(char *expr, char *buf, int length)
145 {
146         struct parse_io io;
147         int return_value = 0;
148         
149         memset(&io, 0, sizeof(io));
150         io.string = expr;  /* to pass to the error routine */
151         
152         ast_yylex_init(&io.scanner);
153         
154         ast_yy_scan_string(expr, io.scanner);
155         
156         ast_yyparse ((void *) &io);
157
158         ast_yylex_destroy(io.scanner);
159
160         if (!io.val) {
161                 if (length > 1) {
162                         strcpy(buf, "0");
163                         return_value = 1;
164                 }
165         } else {
166                 if (io.val->type == AST_EXPR_integer) {
167                         int res_length;
168
169                         res_length = snprintf(buf, length, "%ld", (long int) io.val->u.i);
170                         return_value = (res_length <= length) ? res_length : length;
171                 } else {
172 #ifdef STANDALONE
173                         strncpy(buf, io.val->u.s, length - 1);
174 #else /* !STANDALONE */
175                         ast_copy_string(buf, io.val->u.s, length);
176 #endif /* STANDALONE */
177                         return_value = strlen(buf);
178                         free(io.val->u.s);
179                 }
180                 free(io.val);
181         }
182         return return_value;
183 }
184
185
186 char extra_error_message[4095];
187 int extra_error_message_supplied = 0;
188 void  ast_expr_register_extra_error_info(char *message);
189 void  ast_expr_clear_extra_error_info(void);
190
191 void  ast_expr_register_extra_error_info(char *message)
192 {
193        extra_error_message_supplied=1;
194        strcpy(extra_error_message, message);
195 }
196
197 void  ast_expr_clear_extra_error_info(void)
198 {
199        extra_error_message_supplied=0;
200        extra_error_message[0] = 0;
201 }
202
203 static char *expr2_token_equivs1[] = 
204 {
205         "TOKEN",
206         "TOK_COND",
207         "TOK_COLONCOLON",
208         "TOK_OR",
209         "TOK_AND",
210         "TOK_EQ",
211         "TOK_GT",
212         "TOK_LT",
213         "TOK_GE",
214         "TOK_LE",
215         "TOK_NE",
216         "TOK_PLUS",
217         "TOK_MINUS",
218         "TOK_MULT",
219         "TOK_DIV",
220         "TOK_MOD",
221         "TOK_COMPL",
222         "TOK_COLON",
223         "TOK_EQTILDE",
224         "TOK_RP",
225         "TOK_LP"
226 };
227
228 static char *expr2_token_equivs2[] = 
229 {
230         "<token>",
231         "?",
232         "::",
233         "|",
234         "&",
235         "=",
236         ">",
237         "<",
238         ">=",
239         "<=",
240         "!=",
241         "+",
242         "-",
243         "*",
244         "/",
245         "%",
246         "!",
247         ":",
248         "=~",
249         ")",
250         "("
251 };
252
253
254 static char *expr2_token_subst(char *mess)
255 {
256         /* calc a length, malloc, fill, and return; yyerror had better free it! */
257         int len=0,i;
258         char *p;
259         char *res, *s,*t;
260         int expr2_token_equivs_entries = sizeof(expr2_token_equivs1)/sizeof(char*);
261
262         for (p=mess; *p; p++) {
263                 for (i=0; i<expr2_token_equivs_entries; i++) {
264                         if ( strncmp(p,expr2_token_equivs1[i],strlen(expr2_token_equivs1[i])) == 0 )
265                         {
266                                 len+=strlen(expr2_token_equivs2[i])+2;
267                                 p += strlen(expr2_token_equivs1[i])-1;
268                                 break;
269                         }
270                 }
271                 len++;
272         }
273         res = (char*)malloc(len+1);
274         res[0] = 0;
275         s = res;
276         for (p=mess; *p;) {
277                 int found = 0;
278                 for (i=0; i<expr2_token_equivs_entries; i++) {
279                         if ( strncmp(p,expr2_token_equivs1[i],strlen(expr2_token_equivs1[i])) == 0 ) {
280                                 *s++ = '\'';
281                                 for (t=expr2_token_equivs2[i]; *t;) {
282                                         *s++ = *t++;
283                                 }
284                                 *s++ = '\'';
285                                 p += strlen(expr2_token_equivs1[i]);
286                                 found = 1;
287                                 break;
288                         }
289                 }
290                 if( !found )
291                         *s++ = *p++;
292         }
293         *s++ = 0;
294         return res;
295 }
296
297 int ast_yyerror (const char *s,  yyltype *loc, struct parse_io *parseio )
298 {       
299         struct yyguts_t * yyg = (struct yyguts_t*)(parseio->scanner);
300         char spacebuf[8000]; /* best safe than sorry */
301         char spacebuf2[8000]; /* best safe than sorry */
302         int i=0;
303         char *s2 = expr2_token_subst((char *)s);
304         spacebuf[0] = 0;
305         
306         for(i=0;i< (int)(yytext - YY_CURRENT_BUFFER_LVALUE->yy_ch_buf);i++) spacebuf2[i] = ' ';  /* uh... assuming yyg is defined, then I can use the yycolumn macro,
307                                                                                                                                                                                                 which is the same thing as... get this:
308                                                                                                         yyg->yy_buffer_stack[yyg->yy_buffer_stack_top]->yy_bs_column
309                                                                                                         I was tempted to just use yy_buf_pos in the STATE, but..., well:
310                                                                                                                 a. the yy_buf_pos is the current position in the buffer, which
311                                                                                                                         may not relate to the entire string/buffer because of the
312                                                                                                                         buffering.
313                                                                                                                 b. but, analysis of the situation is that when you use the
314                                                                                                                         yy_scan_string func, it creates a single buffer the size of
315                                                                                                                         string, so the two would be the same... 
316                                                                                                         so, in the end, the yycolumn macro is available, shorter, therefore easier. */
317         spacebuf2[i++]='^';
318         spacebuf2[i]= 0;
319
320 #ifdef STANDALONE3
321         /* easier to read in the standalone version */
322         printf("ast_yyerror(): %s syntax error: %s; Input:\n%s\n%s\n",  
323                         (extra_error_message_supplied?extra_error_message:""), s2, parseio->string,spacebuf2);
324 #else
325         ast_log(LOG_WARNING,"ast_yyerror(): %s syntax error: %s; Input:\n%s\n%s\n",  
326                         (extra_error_message_supplied?extra_error_message:""), s2, parseio->string,spacebuf2);
327 #endif
328 #ifndef STANDALONE
329         ast_log(LOG_WARNING,"If you have questions, please refer to doc/channelvariables.txt in the asterisk source.\n");
330 #endif
331         free(s2);
332         return(0);
333 }