From a7196d6baba447e8fc5b78fb8c2957418b3f9572 Mon Sep 17 00:00:00 2001 From: Thakee Nathees Date: Sat, 27 Jul 2024 00:52:31 +0530 Subject: [PATCH 01/33] check syntax no more explicitly need to call assertXXX --- examples/manual_code/circle.jac | 6 +- examples/manual_code/circle_clean_tests.jac | 6 +- examples/manual_code/circle_pure.test.jac | 6 +- examples/reference/check_statements.jac | 8 +- examples/reference/tests.jac | 6 +- .../compiler/passes/main/pyast_gen_pass.py | 175 +++++++++++++++--- jaclang/langserve/tests/fixtures/circle.jac | 6 +- .../langserve/tests/fixtures/circle_err.jac | 6 +- .../tests/fixtures/circle_pure.test.jac | 6 +- jaclang/tests/fixtures/abc.jac | 6 +- jaclang/tests/fixtures/maxfail_run_test.jac | 8 +- jaclang/tests/fixtures/run_test.jac | 8 +- 12 files changed, 190 insertions(+), 57 deletions(-) diff --git a/examples/manual_code/circle.jac b/examples/manual_code/circle.jac index e0dc0c365..755fab2bf 100644 --- a/examples/manual_code/circle.jac +++ b/examples/manual_code/circle.jac @@ -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; } diff --git a/examples/manual_code/circle_clean_tests.jac b/examples/manual_code/circle_clean_tests.jac index 66fcc4282..c9626eede 100644 --- a/examples/manual_code/circle_clean_tests.jac +++ b/examples/manual_code/circle_clean_tests.jac @@ -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; } diff --git a/examples/manual_code/circle_pure.test.jac b/examples/manual_code/circle_pure.test.jac index 175f67c61..c30518578 100644 --- a/examples/manual_code/circle_pure.test.jac +++ b/examples/manual_code/circle_pure.test.jac @@ -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; } diff --git a/examples/reference/check_statements.jac b/examples/reference/check_statements.jac index f0750c1bc..e6d6ca33c 100644 --- a/examples/reference/check_statements.jac +++ b/examples/reference/check_statements.jac @@ -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; } \ No newline at end of file diff --git a/examples/reference/tests.jac b/examples/reference/tests.jac index 9e6ecd26f..996ab07a8 100644 --- a/examples/reference/tests.jac +++ b/examples/reference/tests.jac @@ -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__ { diff --git a/jaclang/compiler/passes/main/pyast_gen_pass.py b/jaclang/compiler/passes/main/pyast_gen_pass.py index 1f668cd4b..f418bebb2 100644 --- a/jaclang/compiler/passes/main/pyast_gen_pass.py +++ b/jaclang/compiler/passes/main/pyast_gen_pass.py @@ -2139,33 +2139,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(, )`, we're + # using a function because it's reusable to check not isinstance(, ). + 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(), 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 is None. + if op_ty == ast3.Is and isinstance(expr.rights[0], ast.Null): + assert_func_name = "assertIsNone" + assert_args_list.pop() + + # Override for 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(, )' 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. diff --git a/jaclang/langserve/tests/fixtures/circle.jac b/jaclang/langserve/tests/fixtures/circle.jac index e0dc0c365..755fab2bf 100644 --- a/jaclang/langserve/tests/fixtures/circle.jac +++ b/jaclang/langserve/tests/fixtures/circle.jac @@ -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; } diff --git a/jaclang/langserve/tests/fixtures/circle_err.jac b/jaclang/langserve/tests/fixtures/circle_err.jac index 5b6e04880..29736a7c0 100644 --- a/jaclang/langserve/tests/fixtures/circle_err.jac +++ b/jaclang/langserve/tests/fixtures/circle_err.jac @@ -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; } diff --git a/jaclang/langserve/tests/fixtures/circle_pure.test.jac b/jaclang/langserve/tests/fixtures/circle_pure.test.jac index 175f67c61..c30518578 100644 --- a/jaclang/langserve/tests/fixtures/circle_pure.test.jac +++ b/jaclang/langserve/tests/fixtures/circle_pure.test.jac @@ -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; } diff --git a/jaclang/tests/fixtures/abc.jac b/jaclang/tests/fixtures/abc.jac index e0dc0c365..755fab2bf 100644 --- a/jaclang/tests/fixtures/abc.jac +++ b/jaclang/tests/fixtures/abc.jac @@ -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; } diff --git a/jaclang/tests/fixtures/maxfail_run_test.jac b/jaclang/tests/fixtures/maxfail_run_test.jac index f41b9a1e9..19fc69d14 100644 --- a/jaclang/tests/fixtures/maxfail_run_test.jac +++ b/jaclang/tests/fixtures/maxfail_run_test.jac @@ -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; } diff --git a/jaclang/tests/fixtures/run_test.jac b/jaclang/tests/fixtures/run_test.jac index 9bf2512d8..f883aa6e2 100644 --- a/jaclang/tests/fixtures/run_test.jac +++ b/jaclang/tests/fixtures/run_test.jac @@ -1,17 +1,17 @@ glob a = 5, b = 2; test t1 { - check assertAlmostEqual(a, 6); + check almostEqual(a, 6); } test t2 { - check assertTrue(a != b); + check a != b; } test t3 { - check assertIn("d", "abc"); + check "d" in "abc"; } test t4 { - check assertEqual(a - b, 3); + check a - b == 3; } From 16fa5bf9c71369276d6678e0716130c904be36b7 Mon Sep 17 00:00:00 2001 From: Thakee Nathees Date: Mon, 29 Jul 2024 23:22:44 +0530 Subject: [PATCH 02/33] fixes for the failed build --- jaclang/langserve/tests/test_server.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/jaclang/langserve/tests/test_server.py b/jaclang/langserve/tests/test_server.py index f2214d6f9..b9edb9381 100644 --- a/jaclang/langserve/tests/test_server.py +++ b/jaclang/langserve/tests/test_server.py @@ -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, @@ -222,7 +222,7 @@ def test_sem_tokens(self) -> None: ), ( ", ,", - 9, + 6, ), (", ", 6), (", ,", 4), @@ -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) @@ -288,8 +289,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))) From 4dc564919bbfdb326c59acaff11ab071a722e942 Mon Sep 17 00:00:00 2001 From: Thakee Nathees Date: Tue, 30 Jul 2024 14:17:37 +0530 Subject: [PATCH 03/33] runtime errors will dump the stack trace now --- jaclang/runtimelib/importer.py | 10 +++++--- jaclang/utils/helpers.py | 45 ++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 4 deletions(-) diff --git a/jaclang/runtimelib/importer.py b/jaclang/runtimelib/importer.py index 2389cd2bc..5471ef59d 100644 --- a/jaclang/runtimelib/importer.py +++ b/jaclang/runtimelib/importer.py @@ -15,6 +15,7 @@ from jaclang.compiler.constant import Constants as Con from jaclang.runtimelib.machine import JacMachine from jaclang.runtimelib.utils import sys_path_context +from jaclang.utils.helpers import dump_traceback from jaclang.utils.log import logging logger = logging.getLogger(__name__) @@ -169,9 +170,10 @@ def load_jac_mod_as_item( exec(codeobj, new_module.__dict__) return getattr(new_module, name, new_module) except ImportError as e: - logger.error( - f"Failed to load {name} from {jac_file_path} in {module.__name__}: {str(e)}" - ) + logger.error(dump_traceback(e)) + # logger.error( + # f"Failed to load {name} from {jac_file_path} in {module.__name__}: {str(e)}" + # ) return None @@ -403,7 +405,7 @@ def run_import( try: exec(codeobj, module.__dict__) except Exception as e: - logger.error(f"Error while importing {spec.full_target}: {e}") + logger.error(dump_traceback(e)) raise e import_return = ImportReturn(module, unique_loaded_items, self) if spec.items: diff --git a/jaclang/utils/helpers.py b/jaclang/utils/helpers.py index 84a465d03..77b4229a9 100644 --- a/jaclang/utils/helpers.py +++ b/jaclang/utils/helpers.py @@ -5,6 +5,7 @@ import os import pdb import re +from traceback import TracebackException def pascal_to_snake(pascal_string: str) -> str: @@ -137,6 +138,50 @@ def is_standard_lib_module(module_path: str) -> bool: return os.path.isfile(file_path) or os.path.isdir(direc_path) +def dump_traceback(e: Exception) -> str: + """Dump the stack frames of the exception.""" + trace_dump = "" + + # Utility function to get the error line char offset. + def byte_offset_to_char_offset(string: str, offset: int) -> int: + return len(string.encode("utf-8")[:offset].decode("utf-8", errors="replace")) + + tb = TracebackException(type(e), e, e.__traceback__, limit=None, compact=True) + trace_dump += f"Error: {str(e)}" + + # The first frame is the call the to the above `exec` function, not usefull to the enduser, + # and Make the most recent call first. + tb.stack.pop(0) + tb.stack.reverse() + + # FIXME: should be some settings, we should replace to ensure the anchors length match. + dump_tab_width = 4 + + for idx, frame in enumerate(tb.stack): + func_signature = frame.name + ("()" if frame.name.isidentifier() else "") + + # Pretty print the most recent call's location. + if idx == 0 and (frame.line and frame.line.strip() != ""): + line_o = frame._original_line.rstrip() # type: ignore [attr-defined] + line_s = frame.line.rstrip() if frame.line else "" + stripped_chars = len(line_o) - len(line_s) + trace_dump += f'\n{" " * (dump_tab_width * 2)}{line_s}' + if frame.colno is not None and frame.end_colno is not None: + off_start = byte_offset_to_char_offset(line_o, frame.colno) + off_end = byte_offset_to_char_offset(line_o, frame.end_colno) + + # A bunch of caret '^' characters under the error location. + anchors = (" " * (off_start - stripped_chars - 1)) + "^" * len( + line_o[off_start:off_end].replace("\t", " " * dump_tab_width) + ) + + trace_dump += f'\n{" " * (dump_tab_width * 2)}{anchors}' + + trace_dump += f'\n{" " * dump_tab_width}at {func_signature} {frame.filename}:{frame.lineno}' + + return trace_dump + + class Jdb(pdb.Pdb): """Jac debugger.""" From d88cf98829b3e50776aeae870a44e8df98d6759e Mon Sep 17 00:00:00 2001 From: Thakee Nathees Date: Tue, 30 Jul 2024 20:38:56 +0530 Subject: [PATCH 04/33] runtime error dump test cases added --- jaclang/tests/fixtures/err_runtime.jac | 15 ++++++++++++++ jaclang/tests/test_cli.py | 27 ++++++++++++++++++++++++++ jaclang/utils/test.py | 9 +++++++++ 3 files changed, 51 insertions(+) create mode 100644 jaclang/tests/fixtures/err_runtime.jac diff --git a/jaclang/tests/fixtures/err_runtime.jac b/jaclang/tests/fixtures/err_runtime.jac new file mode 100644 index 000000000..662d0d1ea --- /dev/null +++ b/jaclang/tests/fixtures/err_runtime.jac @@ -0,0 +1,15 @@ + +can bar(some_list:list) { + invalid_index = 4; + print(some_list[invalid_index]); # This should fail. +} + + +can foo() { + bar([0, 1, 2, 3]); +} + + +with entry { + foo(); +} diff --git a/jaclang/tests/test_cli.py b/jaclang/tests/test_cli.py index 18bebb52c..66a1269bf 100644 --- a/jaclang/tests/test_cli.py +++ b/jaclang/tests/test_cli.py @@ -49,6 +49,33 @@ def test_jac_cli_alert_based_err(self) -> None: # print(stdout_value) self.assertIn("Error", stdout_value) + def test_jac_cli_alert_based_runtime_err(self) -> None: + """Basic test for pass.""" + captured_output = io.StringIO() + sys.stdout = captured_output + sys.stderr = captured_output + + try: + cli.run(self.fixture_abs_path("err_runtime.jac")) + except Exception as e: + print(f"Error: {e}") + + sys.stdout = sys.__stdout__ + sys.stderr = sys.__stderr__ + + expected_stdout_values = ( + "Error: list index out of range", + " print(some_list[invalid_index]);", + " ^^^^^^^^^^^^^^^^^^^^^^^^", + " at bar() ", + " at foo() ", + " at ", + ) + + logger_capture = "\n".join([rec.message for rec in self.caplog.records]) + for exp in expected_stdout_values: + self.assertIn(exp, logger_capture) + def test_jac_impl_err(self) -> None: """Basic test for pass.""" if "jaclang.tests.fixtures.err" in sys.modules: diff --git a/jaclang/utils/test.py b/jaclang/utils/test.py index 0606646c6..d4532f172 100644 --- a/jaclang/utils/test.py +++ b/jaclang/utils/test.py @@ -5,15 +5,24 @@ from typing import Callable, Optional from unittest import TestCase as _TestCase +from _pytest.logging import LogCaptureFixture import jaclang from jaclang.compiler.passes import Pass from jaclang.utils.helpers import get_ast_nodes_as_snake_case as ast_snakes +import pytest + class TestCase(_TestCase): """Base test case for Jaseci.""" + # Reference: https://stackoverflow.com/a/50375022 + @pytest.fixture(autouse=True) + def inject_fixtures(self, caplog: LogCaptureFixture) -> None: + """Store the logger capture records within the tests.""" + self.caplog = caplog + def setUp(self) -> None: """Set up test case.""" return super().setUp() From 469b624015e8b3593ed194c83cef9b979a1dc715 Mon Sep 17 00:00:00 2001 From: Thakee Nathees Date: Thu, 1 Aug 2024 20:35:54 +0530 Subject: [PATCH 05/33] vscode debugging support using debugpy --- support/vscode_ext/jac/.vscode/launch.json | 5 ++++- support/vscode_ext/jac/package.json | 20 +++++++++++++++++ support/vscode_ext/jac/src/extension.ts | 26 +++++++++++++++++++++- 3 files changed, 49 insertions(+), 2 deletions(-) diff --git a/support/vscode_ext/jac/.vscode/launch.json b/support/vscode_ext/jac/.vscode/launch.json index fa862b4cf..eb8436f4a 100644 --- a/support/vscode_ext/jac/.vscode/launch.json +++ b/support/vscode_ext/jac/.vscode/launch.json @@ -11,7 +11,10 @@ "outFiles": [ "${workspaceFolder}/out/**/*.js" ], - "preLaunchTask": "npm: compile" + "preLaunchTask": "npm: compile", + "env": { + "PATH": "${env:PATH}" + } } ] } \ No newline at end of file diff --git a/support/vscode_ext/jac/package.json b/support/vscode_ext/jac/package.json index bbae3e138..bdfe4d226 100644 --- a/support/vscode_ext/jac/package.json +++ b/support/vscode_ext/jac/package.json @@ -17,6 +17,7 @@ }, "categories": [ "Programming Languages", + "Debuggers", "Linters", "Formatters", "Snippets", @@ -27,6 +28,25 @@ "vscode": "^1.89.0" }, "contributes": { + + "debuggers": [ + { + "type": "jacdebug", + "label": "Jac Debug", + + "initialConfigurations": [ + { + "type": "debugpy", + "request": "launch", + "name": "Run a jac file", + "program": "${command:extension.jaclang-extension.getJacPath}", + "args" : "run ${file}" + } + ] + + } + ], + "languages": [ { "id": "jac", diff --git a/support/vscode_ext/jac/src/extension.ts b/support/vscode_ext/jac/src/extension.ts index fada225f2..f1d780cf4 100644 --- a/support/vscode_ext/jac/src/extension.ts +++ b/support/vscode_ext/jac/src/extension.ts @@ -34,7 +34,7 @@ function getVenvEnvironment(): string | undefined { export function activate(context: vscode.ExtensionContext) { const condaJac = getCondaEnvironment(); const venvJac = getVenvEnvironment(); - const jacCommand = condaJac ? condaJac : (venvJac ? venvJac : 'jac'); + const jacCommand = condaJac || venvJac || 'jac'; let serverOptions: ServerOptions = { run: { command: jacCommand, args: ["lsp"] }, @@ -59,6 +59,30 @@ export function activate(context: vscode.ExtensionContext) { vscode.window.showErrorMessage('Failed to start Jac Language Server: ' + error.message); console.error('Failed to start Jac Language Server: ', error); }); + + // Find and return the jac executable's absolute path. + context.subscriptions.push(vscode.commands.registerCommand('extension.jaclang-extension.getJacPath', config => { + + const programName = "jac.exe"; // FIXME: I don't know what it would be on other systems. + + const paths = process.env.PATH.split(path.delimiter); + for (const dir of paths) { + console.log(dir); + const fullPath = path.join(dir, programName); + try { + fs.accessSync(fullPath, fs.constants.X_OK); // Check if file exists and is executable + console.log(`Found ${programName} at: ${fullPath}`); + return fullPath; + } catch (err) { + // File doesn't exist or isn't executable in this directory + } + } + + const err_msg = `Couldn't find ${programName} in the PATH.`; + console.error(err_msg); + vscode.window.showErrorMessage(err_msg); + return null; + })); } export function deactivate(): Thenable | undefined { From 4fddda89552c5de1f5d5fcb748e132223b3f37d1 Mon Sep 17 00:00:00 2001 From: Ashish Mahendra Date: Fri, 2 Aug 2024 11:03:27 +0000 Subject: [PATCH 06/33] Importer: mod_bundle now only belog to machine --- jaclang/cli/cli.py | 4 +++- jaclang/compiler/passes/main/pyast_gen_pass.py | 11 ----------- jaclang/plugin/default.py | 6 ------ jaclang/plugin/feature.py | 3 --- jaclang/plugin/spec.py | 3 --- jaclang/runtimelib/context.py | 6 +++++- jaclang/runtimelib/importer.py | 2 -- jaclang/runtimelib/machine.py | 11 ++--------- 8 files changed, 10 insertions(+), 36 deletions(-) diff --git a/jaclang/cli/cli.py b/jaclang/cli/cli.py index ce2921125..788f468fc 100644 --- a/jaclang/cli/cli.py +++ b/jaclang/cli/cli.py @@ -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 @@ -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 diff --git a/jaclang/compiler/passes/main/pyast_gen_pass.py b/jaclang/compiler/passes/main/pyast_gen_pass.py index 1f668cd4b..90b760496 100644 --- a/jaclang/compiler/passes/main/pyast_gen_pass.py +++ b/jaclang/compiler/passes/main/pyast_gen_pass.py @@ -631,17 +631,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", diff --git a/jaclang/plugin/default.py b/jaclang/plugin/default.py index 2b10b5c23..7b98836fe 100644 --- a/jaclang/plugin/default.py +++ b/jaclang/plugin/default.py @@ -12,7 +12,6 @@ from functools import wraps from typing import Any, Callable, Optional, Type, Union -from jaclang.compiler.absyntree import Module from jaclang.compiler.constant import EdgeDir, colors from jaclang.compiler.semtable import SemInfo, SemRegistry, SemScope from jaclang.runtimelib.constructs import ( @@ -33,7 +32,6 @@ exec_context, ) from jaclang.runtimelib.importer import ImportPathSpec, JacImporter, PythonImporter -from jaclang.runtimelib.machine import JacProgram from jaclang.runtimelib.utils import traverse_graph from jaclang.plugin.feature import JacFeature as Jac # noqa: I100 from jaclang.plugin.spec import P, T @@ -247,7 +245,6 @@ def jac_import( cachable: bool, mdl_alias: Optional[str], override_name: Optional[str], - mod_bundle: Optional[Module | str], lng: Optional[str], items: Optional[dict[str, Union[str, Optional[str]]]], reload_module: Optional[bool], @@ -263,9 +260,6 @@ def jac_import( lng, items, ) - if not Jac.context().jac_machine.jac_program: - jac_program = JacProgram(mod_bundle, {}) - Jac.context().jac_machine.attach_program(jac_program) if lng == "py": import_result = PythonImporter(Jac.context().jac_machine).run_import(spec) else: diff --git a/jaclang/plugin/feature.py b/jaclang/plugin/feature.py index dc22eb83c..fe2469232 100644 --- a/jaclang/plugin/feature.py +++ b/jaclang/plugin/feature.py @@ -5,7 +5,6 @@ import types from typing import Any, Callable, Optional, Type, TypeAlias, Union -from jaclang.compiler.absyntree import Module from jaclang.plugin.default import ExecutionContext from jaclang.plugin.spec import JacBuiltin, JacCmdSpec, JacFeatureSpec, P, T from jaclang.runtimelib.constructs import ( @@ -109,7 +108,6 @@ def jac_import( cachable: bool = True, mdl_alias: Optional[str] = None, override_name: Optional[str] = None, - mod_bundle: Optional[Module | str] = None, lng: Optional[str] = "jac", items: Optional[dict[str, Union[str, Optional[str]]]] = None, reload_module: Optional[bool] = False, @@ -122,7 +120,6 @@ def jac_import( cachable=cachable, mdl_alias=mdl_alias, override_name=override_name, - mod_bundle=mod_bundle, lng=lng, items=items, reload_module=reload_module, diff --git a/jaclang/plugin/spec.py b/jaclang/plugin/spec.py index 928049bb3..4c42c8c0b 100644 --- a/jaclang/plugin/spec.py +++ b/jaclang/plugin/spec.py @@ -14,8 +14,6 @@ Union, ) -from jaclang.compiler.absyntree import Module - if TYPE_CHECKING: from jaclang.runtimelib.constructs import EdgeArchitype, NodeArchitype from jaclang.plugin.default import ( @@ -117,7 +115,6 @@ def jac_import( cachable: bool, mdl_alias: Optional[str], override_name: Optional[str], - mod_bundle: Optional[Module | str], lng: Optional[str], items: Optional[dict[str, Union[str, Optional[str]]]], reload_module: Optional[bool], diff --git a/jaclang/runtimelib/context.py b/jaclang/runtimelib/context.py index 1c80e33a0..6e5ee38da 100644 --- a/jaclang/runtimelib/context.py +++ b/jaclang/runtimelib/context.py @@ -8,7 +8,7 @@ from uuid import UUID from .architype import Architype, Root -from .machine import JacMachine +from .machine import JacMachine, JacProgram from .memory import Memory, ShelveStorage @@ -24,6 +24,8 @@ def __init__(self) -> None: self.mem = ShelveStorage() self.root = None self.jac_machine = JacMachine() + jac_program = JacProgram(mod_bundle=None, bytecode=None) + self.jac_machine.attach_program(jac_program) def init_memory(self, base_path: str = "", session: str = "") -> None: """Initialize memory.""" @@ -32,6 +34,8 @@ def init_memory(self, base_path: str = "", session: str = "") -> None: else: self.mem = Memory() self.jac_machine = JacMachine(base_path) + jac_program = JacProgram(mod_bundle=None, bytecode=None) + self.jac_machine.attach_program(jac_program) def get_root(self) -> Root: """Get the root object.""" diff --git a/jaclang/runtimelib/importer.py b/jaclang/runtimelib/importer.py index f73be6884..98b47c907 100644 --- a/jaclang/runtimelib/importer.py +++ b/jaclang/runtimelib/importer.py @@ -277,7 +277,6 @@ def handle_directory( module.__name__ = module_name module.__path__ = [full_mod_path] module.__file__ = None - module.__dict__["__jac_mod_bundle__"] = self.jac_machine.get_mod_bundle() if module_name not in sys.modules: sys.modules[module_name] = module @@ -293,7 +292,6 @@ def create_jac_py_module( module = types.ModuleType(module_name) module.__file__ = full_target module.__name__ = module_name - module.__dict__["__jac_mod_bundle__"] = self.jac_machine.get_mod_bundle() if package_path: base_path = full_target.split(package_path.replace(".", path.sep))[0] parts = package_path.split(".") diff --git a/jaclang/runtimelib/machine.py b/jaclang/runtimelib/machine.py index 9e417d54f..3d006c27f 100644 --- a/jaclang/runtimelib/machine.py +++ b/jaclang/runtimelib/machine.py @@ -2,7 +2,6 @@ import marshal import os -import sys import types from typing import Optional @@ -59,16 +58,10 @@ class JacProgram: """Class to hold the mod_bundle and bytecode for Jac modules.""" def __init__( - self, mod_bundle: Optional[Module | str], bytecode: Optional[dict[str, bytes]] + self, mod_bundle: Optional[Module], bytecode: Optional[dict[str, bytes]] ) -> None: """Initialize the JacProgram object.""" - self.mod_bundle = ( - sys.modules[mod_bundle].__jac_mod_bundle__ - if isinstance(mod_bundle, str) - and mod_bundle in sys.modules - and "__jac_mod_bundle__" in sys.modules[mod_bundle].__dict__ - else None - ) + self.mod_bundle = mod_bundle self.bytecode = bytecode or {} def get_bytecode( From 2e951c2cba285ce15d0edbaec80e25d8cc1b458a Mon Sep 17 00:00:00 2001 From: Thakee Nathees Date: Fri, 2 Aug 2024 19:21:23 +0530 Subject: [PATCH 07/33] check node jac first --- .../compiler/passes/main/pyast_gen_pass.py | 53 +++++++++---------- 1 file changed, 24 insertions(+), 29 deletions(-) diff --git a/jaclang/compiler/passes/main/pyast_gen_pass.py b/jaclang/compiler/passes/main/pyast_gen_pass.py index f418bebb2..b30777b56 100644 --- a/jaclang/compiler/passes/main/pyast_gen_pass.py +++ b/jaclang/compiler/passes/main/pyast_gen_pass.py @@ -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 @@ -2158,16 +2159,11 @@ def exit_check_stmt(self, node: ast.CheckStmt) -> None: # assertNotRegex # The return type "struct" for the bellow check_node_isinstance_call. + @dataclass 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 + isit: bool = False + inst: ast3.AST | None = None + clss: ast3.AST | None = None # This will check if a node is `isinstance(, )`, we're # using a function because it's reusable to check not isinstance(, ). @@ -2207,36 +2203,35 @@ def check_node_isinstance_call( 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]) + opty: ast.Token = expr.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", + 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 op_ty in optype2fn: - assert_func_name = optype2fn[op_ty] + 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 is None. - if op_ty == ast3.Is and isinstance(expr.rights[0], ast.Null): + if opty.name == Tok.KW_IS and isinstance(expr.rights[0], ast.Null): assert_func_name = "assertIsNone" assert_args_list.pop() # Override for is not None. - elif op_ty == ast3.IsNot and isinstance(expr.rights[0], ast.Null): + elif opty.name == Tok.KW_ISN and isinstance(expr.rights[0], ast.Null): assert_func_name = "assertIsNotNone" assert_args_list.pop() @@ -2255,9 +2250,9 @@ def check_node_isinstance_call( # Check if 'not isinstance(, )' is called. elif ( isinstance(node.target, ast.UnaryExpr) - and isinstance(node.target.gen.py_ast[0], ast3.UnaryOp) + and isinstance(node.target, ast.UnaryExpr) and isinstance(node.target.operand, ast.FuncCall) - and isinstance(node.target.operand.gen.py_ast[0], ast3.UnaryOp) + and isinstance(node.target.operand, ast.UnaryExpr) ): res = check_node_isinstance_call(node.target.operand) if res.isit: @@ -2275,8 +2270,8 @@ def check_node_isinstance_call( 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) and func.id == "almostEqual": + 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: From a4b51c2741d7eacdcc80b7f1caad83c721c69486 Mon Sep 17 00:00:00 2001 From: Thakee Nathees Date: Mon, 5 Aug 2024 13:13:39 +0530 Subject: [PATCH 08/33] changed the jac compiler executable name depend on platform. --- support/vscode_ext/jac/src/extension.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/support/vscode_ext/jac/src/extension.ts b/support/vscode_ext/jac/src/extension.ts index f1d780cf4..360729946 100644 --- a/support/vscode_ext/jac/src/extension.ts +++ b/support/vscode_ext/jac/src/extension.ts @@ -63,7 +63,7 @@ export function activate(context: vscode.ExtensionContext) { // Find and return the jac executable's absolute path. context.subscriptions.push(vscode.commands.registerCommand('extension.jaclang-extension.getJacPath', config => { - const programName = "jac.exe"; // FIXME: I don't know what it would be on other systems. + const programName = (process.platform === 'win32') ? "jac.exe" : "jac"; const paths = process.env.PATH.split(path.delimiter); for (const dir of paths) { From f39bc39866140c5802e94dc3a12d05b60049aa72 Mon Sep 17 00:00:00 2001 From: Ashish Mahendra Date: Mon, 5 Aug 2024 12:19:19 +0000 Subject: [PATCH 09/33] Formatter: Architype.jac space fix --- jaclang/compiler/passes/tool/jac_formatter_pass.py | 9 ++++++--- .../general_format_checks/architype_test.jac | 13 +++++++++++++ .../fixtures/myca_formatted_code/simple_walker.jac | 2 ++ .../try_block_and_walker_spawn_and_fstrings.jac | 1 + 4 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/architype_test.jac diff --git a/jaclang/compiler/passes/tool/jac_formatter_pass.py b/jaclang/compiler/passes/tool/jac_formatter_pass.py index 8eeb3dadc..769662a75 100644 --- a/jaclang/compiler/passes/tool/jac_formatter_pass.py +++ b/jaclang/compiler/passes/tool/jac_formatter_pass.py @@ -129,11 +129,13 @@ def exit_module(self, node: ast.Module) -> None: if isinstance(i, ast.Import): self.emit_ln(node, i.gen.jac) else: - if isinstance(last_element, ast.Import): + if isinstance( + last_element, (ast.Import, ast.Architype) + ) and not node.gen.jac.endswith("\n\n"): self.emit_ln(node, "") + self.emit_ln(node, i.gen.jac) - if not node.gen.jac.endswith("\n"): - self.emit_ln(node, "") + if counter <= len(node.body) - 1: if ( isinstance(i, ast.Ability) @@ -144,6 +146,7 @@ def exit_module(self, node: ast.Module) -> None: and len(node.body[counter].kid[-1].kid) == 2 and len(node.body[counter - 1].kid[-1].kid) == 2 ) + and node.gen.jac.endswith("\n") ): self.emit(node, "") else: diff --git a/jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/architype_test.jac b/jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/architype_test.jac new file mode 100644 index 000000000..5dccdc022 --- /dev/null +++ b/jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/architype_test.jac @@ -0,0 +1,13 @@ +class Animal {} + +obj Domesticated {} + +@print_base_classes +node Pet :Animal, Domesticated: {} + +walker Person :Animal: {} + +walker Feeder :Person: {} + +@print_base_classes +walker Zoologist :Feeder: {} diff --git a/jaclang/compiler/passes/tool/tests/fixtures/myca_formatted_code/simple_walker.jac b/jaclang/compiler/passes/tool/tests/fixtures/myca_formatted_code/simple_walker.jac index c8f16f22a..8bad992be 100644 --- a/jaclang/compiler/passes/tool/tests/fixtures/myca_formatted_code/simple_walker.jac +++ b/jaclang/compiler/passes/tool/tests/fixtures/myca_formatted_code/simple_walker.jac @@ -1,5 +1,7 @@ node day {} + edge future {} + edge parent {} walker get_last_active_day { diff --git a/jaclang/compiler/passes/tool/tests/fixtures/myca_formatted_code/try_block_and_walker_spawn_and_fstrings.jac b/jaclang/compiler/passes/tool/tests/fixtures/myca_formatted_code/try_block_and_walker_spawn_and_fstrings.jac index 60dea3216..d7bff3ddb 100644 --- a/jaclang/compiler/passes/tool/tests/fixtures/myca_formatted_code/try_block_and_walker_spawn_and_fstrings.jac +++ b/jaclang/compiler/passes/tool/tests/fixtures/myca_formatted_code/try_block_and_walker_spawn_and_fstrings.jac @@ -1,4 +1,5 @@ node user_root {} + walker update_graph {} walker cycle { From 71cf634f4446f1c635e6ba08e5fa6cfbccaaac49 Mon Sep 17 00:00:00 2001 From: Ashish Mahendra Date: Mon, 5 Aug 2024 14:00:08 +0000 Subject: [PATCH 10/33] fix file --- .../myca_formatted_code/ability_impl_long_comprehension.jac | 1 + 1 file changed, 1 insertion(+) diff --git a/jaclang/compiler/passes/tool/tests/fixtures/myca_formatted_code/ability_impl_long_comprehension.jac b/jaclang/compiler/passes/tool/tests/fixtures/myca_formatted_code/ability_impl_long_comprehension.jac index fa072087c..afd864b7c 100644 --- a/jaclang/compiler/passes/tool/tests/fixtures/myca_formatted_code/ability_impl_long_comprehension.jac +++ b/jaclang/compiler/passes/tool/tests/fixtures/myca_formatted_code/ability_impl_long_comprehension.jac @@ -1,4 +1,5 @@ node user_root {} + node day {} walker update_graph { From 7184384e1d7171d68094bec75336dbe6929b3761 Mon Sep 17 00:00:00 2001 From: Thakee Nathees Date: Mon, 5 Aug 2024 21:35:06 +0530 Subject: [PATCH 11/33] Readme for the debugger plugin added. --- support/vscode_ext/jac/README.md | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/support/vscode_ext/jac/README.md b/support/vscode_ext/jac/README.md index 51c8f51cf..a9c8f1261 100644 --- a/support/vscode_ext/jac/README.md +++ b/support/vscode_ext/jac/README.md @@ -6,6 +6,38 @@ This extension provides support for the [Jac](https://doc.jaseci.org) programmin All that is needed is to have jac installed (i.e. `pip install jaclang`) and the `jac` command line tool present in your environment. +# Debugging Jaclang + +Make sure that you've installed [python extention for vscode](https://marketplace.visualstudio.com/items?itemName=ms-python.python) This is needed since the jaclang +produce python bytecode which can be debugged with `debugpy`. + +Now to debug a jac file a launch.json file needs to created with the debug configurations. This can simply generated with: +1. Goto the debug options at the left pannel. +2. click "create a launch.json file" +3. Select `Jac Debug` Option + +This will create a debug configration to run and debug a single jac file, Here is the default sinppit, modify it as your +preference to debug different types of applications or modules. + +```json +{ + "version": "0.2.0", + "configurations": [ + { + "type": "debugpy", + "request": "launch", + "name": "Run a jac file", + "program": "${command:extension.jaclang-extension.getJacPath}", + "args": "run ${file}" + } + ] +} +``` + +This animated GIF bellow will demonstrate on the steps discuessed above. + +![Animation](https://github.com/user-attachments/assets/dcf808a4-b54e-4079-9948-9e88e6b0559e) + # Features - Code completion From 6a1205a5f0ad576a62e8daa987a0f98d7f7a4c77 Mon Sep 17 00:00:00 2001 From: Ashish Mahendra Date: Tue, 6 Aug 2024 11:14:19 +0000 Subject: [PATCH 12/33] fixc formatting --- jaclang/compiler/passes/tool/jac_formatter_pass.py | 12 ++++++++---- .../ability_impl_long_comprehension.jac | 1 - .../fixtures/myca_formatted_code/simple_walker.jac | 2 -- .../try_block_and_walker_spawn_and_fstrings.jac | 1 - 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/jaclang/compiler/passes/tool/jac_formatter_pass.py b/jaclang/compiler/passes/tool/jac_formatter_pass.py index 769662a75..6043d5709 100644 --- a/jaclang/compiler/passes/tool/jac_formatter_pass.py +++ b/jaclang/compiler/passes/tool/jac_formatter_pass.py @@ -129,11 +129,15 @@ def exit_module(self, node: ast.Module) -> None: if isinstance(i, ast.Import): self.emit_ln(node, i.gen.jac) else: - if isinstance( - last_element, (ast.Import, ast.Architype) - ) and not node.gen.jac.endswith("\n\n"): + if isinstance(last_element, ast.Import): + self.emit_ln(node, "") + if last_element and ( + isinstance(i, ast.Architype) + and isinstance(last_element, ast.Architype) + and i.loc.first_line - last_element.loc.last_line == 2 + and not node.gen.jac.endswith("\n\n") + ): self.emit_ln(node, "") - self.emit_ln(node, i.gen.jac) if counter <= len(node.body) - 1: diff --git a/jaclang/compiler/passes/tool/tests/fixtures/myca_formatted_code/ability_impl_long_comprehension.jac b/jaclang/compiler/passes/tool/tests/fixtures/myca_formatted_code/ability_impl_long_comprehension.jac index afd864b7c..fa072087c 100644 --- a/jaclang/compiler/passes/tool/tests/fixtures/myca_formatted_code/ability_impl_long_comprehension.jac +++ b/jaclang/compiler/passes/tool/tests/fixtures/myca_formatted_code/ability_impl_long_comprehension.jac @@ -1,5 +1,4 @@ node user_root {} - node day {} walker update_graph { diff --git a/jaclang/compiler/passes/tool/tests/fixtures/myca_formatted_code/simple_walker.jac b/jaclang/compiler/passes/tool/tests/fixtures/myca_formatted_code/simple_walker.jac index 8bad992be..c8f16f22a 100644 --- a/jaclang/compiler/passes/tool/tests/fixtures/myca_formatted_code/simple_walker.jac +++ b/jaclang/compiler/passes/tool/tests/fixtures/myca_formatted_code/simple_walker.jac @@ -1,7 +1,5 @@ node day {} - edge future {} - edge parent {} walker get_last_active_day { diff --git a/jaclang/compiler/passes/tool/tests/fixtures/myca_formatted_code/try_block_and_walker_spawn_and_fstrings.jac b/jaclang/compiler/passes/tool/tests/fixtures/myca_formatted_code/try_block_and_walker_spawn_and_fstrings.jac index d7bff3ddb..60dea3216 100644 --- a/jaclang/compiler/passes/tool/tests/fixtures/myca_formatted_code/try_block_and_walker_spawn_and_fstrings.jac +++ b/jaclang/compiler/passes/tool/tests/fixtures/myca_formatted_code/try_block_and_walker_spawn_and_fstrings.jac @@ -1,5 +1,4 @@ node user_root {} - walker update_graph {} walker cycle { From e7a57b056bf101df5c165f8af857d2adfefb20e6 Mon Sep 17 00:00:00 2001 From: Ashish Mahendra Date: Tue, 6 Aug 2024 16:10:55 +0000 Subject: [PATCH 13/33] Formatter: Reduce Complexity of Sub_Node_List --- .../passes/tool/jac_formatter_pass.py | 38 ++++--------------- 1 file changed, 8 insertions(+), 30 deletions(-) diff --git a/jaclang/compiler/passes/tool/jac_formatter_pass.py b/jaclang/compiler/passes/tool/jac_formatter_pass.py index 8eeb3dadc..06539332f 100644 --- a/jaclang/compiler/passes/tool/jac_formatter_pass.py +++ b/jaclang/compiler/passes/tool/jac_formatter_pass.py @@ -240,25 +240,18 @@ def exit_sub_node_list(self, node: ast.SubNodeList) -> None: if stmt.name == Tok.LBRACE: self.emit(node, f" {stmt.value}") elif stmt.name == Tok.RBRACE: - if self.indent_level > 0: - self.indent_level -= 1 + self.indent_level = max(0, self.indent_level - 1) if stmt.parent and stmt.parent.gen.jac.strip() == "{": self.emit(node, f"{stmt.value}") else: - if not (node.gen.jac).endswith("\n"): + if not node.gen.jac.endswith("\n"): self.emit_ln(node, "") - self.emit(node, f"{stmt.value}") - else: - self.emit(node, f"{stmt.value}") + self.emit(node, f"{stmt.value}") elif isinstance(stmt, ast.CommentToken): if stmt.is_inline: if isinstance(prev_token, ast.Semi) or ( isinstance(prev_token, ast.Token) - and prev_token.name - in [ - Tok.LBRACE, - Tok.RBRACE, - ] + and prev_token.name in [Tok.LBRACE, Tok.RBRACE] ): self.indent_level -= 1 self.emit(node, f" {stmt.gen.jac}") @@ -306,25 +299,10 @@ def exit_sub_node_list(self, node: ast.SubNodeList) -> None: elif isinstance(prev_token, ast.Ability) and isinstance( stmt, (ast.Ability, ast.AbilityDef) ): - if not isinstance(prev_token.kid[-1], ast.CommentToken) and ( - stmt.body and not isinstance(stmt.body, ast.FuncCall) - ): - self.indent_level -= 1 - self.emit_ln(node, "") - self.indent_level += 1 - self.emit(node, f"{stmt.gen.jac}") - elif stmt.body and isinstance( - stmt.body, (ast.FuncCall, ast.EventSignature) - ): - self.indent_level -= 1 - self.emit_ln(node, "") - self.indent_level += 1 - self.emit(node, stmt.gen.jac) - else: - self.indent_level -= 1 - self.emit_ln(node, "") - self.indent_level += 1 - self.emit(node, f"{stmt.gen.jac}") + self.indent_level -= 1 + self.emit_ln(node, "") + self.indent_level += 1 + self.emit(node, stmt.gen.jac) else: if prev_token and prev_token.gen.jac.strip() == "{": self.emit_ln(node, "") From dcd87ee04a74711dd6eb50770a419ec403c8ed47 Mon Sep 17 00:00:00 2001 From: Ashish Mahendra Date: Tue, 6 Aug 2024 17:38:07 +0000 Subject: [PATCH 14/33] reduced redundancy --- .../compiler/passes/tool/jac_formatter_pass.py | 15 ++++++--------- .../passes/tool/tests/fixtures/corelib_fmt.jac | 1 - 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/jaclang/compiler/passes/tool/jac_formatter_pass.py b/jaclang/compiler/passes/tool/jac_formatter_pass.py index 06539332f..d96481c4e 100644 --- a/jaclang/compiler/passes/tool/jac_formatter_pass.py +++ b/jaclang/compiler/passes/tool/jac_formatter_pass.py @@ -288,21 +288,18 @@ def exit_sub_node_list(self, node: ast.SubNodeList) -> None: continue elif isinstance(stmt, ast.Semi): self.emit(node, stmt.gen.jac) - elif isinstance(prev_token, (ast.HasVar, ast.ArchHas)) and not isinstance( - stmt, (ast.HasVar, ast.ArchHas) + elif ( + isinstance(prev_token, (ast.HasVar, ast.ArchHas)) + and not isinstance(stmt, (ast.HasVar, ast.ArchHas)) + ) or ( + isinstance(prev_token, ast.Ability) + and isinstance(stmt, (ast.Ability, ast.AbilityDef)) ): if not isinstance(prev_token.kid[-1], ast.CommentToken): self.indent_level -= 1 self.emit_ln(node, "") self.indent_level += 1 self.emit(node, stmt.gen.jac) - elif isinstance(prev_token, ast.Ability) and isinstance( - stmt, (ast.Ability, ast.AbilityDef) - ): - self.indent_level -= 1 - self.emit_ln(node, "") - self.indent_level += 1 - self.emit(node, stmt.gen.jac) else: if prev_token and prev_token.gen.jac.strip() == "{": self.emit_ln(node, "") diff --git a/jaclang/compiler/passes/tool/tests/fixtures/corelib_fmt.jac b/jaclang/compiler/passes/tool/tests/fixtures/corelib_fmt.jac index b6d181ee8..9b79435ae 100644 --- a/jaclang/compiler/passes/tool/tests/fixtures/corelib_fmt.jac +++ b/jaclang/compiler/passes/tool/tests/fixtures/corelib_fmt.jac @@ -16,7 +16,6 @@ obj Memory { can save_obj(caller_id: UUID, item: Element); can del_obj(caller_id: UUID, item: Element); #* Utility Functions *# - can get_object_distribution -> dict; can get_mem_size -> float; } From bbc8c0066bf1aef91f22958522f9c9ebea3c541e Mon Sep 17 00:00:00 2001 From: marsninja Date: Tue, 6 Aug 2024 22:45:42 -0400 Subject: [PATCH 15/33] hot test --- jaclang/compiler/passes/main/tests/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jaclang/compiler/passes/main/tests/__init__.py b/jaclang/compiler/passes/main/tests/__init__.py index fe84665b0..3c9399dfb 100644 --- a/jaclang/compiler/passes/main/tests/__init__.py +++ b/jaclang/compiler/passes/main/tests/__init__.py @@ -1 +1 @@ -"""Tests for Jac passes.""" +"""Various tests for Jac passes.""" From 0e12056cc8bab5e02a61570957718bde9a7bd00c Mon Sep 17 00:00:00 2001 From: Jayanaka-98 Date: Thu, 8 Aug 2024 00:29:04 +0530 Subject: [PATCH 16/33] Semstrings for abilities --- jaclang/compiler/passes/main/registry_pass.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/jaclang/compiler/passes/main/registry_pass.py b/jaclang/compiler/passes/main/registry_pass.py index 4db7e1ccf..d7e697661 100644 --- a/jaclang/compiler/passes/main/registry_pass.py +++ b/jaclang/compiler/passes/main/registry_pass.py @@ -84,6 +84,17 @@ def exit_has_var(self, node: ast.HasVar) -> None: if len(self.modules_visited) and self.modules_visited[-1].registry: self.modules_visited[-1].registry.add(scope, seminfo) + def exit_ability(self, node: ast.Ability) -> None: + """Save ability information.""" + scope = get_sem_scope(node) + seminfo = SemInfo( + node.name_ref.sym_name, + None, + node.semstr.lit_value if node.semstr else "", + ) + if len(self.modules_visited) and self.modules_visited[-1].registry: + self.modules_visited[-1].registry.add(scope, seminfo) + def exit_assignment(self, node: ast.Assignment) -> None: """Save assignment information.""" if node.aug_op: From 933c1142ca8a61ede0f40f6df57b1d7eace1dbaf Mon Sep 17 00:00:00 2001 From: kugesan1105 Date: Fri, 9 Aug 2024 02:06:48 +0530 Subject: [PATCH 17/33] feat: Add support for class methods in Jac compiler The code changes introduce support for class methods in the Jac compiler. --- jaclang/compiler/absyntree.py | 17 ++++++++ .../compiler/passes/main/pyast_gen_pass.py | 2 +- jaclang/tests/fixtures/cls_method.jac | 41 +++++++++++++++++++ jaclang/tests/test_language.py | 11 +++++ 4 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 jaclang/tests/fixtures/cls_method.jac diff --git a/jaclang/compiler/absyntree.py b/jaclang/compiler/absyntree.py index 352ba841f..ad7f19dd2 100644 --- a/jaclang/compiler/absyntree.py +++ b/jaclang/compiler/absyntree.py @@ -1529,6 +1529,23 @@ def is_static(self) -> bool: and self.parent.decl_link.is_static ) + @property + def is_class_method(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.""" diff --git a/jaclang/compiler/passes/main/pyast_gen_pass.py b/jaclang/compiler/passes/main/pyast_gen_pass.py index b30777b56..0ef5a19cd 100644 --- a/jaclang/compiler/passes/main/pyast_gen_pass.py +++ b/jaclang/compiler/passes/main/pyast_gen_pass.py @@ -1595,7 +1595,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_class_method else [] ) vararg = None diff --git a/jaclang/tests/fixtures/cls_method.jac b/jaclang/tests/fixtures/cls_method.jac new file mode 100644 index 000000000..8ef3549cf --- /dev/null +++ b/jaclang/tests/fixtures/cls_method.jac @@ -0,0 +1,41 @@ +"""Test file for class method.""" + +class MyClass { + can simple_method() -> str { + return "Hello, World1!"; + } + + @classmethod + can my_method(cls: any) -> str { + x = cls.__name__; + print(x); + return "Hello, World2!"; + } +} + +with entry { + a = MyClass.simple_method(); + b = MyClass.my_method(); + print(a, b); +} + +class MyClass2 { + can Ability_1(self: any) -> str; + @classmethod + can Ability_2(cls: any) -> str; +} + +:obj:MyClass2:can:Ability_1 +(self: any) { + return "Hello, World!"; +} + +:obj:MyClass2:can:Ability_2 { + return "Hello, World22!"; +} + +with entry { + a = MyClass2().Ability_1(); + b = MyClass2.Ability_2(); + print(a, b); +} diff --git a/jaclang/tests/test_language.py b/jaclang/tests/test_language.py index 48658b92c..028add315 100644 --- a/jaclang/tests/test_language.py +++ b/jaclang/tests/test_language.py @@ -903,3 +903,14 @@ def test_double_import_exec(self) -> None: stdout_value = captured_output.getvalue() self.assertEqual(stdout_value.count("Hello World!"), 1) self.assertIn("im still here", stdout_value) + + def test_cls_method(self) -> None: + """Test class method output.""" + captured_output = io.StringIO() + sys.stdout = captured_output + jac_import("cls_method", base_path=self.fixture_abs_path("./")) + sys.stdout = sys.__stdout__ + stdout_value = captured_output.getvalue().split("\n") + self.assertEqual("MyClass", stdout_value[0]) + self.assertEqual("Hello, World1! Hello, World2!", stdout_value[1]) + self.assertEqual("Hello, World! Hello, World22!", stdout_value[2]) From d2f84895edb16dc8d9fdd89430af782f7ff9d8f8 Mon Sep 17 00:00:00 2001 From: kugesan1105 Date: Fri, 9 Aug 2024 02:21:16 +0530 Subject: [PATCH 18/33] test updated --- jaclang/tests/fixtures/simple_archs.jac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jaclang/tests/fixtures/simple_archs.jac b/jaclang/tests/fixtures/simple_archs.jac index cbf40898a..2b7dc3203 100644 --- a/jaclang/tests/fixtures/simple_archs.jac +++ b/jaclang/tests/fixtures/simple_archs.jac @@ -15,7 +15,7 @@ class SimpleClass { var2: int, var3: int = 0; - can init { + can init(self:any) { print(self.var3); } } From 959d318afe71f1c5a273f9f1ec9745af78db98b3 Mon Sep 17 00:00:00 2001 From: kugesan1105 Date: Fri, 9 Aug 2024 03:12:47 +0530 Subject: [PATCH 19/33] Refactor class method signatures in Jac compiler --- jaclang/tests/fixtures/cls_method.jac | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/jaclang/tests/fixtures/cls_method.jac b/jaclang/tests/fixtures/cls_method.jac index 8ef3549cf..388f7b7ae 100644 --- a/jaclang/tests/fixtures/cls_method.jac +++ b/jaclang/tests/fixtures/cls_method.jac @@ -6,7 +6,7 @@ class MyClass { } @classmethod - can my_method(cls: any) -> str { + can my_method(cls: Type[MyClass]) -> str { x = cls.__name__; print(x); return "Hello, World2!"; @@ -20,13 +20,13 @@ with entry { } class MyClass2 { - can Ability_1(self: any) -> str; + can Ability_1(self: MyClass2) -> str; @classmethod - can Ability_2(cls: any) -> str; + can Ability_2(cls: Type[MyClass2]) -> str; } :obj:MyClass2:can:Ability_1 -(self: any) { +(self: MyClass2) -> str { return "Hello, World!"; } From 0c2074cf4cf1a98edbfca5887f571ba6c41ef9fb Mon Sep 17 00:00:00 2001 From: Thakee Nathees Date: Fri, 9 Aug 2024 19:21:35 +0530 Subject: [PATCH 20/33] python extention was added as a dependency for debugger --- support/vscode_ext/jac/README.md | 5 ++--- support/vscode_ext/jac/package.json | 3 ++- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/support/vscode_ext/jac/README.md b/support/vscode_ext/jac/README.md index a9c8f1261..a75d27953 100644 --- a/support/vscode_ext/jac/README.md +++ b/support/vscode_ext/jac/README.md @@ -8,10 +8,9 @@ All that is needed is to have jac installed (i.e. `pip install jaclang`) and the # Debugging Jaclang -Make sure that you've installed [python extention for vscode](https://marketplace.visualstudio.com/items?itemName=ms-python.python) This is needed since the jaclang -produce python bytecode which can be debugged with `debugpy`. +Note that it'll install [python extention for vscode](https://marketplace.visualstudio.com/items?itemName=ms-python.python) as a dependecy as it is needed to debug the python bytecode that jaclang produce. -Now to debug a jac file a launch.json file needs to created with the debug configurations. This can simply generated with: +To debug a jac file a launch.json file needs to created with the debug configurations. This can simply generated with: 1. Goto the debug options at the left pannel. 2. click "create a launch.json file" 3. Select `Jac Debug` Option diff --git a/support/vscode_ext/jac/package.json b/support/vscode_ext/jac/package.json index bdfe4d226..692db693f 100644 --- a/support/vscode_ext/jac/package.json +++ b/support/vscode_ext/jac/package.json @@ -82,7 +82,8 @@ "vsce-package": "mkdir build && vsce package -o build/jac.vsix" }, "dependencies": { - "vscode-languageclient": "^9.0.1" + "vscode-languageclient": "^9.0.1", + "ms-python.python": "^2024.12.2" }, "devDependencies": { "@types/node": "^20.14.1", From f8f2d3f3ad26df82c706770515a4b01aea0825b5 Mon Sep 17 00:00:00 2001 From: marsninja Date: Sat, 10 Aug 2024 08:57:48 -0400 Subject: [PATCH 21/33] test: fixed fixture type of self and name change --- jaclang/compiler/absyntree.py | 2 +- jaclang/compiler/passes/main/pyast_gen_pass.py | 2 +- jaclang/tests/fixtures/simple_archs.jac | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/jaclang/compiler/absyntree.py b/jaclang/compiler/absyntree.py index ad7f19dd2..4b1be5909 100644 --- a/jaclang/compiler/absyntree.py +++ b/jaclang/compiler/absyntree.py @@ -1530,7 +1530,7 @@ def is_static(self) -> bool: ) @property - def is_class_method(self) -> bool: + 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 diff --git a/jaclang/compiler/passes/main/pyast_gen_pass.py b/jaclang/compiler/passes/main/pyast_gen_pass.py index 0ef5a19cd..ae47711d2 100644 --- a/jaclang/compiler/passes/main/pyast_gen_pass.py +++ b/jaclang/compiler/passes/main/pyast_gen_pass.py @@ -1595,7 +1595,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 and not node.is_class_method + if node.is_method and not node.is_static and not node.is_in_py_class else [] ) vararg = None diff --git a/jaclang/tests/fixtures/simple_archs.jac b/jaclang/tests/fixtures/simple_archs.jac index 2b7dc3203..0ffa93fc6 100644 --- a/jaclang/tests/fixtures/simple_archs.jac +++ b/jaclang/tests/fixtures/simple_archs.jac @@ -15,7 +15,7 @@ class SimpleClass { var2: int, var3: int = 0; - can init(self:any) { + can init(self: SimpleClass) { print(self.var3); } } From 442f86aaf001bfc8a03efde39f4d46a87bd39a07 Mon Sep 17 00:00:00 2001 From: Jayanaka-98 Date: Sun, 11 Aug 2024 18:40:48 +0530 Subject: [PATCH 22/33] Abilities integrated to semtables. --- jaclang/compiler/passes/main/registry_pass.py | 26 +++++++++++++++++-- jaclang/compiler/semtable.py | 5 ++-- jaclang/runtimelib/utils.py | 12 ++++++--- 3 files changed, 35 insertions(+), 8 deletions(-) diff --git a/jaclang/compiler/passes/main/registry_pass.py b/jaclang/compiler/passes/main/registry_pass.py index d7e697661..4c401f2b9 100644 --- a/jaclang/compiler/passes/main/registry_pass.py +++ b/jaclang/compiler/passes/main/registry_pass.py @@ -38,6 +38,8 @@ def exit_module(self, node: ast.Module) -> None: os.path.join(module_dir, f"{module_name}.registry.pkl"), "wb" ) as f: pickle.dump(node.registry, f) + print(node.registry.pp()) + # print(node.registry.registry[list(node.registry.registry.keys())[2]]) except Exception as e: self.warning(f"Can't save registry for {module_name}: {e}") self.modules_visited.pop() @@ -86,10 +88,30 @@ def exit_has_var(self, node: ast.HasVar) -> None: def exit_ability(self, node: ast.Ability) -> None: """Save ability information.""" - scope = get_sem_scope(node) + scope = get_sem_scope(node.parent) seminfo = SemInfo( node.name_ref.sym_name, - None, + "Ability", + node.semstr.lit_value if node.semstr else "", + ) + if len(self.modules_visited) and self.modules_visited[-1].registry: + self.modules_visited[-1].registry.add(scope, seminfo) + + if isinstance(node.signature, ast.EventSignature): + if len(self.modules_visited) and self.modules_visited[-1].registry: + self.modules_visited[-1].registry.add( + get_sem_scope(node), SemInfo("No Input Params", "") + ) + + def exit_param_var(self, node: ast.ParamVar) -> None: + """Save param information""" + scope = get_sem_scope(node) + extracted_type = ( + "".join(self.extract_type(node.type_tag.tag)) if node.type_tag else None + ) + seminfo = SemInfo( + node.name.value, + extracted_type, node.semstr.lit_value if node.semstr else "", ) if len(self.modules_visited) and self.modules_visited[-1].registry: diff --git a/jaclang/compiler/semtable.py b/jaclang/compiler/semtable.py index 9d0230575..7be4aafad 100644 --- a/jaclang/compiler/semtable.py +++ b/jaclang/compiler/semtable.py @@ -38,7 +38,8 @@ def __str__(self) -> str: """Return the string representation of the class.""" if self.parent: return f"{self.parent}.{self.scope}({self.type})" - return f"{self.scope}({self.type})" + else: + return f"{self.scope}({self.type})" def __repr__(self) -> str: """Return the string representation of the class.""" @@ -57,7 +58,7 @@ def get_scope_from_str(scope_str: str) -> Optional[SemScope]: @property def as_type_str(self) -> Optional[str]: - """Return the type string representation of the SemsScope.""" + """Return the type string representation of the SemScope.""" if self.type not in ["class", "node", "obj"]: return None type_str = self.scope diff --git a/jaclang/runtimelib/utils.py b/jaclang/runtimelib/utils.py index f8b84e767..6cc7e9bda 100644 --- a/jaclang/runtimelib/utils.py +++ b/jaclang/runtimelib/utils.py @@ -113,17 +113,21 @@ def get_sem_scope(node: ast.AstNode) -> SemScope: a = ( node.name if isinstance(node, ast.Module) - else node.name.value if isinstance(node, (ast.Enum, ast.Architype)) else "" + else ( + node.name.value + if isinstance(node, (ast.Enum, ast.Architype)) + else node.name_ref.sym_name if isinstance(node, ast.Ability) else "" + ) ) if isinstance(node, ast.Module): return SemScope(a, "Module", None) - elif isinstance(node, (ast.Enum, ast.Architype)): + elif isinstance(node, (ast.Enum, ast.Architype, ast.Ability)): node_type = ( node.__class__.__name__ if isinstance(node, ast.Enum) - else node.arch_type.value + else ("Ability" if isinstance(node, ast.Ability) else node.arch_type.value) ) - if node.parent: + if node.parent or isinstance(node, ast.Ability): return SemScope(a, node_type, get_sem_scope(node.parent)) else: if node.parent: From cdd43a4d133a2d13b41ee66b26d02b7f02fc390a Mon Sep 17 00:00:00 2001 From: Jayanaka-98 Date: Sun, 11 Aug 2024 18:41:15 +0530 Subject: [PATCH 23/33] Test fix --- jaclang/tests/test_language.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/jaclang/tests/test_language.py b/jaclang/tests/test_language.py index 48658b92c..732f6a9dd 100644 --- a/jaclang/tests/test_language.py +++ b/jaclang/tests/test_language.py @@ -479,9 +479,9 @@ def test_registry(self) -> None: ) as f: registry = pickle.load(f) - self.assertEqual(len(registry.registry), 3) - self.assertEqual(len(list(registry.registry.items())[0][1]), 10) - self.assertEqual(list(registry.registry.items())[1][0].scope, "Person") + self.assertEqual(len(registry.registry), 7) + self.assertEqual(len(list(registry.registry.items())[0][1]), 2) + self.assertEqual(list(registry.registry.items())[3][0].scope, "Person") def test_enum_inside_arch(self) -> None: """Test Enum as member stmt.""" From d6b2a8cb3f1aaf962989e598165717e7db403e53 Mon Sep 17 00:00:00 2001 From: Jayanaka-98 Date: Sun, 11 Aug 2024 18:44:30 +0530 Subject: [PATCH 24/33] Update --- jaclang/compiler/passes/main/registry_pass.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/jaclang/compiler/passes/main/registry_pass.py b/jaclang/compiler/passes/main/registry_pass.py index 4c401f2b9..47dd2a675 100644 --- a/jaclang/compiler/passes/main/registry_pass.py +++ b/jaclang/compiler/passes/main/registry_pass.py @@ -38,8 +38,6 @@ def exit_module(self, node: ast.Module) -> None: os.path.join(module_dir, f"{module_name}.registry.pkl"), "wb" ) as f: pickle.dump(node.registry, f) - print(node.registry.pp()) - # print(node.registry.registry[list(node.registry.registry.keys())[2]]) except Exception as e: self.warning(f"Can't save registry for {module_name}: {e}") self.modules_visited.pop() From 321675cd20a55e9a89a2e0ffcc6bbd4df930c35c Mon Sep 17 00:00:00 2001 From: Jayanaka-98 Date: Sun, 11 Aug 2024 18:55:59 +0530 Subject: [PATCH 25/33] get_sem_scope argument type changed --- jaclang/runtimelib/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jaclang/runtimelib/utils.py b/jaclang/runtimelib/utils.py index 6cc7e9bda..d30108945 100644 --- a/jaclang/runtimelib/utils.py +++ b/jaclang/runtimelib/utils.py @@ -108,7 +108,7 @@ def traverse_graph( dfs(other_nd, cur_depth + 1) -def get_sem_scope(node: ast.AstNode) -> SemScope: +def get_sem_scope(node: ast.AstNode | None) -> SemScope: """Get scope of the node.""" a = ( node.name From a043f9d633f4efa8810a1904f9427d8e0282cf82 Mon Sep 17 00:00:00 2001 From: Jayanaka-98 Date: Sun, 11 Aug 2024 19:00:26 +0530 Subject: [PATCH 26/33] flake 8 fix --- jaclang/compiler/passes/main/registry_pass.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/jaclang/compiler/passes/main/registry_pass.py b/jaclang/compiler/passes/main/registry_pass.py index 47dd2a675..b190f27cf 100644 --- a/jaclang/compiler/passes/main/registry_pass.py +++ b/jaclang/compiler/passes/main/registry_pass.py @@ -95,11 +95,14 @@ def exit_ability(self, node: ast.Ability) -> None: if len(self.modules_visited) and self.modules_visited[-1].registry: self.modules_visited[-1].registry.add(scope, seminfo) - if isinstance(node.signature, ast.EventSignature): - if len(self.modules_visited) and self.modules_visited[-1].registry: - self.modules_visited[-1].registry.add( - get_sem_scope(node), SemInfo("No Input Params", "") - ) + if ( + isinstance(node.signature, ast.EventSignature) + and len(self.modules_visited) + and self.modules_visited[-1].registry + ): + self.modules_visited[-1].registry.add( + get_sem_scope(node), SemInfo("No Input Params", "") + ) def exit_param_var(self, node: ast.ParamVar) -> None: """Save param information""" From 559ca433338a655afac51ec9649128c1fe300cba Mon Sep 17 00:00:00 2001 From: Jayanaka-98 Date: Sun, 11 Aug 2024 19:06:46 +0530 Subject: [PATCH 27/33] restore and fix flake 8 --- jaclang/compiler/passes/main/registry_pass.py | 2 +- jaclang/runtimelib/utils.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/jaclang/compiler/passes/main/registry_pass.py b/jaclang/compiler/passes/main/registry_pass.py index b190f27cf..2dfcad9e4 100644 --- a/jaclang/compiler/passes/main/registry_pass.py +++ b/jaclang/compiler/passes/main/registry_pass.py @@ -105,7 +105,7 @@ def exit_ability(self, node: ast.Ability) -> None: ) def exit_param_var(self, node: ast.ParamVar) -> None: - """Save param information""" + """Save param information.""" scope = get_sem_scope(node) extracted_type = ( "".join(self.extract_type(node.type_tag.tag)) if node.type_tag else None diff --git a/jaclang/runtimelib/utils.py b/jaclang/runtimelib/utils.py index d30108945..6cc7e9bda 100644 --- a/jaclang/runtimelib/utils.py +++ b/jaclang/runtimelib/utils.py @@ -108,7 +108,7 @@ def traverse_graph( dfs(other_nd, cur_depth + 1) -def get_sem_scope(node: ast.AstNode | None) -> SemScope: +def get_sem_scope(node: ast.AstNode) -> SemScope: """Get scope of the node.""" a = ( node.name From d88a5615bc3b876ee5fc3e18c5897d7d8826dc9d Mon Sep 17 00:00:00 2001 From: Jayanaka-98 Date: Sun, 11 Aug 2024 19:22:49 +0530 Subject: [PATCH 28/33] scope_node --- jaclang/compiler/passes/main/registry_pass.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/jaclang/compiler/passes/main/registry_pass.py b/jaclang/compiler/passes/main/registry_pass.py index 2dfcad9e4..fb32bdd03 100644 --- a/jaclang/compiler/passes/main/registry_pass.py +++ b/jaclang/compiler/passes/main/registry_pass.py @@ -86,7 +86,8 @@ def exit_has_var(self, node: ast.HasVar) -> None: def exit_ability(self, node: ast.Ability) -> None: """Save ability information.""" - scope = get_sem_scope(node.parent) + scope_node = node.parent + scope = get_sem_scope(scope_node) seminfo = SemInfo( node.name_ref.sym_name, "Ability", From 342eec4e57aea70ca5a9a351892a772c49a23166 Mon Sep 17 00:00:00 2001 From: Jayanaka-98 Date: Sun, 11 Aug 2024 19:34:16 +0530 Subject: [PATCH 29/33] mypy fix atmpt --- jaclang/compiler/passes/main/registry_pass.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jaclang/compiler/passes/main/registry_pass.py b/jaclang/compiler/passes/main/registry_pass.py index fb32bdd03..1c28854ca 100644 --- a/jaclang/compiler/passes/main/registry_pass.py +++ b/jaclang/compiler/passes/main/registry_pass.py @@ -86,7 +86,7 @@ def exit_has_var(self, node: ast.HasVar) -> None: def exit_ability(self, node: ast.Ability) -> None: """Save ability information.""" - scope_node = node.parent + scope_node : ast.AstNode = node.parent scope = get_sem_scope(scope_node) seminfo = SemInfo( node.name_ref.sym_name, From d283cc3fabdb202236ce858a3c59777f9393001c Mon Sep 17 00:00:00 2001 From: Jayanaka-98 Date: Sun, 11 Aug 2024 19:57:56 +0530 Subject: [PATCH 30/33] Use owner method for abilities --- jaclang/compiler/passes/main/registry_pass.py | 5 +++-- jaclang/runtimelib/utils.py | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/jaclang/compiler/passes/main/registry_pass.py b/jaclang/compiler/passes/main/registry_pass.py index 1c28854ca..adfdb4216 100644 --- a/jaclang/compiler/passes/main/registry_pass.py +++ b/jaclang/compiler/passes/main/registry_pass.py @@ -38,6 +38,7 @@ def exit_module(self, node: ast.Module) -> None: os.path.join(module_dir, f"{module_name}.registry.pkl"), "wb" ) as f: pickle.dump(node.registry, f) + print(node.registry.pp()) except Exception as e: self.warning(f"Can't save registry for {module_name}: {e}") self.modules_visited.pop() @@ -86,8 +87,8 @@ def exit_has_var(self, node: ast.HasVar) -> None: def exit_ability(self, node: ast.Ability) -> None: """Save ability information.""" - scope_node : ast.AstNode = node.parent - scope = get_sem_scope(scope_node) + # scope_node = node.parent + scope = get_sem_scope(node.owner_method) seminfo = SemInfo( node.name_ref.sym_name, "Ability", diff --git a/jaclang/runtimelib/utils.py b/jaclang/runtimelib/utils.py index 6cc7e9bda..f4cdbab51 100644 --- a/jaclang/runtimelib/utils.py +++ b/jaclang/runtimelib/utils.py @@ -127,11 +127,11 @@ def get_sem_scope(node: ast.AstNode) -> SemScope: if isinstance(node, ast.Enum) else ("Ability" if isinstance(node, ast.Ability) else node.arch_type.value) ) - if node.parent or isinstance(node, ast.Ability): + if node.parent: return SemScope(a, node_type, get_sem_scope(node.parent)) else: if node.parent: - return get_sem_scope(node.parent) + return get_sem_scope(node.parent) if not isinstance(node, ast.Ability) else get_sem_scope(node.owner_method) return SemScope("", "", None) From 80489a331af3fad62687cfcbb70b23b5a6468fdb Mon Sep 17 00:00:00 2001 From: Jayanaka-98 Date: Sun, 11 Aug 2024 22:35:30 +0530 Subject: [PATCH 31/33] Noqa in utilis.py for runtimelib --- jaclang/compiler/passes/main/registry_pass.py | 4 +--- jaclang/runtimelib/utils.py | 8 ++++++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/jaclang/compiler/passes/main/registry_pass.py b/jaclang/compiler/passes/main/registry_pass.py index adfdb4216..75dbab760 100644 --- a/jaclang/compiler/passes/main/registry_pass.py +++ b/jaclang/compiler/passes/main/registry_pass.py @@ -38,7 +38,6 @@ def exit_module(self, node: ast.Module) -> None: os.path.join(module_dir, f"{module_name}.registry.pkl"), "wb" ) as f: pickle.dump(node.registry, f) - print(node.registry.pp()) except Exception as e: self.warning(f"Can't save registry for {module_name}: {e}") self.modules_visited.pop() @@ -87,8 +86,7 @@ def exit_has_var(self, node: ast.HasVar) -> None: def exit_ability(self, node: ast.Ability) -> None: """Save ability information.""" - # scope_node = node.parent - scope = get_sem_scope(node.owner_method) + scope = get_sem_scope(node.owner_method) # type: ignore[arg-type] seminfo = SemInfo( node.name_ref.sym_name, "Ability", diff --git a/jaclang/runtimelib/utils.py b/jaclang/runtimelib/utils.py index f4cdbab51..c134377ae 100644 --- a/jaclang/runtimelib/utils.py +++ b/jaclang/runtimelib/utils.py @@ -128,10 +128,14 @@ def get_sem_scope(node: ast.AstNode) -> SemScope: else ("Ability" if isinstance(node, ast.Ability) else node.arch_type.value) ) if node.parent: - return SemScope(a, node_type, get_sem_scope(node.parent)) + return SemScope( + a, + node_type, + get_sem_scope(node.parent), + ) else: if node.parent: - return get_sem_scope(node.parent) if not isinstance(node, ast.Ability) else get_sem_scope(node.owner_method) + return get_sem_scope(node.parent) return SemScope("", "", None) From be70e95658dfd02a65cc29beeba41bbbcbba28df Mon Sep 17 00:00:00 2001 From: Jayanaka-98 Date: Sun, 11 Aug 2024 22:43:31 +0530 Subject: [PATCH 32/33] Update Owner -> parent --- jaclang/compiler/passes/main/registry_pass.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/jaclang/compiler/passes/main/registry_pass.py b/jaclang/compiler/passes/main/registry_pass.py index 75dbab760..b596dea5f 100644 --- a/jaclang/compiler/passes/main/registry_pass.py +++ b/jaclang/compiler/passes/main/registry_pass.py @@ -38,6 +38,7 @@ def exit_module(self, node: ast.Module) -> None: os.path.join(module_dir, f"{module_name}.registry.pkl"), "wb" ) as f: pickle.dump(node.registry, f) + print(node.registry.pp()) except Exception as e: self.warning(f"Can't save registry for {module_name}: {e}") self.modules_visited.pop() @@ -86,7 +87,7 @@ def exit_has_var(self, node: ast.HasVar) -> None: def exit_ability(self, node: ast.Ability) -> None: """Save ability information.""" - scope = get_sem_scope(node.owner_method) # type: ignore[arg-type] + scope = get_sem_scope(node.parent) # type: ignore[arg-type] seminfo = SemInfo( node.name_ref.sym_name, "Ability", From 8a734364f67975487f60640e287b358277026db9 Mon Sep 17 00:00:00 2001 From: Jayanaka-98 Date: Sun, 11 Aug 2024 22:46:04 +0530 Subject: [PATCH 33/33] pp() remove --- jaclang/compiler/passes/main/registry_pass.py | 1 - 1 file changed, 1 deletion(-) diff --git a/jaclang/compiler/passes/main/registry_pass.py b/jaclang/compiler/passes/main/registry_pass.py index b596dea5f..b4fc2633c 100644 --- a/jaclang/compiler/passes/main/registry_pass.py +++ b/jaclang/compiler/passes/main/registry_pass.py @@ -38,7 +38,6 @@ def exit_module(self, node: ast.Module) -> None: os.path.join(module_dir, f"{module_name}.registry.pkl"), "wb" ) as f: pickle.dump(node.registry, f) - print(node.registry.pp()) except Exception as e: self.warning(f"Can't save registry for {module_name}: {e}") self.modules_visited.pop()