Index: pbx/ael/ael-test/ael-vtest26/extensions.ael =================================================================== --- pbx/ael/ael-test/ael-vtest26/extensions.ael (revision 0) +++ pbx/ael/ael-test/ael-vtest26/extensions.ael (revision 0) @@ -0,0 +1,13 @@ +macro test-retval () { + i=5; + i=${i}+10; + return ${i}; +} + +macro test-macro-assignment () { + foo=&test-retval(); + local bub=&test-retval(); + NoOp(${foo}); + HASH(foo,bar)=&test-retval(); + return; +} Index: pbx/ael/ael-test/ref.ael-vtest26 =================================================================== --- pbx/ael/ael-test/ref.ael-vtest26 (revision 0) +++ pbx/ael/ael-test/ref.ael-vtest26 (revision 0) @@ -0,0 +1,17 @@ + + +[test-retval] +exten => s,1,Set(i=$[5]) +exten => s,2,Set(i=$[${i}+10]) +exten => s,3,Return($[${i}]) + + +[test-macro-assignment] +exten => s,1,Gosub(test-retval,s,1) +exten => s,2,Set(foo=${GOSUB_RETVAL}) +exten => s,3,Gosub(test-retval,s,1) +exten => s,4,Set(LOCAL(bub)=${GOSUB_RETVAL}) +exten => s,5,NoOp(${foo}) +exten => s,6,Gosub(test-retval,s,1) +exten => s,7,Set(HASH(foo,bar)=${GOSUB_RETVAL}) +exten => s,8,Return() Index: CHANGES =================================================================== --- CHANGES (revision 166832) +++ CHANGES (working copy) @@ -128,6 +128,19 @@ If this is included, the server supports event suppression. +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(); + + ------------------------------------------------------------------------------ --- Functionality changes from Asterisk 1.6.0 to Asterisk 1.6.1 ------------- ------------------------------------------------------------------------------ Index: include/asterisk/ael_structs.h =================================================================== --- include/asterisk/ael_structs.h (revision 166832) +++ 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 166832) +++ include/asterisk/pval.h (working copy) @@ -34,6 +34,9 @@ PV_IGNOREPAT, /* 26 */ PV_GLOBALS, /* 27 */ PV_LOCALVARDEC, /* 28 */ + PV_RETURN_VAL, /* 29 */ + PV_MACROVARDEC, /* 30 */ + PV_LOCALMACROVARDEC /* 31 */ } pvaltype; /* why this horrible mess? It's always been a tradeoff-- tons of structs, Index: res/ael/pval.c =================================================================== --- res/ael/pval.c (revision 166863) +++ 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,6 +293,10 @@ case PV_RETURN: fprintf(fin,"return;\n"); break; + + case PV_RETURN_VAL: + fprintf(fin,"return (%s);\n", item->u1.str); + break; case PV_CONTINUE: fprintf(fin,"continue;\n"); @@ -507,7 +521,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. @@ -553,6 +583,11 @@ */ break; + case PV_RETURN_VAL: + /* fields: item->u1.str == value to return + */ + break; + case PV_CONTINUE: /* fields: none */ @@ -667,7 +702,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_RETURN_VAL) { 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); @@ -2658,6 +2693,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. @@ -2745,6 +2794,12 @@ /* fields: none */ break; + + case PV_RETURN_VAL: + /* fields: item->u1.str == the value to return + */ + check_expr2_input(item,item->u1.str); + break; case PV_CONTINUE: /* fields: none @@ -3197,7 +3252,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. @@ -3238,6 +3305,11 @@ /* fields: none */ break; + + case PV_RETURN_VAL: + /* fields: item->u1.str == value to return + */ + break; case PV_CONTINUE: /* fields: none @@ -3435,6 +3507,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(); @@ -3718,7 +3822,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_RETURN_VAL) ) { /* is there a following CASE/PATTERN/DEFAULT? */ if (p2->next && p2->next->type == PV_CASE) { fall_thru = new_prio(); @@ -3789,7 +3893,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_RETURN_VAL)) { /* is there a following CASE/PATTERN/DEFAULT? */ if (p2->next && p2->next->type == PV_CASE) { fall_thru = new_prio(); @@ -3889,7 +3993,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_RETURN_VAL)) { /* is there a following CASE/PATTERN/DEFAULT? */ if (p2->next && p2->next->type == PV_CASE) { fall_thru = new_prio(); @@ -3999,6 +4103,16 @@ linkprio(exten, pr, mother_exten); break; + case PV_RETURN_VAL: + pr = new_prio(); + pr->type = AEL_RETURN_VAL; + pr->app = strdup("Return"); + snprintf(buf1,sizeof(buf1),"$[%s]", p->u1.str); + pr->appargs = strdup(buf1); + pr->origin = p; + linkprio(exten, pr, mother_exten); + break; + case PV_CONTINUE: pr = new_prio(); pr->type = AEL_CONTROL1; /* simple goto */ @@ -4265,6 +4379,10 @@ strcpy(app,"Return"); appargs[0] = 0; break; + case AEL_RETURN_VAL: + strcpy(app,"Return"); + snprintf(appargs,sizeof(appargs),"%s", pr->appargs); + break; default: break; @@ -4691,7 +4809,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. @@ -4743,6 +4871,13 @@ */ break; + case PV_RETURN_VAL: + /* fields: item->u1.str == the value to return + */ + if (item->u1.str) { + free(item->u1.str); + } + break; case PV_CONTINUE: /* fields: none */ @@ -5203,7 +5338,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 166863) +++ 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 ? */ @@ -490,41 +504,23 @@ | 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_RETURN word SEMI { + $$ = npval2(PV_RETURN_VAL, &@1, &@3); + $$->u1.str = $2; } | KW_CONTINUE SEMI { $$ = npval2(PV_CONTINUE, &@1, &@2); } | if_like_head statement opt_else { $$ = update_last($1, &@2); @@ -880,3 +876,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 166863) +++ 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;