mirror of
https://github.com/harttle/liquidjs.git
synced 2026-09-15 20:30:39 -07:00
67 lines
1.9 KiB
TypeScript
67 lines
1.9 KiB
TypeScript
import { assert } from '../util/assert'
|
|
import { rangeLine } from '../parser/lexical'
|
|
import { parseLiteral } from '../parser/literal'
|
|
import { Context } from '../context/context'
|
|
import { range, toValue } from '../util/underscore'
|
|
import { isOperator, precedence, operatorImpls } from './operator'
|
|
import { Tokenizer } from '../parser/tokenizer'
|
|
|
|
export class Expression {
|
|
private operands: any[] = []
|
|
private postfix: string[]
|
|
|
|
public constructor (str = '') {
|
|
const tokenizer = new Tokenizer(str)
|
|
this.postfix = [...toPostfix(tokenizer.readExpression())]
|
|
}
|
|
public * evaluate (ctx: Context): any {
|
|
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 {
|
|
const literal = parseLiteral(token)
|
|
this.operands.push(literal !== undefined ? literal : yield ctx.get(token))
|
|
}
|
|
}
|
|
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()!
|
|
}
|
|
}
|
|
|
|
function * rangeValue (token: string, ctx: Context) {
|
|
let match
|
|
if ((match = token.match(rangeLine))) {
|
|
const low = yield new Expression(match[1]).value(ctx)
|
|
const high = yield new Expression(match[2]).value(ctx)
|
|
return range(+low, +high + 1)
|
|
}
|
|
}
|
|
|
|
function isRange (str: string) {
|
|
return rangeLine.test(str)
|
|
}
|