Skip to content

Commit 33d79c3

Browse files
committed
jac2py codegen refactored
1 parent 0a4a3c9 commit 33d79c3

File tree

10 files changed

+472
-307
lines changed

10 files changed

+472
-307
lines changed

jac/examples/reference/architypes.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,17 @@ def print_base_classes(cls: type) -> type:
66
return cls
77

88

9+
# Since the Animal class cannot be inherit from object, (cause the base class will be changed at run time)
10+
# we need a base class.
11+
#
12+
# reference: https://stackoverflow.com/a/9639512/10846399
13+
#
14+
class Base:
15+
pass
16+
17+
918
@jac.make_obj(on_entry=[], on_exit=[])
10-
class Animal:
19+
class Animal(Base):
1120
pass
1221

1322

jac/examples/reference/data_spatial_walker_statements.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,17 @@
22
from jaclang.plugin.feature import JacFeature as _Jac
33

44

5+
# Since the Animal class cannot be inherit from object, (cause the base class will be changed at run time)
6+
# we need a base class.
7+
#
8+
# reference: https://stackoverflow.com/a/9639512/10846399
9+
#
10+
class Base:
11+
pass
12+
13+
514
@_Jac.make_walker(on_entry=[_Jac.DSFunc("self_destruct", None)], on_exit=[])
6-
class Visitor:
15+
class Visitor(Base):
716
def self_destruct(self, _jac_here_) -> None:
817
print("get's here")
918
_Jac.disengage(self)

jac/examples/reference/disengage_statements.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,17 @@
22
from jaclang.plugin.feature import JacFeature as _Jac
33

44

5+
# Since the Animal class cannot be inherit from object, (cause the base class will be changed at run time)
6+
# we need a base class.
7+
#
8+
# reference: https://stackoverflow.com/a/9639512/10846399
9+
#
10+
class Base:
11+
pass
12+
13+
514
@_Jac.make_walker(on_entry=[_Jac.DSFunc("travel", _Jac.get_root_type())], on_exit=[])
6-
class Visitor:
15+
class Visitor(Base):
716
def travel(self, _jac_here_: _Jac.get_root_type()) -> None:
817
if _Jac.visit_node(
918
self, _Jac.edge_ref(_jac_here_, None, _Jac.EdgeDir.OUT, None, None)
@@ -14,7 +23,7 @@ def travel(self, _jac_here_: _Jac.get_root_type()) -> None:
1423

1524

1625
@_Jac.make_node(on_entry=[_Jac.DSFunc("speak", Visitor)], on_exit=[])
17-
class item:
26+
class item(Base):
1827
def speak(self, _jac_here_: Visitor) -> None:
1928
print("Hey There!!!")
2029
_Jac.disengage(_jac_here_)

jac/examples/reference/match_class_patterns.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,18 @@
33
from dataclasses import dataclass as dataclass
44

55

6+
# Since the Animal class cannot be inherit from object, (cause the base class will be changed at run time)
7+
# we need a base class.
8+
#
9+
# reference: https://stackoverflow.com/a/9639512/10846399
10+
#
11+
class Base:
12+
pass
13+
14+
615
@Jac.make_obj(on_entry=[], on_exit=[])
716
@dataclass(eq=False)
8-
class Point:
17+
class Point(Base):
918
x: float
1019
y: float
1120

jac/examples/reference/special_comprehensions.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,18 @@
44
import random
55

66

7+
# Since the Animal class cannot be inherit from object, (cause the base class will be changed at run time)
8+
# we need a base class.
9+
#
10+
# reference: https://stackoverflow.com/a/9639512/10846399
11+
#
12+
class Base:
13+
pass
14+
15+
716
@Jac.make_obj(on_entry=[], on_exit=[])
817
@dataclass(eq=False)
9-
class TestObj:
18+
class TestObj(Base):
1019
x: int = Jac.has_instance_default(gen_func=lambda: random.randint(0, 15))
1120
y: int = Jac.has_instance_default(gen_func=lambda: random.randint(0, 15))
1221
z: int = Jac.has_instance_default(gen_func=lambda: random.randint(0, 15))
@@ -23,7 +32,7 @@ class TestObj:
2332

2433
@Jac.make_obj(on_entry=[], on_exit=[])
2534
@dataclass(eq=False)
26-
class MyObj:
35+
class MyObj(Base):
2736
apple: int = Jac.has_instance_default(gen_func=lambda: 0)
2837
banana: int = Jac.has_instance_default(gen_func=lambda: 0)
2938

jac/examples/reference/visit_statements.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,17 @@
22
from jaclang.plugin.feature import JacFeature as _Jac
33

44

5+
# Since the Animal class cannot be inherit from object, (cause the base class will be changed at run time)
6+
# we need a base class.
7+
#
8+
# reference: https://stackoverflow.com/a/9639512/10846399
9+
#
10+
class Base:
11+
pass
12+
13+
514
@_Jac.make_walker(on_entry=[_Jac.DSFunc("travel", _Jac.get_root_type())], on_exit=[])
6-
class Visitor:
15+
class Visitor(Base):
716
def travel(self, _jac_here_: _Jac.get_root_type()) -> None:
817
if _Jac.visit_node(
918
self, _Jac.edge_ref(_jac_here_, None, _Jac.EdgeDir.OUT, None, None)
@@ -14,7 +23,7 @@ def travel(self, _jac_here_: _Jac.get_root_type()) -> None:
1423

1524

1625
@_Jac.make_node(on_entry=[_Jac.DSFunc("speak", Visitor)], on_exit=[])
17-
class item:
26+
class item(Base):
1827
def speak(self, _jac_here_: Visitor) -> None:
1928
print("Hey There!!!")
2029

jac/jaclang/__init__.py

Lines changed: 219 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,227 @@
11
"""The Jac Programming Language."""
22

3-
from jaclang.plugin.default import JacFeatureImpl
4-
from jaclang.plugin.feature import JacFeature, plugin_manager
3+
__all__ = [
4+
"JacObj",
5+
"JacWalker",
6+
"JacNode",
7+
"JacEdge",
8+
"with_entry",
9+
"with_exit",
10+
"abstractmethod",
11+
"jac_import",
12+
"field_default",
13+
"root",
14+
"static",
15+
]
16+
17+
import inspect
18+
import types
19+
import typing
20+
from abc import ABC, ABCMeta, abstractmethod
21+
from dataclasses import dataclass, field
22+
from typing import Any, Callable, ClassVar, Dict, Tuple, Type, TypeVar
523

6-
jac_import = JacFeature.jac_import
24+
from jaclang.plugin.default import JacFeatureImpl
25+
from jaclang.plugin.feature import JacFeature as _Jac, plugin_manager
726

27+
# ----------------------------------------------------------------------------
28+
# Plugin Initialization.
29+
# ----------------------------------------------------------------------------
830

931
plugin_manager.register(JacFeatureImpl)
1032
plugin_manager.load_setuptools_entrypoints("jac")
1133

12-
__all__ = ["jac_import"]
34+
T = TypeVar("T")
35+
36+
# ----------------------------------------------------------------------------
37+
# Meta classes.
38+
# ----------------------------------------------------------------------------
39+
40+
41+
# https://stackoverflow.com/a/9639512/10846399
42+
class _JacArchiTypeBase:
43+
pass
44+
45+
46+
class JacMetaCommon(ABCMeta):
47+
"""Common metaclass for Jac types."""
48+
49+
def __new__(
50+
cls,
51+
name: str,
52+
bases: Tuple[Type, ...],
53+
dct: Dict[str, Any],
54+
make_func: Callable[[list, list], Callable[[type], type]],
55+
) -> "JacMetaCommon":
56+
57+
# We have added this "__init__" to the jac base class just to make the type checkers happy.
58+
# Actually the dataclass decorator will create an __init__ function and assign it here bellow.
59+
if bases == (_JacArchiTypeBase,) and "__init__" in dct:
60+
del dct["__init__"]
61+
62+
on_entry, on_exit = [], []
63+
for value in dct.values():
64+
if hasattr(value, "__jac_entry"):
65+
entry_node = getattr(value, "__jac_entry") # noqa: B009
66+
on_entry.append(_Jac.DSFunc(value.__name__, entry_node))
67+
if hasattr(value, "__jac_exit"):
68+
exit_node = getattr(value, "__jac_exit") # noqa: B009
69+
on_exit.append(_Jac.DSFunc(value.__name__, exit_node))
70+
71+
inst = super().__new__(cls, name, bases, dct)
72+
inst = dataclass(eq=False)(inst) # type: ignore [arg-type, assignment]
73+
inst = make_func(on_entry, on_exit)(inst) # type: ignore [assignment]
74+
return inst
75+
76+
77+
class JacMetaObj(JacMetaCommon, ABC):
78+
def __new__(
79+
cls, name: str, bases: Tuple[Type, ...], dct: Dict[str, Any]
80+
) -> "JacMetaCommon":
81+
return super().__new__(cls, name, bases, dct, _Jac.make_obj)
82+
83+
84+
class JacMetaWalker(JacMetaCommon, ABC):
85+
def __new__(
86+
cls, name: str, bases: Tuple[Type, ...], dct: Dict[str, Any]
87+
) -> "JacMetaCommon":
88+
return super().__new__(cls, name, bases, dct, _Jac.make_walker)
89+
90+
91+
class JacMetaNode(JacMetaCommon, ABC):
92+
def __new__(
93+
cls, name: str, bases: Tuple[Type, ...], dct: Dict[str, Any]
94+
) -> "JacMetaCommon":
95+
return super().__new__(cls, name, bases, dct, _Jac.make_node)
96+
97+
98+
class JacMetaEdge(JacMetaCommon, ABC):
99+
def __new__(
100+
cls, name: str, bases: Tuple[Type, ...], dct: Dict[str, Any]
101+
) -> "JacMetaCommon":
102+
return super().__new__(cls, name, bases, dct, _Jac.make_edge)
103+
104+
105+
# ----------------------------------------------------------------------------
106+
# Base classes.
107+
# ----------------------------------------------------------------------------
108+
109+
110+
class JacObj(_JacArchiTypeBase, metaclass=JacMetaObj):
111+
"""Base class for all the jac object types."""
112+
113+
def __init__(self, *args, **kwargs) -> None: # noqa: ANN002, ANN003
114+
"""Initialize Jac architype base."""
115+
116+
117+
class JacWalker(_JacArchiTypeBase, metaclass=JacMetaWalker):
118+
"""Base class for all the jac walker types."""
119+
120+
def __init__(self, *args, **kwargs) -> None: # noqa: ANN002, ANN003
121+
"""Initialize Jac architype base."""
122+
123+
def spawn(self, node: "JacNode") -> None: # REVIEW: What should it return?
124+
"""Spawn a new node from the walker."""
125+
_Jac.spawn_call(self, node) # type: ignore [arg-type]
126+
127+
128+
class JacNode(_JacArchiTypeBase, metaclass=JacMetaNode):
129+
"""Base class for all the jac node types."""
130+
131+
def __init__(self, *args, **kwargs) -> None: # noqa: ANN002, ANN003
132+
"""Initialize Jac architype base."""
133+
134+
def spawn(self, walker: JacWalker) -> None: # REVIEW: What should it return?
135+
"""Spawn a new node from the walker."""
136+
_Jac.spawn_call(self, walker) # type: ignore [arg-type]
137+
138+
def connect(
139+
self,
140+
node: "JacNode",
141+
edge: "type[JacEdge] | JacEdge | None" = None,
142+
unidir: bool = False,
143+
conn_assign: tuple[tuple, tuple] | None = None,
144+
) -> "JacNode":
145+
"""Connect the current node to another node."""
146+
# TODO: The above edge type should be reviewed, as the bellow can also take None, Edge, type[Edge].
147+
_Jac.connect(
148+
left=self, # type: ignore [arg-type]
149+
right=node, # type: ignore [arg-type]
150+
edge_spec=_Jac.build_edge(
151+
is_undirected=unidir, conn_type=edge, conn_assign=conn_assign # type: ignore [arg-type]
152+
),
153+
)
154+
return node
155+
156+
157+
class JacEdge(_JacArchiTypeBase, metaclass=JacMetaEdge):
158+
"""Base class for all the jac edge types."""
159+
160+
def __init__(self, *args, **kwargs) -> None: # noqa: ANN002, ANN003
161+
"""Initialize Jac architype base."""
162+
163+
164+
# ----------------------------------------------------------------------------
165+
# Decorators.
166+
# ----------------------------------------------------------------------------
167+
168+
169+
def with_entry(func: Callable) -> Callable:
170+
"""Mark a method as jac entry with this decorator."""
171+
# Ensure the functioin has 2 parameters (self, here).
172+
sig = inspect.signature(func, eval_str=True)
173+
param_count = len(sig.parameters)
174+
if param_count != 2:
175+
raise ValueError("Jac entry function must have exactly 2 parameters.")
176+
177+
# Get the entry node from the type hints.
178+
second_param_name = list(sig.parameters.keys())[1]
179+
entry_node = typing.get_type_hints(func).get(second_param_name)
180+
181+
# Mark the function as jac entry.
182+
setattr(func, "__jac_entry", entry_node) # noqa: B010
183+
return func
184+
185+
186+
def with_exit(func: Callable) -> Callable:
187+
"""Mark a method as jac exit with this decorator."""
188+
# Ensure the functioin has 2 parameters (self, here).
189+
sig = inspect.signature(func, eval_str=True)
190+
param_count = len(sig.parameters)
191+
if param_count != 2:
192+
raise ValueError("Jac exit function must have exactly 2 parameters.")
193+
194+
# Get the entry node from the type hints.
195+
second_param_name = list(sig.parameters.keys())[1]
196+
exit_node = typing.get_type_hints(func).get(second_param_name)
197+
198+
# Mark the function as jac entry.
199+
setattr(func, "__jac_exit", exit_node) # noqa: B010
200+
return func
201+
202+
203+
# ----------------------------------------------------------------------------
204+
# Functions.
205+
# ----------------------------------------------------------------------------
206+
207+
208+
def field_default(
209+
value: T | None = None,
210+
gen_func: None | Callable[[], T] = None,
211+
postinit: bool = False,
212+
) -> T:
213+
"""Set the default value to jac architype dataclass."""
214+
if postinit:
215+
return field(init=False)
216+
if value is not None:
217+
gen_func = lambda: value # noqa: E731
218+
assert gen_func is not None
219+
return _Jac.has_instance_default(gen_func=gen_func)
220+
221+
222+
jac_import = _Jac.jac_import
223+
root = _Jac.get_root()
224+
static = ClassVar
225+
226+
root.spawn = types.MethodType(JacNode.spawn, root)
227+
root.connect = types.MethodType(JacNode.connect, root)

0 commit comments

Comments
 (0)