UNPKG

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