Skip to content
This repository has been archived by the owner on Sep 12, 2024. It is now read-only.

Commit

Permalink
check syntax no more explicitly need to call assertXXX
Browse files Browse the repository at this point in the history
  • Loading branch information
ThakeeNathees committed Jul 28, 2024
1 parent 5d3c07a commit 2ac3ae3
Show file tree
Hide file tree
Showing 12 changed files with 190 additions and 57 deletions.
6 changes: 3 additions & 3 deletions examples/manual_code/circle.jac
Original file line number Diff line number Diff line change
Expand Up @@ -63,15 +63,15 @@ with entry:__main__ {
glob expected_area = 78.53981633974483;

test calc_area {
check assertAlmostEqual(calculate_area(RAD), expected_area);
check almostEqual(calculate_area(RAD), expected_area);
}

test circle_area {
c = Circle(RAD);
check assertAlmostEqual(c.area(), expected_area);
check almostEqual(c.area(), expected_area);
}

test circle_type {
c = Circle(RAD);
check assertEqual(c.shape_type, ShapeType.CIRCLE);
check c.shape_type == ShapeType.CIRCLE;
}
6 changes: 3 additions & 3 deletions examples/manual_code/circle_clean_tests.jac
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ include:jac circle_clean;
glob expected_area = 78.53981633974483;

test {
check assertAlmostEqual(calculate_area(RAD), expected_area);
check almostEqual(calculate_area(RAD), expected_area);
}

test {
c = Circle(RAD);
check assertAlmostEqual(c.area(), expected_area);
check almostEqual(c.area(), expected_area);
}

test {
c = Circle(RAD);
check assertEqual(c.shape_type, ShapeType.CIRCLE);
check c.shape_type == ShapeType.CIRCLE;
}
6 changes: 3 additions & 3 deletions examples/manual_code/circle_pure.test.jac
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
glob expected_area = 78.53981633974483;

test a1 {
check assertAlmostEqual(calculate_area(RAD), expected_area);
check almostEqual(calculate_area(RAD), expected_area);
}

test a2 {
c = Circle(RAD);
check assertAlmostEqual(c.area(), expected_area);
check almostEqual(c.area(), expected_area);
}

test a3 {
c = Circle(RAD);
check assertEqual(c.shape_type, ShapeType.CIRCLE);
check c.shape_type == ShapeType.CIRCLE;
}
8 changes: 4 additions & 4 deletions examples/reference/check_statements.jac
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
glob a = 5, b = 2;

test test1 {
check assertAlmostEqual(a, 6);
check almostEqual(a, 6);
}

test test2 {
check assertTrue(a != b);
check a != b;
}

test test3 {
check assertIn("d", "abc");
check "d" in "abc";
}

test test4 {
check assertEqual(a - b, 3);
check a - b == 3;
}
6 changes: 3 additions & 3 deletions examples/reference/tests.jac
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
test test1 {
check assertAlmostEqual(4.99999, 4.99999);
check almostEqual(4.99999, 4.99999);
}

test test2 {
check assertEqual(5, 5);
check 5 == 5;
}

test test3 {
check assertIn("e", "qwerty");
check "e" in "qwerty";
}

with entry:__main__ {
Expand Down
175 changes: 154 additions & 21 deletions jaclang/compiler/passes/main/pyast_gen_pass.py
Original file line number Diff line number Diff line change
Expand Up @@ -2137,33 +2137,166 @@ def exit_check_stmt(self, node: ast.CheckStmt) -> None:
target: ExprType,
"""
# TODO: Here is the list of assertions which are not implemented instead a simpler version of them will work.
# ie. [] == [] will be assertEqual instead of assertListEqual. However I don't think this is needed since it can
# only detected if both operand are compile time literal list or type inferable.
#
# assertAlmostEqual
# assertNotAlmostEqual
# assertSequenceEqual
# assertListEqual
# assertTupleEqual
# assertSetEqual
# assertDictEqual
# assertCountEqual
# assertMultiLineEqual
# assertRaisesRegex
# assertWarnsRegex
# assertRegex
# assertNotRegex

# The return type "struct" for the bellow check_node_isinstance_call.
class CheckNodeIsinstanceCallResult:
def __init__(
self,
isit: bool = False,
inst: ast3.AST | None = None,
clss: ast3.AST | None = None,
) -> None:
self.isit: bool = isit
self.inst: ast3.AST | None = inst
self.clss: ast3.AST | None = clss

# This will check if a node is `isinstance(<expr>, <expr>)`, we're
# using a function because it's reusable to check not isinstance(<expr>, <expr>).
def check_node_isinstance_call(
node: ast.FuncCall,
) -> CheckNodeIsinstanceCallResult:

# Ensure the type of the FuncCall node is SubNodeList[Expr]
# since the type can be: Optional[SubNodeList[Expr | KWPair]].
if not (
node.params is not None
and len(node.params.items) == 2
and isinstance(node.params.items[0], ast.Expr)
and isinstance(node.params.items[1], ast.Expr)
):
return CheckNodeIsinstanceCallResult()

func = node.target.gen.py_ast[0]
if not (isinstance(func, ast3.Name) and func.id == "isinstance"):
return CheckNodeIsinstanceCallResult()

return CheckNodeIsinstanceCallResult(
True,
node.params.items[0].gen.py_ast[0],
node.params.items[1].gen.py_ast[0],
)

# By default the check expression will become assertTrue(<expr>), unless any pattern detected.
assert_func_name = "assertTrue"
assert_args_list = node.target.gen.py_ast

# Compare operations. Note that We're only considering the compare
# operation with a single operation ie. a < b < c is ignored here.
if (
isinstance(node.target, ast.CompareExpr)
and isinstance(node.target.gen.py_ast[0], ast3.Compare)
and len(node.target.ops) == 1
):
expr: ast.CompareExpr = node.target
pyexpr: ast3.Compare = node.target.gen.py_ast[0]
op_ty: type[ast3.cmpop] = type(pyexpr.ops[0])

optype2fn = {
ast3.Eq: "assertEqual",
ast3.NotEq: "assertNotEqual",
ast3.Lt: "assertLess",
ast3.LtE: "assertLessEqual",
ast3.Gt: "assertGreater",
ast3.GtE: "assertGreaterEqual",
ast3.In: "assertIn",
ast3.NotIn: "assertNotIn",
ast3.Is: "assertIs",
ast3.IsNot: "assertIsNot",
}

if op_ty in optype2fn:
assert_func_name = optype2fn[op_ty]
assert_args_list = [
expr.left.gen.py_ast[0],
expr.rights[0].gen.py_ast[0],
]

# Override for <expr> is None.
if op_ty == ast3.Is and isinstance(expr.rights[0], ast.Null):
assert_func_name = "assertIsNone"
assert_args_list.pop()

# Override for <expr> is not None.
elif op_ty == ast3.IsNot and isinstance(expr.rights[0], ast.Null):
assert_func_name = "assertIsNotNone"
assert_args_list.pop()

# Check if 'isinstance' is called.
elif isinstance(node.target, ast.FuncCall) and isinstance(
node.target.gen.py_ast[0], ast3.Call
):
res = check_node_isinstance_call(node.target)
if res.isit:
# These assertions will make mypy happy.
assert isinstance(res.inst, ast3.AST)
assert isinstance(res.clss, ast3.AST)
assert_func_name = "assertIsInstance"
assert_args_list = [res.inst, res.clss]

# Check if 'not isinstance(<expr>, <expr>)' is called.
elif (
isinstance(node.target, ast.UnaryExpr)
and isinstance(node.target.gen.py_ast[0], ast3.UnaryOp)
and isinstance(node.target.operand, ast.FuncCall)
and isinstance(node.target.operand.gen.py_ast[0], ast3.UnaryOp)
):
res = check_node_isinstance_call(node.target.operand)
if res.isit:
# These assertions will make mypy happy.
assert isinstance(res.inst, ast3.AST)
assert isinstance(res.clss, ast3.AST)
assert_func_name = "assertIsNotInstance"
assert_args_list = [res.inst, res.clss]

# NOTE That the almost equal is NOT a builtin function of jaclang and won't work outside of the
# check statement. And we're hacking the node here. Not sure if this is a hacky workaround to support
# the almost equal functionality (snice there is no almost equal operator in jac and never needed ig.).

# Check if 'almostEqual' is called.
if isinstance(node.target, ast.FuncCall) and isinstance(
node.target.gen.py_ast[0], ast3.Call
):
func = node.target.target.gen.py_ast[0]
if isinstance(func, ast3.Name):
new_func: ast3.expr = self.sync(
ast3.Attribute(
value=self.sync(ast3.Name(id="_jac_check", ctx=ast3.Load())),
attr=func.id,
ctx=ast3.Load(),
)
)
node.target.gen.py_ast[0].func = new_func
node.gen.py_ast = [
self.sync(
ast3.Expr(
value=node.target.gen.py_ast[0],
)
)
]
return
self.error(
"For now, check statements must be function calls "
"in the style of assertTrue(), assertEqual(), etc.",
node,
if isinstance(func, ast3.Name) and func.id == "almostEqual":
assert_func_name = "assertAlmostEqual"
assert_args_list = []
if node.target.params is not None:
for param in node.target.params.items:
assert_args_list.append(param.gen.py_ast[0])

# assert_func_expr = "_jac_check.assertXXX"
assert_func_expr: ast3.Attribute = self.sync(
ast3.Attribute(
value=self.sync(ast3.Name(id="_jac_check", ctx=ast3.Load())),
attr=assert_func_name,
ctx=ast3.Load(),
)
)

# assert_call_expr = "(_jac_check.assertXXX)(args)"
assert_call_expr: ast3.Call = self.sync(
ast3.Call(func=assert_func_expr, args=assert_args_list, keywords=[])
)

node.gen.py_ast = [self.sync(ast3.Expr(assert_call_expr))]

def exit_ctrl_stmt(self, node: ast.CtrlStmt) -> None:
"""Sub objects.
Expand Down
6 changes: 3 additions & 3 deletions jaclang/langserve/tests/fixtures/circle.jac
Original file line number Diff line number Diff line change
Expand Up @@ -63,15 +63,15 @@ with entry:__main__ {
glob expected_area = 78.53981633974483;

test calc_area {
check assertAlmostEqual(calculate_area(RAD), expected_area);
check almostEqual(calculate_area(RAD), expected_area);
}

test circle_area {
c = Circle(RAD);
check assertAlmostEqual(c.area(), expected_area);
check almostEqual(c.area(), expected_area);
}

test circle_type {
c = Circle(RAD);
check assertEqual(c.shape_type, ShapeType.CIRCLE);
check c.shape_type == ShapeType.CIRCLE;
}
6 changes: 3 additions & 3 deletions jaclang/langserve/tests/fixtures/circle_err.jac
Original file line number Diff line number Diff line change
Expand Up @@ -59,15 +59,15 @@ print(f"Area of a {c.shape_type.value} with radius {RAD} using class: {c.area()}
glob expected_area = 78.53981633974483;

test calc_area {
check assertAlmostEqual(calculate_area(RAD), expected_area);
check almostEqual(calculate_area(RAD), expected_area);
}

test circle_area {
c = Circle(RAD);
check assertAlmostEqual(c.area(), expected_area);
check almostEqual(c.area(), expected_area);
}

test circle_type {
c = Circle(RAD);
check assertEqual(c.shape_type, ShapeType.CIRCLE);
check c.shape_type == ShapeType.CIRCLE;
}
6 changes: 3 additions & 3 deletions jaclang/langserve/tests/fixtures/circle_pure.test.jac
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
glob expected_area = 78.53981633974483;

test a1 {
check assertAlmostEqual(calculate_area(RAD), expected_area);
check almostEqual(calculate_area(RAD), expected_area);
}

test a2 {
c = Circle(RAD);
check assertAlmostEqual(c.area(), expected_area);
check almostEqual(c.area(), expected_area);
}

test a3 {
c = Circle(RAD);
check assertEqual(c.shape_type, ShapeType.CIRCLE);
check c.shape_type == ShapeType.CIRCLE;
}
6 changes: 3 additions & 3 deletions jaclang/tests/fixtures/abc.jac
Original file line number Diff line number Diff line change
Expand Up @@ -63,15 +63,15 @@ with entry:__main__ {
glob expected_area = 78.53981633974483;

test calc_area {
check assertAlmostEqual(calculate_area(RAD), expected_area);
check almostEqual(calculate_area(RAD), expected_area);
}

test circle_area {
c = Circle(RAD);
check assertAlmostEqual(c.area(), expected_area);
check almostEqual(c.area(), expected_area);
}

test circle_type {
c = Circle(RAD);
check assertEqual(c.shape_type, ShapeType.CIRCLE);
check c.shape_type == ShapeType.CIRCLE;
}
8 changes: 4 additions & 4 deletions jaclang/tests/fixtures/maxfail_run_test.jac
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
glob x = 5, y = 2;

test a {
check assertAlmostEqual(5, x);
check almostEqual(5, x);
}

test b {
check assertIn("l", "llm");
check "l" in "llm";
}

test c {
check assertEqual(x - y, 3);
check x - y == 3;
}

test d {
check assertEqual(1, 2);
check 1 == 2;
}
Loading

0 comments on commit 2ac3ae3

Please sign in to comment.