diff --git a/src/query-parser/types.js b/src/query-parser/types.js index 1e2fe82..e84de34 100644 --- a/src/query-parser/types.js +++ b/src/query-parser/types.js @@ -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' class Value { @@ -97,11 +97,11 @@ export class Function { #pop(stack) { const result = [] - const len = stack.length - 1 - const n = Math.min(this.length, len) + let n = Math.min(this.length, stack.length) - for(let i = len; i >= len - n; i--) { + while(n > 0) { result.push(stack.pop()) + n-- } result.reverse() @@ -122,14 +122,14 @@ const Associativity = Object.freeze({ export class Operator extends Function { constructor(name, fn, priority, associativity = Associativity.Left, length = fn.length) { super(name, fn, length) - this.priority = priority + this.precedence = priority this.associativity = associativity } compare(other) { - if (other.priority > this.priority) { + if (other.precedence < this.precedence) { return 1 - } else if (other.priority === this.priority) { + } else if (other.precedence === this.precedence) { if (this.associativity === Associativity.Left) { return 1 } else { @@ -185,9 +185,10 @@ export class Filter { const next = stream.next() if (is(Function, 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) } + operators.push(next) } else if (comma(next)) { output.push.apply(output, this.#takeUntil(leftParen, operators)) @@ -196,9 +197,11 @@ export class Filter { } else if (rightParen(next)) { const ops = this.#takeUntil(leftParen, operators) output.push.apply(output, ops) + if (!leftParen(operators.pop())) { throw new SyntaxError('mismatched parenthesis') } + if (is(Function, last(operators))) { output.push(operators.pop()) } @@ -226,13 +229,13 @@ export class Filter { const next = stream.next() if (is(Operator, next)) { const result = next.apply(stack) - stack.unshift(result) + stack.push(result) } else { stack.push(next) } } - return stack.every(identity) + return stack[0] }) } } diff --git a/src/stream.js b/src/stream.js index b115948..36e3c7e 100644 --- a/src/stream.js +++ b/src/stream.js @@ -40,7 +40,7 @@ export class Iterator { let accumulator = [] for (let i = 0; i < n; i++) { - if(this.done()) { + if (this.done()) { break } accumulator.push(this.next()) @@ -49,10 +49,37 @@ export class Iterator { 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) { this.take(n) 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 { diff --git a/tests/query.test.js b/tests/query.test.js index 17c9199..ab1af09 100644 --- a/tests/query.test.js +++ b/tests/query.test.js @@ -121,11 +121,17 @@ describe('query', () => { ) assert.deepEqual( - query('MATCH (e, h:Health) WHERE h.max = 50 OR h.current < 50 RETURN e, h.max AS maxHealth', engine).unwrap(), - [ - { e: 12, maxHealth: 50 }, - { e: 13, maxHealth: 50 } - ] + 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: 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 } + ] ) }) })