block by curran aa1a9b08f246d4cbd0317b0229a21a72

Parser

Full Screen

Built with blockbuilder.org

index.html

<!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>