fix shunting yard

This commit is contained in:
Rowan 2024-11-27 22:51:24 -06:00
parent d9ebc1e875
commit 3a776ca1e1
3 changed files with 52 additions and 16 deletions

View file

@ -1,4 +1,4 @@
import { assocPath, curry, identity, is, last, mergeRightDeep, path } from '../fn.js' import { curry, is, last, mergeRightDeep, path } from '../fn.js'
import { Stream } from '../stream.js' import { Stream } from '../stream.js'
class Value { class Value {
@ -97,11 +97,11 @@ export class Function {
#pop(stack) { #pop(stack) {
const result = [] const result = []
const len = stack.length - 1 let n = Math.min(this.length, stack.length)
const n = Math.min(this.length, len)
for(let i = len; i >= len - n; i--) { while(n > 0) {
result.push(stack.pop()) result.push(stack.pop())
n--
} }
result.reverse() result.reverse()
@ -122,14 +122,14 @@ const Associativity = Object.freeze({
export class Operator extends Function { export class Operator extends Function {
constructor(name, fn, priority, associativity = Associativity.Left, length = fn.length) { constructor(name, fn, priority, associativity = Associativity.Left, length = fn.length) {
super(name, fn, length) super(name, fn, length)
this.priority = priority this.precedence = priority
this.associativity = associativity this.associativity = associativity
} }
compare(other) { compare(other) {
if (other.priority > this.priority) { if (other.precedence < this.precedence) {
return 1 return 1
} else if (other.priority === this.priority) { } else if (other.precedence === this.precedence) {
if (this.associativity === Associativity.Left) { if (this.associativity === Associativity.Left) {
return 1 return 1
} else { } else {
@ -185,9 +185,10 @@ export class Filter {
const next = stream.next() const next = stream.next()
if (is(Function, next)) { if (is(Function, next)) {
if (is(Operator, next)) { if (is(Operator, next)) {
const ops = this.#takeUntil(op => leftParen(op) && next.compare(op) <= 0, operators) const ops = this.#takeWhile(op => !leftParen(op) && next.compare(op) > 0, operators)
output.push.apply(output, ops) output.push.apply(output, ops)
} }
operators.push(next) operators.push(next)
} else if (comma(next)) { } else if (comma(next)) {
output.push.apply(output, this.#takeUntil(leftParen, operators)) output.push.apply(output, this.#takeUntil(leftParen, operators))
@ -196,9 +197,11 @@ export class Filter {
} else if (rightParen(next)) { } else if (rightParen(next)) {
const ops = this.#takeUntil(leftParen, operators) const ops = this.#takeUntil(leftParen, operators)
output.push.apply(output, ops) output.push.apply(output, ops)
if (!leftParen(operators.pop())) { if (!leftParen(operators.pop())) {
throw new SyntaxError('mismatched parenthesis') throw new SyntaxError('mismatched parenthesis')
} }
if (is(Function, last(operators))) { if (is(Function, last(operators))) {
output.push(operators.pop()) output.push(operators.pop())
} }
@ -226,13 +229,13 @@ export class Filter {
const next = stream.next() const next = stream.next()
if (is(Operator, next)) { if (is(Operator, next)) {
const result = next.apply(stack) const result = next.apply(stack)
stack.unshift(result) stack.push(result)
} else { } else {
stack.push(next) stack.push(next)
} }
} }
return stack.every(identity) return stack[0]
}) })
} }
} }

View file

@ -40,7 +40,7 @@ export class Iterator {
let accumulator = [] let accumulator = []
for (let i = 0; i < n; i++) { for (let i = 0; i < n; i++) {
if(this.done()) { if (this.done()) {
break break
} }
accumulator.push(this.next()) accumulator.push(this.next())
@ -49,10 +49,37 @@ export class Iterator {
return accumulator return accumulator
} }
takeWhile(fn) {
const accumulator = []
while (!this.done() || fn(this.peek())) {
accumulator.push(this.next())
}
return accumulator
}
takeUntil(fn) {
return this.takeWhile(x => !fn(x))
}
drop(n) { drop(n) {
this.take(n) this.take(n)
return this return this
} }
dropWhile(fn) {
while(!this.done() || fn(this.peek())) {
this.next()
}
return this
}
dropUntil(fn) {
this.dropWhile(x => !fn(x))
return this
}
} }
class ArrayIterator extends Iterator { class ArrayIterator extends Iterator {

View file

@ -121,12 +121,18 @@ describe('query', () => {
) )
assert.deepEqual( assert.deepEqual(
query('MATCH (e, h:Health) WHERE h.max = 50 OR h.current < 50 RETURN e, h.max AS maxHealth', engine).unwrap(), query('MATCH (e, h:Health) WHERE h.max = 50 OR h.current > 45 RETURN e, h.max AS maxHealth', engine).unwrap(),
[ [
{ e: 12, maxHealth: 50 }, { e: 12, maxHealth: 50 },
{ e: 13, maxHealth: 50 } { e: 13, maxHealth: 50 }
] ]
) )
assert.deepEqual(
query('MATCH (e, :Player, h:Health) WHERE (h.max = 50 OR h.current > 45) AND e = 12 RETURN e, h.max AS maxHealth', engine).unwrap(),
[
{ e: 12, maxHealth: 50 }
]
)
}) })
}) })