Skip to content

Commit

Permalink
Complete implementation on unconventional expression parser.
Browse files Browse the repository at this point in the history
Support for backets in expressions finally!
  • Loading branch information
IJMacD committed Mar 8, 2019
1 parent 39c87b8 commit 055fd5d
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 94 deletions.
4 changes: 3 additions & 1 deletion frontend/main.js
Expand Up @@ -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) ? `<ul>${node.children.map(c => `<li>${renderNode(c)}</li>`).join('')}</ul>` : ''}`;
return `${node.id || (node.type === NODE_TYPE_LIST ? "LIST" : '')}${Array.isArray(node.children) ? `<ul>${node.children.map(c => `<li>${renderNode(c)}</li>`).join('')}</ul>` : ''}`;
}

function getSuggestions () {
Expand Down
103 changes: 50 additions & 53 deletions src/parser.js
Expand Up @@ -541,7 +541,7 @@ function parseFromTokenList (tokenList, source="") {

const nodes = [];

while (!end()) {
while (!end() && !peek(TOKEN_TYPES.BRACKET, ")")) {
try {
nodes.push(descendNode());
} catch (e) {
Expand Down Expand Up @@ -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;
}

/**
Expand Down
114 changes: 74 additions & 40 deletions test/expr.test.js
Expand Up @@ -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);
});
});
});
});
Expand All @@ -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", () => {
Expand Down

0 comments on commit 055fd5d

Please sign in to comment.