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

check syntax no more explicitly need to call assertXXX #534

Merged
merged 5 commits into from
Aug 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
172 changes: 150 additions & 22 deletions jaclang/compiler/passes/main/pyast_gen_pass.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import ast as ast3
import textwrap
from dataclasses import dataclass
from typing import Optional, Sequence, TypeVar

import jaclang.compiler.absyntree as ast
Expand Down Expand Up @@ -2139,33 +2140,160 @@ 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.
@dataclass
class CheckNodeIsinstanceCallResult:
isit: bool = False
inst: ast3.AST | None = None
clss: ast3.AST | None = None

# 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
opty: ast.Token = expr.ops[0]

optype2fn = {
Tok.EE.name: "assertEqual",
Tok.NE.name: "assertNotEqual",
Tok.LT.name: "assertLess",
Tok.LTE.name: "assertLessEqual",
Tok.GT.name: "assertGreater",
Tok.GTE.name: "assertGreaterEqual",
Tok.KW_IN.name: "assertIn",
Tok.KW_NIN.name: "assertNotIn",
Tok.KW_IS.name: "assertIs",
Tok.KW_ISN.name: "assertIsNot",
}

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

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

# Override for <expr> is not None.
elif opty.name == Tok.KW_ISN 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, ast.UnaryExpr)
and isinstance(node.target.operand, ast.FuncCall)
and isinstance(node.target.operand, ast.UnaryExpr)
):
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,
func = node.target.target
if isinstance(func, ast.Name) and func.value == "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;
}
9 changes: 5 additions & 4 deletions jaclang/langserve/tests/test_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ def test_test_annex(self) -> None:
lsp.lsp._workspace = workspace
circle_file = uris.from_fs_path(self.fixture_abs_path("circle_pure.test.jac"))
lsp.deep_check(circle_file)
pos = lspt.Position(13, 29)
pos = lspt.Position(13, 21)
self.assertIn(
"shape_type: circle_pure.ShapeType",
lsp.get_hover_info(circle_file, pos).contents.value,
Expand Down Expand Up @@ -222,7 +222,7 @@ def test_sem_tokens(self) -> None:
),
(
"<JacSemTokenType.FUNCTION: 12>, <JacSemTokenModifier.DECLARATION: 1>,",
9,
6,
),
("<JacSemTokenType.METHOD: 13>, <JacSemTokenModifier.DECLARATION: 1>", 6),
("<JacSemTokenType.ENUM: 3>, <JacSemTokenModifier.DECLARATION: 1>,", 4),
Expand All @@ -232,6 +232,7 @@ def test_sem_tokens(self) -> None:
3,
),
]
print(str(sem_list))
for token_type, expected_count in expected_counts:
self.assertEqual(str(sem_list).count(token_type), expected_count)

Expand Down Expand Up @@ -343,8 +344,8 @@ def test_go_to_reference(self) -> None:
lsp.deep_check(circle_file)
test_cases = [
(47, 12, ["circle.jac:47:8-47:14", "69:8-69:14", "74:8-74:14"]),
(54, 66, ["54:62-54:76", "65:28-65:42"]),
(62, 14, ["65:49-65:62", "70:38-70:51"]),
(54, 66, ["54:62-54:76", "65:22-65:36"]),
(62, 14, ["65:43-65:56", "70:32-70:45"]),
]
for line, char, expected_refs in test_cases:
references = str(lsp.get_references(circle_file, lspt.Position(line, char)))
Expand Down
Loading
Loading