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

Commit

Permalink
Merge branch 'main' into triple_quoted_string_formatter
Browse files Browse the repository at this point in the history
  • Loading branch information
AshishMahendra committed Aug 12, 2024
2 parents f191f2e + 0bc1f46 commit 27122a4
Show file tree
Hide file tree
Showing 39 changed files with 541 additions and 155 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
4 changes: 3 additions & 1 deletion jaclang/cli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from jaclang.plugin.feature import JacCmd as Cmd
from jaclang.plugin.feature import JacFeature as Jac
from jaclang.runtimelib.constructs import Architype
from jaclang.runtimelib.machine import JacProgram
from jaclang.utils.helpers import debugger as db
from jaclang.utils.lang_tools import AstTool

Expand Down Expand Up @@ -104,12 +105,13 @@ def run(
elif filename.endswith(".jir"):
with open(filename, "rb") as f:
ir = pickle.load(f)
jac_program = JacProgram(mod_bundle=ir, bytecode=None)
Jac.context().jac_machine.attach_program(jac_program)
ret_module = jac_import(
target=mod,
base_path=base,
cachable=cache,
override_name="__main__" if main else None,
mod_bundle=ir,
)
if ret_module is None:
loaded_mod = None
Expand Down
17 changes: 17 additions & 0 deletions jaclang/compiler/absyntree.py
Original file line number Diff line number Diff line change
Expand Up @@ -1529,6 +1529,23 @@ def is_static(self) -> bool:
and self.parent.decl_link.is_static
)

@property
def is_in_py_class(self) -> bool:
"""Check if the ability belongs to a class."""
is_archi = self.find_parent_of_type(Architype)
is_class = is_archi is not None and is_archi.arch_type.name == Tok.KW_CLASS

return (
isinstance(self.parent, Ability)
and self.parent.is_method is not None
and is_class
) or (
isinstance(self.parent, AbilityDef)
and isinstance(self.parent.decl_link, Ability)
and self.parent.decl_link.is_method
and is_class
)


class EventSignature(AstSemStrNode):
"""EventSignature node type for Jac Ast."""
Expand Down
185 changes: 151 additions & 34 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 @@ -631,17 +632,6 @@ def exit_import(self, node: ast.Import) -> None:
),
)
),
self.sync(
ast3.keyword(
arg="mod_bundle",
value=self.sync(
ast3.Name(
id="__name__",
ctx=ast3.Load(),
)
),
)
),
self.sync(
ast3.keyword(
arg="lng",
Expand Down Expand Up @@ -1594,7 +1584,7 @@ def exit_func_signature(self, node: ast.FuncSignature) -> None:
"""
params = (
[self.sync(ast3.arg(arg="self", annotation=None))]
if node.is_method and not node.is_static
if node.is_method and not node.is_static and not node.is_in_py_class
else []
)
vararg = None
Expand Down Expand Up @@ -2139,33 +2129,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
Loading

0 comments on commit 27122a4

Please sign in to comment.