diff --git a/examples/reference/connect_expressions.jac b/examples/reference/connect_expressions.jac index e69de29bb..38c32679e 100644 --- a/examples/reference/connect_expressions.jac +++ b/examples/reference/connect_expressions.jac @@ -0,0 +1,31 @@ +node node_a{ + has value:int; +} +walker Creator { + can create with ` entry; + can travel with `|node_a entry; +} +edge MyEdge { + has val:int =5; +} +:walker:Creator:can:create{ + end = ; + for i = 0 to i<7 by i+=1{ + if i % 2==0{ + end ++> end := node_a(value=i); + } + else{ + end +:MyEdge:val=i:+> end := node_a(value=i+10); + } + } +} +:walker:Creator:can:travel{ + for i in -:MyEdge:val<=6 :->{ + print(i.value); + } + visit-->; +} +with entry{ + spawn Creator(); + # print(._jac_.gen_dot()); +} \ No newline at end of file diff --git a/examples/reference/data_spatial_references.jac b/examples/reference/data_spatial_references.jac index ce7d2ade2..b28b04f64 100644 --- a/examples/reference/data_spatial_references.jac +++ b/examples/reference/data_spatial_references.jac @@ -1,36 +1,3 @@ -import:py random; -node MyNode { - has value: int; - -} -edge MyEdge { - has val:int =5; -} -walker Walk { - #has count: int = 0; - can create with ` entry; - #can do_something with ` entry; -} - -:walker:Walk:can:create{ - for i=0 to i<5 by i+=1 { - +:MyEdge:val=random.randint(1,10):+> MyNode(value=i+1); - ++> MyNode(value=i+1); - } - visit -->; -} -# :walker:Walk:can:do_something{ -# for i in -:MyEdge:val<=5 :->{ -# print(i.val); -# } -# } - - -with entry { - spawn Walk(); - #._jac_.gen_dot(); - -} diff --git a/examples/reference/data_spatial_references.py b/examples/reference/data_spatial_references.py index 879f7a747..8b1378917 100644 --- a/examples/reference/data_spatial_references.py +++ b/examples/reference/data_spatial_references.py @@ -1,22 +1 @@ -""" -need to create some type of edge with fields -edge MyEdge { - has val:int =5; -} - -node MyNode { -} - -create 10 edges off of root - - +:MyEdge:val=randint(0, 10):+> MyNode() - -5 of these - - ++> MyNode() - -then we test the refs -for i in -:MyEdge:val<=5:->: - print i.val; -""" diff --git a/examples/reference/data_spatial_spawn_expressions.jac b/examples/reference/data_spatial_spawn_expressions.jac index 640ca3fc1..b8f978411 100644 --- a/examples/reference/data_spatial_spawn_expressions.jac +++ b/examples/reference/data_spatial_spawn_expressions.jac @@ -23,5 +23,5 @@ node node_a{ } with entry{ - :> Adder spawn ; # spawn will iniiate the walker Adder from root node + Adder() spawn ; # spawn will iniiate the walker Adder from root node } \ No newline at end of file diff --git a/examples/reference/data_spatial_walker_statements.jac b/examples/reference/data_spatial_walker_statements.jac index e1c8f2006..52e5dd231 100644 --- a/examples/reference/data_spatial_walker_statements.jac +++ b/examples/reference/data_spatial_walker_statements.jac @@ -28,11 +28,11 @@ walker walker_2{ visit -->; } :node:node_a:can:print_something -> str{ - f"walker_1 entered to {}"|>print; + print(f"walker_1 entered to {}"); visit -->; } with entry{ - :> walker_1 spawn ; #walker_1 statrt walking from root node + walker_1() spawn ; #walker_1 statrt walking from root node spawn walker_2(); #after walker_1 finish walking walker_2 start walking to nodes } \ No newline at end of file diff --git a/jaclang/compiler/passes/main/pyast_gen_pass.py b/jaclang/compiler/passes/main/pyast_gen_pass.py index b2c00acb2..6fac1990f 100644 --- a/jaclang/compiler/passes/main/pyast_gen_pass.py +++ b/jaclang/compiler/passes/main/pyast_gen_pass.py @@ -2043,18 +2043,13 @@ def translate_edge_op_ref(self, loc: ast3.AST, node: ast.EdgeOpRef) -> ast3.AST: node.filter_type.gen.py_ast if node.filter_type else self.sync(ast3.Constant(value=None)), + node.filter_cond.gen.py_ast + if node.filter_cond + else self.sync(ast3.Constant(value=None)), ], keywords=[], ) ) - if node.filter_cond: - ret = self.sync( - ast3.Call( - func=node.filter_cond.gen.py_ast, - args=[ret], - keywords=[], - ) - ) return ret def exit_disconnect_op(self, node: ast.DisconnectOp) -> None: diff --git a/jaclang/core/construct.py b/jaclang/core/construct.py index e4b8dc309..f4680d62a 100644 --- a/jaclang/core/construct.py +++ b/jaclang/core/construct.py @@ -40,24 +40,45 @@ def connect_node(self, nd: NodeArchitype, edg: EdgeArchitype) -> NodeArchitype: edg._jac_.attach(self.obj, nd) return self.obj + # def edges_to_nodes( + # self, dir: EdgeDir, filter_type: Optional[type], filter_func: Optional[Callable] + # ) -> list[NodeArchitype]: + # """Get set of nodes connected to this node.""" + # filter_func = filter_func if filter_func else lambda x: x + # ret_nodes: list[NodeArchitype] = [] + # if dir in [EdgeDir.OUT]: + # edge_list = [] + # for x in self.edges[EdgeDir.OUT]: + # if x._jac_.target and (not filter_type or isinstance(x, filter_type)): + # edge_list.append(x) + # new_edge = filter_func(edge_list) + # for i in new_edge: + # ret_nodes.append(i._jac_.target) + # elif dir in [EdgeDir.IN]: + # edge_list = [] + # for i in self.edges[EdgeDir.IN]: + # if i._jac_.source and (not filter_type or isinstance(i, filter_type)): + # edge_list.append(i) + # new_edge = filter_func(edge_list) + # for i in new_edge: + # ret_nodes.append(i._jac_.source) + # return ret_nodes + def edges_to_nodes( - self, dir: EdgeDir, filter_type: Optional[type] + self, dir: EdgeDir, filter_type: Optional[type], filter_func: Optional[Callable] ) -> list[NodeArchitype]: """Get set of nodes connected to this node.""" - ret_nodes: list[NodeArchitype] = [] - if dir in [EdgeDir.OUT]: - for i in self.edges[EdgeDir.OUT]: - if i._jac_.target and ( - not filter_type or isinstance(i._jac_.target, filter_type) - ): - ret_nodes.append(i._jac_.target) - elif dir in [EdgeDir.IN]: - for i in self.edges[EdgeDir.IN]: - if i._jac_.source and ( - not filter_type or isinstance(i._jac_.source, filter_type) - ): - ret_nodes.append(i._jac_.source) - return ret_nodes + filter_func = filter_func or (lambda x: x) + edge_list = [ + e + for e in self.edges[dir] + if getattr(e._jac_, "target" if dir == EdgeDir.OUT else "source", None) + and (not filter_type or isinstance(e, filter_type)) + ] + return [ + getattr(e._jac_, "target" if dir == EdgeDir.OUT else "source") + for e in filter_func(edge_list) + ] def gen_dot(self, dot_file: Optional[str] = None) -> str: """Generate Dot file for visualizing nodes and edges.""" @@ -80,8 +101,7 @@ def gen_dot(self, dot_file: Optional[str] = None) -> str: if dot_file: with open(dot_file, "w") as f: f.write(dot_content + "}") - else: - print(dot_content + "}") + return dot_content + "}" @dataclass(eq=False) diff --git a/jaclang/plugin/default.py b/jaclang/plugin/default.py index 53f692039..9c6f2e51d 100644 --- a/jaclang/plugin/default.py +++ b/jaclang/plugin/default.py @@ -129,10 +129,11 @@ def edge_ref( node_obj: NodeArchitype, dir: EdgeDir, filter_type: Optional[type], + filter_func: Optional[Callable], ) -> list[NodeArchitype]: """Jac's apply_dir stmt feature.""" if isinstance(node_obj, NodeArchitype): - return node_obj._jac_.edges_to_nodes(dir, filter_type) + return node_obj._jac_.edges_to_nodes(dir, filter_type, filter_func) else: raise TypeError("Invalid node object") @@ -192,7 +193,7 @@ def get_root() -> Architype: def build_edge( edge_dir: EdgeDir, conn_type: Optional[Type[Architype]], - conn_assign: Optional[tuple], + conn_assign: Optional[tuple[tuple, tuple]], ) -> Architype: """Jac's root getter.""" conn_type = conn_type if conn_type else GenericEdge @@ -201,4 +202,10 @@ def build_edge( edge._jac_.dir = edge_dir else: raise TypeError("Invalid edge object") + if conn_assign: + for fld, val in zip(conn_assign[0], conn_assign[1]): + if hasattr(edge, fld): + setattr(edge, fld, val) + else: + raise ValueError(f"Invalid attribute: {fld}") return edge diff --git a/jaclang/plugin/feature.py b/jaclang/plugin/feature.py index e5aec6b9e..927afca9b 100644 --- a/jaclang/plugin/feature.py +++ b/jaclang/plugin/feature.py @@ -86,10 +86,11 @@ def edge_ref( node_obj: NodeArchitype, dir: EdgeDir, filter_type: Optional[type], + filter_func: Optional[Callable], ) -> list[NodeArchitype]: """Jac's apply_dir stmt feature.""" return JacFeature.pm.hook.edge_ref( - node_obj=node_obj, dir=dir, filter_type=filter_type + node_obj=node_obj, dir=dir, filter_type=filter_type, filter_func=filter_func ) @staticmethod @@ -125,7 +126,7 @@ def get_root() -> Architype: def build_edge( edge_dir: EdgeDir, conn_type: Optional[Type[Architype]], - conn_assign: Optional[tuple], + conn_assign: Optional[tuple[tuple, tuple]], ) -> Architype: """Jac's root getter.""" return JacFeature.pm.hook.build_edge( diff --git a/jaclang/plugin/spec.py b/jaclang/plugin/spec.py index 7c774fb78..0cab0b3bd 100644 --- a/jaclang/plugin/spec.py +++ b/jaclang/plugin/spec.py @@ -110,6 +110,7 @@ def edge_ref( node_obj: NodeArchitype, dir: EdgeDir, filter_type: Optional[type], + filter_func: Optional[Callable], ) -> list[NodeArchitype]: """Jac's apply_dir stmt feature.""" raise NotImplementedError @@ -152,7 +153,7 @@ def get_root() -> Architype: def build_edge( edge_dir: EdgeDir, conn_type: Optional[Type[Architype]], - conn_assign: Optional[tuple], + conn_assign: Optional[tuple[tuple, tuple]], ) -> Architype: """Jac's root getter.""" raise NotImplementedError diff --git a/jaclang/tests/fixtures/edge_ops.jac b/jaclang/tests/fixtures/edge_ops.jac new file mode 100644 index 000000000..5eea5633f --- /dev/null +++ b/jaclang/tests/fixtures/edge_ops.jac @@ -0,0 +1,43 @@ +import:py random; + +node node_a{ + has value:int; +} + +walker Creator { + can create with ` entry; + can travel with `|node_a entry; +} + +edge MyEdge { + has val:int =5, + val2:int = 10; +} + +:walker:Creator:can:create{ + end = ; + for i = 0 to i<5 by i+=1{ + end ++> end := node_a(value=i+1); + if i == 2{ + for j=0 to j<3 by j+=1 { + end +:MyEdge:val=random.randint(1,15), val2=random.randint(1,5):+> node_a(value=j+10); + } + print([(i.val, i.val2) for i in list(end._jac_.edges.values())[1]]); + } + } + for i=0 to i<3 by i+=1 { + end +:MyEdge:val=random.randint(1,20):+> node_a(value=i+5); + } +} +:walker:Creator:can:travel{ + for i in -:MyEdge:val<=5:-> { + print(i.value); + } + visit-->; +} + +with entry{ + random.seed(1); + spawn Creator(); + #print(._jac_.gen_dot()); +} \ No newline at end of file diff --git a/jaclang/tests/fixtures/gendot_bubble_sort.jac b/jaclang/tests/fixtures/gendot_bubble_sort.jac index 2eb934aea..68f93f177 100644 --- a/jaclang/tests/fixtures/gendot_bubble_sort.jac +++ b/jaclang/tests/fixtures/gendot_bubble_sort.jac @@ -57,5 +57,5 @@ with entry{ spawn walker1(); spawn walker2(); spawn walker3(); - ._jac_.gen_dot(); + print(._jac_.gen_dot()); } \ No newline at end of file diff --git a/jaclang/tests/fixtures/semstr.jac b/jaclang/tests/fixtures/semstr.jac index 651c0b1b2..7795a0f7e 100644 --- a/jaclang/tests/fixtures/semstr.jac +++ b/jaclang/tests/fixtures/semstr.jac @@ -5,7 +5,6 @@ enum 'enum' emotion { Angry } - node 'personality' personality{ has diff --git a/jaclang/tests/test_language.py b/jaclang/tests/test_language.py index 2833cc842..e0be59460 100644 --- a/jaclang/tests/test_language.py +++ b/jaclang/tests/test_language.py @@ -202,3 +202,15 @@ def test_has_lambda_goodness(self) -> None: stdout_value = captured_output.getvalue() self.assertEqual(stdout_value.split("\n")[0], "mylist: [1, 2, 3]") self.assertEqual(stdout_value.split("\n")[1], "mydict: {'a': 2, 'b': 4}") + + def test_conn_assign_on_edges(self) -> None: + """Test conn assign on edges.""" + construct.root._jac_.edges[construct.EdgeDir.OUT].clear() + captured_output = io.StringIO() + sys.stdout = captured_output + jac_import("edge_ops", self.fixture_abs_path("./")) + sys.stdout = sys.__stdout__ + stdout_value = captured_output.getvalue() + self.assertEqual(stdout_value.split("\n")[0], "[(3, 5), (14, 1), (5, 1)]") + self.assertEqual(stdout_value.split("\n")[1], "10") + self.assertEqual(stdout_value.split("\n")[2], "12")