From 055fd5dfcad437594bbfb5c9c60bfa7d110cfd34 Mon Sep 17 00:00:00 2001 From: Iain MacDonald Date: Fri, 8 Mar 2019 12:57:14 +0800 Subject: [PATCH] Complete implementation on unconventional expression parser. Support for backets in expressions finally! --- frontend/main.js | 4 +- src/parser.js | 103 ++++++++++++++++++++--------------------- test/expr.test.js | 114 ++++++++++++++++++++++++++++++---------------- 3 files changed, 127 insertions(+), 94 deletions(-) diff --git a/frontend/main.js b/frontend/main.js index 34dab50..3053bb2 100644 --- a/frontend/main.js +++ b/frontend/main.js @@ -231,8 +231,10 @@ function renderAST (json) { return renderNode(ast); } +const NODE_TYPE_LIST = 8; + function renderNode (node) { - return `${node.id || ''}${Array.isArray(node.children) ? `` : ''}`; + return `${node.id || (node.type === NODE_TYPE_LIST ? "LIST" : '')}${Array.isArray(node.children) ? `` : ''}`; } function getSuggestions () { diff --git a/src/parser.js b/src/parser.js index 429f870..c21dd82 100644 --- a/src/parser.js +++ b/src/parser.js @@ -541,7 +541,7 @@ function parseFromTokenList (tokenList, source="") { const nodes = []; - while (!end()) { + while (!end() && !peek(TOKEN_TYPES.BRACKET, ")")) { try { nodes.push(descendNode()); } catch (e) { @@ -569,71 +569,68 @@ function parseFromTokenList (tokenList, source="") { throw Error("Expecting an operator"); } - index++; + // // Unary prefix + // if (t.value === "NOT") {} - const left = nodes[index]; + let left = nodes[++index]; if (left.type === NODE_TYPES.OPERATOR) { - root.children[0] = assembleExpressionTree(nodes) - } else { - root.children[0] = left; + left = assembleExpressionTree(nodes); + } else if (left.type === NODE_TYPES.LIST) { + // If we have a list here it's not actually a list + // it's really a subexpression in brackets. + // That subexpression has already been correctly parsed. + left = left.children[0]; + } + + root.children[0] = left; + + // Unary postfix + if (root.id === "IS NULL" || + root.id === "IS NOT NULL") + { + return root; } - index++; + let right = nodes[++index]; + + if (root.id === "BETWEEN" && right.id === "AND") { + // skip ahead then we have two more nodes to add - const right = nodes[index]; + right = nodes[++index]; + } if (right.type === NODE_TYPES.OPERATOR) { - root.children[1] = assembleExpressionTree(nodes) - } else { - root.children[1] = right; + right = assembleExpressionTree(nodes); + } else if (right.type === NODE_TYPES.LIST && (root.id !== "IN" && root.id !== "NOT IN")) { + // Most of the time 'LISTs' are just bracketed sub-expressions + // but the IN operators can take a list + right = right.children[0]; + } + + root.children[1] = right; + + if (root.id === "BETWEEN") { + // Between has a third child node + let farRight = nodes[++index]; + + if (farRight.type === NODE_TYPES.OPERATOR) { + farRight = assembleExpressionTree(nodes); + } else if (farRight.type === NODE_TYPES.LIST) { + farRight = farRight.children[0]; + } + + root.children[2] = farRight; } return root; } - return assembleExpressionTree(nodes); - - - // const t = next(); - - - // let right; - - // // Unary prefix - // if (t.value === "NOT") {} - // // Unary postfix - // if (t.value === "IS NULL" || - // t.value === "IS NOT NULL") - // { - // op.children.push(left); - // root = op; - // } - // else { - // right = descendExpression(getPrecedence(op)); - - // if (right.type === NODE_TYPES.OPERATOR) { - // // if child operator is weaker than me - // if (getPrecedence(right) < getPrecedence(op)) { - // // Steal left child - // const leftChild = right.children[0]; - // op.children.push(left, leftChild); - // right.children[0] = op; - // root = right; - // } else { - // op.children.push(left, right); - // root = op; - // } - // } else { - // op.children.push(left, right); - // root = op; - // } - // } - // } - - // root.source = source.substring(start, current() && current().start).trim(); - - // return root; + const root = assembleExpressionTree(nodes); + + root.source = source.substring(start, current() && current().start).trim(); + + return root; } /** diff --git a/test/expr.test.js b/test/expr.test.js index fbcfc43..28a7827 100644 --- a/test/expr.test.js +++ b/test/expr.test.js @@ -102,63 +102,65 @@ describe("Maths", () => { }); }); - test("Order of Operations: +-", () => { - return runQuery("SELECT 14 + 28 - 18").then(r => { - expect(r[1][0]).toBe(24); + describe("Order of Operations", () => { + test("+-", () => { + return runQuery("SELECT 14 + 28 - 18").then(r => { + expect(r[1][0]).toBe(24); + }); }); - }); - test("Order of Operations: -+", () => { - return runQuery("SELECT 14 - 8 + 26").then(r => { - expect(r[1][0]).toBe(32); + test("-+", () => { + return runQuery("SELECT 14 - 8 + 26").then(r => { + expect(r[1][0]).toBe(32); + }); }); - }); - test("Order of Operations: *+", () => { - return runQuery("SELECT 14 * 2 + 7").then(r => { - expect(r[1][0]).toBe(35); + test("*+", () => { + return runQuery("SELECT 14 * 2 + 7").then(r => { + expect(r[1][0]).toBe(35); + }); }); - }); - test("Order of Operations: *-", () => { - return runQuery("SELECT 14 * 2 - 7").then(r => { - expect(r[1][0]).toBe(21); + test("*-", () => { + return runQuery("SELECT 14 * 2 - 7").then(r => { + expect(r[1][0]).toBe(21); + }); }); - }); - test("Order of Operations: +*", () => { - return runQuery("SELECT 14 + 7 * 2").then(r => { - expect(r[1][0]).toBe(28); + test("+*", () => { + return runQuery("SELECT 14 + 7 * 2").then(r => { + expect(r[1][0]).toBe(28); + }); }); - }); - test("Order of Operations: -*", () => { - return runQuery("SELECT 42 - 7 * 3").then(r => { - expect(r[1][0]).toBe(21); + test("-*", () => { + return runQuery("SELECT 42 - 7 * 3").then(r => { + expect(r[1][0]).toBe(21); + }); }); - }); - test("Order of Operations: +*+", () => { - return runQuery("SELECT 14 + 7 * 2 + 1").then(r => { - expect(r[1][0]).toBe(29); + test("+*+", () => { + return runQuery("SELECT 14 + 7 * 2 + 1").then(r => { + expect(r[1][0]).toBe(29); + }); }); - }); - test("Order of Operations: +*-", () => { - return runQuery("SELECT 14 + 7 * 5 - 1").then(r => { - expect(r[1][0]).toBe(48); + test("+*-", () => { + return runQuery("SELECT 14 + 7 * 5 - 1").then(r => { + expect(r[1][0]).toBe(48); + }); }); - }); - test("Order of Operations: *+*", () => { - return runQuery("SELECT 14 * 2 + 7 * 2").then(r => { - expect(r[1][0]).toBe(42); + test("*+*", () => { + return runQuery("SELECT 14 * 2 + 7 * 2").then(r => { + expect(r[1][0]).toBe(42); + }); }); - }); - test("Order of Operations: *-*", () => { - return runQuery("SELECT 14 * 3 - 7 * 5").then(r => { - expect(r[1][0]).toBe(7); + test("*-*", () => { + return runQuery("SELECT 14 * 3 - 7 * 5").then(r => { + expect(r[1][0]).toBe(7); + }); }); }); }); @@ -180,7 +182,39 @@ test("Coalesce operator (??)", () => { expect(r[1][0]).toBe(0); }), ]); -}) +}); + +describe("Brackets", () => { + test("(+)*", () => { + return runQuery("SELECT (5 + 2) * 3").then(r => { + expect(r[1][0]).toBe(21); + }); + }); + + test("+(*)", () => { + return runQuery("SELECT 5 + (2 * 3)").then(r => { + expect(r[1][0]).toBe(11); + }); + }); + + test("* (-) *", () => { + return runQuery("SELECT 2 * (10 - 6) * 3").then(r => { + expect(r[1][0]).toBe(24); + }); + }); + + test("+ (-) *", () => { + return runQuery("SELECT 2 + (10 - 4) * 3").then(r => { + expect(r[1][0]).toBe(20); + }); + }); + + test("(+) IN", () => { + return runQuery("SELECT (3 + 4) IN (5, 6, 7, 8)").then(r => { + expect(r[1][0]).toBe(true); + }); + }); +}); describe("Nested Functions", () => { test("Function -> Expression", () => {