openscad-parser.jison 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571
  1. /* description: Parses openscad to openjscad. */
  2. /* lexical grammar */
  3. %lex
  4. %options flex
  5. %s cond_include cond_use cond_comment cond_string cond_import
  6. D [0-9]
  7. E [Ee][+-]?{D}+
  8. %%
  9. /* Note: use and include statements here are ignored. Instead they are preprocessed. */
  10. include[ \t\r\n>]*"<" %{ this.begin('cond_include'); %}
  11. <cond_include>[^\t\r\n>]*"/" %{ yy.filepath = yytext; %}
  12. <cond_include>[^\t\r\n>/]+ %{ yy.filename = yytext; %}
  13. <cond_include>">" %{ this.popState(); %}
  14. use[ \t\r\n>]*"<" %{ this.begin('cond_use');%}
  15. <cond_use>[^\t\r\n>]+ %{ yy.filename = yytext; %}
  16. <cond_use>">" %{ this.popState(); %}
  17. "module" return 'TOK_MODULE'
  18. "function" return 'TOK_FUNCTION'
  19. "if" return 'TOK_IF'
  20. "else" return 'TOK_ELSE'
  21. "true" return 'TOK_TRUE'
  22. "false" return 'TOK_FALSE'
  23. "undef" return 'TOK_UNDEF'
  24. <cond_string>"\\t" %{ stringcontents += ' '; %}
  25. <cond_string>"\\n" %{ stringcontents += '\n'; %}
  26. <cond_string>"\\\"" %{ stringcontents += '\"'; %}
  27. <cond_string>"\\r" %{ stringcontents += '\r'; %}
  28. <cond_string>"\\\\" %{ stringcontents += '\\'; %}
  29. <cond_string>"\\0" %{ stringcontents += '\0'; %}
  30. <cond_string>"\\a" %{ stringcontents += '\a'; %}
  31. <cond_string>"\\b" %{ stringcontents += '\b'; %}
  32. <cond_string>"\\t" %{ stringcontents += '\t'; %}
  33. <cond_string>"\\n" %{ stringcontents += '\n'; %}
  34. <cond_string>"\\v" %{ stringcontents += '\v'; %}
  35. <cond_string>"\\f" %{ stringcontents += '\f'; %}
  36. <cond_string>"\\e" %{ stringcontents += '\e'; %}
  37. <cond_string>[^\\\n\"]+ %{ /*"*/
  38. stringcontents += yytext;
  39. %}
  40. <cond_string>"\"" %{
  41. this.popState();
  42. yytext = stringcontents;
  43. return 'TOK_STRING';
  44. %}
  45. [\"] %{ /*"*/
  46. this.begin('cond_string');
  47. stringcontents = "";
  48. %}
  49. [\n] /* Ignore */
  50. [\r\t ] /* Ignore */
  51. \/\/[^\n]*\n? /* Ignore */
  52. \/\*.+\*\/ /* Ignore Note: multi-line comments are removed via a preparse regex. */
  53. {D}*\.{D}+{E}? return 'TOK_NUMBER'
  54. {D}+\.{D}*{E}? return 'TOK_NUMBER'
  55. {D}+{E}? return 'TOK_NUMBER'
  56. "$"?[a-zA-Z0-9_]+ return 'TOK_ID'
  57. "<=" return 'LE'
  58. ">=" return 'GE'
  59. "==" return 'EQ'
  60. "!=" return 'NE'
  61. "&&" return 'AND'
  62. "||" return 'OR'
  63. . return yytext;
  64. /lex
  65. /* operator associations and precedence */
  66. %right '?' ':'
  67. %left OR
  68. %left AND
  69. %left '<' LE GE '>'
  70. %left EQ NE
  71. %left '!' '+' '-'
  72. %left '*' '/' '%'
  73. %left '[' ']'
  74. %left '.'
  75. %start program
  76. %% /* language grammar */
  77. program:
  78. input
  79. {
  80. return ext.processModule(yy);
  81. }
  82. ;
  83. input:
  84. /* empty */
  85. | input statement
  86. ;
  87. inner_input:
  88. /* empty */
  89. | inner_input statement
  90. ;
  91. statement:
  92. statement_begin statement_end
  93. ;
  94. statement_begin:
  95. /*empty*/
  96. | TOK_MODULE TOK_ID '(' arguments_decl optional_commas ')'
  97. {
  98. ext.stashModule($2, $4.argnames, $4.argexpr);
  99. delete $4;
  100. }
  101. ;
  102. statement_end:
  103. ';'
  104. {
  105. }
  106. | '{' inner_input '}'
  107. {
  108. ext.popModule();
  109. }
  110. | module_instantiation
  111. {
  112. ext.addModuleChild($1);
  113. }
  114. | TOK_ID '=' expr ';'
  115. {
  116. ext.addModuleAssignmentVar($1, $3);
  117. }
  118. | TOK_FUNCTION TOK_ID '(' arguments_decl optional_commas ')' '=' expr ';'
  119. {
  120. ext.addModuleFunction($2, $8, $4.argnames, $4.argexpr);
  121. delete $4;
  122. }
  123. | BR
  124. ;
  125. children_instantiation:
  126. module_instantiation
  127. {
  128. $$ = new ModuleInstantiation();
  129. if ($1) {
  130. $$.children.push($1);
  131. }
  132. }
  133. | '{' module_instantiation_list '}'
  134. {
  135. $$ = $2;
  136. }
  137. ;
  138. if_statement:
  139. TOK_IF '(' expr ')' children_instantiation
  140. {
  141. $$ = new IfElseModuleInstantiation();
  142. $$.argnames.push("");
  143. $$.argexpr.push($3);
  144. if ($$) {
  145. $$.children = $5.children;
  146. } else {
  147. for (var i = 0; i < $5.children.size(); i++)
  148. delete $5.children[i];
  149. }
  150. delete $5;
  151. }
  152. ;
  153. ifelse_statement:
  154. if_statement
  155. {
  156. $$ = $1;
  157. }
  158. | if_statement TOK_ELSE children_instantiation
  159. {
  160. $$ = $1;
  161. if ($$) {
  162. $$.else_children = $3.children;
  163. } else {
  164. for (var i = 0; i < $3.children.size(); i++)
  165. delete $3.children[i];
  166. }
  167. delete $3;
  168. }
  169. ;
  170. module_instantiation:
  171. single_module_instantiation ';'
  172. {
  173. $$ = $1;
  174. }
  175. | single_module_instantiation children_instantiation
  176. {
  177. $$ = $1;
  178. if ($$) {
  179. $$.children = $2.children;
  180. } else {
  181. for (var i = 0; i < $2.children.length; i++)
  182. delete $2.children[i];
  183. }
  184. delete $2;
  185. }
  186. |
  187. ifelse_statement
  188. {
  189. $$ = $1;
  190. }
  191. ;
  192. module_instantiation_list:
  193. /* empty */
  194. {
  195. $$ = new ModuleInstantiation();
  196. }
  197. | module_instantiation_list module_instantiation
  198. {
  199. $$ = $1;
  200. if ($$) {
  201. if ($2) {
  202. $$.children.push($2);
  203. }
  204. } else {
  205. delete $2;
  206. }
  207. }
  208. ;
  209. single_module_instantiation:
  210. TOK_ID '(' arguments_call ')'
  211. {
  212. $$ = new ModuleInstantiation();
  213. $$.name = $1;
  214. $$.argnames = $3.argnames;
  215. $$.argexpr = $3.argexpr;
  216. delete $3;
  217. }
  218. | '!' single_module_instantiation
  219. {
  220. $$ = $2;
  221. if ($$) {
  222. $$.tag_root = true;
  223. }
  224. }
  225. | '#' single_module_instantiation
  226. {
  227. $$ = $2;
  228. if ($$) {
  229. $$.tag_highlight = true;
  230. }
  231. }
  232. | '%' single_module_instantiation
  233. {
  234. /* - NOTE: Currently unimplemented, therefore not displaying parts marked with %
  235. $$ = $2;
  236. if ($$) {
  237. $$.tag_background = true;
  238. }
  239. */
  240. delete $2;
  241. $$ = undefined;
  242. }
  243. | '*' single_module_instantiation
  244. {
  245. delete $2;
  246. $$ = undefined;
  247. }
  248. ;
  249. expr:
  250. TOK_TRUE
  251. {
  252. $$ = new Expression(true);
  253. }
  254. | TOK_FALSE
  255. {
  256. $$ = new Expression(false);
  257. }
  258. | TOK_UNDEF
  259. {
  260. $$ = new Expression(undefined);
  261. }
  262. | TOK_ID
  263. {
  264. $$ = new Expression();
  265. $$.type = "L";
  266. $$.var_name = $1;
  267. }
  268. | expr '.' TOK_ID
  269. {
  270. $$ = new Expression();
  271. $$.type = "N";
  272. $$.children.push($1);
  273. $$.var_name = $3;
  274. }
  275. | TOK_STRING
  276. {
  277. $$ = new Expression(String($1));
  278. }
  279. | TOK_NUMBER
  280. {
  281. $$ = new Expression(Number($1));
  282. }
  283. | '[' expr ':' expr ']'
  284. {
  285. var e_one = new Expression(1.0);
  286. $$ = new Expression();
  287. $$.type = "R";
  288. $$.children.push($2);
  289. $$.children.push(e_one);
  290. $$.children.push($4);
  291. }
  292. | '[' expr ':' expr ':' expr ']'
  293. {
  294. $$ = new Expression();
  295. $$.type = "R";
  296. $$.children.push($2);
  297. $$.children.push($4);
  298. $$.children.push($6);
  299. }
  300. | '[' optional_commas ']'
  301. {
  302. $$ = new Expression([]);
  303. }
  304. | '[' vector_expr optional_commas ']'
  305. {
  306. $$ = $2;
  307. }
  308. | expr '*' expr
  309. {
  310. $$ = new Expression();
  311. $$.type = '*';
  312. $$.children.push($1);
  313. $$.children.push($3);
  314. }
  315. | expr '/' expr
  316. {
  317. $$ = new Expression();
  318. $$.type = '/';
  319. $$.children.push($1);
  320. $$.children.push($3);
  321. }
  322. | expr '%' expr
  323. {
  324. $$ = new Expression();
  325. $$.type = '%';
  326. $$.children.push($1);
  327. $$.children.push($3);
  328. }
  329. | expr '+' expr
  330. {
  331. $$ = new Expression();
  332. $$.type = '+';
  333. $$.children.push($1);
  334. $$.children.push($3);
  335. }
  336. | expr '-' expr
  337. {
  338. $$ = new Expression();
  339. $$.type = '-';
  340. $$.children.push($1);
  341. $$.children.push($3);
  342. }
  343. | expr '<' expr
  344. {
  345. $$ = new Expression();
  346. $$.type = '<';
  347. $$.children.push($1);
  348. $$.children.push($3);
  349. }
  350. | expr LE expr
  351. {
  352. $$ = new Expression();
  353. $$.type = '<=';
  354. $$.children.push($1);
  355. $$.children.push($3);
  356. }
  357. | expr EQ expr
  358. {
  359. $$ = new Expression();
  360. $$.type = '==';
  361. $$.children.push($1);
  362. $$.children.push($3);
  363. }
  364. | expr NE expr
  365. {
  366. $$ = new Expression();
  367. $$.type = '!=';
  368. $$.children.push($1);
  369. $$.children.push($3);
  370. }
  371. | expr GE expr
  372. {
  373. $$ = new Expression();
  374. $$.type = '>=';
  375. $$.children.push($1);
  376. $$.children.push($3);
  377. }
  378. | expr '>' expr
  379. {
  380. $$ = new Expression();
  381. $$.type = '>';
  382. $$.children.push($1);
  383. $$.children.push($3);
  384. }
  385. | expr AND expr
  386. {
  387. $$ = new Expression();
  388. $$.type = '&&';
  389. $$.children.push($1);
  390. $$.children.push($3);
  391. }
  392. | expr OR expr
  393. {
  394. $$ = new Expression();
  395. $$.type = '||';
  396. $$.children.push($1);
  397. $$.children.push($3);
  398. }
  399. | '+' expr
  400. {
  401. $$ = $2;
  402. }
  403. | '-' expr
  404. {
  405. $$ = new Expression();
  406. $$.type = 'I';
  407. $$.children.push($2);
  408. }
  409. | '!' expr
  410. {
  411. $$ = new Expression();
  412. $$.type = '!';
  413. $$.children.push($2);
  414. }
  415. | '(' expr ')'
  416. { $$ = $2; }
  417. | expr '?' expr ':' expr
  418. {
  419. $$ = new Expression();
  420. $$.type = '?:';
  421. $$.children.push($1);
  422. $$.children.push($3);
  423. $$.children.push($5);
  424. }
  425. | expr '[' expr ']'
  426. {
  427. $$ = new Expression();
  428. $$.type = '[]';
  429. $$.children.push($1);
  430. $$.children.push($3);
  431. }
  432. | TOK_ID '(' arguments_call ')'
  433. {
  434. $$ = new Expression();
  435. $$.type = 'F';
  436. $$.call_funcname = $1;
  437. $$.call_argnames = $3.argnames;
  438. $$.children = $3.argexpr;
  439. delete $3;
  440. }
  441. ;
  442. optional_commas:
  443. ',' optional_commas
  444. |
  445. ;
  446. vector_expr:
  447. expr
  448. {
  449. $$ = new Expression();
  450. $$.type = 'V';
  451. $$.children.push($1);
  452. }
  453. | vector_expr ',' optional_commas expr
  454. {
  455. $$ = $1;
  456. $$.children.push($4);
  457. }
  458. ;
  459. arguments_decl:
  460. /* empty */
  461. {
  462. $$ = new ArgsContainer();
  463. }
  464. | argument_decl
  465. {
  466. $$ = new ArgsContainer();
  467. $$.argnames.push($1.argname);
  468. $$.argexpr.push($1.argexpr);
  469. delete $1;
  470. }
  471. | arguments_decl ',' optional_commas argument_decl
  472. {
  473. $$ = $1;
  474. $$.argnames.push($4.argname);
  475. $$.argexpr.push($4.argexpr);
  476. delete $4;
  477. }
  478. ;
  479. argument_decl:
  480. TOK_ID
  481. {
  482. $$ = new ArgContainer();
  483. $$.argname = $1;
  484. $$.argexpr = undefined;
  485. }
  486. | TOK_ID '=' expr
  487. {
  488. $$ = new ArgContainer();
  489. $$.argname = $1;
  490. $$.argexpr = $3;
  491. }
  492. ;
  493. arguments_call:
  494. /* empty */
  495. {
  496. $$ = new ArgsContainer();
  497. }
  498. | argument_call
  499. {
  500. $$ = new ArgsContainer();
  501. $$.argnames.push($1.argname);
  502. $$.argexpr.push($1.argexpr);
  503. delete $1;
  504. }
  505. | arguments_call ',' optional_commas argument_call
  506. {
  507. $$ = $1;
  508. $$.argnames.push($4.argname);
  509. $$.argexpr.push($4.argexpr);
  510. delete $4;
  511. }
  512. ;
  513. argument_call:
  514. expr
  515. {
  516. $$ = new ArgContainer();
  517. $$.argexpr = $1;
  518. }
  519. | TOK_ID '=' expr
  520. {
  521. $$ = new ArgContainer();
  522. $$.argname = $1;
  523. $$.argexpr = $3;
  524. }
  525. ;
  526. %%