index.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.or = exports.and = exports.not = exports.CodeGen = exports.operators = exports.varKinds = exports.ValueScopeName = exports.ValueScope = exports.Scope = exports.Name = exports.stringify = exports.getProperty = exports.nil = exports.strConcat = exports.str = exports._ = void 0;
  4. const code_1 = require("./code");
  5. const scope_1 = require("./scope");
  6. var code_2 = require("./code");
  7. Object.defineProperty(exports, "_", { enumerable: true, get: function () { return code_2._; } });
  8. Object.defineProperty(exports, "str", { enumerable: true, get: function () { return code_2.str; } });
  9. Object.defineProperty(exports, "strConcat", { enumerable: true, get: function () { return code_2.strConcat; } });
  10. Object.defineProperty(exports, "nil", { enumerable: true, get: function () { return code_2.nil; } });
  11. Object.defineProperty(exports, "getProperty", { enumerable: true, get: function () { return code_2.getProperty; } });
  12. Object.defineProperty(exports, "stringify", { enumerable: true, get: function () { return code_2.stringify; } });
  13. Object.defineProperty(exports, "Name", { enumerable: true, get: function () { return code_2.Name; } });
  14. var scope_2 = require("./scope");
  15. Object.defineProperty(exports, "Scope", { enumerable: true, get: function () { return scope_2.Scope; } });
  16. Object.defineProperty(exports, "ValueScope", { enumerable: true, get: function () { return scope_2.ValueScope; } });
  17. Object.defineProperty(exports, "ValueScopeName", { enumerable: true, get: function () { return scope_2.ValueScopeName; } });
  18. Object.defineProperty(exports, "varKinds", { enumerable: true, get: function () { return scope_2.varKinds; } });
  19. exports.operators = {
  20. GT: new code_1._Code(">"),
  21. GTE: new code_1._Code(">="),
  22. LT: new code_1._Code("<"),
  23. LTE: new code_1._Code("<="),
  24. EQ: new code_1._Code("==="),
  25. NEQ: new code_1._Code("!=="),
  26. NOT: new code_1._Code("!"),
  27. OR: new code_1._Code("||"),
  28. AND: new code_1._Code("&&"),
  29. ADD: new code_1._Code("+"),
  30. };
  31. class Node {
  32. optimizeNodes() {
  33. return this;
  34. }
  35. optimizeNames(_names, _constants) {
  36. return this;
  37. }
  38. }
  39. class Def extends Node {
  40. constructor(varKind, name, rhs) {
  41. super();
  42. this.varKind = varKind;
  43. this.name = name;
  44. this.rhs = rhs;
  45. }
  46. render({ es5, _n }) {
  47. const varKind = es5 ? scope_1.varKinds.var : this.varKind;
  48. const rhs = this.rhs === undefined ? "" : ` = ${this.rhs}`;
  49. return `${varKind} ${this.name}${rhs};` + _n;
  50. }
  51. optimizeNames(names, constants) {
  52. if (!names[this.name.str])
  53. return;
  54. if (this.rhs)
  55. this.rhs = optimizeExpr(this.rhs, names, constants);
  56. return this;
  57. }
  58. get names() {
  59. return this.rhs instanceof code_1._CodeOrName ? this.rhs.names : {};
  60. }
  61. }
  62. class Assign extends Node {
  63. constructor(lhs, rhs, sideEffects) {
  64. super();
  65. this.lhs = lhs;
  66. this.rhs = rhs;
  67. this.sideEffects = sideEffects;
  68. }
  69. render({ _n }) {
  70. return `${this.lhs} = ${this.rhs};` + _n;
  71. }
  72. optimizeNames(names, constants) {
  73. if (this.lhs instanceof code_1.Name && !names[this.lhs.str] && !this.sideEffects)
  74. return;
  75. this.rhs = optimizeExpr(this.rhs, names, constants);
  76. return this;
  77. }
  78. get names() {
  79. const names = this.lhs instanceof code_1.Name ? {} : { ...this.lhs.names };
  80. return addExprNames(names, this.rhs);
  81. }
  82. }
  83. class AssignOp extends Assign {
  84. constructor(lhs, op, rhs, sideEffects) {
  85. super(lhs, rhs, sideEffects);
  86. this.op = op;
  87. }
  88. render({ _n }) {
  89. return `${this.lhs} ${this.op}= ${this.rhs};` + _n;
  90. }
  91. }
  92. class Label extends Node {
  93. constructor(label) {
  94. super();
  95. this.label = label;
  96. this.names = {};
  97. }
  98. render({ _n }) {
  99. return `${this.label}:` + _n;
  100. }
  101. }
  102. class Break extends Node {
  103. constructor(label) {
  104. super();
  105. this.label = label;
  106. this.names = {};
  107. }
  108. render({ _n }) {
  109. const label = this.label ? ` ${this.label}` : "";
  110. return `break${label};` + _n;
  111. }
  112. }
  113. class Throw extends Node {
  114. constructor(error) {
  115. super();
  116. this.error = error;
  117. }
  118. render({ _n }) {
  119. return `throw ${this.error};` + _n;
  120. }
  121. get names() {
  122. return this.error.names;
  123. }
  124. }
  125. class AnyCode extends Node {
  126. constructor(code) {
  127. super();
  128. this.code = code;
  129. }
  130. render({ _n }) {
  131. return `${this.code};` + _n;
  132. }
  133. optimizeNodes() {
  134. return `${this.code}` ? this : undefined;
  135. }
  136. optimizeNames(names, constants) {
  137. this.code = optimizeExpr(this.code, names, constants);
  138. return this;
  139. }
  140. get names() {
  141. return this.code instanceof code_1._CodeOrName ? this.code.names : {};
  142. }
  143. }
  144. class ParentNode extends Node {
  145. constructor(nodes = []) {
  146. super();
  147. this.nodes = nodes;
  148. }
  149. render(opts) {
  150. return this.nodes.reduce((code, n) => code + n.render(opts), "");
  151. }
  152. optimizeNodes() {
  153. const { nodes } = this;
  154. let i = nodes.length;
  155. while (i--) {
  156. const n = nodes[i].optimizeNodes();
  157. if (Array.isArray(n))
  158. nodes.splice(i, 1, ...n);
  159. else if (n)
  160. nodes[i] = n;
  161. else
  162. nodes.splice(i, 1);
  163. }
  164. return nodes.length > 0 ? this : undefined;
  165. }
  166. optimizeNames(names, constants) {
  167. const { nodes } = this;
  168. let i = nodes.length;
  169. while (i--) {
  170. // iterating backwards improves 1-pass optimization
  171. const n = nodes[i];
  172. if (n.optimizeNames(names, constants))
  173. continue;
  174. subtractNames(names, n.names);
  175. nodes.splice(i, 1);
  176. }
  177. return nodes.length > 0 ? this : undefined;
  178. }
  179. get names() {
  180. return this.nodes.reduce((names, n) => addNames(names, n.names), {});
  181. }
  182. }
  183. class BlockNode extends ParentNode {
  184. render(opts) {
  185. return "{" + opts._n + super.render(opts) + "}" + opts._n;
  186. }
  187. }
  188. class Root extends ParentNode {
  189. }
  190. class Else extends BlockNode {
  191. }
  192. Else.kind = "else";
  193. class If extends BlockNode {
  194. constructor(condition, nodes) {
  195. super(nodes);
  196. this.condition = condition;
  197. }
  198. render(opts) {
  199. let code = `if(${this.condition})` + super.render(opts);
  200. if (this.else)
  201. code += "else " + this.else.render(opts);
  202. return code;
  203. }
  204. optimizeNodes() {
  205. super.optimizeNodes();
  206. const cond = this.condition;
  207. if (cond === true)
  208. return this.nodes; // else is ignored here
  209. let e = this.else;
  210. if (e) {
  211. const ns = e.optimizeNodes();
  212. e = this.else = Array.isArray(ns) ? new Else(ns) : ns;
  213. }
  214. if (e) {
  215. if (cond === false)
  216. return e instanceof If ? e : e.nodes;
  217. if (this.nodes.length)
  218. return this;
  219. return new If(not(cond), e instanceof If ? [e] : e.nodes);
  220. }
  221. if (cond === false || !this.nodes.length)
  222. return undefined;
  223. return this;
  224. }
  225. optimizeNames(names, constants) {
  226. var _a;
  227. this.else = (_a = this.else) === null || _a === void 0 ? void 0 : _a.optimizeNames(names, constants);
  228. if (!(super.optimizeNames(names, constants) || this.else))
  229. return;
  230. this.condition = optimizeExpr(this.condition, names, constants);
  231. return this;
  232. }
  233. get names() {
  234. const names = super.names;
  235. addExprNames(names, this.condition);
  236. if (this.else)
  237. addNames(names, this.else.names);
  238. return names;
  239. }
  240. }
  241. If.kind = "if";
  242. class For extends BlockNode {
  243. }
  244. For.kind = "for";
  245. class ForLoop extends For {
  246. constructor(iteration) {
  247. super();
  248. this.iteration = iteration;
  249. }
  250. render(opts) {
  251. return `for(${this.iteration})` + super.render(opts);
  252. }
  253. optimizeNames(names, constants) {
  254. if (!super.optimizeNames(names, constants))
  255. return;
  256. this.iteration = optimizeExpr(this.iteration, names, constants);
  257. return this;
  258. }
  259. get names() {
  260. return addNames(super.names, this.iteration.names);
  261. }
  262. }
  263. class ForRange extends For {
  264. constructor(varKind, name, from, to) {
  265. super();
  266. this.varKind = varKind;
  267. this.name = name;
  268. this.from = from;
  269. this.to = to;
  270. }
  271. render(opts) {
  272. const varKind = opts.es5 ? scope_1.varKinds.var : this.varKind;
  273. const { name, from, to } = this;
  274. return `for(${varKind} ${name}=${from}; ${name}<${to}; ${name}++)` + super.render(opts);
  275. }
  276. get names() {
  277. const names = addExprNames(super.names, this.from);
  278. return addExprNames(names, this.to);
  279. }
  280. }
  281. class ForIter extends For {
  282. constructor(loop, varKind, name, iterable) {
  283. super();
  284. this.loop = loop;
  285. this.varKind = varKind;
  286. this.name = name;
  287. this.iterable = iterable;
  288. }
  289. render(opts) {
  290. return `for(${this.varKind} ${this.name} ${this.loop} ${this.iterable})` + super.render(opts);
  291. }
  292. optimizeNames(names, constants) {
  293. if (!super.optimizeNames(names, constants))
  294. return;
  295. this.iterable = optimizeExpr(this.iterable, names, constants);
  296. return this;
  297. }
  298. get names() {
  299. return addNames(super.names, this.iterable.names);
  300. }
  301. }
  302. class Func extends BlockNode {
  303. constructor(name, args, async) {
  304. super();
  305. this.name = name;
  306. this.args = args;
  307. this.async = async;
  308. }
  309. render(opts) {
  310. const _async = this.async ? "async " : "";
  311. return `${_async}function ${this.name}(${this.args})` + super.render(opts);
  312. }
  313. }
  314. Func.kind = "func";
  315. class Return extends ParentNode {
  316. render(opts) {
  317. return "return " + super.render(opts);
  318. }
  319. }
  320. Return.kind = "return";
  321. class Try extends BlockNode {
  322. render(opts) {
  323. let code = "try" + super.render(opts);
  324. if (this.catch)
  325. code += this.catch.render(opts);
  326. if (this.finally)
  327. code += this.finally.render(opts);
  328. return code;
  329. }
  330. optimizeNodes() {
  331. var _a, _b;
  332. super.optimizeNodes();
  333. (_a = this.catch) === null || _a === void 0 ? void 0 : _a.optimizeNodes();
  334. (_b = this.finally) === null || _b === void 0 ? void 0 : _b.optimizeNodes();
  335. return this;
  336. }
  337. optimizeNames(names, constants) {
  338. var _a, _b;
  339. super.optimizeNames(names, constants);
  340. (_a = this.catch) === null || _a === void 0 ? void 0 : _a.optimizeNames(names, constants);
  341. (_b = this.finally) === null || _b === void 0 ? void 0 : _b.optimizeNames(names, constants);
  342. return this;
  343. }
  344. get names() {
  345. const names = super.names;
  346. if (this.catch)
  347. addNames(names, this.catch.names);
  348. if (this.finally)
  349. addNames(names, this.finally.names);
  350. return names;
  351. }
  352. }
  353. class Catch extends BlockNode {
  354. constructor(error) {
  355. super();
  356. this.error = error;
  357. }
  358. render(opts) {
  359. return `catch(${this.error})` + super.render(opts);
  360. }
  361. }
  362. Catch.kind = "catch";
  363. class Finally extends BlockNode {
  364. render(opts) {
  365. return "finally" + super.render(opts);
  366. }
  367. }
  368. Finally.kind = "finally";
  369. class CodeGen {
  370. constructor(extScope, opts = {}) {
  371. this._values = {};
  372. this._blockStarts = [];
  373. this._constants = {};
  374. this.opts = { ...opts, _n: opts.lines ? "\n" : "" };
  375. this._extScope = extScope;
  376. this._scope = new scope_1.Scope({ parent: extScope });
  377. this._nodes = [new Root()];
  378. }
  379. toString() {
  380. return this._root.render(this.opts);
  381. }
  382. // returns unique name in the internal scope
  383. name(prefix) {
  384. return this._scope.name(prefix);
  385. }
  386. // reserves unique name in the external scope
  387. scopeName(prefix) {
  388. return this._extScope.name(prefix);
  389. }
  390. // reserves unique name in the external scope and assigns value to it
  391. scopeValue(prefixOrName, value) {
  392. const name = this._extScope.value(prefixOrName, value);
  393. const vs = this._values[name.prefix] || (this._values[name.prefix] = new Set());
  394. vs.add(name);
  395. return name;
  396. }
  397. getScopeValue(prefix, keyOrRef) {
  398. return this._extScope.getValue(prefix, keyOrRef);
  399. }
  400. // return code that assigns values in the external scope to the names that are used internally
  401. // (same names that were returned by gen.scopeName or gen.scopeValue)
  402. scopeRefs(scopeName) {
  403. return this._extScope.scopeRefs(scopeName, this._values);
  404. }
  405. scopeCode() {
  406. return this._extScope.scopeCode(this._values);
  407. }
  408. _def(varKind, nameOrPrefix, rhs, constant) {
  409. const name = this._scope.toName(nameOrPrefix);
  410. if (rhs !== undefined && constant)
  411. this._constants[name.str] = rhs;
  412. this._leafNode(new Def(varKind, name, rhs));
  413. return name;
  414. }
  415. // `const` declaration (`var` in es5 mode)
  416. const(nameOrPrefix, rhs, _constant) {
  417. return this._def(scope_1.varKinds.const, nameOrPrefix, rhs, _constant);
  418. }
  419. // `let` declaration with optional assignment (`var` in es5 mode)
  420. let(nameOrPrefix, rhs, _constant) {
  421. return this._def(scope_1.varKinds.let, nameOrPrefix, rhs, _constant);
  422. }
  423. // `var` declaration with optional assignment
  424. var(nameOrPrefix, rhs, _constant) {
  425. return this._def(scope_1.varKinds.var, nameOrPrefix, rhs, _constant);
  426. }
  427. // assignment code
  428. assign(lhs, rhs, sideEffects) {
  429. return this._leafNode(new Assign(lhs, rhs, sideEffects));
  430. }
  431. // `+=` code
  432. add(lhs, rhs) {
  433. return this._leafNode(new AssignOp(lhs, exports.operators.ADD, rhs));
  434. }
  435. // appends passed SafeExpr to code or executes Block
  436. code(c) {
  437. if (typeof c == "function")
  438. c();
  439. else if (c !== code_1.nil)
  440. this._leafNode(new AnyCode(c));
  441. return this;
  442. }
  443. // returns code for object literal for the passed argument list of key-value pairs
  444. object(...keyValues) {
  445. const code = ["{"];
  446. for (const [key, value] of keyValues) {
  447. if (code.length > 1)
  448. code.push(",");
  449. code.push(key);
  450. if (key !== value || this.opts.es5) {
  451. code.push(":");
  452. code_1.addCodeArg(code, value);
  453. }
  454. }
  455. code.push("}");
  456. return new code_1._Code(code);
  457. }
  458. // `if` clause (or statement if `thenBody` and, optionally, `elseBody` are passed)
  459. if(condition, thenBody, elseBody) {
  460. this._blockNode(new If(condition));
  461. if (thenBody && elseBody) {
  462. this.code(thenBody).else().code(elseBody).endIf();
  463. }
  464. else if (thenBody) {
  465. this.code(thenBody).endIf();
  466. }
  467. else if (elseBody) {
  468. throw new Error('CodeGen: "else" body without "then" body');
  469. }
  470. return this;
  471. }
  472. // `else if` clause - invalid without `if` or after `else` clauses
  473. elseIf(condition) {
  474. return this._elseNode(new If(condition));
  475. }
  476. // `else` clause - only valid after `if` or `else if` clauses
  477. else() {
  478. return this._elseNode(new Else());
  479. }
  480. // end `if` statement (needed if gen.if was used only with condition)
  481. endIf() {
  482. return this._endBlockNode(If, Else);
  483. }
  484. _for(node, forBody) {
  485. this._blockNode(node);
  486. if (forBody)
  487. this.code(forBody).endFor();
  488. return this;
  489. }
  490. // a generic `for` clause (or statement if `forBody` is passed)
  491. for(iteration, forBody) {
  492. return this._for(new ForLoop(iteration), forBody);
  493. }
  494. // `for` statement for a range of values
  495. forRange(nameOrPrefix, from, to, forBody, varKind = this.opts.es5 ? scope_1.varKinds.var : scope_1.varKinds.let) {
  496. const name = this._scope.toName(nameOrPrefix);
  497. return this._for(new ForRange(varKind, name, from, to), () => forBody(name));
  498. }
  499. // `for-of` statement (in es5 mode replace with a normal for loop)
  500. forOf(nameOrPrefix, iterable, forBody, varKind = scope_1.varKinds.const) {
  501. const name = this._scope.toName(nameOrPrefix);
  502. if (this.opts.es5) {
  503. const arr = iterable instanceof code_1.Name ? iterable : this.var("_arr", iterable);
  504. return this.forRange("_i", 0, code_1._ `${arr}.length`, (i) => {
  505. this.var(name, code_1._ `${arr}[${i}]`);
  506. forBody(name);
  507. });
  508. }
  509. return this._for(new ForIter("of", varKind, name, iterable), () => forBody(name));
  510. }
  511. // `for-in` statement.
  512. // With option `ownProperties` replaced with a `for-of` loop for object keys
  513. forIn(nameOrPrefix, obj, forBody, varKind = this.opts.es5 ? scope_1.varKinds.var : scope_1.varKinds.const) {
  514. if (this.opts.ownProperties) {
  515. return this.forOf(nameOrPrefix, code_1._ `Object.keys(${obj})`, forBody);
  516. }
  517. const name = this._scope.toName(nameOrPrefix);
  518. return this._for(new ForIter("in", varKind, name, obj), () => forBody(name));
  519. }
  520. // end `for` loop
  521. endFor() {
  522. return this._endBlockNode(For);
  523. }
  524. // `label` statement
  525. label(label) {
  526. return this._leafNode(new Label(label));
  527. }
  528. // `break` statement
  529. break(label) {
  530. return this._leafNode(new Break(label));
  531. }
  532. // `return` statement
  533. return(value) {
  534. const node = new Return();
  535. this._blockNode(node);
  536. this.code(value);
  537. if (node.nodes.length !== 1)
  538. throw new Error('CodeGen: "return" should have one node');
  539. return this._endBlockNode(Return);
  540. }
  541. // `try` statement
  542. try(tryBody, catchCode, finallyCode) {
  543. if (!catchCode && !finallyCode)
  544. throw new Error('CodeGen: "try" without "catch" and "finally"');
  545. const node = new Try();
  546. this._blockNode(node);
  547. this.code(tryBody);
  548. if (catchCode) {
  549. const error = this.name("e");
  550. this._currNode = node.catch = new Catch(error);
  551. catchCode(error);
  552. }
  553. if (finallyCode) {
  554. this._currNode = node.finally = new Finally();
  555. this.code(finallyCode);
  556. }
  557. return this._endBlockNode(Catch, Finally);
  558. }
  559. // `throw` statement
  560. throw(error) {
  561. return this._leafNode(new Throw(error));
  562. }
  563. // start self-balancing block
  564. block(body, nodeCount) {
  565. this._blockStarts.push(this._nodes.length);
  566. if (body)
  567. this.code(body).endBlock(nodeCount);
  568. return this;
  569. }
  570. // end the current self-balancing block
  571. endBlock(nodeCount) {
  572. const len = this._blockStarts.pop();
  573. if (len === undefined)
  574. throw new Error("CodeGen: not in self-balancing block");
  575. const toClose = this._nodes.length - len;
  576. if (toClose < 0 || (nodeCount !== undefined && toClose !== nodeCount)) {
  577. throw new Error(`CodeGen: wrong number of nodes: ${toClose} vs ${nodeCount} expected`);
  578. }
  579. this._nodes.length = len;
  580. return this;
  581. }
  582. // `function` heading (or definition if funcBody is passed)
  583. func(name, args = code_1.nil, async, funcBody) {
  584. this._blockNode(new Func(name, args, async));
  585. if (funcBody)
  586. this.code(funcBody).endFunc();
  587. return this;
  588. }
  589. // end function definition
  590. endFunc() {
  591. return this._endBlockNode(Func);
  592. }
  593. optimize(n = 1) {
  594. while (n-- > 0) {
  595. this._root.optimizeNodes();
  596. this._root.optimizeNames(this._root.names, this._constants);
  597. }
  598. }
  599. _leafNode(node) {
  600. this._currNode.nodes.push(node);
  601. return this;
  602. }
  603. _blockNode(node) {
  604. this._currNode.nodes.push(node);
  605. this._nodes.push(node);
  606. }
  607. _endBlockNode(N1, N2) {
  608. const n = this._currNode;
  609. if (n instanceof N1 || (N2 && n instanceof N2)) {
  610. this._nodes.pop();
  611. return this;
  612. }
  613. throw new Error(`CodeGen: not in block "${N2 ? `${N1.kind}/${N2.kind}` : N1.kind}"`);
  614. }
  615. _elseNode(node) {
  616. const n = this._currNode;
  617. if (!(n instanceof If)) {
  618. throw new Error('CodeGen: "else" without "if"');
  619. }
  620. this._currNode = n.else = node;
  621. return this;
  622. }
  623. get _root() {
  624. return this._nodes[0];
  625. }
  626. get _currNode() {
  627. const ns = this._nodes;
  628. return ns[ns.length - 1];
  629. }
  630. set _currNode(node) {
  631. const ns = this._nodes;
  632. ns[ns.length - 1] = node;
  633. }
  634. }
  635. exports.CodeGen = CodeGen;
  636. function addNames(names, from) {
  637. for (const n in from)
  638. names[n] = (names[n] || 0) + (from[n] || 0);
  639. return names;
  640. }
  641. function addExprNames(names, from) {
  642. return from instanceof code_1._CodeOrName ? addNames(names, from.names) : names;
  643. }
  644. function optimizeExpr(expr, names, constants) {
  645. if (expr instanceof code_1.Name)
  646. return replaceName(expr);
  647. if (!canOptimize(expr))
  648. return expr;
  649. return new code_1._Code(expr._items.reduce((items, c) => {
  650. if (c instanceof code_1.Name)
  651. c = replaceName(c);
  652. if (c instanceof code_1._Code)
  653. items.push(...c._items);
  654. else
  655. items.push(c);
  656. return items;
  657. }, []));
  658. function replaceName(n) {
  659. const c = constants[n.str];
  660. if (c === undefined || names[n.str] !== 1)
  661. return n;
  662. delete names[n.str];
  663. return c;
  664. }
  665. function canOptimize(e) {
  666. return (e instanceof code_1._Code &&
  667. e._items.some((c) => c instanceof code_1.Name && names[c.str] === 1 && constants[c.str] !== undefined));
  668. }
  669. }
  670. function subtractNames(names, from) {
  671. for (const n in from)
  672. names[n] = (names[n] || 0) - (from[n] || 0);
  673. }
  674. function not(x) {
  675. return typeof x == "boolean" || typeof x == "number" || x === null ? !x : code_1._ `!${par(x)}`;
  676. }
  677. exports.not = not;
  678. const andCode = mappend(exports.operators.AND);
  679. // boolean AND (&&) expression with the passed arguments
  680. function and(...args) {
  681. return args.reduce(andCode);
  682. }
  683. exports.and = and;
  684. const orCode = mappend(exports.operators.OR);
  685. // boolean OR (||) expression with the passed arguments
  686. function or(...args) {
  687. return args.reduce(orCode);
  688. }
  689. exports.or = or;
  690. function mappend(op) {
  691. return (x, y) => (x === code_1.nil ? y : y === code_1.nil ? x : code_1._ `${par(x)} ${op} ${par(y)}`);
  692. }
  693. function par(x) {
  694. return x instanceof code_1.Name ? x : code_1._ `(${x})`;
  695. }
  696. //# sourceMappingURL=index.js.map