From d3460d5c0af40b5b8e3aba0364e6748dd93dea52 Mon Sep 17 00:00:00 2001 From: ayrton Date: Sun, 19 Nov 2023 11:40:07 -0700 Subject: [PATCH] initial python unparsing Took 31 minutes --- ast_parser/python_unparser.py | 108 ++++++++++++++--- ast_parser/test/test_python_unparse.py | 158 +++++++++++++++++++++++++ 2 files changed, 251 insertions(+), 15 deletions(-) create mode 100644 ast_parser/test/test_python_unparse.py diff --git a/ast_parser/python_unparser.py b/ast_parser/python_unparser.py index d9dd415..ce6b182 100644 --- a/ast_parser/python_unparser.py +++ b/ast_parser/python_unparser.py @@ -1,19 +1,74 @@ +import warnings from xml.etree import ElementTree as ET +from ast_parser.gaz_unparser import GazUnparser from ast_parser.general_unparser import GeneralUnparser from constants import * -class PythonUnparser(GeneralUnparser): +def to_python_type(ty): + if ty == GAZ_INT_KEY: + return "int" + elif ty == GAZ_BOOL_KEY: + return "bool" + elif ty == GAZ_FLOAT_KEY: + return "float" + elif ty == GAZ_CHAR_KEY: + return "str" + elif ty == GAZ_STRING_KEY: + return "str" + else: + raise Exception("Unknown type: " + ty) - def unparse_xhs(self, element_in: ET.Element): - pass + +def to_python_op(param): + if param == "negation" or param == "subtraction": + return "-" + elif param == "addition": + return "+" + elif param == "multiplication": + return "*" + elif param == "division": + return "/" + elif param == "modulus": + return "%" + elif param == "power": + return "**" + elif param == "equality": + return "==" + elif param == "inequality": + return "!=" + elif param == "less-than": + return "<" + elif param == "less-than-or-equal": + return "<=" + elif param == "greater-than": + return ">" + elif param == "greater-than-or-equal": + return ">=" + else: + warnings.warn("Warning, unknown operator: " + param) + return param + + +class PythonUnparser(GazUnparser): + def __init__(self, ast: ET.Element, debug=False): + super().__init__(ast, debug) def unparse_top_block(self, element_in: ET.Element): pass - def unparse_block(self, element_in: ET.Element): - pass + def unparse_block(self, node: ET.Element): + self.source += "\n" + self.indentation += 4 + for child in node: + self.unparse_node(child) + self.indentation -= 4 + + if node.get(GAZ_TY_KEY) is None: + self.source += "\n\n" + elif node.get(GAZ_TY_KEY) in [GAZ_TRUE_BLOCK_TAG, GAZ_FALSE_BLOCK_TAG]: + self.source += "" def unparse_routine(self, element_in: ET.Element): pass @@ -28,17 +83,34 @@ class PythonUnparser(GeneralUnparser): pass def unparse_declaration(self, element_in: ET.Element): - pass + variable = element_in.find(GAZ_VAR_TAG) + rhs = element_in.find(GAZ_RHS_TAG) + self.unparse_variable(variable, True) + self.source += " = " + self.unparse_node(rhs) + self.source += "\n" def unparse_operator(self, element_in: ET.Element): - pass + self.unparse_xhs(element_in.find(GAZ_LHS_TAG)) + self.source += " {} ".format(to_python_op(element_in.get("op"))) + self.unparse_xhs(element_in.find(GAZ_RHS_TAG)) + + def unparse_instream(self, node): + for child in node: + self.unparse_node(child) + + self.source += "input()\n".format(GAZ_IN_STREAM) + + def unparse_outstream(self, node): + self.source += "print(" + for child in node: + self.unparse_node(child) + + self.source += ", end='')\n".format(GAZ_OUT_STREAM) def unparse_unary(self, element_in: ET.Element): pass - def unparse_stream(self, element_in: ET.Element): - pass - def unparse_assignment(self, element_in: ET.Element): pass @@ -52,11 +124,17 @@ class PythonUnparser(GeneralUnparser): pass def unparse_return(self, element_in: ET.Element): - pass + self.source += "return " + for child in element_in: + self.unparse_node(child) + self.source += "\n" - def unparse_literal(self, element_in: ET.Element): - pass + def unparse_variable(self, element_in: ET.Element, is_declaration=False): + if is_declaration: + ty = to_python_type(element_in.get(GAZ_TY_KEY)) + name = element_in.get(GAZ_NAME_KEY) - def unparse_variable(self, element_in: ET.Element): - pass + self.source += f"{name}: {ty}" + else: + super().unparse_variable(element_in) diff --git a/ast_parser/test/test_python_unparse.py b/ast_parser/test/test_python_unparse.py new file mode 100644 index 0000000..f9ce0c0 --- /dev/null +++ b/ast_parser/test/test_python_unparse.py @@ -0,0 +1,158 @@ +import unittest + +import xml.etree.ElementTree as ET + +from ast_parser.python_unparser import PythonUnparser + + +class TestPythonUnparseCode(unittest.TestCase): + + def test_unparse_variable_regular(self): + input = '' + parser = PythonUnparser(ET.fromstring(input), True) + parser.source = "" + parser.unparse_node(parser.xml) + self.assertIsNotNone(parser.source) + self.assertEqual("a", parser.source) + + def test_unparse_variable_declaration(self): + input = '' + parser = PythonUnparser(ET.fromstring(input), True) + parser.source = "" + parser.unparse_variable(parser.xml, True) + self.assertIsNotNone(parser.source) + self.assertEqual("a: int", parser.source) + + + def test_unparse_rhs_single(self): + input = '' + parser = PythonUnparser(ET.fromstring(input), True) + parser.source = "" + parser.unparse_node(parser.xml) + self.assertIsNotNone(parser.source) + self.assertEqual("1", parser.source) + + def test_unparse_declaration(self): + input = '' + parser = PythonUnparser(ET.fromstring(input), True) + parser.source = "" + parser.unparse_node(parser.xml) + self.assertIsNotNone(parser.source) + self.assertEqual("a: int = 1\n", parser.source) + + def test_unparse_stream(self): + input = ' ' + parser = PythonUnparser(ET.fromstring(input), True) + parser.source = "" + parser.unparse_node(parser.xml) + self.assertIsNotNone(parser.source) + self.assertEqual("print(a * 42, end='')\n", parser.source) + + def test_unparse_block(self): + input = ' ' + parser = PythonUnparser(ET.fromstring(input), True) + parser.source = "" + parser.unparse_node(parser.xml) + self.assertIsNotNone(parser.source) + self.assertEqual("\n a: int = 1\n print(a * 42, end='')\n return 0\n\n\n", parser.source) + + def test_unparse_assignment(self): + with open("xml/assignment.xml", "r") as f: + input = f.read() + parser = PythonUnparser(ET.fromstring(input), True) + parser.source = "" + parser.unparse_node(parser.xml) + self.assertIsNotNone(parser.source) + self.assertEqual("C = 30;\n", parser.source) + + def test_unparse_conditional(self): # TODO test the else-if statements + with open("xml/conditional.xml", "r") as f: + input = f.read() + + with open("xml/conditional.out", "r") as f: + output = f.read() + + parser = PythonUnparser(ET.fromstring(input), True) + parser.source = "" + + parser.unparse_node(parser.xml) + + self.assertIsNotNone(parser.source) + self.assertEqual(output, parser.source) + + def test_unparse_loop(self): + with open("xml/loop.xml", "r") as f: + input = f.read() + + with open("xml/loop.out", "r") as f: + output = f.read() + + parser = PythonUnparser(ET.fromstring(input), True) + parser.source = "" + parser.unparse_node(parser.xml) + self.assertIsNotNone(parser.source) + self.assertEqual(output, parser.source) + + def test_unparse_operation_single(self): + input = ' ' + parser = PythonUnparser(ET.fromstring(input), True) + parser.source = "" + parser.unparse_node(parser.xml) + self.assertIsNotNone(parser.source) + self.assertEqual("a * 42", parser.source) + + def test_unparse_return(self): + input = ' ' + parser = PythonUnparser(ET.fromstring(input), True) + parser.source = "" + parser.unparse_node(parser.xml) + self.assertIsNotNone(parser.source) + self.assertEqual("return 0\n", parser.source) + + def test_unparse_unary(self): + with open("xml/unary.xml", "r") as f: + input = f.read() + + with open("xml/unary.out", "r") as f: + output = f.read() + + parser = PythonUnparser(ET.fromstring(input), True) + parser.source = "" + parser.unparse_node(parser.xml) + self.assertIsNotNone(parser.source) + self.assertEqual(output, parser.source) + + def test_unparse_routine(self): + input = '' + parser = PythonUnparser(ET.fromstring(input), True) + parser.source = "" + parser.unparse_node(parser.xml) + self.assertIsNotNone(parser.source) + i = ' ' * parser.indentation + self.assertEqual("procedure main() returns integer {\n var integer a = 1;\n a * 42 -> std_output;\n return 0;\n}\n\n", parser.source) + + def test_unparse_args(self): + with open("xml/many_args.xml", 'r') as f: + input = f.read() + with open("xml/many_args.out", 'r') as f: + output = f.read() + parser = PythonUnparser(ET.fromstring(input), True) + parser.source = "" + parser.unparse_node(parser.xml) + self.assertIsNotNone(parser.source) + self.assertEqual(output, parser.source) + + + def test_unparse_code(self): + with open("xml/test.xml", "r") as input: + parser = PythonUnparser(ET.fromstring(input.read()), True) + parser.unparse() + self.assertIsNotNone(parser.source) + + with open("input.in", "r") as input: + i = input.read() + self.assertEqual(i, parser.source) + + +if __name__ == "__main__": + unittest.main() \ No newline at end of file