Index: CHANGES =================================================================== --- CHANGES (revision 243390) +++ CHANGES (working copy) @@ -372,6 +372,18 @@ coming soon. For more information on the security events framework, see the "Security Events" chapter of the included documentation - doc/tex/asterisk.pdf. +AEL +--- + * Macros now support returning a value, and direct assignment of that value to a + variable or function. For example: + + macro returnmacro() { + MYVAR=10; + Return (${MYVAR}); + } + + FOO=&returnmacro(); + Miscellaneous ------------- * The transmit_silence_during_record option in asterisk.conf.sample has been removed. Index: include/asterisk/ael_structs.h =================================================================== --- include/asterisk/ael_structs.h (revision 243390) +++ include/asterisk/ael_structs.h (working copy) @@ -81,7 +81,7 @@ /* for CODE GENERATION */ -typedef enum { AEL_APPCALL, AEL_CONTROL1, AEL_FOR_CONTROL, AEL_IF_CONTROL, AEL_IFTIME_CONTROL, AEL_RAND_CONTROL, AEL_LABEL, AEL_RETURN } ael_priority_type; +typedef enum { AEL_APPCALL, AEL_CONTROL1, AEL_FOR_CONTROL, AEL_IF_CONTROL, AEL_IFTIME_CONTROL, AEL_RAND_CONTROL, AEL_LABEL, AEL_RETURN, AEL_RETURN_VAL } ael_priority_type; struct ael_priority Index: include/asterisk/pval.h =================================================================== --- include/asterisk/pval.h (revision 243390) +++ include/asterisk/pval.h (working copy) @@ -34,6 +34,8 @@ PV_IGNOREPAT, /* 26 */ PV_GLOBALS, /* 27 */ PV_LOCALVARDEC, /* 28 */ + PV_MACROVARDEC, /* 29 */ + PV_LOCALMACROVARDEC /* 30 */ } pvaltype; /* why this horrible mess? It's always been a tradeoff-- tons of structs, Index: res/ael/pval.c =================================================================== --- res/ael/pval.c (revision 243390) +++ res/ael/pval.c (working copy) @@ -252,7 +252,17 @@ case PV_LOCALVARDEC: fprintf(fin,"local %s=%s;\n", item->u1.str, item->u2.val); break; - + + case PV_MACROVARDEC: + print_pval_list(fin,item->u2.statements,depth); + fprintf(fin,"%s;\n", item->u1.str); + break; + + case PV_LOCALMACROVARDEC: + print_pval_list(fin,item->u2.statements,depth); + fprintf(fin,"%s;\n", item->u1.str); + break; + case PV_GOTO: fprintf(fin,"goto %s", item->u1.list->u1.str); if ( item->u1.list->next ) @@ -283,7 +293,7 @@ case PV_RETURN: fprintf(fin,"return;\n"); break; - + case PV_CONTINUE: fprintf(fin,"continue;\n"); break; @@ -507,7 +517,23 @@ item->u2.val == variable value to assign */ break; - + + case PV_MACROVARDEC: + /* fields: item->u1.str == variable name + + item->u2.statements == macro call + */ + traverse_pval_item_template(item->u2.statements,depth); + break; + + case PV_LOCALMACROVARDEC: + /* fields: item->u1.str == variable name + + item->u2.statements == macro call + */ + traverse_pval_item_template(item->u2.statements,depth); + break; + case PV_GOTO: /* fields: item->u1.list == pval list of PV_WORD target names, up to 3, in order as given by user. item->u1.list->u1.str == where the data on a PV_WORD will always be. @@ -667,7 +693,7 @@ for (i=macro->u3.macro_statements; i; i=i->next) { /* if the last statement in the list is not return, then insert a return there */ if (i->next == NULL) { - if (i->type != PV_RETURN) { + if (i->type != PV_RETURN && (i->type != PV_APPLICATION_CALL && !strcasecmp(i->u1.str, "return"))) { pval *z = calloc(1, sizeof(struct pval)); ast_log(LOG_WARNING, "Warning: file %s, line %d-%d: The macro %s does not end with a return; I will insert one.\n", macro->filename, macro->startline, macro->endline, macro->u1.str); @@ -2506,7 +2532,6 @@ || strcasecmp(item->u1.str,"endwhile") == 0 || strcasecmp(item->u1.str,"random") == 0 || strcasecmp(item->u1.str,"gosub") == 0 - || strcasecmp(item->u1.str,"return") == 0 || strcasecmp(item->u1.str,"gosubif") == 0 || strcasecmp(item->u1.str,"continuewhile") == 0 || strcasecmp(item->u1.str,"endwhile") == 0 @@ -2659,6 +2684,20 @@ check_expr2_input(item,item->u2.val); break; + case PV_MACROVARDEC: + /* fields: item->u1.str == variable name + item->u2.statements == macro call, the return value of which to assign + */ + check_pval(item->u2.statements, apps, in_globals); + break; + + case PV_LOCALMACROVARDEC: + /* fields: item->u1.str == variable name + item->u2.statements == macro call, the return value of which to assign + */ + check_pval(item->u2.statements, apps, in_globals); + break; + case PV_GOTO: /* fields: item->u1.list == pval list of PV_WORD target names, up to 3, in order as given by user. item->u1.list->u1.str == where the data on a PV_WORD will always be. @@ -2746,7 +2785,7 @@ /* fields: none */ break; - + case PV_CONTINUE: /* fields: none */ @@ -3198,7 +3237,19 @@ item->u2.val == variable value to assign */ break; - + + case PV_MACROVARDEC: + /* fields: item->u1.str == variable name + item->u2.statements == macro who return value will be assigned + */ + break; + + case PV_LOCALMACROVARDEC: + /* fields: item->u1.str == variable name + item->u2.statements == macro who return value will be assigned + */ + break; + case PV_GOTO: /* fields: item->u1.list == pval list of PV_WORD target names, up to 3, in order as given by user. item->u1.list->u1.str == where the data on a PV_WORD will always be. @@ -3239,7 +3290,7 @@ /* fields: none */ break; - + case PV_CONTINUE: /* fields: none */ @@ -3458,6 +3509,38 @@ pr->origin = p; linkprio(exten, pr, mother_exten); break; + + case PV_MACROVARDEC: + gen_prios(exten, new_label, p->u2.statements, mother_exten, this_context); + pr = new_prio(); + pr->type = AEL_APPCALL; + snprintf(buf1,sizeof(buf1),"%s=${GOSUB_RETVAL}", p->u1.str); + if (!ast_compat_app_set) { + pr->app = strdup("MSet"); + } else { + pr->app = strdup("Set"); + } + remove_spaces_before_equals(buf1); + pr->appargs = strdup(buf1); + pr->origin = p; + linkprio(exten, pr, mother_exten); + break; + + case PV_LOCALMACROVARDEC: + gen_prios(exten, new_label, p->u2.statements, mother_exten, this_context); + pr = new_prio(); + pr->type = AEL_APPCALL; + snprintf(buf1,sizeof(buf1),"LOCAL(%s)=${GOSUB_RETVAL}", p->u1.str); + if (!ast_compat_app_set) { + pr->app = strdup("MSet"); + } else { + pr->app = strdup("Set"); + } + remove_spaces_before_equals(buf1); + pr->appargs = strdup(buf1); + pr->origin = p; + linkprio(exten, pr, mother_exten); + break; case PV_GOTO: pr = new_prio(); @@ -3741,7 +3824,7 @@ break; } /* p3 now points the last statement... */ - if (!p3 || ( p3->type != PV_GOTO && p3->type != PV_BREAK && p3->type != PV_RETURN) ) { + if (!p3 || ( p3->type != PV_GOTO && p3->type != PV_BREAK && p3->type != PV_RETURN && (p3->type != PV_APPLICATION_CALL && !strcasecmp(p3->u1.str, "return"))) ) { /* is there a following CASE/PATTERN/DEFAULT? */ if (p2->next && p2->next->type == PV_CASE) { fall_thru = new_prio(); @@ -3812,7 +3895,7 @@ break; } /* p3 now points the last statement... */ - if (!p3 || ( p3->type != PV_GOTO && p3->type != PV_BREAK && p3->type != PV_RETURN)) { + if (!p3 || ( p3->type != PV_GOTO && p3->type != PV_BREAK && p3->type != PV_RETURN && (p3->type != PV_APPLICATION_CALL && !strcasecmp(p3->u1.str, "return"))) ) { /* is there a following CASE/PATTERN/DEFAULT? */ if (p2->next && p2->next->type == PV_CASE) { fall_thru = new_prio(); @@ -3912,7 +3995,7 @@ break; } /* p3 now points the last statement... */ - if (!p3 || (p3->type != PV_GOTO && p3->type != PV_BREAK && p3->type != PV_RETURN)) { + if (!p3 || (p3->type != PV_GOTO && p3->type != PV_BREAK && p3->type != PV_RETURN && (p3->type != PV_APPLICATION_CALL && !strcasecmp(p3->u1.str, "return"))) ) { /* is there a following CASE/PATTERN/DEFAULT? */ if (p2->next && p2->next->type == PV_CASE) { fall_thru = new_prio(); @@ -4288,7 +4371,7 @@ strcpy(app,"Return"); appargs[0] = 0; break; - + default: break; } @@ -4714,7 +4797,17 @@ if (item->u2.val) free(item->u2.val); break; - + + case PV_LOCALMACROVARDEC: + case PV_MACROVARDEC: + /* fields: item->u1.str == variable name + item->u2.statements == macro call whose return value is assigned + */ + if (item->u1.str) + free(item->u1.str); + destroy_pval(item->u2.statements); + break; + case PV_GOTO: /* fields: item->u1.list == pval list of PV_WORD target names, up to 3, in order as given by user. item->u1.list->u1.str == where the data on a PV_WORD will always be. @@ -5226,7 +5319,6 @@ return *statement; } - void pvalCatchSetExtName( pval *p, char *name ) { if (!pvalCheckType(p, "pvalCatchSetExtName", PV_CATCH)) Index: res/ael/ael.y =================================================================== --- res/ael/ael.y (revision 243390) +++ res/ael/ael.y (working copy) @@ -52,7 +52,6 @@ int ael_is_funcname(char *name); #endif static char *ael_token_subst(const char *mess); - %} @@ -81,6 +80,9 @@ /* update end position of an object, return the object */ static pval *update_last(pval *, YYLTYPE *); + +/* Rebuild the function call erroneously (but necessarily) parsed as an application call */ +static char *rebuild_funccall(pval *); %} @@ -147,7 +149,7 @@ /* * 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" @@ -239,16 +241,28 @@ | error global_statements {$$=$2;} ; -assignment : word EQ { reset_semicount(parseio->scanner); } word SEMI { +assignment : word EQ reset_semi word SEMI { $$ = npval2(PV_VARDEC, &@1, &@5); $$->u1.str = $1; $$->u2.val = $4; } + | word EQ reset_semi AMPER macro_call SEMI { + $$ = npval2(PV_MACROVARDEC, &@1, &@6); + $$->u1.str = $1; + $$->u2.statements = $5; } ; + +reset_semi : /* empty */ + { reset_semicount(parseio->scanner); } + ; -local_assignment : KW_LOCAL word EQ { reset_semicount(parseio->scanner); } word SEMI { +local_assignment : KW_LOCAL word EQ reset_semi word SEMI { $$ = npval2(PV_LOCALVARDEC, &@1, &@6); $$->u1.str = $2; $$->u2.val = $5; } + | KW_LOCAL word EQ reset_semi AMPER macro_call SEMI { + $$ = npval2(PV_LOCALMACROVARDEC, &@1, &@7); + $$->u1.str = $2; + $$->u2.statements = $6; } ; /* XXX this matches missing arguments, is this desired ? */ @@ -499,39 +513,18 @@ | 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; + | application_call EQ reset_semi word SEMI { $$ = 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 */ + $$->u1.str = rebuild_funccall($1); prev_word = 0; } + | application_call EQ reset_semi AMPER macro_call SEMI { + $$ = npval2(PV_MACROVARDEC, &@1, &@6); + $$->u2.statements=$5; + $$->u1.str = rebuild_funccall($1); + 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); } @@ -889,3 +882,32 @@ t->dad = dad; } +/* This routine rebuilds a left-hand side function call from the parsed components of an application_call nonterminal */ +static char *rebuild_funccall(pval *funccall) { + char *bufx; + int tot=0; + pval *pptr; + tot+=strlen(funccall->u1.str); + for(pptr=funccall->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,funccall->u1.str); + strcat(bufx,"("); + /* XXX need to advance the pointer or the loop is very inefficient */ + for (pptr=funccall->u2.arglist;pptr;pptr=pptr->next) { + if ( pptr != funccall->u2.arglist ) + strcat(bufx,","); + strcat(bufx,pptr->u1.str); + } + strcat(bufx,")"); +#ifdef AAL_ARGCHECK + if ( !ael_is_funcname(funccall->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, funccall->u1.str); +#endif + destroy_pval(funccall); /* the app call it is not, get rid of that chain */ + return bufx; +} Index: res/ael/ael.flex =================================================================== --- res/ael/ael.flex (revision 243390) +++ res/ael/ael.flex (working copy) @@ -210,7 +210,7 @@ NOARGG ([^(),\{\}\[\]]|\\[,()\[\]\{\}])* -NOSEMIC ([^;()\{\}\[\]]|\\[;()\[\]\{\}])* +NOSEMIC ([^;()\&\{\}\[\]]|\\[;()\[\]\{\}])* HIBIT [\x80-\xff] @@ -563,6 +563,7 @@ } yymore(); } +{NOSEMIC}[\&] {STORE_POS; BEGIN(0); return AMPER;} {NOSEMIC}; { STORE_LOC;