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

feat: .impl.jac and .test.jac #183

Merged
merged 5 commits into from
Jan 30, 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
27 changes: 27 additions & 0 deletions examples/manual_code/circle_pure.impl.jac
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
"""Enum for shape types"""

:enum:ShapeType {
CIRCLE = "Circle",
UNKNOWN = "Unknown"
}

"""Function to calculate the area of a circle."""
:can:calculate_area
(radius: float) -> float {
return math.pi * radius * radius;
}

:obj:Circle:can:<init> (radius: float) {
self.radius = radius;
<super>.<init>(ShapeType.CIRCLE);
}

"""Overridden method to calculate the area of the circle."""
:obj:Circle:can:area -> float {
return math.pi * <self>.radius * <self>.radius;
}

:can:main_run {
print(f"Area of a circle with radius {RAD} using function: {calculate_area(RAD)}");
print(f"Area of a {c.shape_type.value} with radius {RAD} using class: {c.area()}");
}
28 changes: 28 additions & 0 deletions examples/manual_code/circle_pure.jac
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
"""
This module demonstrates a simple circle class and a function to calculate
the area of a circle in all of Jac's glory.
"""
import:py math;

enum ShapeType;
can calculate_area(radius: float) -> float;
can main_run;

"""Base class for a shape."""
obj Shape {
has shape_type: ShapeType;
can area -> float abs;
}

"""Circle class inherits from Shape."""
obj Circle:Shape: {
has radius: float;
can <init>(radius: float);
can area -> float;
}

# Radius of the demo circle
glob RAD = 5, c = Circle(radius=RAD);

"""Here we run the main program."""
with entry:__main__ { main_run(); }
4 changes: 4 additions & 0 deletions examples/manual_code/circle_pure.test.jac
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
glob expected_area = 78.53981633974483;
test { check.AlmostEqual(calculate_area(RAD), expected_area); }
test { c = Circle(RAD); check.AlmostEqual(c.area(), expected_area); }
test { c = Circle(RAD); check.Equal(c.shape_type, ShapeType.CIRCLE); }
4 changes: 4 additions & 0 deletions jaclang/compiler/absyntree.py
Original file line number Diff line number Diff line change
Expand Up @@ -271,12 +271,16 @@ def __init__(
body: Sequence[ElementStmt | String | EmptyToken],
is_imported: bool,
kid: Sequence[AstNode],
impl_mod: Optional[Module] = None,
test_mod: Optional[Module] = None,
) -> None:
"""Initialize whole program node."""
self.name = name
self.source = source
self.body = body
self.is_imported = is_imported
self.impl_mod = impl_mod
self.test_mod = test_mod
self.mod_deps: dict[str, Module] = {}
AstNode.__init__(self, kid=kid)
AstDocNode.__init__(self, doc=doc)
Expand Down
8 changes: 0 additions & 8 deletions jaclang/compiler/passes/main/def_use_pass.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,6 @@ class DefUsePass(SymTabPass):

def after_pass(self) -> None:
"""After pass."""
# for i in self.unlinked:
# if not i.sym_name.startswith("[") and type(i.parent) in [
# ast.AtomTrailer,
# ]:
# self.warning(
# f"{i.sym_name} used before being defined.",
# node_override=i,
# )

def enter_architype(self, node: ast.Architype) -> None:
"""Sub objects.
Expand Down
53 changes: 37 additions & 16 deletions jaclang/compiler/passes/main/import_pass.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ def before_pass(self) -> None:
def enter_module(self, node: ast.Module) -> None:
"""Run Importer."""
self.cur_node = node
self.annex_impl(node)
self.terminate() # Turns off auto traversal for deliberate traversal
self.run_again = True
while self.run_again:
Expand All @@ -35,25 +36,44 @@ def enter_module(self, node: ast.Module) -> None:
for i in all_imports:
if i.lang.tag.value == "jac" and not i.sub_module:
self.run_again = True
mod = (
self.import_module(
node=i,
mod_path=node.loc.mod_path,
)
if i.lang.tag.value == "jac"
else self.import_py_module(node=i, mod_path=node.loc.mod_path)
mod = self.import_module(
node=i,
mod_path=node.loc.mod_path,
)
if not mod:
self.run_again = False
continue
self.annex_impl(mod)
i.sub_module = mod
i.add_kids_right([mod], pos_update=False)
# elif i.lang.tag.value == "py":
# self.import_py_module(node=i, mod_path=node.loc.mod_path)
self.enter_import(i)
SubNodeTabPass(prior=self, input_ir=node)
self.annex_impl(node)
node.mod_deps = self.import_table

def annex_impl(self, node: ast.Module) -> None:
"""Annex impl and test modules."""
if not node.loc.mod_path:
self.error("Module has no path")
if node.loc.mod_path.endswith(".jac") and path.exists(
f"{node.loc.mod_path[:-4]}.impl.jac"
):
mod = self.import_mod_from_file(f"{node.loc.mod_path[:-4]}.impl.jac")
if mod:
node.impl_mod = mod
node.add_kids_right([mod], pos_update=False)
mod.parent = node
if node.loc.mod_path.endswith(".jac") and path.exists(
f"{node.loc.mod_path[:-4]}.test.jac"
):
mod = self.import_mod_from_file(f"{node.loc.mod_path[:-4]}.test.jac")
if mod:
node.test_mod = mod
node.add_kids_right([mod], pos_update=False)
mod.parent = node

def enter_import(self, node: ast.Import) -> None:
"""Sub objects.

Expand All @@ -73,19 +93,22 @@ def enter_import(self, node: ast.Import) -> None:

def import_module(self, node: ast.Import, mod_path: str) -> ast.Module | None:
"""Import a module."""
from jaclang.compiler.transpiler import jac_file_to_pass
from jaclang.compiler.passes.main import SubNodeTabPass

self.cur_node = node # impacts error reporting
target = import_target_to_relative_path(
node.path.path_str, path.dirname(node.loc.mod_path)
)
return self.import_mod_from_file(target)

if target in self.import_table:
return self.import_table[target]
def import_mod_from_file(self, target: str) -> ast.Module | None:
"""Import a module from a file."""
from jaclang.compiler.transpiler import jac_file_to_pass
from jaclang.compiler.passes.main import SubNodeTabPass

if not path.exists(target):
self.error(f"Could not find module {target}", node_override=node)
self.error(f"Could not find module {target}")
return None
if target in self.import_table:
return self.import_table[target]
try:
mod_pass = jac_file_to_pass(file_path=target, target=SubNodeTabPass)
self.errors_had += mod_pass.errors_had
Expand All @@ -99,9 +122,7 @@ def import_module(self, node: ast.Import, mod_path: str) -> ast.Module | None:
mod.is_imported = True
return mod
else:
self.error(
f"Module {target} is not a valid Jac module.", node_override=node
)
self.error(f"Module {target} is not a valid Jac module.")
return None

def import_py_module(self, node: ast.Import, mod_path: str) -> Optional[ast.Module]:
Expand Down
11 changes: 11 additions & 0 deletions jaclang/compiler/passes/main/sym_tab_build_pass.py
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,17 @@ def exit_module(self, node: ast.Module) -> None:
is_imported: bool,
"""
self.pop_scope()
if (
isinstance(node.parent, ast.Module)
and node
in [
node.parent.impl_mod,
node.parent.test_mod,
]
and node.sym_tab
):
for v in node.sym_tab.tab.values():
self.def_insert(v.decl, table_override=self.cur_scope())

def enter_global_vars(self, node: ast.GlobalVars) -> None:
"""Sub objects.
Expand Down
4 changes: 2 additions & 2 deletions jaclang/compiler/passes/tool/jac_formatter_pass.py
Original file line number Diff line number Diff line change
Expand Up @@ -1764,8 +1764,8 @@ def exit_test(self, node: ast.Test) -> None:
elif isinstance(i, ast.Semi):
self.emit(node, i.gen.jac)
elif isinstance(i, ast.Name):
# if not i.value.startswith("test"):
self.emit(node, f" {i.value} ")
if not i.value.startswith("test_t"):
self.emit(node, f" {i.value} ")
else:
if start:
self.emit(node, i.gen.jac)
Expand Down
2 changes: 1 addition & 1 deletion jaclang/compiler/passes/tool/tests/test_jac_format_pass.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ def micro_suite_test(self, filename: str) -> None:
target=PyastGenPass,
schedule=without_format,
)
if "circle_clean_tests.jac" in filename:
if "circle_clean_tests.jac" in filename or "circle_pure.test.jac" in filename:
tokens = code_gen_format.ir.gen.jac.split()
num_test = 0
for i in range(len(tokens)):
Expand Down
18 changes: 18 additions & 0 deletions jaclang/tests/test_man_code.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,24 @@ def test_clean_circle_jac(self) -> None:
stdout_value,
)

def test_pure_circle_jac(self) -> None:
"""Basic test for pass."""
captured_output = io.StringIO()
sys.stdout = captured_output

# Execute the function
cli.run(self.fixture_abs_path("../../../examples/manual_code/circle_pure.jac")) # type: ignore

sys.stdout = sys.__stdout__
stdout_value = captured_output.getvalue()

# Assertions or verifications
self.assertEqual(
"Area of a circle with radius 5 using function: 78.53981633974483\n"
"Area of a Circle with radius 5 using class: 78.53981633974483\n",
stdout_value,
)

def test_clean_circle_jac_test(self) -> None:
"""Basic test for pass."""
captured_output = io.StringIO()
Expand Down
Loading