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

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
marsninja committed Jan 30, 2024
2 parents 8397904 + c984b3c commit c5c5be5
Show file tree
Hide file tree
Showing 10 changed files with 132 additions and 27 deletions.
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

0 comments on commit c5c5be5

Please sign in to comment.