def test_condition(self): self.assertEqual( Parser("odd 4 * 3").condition(), { "type": "Odd", "expression": { "type": "Binary", "left": { "type": "Number", "value": 4 }, "right": { "type": "Number", "value": 3 }, "operator": Symbol.TIMES, }, }, ) for symbol, repr in [ (Symbol.EQL, "="), (Symbol.NEQ, "!="), (Symbol.LESS, "<"), (Symbol.LEQ, "<="), (Symbol.GTR, ">"), (Symbol.GEQ, ">="), ]: self.assertEqual( Parser(f"4 {repr} 3").condition(), { "type": "Binary", "left": { "type": "Number", "value": 4 }, "right": { "type": "Number", "value": 3 }, "operator": symbol, }, ) with self.assertRaises(ParserException): Parser("4 ~ 3").condition()
def test_factor(self): # no x declaration with self.assertRaises(ParserException): Parser("x").factor() # proc declaration with self.assertRaises(ParserException): parser = Parser("x") parser.declarations["x"] = Symbol.PROC parser.factor() # identifier node parser = Parser("x") parser.declarations["x"] = Symbol.VAR self.assertEqual(parser.factor(), {"type": "Identifier", "name": "x"}) # number node self.assertEqual( Parser("24").factor(), { "type": "Number", "value": 24 }) # no right paren with self.assertRaises(ParserException): Parser("(23").factor() # grouping node self.assertEqual( Parser("(23)").factor(), { "type": "Grouping", "expression": { "type": "Number", "value": 23 } }, ) with self.assertRaises(ParserException): Parser("=").factor()
def test_term(self): # recurses to factor self.assertEqual(Parser("1").term(), {"type": "Number", "value": 1}) self.assertEqual( Parser("1 * 2").term(), { "type": "Binary", "left": { "type": "Number", "value": 1 }, "right": { "type": "Number", "value": 2 }, "operator": Symbol.TIMES, }, ) self.assertEqual( Parser("1 / 2").term(), { "type": "Binary", "left": { "type": "Number", "value": 1 }, "right": { "type": "Number", "value": 2 }, "operator": Symbol.SLASH, }, ) self.assertEqual( Parser("1 / 2 * 4 * 2").term(), { "type": "Binary", "left": { "type": "Binary", "left": { "type": "Binary", "left": { "type": "Number", "value": 1 }, "right": { "type": "Number", "value": 2 }, "operator": Symbol.SLASH, }, "right": { "type": "Number", "value": 4 }, "operator": Symbol.TIMES, }, "right": { "type": "Number", "value": 2 }, "operator": Symbol.TIMES, }, )
def test_expression(self): self.assertEqual( Parser("+4").expression(), { "type": "Number", "value": 4 }) # unary self.assertEqual( Parser("-4").expression(), { "type": "Unary", "operator": Symbol.MINUS, "right": { "type": "Number", "value": 4 }, }, ) self.assertEqual( Parser("1 + 2").expression(), { "type": "Binary", "left": { "type": "Number", "value": 1 }, "right": { "type": "Number", "value": 2 }, "operator": Symbol.PLUS, }, ) self.assertEqual( Parser("1 - 2").expression(), { "type": "Binary", "left": { "type": "Number", "value": 1 }, "right": { "type": "Number", "value": 2 }, "operator": Symbol.MINUS, }, ) self.assertEqual( Parser("1 * 2 + 4 / 3 - 5").expression(), { "type": "Binary", "left": { "type": "Binary", "left": { "type": "Binary", "left": { "type": "Number", "value": 1 }, "right": { "type": "Number", "value": 2 }, "operator": Symbol.TIMES, }, "right": { "type": "Binary", "left": { "type": "Number", "value": 4 }, "right": { "type": "Number", "value": 3 }, "operator": Symbol.SLASH, }, "operator": Symbol.PLUS, }, "right": { "type": "Number", "value": 5 }, "operator": Symbol.MINUS, }, )
def test_statement(self): # Assignment statement parser = Parser("x := 4") parser.declarations["x"] = Symbol.VAR self.assertEqual( parser.statement(), { "type": "Assignment", "name": "x", "value": { "type": "Number", "value": 4 }, }, ) # Procedure call statement parser = Parser("call foo") parser.declarations["foo"] = Symbol.PROC self.assertEqual(parser.statement(), { "type": "Call", "name": "foo", "arguments": [] }) # Procedure call statement with arguments parser = Parser("call foo(a, b + 4)") parser.declarations["foo"] = Symbol.PROC parser.declarations["a"] = Symbol.VAR parser.declarations["b"] = Symbol.VAR self.assertEqual( parser.statement(), { "type": "Call", "name": "foo", "arguments": [{ "type": "Identifier", "name": "a" }, { "type": "Binary", "left": { "type": "Identifier", "name": "b" }, "right": { "type": "Number", "value": 4 }, "operator": Symbol.PLUS, }] }) # If statement parser = Parser("if x < 4 then write x") parser.declarations["x"] = Symbol.VAR self.assertEqual( parser.statement(), { "type": "If", "condition": { "type": "Binary", "left": { "type": "Identifier", "name": "x" }, "right": { "type": "Number", "value": 4 }, "operator": Symbol.LESS, }, "body": { "type": "Output", "value": { "type": "Identifier", "name": "x" }, }, }, ) # Block statement parser = Parser("begin x := 3; y:= x * 4; end") parser.declarations["x"] = Symbol.VAR parser.declarations["y"] = Symbol.VAR self.assertEqual( parser.statement(), { "type": "Block", "statements": [ { "type": "Assignment", "name": "x", "value": { "type": "Number", "value": 3 }, }, { "type": "Assignment", "name": "y", "value": { "type": "Binary", "left": { "type": "Identifier", "name": "x" }, "right": { "type": "Number", "value": 4 }, "operator": Symbol.TIMES, }, }, ], }, ) # While statement parser = Parser("while i < 10 do i := i + 1") parser.declarations["i"] = Symbol.VAR self.assertEqual( parser.statement(), { "type": "Loop", "condition": { "type": "Binary", "left": { "type": "Identifier", "name": "i" }, "right": { "type": "Number", "value": 10 }, "operator": Symbol.LESS, }, "body": { "type": "Assignment", "name": "i", "value": { "type": "Binary", "left": { "type": "Identifier", "name": "i" }, "right": { "type": "Number", "value": 1 }, "operator": Symbol.PLUS, }, }, }, ) # Write statement self.assertEqual( Parser("write 10").statement(), { "type": "Output", "value": { "type": "Number", "value": 10 } }, ) # Debug statement self.assertEqual(Parser("Debug").statement(), {"type": "Debug"}) # Statements have a noop self.assertEqual(Parser("").statement(), None)
def test_block(self): # Const declaration self.assertEqual( Parser("const x = 4;").block(), [{ "type": "Const", "name": "x", "value": 4 }]) self.assertEqual( Parser("const x = 4, y = 5, z = 6;").block(), [ { "type": "Const", "name": "x", "value": 4 }, { "type": "Const", "name": "y", "value": 5 }, { "type": "Const", "name": "z", "value": 6 }, ], ) # Var declaration self.assertEqual( Parser("var x;").block(), [{ "type": "Var", "name": "x" }]) self.assertEqual( Parser("var x, y, z;").block(), [ { "type": "Var", "name": "x" }, { "type": "Var", "name": "y" }, { "type": "Var", "name": "z" }, ], ) # Procedure declaration self.assertEqual( Parser("procedure foo; write 10;").block(), [{ "type": "Procedure", "name": "foo", "parameters": [], "blocks": [{ "type": "Output", "value": { "type": "Number", "value": 10 } }], }], ) self.assertEqual( Parser( "procedure foo; write 10; procedure bar; write 11;").block(), [ { "type": "Procedure", "name": "foo", "parameters": [], "blocks": [{ "type": "Output", "value": { "type": "Number", "value": 10 } }], }, { "type": "Procedure", "name": "bar", "parameters": [], "blocks": [{ "type": "Output", "value": { "type": "Number", "value": 11 } }], }, ], ) self.assertEqual( Parser( "procedure foo; write 10; procedure bar; write 11;").block(), [ { "type": "Procedure", "name": "foo", "parameters": [], "blocks": [{ "type": "Output", "value": { "type": "Number", "value": 10 } }], }, { "type": "Procedure", "name": "bar", "parameters": [], "blocks": [{ "type": "Output", "value": { "type": "Number", "value": 11 } }], }, ], ) # Procedure declaration with parameters self.assertEqual( Parser("procedure foo(a, b); write 10;").block(), [{ "type": "Procedure", "name": "foo", "parameters": [ { "type": "Var", "name": "a" }, { "type": "Var", "name": "b" }, ], "blocks": [{ "type": "Output", "value": { "type": "Number", "value": 10 } }], }], ) # Statement block parser = Parser("x := 1") parser.declarations["x"] = Symbol.VAR self.assertEqual( parser.block(), [{ "type": "Assignment", "name": "x", "value": { "type": "Number", "value": 1 }, }], ) self.assertEqual(Parser("").block(), [])
def test_program(self): # simplest legal program self.assertEqual(Parser(".").program(), [])
def run(code): ast = Parser.parse(code) instructions = Generator.generate_code(ast) VM(instructions).interpret()
def transpile(code, target): if target.lower() == 'python': ast = Parser.parse(code) return PythonTranspiler.generate_code(ast)