-
-
Notifications
You must be signed in to change notification settings - Fork 231
/
expression.ts
49 lines (45 loc) · 1.46 KB
/
expression.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
import { assert } from '../util/assert'
import { isRange, rangeValue } from './range'
import { Value } from './value'
import { Context } from '../context/context'
import { toValue } from '../util/underscore'
import { isOperator, precedence, operatorImpls } from './operator'
import { tokenize } from '../parser/expression-tokenizer'
export class Expression {
private operands: any[] = []
private postfix: string[]
public constructor (str = '') {
this.postfix = [...toPostfix(tokenize(str))]
}
public * evaluate (ctx: Context) {
assert(ctx, 'unable to evaluate: context not defined')
for (const token of this.postfix) {
if (isOperator(token)) {
const r = this.operands.pop()
const l = this.operands.pop()
const result = operatorImpls[token](l, r)
this.operands.push(result)
} else if (isRange(token)) {
this.operands.push(yield rangeValue(token, ctx))
} else this.operands.push(yield new Value(token).evaluate(ctx))
}
return this.operands[0]
}
public * value (ctx: Context) {
return toValue(yield this.evaluate(ctx))
}
}
function * toPostfix (tokens: IterableIterator<string>): IterableIterator<string> {
const ops = []
for (const token of tokens) {
if (isOperator(token)) {
while (ops.length && precedence[ops[ops.length - 1]] > precedence[token]) {
yield ops.pop()!
}
ops.push(token)
} else yield token
}
while (ops.length) {
yield ops.pop()!
}
}