formcalc_parser.js 28 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117
  1. /**
  2. * @licstart The following is the entire license notice for the
  3. * JavaScript code in this page
  4. *
  5. * Copyright 2022 Mozilla Foundation
  6. *
  7. * Licensed under the Apache License, Version 2.0 (the "License");
  8. * you may not use this file except in compliance with the License.
  9. * You may obtain a copy of the License at
  10. *
  11. * http://www.apache.org/licenses/LICENSE-2.0
  12. *
  13. * Unless required by applicable law or agreed to in writing, software
  14. * distributed under the License is distributed on an "AS IS" BASIS,
  15. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  16. * See the License for the specific language governing permissions and
  17. * limitations under the License.
  18. *
  19. * @licend The above is the entire license notice for the
  20. * JavaScript code in this page
  21. */
  22. "use strict";
  23. Object.defineProperty(exports, "__esModule", {
  24. value: true
  25. });
  26. exports.Parser = exports.Errors = void 0;
  27. var _formcalc_lexer = require("./formcalc_lexer.js");
  28. const Errors = {
  29. assignment: "Invalid token in assignment.",
  30. block: "Invalid token in do ... end declaration.",
  31. elseif: "Invalid elseif declaration.",
  32. for: "Invalid token in for ... endfor declaration.",
  33. foreach: "Invalid token in foreach ... endfor declaration.",
  34. func: "Invalid token in func declaration.",
  35. if: "Invalid token if ... endif declaration.",
  36. index: "Invalid token in index.",
  37. params: "Invalid token in parameter list.",
  38. var: "Invalid token in var declaration.",
  39. while: "Invalid token while ... endwhile declaration."
  40. };
  41. exports.Errors = Errors;
  42. const BUILTINS = new Set(["abs", "avg", "ceil", "count", "floor", "max", "min", "mod", "round", "sum", "date", "date2num", "datefmt", "isodate2num", "isotime2num", "localdatefmt", "localtimefmt", "num2date", "num2gmtime", "num2time", "time", "time2num", "timefmt", "apr", "cterm", "fv", "ipmt", "npv", "pmt", "ppmt", "pv", "rate", "term", "choose", "exists", "hasvalue", "oneof", "within", "at", "concat", "decode", "encode", "format", "left", "len", "lower", "ltrim", "parse", "replace", "right", "rtrim", "space", "str", "stuff", "substr", "uuid", "upper", "wordnum", "get", "post", "put", "eval", "ref", "unitvalue", "unittype", "acos", "asin", "atan", "cos", "deg2rad", "exp", "log", "pi", "pow", "rad2deg", "sin", "sqrt", "tan"]);
  43. const LTR = true;
  44. const RTL = false;
  45. const Operators = {
  46. dot: {
  47. id: 0,
  48. prec: 0,
  49. assoc: RTL,
  50. nargs: 0,
  51. repr: "."
  52. },
  53. dotDot: {
  54. id: 1,
  55. prec: 0,
  56. assoc: RTL,
  57. nargs: 0,
  58. repr: ".."
  59. },
  60. dotHash: {
  61. id: 2,
  62. prec: 0,
  63. assoc: RTL,
  64. nargs: 0,
  65. repr: ".#"
  66. },
  67. call: {
  68. id: 1,
  69. prec: 1,
  70. assoc: LTR,
  71. nargs: 0
  72. },
  73. minus: {
  74. id: 4,
  75. nargs: 1,
  76. prec: 2,
  77. assoc: RTL,
  78. repr: "-",
  79. op: x => -x
  80. },
  81. plus: {
  82. id: 5,
  83. nargs: 1,
  84. prec: 2,
  85. assoc: RTL,
  86. repr: "+",
  87. op: x => +x
  88. },
  89. not: {
  90. id: 6,
  91. nargs: 1,
  92. prec: 2,
  93. assoc: RTL,
  94. repr: "!",
  95. op: x => !x ? 1 : 0
  96. },
  97. mul: {
  98. id: 7,
  99. nargs: 2,
  100. prec: 3,
  101. assoc: LTR,
  102. repr: "*",
  103. op: (x, y) => x * y
  104. },
  105. div: {
  106. id: 8,
  107. nargs: 2,
  108. prec: 3,
  109. assoc: LTR,
  110. repr: "/",
  111. op: (x, y) => x / y
  112. },
  113. add: {
  114. id: 9,
  115. nargs: 2,
  116. prec: 4,
  117. assoc: LTR,
  118. repr: "+",
  119. op: (x, y) => x + y
  120. },
  121. sub: {
  122. id: 10,
  123. nargs: 2,
  124. prec: 4,
  125. assoc: LTR,
  126. repr: "-",
  127. op: (x, y) => x - y
  128. },
  129. lt: {
  130. id: 11,
  131. nargs: 2,
  132. prec: 5,
  133. assoc: LTR,
  134. repr: "<",
  135. op: (x, y) => x < y ? 1 : 0
  136. },
  137. le: {
  138. id: 12,
  139. nargs: 2,
  140. prec: 5,
  141. assoc: LTR,
  142. repr: "<=",
  143. op: (x, y) => x <= y ? 1 : 0
  144. },
  145. gt: {
  146. id: 13,
  147. nargs: 2,
  148. prec: 5,
  149. assoc: LTR,
  150. repr: ">",
  151. op: (x, y) => x > y ? 1 : 0
  152. },
  153. ge: {
  154. id: 14,
  155. nargs: 2,
  156. prec: 5,
  157. assoc: LTR,
  158. repr: ">=",
  159. op: (x, y) => x >= y ? 1 : 0
  160. },
  161. eq: {
  162. id: 15,
  163. nargs: 2,
  164. prec: 6,
  165. assoc: LTR,
  166. repr: "===",
  167. op: (x, y) => x === y ? 1 : 0
  168. },
  169. ne: {
  170. id: 16,
  171. nargs: 2,
  172. prec: 6,
  173. assoc: LTR,
  174. repr: "!==",
  175. op: (x, y) => x !== y ? 1 : 0
  176. },
  177. and: {
  178. id: 17,
  179. nargs: 2,
  180. prec: 7,
  181. assoc: LTR,
  182. repr: "&&",
  183. op: (x, y) => x && y ? 1 : 0
  184. },
  185. or: {
  186. id: 18,
  187. nargs: 2,
  188. prec: 8,
  189. assoc: LTR,
  190. repr: "||",
  191. op: (x, y) => x || y ? 1 : 0
  192. },
  193. paren: {
  194. id: 19,
  195. prec: 9,
  196. assoc: RTL,
  197. nargs: 0
  198. },
  199. subscript: {
  200. id: 20,
  201. prec: 9,
  202. assoc: RTL,
  203. nargs: 0
  204. }
  205. };
  206. const OPERATOR = true;
  207. const OPERAND = false;
  208. class SimpleExprParser {
  209. constructor(lexer) {
  210. this.lexer = lexer;
  211. this.operands = [];
  212. this.operators = [];
  213. this.last = OPERATOR;
  214. }
  215. reset() {
  216. this.operands.length = 0;
  217. this.operators.length = 0;
  218. this.last = OPERATOR;
  219. }
  220. parse(tok) {
  221. tok = tok || this.lexer.next();
  222. while (true) {
  223. switch (tok.id) {
  224. case _formcalc_lexer.TOKEN.and:
  225. if (this.last === OPERAND) {
  226. this.pushOperator(Operators.and);
  227. break;
  228. }
  229. return [tok, this.getNode()];
  230. case _formcalc_lexer.TOKEN.divide:
  231. if (this.last === OPERAND) {
  232. this.pushOperator(Operators.div);
  233. break;
  234. }
  235. return [tok, this.getNode()];
  236. case _formcalc_lexer.TOKEN.dot:
  237. if (this.last === OPERAND) {
  238. this.pushOperator(Operators.dot);
  239. break;
  240. }
  241. return [tok, this.getNode()];
  242. case _formcalc_lexer.TOKEN.dotDot:
  243. if (this.last === OPERAND) {
  244. this.pushOperator(Operators.dotDot);
  245. break;
  246. }
  247. return [tok, this.getNode()];
  248. case _formcalc_lexer.TOKEN.dotHash:
  249. if (this.last === OPERAND) {
  250. this.pushOperator(Operators.dotHash);
  251. break;
  252. }
  253. return [tok, this.getNode()];
  254. case _formcalc_lexer.TOKEN.dotStar:
  255. if (this.last === OPERAND) {
  256. this.pushOperator(Operators.dot);
  257. this.pushOperand(new AstEveryOccurence());
  258. break;
  259. }
  260. return [tok, this.getNode()];
  261. case _formcalc_lexer.TOKEN.eq:
  262. if (this.last === OPERAND) {
  263. this.pushOperator(Operators.eq);
  264. break;
  265. }
  266. return [tok, this.getNode()];
  267. case _formcalc_lexer.TOKEN.ge:
  268. if (this.last === OPERAND) {
  269. this.pushOperator(Operators.ge);
  270. break;
  271. }
  272. return [tok, this.getNode()];
  273. case _formcalc_lexer.TOKEN.gt:
  274. if (this.last === OPERAND) {
  275. this.pushOperator(Operators.gt);
  276. break;
  277. }
  278. return [tok, this.getNode()];
  279. case _formcalc_lexer.TOKEN.le:
  280. if (this.last === OPERAND) {
  281. this.pushOperator(Operators.le);
  282. break;
  283. }
  284. return [tok, this.getNode()];
  285. case _formcalc_lexer.TOKEN.leftBracket:
  286. if (this.last === OPERAND) {
  287. this.flushWithOperator(Operators.subscript);
  288. const operand = this.operands.pop();
  289. const index = SimpleExprParser.parseIndex(this.lexer);
  290. this.operands.push(new AstSubscript(operand, index));
  291. this.last = OPERAND;
  292. break;
  293. }
  294. return [tok, this.getNode()];
  295. case _formcalc_lexer.TOKEN.leftParen:
  296. if (this.last === OPERAND) {
  297. const lastOperand = this.operands.at(-1);
  298. if (!(lastOperand instanceof AstIdentifier)) {
  299. return [tok, this.getNode()];
  300. }
  301. lastOperand.toLowerCase();
  302. const name = lastOperand.id;
  303. this.flushWithOperator(Operators.call);
  304. const callee = this.operands.pop();
  305. const params = SimpleExprParser.parseParams(this.lexer);
  306. if (callee instanceof AstIdentifier && BUILTINS.has(name)) {
  307. this.operands.push(new AstBuiltinCall(name, params));
  308. } else {
  309. this.operands.push(new AstCall(callee, params));
  310. }
  311. this.last = OPERAND;
  312. } else {
  313. this.operators.push(Operators.paren);
  314. this.last = OPERATOR;
  315. }
  316. break;
  317. case _formcalc_lexer.TOKEN.lt:
  318. if (this.last === OPERAND) {
  319. this.pushOperator(Operators.lt);
  320. break;
  321. }
  322. return [tok, this.getNode()];
  323. case _formcalc_lexer.TOKEN.minus:
  324. if (this.last === OPERATOR) {
  325. this.pushOperator(Operators.minus);
  326. } else {
  327. this.pushOperator(Operators.sub);
  328. }
  329. break;
  330. case _formcalc_lexer.TOKEN.ne:
  331. if (this.last === OPERAND) {
  332. this.pushOperator(Operators.ne);
  333. break;
  334. }
  335. return [tok, this.getNode()];
  336. case _formcalc_lexer.TOKEN.not:
  337. if (this.last === OPERAND) {
  338. this.pushOperator(Operators.not);
  339. break;
  340. }
  341. return [tok, this.getNode()];
  342. case _formcalc_lexer.TOKEN.null:
  343. if (this.last === OPERATOR) {
  344. this.pushOperand(new AstNull());
  345. break;
  346. }
  347. return [tok, this.getNode()];
  348. case _formcalc_lexer.TOKEN.number:
  349. if (this.last === OPERATOR) {
  350. this.pushOperand(new AstNumber(tok.value));
  351. break;
  352. }
  353. return [tok, this.getNode()];
  354. case _formcalc_lexer.TOKEN.or:
  355. if (this.last === OPERAND) {
  356. this.pushOperator(Operators.or);
  357. break;
  358. }
  359. return [tok, this.getNode()];
  360. case _formcalc_lexer.TOKEN.plus:
  361. if (this.last === OPERATOR) {
  362. this.pushOperator(Operators.plus);
  363. } else {
  364. this.pushOperator(Operators.add);
  365. }
  366. break;
  367. case _formcalc_lexer.TOKEN.rightBracket:
  368. if (!this.flushUntil(Operators.subscript.id)) {
  369. return [tok, this.getNode()];
  370. }
  371. break;
  372. case _formcalc_lexer.TOKEN.rightParen:
  373. if (!this.flushUntil(Operators.paren.id)) {
  374. return [tok, this.getNode()];
  375. }
  376. break;
  377. case _formcalc_lexer.TOKEN.string:
  378. if (this.last === OPERATOR) {
  379. this.pushOperand(new AstString(tok.value));
  380. break;
  381. }
  382. return [tok, this.getNode()];
  383. case _formcalc_lexer.TOKEN.this:
  384. if (this.last === OPERATOR) {
  385. this.pushOperand(new AstThis());
  386. break;
  387. }
  388. return [tok, this.getNode()];
  389. case _formcalc_lexer.TOKEN.times:
  390. if (this.last === OPERAND) {
  391. this.pushOperator(Operators.mul);
  392. break;
  393. }
  394. return [tok, this.getNode()];
  395. case _formcalc_lexer.TOKEN.identifier:
  396. if (this.last === OPERATOR) {
  397. this.pushOperand(new AstIdentifier(tok.value));
  398. break;
  399. }
  400. return [tok, this.getNode()];
  401. default:
  402. return [tok, this.getNode()];
  403. }
  404. tok = this.lexer.next();
  405. }
  406. }
  407. static parseParams(lexer) {
  408. const parser = new SimpleExprParser(lexer);
  409. const params = [];
  410. while (true) {
  411. const [tok, param] = parser.parse();
  412. if (param) {
  413. params.push(param);
  414. }
  415. if (tok.id === _formcalc_lexer.TOKEN.rightParen) {
  416. return params;
  417. } else if (tok.id !== _formcalc_lexer.TOKEN.comma) {
  418. throw new Error(Errors.params);
  419. }
  420. parser.reset();
  421. }
  422. }
  423. static parseIndex(lexer) {
  424. let tok = lexer.next();
  425. if (tok.id === _formcalc_lexer.TOKEN.times) {
  426. tok = lexer.next();
  427. if (tok.id !== _formcalc_lexer.TOKEN.rightBracket) {
  428. throw new Error(Errors.index);
  429. }
  430. return new AstEveryOccurence();
  431. }
  432. const [token, expr] = new SimpleExprParser(lexer).parse(tok);
  433. if (token.id !== _formcalc_lexer.TOKEN.rightBracket) {
  434. throw new Error(Errors.index);
  435. }
  436. return expr;
  437. }
  438. pushOperator(op) {
  439. this.flushWithOperator(op);
  440. this.operators.push(op);
  441. this.last = OPERATOR;
  442. }
  443. pushOperand(op) {
  444. this.operands.push(op);
  445. this.last = OPERAND;
  446. }
  447. operate(op) {
  448. if (op.nargs === 1) {
  449. const arg = this.operands.pop();
  450. this.operands.push(AstUnaryOperator.getOperatorOrValue(op, arg));
  451. } else {
  452. const arg2 = this.operands.pop();
  453. const arg1 = this.operands.pop();
  454. this.operands.push(AstBinaryOperator.getOperatorOrValue(op, arg1, arg2));
  455. }
  456. }
  457. flushWithOperator(op) {
  458. while (true) {
  459. const top = this.operators.at(-1);
  460. if (top) {
  461. if (top.id >= 0 && SimpleExprParser.checkPrecedence(top, op)) {
  462. this.operators.pop();
  463. this.operate(top);
  464. continue;
  465. }
  466. }
  467. return;
  468. }
  469. }
  470. flush() {
  471. while (true) {
  472. const op = this.operators.pop();
  473. if (!op) {
  474. return;
  475. }
  476. this.operate(op);
  477. }
  478. }
  479. flushUntil(id) {
  480. while (true) {
  481. const op = this.operators.pop();
  482. if (!op) {
  483. return false;
  484. }
  485. if (op.id === id) {
  486. return true;
  487. }
  488. this.operate(op);
  489. }
  490. }
  491. getNode() {
  492. this.flush();
  493. return this.operands.pop();
  494. }
  495. static checkPrecedence(left, right) {
  496. return left.prec < right.prec || left.prec === right.prec && left.assoc === LTR;
  497. }
  498. }
  499. class Leaf {
  500. dump() {
  501. throw new Error("Not implemented method");
  502. }
  503. isSomPredicate() {
  504. return false;
  505. }
  506. isDotExpression() {
  507. return false;
  508. }
  509. isConstant() {
  510. return false;
  511. }
  512. toNumber() {
  513. return 0;
  514. }
  515. toComparable() {
  516. return null;
  517. }
  518. }
  519. class AstCall extends Leaf {
  520. constructor(callee, params) {
  521. super();
  522. this.callee = callee;
  523. this.params = params;
  524. }
  525. dump() {
  526. return {
  527. callee: this.callee.dump(),
  528. params: this.params.map(x => x.dump())
  529. };
  530. }
  531. }
  532. class AstBuiltinCall extends Leaf {
  533. constructor(id, params) {
  534. super();
  535. this.id = id;
  536. this.params = params;
  537. }
  538. dump() {
  539. return {
  540. builtin: this.id,
  541. params: this.params.map(x => x.dump())
  542. };
  543. }
  544. }
  545. class AstSubscript extends Leaf {
  546. constructor(operand, index) {
  547. super();
  548. this.operand = operand;
  549. this.index = index;
  550. }
  551. dump() {
  552. return {
  553. operand: this.operand.dump(),
  554. index: this.index.dump()
  555. };
  556. }
  557. }
  558. class AstBinaryOperator extends Leaf {
  559. constructor(id, left, right, repr) {
  560. super();
  561. this.id = id;
  562. this.left = left;
  563. this.right = right;
  564. this.repr = repr;
  565. }
  566. dump() {
  567. return {
  568. operator: this.repr,
  569. left: this.left.dump(),
  570. right: this.right.dump()
  571. };
  572. }
  573. isDotExpression() {
  574. return Operators.dot.id <= this.id && this.id <= Operators.dotHash.id;
  575. }
  576. isSomPredicate() {
  577. return this.isDotExpression() || Operators.lt.id <= this.id && this.id <= Operators.or.id && (this.left.isDotExpression() && this.right.isConstant() || this.left.isConstant() && this.right.isDotExpression() || this.left.isDotExpression() && this.right.isDotExpression());
  578. }
  579. static getOperatorOrValue(operator, left, right) {
  580. if (!left.isConstant() || !right.isConstant()) {
  581. return new AstBinaryOperator(operator.id, left, right, operator.repr);
  582. }
  583. if (Operators.lt.id <= operator.id && operator.id <= Operators.ne.id && !(left instanceof AstNumber) && !(right instanceof AstNumber)) {
  584. return new AstNumber(operator.op(left.toComparable(), right.toComparable()));
  585. }
  586. return new AstNumber(operator.op(left.toNumber(), right.toNumber()));
  587. }
  588. }
  589. class AstUnaryOperator extends Leaf {
  590. constructor(id, arg, repr) {
  591. super();
  592. this.id = id;
  593. this.arg = arg;
  594. this.repr = repr;
  595. }
  596. dump() {
  597. return {
  598. operator: this.repr,
  599. arg: this.arg.dump()
  600. };
  601. }
  602. static getOperatorOrValue(operator, arg) {
  603. if (!arg.isConstant()) {
  604. return new AstUnaryOperator(operator.id, arg, operator.repr);
  605. }
  606. return new AstNumber(operator.op(arg.toNumber()));
  607. }
  608. }
  609. class AstNumber extends Leaf {
  610. constructor(number) {
  611. super();
  612. this.number = number;
  613. }
  614. dump() {
  615. return this.number;
  616. }
  617. isConstant() {
  618. return true;
  619. }
  620. toNumber() {
  621. return this.number;
  622. }
  623. }
  624. class AstString extends Leaf {
  625. constructor(str) {
  626. super();
  627. this.str = str;
  628. }
  629. dump() {
  630. return this.str;
  631. }
  632. isConstant() {
  633. return true;
  634. }
  635. toNumber() {
  636. return !isNaN(this.str) ? parseFloat(this.str) : 0;
  637. }
  638. toComparable() {
  639. return this.str;
  640. }
  641. }
  642. class AstThis extends Leaf {
  643. dump() {
  644. return {
  645. special: "this"
  646. };
  647. }
  648. }
  649. class AstIdentifier extends Leaf {
  650. constructor(id) {
  651. super();
  652. this.id = id;
  653. }
  654. dump() {
  655. return {
  656. id: this.id
  657. };
  658. }
  659. toLowerCase() {
  660. this.id = this.id.toLowerCase();
  661. }
  662. }
  663. class AstNull extends Leaf {
  664. dump() {
  665. return {
  666. special: null
  667. };
  668. }
  669. isConstant() {
  670. return true;
  671. }
  672. toComparable() {
  673. return null;
  674. }
  675. }
  676. class AstEveryOccurence {
  677. dump() {
  678. return {
  679. special: "*"
  680. };
  681. }
  682. }
  683. class VarDecl extends Leaf {
  684. constructor(id, expr) {
  685. super();
  686. this.id = id;
  687. this.expr = expr;
  688. }
  689. dump() {
  690. return {
  691. var: this.id,
  692. expr: this.expr.dump()
  693. };
  694. }
  695. }
  696. class Assignment extends Leaf {
  697. constructor(id, expr) {
  698. super();
  699. this.id = id;
  700. this.expr = expr;
  701. }
  702. dump() {
  703. return {
  704. assignment: this.id,
  705. expr: this.expr.dump()
  706. };
  707. }
  708. }
  709. class FuncDecl extends Leaf {
  710. constructor(id, params, body) {
  711. super();
  712. this.id = id;
  713. this.params = params;
  714. this.body = body;
  715. }
  716. dump() {
  717. return {
  718. func: this.id,
  719. params: this.params,
  720. body: this.body.dump()
  721. };
  722. }
  723. }
  724. class IfDecl extends Leaf {
  725. constructor(condition, thenClause, elseIfClause, elseClause) {
  726. super();
  727. this.condition = condition;
  728. this.then = thenClause;
  729. this.elseif = elseIfClause;
  730. this.else = elseClause;
  731. }
  732. dump() {
  733. return {
  734. decl: "if",
  735. condition: this.condition.dump(),
  736. then: this.then.dump(),
  737. elseif: this.elseif ? this.elseif.map(x => x.dump()) : null,
  738. else: this.else ? this.else.dump() : null
  739. };
  740. }
  741. }
  742. class ElseIfDecl extends Leaf {
  743. constructor(condition, thenClause) {
  744. super();
  745. this.condition = condition;
  746. this.then = thenClause;
  747. }
  748. dump() {
  749. return {
  750. decl: "elseif",
  751. condition: this.condition.dump(),
  752. then: this.then.dump()
  753. };
  754. }
  755. }
  756. class WhileDecl extends Leaf {
  757. constructor(condition, whileClause) {
  758. super();
  759. this.condition = condition;
  760. this.body = whileClause;
  761. }
  762. dump() {
  763. return {
  764. decl: "while",
  765. condition: this.condition.dump(),
  766. body: this.body.dump()
  767. };
  768. }
  769. }
  770. class ForDecl extends Leaf {
  771. constructor(assignment, upto, end, step, body) {
  772. super();
  773. this.assignment = assignment;
  774. this.upto = upto;
  775. this.end = end;
  776. this.step = step;
  777. this.body = body;
  778. }
  779. dump() {
  780. return {
  781. decl: "for",
  782. assignment: this.assignment.dump(),
  783. type: this.upto ? "upto" : "downto",
  784. end: this.end.dump(),
  785. step: this.step ? this.step.dump() : null,
  786. body: this.body.dump()
  787. };
  788. }
  789. }
  790. class ForeachDecl extends Leaf {
  791. constructor(id, params, body) {
  792. super();
  793. this.id = id;
  794. this.params = params;
  795. this.body = body;
  796. }
  797. dump() {
  798. return {
  799. decl: "foreach",
  800. id: this.id,
  801. params: this.params.map(x => x.dump()),
  802. body: this.body.dump()
  803. };
  804. }
  805. }
  806. class BlockDecl extends Leaf {
  807. constructor(body) {
  808. super();
  809. this.body = body;
  810. }
  811. dump() {
  812. return {
  813. decl: "block",
  814. body: this.body.dump()
  815. };
  816. }
  817. }
  818. class ExprList extends Leaf {
  819. constructor(expressions) {
  820. super();
  821. this.expressions = expressions;
  822. }
  823. dump() {
  824. return this.expressions.map(x => x.dump());
  825. }
  826. }
  827. class BreakDecl extends Leaf {
  828. dump() {
  829. return {
  830. special: "break"
  831. };
  832. }
  833. }
  834. class ContinueDecl extends Leaf {
  835. dump() {
  836. return {
  837. special: "continue"
  838. };
  839. }
  840. }
  841. class Parser {
  842. constructor(code) {
  843. this.lexer = new _formcalc_lexer.Lexer(code);
  844. }
  845. parse() {
  846. const [tok, decls] = this.parseExprList();
  847. if (tok.id !== _formcalc_lexer.TOKEN.eof) {
  848. throw new Error("Invalid token in Form code");
  849. }
  850. return decls;
  851. }
  852. parseExprList() {
  853. const expressions = [];
  854. let tok = null,
  855. expr;
  856. while (true) {
  857. [tok, expr] = this.parseExpr(tok);
  858. if (!expr) {
  859. return [tok, new ExprList(expressions)];
  860. }
  861. expressions.push(expr);
  862. }
  863. }
  864. parseExpr(tok) {
  865. tok = tok || this.lexer.next();
  866. switch (tok.id) {
  867. case _formcalc_lexer.TOKEN.identifier:
  868. return this.parseAssigmentOrExpr(tok);
  869. case _formcalc_lexer.TOKEN.break:
  870. return [null, new BreakDecl()];
  871. case _formcalc_lexer.TOKEN.continue:
  872. return [null, new ContinueDecl()];
  873. case _formcalc_lexer.TOKEN.do:
  874. return this.parseBlock();
  875. case _formcalc_lexer.TOKEN.for:
  876. return this.parseFor();
  877. case _formcalc_lexer.TOKEN.foreach:
  878. return this.parseForeach();
  879. case _formcalc_lexer.TOKEN.func:
  880. return this.parseFuncDecl();
  881. case _formcalc_lexer.TOKEN.if:
  882. return this.parseIf();
  883. case _formcalc_lexer.TOKEN.var:
  884. return this.parseVarDecl();
  885. case _formcalc_lexer.TOKEN.while:
  886. return this.parseWhile();
  887. default:
  888. return this.parseSimpleExpr(tok);
  889. }
  890. }
  891. parseAssigmentOrExpr(tok) {
  892. const savedTok = tok;
  893. tok = this.lexer.next();
  894. if (tok.id === _formcalc_lexer.TOKEN.assign) {
  895. const [tok1, expr] = this.parseSimpleExpr(null);
  896. return [tok1, new Assignment(savedTok.value, expr)];
  897. }
  898. const parser = new SimpleExprParser(this.lexer);
  899. parser.pushOperand(new AstIdentifier(savedTok.value));
  900. return parser.parse(tok);
  901. }
  902. parseBlock() {
  903. const [tok1, body] = this.parseExprList();
  904. const tok = tok1 || this.lexer.next();
  905. if (tok.id !== _formcalc_lexer.TOKEN.end) {
  906. throw new Error(Errors.block);
  907. }
  908. return [null, new BlockDecl(body)];
  909. }
  910. parseVarDecl() {
  911. let tok = this.lexer.next();
  912. if (tok.id !== _formcalc_lexer.TOKEN.identifier) {
  913. throw new Error(Errors.var);
  914. }
  915. const identifier = tok.value;
  916. tok = this.lexer.next();
  917. if (tok.id !== _formcalc_lexer.TOKEN.assign) {
  918. return [tok, new VarDecl(identifier, null)];
  919. }
  920. const [tok1, expr] = this.parseSimpleExpr();
  921. return [tok1, new VarDecl(identifier, expr)];
  922. }
  923. parseFuncDecl() {
  924. let tok = this.lexer.next();
  925. if (tok.id !== _formcalc_lexer.TOKEN.identifier) {
  926. throw new Error(Errors.func);
  927. }
  928. const identifier = tok.value;
  929. const params = this.parseParamList();
  930. tok = this.lexer.next();
  931. if (tok.id !== _formcalc_lexer.TOKEN.do) {
  932. throw new Error(Errors.func);
  933. }
  934. const [tok1, body] = this.parseExprList();
  935. tok = tok1 || this.lexer.next();
  936. if (tok.id !== _formcalc_lexer.TOKEN.endfunc) {
  937. throw new Error(Errors.func);
  938. }
  939. return [null, new FuncDecl(identifier, params, body)];
  940. }
  941. parseParamList() {
  942. const params = [];
  943. let tok = this.lexer.next();
  944. if (tok.id !== _formcalc_lexer.TOKEN.leftParen) {
  945. throw new Error(Errors.func);
  946. }
  947. tok = this.lexer.next();
  948. if (tok.id === _formcalc_lexer.TOKEN.rightParen) {
  949. return params;
  950. }
  951. while (true) {
  952. if (tok.id !== _formcalc_lexer.TOKEN.identifier) {
  953. throw new Error(Errors.func);
  954. }
  955. params.push(tok.value);
  956. tok = this.lexer.next();
  957. if (tok.id === _formcalc_lexer.TOKEN.rightParen) {
  958. return params;
  959. }
  960. if (tok.id !== _formcalc_lexer.TOKEN.comma) {
  961. throw new Error(Errors.func);
  962. }
  963. tok = this.lexer.next();
  964. }
  965. }
  966. parseSimpleExpr(tok = null) {
  967. return new SimpleExprParser(this.lexer).parse(tok);
  968. }
  969. parseIf() {
  970. let elseIfClause = [];
  971. let tok = this.lexer.next();
  972. if (tok.id !== _formcalc_lexer.TOKEN.leftParen) {
  973. throw new Error(Errors.if);
  974. }
  975. const [tok1, condition] = this.parseSimpleExpr();
  976. tok = tok1 || this.lexer.next();
  977. if (tok.id !== _formcalc_lexer.TOKEN.rightParen) {
  978. throw new Error(Errors.if);
  979. }
  980. tok = this.lexer.next();
  981. if (tok.id !== _formcalc_lexer.TOKEN.then) {
  982. throw new Error(Errors.if);
  983. }
  984. const [tok2, thenClause] = this.parseExprList();
  985. tok = tok2 || this.lexer.next();
  986. while (tok.id === _formcalc_lexer.TOKEN.elseif) {
  987. tok = this.lexer.next();
  988. if (tok.id !== _formcalc_lexer.TOKEN.leftParen) {
  989. throw new Error(Errors.elseif);
  990. }
  991. const [tok3, elseIfCondition] = this.parseSimpleExpr();
  992. tok = tok3 || this.lexer.next();
  993. if (tok.id !== _formcalc_lexer.TOKEN.rightParen) {
  994. throw new Error(Errors.elseif);
  995. }
  996. tok = this.lexer.next();
  997. if (tok.id !== _formcalc_lexer.TOKEN.then) {
  998. throw new Error(Errors.elseif);
  999. }
  1000. const [tok4, elseIfThenClause] = this.parseExprList();
  1001. elseIfClause.push(new ElseIfDecl(elseIfCondition, elseIfThenClause));
  1002. tok = tok4 || this.lexer.next();
  1003. }
  1004. if (elseIfClause.length === 0) {
  1005. elseIfClause = null;
  1006. }
  1007. if (tok.id === _formcalc_lexer.TOKEN.endif) {
  1008. return [null, new IfDecl(condition, thenClause, elseIfClause, null)];
  1009. }
  1010. if (tok.id !== _formcalc_lexer.TOKEN.else) {
  1011. throw new Error(Errors.if);
  1012. }
  1013. const [tok5, elseClause] = this.parseExprList();
  1014. tok = tok5 || this.lexer.next();
  1015. if (tok.id !== _formcalc_lexer.TOKEN.endif) {
  1016. throw new Error(Errors.if);
  1017. }
  1018. return [null, new IfDecl(condition, thenClause, elseIfClause, elseClause)];
  1019. }
  1020. parseWhile() {
  1021. let tok = this.lexer.next();
  1022. if (tok.id !== _formcalc_lexer.TOKEN.leftParen) {
  1023. throw new Error(Errors.while);
  1024. }
  1025. const [tok1, condition] = this.parseSimpleExpr();
  1026. tok = tok1 || this.lexer.next();
  1027. if (tok.id !== _formcalc_lexer.TOKEN.rightParen) {
  1028. throw new Error(Errors.while);
  1029. }
  1030. tok = this.lexer.next();
  1031. if (tok.id !== _formcalc_lexer.TOKEN.do) {
  1032. throw new Error(Errors.while);
  1033. }
  1034. const [tok2, whileClause] = this.parseExprList();
  1035. tok = tok2 || this.lexer.next();
  1036. if (tok.id !== _formcalc_lexer.TOKEN.endwhile) {
  1037. throw new Error(Errors.while);
  1038. }
  1039. return [null, new WhileDecl(condition, whileClause)];
  1040. }
  1041. parseAssignment() {
  1042. let tok = this.lexer.next();
  1043. let hasVar = false;
  1044. if (tok.id === _formcalc_lexer.TOKEN.var) {
  1045. hasVar = true;
  1046. tok = this.lexer.next();
  1047. }
  1048. if (tok.id !== _formcalc_lexer.TOKEN.identifier) {
  1049. throw new Error(Errors.assignment);
  1050. }
  1051. const identifier = tok.value;
  1052. tok = this.lexer.next();
  1053. if (tok.id !== _formcalc_lexer.TOKEN.assign) {
  1054. throw new Error(Errors.assignment);
  1055. }
  1056. const [tok1, expr] = this.parseSimpleExpr();
  1057. if (hasVar) {
  1058. return [tok1, new VarDecl(identifier, expr)];
  1059. }
  1060. return [tok1, new Assignment(identifier, expr)];
  1061. }
  1062. parseFor() {
  1063. let tok,
  1064. step = null;
  1065. let upto = false;
  1066. const [tok1, assignment] = this.parseAssignment();
  1067. tok = tok1 || this.lexer.next();
  1068. if (tok.id === _formcalc_lexer.TOKEN.upto) {
  1069. upto = true;
  1070. } else if (tok.id !== _formcalc_lexer.TOKEN.downto) {
  1071. throw new Error(Errors.for);
  1072. }
  1073. const [tok2, end] = this.parseSimpleExpr();
  1074. tok = tok2 || this.lexer.next();
  1075. if (tok.id === _formcalc_lexer.TOKEN.step) {
  1076. [tok, step] = this.parseSimpleExpr();
  1077. tok = tok || this.lexer.next();
  1078. }
  1079. if (tok.id !== _formcalc_lexer.TOKEN.do) {
  1080. throw new Error(Errors.for);
  1081. }
  1082. const [tok3, body] = this.parseExprList();
  1083. tok = tok3 || this.lexer.next();
  1084. if (tok.id !== _formcalc_lexer.TOKEN.endfor) {
  1085. throw new Error(Errors.for);
  1086. }
  1087. return [null, new ForDecl(assignment, upto, end, step, body)];
  1088. }
  1089. parseForeach() {
  1090. let tok = this.lexer.next();
  1091. if (tok.id !== _formcalc_lexer.TOKEN.identifier) {
  1092. throw new Error(Errors.foreach);
  1093. }
  1094. const identifier = tok.value;
  1095. tok = this.lexer.next();
  1096. if (tok.id !== _formcalc_lexer.TOKEN.in) {
  1097. throw new Error(Errors.foreach);
  1098. }
  1099. tok = this.lexer.next();
  1100. if (tok.id !== _formcalc_lexer.TOKEN.leftParen) {
  1101. throw new Error(Errors.foreach);
  1102. }
  1103. const params = SimpleExprParser.parseParams(this.lexer);
  1104. tok = this.lexer.next();
  1105. if (tok.id !== _formcalc_lexer.TOKEN.do) {
  1106. throw new Error(Errors.foreach);
  1107. }
  1108. const [tok1, body] = this.parseExprList();
  1109. tok = tok1 || this.lexer.next();
  1110. if (tok.id !== _formcalc_lexer.TOKEN.endfor) {
  1111. throw new Error(Errors.foreach);
  1112. }
  1113. return [null, new ForeachDecl(identifier, params, body)];
  1114. }
  1115. }
  1116. exports.Parser = Parser;