Skip to content

Instantly share code, notes, and snippets.

@curran
Last active August 12, 2017 09:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save curran/aa1a9b08f246d4cbd0317b0229a21a72 to your computer and use it in GitHub Desktop.
Save curran/aa1a9b08f246d4cbd0317b0229a21a72 to your computer and use it in GitHub Desktop.
Parser
license: mit
<!DOCTYPE html>
<head>
<meta charset="utf-8">
</head>
<body>
<script>
function isLetter(str) {
return str.match(/[a-z]/i);
}
function parse(str) {
// Parse the next token.
// We assume each token is a single character.
var i = 0, sym;
function nextSym () {
sym = str[i++];
if (sym && isLetter(sym)) {
sym += str[i++]
}
}
function symType () {
if (sym === "/" || sym === "+") {
return "operator";
}
if (sym === "(" || sym === ")") {
return "paren";
}
return "name";
}
// Our grammar:
// expression = ["+"] term {("+") term} .
// term = factor {("/") factor} .
// factor =
// name
// | "(" expression ")" .
function factor (){
if (symType() === "name") {
var name = sym;
nextSym();
return name;
}
if (sym === "(") {
nextSym();
var result = expression();
if (sym === ")") {
nextSym();
return result;
}
console.error("Expected end paren ')'.")
}
}
function operator (symbol, orientation, fn){
return function () {
var result = fn();
if (sym !== symbol) {
return result;
}
var children = [result];
while (sym === symbol) {
nextSym();
children = children.concat(fn());
}
return {
orientation: orientation,
children: children
};
}
}
var term = operator("/", "vertical", factor);
var expression = operator("+", "horizontal", term);
nextSym();
return expression();
}
function test (str, expected){
if (JSON.stringify(parse(str)) !== JSON.stringify(expected)) {
console.error("Test failed for " + str);
console.error(JSON.stringify(parse(str)));
}
}
console.log("Running tests...");
test("A", "A");
test("A/B", { "orientation": "vertical", "children": ["A", "B"] });
test("A+B", { "orientation": "horizontal", "children": ["A", "B"] });
test("A/B/C", { "orientation": "vertical", "children": ["A", "B", "C"] });
test("A+B+C", { "orientation": "horizontal", "children": ["A", "B", "C"] });
test("A/B+C", {
"orientation": "horizontal",
"children": [ { "orientation": "vertical", "children": ["A", "B"] }, "C" ]
});
test("A+B/C", {
"orientation": "horizontal",
"children": [ "A", { "orientation": "vertical", "children": ["B", "C"] } ]
});
test("(A)", "A");
test("(A/B)", { "orientation": "vertical", "children": ["A", "B"] });
test("(A+B)", { "orientation": "horizontal", "children": ["A", "B"] });
test("(A)/B", { "orientation": "vertical", "children": ["A", "B"] });
test("A+(B)", { "orientation": "horizontal", "children": ["A", "B"] });
test("(A)/B", { "orientation": "vertical", "children": ["A", "B"] });
test("(A)+(B)", { "orientation": "horizontal", "children": ["A", "B"] });
test("((A))", "A");
test("((A)+(B))", { "orientation": "horizontal", "children": ["A", "B"] });
test("(A)/(B)/(C)", { "orientation": "vertical", "children": ["A", "B", "C"] });
test("A/(B/C)", {
"orientation":"vertical",
"children":["A",{"orientation":"vertical","children":["B","C"]}]
});
test("(A/B)/C", {
"orientation":"vertical",
"children":[{"orientation":"vertical","children":["A","B"]},"C"]
});
test("A+(B+C)", {
"orientation": "horizontal",
"children":["A",{"orientation":"horizontal","children":["B","C"]}]
});
test("A/(B+C)", {
"orientation": "vertical",
"children":["A",{"orientation":"horizontal","children":["B","C"]}]
});
test("A/(B+C/D+E)", {
"orientation":"vertical",
"children":[
"A",
{
"orientation":"horizontal",
"children":[
"B",
{"orientation":"vertical","children":["C","D"]},
"E"
]
}
]
});
test("Foo", "Foo");
console.log("Ran all tests!");
//var complexCase = "A/(B+C/D+E)";
</script>
</body>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment