1 | import { factory } from '../utils/factory'
|
2 | import { extend, hasOwnProperty } from '../utils/object'
|
3 | import { getSafeProperty, setSafeProperty } from '../utils/customs'
|
4 | import { warnOnce } from '../utils/log'
|
5 |
|
6 | const name = 'Parser'
|
7 | const dependencies = ['parse']
|
8 |
|
9 | export const createParserClass = /* #__PURE__ */ factory(name, dependencies, ({ parse }) => {
|
10 | /**
|
11 | * @constructor Parser
|
12 | * Parser contains methods to evaluate or parse expressions, and has a number
|
13 | * of convenience methods to get, set, and remove variables from memory. Parser
|
14 | * keeps a scope containing variables in memory, which is used for all
|
15 | * evaluations.
|
16 | *
|
17 | * Methods:
|
18 | * const result = parser.evaluate(expr) // evaluate an expression
|
19 | * const value = parser.get(name) // retrieve a variable from the parser
|
20 | * const values = parser.getAll() // retrieve all defined variables
|
21 | * parser.set(name, value) // set a variable in the parser
|
22 | * parser.remove(name) // clear a variable from the
|
23 | * // parsers scope
|
24 | * parser.clear() // clear the parsers scope
|
25 | *
|
26 | * Example usage:
|
27 | * const parser = new Parser()
|
28 | * // Note: there is a convenience method which can be used instead:
|
29 | * // const parser = new math.parser()
|
30 | *
|
31 | * // evaluate expressions
|
32 | * parser.evaluate('sqrt(3^2 + 4^2)') // 5
|
33 | * parser.evaluate('sqrt(-4)') // 2i
|
34 | * parser.evaluate('2 inch in cm') // 5.08 cm
|
35 | * parser.evaluate('cos(45 deg)') // 0.7071067811865476
|
36 | *
|
37 | * // define variables and functions
|
38 | * parser.evaluate('x = 7 / 2') // 3.5
|
39 | * parser.evaluate('x + 3') // 6.5
|
40 | * parser.evaluate('function f(x, y) = x^y') // f(x, y)
|
41 | * parser.evaluate('f(2, 3)') // 8
|
42 | *
|
43 | * // get and set variables and functions
|
44 | * const x = parser.get('x') // 7
|
45 | * const f = parser.get('f') // function
|
46 | * const g = f(3, 2) // 9
|
47 | * parser.set('h', 500)
|
48 | * const i = parser.evaluate('h / 2') // 250
|
49 | * parser.set('hello', function (name) {
|
50 | * return 'hello, ' + name + '!'
|
51 | * })
|
52 | * parser.evaluate('hello("user")') // "hello, user!"
|
53 | *
|
54 | * // clear defined functions and variables
|
55 | * parser.clear()
|
56 | *
|
57 | */
|
58 | function Parser () {
|
59 | if (!(this instanceof Parser)) {
|
60 | throw new SyntaxError(
|
61 | 'Constructor must be called with the new operator')
|
62 | }
|
63 | this.scope = {}
|
64 | }
|
65 |
|
66 | /**
|
67 | * Attach type information
|
68 | */
|
69 | Parser.prototype.type = 'Parser'
|
70 | Parser.prototype.isParser = true
|
71 |
|
72 | /**
|
73 | * Parse an expression and return the parsed function node.
|
74 | * The node tree can be compiled via `code = node.compile(math)`,
|
75 | * and the compiled code can be executed as `code.evaluate([scope])`
|
76 | * @param {string} expr
|
77 | * @return {Node} node
|
78 | * @throws {Error}
|
79 | */
|
80 | Parser.prototype.parse = function (expr) {
|
81 | throw new Error('Parser.parse is deprecated. Use math.parse instead.')
|
82 | }
|
83 |
|
84 | /**
|
85 | * Parse and compile an expression, return the compiled javascript code.
|
86 | * The node can be evaluated via code.evaluate([scope])
|
87 | * @param {string} expr
|
88 | * @return {{evaluate: function}} code
|
89 | * @throws {Error}
|
90 | */
|
91 | Parser.prototype.compile = function (expr) {
|
92 | throw new Error('Parser.compile is deprecated. Use math.compile instead.')
|
93 | }
|
94 |
|
95 | /**
|
96 | * Parse and evaluate the given expression
|
97 | * @param {string} expr A string containing an expression, for example "2+3"
|
98 | * @return {*} result The result, or undefined when the expression was empty
|
99 | * @throws {Error}
|
100 | */
|
101 | Parser.prototype.evaluate = function (expr) {
|
102 | // TODO: validate arguments
|
103 | return parse(expr)
|
104 | .compile()
|
105 | .evaluate(this.scope)
|
106 | }
|
107 |
|
108 | /**
|
109 | * Parse and evaluate the given expression
|
110 | * @param {string} expr A string containing an expression, for example "2+3"
|
111 | * @return {*} result The result, or undefined when the expression was empty
|
112 | * @throws {Error}
|
113 | */
|
114 | // TODO: Deprecated since v6.0.0. Clean up some day
|
115 | Parser.prototype.eval = function (expr) {
|
116 | warnOnce('Method Parser.eval is renamed to Parser.evaluate. Please use the new method name.')
|
117 |
|
118 | return this.evaluate(expr)
|
119 | }
|
120 |
|
121 | /**
|
122 | * Get a variable (a function or variable) by name from the parsers scope.
|
123 | * Returns undefined when not found
|
124 | * @param {string} name
|
125 | * @return {* | undefined} value
|
126 | */
|
127 | Parser.prototype.get = function (name) {
|
128 | // TODO: validate arguments
|
129 | return name in this.scope
|
130 | ? getSafeProperty(this.scope, name)
|
131 | : undefined
|
132 | }
|
133 |
|
134 | /**
|
135 | * Get a map with all defined variables
|
136 | * @return {Object} values
|
137 | */
|
138 | Parser.prototype.getAll = function () {
|
139 | return extend({}, this.scope)
|
140 | }
|
141 |
|
142 | /**
|
143 | * Set a symbol (a function or variable) by name from the parsers scope.
|
144 | * @param {string} name
|
145 | * @param {* | undefined} value
|
146 | */
|
147 | Parser.prototype.set = function (name, value) {
|
148 | // TODO: validate arguments
|
149 | return setSafeProperty(this.scope, name, value)
|
150 | }
|
151 |
|
152 | /**
|
153 | * Remove a variable from the parsers scope
|
154 | * @param {string} name
|
155 | */
|
156 | Parser.prototype.remove = function (name) {
|
157 | // TODO: validate arguments
|
158 | delete this.scope[name]
|
159 | }
|
160 |
|
161 | /**
|
162 | * Clear the scope with variables and functions
|
163 | */
|
164 | Parser.prototype.clear = function () {
|
165 | for (const name in this.scope) {
|
166 | if (hasOwnProperty(this.scope, name)) {
|
167 | delete this.scope[name]
|
168 | }
|
169 | }
|
170 | }
|
171 |
|
172 | return Parser
|
173 | }, { isClass: true })
|