Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[REFACTOR]: Migrate all process to pluggy
Browse files Browse the repository at this point in the history
amadolid committed Sep 26, 2024
1 parent f1566c3 commit 972e89f
Showing 15 changed files with 1,093 additions and 774 deletions.
15 changes: 5 additions & 10 deletions jac/jaclang/__init__.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,12 @@
"""The Jac Programming Language."""

from jaclang.plugin.default import ( # noqa: E402
JacBuiltin,
JacCmdDefaults,
JacFeatureDefaults,
)
from jaclang.plugin.feature import JacFeature, pm # noqa: E402
from jaclang.plugin.default import JacFeatureImpl
from jaclang.plugin.feature import JacFeature, hookmanager

jac_import = JacFeature.jac_import

pm.register(JacFeatureDefaults)
pm.register(JacBuiltin)
pm.register(JacCmdDefaults)
pm.load_setuptools_entrypoints("jac")

hookmanager.register(JacFeatureImpl)
hookmanager.load_setuptools_entrypoints("jac")

__all__ = ["jac_import"]
5 changes: 4 additions & 1 deletion jac/jaclang/cli/cli.py
Original file line number Diff line number Diff line change
@@ -28,6 +28,7 @@


Cmd.create_cmd()
Jac.setup()


@cmd_registry.register
@@ -283,7 +284,9 @@ def enter(

jctx.set_entry_node(node)

if isinstance(architype, WalkerArchitype) and jctx.validate_access():
if isinstance(architype, WalkerArchitype) and Jac.check_read_access(
jctx.entry_node
):
Jac.spawn_call(jctx.entry_node.architype, architype)

jctx.close()
6 changes: 3 additions & 3 deletions jac/jaclang/plugin/builtin.py
Original file line number Diff line number Diff line change
@@ -19,17 +19,17 @@ def dotgen(
dot_file: Optional[str] = None,
) -> str:
"""Print the dot graph."""
from jaclang.plugin.feature import pm
from jaclang.plugin.feature import JacFeature as Jac

root = pm.hook.get_root()
root = Jac.get_root()
node = node if node is not None else root
depth = depth if depth is not None else -1
traverse = traverse if traverse is not None else False
bfs = bfs if bfs is not None else True
edge_limit = edge_limit if edge_limit is not None else 512
node_limit = node_limit if node_limit is not None else 512

return pm.hook.dotgen(
return Jac.dotgen(
edge_type=edge_type,
node=node,
depth=depth,
769 changes: 566 additions & 203 deletions jac/jaclang/plugin/default.py

Large diffs are not rendered by default.

363 changes: 265 additions & 98 deletions jac/jaclang/plugin/feature.py

Large diffs are not rendered by default.

316 changes: 231 additions & 85 deletions jac/jaclang/plugin/spec.py
Original file line number Diff line number Diff line change
@@ -11,37 +11,235 @@
Optional,
ParamSpec,
Sequence,
TYPE_CHECKING,
Type,
TypeVar,
Union,
)
from uuid import UUID

import jaclang.compiler.absyntree as ast
from jaclang.compiler import absyntree as ast
from jaclang.compiler.constant import EdgeDir
from jaclang.compiler.passes.main.pyast_gen_pass import PyastGenPass

if TYPE_CHECKING:
from jaclang.plugin.default import (
Architype,
EdgeDir,
WalkerArchitype,
Root,
DSFunc,
)
from jaclang.runtimelib.constructs import EdgeArchitype, NodeAnchor, NodeArchitype
from jaclang.runtimelib.context import ExecutionContext
from jaclang.runtimelib.constructs import (
AccessLevel,
Anchor,
Architype,
DSFunc,
EdgeAnchor,
EdgeArchitype,
NodeAnchor,
NodeArchitype,
Root,
WalkerArchitype,
)
from jaclang.runtimelib.context import ExecutionContext

import pluggy

hookspec = pluggy.HookspecMarker("jac")
hookmanager = pluggy.PluginManager("jac")

T = TypeVar("T")
P = ParamSpec("P")


class JacFeatureSpec:
class JacAccessValidationSpec:
"""Jac Access Validation Specs."""

@staticmethod
@hookspec(firstresult=True)
def allow_root(
anchor: Anchor, root_id: UUID, level: AccessLevel | int | str = AccessLevel.READ
) -> None:
"""Allow all access from target root graph to current Architype."""
raise NotImplementedError

@staticmethod
@hookspec(firstresult=True)
def disallow_root(
anchor: Anchor, root_id: UUID, level: AccessLevel | int | str = AccessLevel.READ
) -> None:
"""Disallow all access from target root graph to current Architype."""
raise NotImplementedError

@staticmethod
@hookspec(firstresult=True)
def unrestrict(
anchor: Anchor, level: AccessLevel | int | str = AccessLevel.READ
) -> None:
"""Allow everyone to access current Architype."""
raise NotImplementedError

@staticmethod
@hookspec(firstresult=True)
def restrict(anchor: Anchor) -> None:
"""Disallow others to access current Architype."""
raise NotImplementedError

@staticmethod
@hookspec(firstresult=True)
def check_read_access(to: Anchor) -> bool:
"""Read Access Validation."""
raise NotImplementedError

@staticmethod
@hookspec(firstresult=True)
def check_connect_access(to: Anchor) -> bool:
"""Write Access Validation."""
raise NotImplementedError

@staticmethod
@hookspec(firstresult=True)
def check_write_access(to: Anchor) -> bool:
"""Write Access Validation."""
raise NotImplementedError

@staticmethod
@hookspec(firstresult=True)
def check_access_level(to: Anchor) -> AccessLevel:
"""Access validation."""
raise NotImplementedError


class JacNodeSpec:
"""Jac Node Operations."""

@staticmethod
@hookspec(firstresult=True)
def node_dot(node: NodeArchitype, dot_file: Optional[str] = None) -> str:
"""Generate Dot file for visualizing nodes and edges."""
raise NotImplementedError

@staticmethod
@hookspec(firstresult=True)
def get_edges(
node: NodeAnchor,
dir: EdgeDir,
filter_func: Optional[Callable[[list[EdgeArchitype]], list[EdgeArchitype]]],
target_obj: Optional[list[NodeArchitype]],
) -> list[EdgeArchitype]:
"""Get edges connected to this node."""
raise NotImplementedError

@staticmethod
@hookspec(firstresult=True)
def edges_to_nodes(
node: NodeAnchor,
dir: EdgeDir,
filter_func: Optional[Callable[[list[EdgeArchitype]], list[EdgeArchitype]]],
target_obj: Optional[list[NodeArchitype]],
) -> list[NodeArchitype]:
"""Get set of nodes connected to this node."""
raise NotImplementedError

@staticmethod
@hookspec(firstresult=True)
def remove_edge(node: NodeAnchor, edge: EdgeAnchor) -> None:
"""Remove reference without checking sync status."""
raise NotImplementedError


class JacEdgeSpec:
"""Jac Edge Operations."""

@staticmethod
@hookspec(firstresult=True)
def detach(edge: EdgeAnchor) -> None:
"""Detach edge from nodes."""
raise NotImplementedError


class JacWalkerSpec:
"""Jac Edge Operations."""

@staticmethod
@hookspec(firstresult=True)
def visit_node(
walker: WalkerArchitype,
expr: (
list[NodeArchitype | EdgeArchitype]
| list[NodeArchitype]
| list[EdgeArchitype]
| NodeArchitype
| EdgeArchitype
),
) -> bool: # noqa: ANN401
"""Jac's visit stmt feature."""
raise NotImplementedError

@staticmethod
@hookspec(firstresult=True)
def ignore(
walker: WalkerArchitype,
expr: (
list[NodeArchitype | EdgeArchitype]
| list[NodeArchitype]
| list[EdgeArchitype]
| NodeArchitype
| EdgeArchitype
),
) -> bool:
"""Jac's ignore stmt feature."""
raise NotImplementedError

@staticmethod
@hookspec(firstresult=True)
def spawn_call(op1: Architype, op2: Architype) -> WalkerArchitype:
"""Invoke data spatial call."""
raise NotImplementedError

@staticmethod
@hookspec(firstresult=True)
def disengage(walker: WalkerArchitype) -> bool:
"""Jac's disengage stmt feature."""
raise NotImplementedError


class JacBuiltinSpec:
"""Jac Builtins."""

@staticmethod
@hookspec(firstresult=True)
def dotgen(
node: NodeArchitype,
depth: int,
traverse: bool,
edge_type: Optional[list[str]],
bfs: bool,
edge_limit: int,
node_limit: int,
dot_file: Optional[str],
) -> str:
"""Print the dot graph."""
raise NotImplementedError


class JacCmdSpec:
"""Jac CLI command."""

@staticmethod
@hookspec
def create_cmd() -> None:
"""Create Jac CLI cmds."""
raise NotImplementedError


class JacFeatureSpec(
JacAccessValidationSpec,
JacNodeSpec,
JacEdgeSpec,
JacWalkerSpec,
JacBuiltinSpec,
JacCmdSpec,
):
"""Jac Feature."""

@staticmethod
@hookspec(firstresult=True)
def setup() -> None:
"""Set Class References."""
raise NotImplementedError

@staticmethod
@hookspec(firstresult=True)
def get_context() -> ExecutionContext:
@@ -51,13 +249,13 @@ def get_context() -> ExecutionContext:
@staticmethod
@hookspec(firstresult=True)
def get_object(id: str) -> Architype | None:
"""Get object given id.."""
"""Get object by id."""
raise NotImplementedError

@staticmethod
@hookspec(firstresult=True)
def object_ref(obj: Architype) -> str:
"""Get object given id.."""
"""Get object's id."""
raise NotImplementedError

@staticmethod
@@ -158,54 +356,12 @@ def has_instance_default(gen_func: Callable[[], T]) -> T:
"""Jac's has container default feature."""
raise NotImplementedError

@staticmethod
@hookspec(firstresult=True)
def spawn_call(op1: Architype, op2: Architype) -> WalkerArchitype:
"""Jac's spawn operator feature."""
raise NotImplementedError

@staticmethod
@hookspec(firstresult=True)
def report(expr: Any) -> Any: # noqa: ANN401
"""Jac's report stmt feature."""
raise NotImplementedError

@staticmethod
@hookspec(firstresult=True)
def ignore(
walker: WalkerArchitype,
expr: (
list[NodeArchitype | EdgeArchitype]
| list[NodeArchitype]
| list[EdgeArchitype]
| NodeArchitype
| EdgeArchitype
),
) -> bool:
"""Jac's ignore stmt feature."""
raise NotImplementedError

@staticmethod
@hookspec(firstresult=True)
def visit_node(
walker: WalkerArchitype,
expr: (
list[NodeArchitype | EdgeArchitype]
| list[NodeArchitype]
| list[EdgeArchitype]
| NodeArchitype
| EdgeArchitype
),
) -> bool: # noqa: ANN401
"""Jac's visit stmt feature."""
raise NotImplementedError

@staticmethod
@hookspec(firstresult=True)
def disengage(walker: WalkerArchitype) -> bool: # noqa: ANN401
"""Jac's disengage stmt feature."""
raise NotImplementedError

@staticmethod
@hookspec(firstresult=True)
def edge_ref(
@@ -273,6 +429,22 @@ def build_edge(
"""Jac's root getter."""
raise NotImplementedError

@staticmethod
@hookspec(firstresult=True)
def save(
obj: Architype | Anchor,
) -> None:
"""Destroy object."""
raise NotImplementedError

@staticmethod
@hookspec(firstresult=True)
def destroy(
obj: Architype | Anchor,
) -> None:
"""Destroy object."""
raise NotImplementedError

@staticmethod
@hookspec(firstresult=True)
def get_semstr_type(
@@ -339,30 +511,4 @@ def get_by_llm_call_args(_pass: PyastGenPass, node: ast.FuncCall) -> dict:
raise NotImplementedError


class JacBuiltin:
"""Jac Builtins."""

@staticmethod
@hookspec(firstresult=True)
def dotgen(
node: NodeArchitype,
depth: int,
traverse: bool,
edge_type: list[str],
bfs: bool,
edge_limit: int,
node_limit: int,
dot_file: Optional[str],
) -> str:
"""Print the dot graph."""
raise NotImplementedError


class JacCmdSpec:
"""Jac CLI command."""

@staticmethod
@hookspec
def create_cmd() -> None:
"""Create Jac CLI cmds."""
raise NotImplementedError
hookmanager.add_hookspecs(JacFeatureSpec)
18 changes: 9 additions & 9 deletions jac/jaclang/plugin/tests/fixtures/other_root_access.jac
Original file line number Diff line number Diff line change
@@ -36,7 +36,7 @@ walker create_node {
walker create_other_root {
can enter with `root entry {
other_root = `root().__jac__;
other_root.save();
Jac.save(other_root);
print(other_root.id);
}
}
@@ -46,17 +46,17 @@ walker allow_other_root_access {

can enter_root with `root entry {
if self.via_all {
here.__jac__.unrestrict(self.level);
Jac.unrestrict(here.__jac__, self.level);
} else {
here.__jac__.allow_root(UUID(self.root_id), self.level);
Jac.allow_root(here.__jac__, UUID(self.root_id), self.level);
}
}

can enter_nested with A entry {
if self.via_all {
here.__jac__.unrestrict(self.level);
Jac.unrestrict(here.__jac__, self.level);
} else {
here.__jac__.allow_root(UUID(self.root_id), self.level);
Jac.allow_root(here.__jac__, UUID(self.root_id), self.level);
}
}
}
@@ -66,17 +66,17 @@ walker disallow_other_root_access {

can enter_root with `root entry {
if self.via_all {
here.__jac__.restrict();
Jac.restrict(here.__jac__);
} else {
here.__jac__.disallow_root(UUID(self.root_id));
Jac.disallow_root(here.__jac__, UUID(self.root_id));
}
}

can enter_nested with A entry {
if self.via_all {
here.__jac__.restrict();
Jac.restrict(here.__jac__);
} else {
here.__jac__.disallow_root(UUID(self.root_id));
Jac.disallow_root(here.__jac__, UUID(self.root_id));
}
}
}
4 changes: 2 additions & 2 deletions jac/jaclang/plugin/tests/test_features.py
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@
import inspect
from typing import List, Type

from jaclang.plugin.default import JacFeatureDefaults
from jaclang.plugin.default import JacFeatureImpl
from jaclang.plugin.feature import JacFeature
from jaclang.plugin.spec import JacFeatureSpec
from jaclang.utils.test import TestCase
@@ -46,7 +46,7 @@ def test_feature_funcs_synced(self) -> None:
"""Test if JacFeature, JacFeatureDefaults, and JacFeatureSpec have synced methods."""
# Get methods of each class
jac_feature_methods = self.get_methods(JacFeature)
jac_feature_defaults_methods = self.get_methods(JacFeatureDefaults)
jac_feature_defaults_methods = self.get_methods(JacFeatureImpl)
jac_feature_spec_methods = self.get_methods(JacFeatureSpec)

# Check if all methods are the same in all classes
353 changes: 1 addition & 352 deletions jac/jaclang/runtimelib/architype.py
Original file line number Diff line number Diff line change
@@ -7,12 +7,9 @@
from logging import getLogger
from pickle import dumps
from types import UnionType
from typing import Any, Callable, ClassVar, Iterable, Optional, TypeVar
from typing import Any, Callable, ClassVar, Optional, TypeVar
from uuid import UUID, uuid4

from jaclang.compiler.constant import EdgeDir
from jaclang.runtimelib.utils import collect_node_connections

logger = getLogger(__name__)

TARCH = TypeVar("TARCH", bound="Architype")
@@ -77,128 +74,6 @@ class Anchor:
persistent: bool = False
hash: int = 0

##########################################################################
# ACCESS CONTROL: TODO: Make Base Type #
##########################################################################

def allow_root(
self, root_id: UUID, level: AccessLevel | int | str = AccessLevel.READ
) -> None:
"""Allow all access from target root graph to current Architype."""
level = AccessLevel.cast(level)
access = self.access.roots

_root_id = str(root_id)
if level != access.anchors.get(_root_id, AccessLevel.NO_ACCESS):
access.anchors[_root_id] = level

def disallow_root(
self, root_id: UUID, level: AccessLevel | int | str = AccessLevel.READ
) -> None:
"""Disallow all access from target root graph to current Architype."""
level = AccessLevel.cast(level)
access = self.access.roots

access.anchors.pop(str(root_id), None)

def unrestrict(self, level: AccessLevel | int | str = AccessLevel.READ) -> None:
"""Allow everyone to access current Architype."""
level = AccessLevel.cast(level)
if level != self.access.all:
self.access.all = level

def restrict(self) -> None:
"""Disallow others to access current Architype."""
if self.access.all > AccessLevel.NO_ACCESS:
self.access.all = AccessLevel.NO_ACCESS

def has_read_access(self, to: Anchor) -> bool:
"""Read Access Validation."""
if not (access_level := self.access_level(to) > AccessLevel.NO_ACCESS):
logger.info(
f"Current root doesn't have read access to {to.__class__.__name__}[{to.id}]"
)
return access_level

def has_connect_access(self, to: Anchor) -> bool:
"""Write Access Validation."""
if not (access_level := self.access_level(to) > AccessLevel.READ):
logger.info(
f"Current root doesn't have connect access to {to.__class__.__name__}[{to.id}]"
)
return access_level

def has_write_access(self, to: Anchor) -> bool:
"""Write Access Validation."""
if not (access_level := self.access_level(to) > AccessLevel.CONNECT):
logger.info(
f"Current root doesn't have write access to {to.__class__.__name__}[{to.id}]"
)
return access_level

def access_level(self, to: Anchor) -> AccessLevel:
"""Access validation."""
if not to.persistent:
return AccessLevel.WRITE

from jaclang.plugin.feature import JacFeature as Jac

jctx = Jac.get_context()

jroot = jctx.root

# if current root is system_root
# if current root id is equal to target anchor's root id
# if current root is the target anchor
if jroot == jctx.system_root or jroot.id == to.root or jroot == to:
return AccessLevel.WRITE

access_level = AccessLevel.NO_ACCESS

# if target anchor have set access.all
if (to_access := to.access).all > AccessLevel.NO_ACCESS:
access_level = to_access.all

# if target anchor's root have set allowed roots
# if current root is allowed to the whole graph of target anchor's root
if to.root and isinstance(to_root := jctx.mem.find_one(to.root), Anchor):
if to_root.access.all > access_level:
access_level = to_root.access.all

level = to_root.access.roots.check(str(jroot.id))
if level > AccessLevel.NO_ACCESS and access_level == AccessLevel.NO_ACCESS:
access_level = level

# if target anchor have set allowed roots
# if current root is allowed to target anchor
level = to_access.roots.check(str(jroot.id))
if level > AccessLevel.NO_ACCESS and access_level == AccessLevel.NO_ACCESS:
access_level = level

return access_level

# ---------------------------------------------------------------------- #

def save(self) -> None:
"""Save Anchor."""
from jaclang.plugin.feature import JacFeature as Jac

jctx = Jac.get_context()

self.persistent = True
self.root = jctx.root.id

jctx.mem.set(self.id, self)

def destroy(self) -> None:
"""Destroy Anchor."""
from jaclang.plugin.feature import JacFeature as Jac

jctx = Jac.get_context()

if jctx.root.has_write_access(self):
jctx.mem.remove(self.id)

def is_populated(self) -> bool:
"""Check if state."""
return "architype" in self.__dict__
@@ -304,122 +179,6 @@ class NodeAnchor(Anchor):
architype: NodeArchitype
edges: list[EdgeAnchor]

def get_edges(
self,
dir: EdgeDir,
filter_func: Optional[Callable[[list[EdgeArchitype]], list[EdgeArchitype]]],
target_obj: Optional[list[NodeArchitype]],
) -> list[EdgeArchitype]:
"""Get edges connected to this node."""
from jaclang.plugin.feature import JacFeature as Jac

root = Jac.get_root().__jac__
ret_edges: list[EdgeArchitype] = []
for anchor in self.edges:
if (
(source := anchor.source)
and (target := anchor.target)
and (not filter_func or filter_func([anchor.architype]))
and source.architype
and target.architype
):
if (
dir in [EdgeDir.OUT, EdgeDir.ANY]
and self == source
and (not target_obj or target.architype in target_obj)
and root.has_read_access(target)
):
ret_edges.append(anchor.architype)
if (
dir in [EdgeDir.IN, EdgeDir.ANY]
and self == target
and (not target_obj or source.architype in target_obj)
and root.has_read_access(source)
):
ret_edges.append(anchor.architype)
return ret_edges

def edges_to_nodes(
self,
dir: EdgeDir,
filter_func: Optional[Callable[[list[EdgeArchitype]], list[EdgeArchitype]]],
target_obj: Optional[list[NodeArchitype]],
) -> list[NodeArchitype]:
"""Get set of nodes connected to this node."""
from jaclang.plugin.feature import JacFeature as Jac

root = Jac.get_root().__jac__
ret_edges: list[NodeArchitype] = []
for anchor in self.edges:
if (
(source := anchor.source)
and (target := anchor.target)
and (not filter_func or filter_func([anchor.architype]))
and source.architype
and target.architype
):
if (
dir in [EdgeDir.OUT, EdgeDir.ANY]
and self == source
and (not target_obj or target.architype in target_obj)
and root.has_read_access(target)
):
ret_edges.append(target.architype)
if (
dir in [EdgeDir.IN, EdgeDir.ANY]
and self == target
and (not target_obj or source.architype in target_obj)
and root.has_read_access(source)
):
ret_edges.append(source.architype)
return ret_edges

def remove_edge(self, edge: EdgeAnchor) -> None:
"""Remove reference without checking sync status."""
for idx, ed in enumerate(self.edges):
if ed.id == edge.id:
self.edges.pop(idx)
break

def gen_dot(self, dot_file: Optional[str] = None) -> str:
"""Generate Dot file for visualizing nodes and edges."""
visited_nodes: set[NodeAnchor] = set()
connections: set[tuple[NodeArchitype, NodeArchitype, str]] = set()
unique_node_id_dict = {}

collect_node_connections(self, visited_nodes, connections)
dot_content = 'digraph {\nnode [style="filled", shape="ellipse", fillcolor="invis", fontcolor="black"];\n'
for idx, i in enumerate([nodes_.architype for nodes_ in visited_nodes]):
unique_node_id_dict[i] = (i.__class__.__name__, str(idx))
dot_content += f'{idx} [label="{i}"];\n'
dot_content += 'edge [color="gray", style="solid"];\n'

for pair in list(set(connections)):
dot_content += (
f"{unique_node_id_dict[pair[0]][1]} -> {unique_node_id_dict[pair[1]][1]}"
f' [label="{pair[2]}"];\n'
)
if dot_file:
with open(dot_file, "w") as f:
f.write(dot_content + "}")
return dot_content + "}"

def spawn_call(self, walk: WalkerAnchor) -> WalkerArchitype:
"""Invoke data spatial call."""
return walk.spawn_call(self)

def destroy(self) -> None:
"""Destroy Anchor."""
from jaclang.plugin.feature import JacFeature as Jac

jctx = Jac.get_context()

if jctx.root.has_write_access(self):
for edge in self.edges:
edge.destroy()

jctx.mem.remove(self.id)

def __getstate__(self) -> dict[str, object]:
"""Serialize Node Anchor."""
state = super().__getstate__()
@@ -439,30 +198,6 @@ class EdgeAnchor(Anchor):
target: NodeAnchor
is_undirected: bool

def __post_init__(self) -> None:
"""Populate edge to source and target."""
self.source.edges.append(self)
self.target.edges.append(self)

def detach(self) -> None:
"""Detach edge from nodes."""
self.source.remove_edge(self)
self.target.remove_edge(self)

def spawn_call(self, walk: WalkerAnchor) -> WalkerArchitype:
"""Invoke data spatial call."""
return walk.spawn_call(self.target)

def destroy(self) -> None:
"""Destroy Anchor."""
from jaclang.plugin.feature import JacFeature as Jac

jctx = Jac.get_context()

if jctx.root.has_write_access(self):
self.detach()
jctx.mem.remove(self.id)

def __getstate__(self) -> dict[str, object]:
"""Serialize Node Anchor."""
state = super().__getstate__()
@@ -489,81 +224,6 @@ class WalkerAnchor(Anchor):
ignores: list[Anchor] = field(default_factory=list)
disengaged: bool = False

def visit_node(self, anchors: Iterable[NodeAnchor | EdgeAnchor]) -> bool:
"""Walker visits node."""
before_len = len(self.next)
for anchor in anchors:
if anchor not in self.ignores:
if isinstance(anchor, NodeAnchor):
self.next.append(anchor)
elif isinstance(anchor, EdgeAnchor):
if target := anchor.target:
self.next.append(target)
else:
raise ValueError("Edge has no target.")
return len(self.next) > before_len

def ignore_node(self, anchors: Iterable[NodeAnchor | EdgeAnchor]) -> bool:
"""Walker ignores node."""
before_len = len(self.ignores)
for anchor in anchors:
if anchor not in self.ignores:
if isinstance(anchor, NodeAnchor):
self.ignores.append(anchor)
elif isinstance(anchor, EdgeAnchor):
if target := anchor.target:
self.ignores.append(target)
else:
raise ValueError("Edge has no target.")
return len(self.ignores) > before_len

def disengage_now(self) -> None:
"""Disengage walker from traversal."""
self.disengaged = True

def spawn_call(self, node: Anchor) -> WalkerArchitype:
"""Invoke data spatial call."""
if walker := self.architype:
self.path = []
self.next = [node]
while len(self.next):
if current_node := self.next.pop(0).architype:
for i in current_node._jac_entry_funcs_:
if not i.trigger or isinstance(walker, i.trigger):
if i.func:
i.func(current_node, walker)
else:
raise ValueError(f"No function {i.name} to call.")
if self.disengaged:
return walker
for i in walker._jac_entry_funcs_:
if not i.trigger or isinstance(current_node, i.trigger):
if i.func:
i.func(walker, current_node)
else:
raise ValueError(f"No function {i.name} to call.")
if self.disengaged:
return walker
for i in walker._jac_exit_funcs_:
if not i.trigger or isinstance(current_node, i.trigger):
if i.func:
i.func(walker, current_node)
else:
raise ValueError(f"No function {i.name} to call.")
if self.disengaged:
return walker
for i in current_node._jac_exit_funcs_:
if not i.trigger or isinstance(walker, i.trigger):
if i.func:
i.func(current_node, walker)
else:
raise ValueError(f"No function {i.name} to call.")
if self.disengaged:
return walker
self.ignores = []
return walker
raise Exception(f"Invalid Reference {self.id}")


class Architype:
"""Architype Protocol."""
@@ -595,17 +255,6 @@ class EdgeArchitype(Architype):

__jac__: EdgeAnchor

def __attach__(
self,
source: NodeAnchor,
target: NodeAnchor,
is_undirected: bool,
) -> None:
"""Attach EdgeAnchor properly."""
self.__jac__ = EdgeAnchor(
architype=self, source=source, target=target, is_undirected=is_undirected
)


class WalkerArchitype(Architype):
"""Walker Architype Protocol."""
2 changes: 2 additions & 0 deletions jac/jaclang/runtimelib/constructs.py
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@


from .architype import (
AccessLevel,
Anchor,
Architype,
DSFunc,
@@ -21,6 +22,7 @@
from .test import JacTestCheck, JacTestResult, JacTextTestRunner

__all__ = [
"AccessLevel",
"Anchor",
"NodeAnchor",
"EdgeAnchor",
4 changes: 0 additions & 4 deletions jac/jaclang/runtimelib/context.py
Original file line number Diff line number Diff line change
@@ -42,10 +42,6 @@ def init_anchor(
raise ValueError(f"Invalid anchor id {anchor_id} !")
return default

def validate_access(self) -> bool:
"""Validate access."""
return self.root.has_read_access(self.entry_node)

def set_entry_node(self, entry_node: str | None) -> None:
"""Override entry."""
self.entry_node = self.init_anchor(entry_node, self.root)
6 changes: 2 additions & 4 deletions jac/jaclang/runtimelib/memory.py
Original file line number Diff line number Diff line change
@@ -82,8 +82,6 @@ def close(self) -> None:
if isinstance(self.__shelf__, Shelf):
from jaclang.plugin.feature import JacFeature as Jac

root = Jac.get_root().__jac__

for anchor in self.__gc__:
self.__shelf__.pop(str(anchor.id), None)
self.__mem__.pop(anchor.id, None)
@@ -96,14 +94,14 @@ def close(self) -> None:
isinstance(p_d, NodeAnchor)
and isinstance(d, NodeAnchor)
and p_d.edges != d.edges
and root.has_connect_access(d)
and Jac.check_connect_access(d)
):
if not d.edges:
self.__shelf__.pop(_id, None)
continue
p_d.edges = d.edges

if root.has_write_access(d):
if Jac.check_write_access(d):
if hash(dumps(p_d.access)) != hash(dumps(d.access)):
p_d.access = d.access
if hash(dumps(d.architype)) != hash(dumps(d.architype)):
2 changes: 1 addition & 1 deletion jac/jaclang/tests/fixtures/edge_node_walk.jac
Original file line number Diff line number Diff line change
@@ -37,7 +37,7 @@ edge Edge_c {

with entry {
print(root spawn creator());
print(root.__jac__.gen_dot());
print(Jac.node_dot(root));
print([root-:Edge_a:->]);
print([root-:Edge_c:->]);
print([root-:Edge_a:->-:Edge_b:->]);
2 changes: 1 addition & 1 deletion jac/jaclang/tests/fixtures/edges_walk.jac
Original file line number Diff line number Diff line change
@@ -30,7 +30,7 @@ edge Edge_c{

with entry{
print(root spawn creator());
print(root.__jac__.gen_dot());
print(Jac.node_dot(root));
print([root -:Edge_a:->]);
print([root -:Edge_c:->]);
print([root -:Edge_a:-> -:Edge_b:->]);
2 changes: 1 addition & 1 deletion jac/jaclang/tests/fixtures/gendot_bubble_sort.jac
Original file line number Diff line number Diff line change
@@ -73,5 +73,5 @@ with entry {
root spawn walker1();
root spawn walker2();
root spawn walker3();
print(root.__jac__.gen_dot());
print(Jac.node_dot(root));
}

0 comments on commit 972e89f

Please sign in to comment.