nerdamer 1.1.13 → 2.0.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (341) hide show
  1. package/LICENSE.md +184 -0
  2. package/README.md +293 -312
  3. package/dist/bundle.js +2 -0
  4. package/dist/bundle.js.LICENSE.txt +7 -0
  5. package/dist/parser.js +2 -0
  6. package/dist/parser.js.LICENSE.txt +7 -0
  7. package/docs-data/parser-functions.json +1960 -0
  8. package/index.d.ts +10 -426
  9. package/output/algebra/adapters.d.ts +33 -0
  10. package/output/algebra/adapters.js +109 -0
  11. package/output/algebra/algorithms/arith.d.ts +84 -0
  12. package/output/algebra/algorithms/arith.js +323 -0
  13. package/output/algebra/algorithms/factor.d.ts +3 -0
  14. package/output/algebra/algorithms/factor.js +5 -0
  15. package/output/algebra/algorithms/factorMultivariate.d.ts +23 -0
  16. package/output/algebra/algorithms/factorMultivariate.js +2393 -0
  17. package/output/algebra/algorithms/factorUnivariate.d.ts +72 -0
  18. package/output/algebra/algorithms/factorUnivariate.js +1072 -0
  19. package/output/algebra/algorithms/gcd.d.ts +22 -0
  20. package/output/algebra/algorithms/gcd.js +690 -0
  21. package/output/algebra/algorithms/groebnerBase.d.ts +157 -0
  22. package/output/algebra/algorithms/groebnerBase.js +1166 -0
  23. package/output/algebra/algorithms/multiPoly/MultiPoly.d.ts +137 -0
  24. package/output/algebra/algorithms/multiPoly/MultiPoly.js +346 -0
  25. package/output/algebra/algorithms/poly.d.ts +228 -0
  26. package/output/algebra/algorithms/poly.js +1299 -0
  27. package/output/algebra/algorithms/rational.d.ts +17 -0
  28. package/output/algebra/algorithms/rational.js +115 -0
  29. package/output/algebra/factor/factor.d.ts +62 -0
  30. package/output/algebra/factor/factor.js +158 -0
  31. package/output/algebra/gcd/gcd.d.ts +29 -0
  32. package/output/algebra/gcd/gcd.js +141 -0
  33. package/output/algebra/groebner.d.ts +21 -0
  34. package/output/algebra/groebner.js +42 -0
  35. package/output/algebra/partfrac.d.ts +19 -0
  36. package/output/algebra/partfrac.js +398 -0
  37. package/output/algebra/polynomialize.d.ts +20 -0
  38. package/output/algebra/polynomialize.js +24 -0
  39. package/output/algebra/simplify/complexsimp.d.ts +7 -0
  40. package/output/algebra/simplify/complexsimp.js +24 -0
  41. package/output/algebra/simplify/factorCommon.d.ts +13 -0
  42. package/output/algebra/simplify/factorCommon.js +136 -0
  43. package/output/algebra/simplify/funcsimp.d.ts +26 -0
  44. package/output/algebra/simplify/funcsimp.js +633 -0
  45. package/output/algebra/simplify/invtrigrewrite.d.ts +10 -0
  46. package/output/algebra/simplify/invtrigrewrite.js +96 -0
  47. package/output/algebra/simplify/ratsimp.d.ts +21 -0
  48. package/output/algebra/simplify/ratsimp.js +140 -0
  49. package/output/algebra/simplify/simplify.d.ts +25 -0
  50. package/output/algebra/simplify/simplify.js +228 -0
  51. package/output/algebra/simplify/trigreduce.d.ts +38 -0
  52. package/output/algebra/simplify/trigreduce.js +424 -0
  53. package/output/algebra/simplify/trigrewrite.d.ts +17 -0
  54. package/output/algebra/simplify/trigrewrite.js +156 -0
  55. package/output/algebra/simplify/trigsimp.d.ts +11 -0
  56. package/output/algebra/simplify/trigsimp.js +369 -0
  57. package/output/algebra/simplify/utils.d.ts +32 -0
  58. package/output/algebra/simplify/utils.js +56 -0
  59. package/output/algebra/utils.d.ts +32 -0
  60. package/output/algebra/utils.js +48 -0
  61. package/output/api/advanced.d.ts +7 -0
  62. package/output/api/advanced.js +18 -0
  63. package/output/api/algebra.d.ts +17 -0
  64. package/output/api/algebra.js +32 -0
  65. package/output/api/assumptions.d.ts +4 -0
  66. package/output/api/assumptions.js +11 -0
  67. package/output/api/calculus.d.ts +9 -0
  68. package/output/api/calculus.js +21 -0
  69. package/output/api/core.d.ts +17 -0
  70. package/output/api/core.js +41 -0
  71. package/output/api/debug.d.ts +16 -0
  72. package/output/api/debug.js +23 -0
  73. package/output/api/parser.d.ts +85 -0
  74. package/output/api/parser.js +16 -0
  75. package/output/api/solve.d.ts +10 -0
  76. package/output/api/solve.js +16 -0
  77. package/output/api/structures.d.ts +10 -0
  78. package/output/api/structures.js +22 -0
  79. package/output/calculus/derivative/diff.d.ts +30 -0
  80. package/output/calculus/derivative/diff.js +238 -0
  81. package/output/calculus/fresnel.d.ts +16 -0
  82. package/output/calculus/fresnel.js +39 -0
  83. package/output/calculus/integrate/byParts.d.ts +18 -0
  84. package/output/calculus/integrate/byParts.js +339 -0
  85. package/output/calculus/integrate/bySubstitution.d.ts +135 -0
  86. package/output/calculus/integrate/bySubstitution.js +411 -0
  87. package/output/calculus/integrate/integrate.d.ts +38 -0
  88. package/output/calculus/integrate/integrate.js +652 -0
  89. package/output/calculus/integrate/integrationTable.d.ts +2 -0
  90. package/output/calculus/integrate/integrationTable.js +914 -0
  91. package/output/calculus/integrate/utils.d.ts +28 -0
  92. package/output/calculus/integrate/utils.js +96 -0
  93. package/output/calculus/laplace/ilaplace.d.ts +31 -0
  94. package/output/calculus/laplace/ilaplace.js +192 -0
  95. package/output/calculus/laplace/ilaplaceTable.d.ts +2 -0
  96. package/output/calculus/laplace/ilaplaceTable.js +200 -0
  97. package/output/calculus/laplace/laplace.d.ts +22 -0
  98. package/output/calculus/laplace/laplace.js +109 -0
  99. package/output/calculus/laplace/laplaceTable.d.ts +2 -0
  100. package/output/calculus/laplace/laplaceTable.js +187 -0
  101. package/output/calculus/limit/limit.d.ts +35 -0
  102. package/output/calculus/limit/limit.js +1183 -0
  103. package/output/calculus/limit/limitsTable.d.ts +2 -0
  104. package/output/calculus/limit/limitsTable.js +7 -0
  105. package/output/core/Settings.d.ts +32 -0
  106. package/output/core/Settings.js +68 -0
  107. package/output/core/adapters.d.ts +18 -0
  108. package/output/core/adapters.js +40 -0
  109. package/output/core/classes/assumption/Assumption.d.ts +218 -0
  110. package/output/core/classes/assumption/Assumption.js +605 -0
  111. package/output/core/classes/assumption/assertiveFunctions.d.ts +6 -0
  112. package/output/core/classes/assumption/assertiveFunctions.js +64 -0
  113. package/output/core/classes/assumption/assume.d.ts +90 -0
  114. package/output/core/classes/assumption/assume.js +145 -0
  115. package/output/core/classes/collection/Collection.d.ts +107 -0
  116. package/output/core/classes/collection/Collection.js +231 -0
  117. package/output/core/classes/complex/Complex.d.ts +99 -0
  118. package/output/core/classes/complex/Complex.js +135 -0
  119. package/output/core/classes/decimalSet/DecimalSet.d.ts +226 -0
  120. package/output/core/classes/decimalSet/DecimalSet.js +453 -0
  121. package/output/core/classes/dictionary/Dictionary.d.ts +102 -0
  122. package/output/core/classes/dictionary/Dictionary.js +213 -0
  123. package/output/core/classes/equation/Equation.d.ts +256 -0
  124. package/output/core/classes/equation/Equation.js +350 -0
  125. package/output/core/classes/expression/CoeffObject.d.ts +100 -0
  126. package/output/core/classes/expression/CoeffObject.js +198 -0
  127. package/output/core/classes/expression/Expression.d.ts +1674 -0
  128. package/output/core/classes/expression/Expression.js +2332 -0
  129. package/output/core/classes/expression/analysis.d.ts +25 -0
  130. package/output/core/classes/expression/analysis.js +218 -0
  131. package/output/core/classes/expression/collect.d.ts +9 -0
  132. package/output/core/classes/expression/collect.js +49 -0
  133. package/output/core/classes/expression/format.d.ts +45 -0
  134. package/output/core/classes/expression/format.js +217 -0
  135. package/output/core/classes/expression/products.d.ts +23 -0
  136. package/output/core/classes/expression/products.js +115 -0
  137. package/output/core/classes/expression/shortcuts.d.ts +35 -0
  138. package/output/core/classes/expression/shortcuts.js +129 -0
  139. package/output/core/classes/expression/traversal.d.ts +31 -0
  140. package/output/core/classes/expression/traversal.js +216 -0
  141. package/output/core/classes/expression/trig.d.ts +25 -0
  142. package/output/core/classes/expression/trig.js +40 -0
  143. package/output/core/classes/expression/utils.d.ts +52 -0
  144. package/output/core/classes/expression/utils.js +189 -0
  145. package/output/core/classes/lookupTable/LookupTable.d.ts +12 -0
  146. package/output/core/classes/lookupTable/LookupTable.js +55 -0
  147. package/output/core/classes/matrix/Matrix.d.ts +227 -0
  148. package/output/core/classes/matrix/Matrix.js +889 -0
  149. package/output/core/classes/matrix/Sylvester.d.ts +11 -0
  150. package/output/core/classes/matrix/Sylvester.js +54 -0
  151. package/output/core/classes/matrix/functions.d.ts +26 -0
  152. package/output/core/classes/matrix/functions.js +52 -0
  153. package/output/core/classes/matrix/utils.d.ts +9 -0
  154. package/output/core/classes/matrix/utils.js +22 -0
  155. package/output/core/classes/parser/Parser.d.ts +480 -0
  156. package/output/core/classes/parser/Parser.js +1812 -0
  157. package/output/core/classes/parser/Token.d.ts +58 -0
  158. package/output/core/classes/parser/Token.js +137 -0
  159. package/output/core/classes/parser/constants.d.ts +160 -0
  160. package/output/core/classes/parser/constants.js +227 -0
  161. package/output/core/classes/parser/helpers.d.ts +21 -0
  162. package/output/core/classes/parser/helpers.js +42 -0
  163. package/output/core/classes/parser/operations/add.d.ts +15 -0
  164. package/output/core/classes/parser/operations/add.js +238 -0
  165. package/output/core/classes/parser/operations/comma.d.ts +7 -0
  166. package/output/core/classes/parser/operations/comma.js +11 -0
  167. package/output/core/classes/parser/operations/compare.d.ts +21 -0
  168. package/output/core/classes/parser/operations/compare.js +461 -0
  169. package/output/core/classes/parser/operations/divide.d.ts +2 -0
  170. package/output/core/classes/parser/operations/divide.js +72 -0
  171. package/output/core/classes/parser/operations/functions.d.ts +46 -0
  172. package/output/core/classes/parser/operations/functions.js +430 -0
  173. package/output/core/classes/parser/operations/multiply.d.ts +11 -0
  174. package/output/core/classes/parser/operations/multiply.js +321 -0
  175. package/output/core/classes/parser/operations/power.d.ts +48 -0
  176. package/output/core/classes/parser/operations/power.js +673 -0
  177. package/output/core/classes/parser/operations/subtract.d.ts +3 -0
  178. package/output/core/classes/parser/operations/subtract.js +35 -0
  179. package/output/core/classes/parser/preprocess.d.ts +14 -0
  180. package/output/core/classes/parser/preprocess.js +259 -0
  181. package/output/core/classes/parser/scripting/controlFlow.d.ts +121 -0
  182. package/output/core/classes/parser/scripting/controlFlow.js +355 -0
  183. package/output/core/classes/parser/scripting/evaluate.d.ts +10 -0
  184. package/output/core/classes/parser/scripting/evaluate.js +112 -0
  185. package/output/core/classes/parser/scripting/functions.d.ts +14 -0
  186. package/output/core/classes/parser/scripting/functions.js +59 -0
  187. package/output/core/classes/parser/scripting/scope.d.ts +35 -0
  188. package/output/core/classes/parser/scripting/scope.js +155 -0
  189. package/output/core/classes/parser/types.d.ts +125 -0
  190. package/output/core/classes/parser/types.js +2 -0
  191. package/output/core/classes/parser/wrappers/IndexedReference.d.ts +31 -0
  192. package/output/core/classes/parser/wrappers/IndexedReference.js +68 -0
  193. package/output/core/classes/parser/wrappers/KeyValuePair.d.ts +24 -0
  194. package/output/core/classes/parser/wrappers/KeyValuePair.js +51 -0
  195. package/output/core/classes/polynomial/Polynomial.d.ts +500 -0
  196. package/output/core/classes/polynomial/Polynomial.js +1149 -0
  197. package/output/core/classes/polynomial/Term.d.ts +222 -0
  198. package/output/core/classes/polynomial/Term.js +430 -0
  199. package/output/core/classes/polynomial/functions.d.ts +69 -0
  200. package/output/core/classes/polynomial/functions.js +126 -0
  201. package/output/core/classes/polynomial/utils.d.ts +62 -0
  202. package/output/core/classes/polynomial/utils.js +206 -0
  203. package/output/core/classes/rational/Rational.d.ts +504 -0
  204. package/output/core/classes/rational/Rational.js +779 -0
  205. package/output/core/classes/seq/SEQ.d.ts +13 -0
  206. package/output/core/classes/seq/SEQ.js +137 -0
  207. package/output/core/classes/valuesSet/ValuesSet.d.ts +179 -0
  208. package/output/core/classes/valuesSet/ValuesSet.js +381 -0
  209. package/output/core/classes/vector/Vector.d.ts +222 -0
  210. package/output/core/classes/vector/Vector.js +497 -0
  211. package/output/core/classes/vector/functions.d.ts +28 -0
  212. package/output/core/classes/vector/functions.js +47 -0
  213. package/output/core/common/classes/MathematicalAggregate.d.ts +53 -0
  214. package/output/core/common/classes/MathematicalAggregate.js +140 -0
  215. package/output/core/common/classes/Scope.d.ts +62 -0
  216. package/output/core/common/classes/Scope.js +122 -0
  217. package/output/core/common/classes/StructuredEntity.d.ts +55 -0
  218. package/output/core/common/classes/StructuredEntity.js +151 -0
  219. package/output/core/common/common.d.ts +63 -0
  220. package/output/core/common/common.js +279 -0
  221. package/output/core/common/functions/functions.d.ts +14 -0
  222. package/output/core/common/functions/functions.js +33 -0
  223. package/output/core/common/functions/structuredEntityUtils.d.ts +27 -0
  224. package/output/core/common/functions/structuredEntityUtils.js +40 -0
  225. package/output/core/converters/BaseConverter.d.ts +97 -0
  226. package/output/core/converters/BaseConverter.js +405 -0
  227. package/output/core/converters/Converter.d.ts +111 -0
  228. package/output/core/converters/Converter.js +802 -0
  229. package/output/core/converters/Pattern.d.ts +71 -0
  230. package/output/core/converters/Pattern.js +302 -0
  231. package/output/core/dispatch.d.ts +34 -0
  232. package/output/core/dispatch.js +304 -0
  233. package/output/core/errors.d.ts +531 -0
  234. package/output/core/errors.js +621 -0
  235. package/output/core/functions/bigint/bigint.d.ts +121 -0
  236. package/output/core/functions/bigint/bigint.js +392 -0
  237. package/output/core/functions/bigint/primeFactor.d.ts +49 -0
  238. package/output/core/functions/bigint/primeFactor.js +267 -0
  239. package/output/core/functions/bigint/primes.d.ts +1 -0
  240. package/output/core/functions/bigint/primes.js +11 -0
  241. package/output/core/functions/build/definitions.d.ts +12 -0
  242. package/output/core/functions/build/definitions.js +133 -0
  243. package/output/core/functions/build/index.d.ts +32 -0
  244. package/output/core/functions/build/index.js +136 -0
  245. package/output/core/functions/complex.d.ts +90 -0
  246. package/output/core/functions/complex.js +482 -0
  247. package/output/core/functions/decimal.d.ts +24 -0
  248. package/output/core/functions/decimal.js +664 -0
  249. package/output/core/functions/expand/expand.d.ts +42 -0
  250. package/output/core/functions/expand/expand.js +376 -0
  251. package/output/core/functions/fresnelNumeric.d.ts +12 -0
  252. package/output/core/functions/fresnelNumeric.js +123 -0
  253. package/output/core/functions/numeric.d.ts +140 -0
  254. package/output/core/functions/numeric.js +790 -0
  255. package/output/core/functions/rationalNormalization.d.ts +11 -0
  256. package/output/core/functions/rationalNormalization.js +117 -0
  257. package/output/core/functions/setFunction.d.ts +19 -0
  258. package/output/core/functions/setFunction.js +75 -0
  259. package/output/core/functions/string.d.ts +26 -0
  260. package/output/core/functions/string.js +101 -0
  261. package/output/core/functions/subst.d.ts +90 -0
  262. package/output/core/functions/subst.js +498 -0
  263. package/output/core/functions/utils.d.ts +24 -0
  264. package/output/core/functions/utils.js +51 -0
  265. package/output/core/types.d.ts +48 -0
  266. package/output/core/types.js +2 -0
  267. package/output/index.d.ts +339 -0
  268. package/output/index.js +715 -0
  269. package/output/math/defint/defintDecimal.d.ts +37 -0
  270. package/output/math/defint/defintDecimal.js +320 -0
  271. package/output/math/defint/defintNative.d.ts +57 -0
  272. package/output/math/defint/defintNative.js +292 -0
  273. package/output/math/geometry.d.ts +8 -0
  274. package/output/math/geometry.js +32 -0
  275. package/output/math/math.d.ts +655 -0
  276. package/output/math/math.js +1811 -0
  277. package/output/math/trig.d.ts +518 -0
  278. package/output/math/trig.js +1444 -0
  279. package/output/math/trunc.d.ts +19 -0
  280. package/output/math/trunc.js +36 -0
  281. package/output/math/utils.d.ts +60 -0
  282. package/output/math/utils.js +180 -0
  283. package/output/solve/classes/DecimalMatrix.d.ts +17 -0
  284. package/output/solve/classes/DecimalMatrix.js +86 -0
  285. package/output/solve/classes/FunctionSolver.d.ts +135 -0
  286. package/output/solve/classes/FunctionSolver.js +433 -0
  287. package/output/solve/classes/MultivariateSolver.d.ts +84 -0
  288. package/output/solve/classes/MultivariateSolver.js +240 -0
  289. package/output/solve/classes/PolynomialSolver.d.ts +85 -0
  290. package/output/solve/classes/PolynomialSolver.js +292 -0
  291. package/output/solve/classes/SolutionSet.d.ts +153 -0
  292. package/output/solve/classes/SolutionSet.js +357 -0
  293. package/output/solve/classes/Solver.d.ts +17 -0
  294. package/output/solve/classes/Solver.js +132 -0
  295. package/output/solve/classes/SymbolicSolver.d.ts +74 -0
  296. package/output/solve/classes/SymbolicSolver.js +601 -0
  297. package/output/solve/linsolve.d.ts +51 -0
  298. package/output/solve/linsolve.js +225 -0
  299. package/output/solve/solve.d.ts +34 -0
  300. package/output/solve/solve.js +260 -0
  301. package/output/solve/solveSystem.d.ts +31 -0
  302. package/output/solve/solveSystem.js +290 -0
  303. package/output/solve/utils.d.ts +9 -0
  304. package/output/solve/utils.js +62 -0
  305. package/output/utils/array.d.ts +42 -0
  306. package/output/utils/array.js +98 -0
  307. package/output/utils/debug.d.ts +131 -0
  308. package/output/utils/debug.js +295 -0
  309. package/output/utils/decimal.d.ts +3 -0
  310. package/output/utils/decimal.js +16 -0
  311. package/output/utils/numeric.d.ts +14 -0
  312. package/output/utils/numeric.js +51 -0
  313. package/output/utils/object.d.ts +26 -0
  314. package/output/utils/object.js +56 -0
  315. package/package.json +213 -57
  316. package/.travis.yml +0 -3
  317. package/Algebra.js +0 -4568
  318. package/BREAKING_CHANGES.md +0 -5
  319. package/CODE_OF_CONDUCT.md +0 -46
  320. package/CONTRIBUTING.md +0 -14
  321. package/Calculus.js +0 -2675
  322. package/Extra.js +0 -622
  323. package/Solve.js +0 -1783
  324. package/all.js +0 -16
  325. package/all.min.js +0 -1
  326. package/gulpfile.js +0 -16
  327. package/index.html +0 -158
  328. package/license.txt +0 -19
  329. package/nerdamer.core.js +0 -12510
  330. package/spec/LaTeX.spec.js +0 -302
  331. package/spec/TeXConvert.spec.js +0 -8
  332. package/spec/algebra.spec.js +0 -287
  333. package/spec/basic_parser.spec.js +0 -134
  334. package/spec/build.spec.js +0 -131
  335. package/spec/calculus.spec.js +0 -183
  336. package/spec/core.spec.js +0 -2970
  337. package/spec/extra.spec.js +0 -54
  338. package/spec/solve.spec.js +0 -126
  339. package/spec/support/jasmine.json +0 -11
  340. package/spec/support/utils.js +0 -42
  341. package/spec/text.spec.js +0 -132
@@ -0,0 +1,1812 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Parser = void 0;
4
+ const array_1 = require("../../../utils/array");
5
+ const Scope_1 = require("../../common/classes/Scope");
6
+ const common_1 = require("../../common/common");
7
+ const functions_1 = require("../../common/functions/functions");
8
+ const structuredEntityUtils_1 = require("../../common/functions/structuredEntityUtils");
9
+ const dispatch_1 = require("../../dispatch");
10
+ const errors_1 = require("../../errors");
11
+ const string_1 = require("../../functions/string");
12
+ const Settings_1 = require("../../Settings");
13
+ const Collection_1 = require("../collection/Collection");
14
+ const Dictionary_1 = require("../dictionary/Dictionary");
15
+ const Equation_1 = require("../equation/Equation");
16
+ const Expression_1 = require("../expression/Expression");
17
+ const Matrix_1 = require("../matrix/Matrix");
18
+ const Rational_1 = require("../rational/Rational");
19
+ const ValuesSet_1 = require("../valuesSet/ValuesSet");
20
+ const Vector_1 = require("../vector/Vector");
21
+ const constants_1 = require("./constants");
22
+ const functions_2 = require("./operations/functions");
23
+ const preprocess_1 = require("./preprocess");
24
+ const Token_1 = require("./Token");
25
+ const IndexedReference_1 = require("./wrappers/IndexedReference");
26
+ const KeyValuePair_1 = require("./wrappers/KeyValuePair");
27
+ function shouldPopOperator(stackOperator, incomingOperator) {
28
+ let retval = stackOperator.precedence > incomingOperator.precedence;
29
+ if (!retval && stackOperator.precedence === incomingOperator.precedence) {
30
+ retval = incomingOperator.leftAssoc;
31
+ }
32
+ return retval;
33
+ }
34
+ /**
35
+ * Parses Nerdamer notation into symbolic and structured parser entities.
36
+ *
37
+ * @remarks
38
+ * The parser has three main stages: {@link tokenize} builds a nested token tree,
39
+ * {@link toRPN} applies the Shunting Yard algorithm within those scopes, and
40
+ * {@link parseRPN} evaluates the resulting stack representation. {@link parse} is
41
+ * the ordinary front end that runs that pipeline for a caller.
42
+ *
43
+ * Parsing does not always produce an {@link Expression}. Depending on the syntax
44
+ * and functions involved, the result can also be an equation, vector, matrix,
45
+ * collection, finite value set, or dictionary. Callers that require a specific
46
+ * parser entity should narrow the returned type rather than assuming an expression.
47
+ *
48
+ * The exported {@link Parser} singleton is the normal parser instance. Several
49
+ * parts of parser behavior are process-wide rather than instance-local, including
50
+ * parser settings, the constant and operator registries, Rational/Decimal precision,
51
+ * and the symbol reserved for the imaginary unit. {@link create} therefore creates
52
+ * another parser object, but it does not create an isolated parser context.
53
+ *
54
+ * The parser is a convenient notation front end, not the only supported way to use
55
+ * Nerdamer. Algorithms that already have {@link Expression} or other Nerdamer
56
+ * objects can operate on those objects directly without converting through text.
57
+ *
58
+ * @example
59
+ * ```ts
60
+ * Parser.parse('2*x + 1').text(); // "1+2*x"
61
+ * Parser.parse('x^2', { x: 3 }).text(); // "9"
62
+ * Parser.parse('[x, 2]').text(); // "[x, 2]"
63
+ * Parser.parse('{a => x, b => 2}').text(); // "{a => x, b => 2}"
64
+ * ```
65
+ */
66
+ class ExpressionParser {
67
+ /**
68
+ * Registry of named constants recognized by this parser.
69
+ *
70
+ * @remarks
71
+ * The registry object is shared by parser instances. Use {@link setConstants}
72
+ * instead of replacing or mutating this object directly so constant registration
73
+ * follows the parser's normal behavior.
74
+ */
75
+ CONSTANTS = constants_1.PARSER_CONSTANTS;
76
+ /**
77
+ * The maximum number of operators a compound operator can consist of
78
+ */
79
+ MAX_COMPOUND_OPERATOR_LENGTH = 2;
80
+ // Operators supported by the parser
81
+ operators = (0, functions_1.getOperators)();
82
+ VALUE_SETS = [constants_1.VECTOR, constants_1.MATRIX];
83
+ // Define whitespace to be ignored.
84
+ WHITE_SPACE = [constants_1.NEWLINE, constants_1.TAB, constants_1.RETURN];
85
+ /**
86
+ * Persistent known-value substitutions consulted during parsing.
87
+ *
88
+ * @remarks
89
+ * Known values are substituted only while the global `SUBSTITUTE` setting is
90
+ * enabled. A stored parser entity is copied before it is inserted into a parsed
91
+ * result, so ordinary substitution does not hand out the stored object itself.
92
+ */
93
+ KNOWN_VALUES = {};
94
+ /**
95
+ * Converts an index scope to the zero-based indices used internally.
96
+ *
97
+ * Literal numeric indices are converted directly without parsing or evaluation.
98
+ * Symbolic or computed indices are returned as Expressions when they cannot yet
99
+ * be resolved. Resolved numeric values subtract `INDEX_BASE`; symbolic values retain
100
+ * the public index expressions so they can be evaluated later.
101
+ */
102
+ extractIndices(scope, values, assertive) {
103
+ const indices = [];
104
+ let allNumeric = true;
105
+ for (let k = 0; k < scope.length; k++) {
106
+ const item = scope[k];
107
+ if (Scope_1.Scope.isScope(item)) {
108
+ allNumeric = false;
109
+ break;
110
+ }
111
+ if (item.type === Token_1.Token.OPERATOR) {
112
+ continue;
113
+ }
114
+ if (item.type === Token_1.Token.NUMBER) {
115
+ indices.push(Number(item.value) - Settings_1.Settings.INDEX_BASE);
116
+ }
117
+ else {
118
+ allNumeric = false;
119
+ break;
120
+ }
121
+ }
122
+ let retval;
123
+ if (allNumeric && indices.length > 0) {
124
+ retval = indices;
125
+ }
126
+ else {
127
+ // Retain the parsed public expressions if evaluation is not concrete yet.
128
+ const indexExpr = this.parseRPN(scope, values, assertive);
129
+ const symbolicIndices = [];
130
+ if (Vector_1.Vector.isVector(indexExpr)) {
131
+ for (let j = 0; j < indexExpr.elements.length; j++) {
132
+ symbolicIndices.push(Expression_1.Expression.create(indexExpr.elements[j]));
133
+ }
134
+ }
135
+ else if (Collection_1.Collection.isCollection(indexExpr)) {
136
+ for (const el of indexExpr.getElements()) {
137
+ symbolicIndices.push(Expression_1.Expression.create(el));
138
+ }
139
+ }
140
+ else {
141
+ symbolicIndices.push(Expression_1.Expression.create(indexExpr));
142
+ }
143
+ const resolvedIndices = [];
144
+ let allResolved = true;
145
+ for (let j = 0; j < symbolicIndices.length; j++) {
146
+ const numericIndex = Number(symbolicIndices[j].evaluate().text());
147
+ if (!Number.isFinite(numericIndex)) {
148
+ allResolved = false;
149
+ break;
150
+ }
151
+ resolvedIndices.push(numericIndex - Settings_1.Settings.INDEX_BASE);
152
+ }
153
+ retval = allResolved ? resolvedIndices : symbolicIndices;
154
+ }
155
+ return retval;
156
+ }
157
+ /**
158
+ * Extracts string keys from an index scope for Dictionary access.
159
+ * For d[x], the scope contains a single variable token "x" → returns ["x"].
160
+ * For d[myKey], returns ["myKey"].
161
+ */
162
+ extractStringKeys(scope) {
163
+ const keys = [];
164
+ for (let k = 0; k < scope.length; k++) {
165
+ const item = scope[k];
166
+ if (Scope_1.Scope.isScope(item)) {
167
+ // Nested scope — not expected for dictionary keys
168
+ continue;
169
+ }
170
+ if (item.type === Token_1.Token.OPERATOR) {
171
+ // Skip comma separators
172
+ continue;
173
+ }
174
+ // Use the token value as the string key
175
+ keys.push(item.value);
176
+ }
177
+ return keys;
178
+ }
179
+ /** Creates another spelling for an existing operator without changing its action. */
180
+ aliasOperator(symbol, alias) {
181
+ const source = this.operators[symbol];
182
+ if (!source) {
183
+ throw new errors_1.ParserError(`Unknown operator ${symbol}`);
184
+ }
185
+ this.operators[alias] = {
186
+ ...source,
187
+ operator: alias,
188
+ postfixVariant: source.postfixVariant
189
+ ? { ...source.postfixVariant, operator: alias }
190
+ : undefined,
191
+ };
192
+ (0, Token_1.refreshOperatorSymbols)();
193
+ return this;
194
+ }
195
+ /**
196
+ * Creates another parser instance.
197
+ *
198
+ * @remarks
199
+ * The returned parser object is distinct and starts with its own `KNOWN_VALUES`
200
+ * table, but it is not an isolated parser environment. Parser settings, constants,
201
+ * operators, Rational/Decimal precision, and the imaginary-unit symbol are shared
202
+ * process-wide. Changes made through methods such as {@link set},
203
+ * {@link setConstants}, {@link setPrecision}, or {@link setI} therefore affect
204
+ * parsing outside the newly created instance as well.
205
+ *
206
+ * @returns A distinct parser instance that shares Nerdamer's global parser state.
207
+ *
208
+ * @example
209
+ * ```ts
210
+ * const parser = Parser.create();
211
+ *
212
+ * parser === Parser; // false
213
+ * parser.parse('x + 1').text(); // "1+x"
214
+ * ```
215
+ */
216
+ create() {
217
+ return new ExpressionParser();
218
+ }
219
+ /**
220
+ * Parses an expression with evaluation mode temporarily enabled.
221
+ *
222
+ * @remarks
223
+ * Supported numeric constants and functions are evaluated eagerly at the current
224
+ * configured precision, but this method does not guarantee a purely numeric result;
225
+ * symbolic terms that cannot be evaluated remain symbolic. The previous global
226
+ * `EVALUATE` setting is restored after parsing, including when parsing throws.
227
+ *
228
+ * @param str - Expression text to parse while evaluation mode is enabled.
229
+ * @param values - Optional call-scoped substitutions applied as in {@link parse}.
230
+ * @returns The parser entity produced by the evaluated parse.
231
+ *
232
+ * @example
233
+ * ```ts
234
+ * Parser.evaluate('2^10').text(); // "1024"
235
+ * Parser.evaluate('x+y', { x: 1, y: 2 }).text(); // "3"
236
+ * Parser.evaluate('x+1').text(); // "1+x"
237
+ * ```
238
+ */
239
+ evaluate(str, values) {
240
+ return (0, Settings_1.scopedBlock)('EVALUATE', true, () => {
241
+ return exports.Parser.parse(str, values);
242
+ });
243
+ }
244
+ /**
245
+ * Reads the current value of a process-wide parser setting.
246
+ *
247
+ * @param setting - The parser setting to read.
248
+ * @returns The setting's current global value.
249
+ *
250
+ * @example
251
+ * ```ts
252
+ * const evaluate = Parser.get('EVALUATE');
253
+ * const indexBase = Parser.get('INDEX_BASE');
254
+ * ```
255
+ */
256
+ get(setting) {
257
+ return Settings_1.Settings[setting];
258
+ }
259
+ /**
260
+ * Returns the symbol currently reserved for the imaginary unit.
261
+ *
262
+ * @remarks
263
+ * The symbol is process-wide and can be changed through {@link setI}. Changing it
264
+ * affects subsequent parser behavior and complex-number classification; it does
265
+ * not rewrite expressions that have already been created.
266
+ *
267
+ * @returns The current imaginary-unit variable name.
268
+ *
269
+ * @example
270
+ * ```ts
271
+ * Parser.getI(); // "i" with the default configuration
272
+ * ```
273
+ */
274
+ getI() {
275
+ return Expression_1.Expression.imaginary;
276
+ }
277
+ /** Returns a detached snapshot of registered operator metadata. */
278
+ getOperator(symbol) {
279
+ const operator = this.operators[symbol];
280
+ let retval;
281
+ if (operator) {
282
+ retval = {
283
+ ...operator,
284
+ postfixVariant: operator.postfixVariant
285
+ ? { ...operator.postfixVariant }
286
+ : undefined,
287
+ };
288
+ }
289
+ return retval;
290
+ }
291
+ /**
292
+ * Returns the current shared precision used by Decimal-backed numeric operations.
293
+ *
294
+ * @remarks
295
+ * This is the same precision reported by {@link Rational.getPrecision}. Exact
296
+ * rational arithmetic remains exact; the setting matters when Nerdamer crosses
297
+ * into Decimal-backed numerical work, generates numerical constants, or formats
298
+ * precision-sensitive decimal results.
299
+ *
300
+ * @returns The configured number of significant digits.
301
+ */
302
+ getPrecision() {
303
+ return Rational_1.Rational.getPrecision();
304
+ }
305
+ /**
306
+ * Parses supported expression input into a Nerdamer parser entity.
307
+ *
308
+ * @remarks
309
+ * The input is converted to text, tokenized with {@link tokenize}, transformed
310
+ * with {@link toRPN}, and evaluated with {@link parseRPN}. Passing an existing
311
+ * {@link Expression} therefore reparses its textual representation rather than
312
+ * preserving that object's identity.
313
+ *
314
+ * The result is determined by the parsed syntax. Ordinary scalar input produces
315
+ * an {@link Expression}, while equations and structured syntax can produce other
316
+ * parser entities such as vectors, matrices, collections, finite value sets, or
317
+ * dictionaries.
318
+ *
319
+ * The `values` object provides substitutions for this parse call. The caller's
320
+ * object is not mutated; scalar values are normalized to expressions before use,
321
+ * while existing parser entities are preserved structurally. Each inserted parser
322
+ * entity is copied. Registered string constants are resolved before call-scoped
323
+ * values. Call-scoped values in turn take precedence over persistent known values
324
+ * and over function-backed constants such as `pi` and `e` when those constants are
325
+ * being numerically evaluated.
326
+ *
327
+ * Parser and operator errors are propagated when the input is malformed or an
328
+ * operation is not valid for the parsed operands.
329
+ *
330
+ * @param str - Expression-compatible input whose textual form should be parsed.
331
+ * @param values - Optional call-scoped substitutions. Scalar values are normalized
332
+ * to {@link Expression} objects; existing parser entities are preserved structurally
333
+ * and copied when inserted.
334
+ * @returns The expression or structured parser entity represented by the input.
335
+ *
336
+ * @example
337
+ * ```ts
338
+ * Parser.parse('x^2 + 2*x + 1').text(); // "1+2*x+x^2"
339
+ * Parser.parse('a + b', { a: 3, b: 4 }).text(); // "7"
340
+ * Parser.parse('x = 2').text(); // "x=2"
341
+ * Parser.parse('[x, 2, 3]').text(); // "[x, 2, 3]"
342
+ * ```
343
+ */
344
+ parse(str, values) {
345
+ // Normalize scalar values once while preserving existing parser entities.
346
+ if (values) {
347
+ // We don't want to modify the object.
348
+ const valuesObj = {};
349
+ for (const value in values) {
350
+ // Ignore values such as e or pi
351
+ // if (SPECIAL.includes(value)) {
352
+ // continue;
353
+ // }
354
+ const input = values[value];
355
+ valuesObj[value] =
356
+ Expression_1.Expression.isExpression(input) || Equation_1.Equation.isEquation(input) || (0, structuredEntityUtils_1.isEnumerable)(input)
357
+ ? input
358
+ : Expression_1.Expression.create(input);
359
+ }
360
+ values = valuesObj;
361
+ }
362
+ // Normalize accepted parser shorthand before the ordinary tokenizer resolves
363
+ // adjacency and implicit multiplication.
364
+ const source = (0, preprocess_1.preprocess)(String(str), dispatch_1.mathFunctionRegistry);
365
+ const rpn = this.toRPN(this.tokenize(source));
366
+ // Read it into an expression.
367
+ const retval = this.parseRPN(rpn, values);
368
+ return retval;
369
+ }
370
+ /**
371
+ * Evaluates a token scope that has been converted to Reverse Polish Notation.
372
+ *
373
+ * @remarks
374
+ * This is the parser's core stack evaluator. It applies prefix, postfix, and
375
+ * binary operators; dispatches function calls; resolves substitutions and known
376
+ * values; performs indexing and indexed assignment; and routes operations involving
377
+ * equations or enumerable structures to the appropriate aggregate implementation.
378
+ *
379
+ * Square scopes become vectors. Curly scopes become dictionaries when they contain
380
+ * `=>` key/value markers and otherwise become finite value sets. Multiple unresolved
381
+ * top-level values become a collection. Internal stack markers used for indexing and
382
+ * dictionary construction are resolved or consumed before a successful return.
383
+ *
384
+ * When `assertive` is `true`, operators use their assertive actions where defined.
385
+ * This is used by assumption-related functions so comparisons can assert conditions
386
+ * instead of performing ordinary comparison evaluation.
387
+ *
388
+ * @param rpn - RPN scope, normally produced by {@link toRPN}.
389
+ * @param values - Optional call-scoped substitutions already prepared for parser use.
390
+ * @param assertive - Use assertion-oriented operator actions where the operator defines one.
391
+ * @returns The parser entity produced by evaluating the scope.
392
+ * @throws {@link ParserError} When the stack contains a malformed or unresolved parser construct.
393
+ */
394
+ parseRPN(rpn, values, assertive) {
395
+ // The output stack
396
+ const output = [];
397
+ const operators = this.operators;
398
+ // TODO: this needs to be applied at the operation level. It's somewhat clunky to apply this here.
399
+ // const precision = this.getPrecision();
400
+ function addToOutput(expression) {
401
+ if (expression === undefined) {
402
+ throw new errors_1.ParserError((0, errors_1.message)('malformedExpression'));
403
+ }
404
+ // Keep track of the precision used to make this calculation
405
+ // TODO: this needs to be applied at the operation level. It's somewhat clunky to apply this here.
406
+ // expression.precision = expression.precision || precision;
407
+ output.push(expression);
408
+ }
409
+ function resolveStackValue(value) {
410
+ if (IndexedReference_1.IndexedReference.isIndexedReference(value)) {
411
+ return value.resolve();
412
+ }
413
+ if (KeyValuePair_1.KeyValuePair.isKeyValuePair(value) || typeof value === 'function') {
414
+ throw new errors_1.ParserError((0, errors_1.message)('malformedExpression'));
415
+ }
416
+ return value;
417
+ }
418
+ function resolveStackValues(values) {
419
+ const resolved = [];
420
+ for (const value of values) {
421
+ resolved.push(resolveStackValue(value));
422
+ }
423
+ return resolved;
424
+ }
425
+ const createDeferredArgument = (getArgumentRPN, inheritedValues, functionAssertive) => {
426
+ let argumentRPN;
427
+ return (options) => {
428
+ const rpn = argumentRPN ?? getArgumentRPN();
429
+ argumentRPN = rpn;
430
+ let argumentValues = options?.inheritValues === false ? undefined : inheritedValues;
431
+ if (argumentValues && options?.excludeValues?.length) {
432
+ argumentValues = { ...argumentValues };
433
+ for (const name of options.excludeValues) {
434
+ delete argumentValues[name];
435
+ }
436
+ }
437
+ let retval;
438
+ if (typeof options?.substitute === 'boolean') {
439
+ retval = (0, Settings_1.scopedBlock)('SUBSTITUTE', options.substitute, () => {
440
+ return this.parseRPN(rpn, argumentValues, functionAssertive);
441
+ });
442
+ }
443
+ else {
444
+ retval = this.parseRPN(rpn, argumentValues, functionAssertive);
445
+ }
446
+ return retval;
447
+ };
448
+ };
449
+ // Begin parsing of tokens
450
+ for (let i = 0; i < rpn.length; i++) {
451
+ // Grab the token or Scope
452
+ const token = rpn[i];
453
+ if (Scope_1.Scope.isScope(token)) {
454
+ const lastOutput = output[output.length - 1];
455
+ if (token.deferRHSResolution) {
456
+ addToOutput(createDeferredArgument(() => token, values, assertive));
457
+ }
458
+ else if (token.deferLHSResolution) {
459
+ // Left-hand targets are parsed without substitution so assignment can update a
460
+ // variable that already has a known value.
461
+ const parsed = (0, Settings_1.scopedBlock)('SUBSTITUTE', false, () => {
462
+ return this.parseRPN(token, undefined, assertive);
463
+ });
464
+ addToOutput(parsed);
465
+ }
466
+ // A square scope is a candidate for indexing or implicit multiplication ONLY
467
+ // when the tokenizer flagged it with implicitMultiply (meaning '[' directly
468
+ // followed a non-operator/non-function token). Without that flag, the scope
469
+ // is a standalone vector literal (e.g. in matrix([1,0],[2,3]) where commas
470
+ // separate the square scopes).
471
+ else if (token.type === 'square' && token.implicitMultiply && lastOutput) {
472
+ if ((0, structuredEntityUtils_1.isEnumerable)(lastOutput) ||
473
+ IndexedReference_1.IndexedReference.isIndexedReference(lastOutput)) {
474
+ // Indexing: target[indices]
475
+ const resolvedTarget = IndexedReference_1.IndexedReference.isIndexedReference(lastOutput)
476
+ ? lastOutput.resolve()
477
+ : lastOutput;
478
+ const previousRPN = rpn[i - 1];
479
+ let targetName;
480
+ if (Token_1.Token.isToken(previousRPN) &&
481
+ previousRPN.type === Token_1.Token.VARIABLE &&
482
+ !(values && previousRPN.value in values) &&
483
+ previousRPN.value in this.KNOWN_VALUES) {
484
+ targetName = previousRPN.value;
485
+ }
486
+ let indices;
487
+ if (Dictionary_1.Dictionary.isDictionary(resolvedTarget)) {
488
+ // Dictionary indexing: extract string keys from the index scope.
489
+ // d[x] → key "x"
490
+ const keys = this.extractStringKeys(token);
491
+ indices = keys[0];
492
+ }
493
+ else {
494
+ // Numeric indexing for Vector, Matrix, etc.
495
+ indices = this.extractIndices(token, values, assertive);
496
+ }
497
+ output.pop();
498
+ if (Array.isArray(indices) &&
499
+ indices.length > 0 &&
500
+ Expression_1.Expression.isExpression(indices[0])) {
501
+ const symbolicIndices = indices;
502
+ let symbolicTarget;
503
+ if ((Vector_1.Vector.isVector(resolvedTarget) || Matrix_1.Matrix.isMatrix(resolvedTarget)) &&
504
+ resolvedTarget.symbolicTarget) {
505
+ symbolicTarget = resolvedTarget.symbolicTarget;
506
+ }
507
+ else if (Token_1.Token.isToken(previousRPN) &&
508
+ previousRPN.type === Token_1.Token.VARIABLE) {
509
+ symbolicTarget = Expression_1.Expression.Variable(previousRPN.value);
510
+ }
511
+ if (!symbolicTarget ||
512
+ !(Vector_1.Vector.isVector(resolvedTarget) || Matrix_1.Matrix.isMatrix(resolvedTarget))) {
513
+ throw new errors_1.ParserError('Symbolic indexed access requires a named Vector or Matrix target.');
514
+ }
515
+ if (Vector_1.Vector.isVector(resolvedTarget) && symbolicIndices.length !== 1) {
516
+ throw new errors_1.ParserError('Symbolic Vector access requires exactly one index.');
517
+ }
518
+ if (Matrix_1.Matrix.isMatrix(resolvedTarget) && symbolicIndices.length !== 2) {
519
+ if (symbolicIndices.length === 1) {
520
+ throw new errors_1.ParserError('Symbolic Matrix row access is not scalar and cannot be deferred as an Expression.');
521
+ }
522
+ throw new errors_1.ParserError('Symbolic Matrix cell access requires exactly two indices.');
523
+ }
524
+ addToOutput(resolvedTarget.withSymbolicAccessor(symbolicTarget, symbolicIndices));
525
+ }
526
+ else {
527
+ addToOutput(new IndexedReference_1.IndexedReference(resolvedTarget, indices, targetName));
528
+ }
529
+ }
530
+ else if (Expression_1.Expression.isExpression(lastOutput) && lastOutput.isPlainVariable()) {
531
+ // An unresolved variable followed by symbolic indices cannot yet be
532
+ // classified from runtime type information. Preserve the access in a
533
+ // structured carrier; concrete bracket adjacency keeps its legacy
534
+ // implicit-multiplication behavior.
535
+ const indices = this.extractIndices(token, values, assertive);
536
+ if (indices.length > 0 &&
537
+ Expression_1.Expression.isExpression(indices[0])) {
538
+ const symbolicIndices = indices;
539
+ if (symbolicIndices.length > 2) {
540
+ throw new errors_1.ParserError('Symbolic indexed access supports at most two unresolved indices.');
541
+ }
542
+ const carrier = symbolicIndices.length === 2 ? new Matrix_1.Matrix([]) : new Vector_1.Vector();
543
+ output.pop();
544
+ addToOutput(carrier.withSymbolicAccessor(lastOutput, symbolicIndices));
545
+ }
546
+ else if (Settings_1.Settings.ALLOW_IMPLICIT_MULTIPLICATION) {
547
+ const indexExpr = this.parseRPN(token, values, assertive);
548
+ const mulFn = functions_2._['multiply'];
549
+ const a = resolveStackValue(output.pop());
550
+ addToOutput(mulFn(a, indexExpr));
551
+ }
552
+ else {
553
+ const parsed = this.parseRPN(token, values, assertive);
554
+ addToOutput(parsed);
555
+ }
556
+ }
557
+ else if (token.implicitMultiply && Settings_1.Settings.ALLOW_IMPLICIT_MULTIPLICATION) {
558
+ // Implicit multiplication: x[1,2] where x is not a structured entity.
559
+ const indexExpr = this.parseRPN(token, values, assertive);
560
+ const mulFn = functions_2._['multiply'];
561
+ const a = resolveStackValue(output.pop());
562
+ addToOutput(mulFn(a, indexExpr));
563
+ }
564
+ else {
565
+ const parsed = this.parseRPN(token, values, assertive);
566
+ addToOutput(parsed);
567
+ }
568
+ }
569
+ else {
570
+ // Non-square scope or inside parentheses or nothing on output — parse normally
571
+ const parsed = this.parseRPN(token, values, assertive);
572
+ addToOutput(parsed);
573
+ }
574
+ }
575
+ else {
576
+ if (token.type === Token_1.Token.OPERATOR) {
577
+ // Grab the operator. The action will be defined by the operation property
578
+ const operator = token.resolvedOperator ?? this.operators[token.value];
579
+ // Get the last element on output
580
+ let b = output.pop();
581
+ let result;
582
+ // Comma is the only operation that passes parser stack markers through unchanged.
583
+ // This allows KeyValuePair markers to survive until the enclosing curly scope is
584
+ // converted to a Dictionary.
585
+ if (operator.action === 'comma') {
586
+ const a = output.pop();
587
+ const fn = functions_2._[operator.action];
588
+ result = fn(a, b);
589
+ }
590
+ // If it's a postfix operator then the operation occurs only on the previous element
591
+ else if (operator.isPostfix) {
592
+ b = resolveStackValue(b);
593
+ b = Expression_1.Expression.fromSymbolicAccess(b) ?? b;
594
+ const fn = functions_2._[operator.action];
595
+ result = fn(b);
596
+ }
597
+ else {
598
+ let a = output.pop();
599
+ const previousRPN = rpn[i - 1];
600
+ const possibleFunction = rpn[i - 2];
601
+ if (token.position === -1 &&
602
+ operator.action === 'times' &&
603
+ Scope_1.Scope.isScope(previousRPN) &&
604
+ Collection_1.Collection.isCollection(b) &&
605
+ Token_1.Token.isToken(possibleFunction) &&
606
+ possibleFunction.type === Token_1.Token.VARIABLE &&
607
+ Expression_1.Expression.isExpression(a) &&
608
+ a.isPlainVariable()) {
609
+ throw new errors_1.ParserError(`Unsupported function ${possibleFunction.value}`);
610
+ }
611
+ // Handle indexed assignment before ordinary left-hand resolution so the
612
+ // reference itself remains available to the setter.
613
+ if (operator.action === 'assign' &&
614
+ IndexedReference_1.IndexedReference.isIndexedReference(a)) {
615
+ b = resolveStackValue(b);
616
+ if (typeof a.indices === 'string') {
617
+ a.target.__set__(a.indices, b);
618
+ }
619
+ else {
620
+ a.target.__set__(a.indices, b);
621
+ }
622
+ if (a.targetName) {
623
+ this.KNOWN_VALUES[a.targetName] = a.target;
624
+ }
625
+ result = a.target;
626
+ }
627
+ else if (operator.deferRHSResolution) {
628
+ a = resolveStackValue(a);
629
+ if (typeof b !== 'function') {
630
+ throw new errors_1.ParserError((0, errors_1.message)('malformedExpression'));
631
+ }
632
+ const fn = functions_2._[operator.action];
633
+ result = fn(a, b);
634
+ }
635
+ else if (operator.deferLHSResolution) {
636
+ a = resolveStackValue(a);
637
+ b = resolveStackValue(b);
638
+ const fn = functions_2._[operator.action];
639
+ result = fn(a, b);
640
+ }
641
+ else if (operator.action === 'mapTo') {
642
+ // '=>' operator: create a key-value pair for Dictionary construction
643
+ // 'a' is the key (should be a variable name), 'b' is the value
644
+ a = resolveStackValue(a);
645
+ b = resolveStackValue(b);
646
+ const key = Expression_1.Expression.isExpression(a) ? a.text() : String(a.text());
647
+ result = new KeyValuePair_1.KeyValuePair(key, b);
648
+ }
649
+ else {
650
+ // Resolve concrete indexed references and normalize symbolic structured
651
+ // access before ordinary scalar/aggregate operator routing.
652
+ a = resolveStackValue(a);
653
+ b = resolveStackValue(b);
654
+ a = Expression_1.Expression.fromSymbolicAccess(a) ?? a;
655
+ b = Expression_1.Expression.fromSymbolicAccess(b) ?? b;
656
+ const action = assertive && operator.assertiveAction
657
+ ? operator.assertiveAction
658
+ : operator.action;
659
+ const fn = functions_2._[action];
660
+ if (a.isEnumerable ||
661
+ b.isEnumerable ||
662
+ Equation_1.Equation.isEquation(a) ||
663
+ Equation_1.Equation.isEquation(b)) {
664
+ // Delegate this to the router who can handle more
665
+ result = (0, functions_2.route)(a, b, operator.action);
666
+ }
667
+ else {
668
+ result = fn(a, b);
669
+ }
670
+ }
671
+ }
672
+ if (Array.isArray(result)) {
673
+ addToOutput(result[0]);
674
+ addToOutput(result[1]);
675
+ }
676
+ else {
677
+ addToOutput(result);
678
+ }
679
+ }
680
+ else if (token.type === Token_1.Token.FUNCTION) {
681
+ // Get the next token and move along
682
+ const argsScope = rpn[++i];
683
+ if (token.value === constants_1.SYMBOLIC_ACCESSOR) {
684
+ // Preserve the original target and index expressions while resolving the
685
+ // same RPN scope under the active values. This avoids serializing either
686
+ // side back through tokenize/toRPN just to apply substitutions.
687
+ const preserved = (0, Settings_1.scopedBlock)('SUBSTITUTE', false, () => {
688
+ return this.parseRPN(argsScope, undefined, assertive);
689
+ });
690
+ const preservedArgs = Collection_1.Collection.isCollection(preserved)
691
+ ? preserved.getElements()
692
+ : [preserved];
693
+ const resolved = this.parseRPN(argsScope, values, assertive);
694
+ const resolvedArgs = Collection_1.Collection.isCollection(resolved)
695
+ ? resolved.getElements()
696
+ : [resolved];
697
+ if (preservedArgs.length < 2 ||
698
+ preservedArgs.length !== resolvedArgs.length ||
699
+ !Expression_1.Expression.isExpression(preservedArgs[0]) ||
700
+ !preservedArgs[0].isPlainVariable()) {
701
+ throw new errors_1.ParserError('Malformed internal symbolic accessor.');
702
+ }
703
+ const symbolicTarget = preservedArgs[0];
704
+ const resolvedTarget = resolvedArgs[0];
705
+ const symbolicIndices = [];
706
+ const numericIndices = [];
707
+ let allResolved = true;
708
+ for (let j = 1; j < preservedArgs.length; j++) {
709
+ const preservedIndex = preservedArgs[j];
710
+ const resolvedIndex = resolvedArgs[j];
711
+ if (!Expression_1.Expression.isExpression(preservedIndex)) {
712
+ throw new errors_1.ParserError('Malformed internal symbolic accessor index.');
713
+ }
714
+ if (!Expression_1.Expression.isExpression(resolvedIndex)) {
715
+ throw new errors_1.ParserError('Symbolic accessor indices must evaluate to scalar expressions.');
716
+ }
717
+ symbolicIndices.push(resolvedIndex);
718
+ const numericIndex = Number(resolvedIndex.text());
719
+ if (Number.isFinite(numericIndex)) {
720
+ numericIndices.push(numericIndex - Settings_1.Settings.INDEX_BASE);
721
+ }
722
+ else {
723
+ allResolved = false;
724
+ }
725
+ }
726
+ if (symbolicIndices.length > 2) {
727
+ throw new errors_1.ParserError('Symbolic indexed access supports at most two unresolved indices.');
728
+ }
729
+ if (Vector_1.Vector.isVector(resolvedTarget) && symbolicIndices.length !== 1) {
730
+ throw new errors_1.ParserError('Symbolic Vector access requires exactly one index.');
731
+ }
732
+ if (Matrix_1.Matrix.isMatrix(resolvedTarget) && symbolicIndices.length !== 2) {
733
+ if (symbolicIndices.length === 1) {
734
+ throw new errors_1.ParserError('Symbolic Matrix row access is not scalar and cannot be deferred as an Expression.');
735
+ }
736
+ throw new errors_1.ParserError('Symbolic Matrix cell access requires exactly two indices.');
737
+ }
738
+ let result;
739
+ if ((Vector_1.Vector.isVector(resolvedTarget) || Matrix_1.Matrix.isMatrix(resolvedTarget)) && allResolved) {
740
+ result = resolvedTarget.__get__(numericIndices);
741
+ }
742
+ else {
743
+ let carrier;
744
+ if (Vector_1.Vector.isVector(resolvedTarget) || Matrix_1.Matrix.isMatrix(resolvedTarget)) {
745
+ carrier = resolvedTarget;
746
+ }
747
+ else if (Expression_1.Expression.isExpression(resolvedTarget) &&
748
+ resolvedTarget.isPlainVariable()) {
749
+ carrier = symbolicIndices.length === 2 ? new Matrix_1.Matrix([]) : new Vector_1.Vector();
750
+ }
751
+ else {
752
+ throw new errors_1.ParserError('Symbolic indexed access target must resolve to a Vector or Matrix.');
753
+ }
754
+ result = carrier.withSymbolicAccessor(symbolicTarget, symbolicIndices);
755
+ }
756
+ addToOutput(result);
757
+ }
758
+ else if (dispatch_1.mathFunctionRegistry[token.value]?.deferArguments) {
759
+ // Deferred functions receive callable arguments and decide when, whether, and how
760
+ // often each argument is evaluated.
761
+ const argumentScopes = [];
762
+ if (argsScope.length > 0) {
763
+ let argumentScope = new Scope_1.Scope(argsScope.type, argsScope.column);
764
+ for (const argumentToken of argsScope) {
765
+ if (Token_1.Token.isToken(argumentToken) &&
766
+ argumentToken.type === Token_1.Token.OPERATOR &&
767
+ argumentToken.value === constants_1.COMMA) {
768
+ argumentScopes.push(argumentScope);
769
+ argumentScope = new Scope_1.Scope(argsScope.type, argsScope.column);
770
+ }
771
+ else {
772
+ argumentScope.push(argumentToken);
773
+ }
774
+ }
775
+ argumentScopes.push(argumentScope);
776
+ }
777
+ const normalizeArguments = dispatch_1.mathFunctionRegistry[token.value].normalizeDeferredArguments;
778
+ const normalizedScopes = normalizeArguments
779
+ ? normalizeArguments(argumentScopes)
780
+ : argumentScopes;
781
+ const functionAssertive = common_1.ASSERTIVE_FUNCTIONS.includes(token.value);
782
+ const args = normalizedScopes.map(argumentScope => {
783
+ return createDeferredArgument(() => this.toRPN(argumentScope), values, functionAssertive);
784
+ });
785
+ const result = (0, functions_2.callFunction)(token.value, args);
786
+ addToOutput(result);
787
+ }
788
+ else if (argsScope.deferLHSResolution) {
789
+ let args = [];
790
+ if (argsScope.length > 0) {
791
+ const parsed = (0, Settings_1.scopedBlock)('SUBSTITUTE', false, () => {
792
+ return this.parseRPN(argsScope, undefined, assertive);
793
+ });
794
+ args = Collection_1.Collection.isCollection(parsed) ? [...parsed.getElements()] : [parsed];
795
+ }
796
+ // Preserve the scoped values followed by their left-hand target. The operator
797
+ // action is responsible for interpreting that structure.
798
+ args.push(Expression_1.Expression.Variable(token.value));
799
+ addToOutput(new Collection_1.Collection(args));
800
+ }
801
+ else {
802
+ // Parse it as an argument. If the value is of the token is an assertive function then parseRPN needs to
803
+ // know so it can modify it's behavior accordingly. This would be in the case of functions like assume where
804
+ // the operators assert a value rather than compare it.
805
+ const parsed = this.parseRPN(argsScope, values, common_1.ASSERTIVE_FUNCTIONS.includes(token.value));
806
+ const args = Collection_1.Collection.isCollection(parsed) ? parsed.getElements() : [parsed];
807
+ const result = (0, functions_2.callFunction)(token.value, args);
808
+ addToOutput(result);
809
+ }
810
+ }
811
+ else if (token.type === Token_1.Token.PREFIX) {
812
+ const resolved = resolveStackValue(output.pop());
813
+ const e = Expression_1.Expression.fromSymbolicAccess(resolved) ?? resolved;
814
+ const prefixAction = `${operators[token.value].action}Prefix`;
815
+ const fn = functions_2._[prefixAction];
816
+ // TODO: Rethink. What happens when a prefix is applied to a vector
817
+ addToOutput(fn(e));
818
+ }
819
+ else {
820
+ let expression;
821
+ const constant = this.CONSTANTS[token.value];
822
+ // User-defined constants are stored as strings and substitute during ordinary parsing.
823
+ // They take precedence over scoped values, matching the public setConstant method.
824
+ if (typeof constant === 'string') {
825
+ expression = this.parse(constant);
826
+ }
827
+ // Substitute values from the values object. A number is not considered a proper LH value.
828
+ else if (values && token.value in values && !token.is(Token_1.Token.NUMBER)) {
829
+ // expression = this.parse(String(values[token.value]), values);
830
+ const scopedValue = values[token.value];
831
+ if (Expression_1.Expression.isExpression(scopedValue) ||
832
+ Equation_1.Equation.isEquation(scopedValue) ||
833
+ (0, structuredEntityUtils_1.isEnumerable)(scopedValue)) {
834
+ expression = scopedValue.copy();
835
+ }
836
+ else {
837
+ expression = Expression_1.Expression.create(scopedValue, undefined, true);
838
+ }
839
+ }
840
+ else if (token.value in this.KNOWN_VALUES && Settings_1.Settings.SUBSTITUTE) {
841
+ expression = this.KNOWN_VALUES[token.value].copy();
842
+ }
843
+ // Built-in constants are factories so they evaluate at the active precision.
844
+ else if (Settings_1.Settings.EVALUATE && typeof constant === 'function') {
845
+ expression = this.parse(constant(), values);
846
+ }
847
+ else {
848
+ // At this point the expression type is variable or a number
849
+ switch (token.type) {
850
+ case Token_1.Token.FUNCTION:
851
+ expression = Expression_1.Expression.Function(token.value);
852
+ break;
853
+ case Token_1.Token.VARIABLE:
854
+ if (constants_1.INFINITY.includes(token.value)) {
855
+ expression = Expression_1.Expression.Inf();
856
+ }
857
+ else {
858
+ expression = Expression_1.Expression.Variable(token.value);
859
+ }
860
+ break;
861
+ default:
862
+ expression = Expression_1.Expression.Number(token.value);
863
+ break;
864
+ }
865
+ }
866
+ // Add it to output
867
+ addToOutput(expression);
868
+ }
869
+ }
870
+ }
871
+ let retval;
872
+ // Since as function args can have multiple arguments in the output
873
+ // the entire array gets returned. We do the same with
874
+ if (rpn.type === 'square') {
875
+ retval = new Vector_1.Vector(resolveStackValues(output));
876
+ }
877
+ else if (rpn.type === 'curly') {
878
+ // If the output contains KeyValuePairs (from '=>' operators), build a Dictionary.
879
+ // Otherwise build a ValuesSet as before.
880
+ if (output.length > 0 && KeyValuePair_1.KeyValuePair.isKeyValuePair(output[0])) {
881
+ const dict = new Dictionary_1.Dictionary();
882
+ for (const item of output) {
883
+ if (KeyValuePair_1.KeyValuePair.isKeyValuePair(item)) {
884
+ dict.set(item.key, item.value);
885
+ }
886
+ }
887
+ retval = dict;
888
+ }
889
+ else {
890
+ retval = new ValuesSet_1.ValuesSet(resolveStackValues(output));
891
+ }
892
+ }
893
+ else if (output.length > 1) {
894
+ retval = new Collection_1.Collection(resolveStackValues(output));
895
+ }
896
+ else {
897
+ retval = resolveStackValue(output[0]);
898
+ }
899
+ return retval;
900
+ }
901
+ /**
902
+ * Converts a preprocessed TeX token scope to Nerdamer's ordinary parser syntax.
903
+ *
904
+ * @remarks
905
+ * This is a low-level conversion helper used by the TeX converter, not a complete
906
+ * LaTeX parser. The input is the nested scope produced after TeX-specific cleanup
907
+ * and tokenization; despite this method's historical name, callers should not assume
908
+ * that the scope has passed through {@link toRPN}.
909
+ *
910
+ * The converter recognizes the subset of commands handled in this routine, including
911
+ * fractions, integrals, sums, products, limits, square roots, infinity, real/imaginary
912
+ * part notation, and common trigonometric functions. Some command branches consume or
913
+ * modify child scopes while assembling the output, so callers should treat the supplied
914
+ * token tree as conversion input rather than as an immutable value to reuse afterward.
915
+ *
916
+ * Prefer the public TeX conversion API for ordinary LaTeX input.
917
+ *
918
+ * @param rpn - Preprocessed TeX token scope to convert.
919
+ * @returns Equivalent Nerdamer parser text for the supported TeX subset.
920
+ *
921
+ * @example
922
+ * ```ts
923
+ * // Internal conversion examples:
924
+ * // \frac{x}{2} -> "(x)/(2)"
925
+ * // \int x^2 dx -> "int(x^2,x)"
926
+ * ```
927
+ */
928
+ parseTeXRPN(rpn) {
929
+ const commands = ['int', 'int_', 'frac', 'sum_', 'prod_', 'lim_', 'infty', 'mathrm'];
930
+ const functionMap = {
931
+ Re: 'realpart',
932
+ Im: 'imagpart',
933
+ arccos: 'acos',
934
+ arcsin: 'asin',
935
+ arctan: 'atan',
936
+ };
937
+ const output = [];
938
+ const { FUNCTION, VARIABLE } = Token_1.Token;
939
+ // Declare globally since it get increment in multiple loops
940
+ let i;
941
+ const find = (condition) => {
942
+ const retval = new Scope_1.Scope('', -1);
943
+ for (; i < rpn.length; i++) {
944
+ const token = rpn[i];
945
+ retval.push(token);
946
+ if (condition(token)) {
947
+ break;
948
+ }
949
+ }
950
+ return retval;
951
+ };
952
+ const integral = () => {
953
+ // Get the tokens up until the first token that starts with d
954
+ const tokens = find(token => {
955
+ return /^d/.test(token.value);
956
+ });
957
+ // The last items returned should be dx so we can remove that get the variable of integration but the remove the d
958
+ const voi = Token_1.Token.V(tokens.pop().text().substring(1));
959
+ // Process the remainder
960
+ if (tokens.at(-1)?.text() === constants_1.COMMA) {
961
+ tokens.pop();
962
+ }
963
+ return [tokens, voi];
964
+ };
965
+ const getLimits = (limits) => {
966
+ const start = new Scope_1.Scope('', -1);
967
+ const end = new Scope_1.Scope('', -1);
968
+ let target = start;
969
+ for (let j = 0; j < limits.length; j++) {
970
+ const token = limits[j];
971
+ if (token.text() === 'to') {
972
+ target = end;
973
+ continue;
974
+ }
975
+ target.push(token);
976
+ }
977
+ return [start, end];
978
+ };
979
+ // Begin parsing of tokens
980
+ for (i = 0; i < rpn.length; i++) {
981
+ // Grab the token or Scope
982
+ const token = rpn[i];
983
+ if (Scope_1.Scope.isScope(token)) {
984
+ // Read it to an expression and put it to output
985
+ const parsed = this.parseTeXRPN(token);
986
+ output.push(`(${parsed})`);
987
+ }
988
+ else if (token.is(FUNCTION) ||
989
+ (token.is(VARIABLE) && commands.includes(token.value))) {
990
+ // Move forward to skip over the command
991
+ i++;
992
+ // Provide a "cleaned" optional function name
993
+ const f = token.text().split('_')[0];
994
+ const command = token.text();
995
+ switch (token.value) {
996
+ case 'cos':
997
+ case 'sin':
998
+ case 'tan':
999
+ case 'sec':
1000
+ case 'csc':
1001
+ case 'cot':
1002
+ case 'acos':
1003
+ case 'arccos':
1004
+ case 'arcsin':
1005
+ case 'arctan':
1006
+ case 'asec':
1007
+ case 'acsc':
1008
+ case 'acot':
1009
+ case 'sqrt':
1010
+ case 'gcd':
1011
+ case 'Re':
1012
+ case 'Im': {
1013
+ output.push(`${functionMap[command] || command}(${this.parseTeXRPN(rpn[i])})`);
1014
+ break;
1015
+ }
1016
+ case 'sum_':
1017
+ case 'prod_': {
1018
+ const lower = rpn[i];
1019
+ const ios = lower.shift().text();
1020
+ // Remove the equal sign
1021
+ lower.shift();
1022
+ const start = this.parseTeXRPN(lower);
1023
+ // Just forward two spots
1024
+ i += 2;
1025
+ const end = this.parseTeXRPN(rpn[i]);
1026
+ output.push(`${f}(${this.parseTeXRPN(rpn[++i])},${ios},${start},${end})`);
1027
+ break;
1028
+ }
1029
+ case 'lim_': {
1030
+ const [start, end] = getLimits(rpn[i]);
1031
+ output.push(`${f}(${this.parseTeXRPN(rpn[++i])},${start},${end})`);
1032
+ break;
1033
+ }
1034
+ case 'int_': {
1035
+ // Get the tokens up until the first token that starts with d
1036
+ const [tokens, voi] = integral();
1037
+ const a = this.parseTeXRPN(tokens.shift());
1038
+ // discard the caret
1039
+ tokens.shift();
1040
+ const b = this.parseTeXRPN(tokens.shift());
1041
+ // return the output
1042
+ output.push(`defint(${this.parseTeXRPN(tokens)},${a},${b},${voi})`);
1043
+ break;
1044
+ }
1045
+ case 'int': {
1046
+ // Get the tokens up until the first token that starts with d
1047
+ const [tokens, voi] = integral();
1048
+ // return the output
1049
+ output.push(`${f}(${this.parseTeXRPN(tokens)},${voi})`);
1050
+ break;
1051
+ }
1052
+ case 'frac': {
1053
+ const a = this.parseTeXRPN(rpn[i]);
1054
+ const b = this.parseTeXRPN(rpn[++i]);
1055
+ output.push(`(${a})/(${b})`);
1056
+ // Get the next two
1057
+ break;
1058
+ }
1059
+ case 'infty': {
1060
+ output.push(constants_1.INFINITY[0]);
1061
+ break;
1062
+ }
1063
+ case 'mathrm': {
1064
+ output.push(`${this.parseTeXRPN(rpn[i])}(${this.parseTeXRPN(rpn[++i])})`);
1065
+ break;
1066
+ }
1067
+ default: {
1068
+ output.push(`${command}(${this.parseTeXRPN(rpn[i])})`);
1069
+ }
1070
+ }
1071
+ }
1072
+ else {
1073
+ // White space is only used when looking ahead and can be discarded at this point
1074
+ if (!/\s+/.test(token.value)) {
1075
+ output.push(token.value);
1076
+ }
1077
+ }
1078
+ }
1079
+ return output.join('');
1080
+ }
1081
+ /**
1082
+ * Runs a callback while temporarily overriding one global parser setting.
1083
+ *
1084
+ * @remarks
1085
+ * The previous value is restored in a `finally` block, so it is restored even if
1086
+ * the callback throws. The override is process-wide for the duration of the
1087
+ * synchronous callback; it is not an isolated setting attached to this parser
1088
+ * instance. The `setting` parameter remains a string for compatibility, so callers
1089
+ * should supply a valid parser setting name.
1090
+ *
1091
+ * @param setting - Parser setting to override for the callback.
1092
+ * @param value - Temporary boolean value for that setting.
1093
+ * @param callback - Synchronous parser operation to run under the temporary setting.
1094
+ * @returns The parser entity returned by `callback`.
1095
+ *
1096
+ * @example
1097
+ * ```ts
1098
+ * const symbolicPi = Parser.scopedBlock('EVALUATE', false, () => {
1099
+ * return Parser.parse('pi');
1100
+ * });
1101
+ *
1102
+ * symbolicPi.text(); // "pi"
1103
+ * ```
1104
+ */
1105
+ scopedBlock(setting, value, callback) {
1106
+ return (0, Settings_1.scopedBlock)(setting, value, callback);
1107
+ }
1108
+ /**
1109
+ * Changes one or more process-wide parser settings.
1110
+ *
1111
+ * @remarks
1112
+ * Settings persist until changed again and are shared by the exported parser,
1113
+ * parsers returned by {@link create}, and higher-level Nerdamer entry points.
1114
+ * Use {@link scopedBlock} when a setting should apply only for one synchronous
1115
+ * operation.
1116
+ *
1117
+ * This method accepts string keys for compatibility and does not validate arbitrary
1118
+ * setting names at runtime. Callers should use the settings defined by Nerdamer
1119
+ * rather than adding ad hoc properties to the settings object.
1120
+ *
1121
+ * @param setting - Setting name, or an object containing settings to update.
1122
+ * @param value - New value when `setting` is supplied as a single name.
1123
+ * @returns This parser instance for chaining.
1124
+ *
1125
+ * @example
1126
+ * ```ts
1127
+ * Parser.set('EVALUATE', false);
1128
+ * Parser.set({ EVALUATE: false, ALLOW_IMPLICIT_MULTIPLICATION: true });
1129
+ * ```
1130
+ */
1131
+ set(setting, value) {
1132
+ if (typeof setting === 'object') {
1133
+ for (const x in setting) {
1134
+ Settings_1.Settings[x] = setting[x];
1135
+ }
1136
+ }
1137
+ else {
1138
+ Settings_1.Settings[setting] = value;
1139
+ }
1140
+ return this;
1141
+ }
1142
+ /**
1143
+ * Registers, updates, or removes named parser constants.
1144
+ *
1145
+ * @remarks
1146
+ * The constant registry is shared by parser instances, so changes affect subsequent
1147
+ * parsing throughout the process. String-valued constants are reparsed and substituted
1148
+ * whenever their name is encountered, even when evaluation mode is disabled. They also
1149
+ * take precedence over a same-named value supplied in a parse call.
1150
+ *
1151
+ * Function-valued constants are deferred factories. They are invoked when evaluation
1152
+ * mode is enabled; otherwise the constant name remains symbolic. Nerdamer's built-in
1153
+ * `pi` and `e` factories use this mechanism to generate values at the active precision.
1154
+ *
1155
+ * Assign an empty string to an existing constant to remove it. Changing the registry
1156
+ * does not rewrite expressions that have already been parsed.
1157
+ *
1158
+ * @param constants - Constant names mapped to parser text or to factories that return
1159
+ * parser text.
1160
+ *
1161
+ * @example
1162
+ * ```ts
1163
+ * Parser.setConstants({ G: '6.674e-11' });
1164
+ * Parser.parse('G').text(); // "0.00000000006674"
1165
+ *
1166
+ * Parser.setConstants({ G: '' });
1167
+ * Parser.parse('G').text(); // "G"
1168
+ * ```
1169
+ */
1170
+ setConstants(constants) {
1171
+ for (const name in constants) {
1172
+ const value = constants[name];
1173
+ if (typeof value === 'string' && value === '' && name in this.CONSTANTS) {
1174
+ delete this.CONSTANTS[name];
1175
+ }
1176
+ else {
1177
+ this.CONSTANTS[name] = value;
1178
+ }
1179
+ }
1180
+ }
1181
+ /**
1182
+ * Changes the symbol Nerdamer treats as the imaginary unit.
1183
+ *
1184
+ * @remarks
1185
+ * This is a process-wide change. The previous imaginary-unit name is removed from
1186
+ * Nerdamer's restricted-variable list, the new name is reserved, and
1187
+ * {@link Expression.imaginary} is updated. Existing expression trees are not renamed
1188
+ * or reparsed, so changing this setting affects subsequent interpretation rather than
1189
+ * rewriting values that already exist.
1190
+ *
1191
+ * @param variable - Parser variable name to reserve for the imaginary unit.
1192
+ * @returns This parser instance for chaining.
1193
+ *
1194
+ * @example
1195
+ * ```ts
1196
+ * const previous = Parser.getI();
1197
+ * Parser.setI('j');
1198
+ * Parser.parse('3+2*j').text(); // "3+2*j"
1199
+ * Parser.setI(previous);
1200
+ * ```
1201
+ */
1202
+ setI(variable) {
1203
+ const i = Expression_1.Expression.imaginary;
1204
+ // Remove it from the restricted list
1205
+ (0, array_1.remove)(Settings_1.RESTRICTED, i);
1206
+ // Point to the new variable
1207
+ Expression_1.Expression.imaginary = variable;
1208
+ // Add the new variable to the restricted list
1209
+ Settings_1.RESTRICTED.push(variable);
1210
+ return this;
1211
+ }
1212
+ /**
1213
+ * Alias for {@link setI}.
1214
+ *
1215
+ * @param variable - Parser variable name to reserve for the imaginary unit.
1216
+ * @returns This parser instance for chaining.
1217
+ */
1218
+ setImaginary(variable) {
1219
+ return this.setI(variable);
1220
+ }
1221
+ /**
1222
+ * Registers or updates an operator in the shared parser registry. Legacy `prefix`
1223
+ * and `postfix` fields are accepted alongside the current metadata names.
1224
+ */
1225
+ setOperator(definition, action) {
1226
+ const { prefix, postfix, ...metadata } = definition;
1227
+ const operator = {
1228
+ ...metadata,
1229
+ isPostfix: metadata.isPostfix ?? postfix ?? false,
1230
+ isPrefix: metadata.isPrefix ?? prefix ?? false,
1231
+ iterates: metadata.iterates ?? true,
1232
+ };
1233
+ if (action) {
1234
+ functions_2._[operator.action] = action;
1235
+ }
1236
+ else if (!functions_2._[operator.action]) {
1237
+ throw new errors_1.ParserError(`Unknown operator action ${operator.action}`);
1238
+ }
1239
+ this.operators[operator.operator] = operator;
1240
+ (0, Token_1.refreshOperatorSymbols)();
1241
+ return this;
1242
+ }
1243
+ /**
1244
+ * Sets the shared precision used by Decimal-backed numerical calculations.
1245
+ *
1246
+ * @remarks
1247
+ * This delegates to {@link Rational.set}. It updates the global `decimal.js`
1248
+ * precision and recomputes Nerdamer's finite-precision `pi` and `e` rational
1249
+ * constants. Exact numerator/denominator arithmetic remains exact and does not
1250
+ * become approximate merely because this setting changes.
1251
+ *
1252
+ * The setting is process-wide and affects parsers returned by {@link create} as
1253
+ * well as the exported {@link Parser} singleton.
1254
+ *
1255
+ * @param precision - Number of significant digits for Decimal-backed work.
1256
+ *
1257
+ * @example
1258
+ * ```ts
1259
+ * const previous = Parser.getPrecision();
1260
+ * Parser.setPrecision(50);
1261
+ * Parser.getPrecision(); // 50
1262
+ * Parser.setPrecision(previous);
1263
+ * ```
1264
+ */
1265
+ setPrecision(precision) {
1266
+ Rational_1.Rational.set({ precision: precision });
1267
+ }
1268
+ /**
1269
+ * Tokenizes parser text into a nested scope tree.
1270
+ *
1271
+ * @remarks
1272
+ * The tokenizer recognizes numbers, variables, functions, keyword and symbolic
1273
+ * operators, and round/square/curly bracket scopes. Scientific notation is normalized
1274
+ * to decimal token text and configured aliases such as `π` and `∞` are normalized to
1275
+ * their parser names.
1276
+ *
1277
+ * When implicit multiplication is enabled, the normal mode inserts synthetic `*`
1278
+ * tokens where adjacency implies multiplication. Square-bracket adjacency is deferred:
1279
+ * the child scope is marked so {@link parseRPN} can decide at evaluation time whether
1280
+ * the construct is indexing or multiplication after the left operand's type is known.
1281
+ * Unknown variable-parenthesis adjacency keeps its synthetic multiplication token but
1282
+ * is also marked so {@link toRPN} can reinterpret it as a function call if the name is
1283
+ * registered before that scope is converted. `options.pure` suppresses inserted
1284
+ * multiplication tokens and is used by conversion code that needs the original token
1285
+ * relationships. `options.keepWhiteSpace` retains whitespace tokens instead of
1286
+ * discarding them.
1287
+ *
1288
+ * This is a low-level parser-pipeline API. Ordinary callers should normally use
1289
+ * {@link parse} instead of depending on token representation details.
1290
+ *
1291
+ * @param inputStr - Parser text to tokenize.
1292
+ * @param options - Tokenization options. Set `pure` to suppress synthetic
1293
+ * implicit-multiplication tokens, or `keepWhiteSpace` to retain whitespace tokens.
1294
+ * @returns The root `Scope` containing the nested token tree.
1295
+ * @throws {@link UnexpectedTokenError} When brackets are mismatched or token adjacency is invalid.
1296
+ *
1297
+ * @example
1298
+ * ```ts
1299
+ * const tokens = Parser.tokenize('2*x + 1');
1300
+ * tokens.text(); // "2 * x + 1"
1301
+ * ```
1302
+ */
1303
+ tokenize(inputStr, options) {
1304
+ // options = Object.assign({ pure: false, keepWhiteSpace: false }, options);
1305
+ options = { ...{ pure: false, keepWhiteSpace: false }, ...options };
1306
+ // A key element to be aware of is that a terminating character is appended to the string.
1307
+ // This avoids an extra step since the last item is ignored. Brackets are wrapped in a Scope object
1308
+ // which preserves the type for later parsing.
1309
+ const terminator = '\u0000';
1310
+ // Input must be a string and append a terminator. This avoids having to iterate over undefined will be ignored at the end.
1311
+ let str = String(inputStr) + terminator;
1312
+ // Remove whitespace if allows
1313
+ if (!options.keepWhiteSpace) {
1314
+ str = str.replace(/\s+/g, ' ').trim();
1315
+ }
1316
+ // The list of operators supported at the time of tokenization
1317
+ const operators = Object.keys(this.operators).sort();
1318
+ const deferredLHSOperators = operators
1319
+ .filter(operator => this.operators[operator].deferLHSResolution)
1320
+ .sort((a, b) => b.length - a.length);
1321
+ // The column position that the token was found
1322
+ let col = 0;
1323
+ // The tokens container. A Scope object is used to denote tokens between brackets. This greatly simplifies
1324
+ // parsing in the future. This allows the tracking of the bracket type and enables parsing to be limited to one scope.
1325
+ const tokens = new Scope_1.Scope('top', 0);
1326
+ // The scope being appended to
1327
+ let scope = tokens;
1328
+ // The object to hold portions of the string between tokens. We create an object so we can pass it around by reference.
1329
+ const tokenBuffer = {
1330
+ tokenType: Token_1.Token.UNDEFINED,
1331
+ chars: [],
1332
+ inputString: str,
1333
+ last: function () {
1334
+ return this.chars[this.chars.length - 1];
1335
+ },
1336
+ next: function (at) {
1337
+ return this.inputString.charAt(at + 1);
1338
+ },
1339
+ is(tokenType) {
1340
+ return this.tokenType === tokenType;
1341
+ },
1342
+ prev: function (at) {
1343
+ return this.inputString.charAt(at - 1);
1344
+ },
1345
+ scientificNumber: false,
1346
+ };
1347
+ // Traverse the string and look at each character
1348
+ for (; col < str.length; col++) {
1349
+ const ch = str.charAt(col);
1350
+ // Get the character type for comparison. Search for changes in the character type to determine
1351
+ // the end of a token.
1352
+ const charType = Token_1.Token.getCharType(ch, col, tokenBuffer);
1353
+ // Brackets point to a new scope. Once one is encountered, update the target and point to the new scope
1354
+ // Subsequent tokens are now pushed to that scope
1355
+ if (tokenBuffer.is(Token_1.Token.BRACKET)) {
1356
+ // Get the bracket.
1357
+ const bracketCharacter = tokenBuffer.last();
1358
+ if (bracketCharacter === undefined) {
1359
+ throw new errors_1.ParserError('Bracket token is missing its character.');
1360
+ }
1361
+ const bracket = common_1.brackets[bracketCharacter];
1362
+ if (bracket.isOpen) {
1363
+ // Check to see if there's a variable on the token stack, if it's a variable, and if it's in functions
1364
+ const lastToken = scope[scope.length - 1];
1365
+ if (lastToken &&
1366
+ lastToken.type === Token_1.Token.VARIABLE &&
1367
+ (lastToken.value in dispatch_1.mathFunctionRegistry || lastToken.value === constants_1.SYMBOLIC_ACCESSOR)) {
1368
+ // Mark it as a function
1369
+ lastToken.type = Token_1.Token.FUNCTION;
1370
+ }
1371
+ // Implicit multiplication
1372
+ // Track whether to mark the new scope for deferred implicit multiplication
1373
+ let deferImplicitMultiply = false;
1374
+ if (lastToken &&
1375
+ !(lastToken.type === Token_1.Token.FUNCTION || lastToken.type === Token_1.Token.OPERATOR)) {
1376
+ if (bracket.type === 'square') {
1377
+ // Don't insert implicit multiplication before a square bracket.
1378
+ // Mark it so parseRPN can decide whether it's indexing or multiplication
1379
+ // once resolved types are known.
1380
+ deferImplicitMultiply = true;
1381
+ }
1382
+ else if (Settings_1.Settings.ALLOW_IMPLICIT_MULTIPLICATION) {
1383
+ if (bracket.type === 'parenthesis' && lastToken.type === Token_1.Token.VARIABLE) {
1384
+ // Preserve the multiplication fallback and its precedence, but remember that
1385
+ // this name may become a registered function before deferred evaluation.
1386
+ deferImplicitMultiply = true;
1387
+ }
1388
+ // Push a multiplication token with position -1 to indicate that it was added
1389
+ if (!options.pure) {
1390
+ scope.push(new Token_1.Token('*', Token_1.Token.OPERATOR, -1));
1391
+ }
1392
+ }
1393
+ else {
1394
+ throw new errors_1.UnexpectedTokenError(`Expected operator or function name but "${lastToken.value}" found!`);
1395
+ }
1396
+ }
1397
+ // Go down in scope to the new bracket
1398
+ scope = scope.addScope(bracket.type, col);
1399
+ if (deferImplicitMultiply) {
1400
+ scope.implicitMultiply = true;
1401
+ }
1402
+ }
1403
+ else {
1404
+ // Ensure that the brackets match
1405
+ const bracketsMatch = scope.type === bracket.type;
1406
+ if (bracketsMatch && bracket.type === 'parenthesis' && scope.parent) {
1407
+ const remainingInput = str.slice(col).trimStart();
1408
+ const deferredOperator = deferredLHSOperators.find(operator => {
1409
+ return remainingInput.startsWith(operator);
1410
+ });
1411
+ if (deferredOperator) {
1412
+ const parentScope = scope.parent;
1413
+ let lhsIndex = parentScope.length - 2;
1414
+ let lhsToken = parentScope[lhsIndex];
1415
+ // Unknown function notation normally inserts an implicit multiplication
1416
+ // before the parenthesis. The following operator has asked us not to
1417
+ // resolve that ambiguity yet, so remove only that synthetic operator.
1418
+ if (Token_1.Token.isToken(lhsToken) &&
1419
+ lhsToken.type === Token_1.Token.OPERATOR &&
1420
+ lhsToken.value === '*' &&
1421
+ lhsToken.position === -1) {
1422
+ parentScope.splice(lhsIndex, 1);
1423
+ lhsToken = parentScope[--lhsIndex];
1424
+ }
1425
+ if (Token_1.Token.isToken(lhsToken) &&
1426
+ (lhsToken.type === Token_1.Token.VARIABLE || lhsToken.type === Token_1.Token.FUNCTION)) {
1427
+ lhsToken.type = Token_1.Token.FUNCTION;
1428
+ scope.deferLHSResolution = true;
1429
+ }
1430
+ }
1431
+ }
1432
+ // We're done so we can go up in scope to the parent scope
1433
+ scope = scope.upperScope();
1434
+ // If there's not scope or the types don't match then we have a mismatched bracket
1435
+ if (scope === undefined || !bracketsMatch) {
1436
+ throw new errors_1.UnexpectedTokenError(`Missing opening bracket for "${tokenBuffer.last()}":${col}`);
1437
+ }
1438
+ }
1439
+ // Discard the bracket since it's no longer needed
1440
+ tokenBuffer.chars.pop();
1441
+ }
1442
+ // Handle single variables and brackets since we don't have compound brackets nor will we support them.
1443
+ // If the type has changed but doesn't equal UNDEFINED, then finalize it
1444
+ if (tokenBuffer.tokenType !== Token_1.Token.UNDEFINED && tokenBuffer.tokenType !== charType) {
1445
+ // Collapse the tokenBuffer
1446
+ let tokenStr = tokenBuffer.chars.join(constants_1.BLANK);
1447
+ // If there's an alias then use that instead
1448
+ const alias = constants_1.ALIASES[tokenStr];
1449
+ if (alias) {
1450
+ tokenStr = alias;
1451
+ }
1452
+ // Mark the beginning of the token
1453
+ const SOT = col - tokenBuffer.chars.length;
1454
+ // In order to support keyword operators, we can check here
1455
+ if (operators.includes(tokenStr) && tokenBuffer.tokenType !== Token_1.Token.PREFIX) {
1456
+ tokenBuffer.tokenType = Token_1.Token.OPERATOR;
1457
+ }
1458
+ // type prefix operators and compound operators
1459
+ else if (tokenBuffer.is(Token_1.Token.OPERATOR) && tokenBuffer.chars.length > 1) {
1460
+ let compoundOperatorStr = tokenBuffer.chars[0];
1461
+ // Get the largest possible operator chunks
1462
+ for (let i = 1; i < tokenBuffer.chars.length + 1; i++) {
1463
+ // Check if the current combination is an operator
1464
+ const tempStr = compoundOperatorStr + tokenBuffer.chars[i];
1465
+ // If it is then just make that the chunk
1466
+ if (tempStr in this.operators) {
1467
+ compoundOperatorStr = tempStr;
1468
+ }
1469
+ else {
1470
+ // Place it on the tokens stack
1471
+ scope.push(new Token_1.Token(compoundOperatorStr, Token_1.Token.OPERATOR, SOT + i - compoundOperatorStr.length));
1472
+ // Reset the compound operator and move forward one position
1473
+ compoundOperatorStr = tokenBuffer.chars[i];
1474
+ // Clear the token string
1475
+ tokenStr = constants_1.BLANK;
1476
+ }
1477
+ }
1478
+ // Reset the buffer
1479
+ tokenBuffer.chars = [];
1480
+ tokenBuffer.scientificNumber = false;
1481
+ }
1482
+ // Ignore blanks and spaces
1483
+ // Don't add the token if it's been cleared
1484
+ const isWhiteSpace = tokenStr === constants_1.SPACE;
1485
+ if (isWhiteSpace && options.keepWhiteSpace) {
1486
+ scope.push(new Token_1.Token(tokenStr, Token_1.Token.SPACE, SOT));
1487
+ }
1488
+ else if (!(tokenStr === constants_1.BLANK || isWhiteSpace)) {
1489
+ if (tokenBuffer.scientificNumber) {
1490
+ tokenStr = (0, string_1.scientificToDecimal)(tokenStr);
1491
+ tokenBuffer.scientificNumber = false;
1492
+ }
1493
+ const lastToken = scope[scope.length - 1];
1494
+ const lastOperator = Token_1.Token.isToken(lastToken) && lastToken.type === Token_1.Token.OPERATOR
1495
+ ? this.operators[lastToken.value]
1496
+ : undefined;
1497
+ // Implicit multiplication
1498
+ if (lastToken &&
1499
+ ((tokenBuffer.is(Token_1.Token.VARIABLE) &&
1500
+ !(lastToken.type === Token_1.Token.OPERATOR || lastToken.type === Token_1.Token.PREFIX)) ||
1501
+ ((tokenBuffer.is(Token_1.Token.VARIABLE) || tokenBuffer.is(Token_1.Token.NUMBER)) &&
1502
+ lastOperator?.isPostfix))) {
1503
+ if (Settings_1.Settings.ALLOW_IMPLICIT_MULTIPLICATION) {
1504
+ if (!options.pure) {
1505
+ // Push a multiplication token with position -1 to indicate that it was added
1506
+ scope.push(new Token_1.Token('*', Token_1.Token.OPERATOR, -1));
1507
+ }
1508
+ }
1509
+ else {
1510
+ throw new errors_1.UnexpectedTokenError(`Expected operator or function name but "${lastToken.value}" found!`);
1511
+ }
1512
+ }
1513
+ // Allows for each single letter in a variable to be treated like an individual variable
1514
+ if (Settings_1.Settings.USE_SINGLE_LETTER_VARIABLES && !(tokenStr in dispatch_1.mathFunctionRegistry)) {
1515
+ // Insert a new multiplication token between them and add them to the scope
1516
+ scope.push(...tokenStr
1517
+ .split('')
1518
+ .map(x => {
1519
+ return new Token_1.Token(x, tokenBuffer.tokenType, -1);
1520
+ // Add the multiplication operator between them
1521
+ })
1522
+ .flatMap(x => [new Token_1.Token('*', Token_1.Token.OPERATOR, -1), x])
1523
+ .slice(1));
1524
+ }
1525
+ else {
1526
+ // We can collapse the buffer
1527
+ scope.push(new Token_1.Token(tokenStr, tokenBuffer.tokenType, SOT));
1528
+ }
1529
+ }
1530
+ // Clear the buffer and place last character on stack
1531
+ tokenBuffer.chars = [ch];
1532
+ }
1533
+ else {
1534
+ tokenBuffer.chars.push(ch);
1535
+ }
1536
+ tokenBuffer.tokenType = charType;
1537
+ }
1538
+ // If the last item in the tokens array is a scope and it has a parent then it's missing a closing bracket
1539
+ const lastItem = tokens[tokens.length - 1];
1540
+ if (Scope_1.Scope.isScope(lastItem) && lastItem.isOpen) {
1541
+ throw new errors_1.UnexpectedTokenError(`Missing closing bracket for "${str.charAt(lastItem.column - 1)}":${lastItem.column}`);
1542
+ }
1543
+ return tokens;
1544
+ }
1545
+ /**
1546
+ * Converts a tokenized scope to Nerdamer's nested Reverse Polish Notation form.
1547
+ *
1548
+ * @remarks
1549
+ * The conversion uses the Shunting Yard algorithm to apply precedence,
1550
+ * associativity, prefix/postfix rules, and bracket scoping. Nested scopes remain
1551
+ * nested; each child scope is recursively converted rather than flattening the
1552
+ * entire expression into one token array. Deferred adjacency markers are retained
1553
+ * on child scopes so square-bracket indexing can be decided by {@link parseRPN} and
1554
+ * newly registered function names can be recognized before parenthesis adjacency is
1555
+ * committed to implicit multiplication.
1556
+ *
1557
+ * The returned `Scope` containers are new, but token objects are reused. Prefix
1558
+ * detection can retag those shared `Token` objects while converting the input, so
1559
+ * callers should not treat the original token tree as an immutable snapshot after
1560
+ * this method has run.
1561
+ *
1562
+ * @param scope - Tokenized scope, normally produced by {@link tokenize}.
1563
+ * @returns A new scope hierarchy containing the tokens in evaluation order.
1564
+ * @throws {@link ParserError} When an invalid operator sequence requires a prefix operator.
1565
+ *
1566
+ * @example
1567
+ * ```ts
1568
+ * const tokens = Parser.tokenize('2 + 3 * x');
1569
+ * const rpn = Parser.toRPN(tokens);
1570
+ *
1571
+ * rpn.text(); // "2 3 x * +"
1572
+ * ```
1573
+ */
1574
+ toRPN(scope) {
1575
+ // The output to be returned
1576
+ const output = new Scope_1.Scope(scope.type, scope.depth);
1577
+ // The operator stack
1578
+ const stack = [];
1579
+ /**
1580
+ * Return the last bracket on the stack. Must be guarded against an empty stack.
1581
+ * @returns Bracket
1582
+ */
1583
+ const peek = () => {
1584
+ const lastToken = stack[stack.length - 1];
1585
+ return lastToken;
1586
+ };
1587
+ const peekOperator = () => {
1588
+ const operatorToken = peek();
1589
+ if (operatorToken) {
1590
+ return operatorToken.resolvedOperator ?? this.operators[operatorToken.value];
1591
+ }
1592
+ };
1593
+ // Begin Shunting Yard
1594
+ for (let i = 0; i < scope.length; i++) {
1595
+ // Read the token
1596
+ let token = scope[i];
1597
+ if (Scope_1.Scope.isScope(token)) {
1598
+ const previousToken = output[output.length - 1];
1599
+ const preserveArguments = Token_1.Token.isToken(previousToken) &&
1600
+ previousToken.type === Token_1.Token.FUNCTION &&
1601
+ dispatch_1.mathFunctionRegistry[previousToken.value]?.deferArguments;
1602
+ if (preserveArguments) {
1603
+ // Keep deferred function arguments tokenized so the registered function can
1604
+ // control when and how often they are evaluated.
1605
+ output.push(token);
1606
+ }
1607
+ else {
1608
+ // Send the contents of the scope to be put into RPN
1609
+ const rpnScope = this.toRPN(token);
1610
+ // Preserve parser decisions made while tokenizing the original scope.
1611
+ rpnScope.implicitMultiply = token.implicitMultiply;
1612
+ rpnScope.deferLHSResolution = token.deferLHSResolution;
1613
+ rpnScope.deferRHSResolution = token.deferRHSResolution;
1614
+ output.push(rpnScope);
1615
+ }
1616
+ }
1617
+ else {
1618
+ const implicitOperator = scope[i + 1];
1619
+ const implicitScope = scope[i + 2];
1620
+ if (token.type === Token_1.Token.VARIABLE &&
1621
+ Token_1.Token.isToken(implicitOperator) &&
1622
+ implicitOperator.type === Token_1.Token.OPERATOR &&
1623
+ implicitOperator.value === '*' &&
1624
+ implicitOperator.position === -1 &&
1625
+ Scope_1.Scope.isScope(implicitScope) &&
1626
+ implicitScope.type === 'parenthesis' &&
1627
+ implicitScope.implicitMultiply &&
1628
+ token.value in dispatch_1.mathFunctionRegistry) {
1629
+ // The source was tokenized before this name was registered. Reclassify only
1630
+ // for this conversion so reusable deferred token scopes remain unchanged.
1631
+ token = new Token_1.Token(token.value, Token_1.Token.FUNCTION, token.position);
1632
+ i++;
1633
+ }
1634
+ // If it's a number then there's nothing else to evaluate so it can go straight to output
1635
+ switch (token.type) {
1636
+ case Token_1.Token.OPERATOR: {
1637
+ // Get the operator
1638
+ let operator = token.resolvedOperator ?? this.operators[token.value];
1639
+ // Some operators can be either infix or postfix. Resolve that role from
1640
+ // syntax before prefix detection so the rest of Shunting Yard only sees
1641
+ // one unambiguous operator definition.
1642
+ if (operator.postfixVariant) {
1643
+ let nextIndex = i + 1;
1644
+ let nextToken = scope[nextIndex];
1645
+ while (Token_1.Token.isToken(nextToken) &&
1646
+ (nextToken.type === Token_1.Token.SPACE ||
1647
+ nextToken.type === Token_1.Token.UNDEFINED)) {
1648
+ nextToken = scope[++nextIndex];
1649
+ }
1650
+ if (nextToken === undefined ||
1651
+ (Token_1.Token.isToken(nextToken) &&
1652
+ (nextToken.type === Token_1.Token.OPERATOR ||
1653
+ nextToken.type === Token_1.Token.PREFIX))) {
1654
+ operator = operator.postfixVariant;
1655
+ token.resolvedOperator = operator;
1656
+ }
1657
+ }
1658
+ // Get the last operator on the stack
1659
+ let lastOperator = peekOperator();
1660
+ /******************** PREFIX OPERATORS ********************/
1661
+ // We can now traverse the tokens since we've encountered an operator. Any remaining operators should be prefix operators
1662
+ // If not then complain.
1663
+ for (let j = i + 1; j < scope.length; j++) {
1664
+ const prefixOperator = scope[j];
1665
+ // If a non-operator token is encountered, then we're done. Or if we're at a postfix operator then the next operator may be a valid one and this assumption no longer holds true
1666
+ if (!prefixOperator ||
1667
+ prefixOperator.type !== Token_1.Token.OPERATOR ||
1668
+ operator.isPostfix) {
1669
+ break;
1670
+ }
1671
+ // If the encountered operator is not a prefix then complain
1672
+ if (!this.operators[prefixOperator.value].isPrefix) {
1673
+ throw new errors_1.ParserError(`Prefix operator expected but ${prefixOperator.value} encountered.`);
1674
+ }
1675
+ // We mark it so the next time it's encountered, it's placed to output immediately
1676
+ prefixOperator.type = Token_1.Token.PREFIX;
1677
+ }
1678
+ // If we're at the beginning then it's a prefix operator so put it to output and break
1679
+ // If the last item on output is a prefix then this has to be a prefix
1680
+ const last = output[output.length - 1];
1681
+ if (operator.isPrefix &&
1682
+ (output.length === 0 ||
1683
+ (output.length > 0 && last && last.type === Token_1.Token.PREFIX))) {
1684
+ token.type = Token_1.Token.PREFIX;
1685
+ stack.push(token);
1686
+ break;
1687
+ }
1688
+ const greaterPrecedence = () => {
1689
+ let retval = false;
1690
+ if (lastOperator) {
1691
+ retval = shouldPopOperator(lastOperator, operator);
1692
+ // Prefixes have to happen first. For instance x^-1. The minus comes first but not for -x!.
1693
+ const lastOnStack = stack[stack.length - 1];
1694
+ if (!retval &&
1695
+ lastOnStack &&
1696
+ lastOnStack.type === Token_1.Token.PREFIX &&
1697
+ operator.leftAssoc) {
1698
+ retval = true;
1699
+ }
1700
+ }
1701
+ return retval;
1702
+ };
1703
+ while (greaterPrecedence()) {
1704
+ // Move it to the output
1705
+ output.push(stack.pop());
1706
+ // Get the next operator on the stack
1707
+ lastOperator = peekOperator();
1708
+ }
1709
+ if (operator.deferLHSResolution) {
1710
+ const lhs = output[output.length - 1];
1711
+ if (Token_1.Token.isToken(lhs) && lhs.type === Token_1.Token.VARIABLE) {
1712
+ // Preserve a plain assignment target as its own scope so it can bypass
1713
+ // known-value substitution without changing evaluation of the right side.
1714
+ const deferredLHS = new Scope_1.Scope('', lhs.position);
1715
+ deferredLHS.deferLHSResolution = true;
1716
+ deferredLHS.push(output.pop());
1717
+ output.push(deferredLHS);
1718
+ }
1719
+ }
1720
+ if (operator.deferRHSResolution) {
1721
+ const deferredRHS = new Scope_1.Scope('', token.position);
1722
+ let rhsEnd = i + 1;
1723
+ for (; rhsEnd < scope.length; rhsEnd++) {
1724
+ const rhsToken = scope[rhsEnd];
1725
+ if (Token_1.Token.isToken(rhsToken) && rhsToken.type === Token_1.Token.OPERATOR) {
1726
+ const rhsOperator = rhsToken.resolvedOperator ?? this.operators[rhsToken.value];
1727
+ if (rhsOperator &&
1728
+ !shouldPopOperator(rhsOperator, operator)) {
1729
+ break;
1730
+ }
1731
+ }
1732
+ deferredRHS.push(rhsToken);
1733
+ }
1734
+ if (deferredRHS.length === 0) {
1735
+ throw new errors_1.ParserError((0, errors_1.message)('malformedExpression'));
1736
+ }
1737
+ const deferredRPN = this.toRPN(deferredRHS);
1738
+ deferredRPN.deferRHSResolution = true;
1739
+ output.push(deferredRPN);
1740
+ i = rhsEnd - 1;
1741
+ }
1742
+ // Put the last operator on the stack
1743
+ stack.push(token);
1744
+ break;
1745
+ }
1746
+ case Token_1.Token.BRACKET: {
1747
+ // Add brackets to output right away since they have the highest precedence
1748
+ const bracket = common_1.brackets[String(token.value)];
1749
+ if (bracket.isOpen) {
1750
+ stack.push(token);
1751
+ }
1752
+ else {
1753
+ output.push(token);
1754
+ const lastToken = peek();
1755
+ while (lastToken) {
1756
+ const popped = stack.pop();
1757
+ output.push(popped);
1758
+ // Exit if it's a matching bracket
1759
+ // The resulting RPN will be a reverse bracket. Nonetheless, it's a good way to know that every operation is
1760
+ // within a new scope.
1761
+ if (popped.type === Token_1.Token.BRACKET) {
1762
+ if (common_1.brackets[token.value].matches === popped.value) {
1763
+ // Done
1764
+ break;
1765
+ }
1766
+ }
1767
+ }
1768
+ }
1769
+ break;
1770
+ }
1771
+ case Token_1.Token.PREFIX: {
1772
+ // The prefix belongs to the next token so it goes to output right away
1773
+ stack.push(token);
1774
+ break;
1775
+ }
1776
+ case Token_1.Token.UNDEFINED:
1777
+ case Token_1.Token.SPACE: {
1778
+ // Do nothing and discard
1779
+ break;
1780
+ }
1781
+ default: {
1782
+ output.push(token);
1783
+ break;
1784
+ }
1785
+ }
1786
+ }
1787
+ }
1788
+ // Clear the stack
1789
+ while (stack.length) {
1790
+ const token = stack.pop();
1791
+ output.push(token);
1792
+ }
1793
+ return output;
1794
+ }
1795
+ }
1796
+ /**
1797
+ * The shared parser instance used by Nerdamer's ordinary parsing APIs.
1798
+ *
1799
+ * @remarks
1800
+ * {@link Parser.parse} is the primary notation-to-object entry point. State-changing
1801
+ * methods on this object can affect the rest of Nerdamer because settings, constants,
1802
+ * numeric precision, operator metadata, and the imaginary-unit symbol are process-wide.
1803
+ * Use scoped setting changes where possible, and do not assume that {@link Parser.create}
1804
+ * provides an isolated parser environment.
1805
+ *
1806
+ * @example
1807
+ * ```ts
1808
+ * Parser.parse('x^2 + 1').text(); // "1+x^2"
1809
+ * Parser.evaluate('2^10').text(); // "1024"
1810
+ * ```
1811
+ */
1812
+ exports.Parser = new ExpressionParser();