fix: expression and string literal parser, #186

This commit is contained in:
harttle
2020-02-08 03:15:55 +08:00
parent 46b69db87a
commit fc0cf6fe7b
8 changed files with 222 additions and 50 deletions
+8 -40
View File
@@ -4,20 +4,24 @@ 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(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)) {
this.evaluateOnce(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))
@@ -27,47 +31,11 @@ export class Expression {
public * value (ctx: Context) {
return toValue(yield this.evaluate(ctx))
}
private evaluateOnce (token: string) {
const r = this.operands.pop()
const l = this.operands.pop()
const result = operatorImpls[token](l, r)
this.operands.push(result)
}
}
function * tokenize (expr: string): IterableIterator<string> {
const N = expr.length
let str = ''
const pairs = { '"': '"', "'": "'", '[': ']', '(': ')' }
for (let i = 0; i < N; i++) {
const c = expr[i]
switch (c) {
case '[':
case '"':
case "'":
str += c
while (i + 1 < N) {
str += expr[++i]
if (expr[i] === pairs[c]) break
}
break
case ' ':
case '\t':
case '\n':
if (str) yield str
str = ''
break
default:
str += c
}
}
if (str) yield str
}
function * toPostfix (expr: string): IterableIterator<string> {
function * toPostfix (tokens: IterableIterator<string>): IterableIterator<string> {
const ops = []
for (const token of tokenize(expr)) {
for (const token of tokens) {
if (isOperator(token)) {
while (ops.length && precedence[ops[ops.length - 1]] > precedence[token]) {
yield ops.pop()!