aboutsummaryrefslogtreecommitdiffstats
path: root/trunk/res/ael/ael.y
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/res/ael/ael.y')
-rw-r--r--trunk/res/ael/ael.y823
1 files changed, 823 insertions, 0 deletions
diff --git a/trunk/res/ael/ael.y b/trunk/res/ael/ael.y
new file mode 100644
index 000000000..a8df1cb51
--- /dev/null
+++ b/trunk/res/ael/ael.y
@@ -0,0 +1,823 @@
+%{
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2006, Digium, Inc.
+ *
+ * Steve Murphy <murf@parsetree.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+/*! \file
+ *
+ * \brief Bison Grammar description of AEL2.
+ *
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "asterisk/logger.h"
+#include "asterisk/ael_structs.h"
+
+pval * linku1(pval *head, pval *tail);
+static void set_dads(pval *dad, pval *child_list);
+void reset_parencount(yyscan_t yyscanner);
+void reset_semicount(yyscan_t yyscanner);
+void reset_argcount(yyscan_t yyscanner );
+
+#define YYLEX_PARAM ((struct parse_io *)parseio)->scanner
+#define YYERROR_VERBOSE 1
+
+extern char *my_file;
+#ifdef AAL_ARGCHECK
+int ael_is_funcname(char *name);
+#endif
+static char *ael_token_subst(const char *mess);
+
+%}
+
+
+%union {
+ int intval; /* integer value, typically flags */
+ char *str; /* strings */
+ struct pval *pval; /* full objects */
+}
+
+%{
+ /* declaring these AFTER the union makes things a lot simpler! */
+void yyerror(YYLTYPE *locp, struct parse_io *parseio, char const *s);
+int ael_yylex (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , void * yyscanner);
+
+/* create a new object with start-end marker */
+pval *npval(pvaltype type, int first_line, int last_line,
+ int first_column, int last_column);
+
+/* create a new object with start-end marker, simplified interface.
+ * Must be declared here because YYLTYPE is not known before
+ */
+static pval *npval2(pvaltype type, YYLTYPE *first, YYLTYPE *last);
+
+/* another frontend for npval, this time for a string */
+static pval *nword(char *string, YYLTYPE *pos);
+
+/* update end position of an object, return the object */
+static pval *update_last(pval *, YYLTYPE *);
+%}
+
+
+%token KW_CONTEXT LC RC LP RP SEMI EQ COMMA COLON AMPER BAR AT
+%token KW_MACRO KW_GLOBALS KW_IGNOREPAT KW_SWITCH KW_IF KW_IFTIME KW_ELSE KW_RANDOM KW_ABSTRACT KW_EXTEND
+%token EXTENMARK KW_GOTO KW_JUMP KW_RETURN KW_BREAK KW_CONTINUE KW_REGEXTEN KW_HINT
+%token KW_FOR KW_WHILE KW_CASE KW_PATTERN KW_DEFAULT KW_CATCH KW_SWITCHES KW_ESWITCHES
+%token KW_INCLUDES KW_LOCAL
+
+%right BAR COMMA
+
+%token <str> word
+
+%type <pval>includes
+%type <pval>includeslist
+%type <pval>switchlist
+%type <pval>eswitches
+%type <pval>switches
+%type <pval>macro_statement
+%type <pval>macro_statements
+%type <pval>case_statement
+%type <pval>case_statements
+%type <pval>eval_arglist
+%type <pval>application_call
+%type <pval>application_call_head
+%type <pval>macro_call
+%type <pval>target jumptarget
+%type <pval>statement
+%type <pval>switch_statement
+
+%type <pval>if_like_head
+%type <pval>statements
+%type <pval>extension
+%type <pval>ignorepat
+%type <pval>element
+%type <pval>elements
+%type <pval>arglist
+%type <pval>assignment
+%type <pval>local_assignment
+%type <pval>global_statements
+%type <pval>globals
+%type <pval>macro
+%type <pval>context
+%type <pval>object
+%type <pval>objects
+%type <pval>file
+/* XXX lr changes */
+%type <pval>opt_else
+%type <pval>timespec
+%type <pval>included_entry
+
+%type <str>opt_word
+%type <str>context_name
+%type <str>timerange
+
+%type <str>goto_word
+%type <str>word_list
+%type <str>word3_list hint_word
+%type <str>test_expr
+%type <str>opt_pri
+
+%type <intval>opt_abstract
+
+/*
+ * OPTIONS
+ */
+
+%locations /* track source location using @n variables (yylloc in flex) */
+%pure-parser /* pass yylval and yylloc as arguments to yylex(). */
+%name-prefix="ael_yy"
+/*
+ * add an additional argument, parseio, to yyparse(),
+ * which is then accessible in the grammar actions
+ */
+%parse-param {struct parse_io *parseio}
+
+/* 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
+ the default action to shift will attach the else to the preceeding if. */
+%expect 3
+%error-verbose
+
+/*
+ * declare destructors for objects.
+ * The former is for pval, the latter for strings.
+ * NOTE: we must not have a destructor for a 'file' object.
+ */
+%destructor {
+ destroy_pval($$);
+ prev_word=0;
+ } includes includeslist switchlist eswitches switches
+ macro_statement macro_statements case_statement case_statements
+ eval_arglist application_call application_call_head
+ macro_call target jumptarget statement switch_statement
+ if_like_head statements extension
+ ignorepat element elements arglist assignment local_assignment
+ global_statements globals macro context object objects
+ opt_else
+ timespec included_entry
+
+%destructor { free($$);} word word_list goto_word word3_list opt_word context_name
+ timerange
+ test_expr
+ opt_pri
+
+
+%%
+
+file : objects { $$ = parseio->pval = $1; }
+ ;
+
+objects : object {$$=$1;}
+ | objects object { $$ = linku1($1, $2); }
+ | objects error {$$=$1;}
+ ;
+
+object : context {$$=$1;}
+ | macro {$$=$1;}
+ | globals {$$=$1;}
+ | SEMI {$$=0;/* allow older docs to be read */}
+ ;
+
+context_name : word { $$ = $1; }
+ | KW_DEFAULT { $$ = strdup("default"); }
+ ;
+
+context : opt_abstract KW_CONTEXT context_name LC elements RC {
+ $$ = npval2(PV_CONTEXT, &@1, &@6);
+ $$->u1.str = $3;
+ $$->u2.statements = $5;
+ set_dads($$,$5);
+ $$->u3.abstract = $1;}
+ ;
+
+/* optional "abstract" keyword XXX there is no regression test for this */
+opt_abstract: KW_ABSTRACT { $$ = 1; }
+ | /* nothing */ { $$ = 0; }
+ | KW_EXTEND { $$ = 2; }
+ | KW_EXTEND KW_ABSTRACT { $$=3; }
+ | KW_ABSTRACT KW_EXTEND { $$=3; }
+ ;
+
+macro : KW_MACRO word LP arglist RP LC macro_statements RC {
+ $$ = npval2(PV_MACRO, &@1, &@8);
+ $$->u1.str = $2; $$->u2.arglist = $4; $$->u3.macro_statements = $7;
+ set_dads($$,$7);}
+ ;
+
+globals : KW_GLOBALS LC global_statements RC {
+ $$ = npval2(PV_GLOBALS, &@1, &@4);
+ $$->u1.statements = $3;
+ set_dads($$,$3);}
+ ;
+
+global_statements : { $$ = NULL; }
+ | assignment global_statements {$$ = linku1($1, $2); }
+ | error global_statements {$$=$2;}
+ ;
+
+assignment : word EQ { reset_semicount(parseio->scanner); } word SEMI {
+ $$ = npval2(PV_VARDEC, &@1, &@5);
+ $$->u1.str = $1;
+ $$->u2.val = $4; }
+ ;
+
+local_assignment : KW_LOCAL word EQ { reset_semicount(parseio->scanner); } word SEMI {
+ $$ = npval2(PV_LOCALVARDEC, &@1, &@6);
+ $$->u1.str = $2;
+ $$->u2.val = $5; }
+ ;
+
+/* XXX this matches missing arguments, is this desired ? */
+arglist : /* empty */ { $$ = NULL; }
+ | word { $$ = nword($1, &@1); }
+ | arglist COMMA word { $$ = linku1($1, nword($3, &@3)); }
+ | arglist error {$$=$1;}
+ ;
+
+elements : {$$=0;}
+ | element elements { $$ = linku1($1, $2); }
+ | error elements { $$=$2;}
+ ;
+
+element : extension {$$=$1;}
+ | includes {$$=$1;}
+ | switches {$$=$1;}
+ | eswitches {$$=$1;}
+ | ignorepat {$$=$1;}
+ | assignment {$$=$1;}
+ | local_assignment {$$=$1;}
+ | word error {free($1); $$=0;}
+ | SEMI {$$=0;/* allow older docs to be read */}
+ ;
+
+ignorepat : KW_IGNOREPAT EXTENMARK word SEMI {
+ $$ = npval2(PV_IGNOREPAT, &@1, &@4);
+ $$->u1.str = $3;}
+ ;
+
+extension : word EXTENMARK statement {
+ $$ = npval2(PV_EXTENSION, &@1, &@3);
+ $$->u1.str = $1;
+ $$->u2.statements = $3; set_dads($$,$3);}
+ | word AT word EXTENMARK statement {
+ $$ = npval2(PV_EXTENSION, &@1, &@3);
+ $$->u1.str = malloc(strlen($1)+strlen($3)+2);
+ strcpy($$->u1.str,$1);
+ strcat($$->u1.str,"@");
+ strcat($$->u1.str,$3);
+ free($1);
+ $$->u2.statements = $5; set_dads($$,$5);}
+ | KW_REGEXTEN word EXTENMARK statement {
+ $$ = npval2(PV_EXTENSION, &@1, &@4);
+ $$->u1.str = $2;
+ $$->u2.statements = $4; set_dads($$,$4);
+ $$->u4.regexten=1;}
+ | KW_HINT LP hint_word RP word EXTENMARK statement {
+ $$ = npval2(PV_EXTENSION, &@1, &@7);
+ $$->u1.str = $5;
+ $$->u2.statements = $7; set_dads($$,$7);
+ $$->u3.hints = $3;}
+ | KW_REGEXTEN KW_HINT LP hint_word RP word EXTENMARK statement {
+ $$ = npval2(PV_EXTENSION, &@1, &@8);
+ $$->u1.str = $6;
+ $$->u2.statements = $8; set_dads($$,$8);
+ $$->u4.regexten=1;
+ $$->u3.hints = $4;}
+ ;
+
+/* list of statements in a block or after a case label - can be empty */
+statements : /* empty */ { $$ = NULL; }
+ | statement statements { $$ = linku1($1, $2); }
+ | error statements {$$=$2;}
+ ;
+
+/* hh:mm-hh:mm, due to the way the parser works we do not
+ * detect the '-' but only the ':' as separator
+ */
+timerange: word3_list COLON word3_list COLON word3_list {
+ asprintf(&$$, "%s:%s:%s", $1, $3, $5);
+ free($1);
+ free($3);
+ free($5); }
+ | word { $$ = $1; }
+ ;
+
+/* full time specification range|dow|*|* */
+timespec : timerange BAR word3_list BAR word3_list BAR word3_list {
+ $$ = nword($1, &@1);
+ $$->next = nword($3, &@3);
+ $$->next->next = nword($5, &@5);
+ $$->next->next->next = nword($7, &@7); }
+ ;
+
+/* expression used in if, random, while, switch */
+test_expr : LP { reset_parencount(parseio->scanner); } word_list RP { $$ = $3; }
+ ;
+
+/* 'if' like statements: if, iftime, random */
+if_like_head : KW_IF test_expr {
+ $$= npval2(PV_IF, &@1, &@2);
+ $$->u1.str = $2; }
+ | KW_RANDOM test_expr {
+ $$ = npval2(PV_RANDOM, &@1, &@2);
+ $$->u1.str=$2;}
+ | KW_IFTIME LP timespec RP {
+ $$ = npval2(PV_IFTIME, &@1, &@4);
+ $$->u1.list = $3;
+ prev_word = 0; }
+ ;
+
+/* word_list is a hack to fix a problem with context switching between bison and flex;
+ by the time you register a new context with flex, you've already got a look-ahead token
+ from the old context, with no way to put it back and start afresh. So, we kludge this
+ and merge the words back together. */
+
+word_list : word { $$ = $1;}
+ | word word {
+ asprintf(&($$), "%s%s", $1, $2);
+ free($1);
+ free($2);
+ prev_word = $$;}
+ ;
+
+hint_word : word { $$ = $1; }
+ | hint_word word {
+ asprintf(&($$), "%s %s", $1, $2);
+ free($1);
+ free($2); }
+ | hint_word COLON word {
+ asprintf(&($$), "%s:%s", $1, $3);
+ free($1);
+ free($3); }
+ | hint_word AMPER word { /* there are often '&' in hints */
+ asprintf(&($$), "%s&%s", $1, $3);
+ free($1);
+ free($3);}
+
+
+word3_list : word { $$ = $1;}
+ | word word {
+ asprintf(&($$), "%s%s", $1, $2);
+ free($1);
+ free($2);
+ prev_word = $$;}
+ | word word word {
+ asprintf(&($$), "%s%s%s", $1, $2, $3);
+ free($1);
+ free($2);
+ free($3);
+ prev_word=$$;}
+ ;
+
+goto_word : word { $$ = $1;}
+ | word word {
+ asprintf(&($$), "%s%s", $1, $2);
+ free($1);
+ free($2);}
+ | goto_word COLON word {
+ asprintf(&($$), "%s:%s", $1, $3);
+ free($1);
+ free($3);}
+ ;
+
+switch_statement : KW_SWITCH test_expr LC case_statements RC {
+ $$ = npval2(PV_SWITCH, &@1, &@5);
+ $$->u1.str = $2;
+ $$->u2.statements = $4; set_dads($$,$4);}
+ ;
+
+/*
+ * Definition of a statememt in our language
+ */
+statement : LC statements RC {
+ $$ = npval2(PV_STATEMENTBLOCK, &@1, &@3);
+ $$->u1.list = $2; set_dads($$,$2);}
+ | assignment { $$ = $1; }
+ | local_assignment { $$ = $1; }
+ | KW_GOTO target SEMI {
+ $$ = npval2(PV_GOTO, &@1, &@3);
+ $$->u1.list = $2;}
+ | KW_JUMP jumptarget SEMI {
+ $$ = npval2(PV_GOTO, &@1, &@3);
+ $$->u1.list = $2;}
+ | word COLON {
+ $$ = npval2(PV_LABEL, &@1, &@2);
+ $$->u1.str = $1; }
+ | KW_FOR LP {reset_semicount(parseio->scanner);} word SEMI
+ {reset_semicount(parseio->scanner);} word SEMI
+ {reset_parencount(parseio->scanner);} word RP statement { /* XXX word_list maybe ? */
+ $$ = npval2(PV_FOR, &@1, &@12);
+ $$->u1.for_init = $4;
+ $$->u2.for_test=$7;
+ $$->u3.for_inc = $10;
+ $$->u4.for_statements = $12; set_dads($$,$12);}
+ | KW_WHILE test_expr statement {
+ $$ = npval2(PV_WHILE, &@1, &@3);
+ $$->u1.str = $2;
+ $$->u2.statements = $3; set_dads($$,$3);}
+ | switch_statement { $$ = $1; }
+ | AMPER macro_call SEMI { $$ = update_last($2, &@2); }
+ | application_call SEMI { $$ = update_last($1, &@2); }
+ | word SEMI {
+ $$= npval2(PV_APPLICATION_CALL, &@1, &@2);
+ $$->u1.str = $1;}
+ | application_call EQ {reset_semicount(parseio->scanner);} word SEMI {
+ char *bufx;
+ int tot=0;
+ pval *pptr;
+ $$ = npval2(PV_VARDEC, &@1, &@5);
+ $$->u2.val=$4;
+ /* rebuild the original string-- this is not an app call, it's an unwrapped vardec, with a func call on the LHS */
+ /* string to big to fit in the buffer? */
+ tot+=strlen($1->u1.str);
+ for(pptr=$1->u2.arglist;pptr;pptr=pptr->next) {
+ tot+=strlen(pptr->u1.str);
+ tot++; /* for a sep like a comma */
+ }
+ tot+=4; /* for safety */
+ bufx = calloc(1, tot);
+ strcpy(bufx,$1->u1.str);
+ strcat(bufx,"(");
+ /* XXX need to advance the pointer or the loop is very inefficient */
+ for (pptr=$1->u2.arglist;pptr;pptr=pptr->next) {
+ if ( pptr != $1->u2.arglist )
+ strcat(bufx,",");
+ strcat(bufx,pptr->u1.str);
+ }
+ strcat(bufx,")");
+#ifdef AAL_ARGCHECK
+ if ( !ael_is_funcname($1->u1.str) )
+ 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",
+ my_file, @1.first_line, @1.first_column, @1.last_column, $1->u1.str);
+#endif
+ $$->u1.str = bufx;
+ destroy_pval($1); /* the app call it is not, get rid of that chain */
+ prev_word = 0;
+ }
+ | KW_BREAK SEMI { $$ = npval2(PV_BREAK, &@1, &@2); }
+ | KW_RETURN SEMI { $$ = npval2(PV_RETURN, &@1, &@2); }
+ | KW_CONTINUE SEMI { $$ = npval2(PV_CONTINUE, &@1, &@2); }
+ | if_like_head statement opt_else {
+ $$ = update_last($1, &@2);
+ $$->u2.statements = $2; set_dads($$,$2);
+ $$->u3.else_statements = $3;set_dads($$,$3);}
+ | SEMI { $$=0; }
+ ;
+
+opt_else : KW_ELSE statement { $$ = $2; }
+ | { $$ = NULL ; }
+
+
+target : goto_word { $$ = nword($1, &@1); }
+ | goto_word BAR goto_word {
+ $$ = nword($1, &@1);
+ $$->next = nword($3, &@3); }
+ | goto_word COMMA goto_word {
+ $$ = nword($1, &@1);
+ $$->next = nword($3, &@3); }
+ | goto_word BAR goto_word BAR goto_word {
+ $$ = nword($1, &@1);
+ $$->next = nword($3, &@3);
+ $$->next->next = nword($5, &@5); }
+ | goto_word COMMA goto_word COMMA goto_word {
+ $$ = nword($1, &@1);
+ $$->next = nword($3, &@3);
+ $$->next->next = nword($5, &@5); }
+ | KW_DEFAULT BAR goto_word BAR goto_word {
+ $$ = nword(strdup("default"), &@1);
+ $$->next = nword($3, &@3);
+ $$->next->next = nword($5, &@5); }
+ | KW_DEFAULT COMMA goto_word COMMA goto_word {
+ $$ = nword(strdup("default"), &@1);
+ $$->next = nword($3, &@3);
+ $$->next->next = nword($5, &@5); }
+ ;
+
+opt_pri : /* empty */ { $$ = strdup("1"); }
+ | COMMA word { $$ = $2; }
+ ;
+
+/* XXX please document the form of jumptarget */
+jumptarget : goto_word opt_pri { /* ext[, pri] default 1 */
+ $$ = nword($1, &@1);
+ $$->next = nword($2, &@2); } /* jump extension[,priority][@context] */
+ | goto_word opt_pri AT context_name { /* context, ext, pri */
+ $$ = nword($4, &@4);
+ $$->next = nword($1, &@1);
+ $$->next->next = nword($2, &@2); }
+ ;
+
+macro_call : word LP {reset_argcount(parseio->scanner);} eval_arglist RP {
+ /* XXX original code had @2 but i think we need @5 */
+ $$ = npval2(PV_MACRO_CALL, &@1, &@5);
+ $$->u1.str = $1;
+ $$->u2.arglist = $4;}
+ | word LP RP {
+ $$= npval2(PV_MACRO_CALL, &@1, &@3);
+ $$->u1.str = $1; }
+ ;
+
+/* XXX application_call_head must be revised. Having 'word LP { ...'
+ * just as above should work fine, however it gives a different result.
+ */
+application_call_head: word LP {reset_argcount(parseio->scanner);} {
+ if (strcasecmp($1,"goto") == 0) {
+ $$ = npval2(PV_GOTO, &@1, &@2);
+ free($1); /* won't be using this */
+ 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 );
+ } else {
+ $$= npval2(PV_APPLICATION_CALL, &@1, &@2);
+ $$->u1.str = $1;
+ } }
+ ;
+
+application_call : application_call_head eval_arglist RP {
+ $$ = update_last($1, &@3);
+ if( $$->type == PV_GOTO )
+ $$->u1.list = $2;
+ else
+ $$->u2.arglist = $2;
+ }
+ | application_call_head RP { $$ = update_last($1, &@2); }
+ ;
+
+opt_word : word { $$ = $1 }
+ | { $$ = strdup(""); }
+ ;
+
+eval_arglist : word_list { $$ = nword($1, &@1); }
+ | /*nothing! */ {
+ $$= npval(PV_WORD,0/*@1.first_line*/,0/*@1.last_line*/,0/* @1.first_column*/, 0/*@1.last_column*/);
+ $$->u1.str = strdup(""); }
+ | eval_arglist COMMA opt_word { $$ = linku1($1, nword($3, &@3)); }
+ ;
+
+case_statements: /* empty */ { $$ = NULL; }
+ | case_statement case_statements { $$ = linku1($1, $2); }
+ ;
+
+case_statement: KW_CASE word COLON statements {
+ $$ = npval2(PV_CASE, &@1, &@3); /* XXX 3 or 4 ? */
+ $$->u1.str = $2;
+ $$->u2.statements = $4; set_dads($$,$4);}
+ | KW_DEFAULT COLON statements {
+ $$ = npval2(PV_DEFAULT, &@1, &@3);
+ $$->u1.str = NULL;
+ $$->u2.statements = $3;set_dads($$,$3);}
+ | KW_PATTERN word COLON statements {
+ $$ = npval2(PV_PATTERN, &@1, &@4); /* XXX@3 or @4 ? */
+ $$->u1.str = $2;
+ $$->u2.statements = $4;set_dads($$,$4);}
+ ;
+
+macro_statements: /* empty */ { $$ = NULL; }
+ | macro_statement macro_statements { $$ = linku1($1, $2); }
+ ;
+
+macro_statement : statement {$$=$1;}
+ | includes { $$=$1;}
+ | KW_CATCH word LC statements RC {
+ $$ = npval2(PV_CATCH, &@1, &@5);
+ $$->u1.str = $2;
+ $$->u2.statements = $4; set_dads($$,$4);}
+ ;
+
+switches : KW_SWITCHES LC switchlist RC {
+ $$ = npval2(PV_SWITCHES, &@1, &@2);
+ $$->u1.list = $3; set_dads($$,$3);}
+ ;
+
+eswitches : KW_ESWITCHES LC switchlist RC {
+ $$ = npval2(PV_ESWITCHES, &@1, &@2);
+ $$->u1.list = $3; set_dads($$,$3);}
+ ;
+
+switchlist : /* empty */ { $$ = NULL; }
+ | word SEMI switchlist { $$ = linku1(nword($1, &@1), $3); }
+ | word AT word SEMI switchlist { char *x; asprintf(&x,"%s@%s", $1,$3); free($1); free($3);
+ $$ = linku1(nword(x, &@1), $5);}
+ | error switchlist {$$=$2;}
+ ;
+
+included_entry : context_name { $$ = nword($1, &@1); }
+ | context_name BAR timespec {
+ $$ = nword($1, &@1);
+ $$->u2.arglist = $3;
+ prev_word=0; /* XXX sure ? */ }
+ ;
+
+/* list of ';' separated context names followed by optional timespec */
+includeslist : included_entry SEMI { $$ = $1; }
+ | includeslist included_entry SEMI { $$ = linku1($1, $2); }
+ | includeslist error {$$=$1;}
+ ;
+
+includes : KW_INCLUDES LC includeslist RC {
+ $$ = npval2(PV_INCLUDES, &@1, &@4);
+ $$->u1.list = $3;set_dads($$,$3);}
+ | KW_INCLUDES LC RC {
+ $$ = npval2(PV_INCLUDES, &@1, &@3);}
+ ;
+
+
+%%
+
+static char *token_equivs1[] =
+{
+ "AMPER",
+ "AT",
+ "BAR",
+ "COLON",
+ "COMMA",
+ "EQ",
+ "EXTENMARK",
+ "KW_BREAK",
+ "KW_CASE",
+ "KW_CATCH",
+ "KW_CONTEXT",
+ "KW_CONTINUE",
+ "KW_DEFAULT",
+ "KW_ELSE",
+ "KW_ESWITCHES",
+ "KW_FOR",
+ "KW_GLOBALS",
+ "KW_GOTO",
+ "KW_HINT",
+ "KW_IFTIME",
+ "KW_IF",
+ "KW_IGNOREPAT",
+ "KW_INCLUDES"
+ "KW_JUMP",
+ "KW_MACRO",
+ "KW_PATTERN",
+ "KW_REGEXTEN",
+ "KW_RETURN",
+ "KW_SWITCHES",
+ "KW_SWITCH",
+ "KW_WHILE",
+ "LC",
+ "LP",
+ "RC",
+ "RP",
+ "SEMI",
+};
+
+static char *token_equivs2[] =
+{
+ "&",
+ "@",
+ "|",
+ ":",
+ ",",
+ "=",
+ "=>",
+ "break",
+ "case",
+ "catch",
+ "context",
+ "continue",
+ "default",
+ "else",
+ "eswitches",
+ "for",
+ "globals",
+ "goto",
+ "hint",
+ "ifTime",
+ "if",
+ "ignorepat",
+ "includes"
+ "jump",
+ "macro",
+ "pattern",
+ "regexten",
+ "return",
+ "switches",
+ "switch",
+ "while",
+ "{",
+ "(",
+ "}",
+ ")",
+ ";",
+};
+
+
+static char *ael_token_subst(const char *mess)
+{
+ /* calc a length, malloc, fill, and return; yyerror had better free it! */
+ int len=0,i;
+ const char *p;
+ char *res, *s,*t;
+ int token_equivs_entries = sizeof(token_equivs1)/sizeof(char*);
+
+ for (p=mess; *p; p++) {
+ for (i=0; i<token_equivs_entries; i++) {
+ if ( strncmp(p,token_equivs1[i],strlen(token_equivs1[i])) == 0 )
+ {
+ len+=strlen(token_equivs2[i])+2;
+ p += strlen(token_equivs1[i])-1;
+ break;
+ }
+ }
+ len++;
+ }
+ res = calloc(1, len+1);
+ res[0] = 0;
+ s = res;
+ for (p=mess; *p;) {
+ int found = 0;
+ for (i=0; i<token_equivs_entries; i++) {
+ if ( strncmp(p,token_equivs1[i],strlen(token_equivs1[i])) == 0 ) {
+ *s++ = '\'';
+ for (t=token_equivs2[i]; *t;) {
+ *s++ = *t++;
+ }
+ *s++ = '\'';
+ p += strlen(token_equivs1[i]);
+ found = 1;
+ break;
+ }
+ }
+ if( !found )
+ *s++ = *p++;
+ }
+ *s++ = 0;
+ return res;
+}
+
+void yyerror(YYLTYPE *locp, struct parse_io *parseio, char const *s)
+{
+ char *s2 = ael_token_subst((char *)s);
+ if (locp->first_line == locp->last_line) {
+ 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);
+ } else {
+ 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);
+ }
+ free(s2);
+ parseio->syntax_error_count++;
+}
+
+struct pval *npval(pvaltype type, int first_line, int last_line,
+ int first_column, int last_column)
+{
+ pval *z = calloc(1, sizeof(struct pval));
+ z->type = type;
+ z->startline = first_line;
+ z->endline = last_line;
+ z->startcol = first_column;
+ z->endcol = last_column;
+ z->filename = strdup(my_file);
+ return z;
+}
+
+static struct pval *npval2(pvaltype type, YYLTYPE *first, YYLTYPE *last)
+{
+ return npval(type, first->first_line, last->last_line,
+ first->first_column, last->last_column);
+}
+
+static struct pval *update_last(pval *obj, YYLTYPE *last)
+{
+ obj->endline = last->last_line;
+ obj->endcol = last->last_column;
+ return obj;
+}
+
+/* frontend for npval to create a PV_WORD string from the given token */
+static pval *nword(char *string, YYLTYPE *pos)
+{
+ pval *p = npval2(PV_WORD, pos, pos);
+ if (p)
+ p->u1.str = string;
+ return p;
+}
+
+/* this routine adds a dad ptr to each element in the list */
+static void set_dads(struct pval *dad, struct pval *child_list)
+{
+ struct pval *t;
+
+ for(t=child_list;t;t=t->next) /* simple stuff */
+ t->dad = dad;
+}
+