def test_get_expression(self): st = SymbolTable() self.assertRaises(AssertionError, st.get_expression, "a") st.store("a", Expression(Type.list_of(Type.int()), [1])) self.assertEqual([1], st.get_expression("a").value) self.assertEqual(1, st.get_expression("a").value[0]) self.assertEqual(Type.list_of(Type.int()), st.get_expression("a").type)
def visitInsert(self, ctx: xDroneParser.InsertContext) -> None: list = self.visit(ctx.expr(0)) if not isinstance(list.type, ListType): raise CompileError( "Expression {} should have type list, but is {}".format( list, list.type)) if ctx.AT(): index = self.visit(ctx.expr(1)) value = self.visit(ctx.expr(2)) else: index = Expression(Type.int(), len(list.value)) value = self.visit(ctx.expr(1)) if index.type != Type.int(): raise CompileError( "Expression {} should have type int, but is {}".format( index, index.type)) if index.value > len(list.value) or index.value < 0: raise CompileError( "List {} has length {}, but has been inserted at out-of-range index {}" .format(list, len(list.value), index.value)) if not isinstance(list.type, EmptyList) and value.type != list.type.elem_type: raise CompileError( "List {} has been declared as {}, but inserted with element type {}" .format(list, list.type, value.type)) self._insert_nested_ident(list.ident, value, index.value)
def test_assign_list_elem_with_different_type_should_give_error(self): types = [ Type.int(), Type.decimal(), Type.string(), Type.boolean(), Type.vector(), Type.drone(), Type.list_of(Type.int()), Type.list_of(Type.list_of(Type.int())) ] for t1 in types: for t2 in types: if t1 == t2: continue with self.assertRaises(CompileError) as context: generate_commands(""" main () {{ {} a; {} b; list[{}] c <- [a]; c[0] <- b; }} """.format(t1, t2, t1)) self.assertTrue( "Assigned value {} should have type {}, but is {}".format( Expression(t2, t2.default_value, ident="b"), t1.type_name, t2.type_name) in str(context.exception))
def test_update(self): st = SymbolTable() self.assertRaises(AssertionError, st.update, "a", 1) st.store("a", Expression(Type.int(), 1)) st.update("a", 2) self.assertEqual(Type.int(), st.get_expression("a").type) self.assertEqual(2, st.get_expression("a").value)
def test_assess_list_elem_nested_out_of_bound_should_give_error(self): with self.assertRaises(CompileError) as context: generate_commands(""" main () { list[list[list[int]]] a <- [[[1], [2]]]; a[0][2] <- [1]; } """) self.assertTrue( "List {} has length 2, but has been assessed with out-of-range index 2" .format( Expression(Type.list_of(Type.list_of(Type.int())), [[1], [2]], ident="a[0]")) in str(context.exception)) with self.assertRaises(CompileError) as context: generate_commands(""" main () { list[list[list[int]]] a <- [[[1], [2]]]; a[0][1][1] <- 1; } """) self.assertTrue( "List {} has length 1, but has been assessed with out-of-range index 1" .format(Expression(Type.list_of(Type.int()), [2], ident="a[0][1]")) in str(context.exception))
def test_declare_and_assign_with_different_type_should_give_error(self): types = [ Type.int(), Type.decimal(), Type.string(), Type.boolean(), Type.vector(), Type.drone(), Type.list_of(Type.int()), Type.list_of(Type.list_of(Type.int())) ] for t1 in types: for t2 in types: if t1 == t2: continue with self.assertRaises(CompileError) as context: generate_commands(""" main () {{ {} a; {} b <- a; }} """.format(t1, t2)) self.assertTrue( "Identifier b has been declared as {}, but assigned as {}". format(t2.type_name, t1.type_name) in str( context.exception))
def test_list_elem_assign_with_wrong_index_should_give_error(self): types = [ Type.decimal(), Type.string(), Type.boolean(), Type.vector(), Type.drone(), Type.list_of(Type.int()), Type.list_of(Type.decimal()), Type.list_of(Type.list_of(Type.int())) ] for type in types: with self.assertRaises(CompileError) as context: generate_commands(""" main () {{ {} a; list[int] b; b[a] <- 1; }} """.format(type)) self.assertTrue( "Expression {} should have type int, but is {}".format( Expression(type, type.default_value, ident="a"), type.type_name) in str(context.exception))
def test_list_insert_with_wrong_type_value_should_give_error(self): types = [ Type.int(), Type.decimal(), Type.string(), Type.boolean(), Type.vector(), Type.drone(), Type.list_of(Type.int()), Type.list_of(Type.decimal()), Type.list_of(Type.list_of(Type.int())) ] for t1 in types: for t2 in types: if t1 == t2: continue with self.assertRaises(CompileError) as context: generate_commands(""" main () {{ {} a; list[{}] b; b.insert(a); }} """.format(t1, t2)) self.assertTrue( "List {} has been declared as {}, but inserted with element type {}" .format(Expression(Type.list_of(t2), [], ident="b"), Type.list_of(t2), t1) in str(context.exception))
def test_list_elem_expr_out_of_range_should_give_error(self): with self.assertRaises(CompileError) as context: generate_commands(""" main () {{ list[int] a <- [1, 2]; int b <- a[2]; }} """.format(type)) self.assertTrue( "List {} has length {}, but has been assessed with out-of-range index {}" .format(Expression(Type.list_of(Type.int()), [1, 2], ident="a"), 2, 2) in str(context.exception)) with self.assertRaises(CompileError) as context: generate_commands(""" main () {{ list[int] a <- [1, 2]; int b <- a[-1]; }} """.format(type)) self.assertTrue( "List {} has length {}, but has been assessed with out-of-range index {}" .format(Expression(Type.list_of(Type.int()), [1, 2], ident="a"), 2, -1) in str(context.exception))
def test_repeated_declare_and_assign_drone_constant_should_give_error( self): types = [ Type.int(), Type.decimal(), Type.string(), Type.boolean(), Type.vector(), Type.drone(), Type.list_of(Type.int()), Type.list_of(Type.list_of(Type.int())) ] for type in types: with self.assertRaises(CompileError) as context: generate_commands( """ main () {{ {} a; {} DRONE <- a; }} """.format(type.type_name, type.type_name), drone_config_map={"DRONE": DefaultDroneConfig()}) self.assertTrue( "Identifier DRONE already declared" in str(context.exception))
def test_nested_list(self): inner = Type.list_of(Type.int()) outer = Type.list_of(inner) self.assertEqual(ListType(Type.list_of(Type.int())), outer) self.assertEqual("list[list[int]]", outer.type_name) self.assertEqual([], outer.default_value) self.assertEqual(Type.list_of(Type.int()), outer.elem_type) self.assertEqual("list[list[int]]", str(outer))
def test_list(self): list_type = Type.list_of(Type.int()) self.assertEqual(Type.list_of(Type.int()), list_type) self.assertEqual(ListType(Type.int()), list_type) self.assertEqual("list[int]", list_type.type_name) self.assertEqual([], list_type.default_value) self.assertEqual(Type.int(), list_type.elem_type) self.assertEqual("list[int]", str(list_type))
def test_store(self): st = SymbolTable() st.store("a", Expression(Type.int(), 1)) self.assertTrue("a" in st) self.assertEqual(Type.int(), st.get_expression("a").type) self.assertEqual(1, st.get_expression("a").value) self.assertRaises(AssertionError, st.store, "a", Expression(Type.int(), 1))
def test_corrupted_type_should_not_affect_correct_type(self): int_type = Type.int() corrupted_type = Type.int() corrupted_type._type_name = "corrupted" self.assertNotEqual(int_type, corrupted_type) self.assertEqual("int", str(Type.int())) self.assertEqual(0, Type.int().default_value) self.assertEqual("corrupted", str(corrupted_type)) self.assertEqual(0, corrupted_type.default_value)
def test_eq(self): st1 = SymbolTable() st2 = SymbolTable() self.assertTrue(st1 == st2) st1.store("a", Expression(Type.int(), 0)) self.assertFalse(st1 == st2) st2.store("a", Expression(Type.int(), 0)) self.assertTrue(st1 == st2) self.assertNotEqual(SymbolTable(), None)
def test_return_in_main_should_give_error(self): types = [Type.int(), Type.decimal(), Type.string(), Type.boolean(), Type.vector(), Type.drone(), Type.list_of(Type.int()), Type.list_of(Type.decimal()), Type.list_of(Type.list_of(Type.int()))] for type in types: with self.assertRaises(CompileError) as context: generate_commands(""" main () {{ {} a; return a; }} """.format(type)) self.assertTrue("Cannot return in the Main function" in str(context.exception))
def test_define_functions_should_change_function_table(self): types = [Type.int(), Type.decimal(), Type.string(), Type.boolean(), Type.vector(), Type.drone(), Type.list_of(Type.int()), Type.list_of(Type.decimal()), Type.list_of(Type.list_of(Type.int()))] for type in types: actual = FunctionTable() generate_commands(""" function func() return {} {{}} procedure proc() {{}} main () {{}} """.format(type), function_table=actual) expected = FunctionTable() expected.store("func", Function("func", [], type, [])) expected.store("proc", Function("proc", [], None, [])) self.assertEqual(expected, actual)
def test_define_functions_with_parameter_should_change_function_table(self): types = [Type.int(), Type.decimal(), Type.string(), Type.boolean(), Type.vector(), Type.drone(), Type.list_of(Type.int()), Type.list_of(Type.decimal()), Type.list_of(Type.list_of(Type.int()))] for type1 in types: for type2 in types: actual = FunctionTable() generate_commands(""" function func({} a, {} b) return int {{}} procedure proc({} a, {} b) {{}} main () {{}} """.format(type1, type2, type1, type2), function_table=actual) expected = FunctionTable() expected.store("func", Function("func", [Parameter("a", type1), Parameter("b", type2)], Type.int(), [])) expected.store("proc", Function("proc", [Parameter("a", type1), Parameter("b", type2)], None, [])) self.assertEqual(expected, actual)
def test_return_not_exist_in_function_should_give_error(self): types = [Type.int(), Type.decimal(), Type.string(), Type.boolean(), Type.vector(), Type.drone(), Type.list_of(Type.int()), Type.list_of(Type.decimal()), Type.list_of(Type.list_of(Type.int()))] for type in types: with self.assertRaises(CompileError) as context: generate_commands(""" function func() return {} {{ }} main () {{ {} a <- func(); }} """.format(type, type)) self.assertTrue("Function func has returned type {}, but nothing is returned" .format(type) in str(context.exception))
def visitSize(self, ctx: xDroneParser.SizeContext) -> Expression: expr = self.visit(ctx.expr()) if not isinstance(expr.type, ListType): raise CompileError( "Expression {} should have type list, but is {}".format( expr, expr.type)) return Expression(Type.int(), len(expr.value))
def test_return_correct_type_in_function_should_return_correct_value(self): types = [Type.int(), Type.decimal(), Type.string(), Type.boolean(), Type.vector(), Type.drone(), Type.list_of(Type.int()), Type.list_of(Type.decimal()), Type.list_of(Type.list_of(Type.int()))] for type in types: actual = SymbolTable() generate_commands(""" function func() return {} {{ {} a; return a; }} main () {{ {} a <- func(); }} """.format(type, type, type), symbol_table=actual) expected = SymbolTable() expected.store("a", Expression(type, type.default_value, ident="a")) self.assertEqual(expected, actual)
def test_return_value_in_procedure_should_give_error(self): types = [Type.int(), Type.decimal(), Type.string(), Type.boolean(), Type.vector(), Type.drone(), Type.list_of(Type.int()), Type.list_of(Type.decimal()), Type.list_of(Type.list_of(Type.int()))] for type in types: with self.assertRaises(CompileError) as context: generate_commands(""" procedure proc() {{ {} a; return a; }} main () {{ proc(); }} """.format(type)) self.assertTrue("Procedure proc should not return anything, but {} is returned" .format(Expression(type, type.default_value, ident=None)) in str(context.exception))
def test_declare_and_assign_int_should_change_symbol_table(self): actual = SymbolTable() generate_commands(""" main () { int a <- 1; } """, symbol_table=actual) expected = SymbolTable() expected.store("a", Expression(Type.int(), 1, ident="a")) self.assertEqual(expected, actual)
def test_define_functions_with_duplicated_parameter_should_give_error(self): types = [Type.int(), Type.decimal(), Type.string(), Type.boolean(), Type.vector(), Type.drone(), Type.list_of(Type.int()), Type.list_of(Type.decimal()), Type.list_of(Type.list_of(Type.int()))] for type1 in types: for type2 in types: with self.assertRaises(CompileError) as context: generate_commands(""" function func({} a, {} a) return int {{}} main () {{}} """.format(type1, type2, type1, type2)) self.assertTrue("Parameter names are duplicated in ['a', 'a']" in str(context.exception)) with self.assertRaises(CompileError) as context: generate_commands(""" procedure proc({} a, {} a) {{}} main () {{}} """.format(type1, type2, type1, type2)) self.assertTrue("Parameter names are duplicated in ['a', 'a']" in str(context.exception))
def test_for_should_keep_updates_but_discard_new_variables(self): actual = SymbolTable() actual_commands = generate_commands(""" main () { int a <- 0; int i <- 1; for i from 1 to 5 { int b <- 1; a <- a + 1; } } """, symbol_table=actual) expected = SymbolTable() expected.store("a", Expression(Type.int(), 5, ident="a")) expected.store("i", Expression(Type.int(), 5, ident="i")) self.assertEqual(expected, actual) expected_commands = [] self.assertEqual(expected_commands, actual_commands)
def test_str(self): st = SymbolTable() st.store("a", Expression(Type.int(), 0)) st.store("b", Expression(Type.list_of(Type.boolean()), [True])) expected = "SymbolTable: {\n" + \ " a -> Expression: { type: int, value: 0, ident: None }\n" + \ " b -> Expression: { type: list[boolean], value: [True], ident: None }\n" + \ "}" self.assertEqual(expected, str(st))
def test_list_elem_expr_should_return_correct_value(self): actual = SymbolTable() generate_commands(""" main () { list[int] a <- [1, 2, 3]; int b <- a[0]; int c <- a[1]; int d <- a[2]; } """, symbol_table=actual) expected = SymbolTable() expected.store( "a", Expression(Type.list_of(Type.int()), [1, 2, 3], ident="a")) expected.store("b", Expression(Type.int(), 1, ident="b")) expected.store("c", Expression(Type.int(), 2, ident="c")) expected.store("d", Expression(Type.int(), 3, ident="d")) self.assertEqual(expected, actual)
def test_to_expression_should_return_correct_value(self): expressions = [ None, Expression(Type.int(), 1, "a"), Expression(Type.decimal(), 1.1, "a"), Expression(Type.boolean(), False, "a"), Expression(Type.vector(), [1.1, 2.2, -1.1], "a"), Expression(Type.drone(), Drone("DRONE", DefaultDroneConfig()), "a"), Expression(Type.list_of(Type.int()), [1, 2, 3, 4], "a"), Expression(Type.list_of(Type.int()), [], "a"), Expression(Type.list_of(Type.decimal()), [1.0, 2.0, 3.0, 4.0], "a"), Expression(Type.list_of(Type.list_of(Type.vector())), [[[1.1, 2.2, -1.1], [1, 2, -1]]], "a") ] for expression in expressions: self.assertEqual(expression, Identifier("a", expression).to_expression())
def test_declare_list_should_change_symbol_table(self): actual = SymbolTable() generate_commands(""" main () { list[int] a; } """, symbol_table=actual) expected = SymbolTable() expected.store("a", Expression(Type.list_of(Type.int()), [], ident="a")) self.assertEqual(expected, actual)
def test_str(self): self.assertEqual( "ListElem: { ident: a, " + "container: Expression: { type: list[int], value: [1, 2, 3, 4], ident: a }, " + "index: 0, expression: Expression: { type: int, value: 1, ident: a[0] } }", str( ListElem( "a", Expression(Type.list_of(Type.int()), [1, 2, 3, 4], "a"), 0)))