ael.y 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890
  1. %{
  2. /*
  3. * Asterisk -- An open source telephony toolkit.
  4. *
  5. * Copyright (C) 2006, Digium, Inc.
  6. *
  7. * Steve Murphy <murf@parsetree.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. /*! \file
  20. *
  21. * \brief Bison Grammar description of AEL2.
  22. *
  23. */
  24. #include "asterisk.h"
  25. ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
  26. #include <stdio.h>
  27. #include <stdlib.h>
  28. #include <string.h>
  29. #include "asterisk/logger.h"
  30. #include "asterisk/lock.h"
  31. #include "asterisk/hashtab.h"
  32. #include "asterisk/ael_structs.h"
  33. #include "asterisk/utils.h"
  34. extern struct ast_flags ast_compat;
  35. pval * linku1(pval *head, pval *tail);
  36. static void set_dads(pval *dad, pval *child_list);
  37. void reset_parencount(yyscan_t yyscanner);
  38. void reset_semicount(yyscan_t yyscanner);
  39. void reset_argcount(yyscan_t yyscanner );
  40. #define YYLEX_PARAM ((struct parse_io *)parseio)->scanner
  41. #define YYERROR_VERBOSE 1
  42. extern char *my_file;
  43. #ifdef AAL_ARGCHECK
  44. int ael_is_funcname(char *name);
  45. #endif
  46. static char *ael_token_subst(const char *mess);
  47. %}
  48. %union {
  49. int intval; /* integer value, typically flags */
  50. char *str; /* strings */
  51. struct pval *pval; /* full objects */
  52. }
  53. %{
  54. /* declaring these AFTER the union makes things a lot simpler! */
  55. void yyerror(YYLTYPE *locp, struct parse_io *parseio, char const *s);
  56. int ael_yylex (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , void * yyscanner);
  57. /* create a new object with start-end marker */
  58. pval *npval(pvaltype type, int first_line, int last_line,
  59. int first_column, int last_column);
  60. /* create a new object with start-end marker, simplified interface.
  61. * Must be declared here because YYLTYPE is not known before
  62. */
  63. static pval *npval2(pvaltype type, YYLTYPE *first, YYLTYPE *last);
  64. /* another frontend for npval, this time for a string */
  65. static pval *nword(char *string, YYLTYPE *pos);
  66. /* update end position of an object, return the object */
  67. static pval *update_last(pval *, YYLTYPE *);
  68. %}
  69. %token KW_CONTEXT LC RC LP RP SEMI EQ COMMA COLON AMPER BAR AT
  70. %token KW_MACRO KW_GLOBALS KW_IGNOREPAT KW_SWITCH KW_IF KW_IFTIME KW_ELSE KW_RANDOM KW_ABSTRACT KW_EXTEND
  71. %token EXTENMARK KW_GOTO KW_JUMP KW_RETURN KW_BREAK KW_CONTINUE KW_REGEXTEN KW_HINT
  72. %token KW_FOR KW_WHILE KW_CASE KW_PATTERN KW_DEFAULT KW_CATCH KW_SWITCHES KW_ESWITCHES
  73. %token KW_INCLUDES KW_LOCAL
  74. %right BAR COMMA
  75. %token <str> word
  76. %type <pval>includes
  77. %type <pval>includeslist
  78. %type <pval>switchlist
  79. %type <pval>eswitches
  80. %type <pval>switches
  81. %type <pval>macro_statement
  82. %type <pval>macro_statements
  83. %type <pval>case_statement
  84. %type <pval>case_statements
  85. %type <pval>eval_arglist
  86. %type <pval>application_call
  87. %type <pval>application_call_head
  88. %type <pval>macro_call
  89. %type <pval>target jumptarget
  90. %type <pval>statement
  91. %type <pval>switch_statement
  92. %type <pval>if_like_head
  93. %type <pval>statements
  94. %type <pval>extension
  95. %type <pval>ignorepat
  96. %type <pval>element
  97. %type <pval>elements
  98. %type <pval>arglist
  99. %type <pval>assignment
  100. %type <pval>local_assignment
  101. %type <pval>global_statements
  102. %type <pval>globals
  103. %type <pval>macro
  104. %type <pval>context
  105. %type <pval>object
  106. %type <pval>objects
  107. %type <pval>file
  108. /* XXX lr changes */
  109. %type <pval>opt_else
  110. %type <pval>timespec
  111. %type <pval>included_entry
  112. %type <str>opt_word
  113. %type <str>context_name
  114. %type <str>timerange
  115. %type <str>goto_word
  116. %type <str>word_list
  117. %type <str>word3_list hint_word
  118. %type <str>test_expr
  119. %type <str>opt_pri
  120. %type <intval>opt_abstract
  121. /*
  122. * OPTIONS
  123. */
  124. %locations /* track source location using @n variables (yylloc in flex) */
  125. %pure-parser /* pass yylval and yylloc as arguments to yylex(). */
  126. %name-prefix="ael_yy"
  127. /*
  128. * add an additional argument, parseio, to yyparse(),
  129. * which is then accessible in the grammar actions
  130. */
  131. %parse-param {struct parse_io *parseio}
  132. /* 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
  133. the default action to shift will attach the else to the preceeding if. */
  134. %expect 30
  135. %error-verbose
  136. /*
  137. * declare destructors for objects.
  138. * The former is for pval, the latter for strings.
  139. * NOTE: we must not have a destructor for a 'file' object.
  140. */
  141. %destructor {
  142. destroy_pval($$);
  143. prev_word=0;
  144. } includes includeslist switchlist eswitches switches
  145. macro_statement macro_statements case_statement case_statements
  146. eval_arglist application_call application_call_head
  147. macro_call target jumptarget statement switch_statement
  148. if_like_head statements extension
  149. ignorepat element elements arglist assignment local_assignment
  150. global_statements globals macro context object objects
  151. opt_else
  152. timespec included_entry
  153. %destructor { free($$);} word word_list goto_word word3_list opt_word context_name
  154. timerange
  155. test_expr
  156. opt_pri
  157. %%
  158. file : objects { $$ = parseio->pval = $1; }
  159. ;
  160. objects : object {$$=$1;}
  161. | objects object { $$ = linku1($1, $2); }
  162. | objects error {$$=$1;}
  163. ;
  164. object : context {$$=$1;}
  165. | macro {$$=$1;}
  166. | globals {$$=$1;}
  167. | SEMI {$$=0;/* allow older docs to be read */}
  168. ;
  169. context_name : word { $$ = $1; }
  170. | KW_DEFAULT { $$ = strdup("default"); }
  171. ;
  172. context : opt_abstract KW_CONTEXT context_name LC elements RC {
  173. $$ = npval2(PV_CONTEXT, &@1, &@6);
  174. $$->u1.str = $3;
  175. $$->u2.statements = $5;
  176. set_dads($$,$5);
  177. $$->u3.abstract = $1;}
  178. ;
  179. /* optional "abstract" keyword XXX there is no regression test for this */
  180. opt_abstract: KW_ABSTRACT { $$ = 1; }
  181. | /* nothing */ { $$ = 0; }
  182. | KW_EXTEND { $$ = 2; }
  183. | KW_EXTEND KW_ABSTRACT { $$=3; }
  184. | KW_ABSTRACT KW_EXTEND { $$=3; }
  185. ;
  186. macro : KW_MACRO word LP arglist RP LC macro_statements RC {
  187. $$ = npval2(PV_MACRO, &@1, &@8);
  188. $$->u1.str = $2; $$->u2.arglist = $4; $$->u3.macro_statements = $7;
  189. set_dads($$,$7);}
  190. ;
  191. globals : KW_GLOBALS LC global_statements RC {
  192. $$ = npval2(PV_GLOBALS, &@1, &@4);
  193. $$->u1.statements = $3;
  194. set_dads($$,$3);}
  195. ;
  196. global_statements : { $$ = NULL; }
  197. | global_statements assignment {$$ = linku1($1, $2); }
  198. | error global_statements {$$=$2;}
  199. ;
  200. assignment : word EQ { reset_semicount(parseio->scanner); } word SEMI {
  201. $$ = npval2(PV_VARDEC, &@1, &@5);
  202. $$->u1.str = $1;
  203. $$->u2.val = $4; }
  204. ;
  205. local_assignment : KW_LOCAL word EQ { reset_semicount(parseio->scanner); } word SEMI {
  206. $$ = npval2(PV_LOCALVARDEC, &@1, &@6);
  207. $$->u1.str = $2;
  208. $$->u2.val = $5; }
  209. ;
  210. /* XXX this matches missing arguments, is this desired ? */
  211. arglist : /* empty */ { $$ = NULL; }
  212. | word { $$ = nword($1, &@1); }
  213. | arglist COMMA word { $$ = linku1($1, nword($3, &@3)); }
  214. | arglist error {$$=$1;}
  215. ;
  216. elements : {$$=0;}
  217. | elements element { $$ = linku1($1, $2); }
  218. | error elements { $$=$2;}
  219. ;
  220. element : extension {$$=$1;}
  221. | includes {$$=$1;}
  222. | switches {$$=$1;}
  223. | eswitches {$$=$1;}
  224. | ignorepat {$$=$1;}
  225. | assignment {$$=$1;}
  226. | local_assignment {$$=$1;}
  227. | word error {free($1); $$=0;}
  228. | SEMI {$$=0;/* allow older docs to be read */}
  229. ;
  230. ignorepat : KW_IGNOREPAT EXTENMARK word SEMI {
  231. $$ = npval2(PV_IGNOREPAT, &@1, &@4);
  232. $$->u1.str = $3;}
  233. ;
  234. extension : word EXTENMARK statement {
  235. $$ = npval2(PV_EXTENSION, &@1, &@3);
  236. $$->u1.str = $1;
  237. $$->u2.statements = $3; set_dads($$,$3);}
  238. | word AT word EXTENMARK statement {
  239. $$ = npval2(PV_EXTENSION, &@1, &@3);
  240. $$->u1.str = malloc(strlen($1)+strlen($3)+2);
  241. strcpy($$->u1.str,$1);
  242. strcat($$->u1.str,"@");
  243. strcat($$->u1.str,$3);
  244. free($1);
  245. $$->u2.statements = $5; set_dads($$,$5);}
  246. | KW_REGEXTEN word EXTENMARK statement {
  247. $$ = npval2(PV_EXTENSION, &@1, &@4);
  248. $$->u1.str = $2;
  249. $$->u2.statements = $4; set_dads($$,$4);
  250. $$->u4.regexten=1;}
  251. | KW_HINT LP hint_word RP word EXTENMARK statement {
  252. $$ = npval2(PV_EXTENSION, &@1, &@7);
  253. $$->u1.str = $5;
  254. $$->u2.statements = $7; set_dads($$,$7);
  255. $$->u3.hints = $3;}
  256. | KW_REGEXTEN KW_HINT LP hint_word RP word EXTENMARK statement {
  257. $$ = npval2(PV_EXTENSION, &@1, &@8);
  258. $$->u1.str = $6;
  259. $$->u2.statements = $8; set_dads($$,$8);
  260. $$->u4.regexten=1;
  261. $$->u3.hints = $4;}
  262. ;
  263. /* list of statements in a block or after a case label - can be empty */
  264. statements : /* empty */ { $$ = NULL; }
  265. | statements statement { $$ = linku1($1, $2); }
  266. | error statements {$$=$2;}
  267. ;
  268. /* hh:mm-hh:mm, due to the way the parser works we do not
  269. * detect the '-' but only the ':' as separator
  270. */
  271. timerange: word3_list COLON word3_list COLON word3_list {
  272. if (asprintf(&$$, "%s:%s:%s", $1, $3, $5) < 0) {
  273. ast_log(LOG_WARNING, "asprintf() failed\n");
  274. $$ = NULL;
  275. } else {
  276. free($1);
  277. free($3);
  278. free($5);
  279. }
  280. }
  281. | word { $$ = $1; }
  282. ;
  283. /* full time specification range|dow|*|* */
  284. timespec : timerange BAR word3_list BAR word3_list BAR word3_list {
  285. $$ = nword($1, &@1);
  286. $$->next = nword($3, &@3);
  287. $$->next->next = nword($5, &@5);
  288. $$->next->next->next = nword($7, &@7); }
  289. ;
  290. /* expression used in if, random, while, switch */
  291. test_expr : LP { reset_parencount(parseio->scanner); } word_list RP { $$ = $3; }
  292. ;
  293. /* 'if' like statements: if, iftime, random */
  294. if_like_head : KW_IF test_expr {
  295. $$= npval2(PV_IF, &@1, &@2);
  296. $$->u1.str = $2; }
  297. | KW_RANDOM test_expr {
  298. $$ = npval2(PV_RANDOM, &@1, &@2);
  299. $$->u1.str=$2;}
  300. | KW_IFTIME LP timespec RP {
  301. $$ = npval2(PV_IFTIME, &@1, &@4);
  302. $$->u1.list = $3;
  303. prev_word = 0; }
  304. ;
  305. /* word_list is a hack to fix a problem with context switching between bison and flex;
  306. by the time you register a new context with flex, you've already got a look-ahead token
  307. from the old context, with no way to put it back and start afresh. So, we kludge this
  308. and merge the words back together. */
  309. word_list : word { $$ = $1;}
  310. | word word {
  311. if (asprintf(&($$), "%s%s", $1, $2) < 0) {
  312. ast_log(LOG_WARNING, "asprintf() failed\n");
  313. $$ = NULL;
  314. } else {
  315. free($1);
  316. free($2);
  317. prev_word = $$;
  318. }
  319. }
  320. ;
  321. hint_word : word { $$ = $1; }
  322. | hint_word word {
  323. if (asprintf(&($$), "%s %s", $1, $2) < 0) {
  324. ast_log(LOG_WARNING, "asprintf() failed\n");
  325. $$ = NULL;
  326. } else {
  327. free($1);
  328. free($2);
  329. }
  330. }
  331. | hint_word COLON word {
  332. if (asprintf(&($$), "%s:%s", $1, $3) < 0) {
  333. ast_log(LOG_WARNING, "asprintf() failed\n");
  334. $$ = NULL;
  335. } else {
  336. free($1);
  337. free($3);
  338. }
  339. }
  340. | hint_word AMPER word { /* there are often '&' in hints */
  341. if (asprintf(&($$), "%s&%s", $1, $3) < 0) {
  342. ast_log(LOG_WARNING, "asprintf() failed\n");
  343. $$ = NULL;
  344. } else {
  345. free($1);
  346. free($3);
  347. }
  348. }
  349. | hint_word AT word {
  350. if (asprintf(&($$), "%s@%s", $1, $3) < 0) {
  351. ast_log(LOG_WARNING, "asprintf() failed\n");
  352. $$ = NULL;
  353. } else {
  354. free($1);
  355. free($3);
  356. }
  357. }
  358. ;
  359. word3_list : word { $$ = $1;}
  360. | word word {
  361. if (asprintf(&($$), "%s%s", $1, $2) < 0) {
  362. ast_log(LOG_WARNING, "asprintf() failed\n");
  363. $$ = NULL;
  364. } else {
  365. free($1);
  366. free($2);
  367. prev_word = $$;
  368. }
  369. }
  370. | word word word {
  371. if (asprintf(&($$), "%s%s%s", $1, $2, $3) < 0) {
  372. ast_log(LOG_WARNING, "asprintf() failed\n");
  373. $$ = NULL;
  374. } else {
  375. free($1);
  376. free($2);
  377. free($3);
  378. prev_word=$$;
  379. }
  380. }
  381. ;
  382. goto_word : word { $$ = $1;}
  383. | word word {
  384. if (asprintf(&($$), "%s%s", $1, $2) < 0) {
  385. ast_log(LOG_WARNING, "asprintf() failed\n");
  386. $$ = NULL;
  387. } else {
  388. free($1);
  389. free($2);
  390. }
  391. }
  392. | goto_word COLON word {
  393. if (asprintf(&($$), "%s:%s", $1, $3) < 0) {
  394. ast_log(LOG_WARNING, "asprintf() failed\n");
  395. $$ = NULL;
  396. } else {
  397. free($1);
  398. free($3);
  399. }
  400. }
  401. ;
  402. switch_statement : KW_SWITCH test_expr LC case_statements RC {
  403. $$ = npval2(PV_SWITCH, &@1, &@5);
  404. $$->u1.str = $2;
  405. $$->u2.statements = $4; set_dads($$,$4);}
  406. ;
  407. /*
  408. * Definition of a statememt in our language
  409. */
  410. statement : LC statements RC {
  411. $$ = npval2(PV_STATEMENTBLOCK, &@1, &@3);
  412. $$->u1.list = $2; set_dads($$,$2);}
  413. | assignment { $$ = $1; }
  414. | local_assignment { $$ = $1; }
  415. | KW_GOTO target SEMI {
  416. $$ = npval2(PV_GOTO, &@1, &@3);
  417. $$->u1.list = $2;}
  418. | KW_JUMP jumptarget SEMI {
  419. $$ = npval2(PV_GOTO, &@1, &@3);
  420. $$->u1.list = $2;}
  421. | word COLON {
  422. $$ = npval2(PV_LABEL, &@1, &@2);
  423. $$->u1.str = $1; }
  424. | KW_FOR LP {reset_semicount(parseio->scanner);} word SEMI
  425. {reset_semicount(parseio->scanner);} word SEMI
  426. {reset_parencount(parseio->scanner);} word RP statement { /* XXX word_list maybe ? */
  427. $$ = npval2(PV_FOR, &@1, &@12);
  428. $$->u1.for_init = $4;
  429. $$->u2.for_test=$7;
  430. $$->u3.for_inc = $10;
  431. $$->u4.for_statements = $12; set_dads($$,$12);}
  432. | KW_WHILE test_expr statement {
  433. $$ = npval2(PV_WHILE, &@1, &@3);
  434. $$->u1.str = $2;
  435. $$->u2.statements = $3; set_dads($$,$3);}
  436. | switch_statement { $$ = $1; }
  437. | AMPER macro_call SEMI { $$ = update_last($2, &@2); }
  438. | application_call SEMI { $$ = update_last($1, &@2); }
  439. | word SEMI {
  440. $$= npval2(PV_APPLICATION_CALL, &@1, &@2);
  441. $$->u1.str = $1;}
  442. | application_call EQ {reset_semicount(parseio->scanner);} word SEMI {
  443. char *bufx;
  444. int tot=0;
  445. pval *pptr;
  446. $$ = npval2(PV_VARDEC, &@1, &@5);
  447. $$->u2.val=$4;
  448. /* rebuild the original string-- this is not an app call, it's an unwrapped vardec, with a func call on the LHS */
  449. /* string to big to fit in the buffer? */
  450. tot+=strlen($1->u1.str);
  451. for(pptr=$1->u2.arglist;pptr;pptr=pptr->next) {
  452. tot+=strlen(pptr->u1.str);
  453. tot++; /* for a sep like a comma */
  454. }
  455. tot+=4; /* for safety */
  456. bufx = calloc(1, tot);
  457. strcpy(bufx,$1->u1.str);
  458. strcat(bufx,"(");
  459. /* XXX need to advance the pointer or the loop is very inefficient */
  460. for (pptr=$1->u2.arglist;pptr;pptr=pptr->next) {
  461. if ( pptr != $1->u2.arglist )
  462. strcat(bufx,",");
  463. strcat(bufx,pptr->u1.str);
  464. }
  465. strcat(bufx,")");
  466. #ifdef AAL_ARGCHECK
  467. if ( !ael_is_funcname($1->u1.str) )
  468. 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",
  469. my_file, @1.first_line, @1.first_column, @1.last_column, $1->u1.str);
  470. #endif
  471. $$->u1.str = bufx;
  472. destroy_pval($1); /* the app call it is not, get rid of that chain */
  473. prev_word = 0;
  474. }
  475. | KW_BREAK SEMI { $$ = npval2(PV_BREAK, &@1, &@2); }
  476. | KW_RETURN SEMI { $$ = npval2(PV_RETURN, &@1, &@2); }
  477. | KW_CONTINUE SEMI { $$ = npval2(PV_CONTINUE, &@1, &@2); }
  478. | if_like_head statement opt_else {
  479. $$ = update_last($1, &@2);
  480. $$->u2.statements = $2; set_dads($$,$2);
  481. $$->u3.else_statements = $3;set_dads($$,$3);}
  482. | SEMI { $$=0; }
  483. ;
  484. opt_else : KW_ELSE statement { $$ = $2; }
  485. | { $$ = NULL ; }
  486. target : goto_word { $$ = nword($1, &@1); }
  487. | goto_word BAR goto_word {
  488. $$ = nword($1, &@1);
  489. $$->next = nword($3, &@3); }
  490. | goto_word COMMA goto_word {
  491. $$ = nword($1, &@1);
  492. $$->next = nword($3, &@3); }
  493. | goto_word BAR goto_word BAR goto_word {
  494. $$ = nword($1, &@1);
  495. $$->next = nword($3, &@3);
  496. $$->next->next = nword($5, &@5); }
  497. | goto_word COMMA goto_word COMMA goto_word {
  498. $$ = nword($1, &@1);
  499. $$->next = nword($3, &@3);
  500. $$->next->next = nword($5, &@5); }
  501. | KW_DEFAULT BAR goto_word BAR goto_word {
  502. $$ = nword(strdup("default"), &@1);
  503. $$->next = nword($3, &@3);
  504. $$->next->next = nword($5, &@5); }
  505. | KW_DEFAULT COMMA goto_word COMMA goto_word {
  506. $$ = nword(strdup("default"), &@1);
  507. $$->next = nword($3, &@3);
  508. $$->next->next = nword($5, &@5); }
  509. ;
  510. opt_pri : /* empty */ { $$ = strdup("1"); }
  511. | COMMA word { $$ = $2; }
  512. ;
  513. /* XXX please document the form of jumptarget */
  514. jumptarget : goto_word opt_pri { /* ext[, pri] default 1 */
  515. $$ = nword($1, &@1);
  516. $$->next = nword($2, &@2); } /* jump extension[,priority][@context] */
  517. | goto_word opt_pri AT context_name { /* context, ext, pri */
  518. $$ = nword($4, &@4);
  519. $$->next = nword($1, &@1);
  520. $$->next->next = nword($2, &@2); }
  521. ;
  522. macro_call : word LP {reset_argcount(parseio->scanner);} eval_arglist RP {
  523. /* XXX original code had @2 but i think we need @5 */
  524. $$ = npval2(PV_MACRO_CALL, &@1, &@5);
  525. $$->u1.str = $1;
  526. $$->u2.arglist = $4;}
  527. | word LP RP {
  528. $$= npval2(PV_MACRO_CALL, &@1, &@3);
  529. $$->u1.str = $1; }
  530. ;
  531. /* XXX application_call_head must be revised. Having 'word LP { ...'
  532. * just as above should work fine, however it gives a different result.
  533. */
  534. application_call_head: word LP {reset_argcount(parseio->scanner);} {
  535. if (strcasecmp($1,"goto") == 0) {
  536. $$ = npval2(PV_GOTO, &@1, &@2);
  537. free($1); /* won't be using this */
  538. 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 );
  539. } else {
  540. $$= npval2(PV_APPLICATION_CALL, &@1, &@2);
  541. $$->u1.str = $1;
  542. } }
  543. ;
  544. application_call : application_call_head eval_arglist RP {
  545. $$ = update_last($1, &@3);
  546. if( $$->type == PV_GOTO )
  547. $$->u1.list = $2;
  548. else
  549. $$->u2.arglist = $2;
  550. }
  551. | application_call_head RP { $$ = update_last($1, &@2); }
  552. ;
  553. opt_word : word { $$ = $1 }
  554. | { $$ = strdup(""); }
  555. ;
  556. eval_arglist : word_list { $$ = nword($1, &@1); }
  557. | /*nothing! */ {
  558. $$= npval(PV_WORD,0/*@1.first_line*/,0/*@1.last_line*/,0/* @1.first_column*/, 0/*@1.last_column*/);
  559. $$->u1.str = strdup(""); }
  560. | eval_arglist COMMA opt_word { $$ = linku1($1, nword($3, &@3)); }
  561. ;
  562. case_statements: /* empty */ { $$ = NULL; }
  563. | case_statements case_statement { $$ = linku1($1, $2); }
  564. ;
  565. case_statement: KW_CASE word COLON statements {
  566. $$ = npval2(PV_CASE, &@1, &@3); /* XXX 3 or 4 ? */
  567. $$->u1.str = $2;
  568. $$->u2.statements = $4; set_dads($$,$4);}
  569. | KW_DEFAULT COLON statements {
  570. $$ = npval2(PV_DEFAULT, &@1, &@3);
  571. $$->u1.str = NULL;
  572. $$->u2.statements = $3;set_dads($$,$3);}
  573. | KW_PATTERN word COLON statements {
  574. $$ = npval2(PV_PATTERN, &@1, &@4); /* XXX@3 or @4 ? */
  575. $$->u1.str = $2;
  576. $$->u2.statements = $4;set_dads($$,$4);}
  577. ;
  578. macro_statements: /* empty */ { $$ = NULL; }
  579. | macro_statements macro_statement { $$ = linku1($1, $2); }
  580. ;
  581. macro_statement : statement {$$=$1;}
  582. | includes { $$=$1;}
  583. | KW_CATCH word LC statements RC {
  584. $$ = npval2(PV_CATCH, &@1, &@5);
  585. $$->u1.str = $2;
  586. $$->u2.statements = $4; set_dads($$,$4);}
  587. ;
  588. switches : KW_SWITCHES LC switchlist RC {
  589. $$ = npval2(PV_SWITCHES, &@1, &@2);
  590. $$->u1.list = $3; set_dads($$,$3);}
  591. ;
  592. eswitches : KW_ESWITCHES LC switchlist RC {
  593. $$ = npval2(PV_ESWITCHES, &@1, &@2);
  594. $$->u1.list = $3; set_dads($$,$3);}
  595. ;
  596. switchlist : /* empty */ { $$ = NULL; }
  597. | switchlist word SEMI { $$ = linku1($1,nword($2, &@2)); }
  598. | switchlist word AT word SEMI {
  599. char *x;
  600. if (asprintf(&x,"%s@%s", $2, $4) < 0) {
  601. ast_log(LOG_WARNING, "asprintf() failed\n");
  602. $$ = NULL;
  603. } else {
  604. free($2);
  605. free($4);
  606. $$ = linku1($1,nword(x, &@2));
  607. }
  608. }
  609. | error switchlist {$$=$2;}
  610. ;
  611. included_entry : context_name { $$ = nword($1, &@1); }
  612. | context_name BAR timespec {
  613. $$ = nword($1, &@1);
  614. $$->u2.arglist = $3;
  615. prev_word=0; /* XXX sure ? */ }
  616. ;
  617. /* list of ';' separated context names followed by optional timespec */
  618. includeslist : included_entry SEMI { $$ = $1; }
  619. | includeslist included_entry SEMI { $$ = linku1($1, $2); }
  620. | includeslist error {$$=$1;}
  621. ;
  622. includes : KW_INCLUDES LC includeslist RC {
  623. $$ = npval2(PV_INCLUDES, &@1, &@4);
  624. $$->u1.list = $3;set_dads($$,$3);}
  625. | KW_INCLUDES LC RC {
  626. $$ = npval2(PV_INCLUDES, &@1, &@3);}
  627. ;
  628. %%
  629. static char *token_equivs1[] =
  630. {
  631. "AMPER",
  632. "AT",
  633. "BAR",
  634. "COLON",
  635. "COMMA",
  636. "EQ",
  637. "EXTENMARK",
  638. "KW_BREAK",
  639. "KW_CASE",
  640. "KW_CATCH",
  641. "KW_CONTEXT",
  642. "KW_CONTINUE",
  643. "KW_DEFAULT",
  644. "KW_ELSE",
  645. "KW_ESWITCHES",
  646. "KW_FOR",
  647. "KW_GLOBALS",
  648. "KW_GOTO",
  649. "KW_HINT",
  650. "KW_IFTIME",
  651. "KW_IF",
  652. "KW_IGNOREPAT",
  653. "KW_INCLUDES"
  654. "KW_JUMP",
  655. "KW_MACRO",
  656. "KW_PATTERN",
  657. "KW_REGEXTEN",
  658. "KW_RETURN",
  659. "KW_SWITCHES",
  660. "KW_SWITCH",
  661. "KW_WHILE",
  662. "LC",
  663. "LP",
  664. "RC",
  665. "RP",
  666. "SEMI",
  667. };
  668. static char *token_equivs2[] =
  669. {
  670. "&",
  671. "@",
  672. "|",
  673. ":",
  674. ",",
  675. "=",
  676. "=>",
  677. "break",
  678. "case",
  679. "catch",
  680. "context",
  681. "continue",
  682. "default",
  683. "else",
  684. "eswitches",
  685. "for",
  686. "globals",
  687. "goto",
  688. "hint",
  689. "ifTime",
  690. "if",
  691. "ignorepat",
  692. "includes"
  693. "jump",
  694. "macro",
  695. "pattern",
  696. "regexten",
  697. "return",
  698. "switches",
  699. "switch",
  700. "while",
  701. "{",
  702. "(",
  703. "}",
  704. ")",
  705. ";",
  706. };
  707. static char *ael_token_subst(const char *mess)
  708. {
  709. /* calc a length, malloc, fill, and return; yyerror had better free it! */
  710. int len=0,i;
  711. const char *p;
  712. char *res, *s,*t;
  713. int token_equivs_entries = sizeof(token_equivs1)/sizeof(char*);
  714. for (p=mess; *p; p++) {
  715. for (i=0; i<token_equivs_entries; i++) {
  716. if ( strncmp(p,token_equivs1[i],strlen(token_equivs1[i])) == 0 )
  717. {
  718. len+=strlen(token_equivs2[i])+2;
  719. p += strlen(token_equivs1[i])-1;
  720. break;
  721. }
  722. }
  723. len++;
  724. }
  725. res = calloc(1, len+1);
  726. res[0] = 0;
  727. s = res;
  728. for (p=mess; *p;) {
  729. int found = 0;
  730. for (i=0; i<token_equivs_entries; i++) {
  731. if ( strncmp(p,token_equivs1[i],strlen(token_equivs1[i])) == 0 ) {
  732. *s++ = '\'';
  733. for (t=token_equivs2[i]; *t;) {
  734. *s++ = *t++;
  735. }
  736. *s++ = '\'';
  737. p += strlen(token_equivs1[i]);
  738. found = 1;
  739. break;
  740. }
  741. }
  742. if( !found )
  743. *s++ = *p++;
  744. }
  745. *s++ = 0;
  746. return res;
  747. }
  748. void yyerror(YYLTYPE *locp, struct parse_io *parseio, char const *s)
  749. {
  750. char *s2 = ael_token_subst((char *)s);
  751. if (locp->first_line == locp->last_line) {
  752. 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);
  753. } else {
  754. 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);
  755. }
  756. free(s2);
  757. parseio->syntax_error_count++;
  758. }
  759. struct pval *npval(pvaltype type, int first_line, int last_line,
  760. int first_column, int last_column)
  761. {
  762. pval *z = calloc(1, sizeof(struct pval));
  763. z->type = type;
  764. z->startline = first_line;
  765. z->endline = last_line;
  766. z->startcol = first_column;
  767. z->endcol = last_column;
  768. z->filename = strdup(S_OR(my_file, "<none>"));
  769. return z;
  770. }
  771. static struct pval *npval2(pvaltype type, YYLTYPE *first, YYLTYPE *last)
  772. {
  773. return npval(type, first->first_line, last->last_line,
  774. first->first_column, last->last_column);
  775. }
  776. static struct pval *update_last(pval *obj, YYLTYPE *last)
  777. {
  778. obj->endline = last->last_line;
  779. obj->endcol = last->last_column;
  780. return obj;
  781. }
  782. /* frontend for npval to create a PV_WORD string from the given token */
  783. static pval *nword(char *string, YYLTYPE *pos)
  784. {
  785. pval *p = npval2(PV_WORD, pos, pos);
  786. if (p)
  787. p->u1.str = string;
  788. return p;
  789. }
  790. /* this routine adds a dad ptr to each element in the list */
  791. static void set_dads(struct pval *dad, struct pval *child_list)
  792. {
  793. struct pval *t;
  794. for(t=child_list;t;t=t->next) /* simple stuff */
  795. t->dad = dad;
  796. }