360 lines
12 KiB
Python
360 lines
12 KiB
Python
import unittest
|
|
import xml
|
|
import xml.dom.minidom
|
|
|
|
import yaml
|
|
|
|
from ast_generator.ast_generator import *
|
|
from ast_generator.utils import Variable
|
|
|
|
|
|
def reachable_return(block):
|
|
return True #TODO we actually need to check this
|
|
|
|
|
|
class TestGeneration(unittest.TestCase):
|
|
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
with open("config.yaml", 'r') as stream:
|
|
props = yaml.safe_load(stream)
|
|
cls.ast_gen = AstGenerator(props)
|
|
|
|
def setUp(self):
|
|
self.ast_gen.current_nesting_depth = 0
|
|
self.ast_gen.current_control_flow_nesting_depth = 0
|
|
|
|
def test_generate_literal(self):
|
|
self.ast_gen.ast = ET.Element("block")
|
|
self.ast_gen.current_ast_element = self.ast_gen.ast
|
|
self.ast_gen.generate_literal('int')
|
|
|
|
self.assertIsNotNone(self.ast_gen.ast.find(GAZ_LIT_TAG))
|
|
self.assertEqual("int", self.ast_gen.ast.find(GAZ_LIT_TAG).get("type"))
|
|
self.assertIsNotNone(self.ast_gen.ast.find(GAZ_LIT_TAG).get("value"))
|
|
|
|
self.assertIsNotNone(ET.tostring(self.ast_gen.ast, 'utf-8').decode('utf-8'))
|
|
|
|
def test_generate_variable(self):
|
|
out: Variable = self.ast_gen.generate_variable('int')
|
|
|
|
self.assertEqual("int", out.xml.get("type"))
|
|
self.assertIsNotNone(out.xml.get("name"))
|
|
self.assertIsNotNone(out.xml.get("mut"))
|
|
|
|
self.assertIsNotNone(ET.tostring(out.xml, 'utf-8').decode('utf-8'))
|
|
|
|
def test_generate_declaration(self):
|
|
self.ast_gen.ast = ET.Element("block")
|
|
self.ast_gen.current_ast_element = self.ast_gen.ast
|
|
self.ast_gen.generate_declaration()
|
|
|
|
self.assertIsNotNone(self.ast_gen.ast.find("declaration"))
|
|
|
|
decl = self.ast_gen.ast.find("declaration")
|
|
self.assertIsNotNone(decl.find("variable"))
|
|
self.assertIsNotNone(decl.find("rhs"))
|
|
|
|
# print(ET.tostring(decl, 'utf-8').decode('utf-8'))
|
|
|
|
def test_generate_assignment(self):
|
|
self.ast_gen.ast = ET.Element("block")
|
|
self.ast_gen.current_ast_element = self.ast_gen.ast
|
|
self.ast_gen.generate_declaration(mut='var')
|
|
self.ast_gen.generate_assignment()
|
|
|
|
self.assertIsNotNone(self.ast_gen.ast.find("assignment"))
|
|
|
|
decl = self.ast_gen.ast.find("assignment")
|
|
|
|
# print(ET.tostring(decl, 'utf-8').decode('utf-8'))
|
|
|
|
self.assertIsNotNone(decl.find("variable"))
|
|
self.assertIsNotNone(decl.find("rhs"))
|
|
|
|
def test_generate_bin_operation(self):
|
|
self.ast_gen.ast = ET.Element("block")
|
|
self.ast_gen.current_ast_element = self.ast_gen.ast
|
|
self.ast_gen.generate_binary('+', 'int')
|
|
|
|
self.assertIsNotNone(self.ast_gen.ast.find("operator"))
|
|
operator = self.ast_gen.ast.find("operator")
|
|
self.assertEqual('+', operator.get("op"))
|
|
self.assertEqual('int', operator.get("type"))
|
|
|
|
def test_generate_unary_operation(self):
|
|
self.ast_gen.ast = ET.Element("block")
|
|
self.ast_gen.current_ast_element = self.ast_gen.ast
|
|
self.ast_gen.generate_unary('-', 'int')
|
|
|
|
self.assertIsNotNone(self.ast_gen.ast.find("unary"))
|
|
operator = self.ast_gen.ast.find("unary")
|
|
self.assertEqual('-', operator.get("op"))
|
|
self.assertEqual('int', operator.get("type"))
|
|
|
|
def test_generate_stream(self):
|
|
for type in ["std_input", "std_output"]:
|
|
self.ast_gen.ast = ET.Element("block")
|
|
self.ast_gen.current_ast_element = self.ast_gen.ast
|
|
self.ast_gen.generate_in_stream()
|
|
|
|
self.assertIsNotNone(self.ast_gen.ast.find("stream"))
|
|
in_stream = self.ast_gen.ast.find("stream")
|
|
self.assertEqual("std_input", in_stream.get("type"))
|
|
|
|
lad = None
|
|
for child in in_stream.iter():
|
|
lad = child.attrib
|
|
|
|
self.assertIsNotNone(lad)
|
|
|
|
def test_generate_block(self):
|
|
self.ast_gen.ast = ET.Element("block")
|
|
self.ast_gen.current_ast_element = self.ast_gen.ast
|
|
self.ast_gen.generate_block()
|
|
|
|
self.assertIsNotNone(self.ast_gen.ast.find("block"))
|
|
|
|
elem = None
|
|
for child in self.ast_gen.ast.iter():
|
|
elem = child.attrib
|
|
self.assertIsNotNone(elem)
|
|
|
|
def test_generate_conditional(self):
|
|
self.ast_gen.ast = ET.Element("block")
|
|
self.ast_gen.current_ast_element = self.ast_gen.ast
|
|
self.ast_gen.generate_conditional()
|
|
|
|
# print(ET.tostring(self.ast_gen.ast, 'utf-8').decode('utf-8'))
|
|
|
|
self.assertIsNotNone(self.ast_gen.current_ast_element.find("conditional"))
|
|
conditional = self.ast_gen.ast.find("conditional")
|
|
|
|
print(ET.tostring(conditional, 'utf-8').decode('utf-8'))
|
|
|
|
self.has_child(conditional)
|
|
|
|
block = conditional.findall("block")
|
|
self.assertEqual(2, len(block))
|
|
|
|
def has_child(self, conditional):
|
|
opts = ['operator', 'unary_operator', 'literal', 'brackets']
|
|
res = []
|
|
for i in opts:
|
|
res.append(conditional.find(i))
|
|
res_list = list(filter(lambda x: x is not None, res))
|
|
self.assertGreater(len(res_list), 0)
|
|
|
|
def test_generate_loop(self):
|
|
self.ast_gen.ast = ET.Element("block")
|
|
self.ast_gen.current_ast_element = self.ast_gen.ast
|
|
self.ast_gen.generate_loop()
|
|
|
|
self.assertIsNotNone(self.ast_gen.ast.find("loop"))
|
|
loop = self.ast_gen.ast.find("loop")
|
|
|
|
# print(ET.tostring(loop, 'utf-8').decode('utf-8'))
|
|
|
|
self.has_child(loop)
|
|
|
|
block = loop.findall("block")
|
|
self.assertEqual(1, len(block))
|
|
|
|
|
|
def test_generate_routine(self):
|
|
self.ast_gen.ast = ET.Element("block")
|
|
self.ast_gen.current_ast_element = self.ast_gen.ast
|
|
self.ast_gen.generate_routine()
|
|
|
|
self.assertIsNotNone(self.ast_gen.ast.find("procedure") or self.ast_gen.ast.find("function"))
|
|
routine = self.ast_gen.ast.find("procedure") or self.ast_gen.ast.find("function")
|
|
|
|
# print(ET.tostring(routine, 'utf-8').decode('utf-8'))
|
|
|
|
self.assertIsNotNone(routine.find("block"))
|
|
self.assertIsNotNone(routine.find("argument"))
|
|
|
|
def test_generate_function_ASSERT_RETURNS(self):
|
|
self.ast_gen.ast = ET.Element("block")
|
|
self.ast_gen.current_ast_element = self.ast_gen.ast
|
|
self.ast_gen.generate_routine(routine_type="function")
|
|
|
|
self.assertIsNotNone(self.ast_gen.ast.find("function"))
|
|
routine = self.ast_gen.ast.find("function")
|
|
|
|
# print(ET.tostring(routine, 'utf-8').decode('utf-8'))
|
|
|
|
self.assertIsNotNone(routine.find("block"))
|
|
self.assertIsNotNone(routine.find("argument"))
|
|
|
|
block = routine.find("block")
|
|
# print(ET.tostring(block, 'utf-8').decode('utf-8'))
|
|
rets = block.find("return")
|
|
# print(rets)
|
|
self.assertLess(0, len(rets))
|
|
self.assertTrue(reachable_return(block))
|
|
|
|
|
|
def test_generate_main(self):
|
|
self.ast_gen.ast = ET.Element("block")
|
|
self.ast_gen.current_ast_element = self.ast_gen.ast
|
|
self.ast_gen.generate_main()
|
|
|
|
self.assertIsNotNone(self.ast_gen.ast.find("procedure"))
|
|
out = self.ast_gen.ast.find("procedure")
|
|
|
|
# print(ET.tostring(out, 'utf-8').decode('utf-8'))
|
|
|
|
self.assertEqual("main", out.get("name"))
|
|
self.assertEqual("int", out.get("return_type"))
|
|
|
|
self.assertIsNotNone(out.find("block"))
|
|
block = out.find("block")
|
|
self.assertTrue(reachable_return(block))
|
|
|
|
self.assertIsNone(out.find("argument"))
|
|
|
|
def test_generate_ast(self):
|
|
self.ast_gen.generate_ast()
|
|
|
|
self.assertIsNotNone(self.ast_gen.ast)
|
|
|
|
print(ET.tostring(self.ast_gen.ast, 'utf-8').decode('utf-8'))
|
|
|
|
procedures = self.ast_gen.ast.findall("procedure")
|
|
self.assertLess(0, len(procedures))
|
|
|
|
main = False
|
|
for proc in procedures:
|
|
if proc.get("name") == "main":
|
|
main = True
|
|
self.assertTrue(main)
|
|
|
|
def test_no_op_operation(self):
|
|
for l in range(1000):
|
|
# print("iteration: " + str(l))
|
|
self.ast_gen.ast = ET.Element("block")
|
|
self.ast_gen.current_ast_element = self.ast_gen.ast
|
|
self.ast_gen.generate_int_expr()
|
|
# self.write_ast()
|
|
|
|
if self.ast_gen.ast.find("operator") is None:
|
|
l -= 1
|
|
continue
|
|
operator = self.ast_gen.ast.find("operator")
|
|
self.assertFalse(self.is_no_op(operator))
|
|
|
|
def test_create_global(self):
|
|
self.ast_gen.ast = ET.Element("block")
|
|
self.ast_gen.current_ast_element = self.ast_gen.ast
|
|
self.ast_gen.generate_main()
|
|
|
|
global_block = self.ast_gen.current_ast_element
|
|
global_scope = self.ast_gen.current_scope
|
|
|
|
self.assertIsNotNone(self.ast_gen.ast.find("procedure"))
|
|
self.ast_gen.current_ast_element = self.ast_gen.ast.find("procedure")
|
|
self.ast_gen.generate_global()
|
|
|
|
self.assertGreater(len(global_scope.symbols), 0)
|
|
self.assertIsNotNone(global_block.find("declaration"))
|
|
|
|
def test_generate_assignment_no_declaration(self):
|
|
for l in range(1000):
|
|
self.ast_gen.ast = ET.Element("block")
|
|
self.ast_gen.current_ast_element = self.ast_gen.ast
|
|
self.ast_gen.generate_declaration(mut='var')
|
|
self.ast_gen.generate_assignment()
|
|
|
|
self.assertIsNotNone(self.ast_gen.ast.find("assignment"))
|
|
|
|
decl = self.ast_gen.ast.find("assignment")
|
|
self.assertIsNone(decl.find("declaration"))
|
|
|
|
def test_failing_assignment(self):
|
|
self.ast_gen.ast = ET.Element("block")
|
|
self.ast_gen.current_ast_element = self.ast_gen.ast
|
|
with self.assertRaises(ValueError):
|
|
self.ast_gen.generate_assignment()
|
|
print(ET.tostring(self.ast_gen.ast, 'utf-8').decode('utf-8'))
|
|
|
|
self.assertIsNone(self.ast_gen.ast.find("assignment"))
|
|
|
|
def is_no_op(self, operator):
|
|
"""
|
|
recursively check if operator is a no-op
|
|
@param operator:
|
|
@return:
|
|
"""
|
|
res = False
|
|
if operator.get("op") == '':
|
|
return True
|
|
else:
|
|
lhs = operator.find("lhs")
|
|
rhs = operator.find("rhs")
|
|
if lhs is None:
|
|
if rhs.find("operator") is not None:
|
|
res = self.is_no_op(rhs.find("operator"))
|
|
elif rhs.find("unary") is not None:
|
|
res = self.is_no_op(rhs.find("unary"))
|
|
else:
|
|
if lhs.find("operator") is not None:
|
|
res = self.is_no_op(lhs.find("operator"))
|
|
elif lhs.find("unary") is not None:
|
|
res = self.is_no_op(lhs.find("unary"))
|
|
elif rhs.find("operator") is not None:
|
|
res = self.is_no_op(rhs.find("operator"))
|
|
elif rhs.find("unary") is not None:
|
|
res = self.is_no_op(rhs.find("unary"))
|
|
|
|
return res
|
|
|
|
def test_print_all_current_scope_vars(self):
|
|
self.ast_gen.ast = ET.Element("block")
|
|
self.ast_gen.current_ast_element = self.ast_gen.ast
|
|
self.ast_gen.generate_declaration(mut='var')
|
|
self.ast_gen.generate_declaration(mut='var')
|
|
self.ast_gen.generate_declaration(mut='var')
|
|
self.ast_gen.generate_declaration(mut='var')
|
|
|
|
self.ast_gen.print_all_current_scope_vars()
|
|
|
|
print(ET.tostring(self.ast_gen.ast))
|
|
|
|
streams = self.ast_gen.ast.findall("stream")
|
|
|
|
self.assertEqual(4, len(streams))
|
|
|
|
def write_ast(self):
|
|
dom = xml.dom.minidom.parseString(ET.tostring(self.ast_gen.ast).decode('utf-8'))
|
|
pretty: str = dom.toprettyxml()
|
|
|
|
randint = random.randint(0, 1000)
|
|
print(randint)
|
|
with open("debug/ast/debug_{}.xml".format(randint), 'w') as f:
|
|
f.write(pretty)
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
with open("config.yaml", 'r') as stream:
|
|
props = yaml.safe_load(stream)
|
|
ast_gen = AstGenerator(props)
|
|
|
|
for a in range(20):
|
|
ast_gen.generate_ast()
|
|
ast = ast_gen.ast
|
|
|
|
with open(f"xml/ast{a}.xml", 'x') as t:
|
|
dom = xml.dom.minidom.parseString(ET.tostring(ast).decode('utf-8'))
|
|
pretty: str = dom.toprettyxml()
|
|
repretty = ""
|
|
for line in pretty.split('\n'):
|
|
if line.startswith("<?xml"):
|
|
pass
|
|
else:
|
|
repretty += (line + '\n')
|
|
|
|
t.write(repretty)
|