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_eq(self): vector_elems1 = [ None, VectorElem("a", Expression(Type.vector(), [1.0, 2.0, 3.0], "a"), 0), VectorElem("b", Expression(Type.vector(), [1.0, 2.0, 3.0], "b"), 0), VectorElem("a", Expression(Type.vector(), [2.0, 2.0, 3.0], "a"), 0), VectorElem("a", Expression(Type.vector(), [1.0, 2.0, 3.0], "a"), 1), VectorElem("b[1]", Expression(Type.vector(), [1.0, 2.0, 3.0], "a"), 0) ] vector_elems2 = [ None, VectorElem("a", Expression(Type.vector(), [1.0, 2.0, 3.0], "a"), 0), VectorElem("b", Expression(Type.vector(), [1.0, 2.0, 3.0], "b"), 0), VectorElem("a", Expression(Type.vector(), [2.0, 2.0, 3.0], "a"), 0), VectorElem("a", Expression(Type.vector(), [1.0, 2.0, 3.0], "a"), 1), VectorElem("b[1]", Expression(Type.vector(), [1.0, 2.0, 3.0], "a"), 0) ] for i in range(len(vector_elems1)): for j in range(len(vector_elems2)): if i == j: self.assertEqual(vector_elems1[i], vector_elems2[j]) else: self.assertNotEqual(vector_elems1[i], vector_elems2[j])
def visitNot(self, ctx: xDroneParser.NotContext) -> Expression: expr = self.visit(ctx.expr()) if expr.type != Type.boolean(): raise CompileError( "Expression {} should have type boolean, but is {}".format( expr, expr.type)) return Expression(Type.boolean(), not expr.value)
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_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_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 visitPlusMinus(self, ctx: xDroneParser.PlusMinusContext) -> Expression: expr1, expr2 = self.visit(ctx.expr(0)), self.visit(ctx.expr(1)) if self._is_int_or_decimal(expr1.type) and self._is_int_or_decimal( expr2.type): result_type, func = self._get_int_decimal_result_type( expr1.type, expr2.type) if ctx.PLUS(): result_value = func(expr1.value + expr2.value) else: # MINUS result_value = func(expr1.value - expr2.value) elif expr1.type == Type.vector() and expr2.type == Type.vector(): result_type = Type.vector() if ctx.PLUS(): result_value = [ e1 + e2 for e1, e2 in zip(expr1.value, expr2.value) ] else: # MINUS result_value = [ e1 - e2 for e1, e2 in zip(expr1.value, expr2.value) ] else: raise CompileError( "Expression {} and {} have wrong types to perform addition or subtraction" .format(expr1, expr2)) return Expression(result_type, result_value)
def visitMultiDivide(self, ctx: xDroneParser.MultiDivideContext) -> Expression: expr1, expr2 = self.visit(ctx.expr(0)), self.visit(ctx.expr(1)) if self._is_int_or_decimal(expr1.type) and self._is_int_or_decimal( expr2.type): result_type, func = self._get_int_decimal_result_type( expr1.type, expr2.type) if ctx.MULTI(): result_value = func(expr1.value * expr2.value) else: # DIV if expr2.value == 0: raise CompileError("Division by zero") result_value = func(expr1.value / expr2.value) elif self._is_int_or_decimal( expr1.type) and expr2.type == Type.vector(): result_type = Type.vector() if ctx.MULTI(): result_value = [expr1.value * e for e in expr2.value] else: # DIV raise CompileError( "Expression {} and {} have wrong types to perform multiplication or division" .format(expr1, expr2)) elif expr1.type == Type.vector() and self._is_int_or_decimal( expr2.type): result_type = Type.vector() if ctx.MULTI(): result_value = [e * expr2.value for e in expr1.value] else: # DIV result_value = [e / expr2.value for e in expr1.value] else: raise CompileError( "Expression {} and {} have wrong types to perform multiplication or division" .format(expr1, expr2)) return Expression(result_type, result_value)
def test_empty_list(self): empty_list = Type.empty_list() self.assertEqual(Type.empty_list(), empty_list) self.assertEqual(EmptyList(), empty_list) self.assertEqual("list[]", empty_list.type_name) self.assertEqual([], empty_list.default_value) self.assertEqual(Type("all", 0), empty_list.elem_type) self.assertEqual("list[]", str(empty_list))
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_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 visitList(self, ctx: xDroneParser.ListContext) -> Expression: exprs = [self.visit(expr) for expr in ctx.expr()] if len(exprs) == 0: return Expression(Type.empty_list(), []) if not all(e.type == exprs[0].type for e in exprs): raise CompileError( "Elements in list {} should have the same type".format( [str(e) for e in exprs])) return Expression(Type.list_of(exprs[0].type), [e.value for e in exprs])
def test_to_expression_should_return_correct_value(self): expression = Expression(Type.vector(), [1.0, 2.0, 3.0], "a") expected = Expression(Type.decimal(), 1.0) self.assertEqual(expected, VectorElem("a", expression, 0).to_expression()) expression = Expression(Type.vector(), [1.0, 2.0, 3.0], "b[1]") expected = Expression(Type.decimal(), 3.0) self.assertEqual(expected, VectorElem("b[1]", expression, 2).to_expression())
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)))
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_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 visitConcat(self, ctx: xDroneParser.ConcatContext) -> Expression: expr1, expr2 = self.visit(ctx.expr(0)), self.visit(ctx.expr(1)) if expr1.type != Type.string(): raise CompileError( "Expression {} should have type string, but is {}".format( expr1, expr1.type)) if expr2.type != Type.string(): raise CompileError( "Expression {} should have type string, but is {}".format( expr2, expr2.type)) return Expression(Type.string(), expr1.value + expr2.value)
def visitOr(self, ctx: xDroneParser.OrContext) -> Expression: expr1, expr2 = self.visit(ctx.expr(0)), self.visit(ctx.expr(1)) if expr1.type != Type.boolean(): raise CompileError( "Expression {} should have type boolean, but is {}".format( expr1, expr1.type)) if expr2.type != Type.boolean(): raise CompileError( "Expression {} should have type boolean, but is {}".format( expr2, expr2.type)) return Expression(Type.boolean(), expr1.value or expr2.value)
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_list_with_inconsistent_type_should_give_error(self): with self.assertRaises(CompileError) as context: generate_commands(""" main () { list[int] a <- [1, 1.0]; } """) self.assertTrue( "Elements in list {} should have the same type".format([ str(Expression(Type.int(), 1, None)), str(Expression(Type.decimal(), 1.0, None)) ]) in str(context.exception))
def test_list_remove_from_empty_list_should_give_error(self): with self.assertRaises(CompileError) as context: generate_commands(""" main () { list[int] a <- []; a.remove(); } """) self.assertTrue( "List {} has length {}, but has been removed at out-of-range index {}" .format(Expression(Type.list_of(Type.int()), [], ident="a"), 0, -1) in str(context.exception))
def visitVector(self, ctx: xDroneParser.VectorContext) -> Expression: expr1, expr2, expr3 = self.visit(ctx.expr(0)), self.visit( ctx.expr(1)), self.visit(ctx.expr(2)) for expr in [expr1, expr2, expr3]: if expr.type != Type.int() and expr.type != Type.decimal(): raise CompileError( "Expression {} should have type int or decimal, but is {}". format(expr, expr.type)) return Expression( Type.vector(), [float(expr1.value), float(expr2.value), float(expr3.value)])
def visitUp(self, ctx: xDroneParser.UpContext) -> None: exprs = [self.visit(expr) for expr in ctx.expr()] if ctx.DOT(): drone_expr, expr = exprs else: drone_expr, expr = None, exprs[0] if expr.type != Type.int() and expr.type != Type.decimal(): raise CompileError( "Expression {} should have type int or decimal, but is {}". format(expr, expr.type)) drone_name = self._get_drone_name(drone_expr) self._get_latest_commands().append( SingleDroneCommand(drone_name, Command.up(expr.value)))
def test_list_insert_to_empty_list_should_update_symbol_table(self): actual = SymbolTable() generate_commands(""" main () { list[int] a <- []; a.insert(0); } """, symbol_table=actual) expected = SymbolTable() expected.store("a", Expression(Type.list_of(Type.int()), [0], ident="a")) self.assertEqual(expected, actual)
def test_assign_list_elem_to_variable_out_of_bound_should_give_error(self): with self.assertRaises(CompileError) as context: generate_commands(""" main () { list[int] a <- []; a[0] <- 1; } """) self.assertTrue( "List {} has length 0, but has been assessed with out-of-range index 0" .format(Expression(Type.list_of(Type.int()), [], ident="a")) in str(context.exception))
def test_assign_ident_list_should_update_symbol_table(self): actual = SymbolTable() generate_commands(""" main () { list[int] a; a <- [1, -1, +1]; } """, symbol_table=actual) expected = SymbolTable() expected.store( "a", Expression(Type.list_of(Type.int()), [1, -1, 1], ident="a")) self.assertEqual(expected, actual)
def test_list_elem_assign_should_update_symbol_table_correctly(self): actual = SymbolTable() generate_commands(""" main () { list[int] a <- [1, 2, 3]; a[0] <- 4; } """, symbol_table=actual) expected = SymbolTable() expected.store( "a", Expression(Type.list_of(Type.int()), [4, 2, 3], ident="a")) self.assertEqual(expected, actual)
def test_list_remove_without_index_should_update_symbol_table(self): actual = SymbolTable() generate_commands(""" main () { list[int] a <- [1, 2, 3]; a.remove(); } """, symbol_table=actual) expected = SymbolTable() expected.store("a", Expression(Type.list_of(Type.int()), [1, 2], ident="a")) self.assertEqual(expected, actual)