jaclang 0.7.16__py3-none-any.whl → 0.7.18__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of jaclang might be problematic. Click here for more details.

Files changed (81) hide show
  1. jaclang/cli/cli.py +140 -77
  2. jaclang/compiler/absyntree.py +9 -4
  3. jaclang/compiler/constant.py +8 -8
  4. jaclang/compiler/parser.py +10 -2
  5. jaclang/compiler/passes/main/__init__.py +1 -1
  6. jaclang/compiler/passes/main/access_modifier_pass.py +96 -147
  7. jaclang/compiler/passes/main/fuse_typeinfo_pass.py +152 -50
  8. jaclang/compiler/passes/main/import_pass.py +88 -59
  9. jaclang/compiler/passes/main/py_collect_dep_pass.py +70 -0
  10. jaclang/compiler/passes/main/pyast_gen_pass.py +46 -6
  11. jaclang/compiler/passes/main/pyast_load_pass.py +1 -0
  12. jaclang/compiler/passes/main/pyjac_ast_link_pass.py +7 -0
  13. jaclang/compiler/passes/main/schedules.py +9 -2
  14. jaclang/compiler/passes/main/sym_tab_build_pass.py +9 -5
  15. jaclang/compiler/passes/main/tests/fixtures/autoimpl.empty.impl.jac +0 -0
  16. jaclang/compiler/passes/main/tests/fixtures/autoimpl.jac +1 -1
  17. jaclang/compiler/passes/main/tests/fixtures/py_imp_test.jac +29 -0
  18. jaclang/compiler/passes/main/tests/fixtures/pygame_mock/__init__.py +3 -0
  19. jaclang/compiler/passes/main/tests/fixtures/pygame_mock/color.py +3 -0
  20. jaclang/compiler/passes/main/tests/fixtures/pygame_mock/constants.py +5 -0
  21. jaclang/compiler/passes/main/tests/fixtures/pygame_mock/display.py +2 -0
  22. jaclang/compiler/passes/main/tests/test_import_pass.py +72 -13
  23. jaclang/compiler/passes/main/type_check_pass.py +15 -5
  24. jaclang/compiler/passes/tool/jac_formatter_pass.py +13 -3
  25. jaclang/compiler/passes/tool/tests/fixtures/corelib.jac +37 -41
  26. jaclang/compiler/passes/tool/tests/fixtures/corelib_fmt.jac +37 -41
  27. jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/access_mod_check.jac +27 -0
  28. jaclang/compiler/passes/utils/mypy_ast_build.py +45 -0
  29. jaclang/compiler/symtable.py +16 -11
  30. jaclang/compiler/tests/test_importer.py +17 -9
  31. jaclang/langserve/engine.py +64 -16
  32. jaclang/langserve/server.py +16 -1
  33. jaclang/langserve/tests/fixtures/import_include_statements.jac +3 -3
  34. jaclang/langserve/tests/fixtures/rename.jac +30 -0
  35. jaclang/langserve/tests/test_server.py +224 -6
  36. jaclang/langserve/utils.py +28 -98
  37. jaclang/plugin/builtin.py +8 -4
  38. jaclang/plugin/default.py +86 -64
  39. jaclang/plugin/feature.py +13 -13
  40. jaclang/plugin/spec.py +10 -11
  41. jaclang/plugin/tests/fixtures/other_root_access.jac +82 -0
  42. jaclang/plugin/tests/test_jaseci.py +414 -42
  43. jaclang/runtimelib/architype.py +481 -333
  44. jaclang/runtimelib/constructs.py +5 -8
  45. jaclang/runtimelib/context.py +89 -69
  46. jaclang/runtimelib/importer.py +16 -15
  47. jaclang/runtimelib/machine.py +66 -2
  48. jaclang/runtimelib/memory.py +134 -75
  49. jaclang/runtimelib/utils.py +17 -10
  50. jaclang/settings.py +2 -4
  51. jaclang/tests/fixtures/access_checker.jac +12 -17
  52. jaclang/tests/fixtures/access_modifier.jac +88 -33
  53. jaclang/tests/fixtures/baddy.jac +3 -0
  54. jaclang/tests/fixtures/bar.jac +34 -0
  55. jaclang/tests/fixtures/builtin_dotgen.jac +1 -0
  56. jaclang/tests/fixtures/edge_node_walk.jac +1 -1
  57. jaclang/tests/fixtures/edge_ops.jac +1 -1
  58. jaclang/tests/fixtures/edges_walk.jac +1 -1
  59. jaclang/tests/fixtures/foo.jac +43 -0
  60. jaclang/tests/fixtures/game1.jac +1 -1
  61. jaclang/tests/fixtures/gendot_bubble_sort.jac +1 -1
  62. jaclang/tests/fixtures/import.jac +9 -0
  63. jaclang/tests/fixtures/index_slice.jac +30 -0
  64. jaclang/tests/fixtures/objref.jac +12 -0
  65. jaclang/tests/fixtures/pyfunc_1.py +1 -1
  66. jaclang/tests/fixtures/pyfunc_2.py +2 -2
  67. jaclang/tests/fixtures/pygame_mock/__init__.py +3 -0
  68. jaclang/tests/fixtures/pygame_mock/color.py +3 -0
  69. jaclang/tests/fixtures/pygame_mock/constants.py +5 -0
  70. jaclang/tests/fixtures/pygame_mock/display.py +2 -0
  71. jaclang/tests/fixtures/pygame_mock/inner/__init__.py +0 -0
  72. jaclang/tests/fixtures/pygame_mock/inner/iner_mod.py +2 -0
  73. jaclang/tests/test_cli.py +49 -6
  74. jaclang/tests/test_language.py +126 -80
  75. jaclang/tests/test_reference.py +2 -9
  76. jaclang/utils/treeprinter.py +30 -3
  77. {jaclang-0.7.16.dist-info → jaclang-0.7.18.dist-info}/METADATA +3 -1
  78. {jaclang-0.7.16.dist-info → jaclang-0.7.18.dist-info}/RECORD +81 -59
  79. /jaclang/tests/fixtures/{err.test.jac → baddy.test.jac} +0 -0
  80. {jaclang-0.7.16.dist-info → jaclang-0.7.18.dist-info}/WHEEL +0 -0
  81. {jaclang-0.7.16.dist-info → jaclang-0.7.18.dist-info}/entry_points.txt +0 -0
@@ -2,10 +2,7 @@
2
2
 
3
3
  import asyncio
4
4
  import builtins
5
- import importlib.util
6
- import os
7
5
  import re
8
- import sys
9
6
  from functools import wraps
10
7
  from typing import Any, Awaitable, Callable, Coroutine, Optional, ParamSpec, TypeVar
11
8
 
@@ -209,6 +206,17 @@ def create_range(loc: CodeLocInfo) -> lspt.Range:
209
206
  )
210
207
 
211
208
 
209
+ def get_location_range(mod_item: ast.ModuleItem) -> tuple[int, int, int, int]:
210
+ """Get location range."""
211
+ if not mod_item.from_mod_path.sub_module:
212
+ raise ValueError("Module items should have module path. Not Possible.")
213
+ lookup = mod_item.from_mod_path.sub_module.sym_tab.lookup(mod_item.name.value)
214
+ if not lookup:
215
+ raise ValueError("Module items should have a symbol table entry. Not Possible.")
216
+ loc = lookup.decl.loc
217
+ return loc.first_line, loc.col_start, loc.last_line, loc.col_end
218
+
219
+
212
220
  def kind_map(sub_tab: ast.AstNode) -> lspt.SymbolKind:
213
221
  """Map the symbol node to an lspt.SymbolKind."""
214
222
  return (
@@ -273,101 +281,6 @@ def label_map(sub_tab: SymbolType) -> lspt.CompletionItemKind:
273
281
  )
274
282
 
275
283
 
276
- def get_mod_path(mod_path: ast.ModulePath, name_node: ast.Name) -> str | None:
277
- """Get path for a module import name."""
278
- ret_target = None
279
- if mod_path.parent and (
280
- (
281
- isinstance(mod_path.parent.parent, ast.Import)
282
- and mod_path.parent.parent.is_py
283
- )
284
- or (
285
- isinstance(mod_path.parent, ast.Import)
286
- and mod_path.parent.from_loc
287
- and mod_path.parent.is_py
288
- )
289
- ):
290
- if mod_path.path and name_node in mod_path.path:
291
- temporary_path_str = ("." * mod_path.level) + ".".join(
292
- [p.value for p in mod_path.path[: mod_path.path.index(name_node) + 1]]
293
- if mod_path.path
294
- else ""
295
- )
296
- else:
297
- temporary_path_str = mod_path.path_str
298
- sys.path.append(os.path.dirname(mod_path.loc.mod_path))
299
- spec = importlib.util.find_spec(temporary_path_str)
300
- sys.path.remove(os.path.dirname(mod_path.loc.mod_path))
301
- if spec and spec.origin and spec.origin.endswith(".py"):
302
- ret_target = spec.origin
303
- elif mod_path.parent and (
304
- (
305
- isinstance(mod_path.parent.parent, ast.Import)
306
- and mod_path.parent.parent.is_jac
307
- )
308
- or (
309
- isinstance(mod_path.parent, ast.Import)
310
- and mod_path.parent.from_loc
311
- and mod_path.parent.is_jac
312
- )
313
- ):
314
- ret_target = mod_path.resolve_relative_path()
315
- return ret_target
316
-
317
-
318
- def get_item_path(mod_item: ast.ModuleItem) -> tuple[str, tuple[int, int]] | None:
319
- """Get path."""
320
- item_name = mod_item.name.value
321
- if mod_item.from_parent.is_py and mod_item.from_parent.from_loc:
322
- path = get_mod_path(mod_item.from_parent.from_loc, mod_item.name)
323
- if path:
324
- return get_definition_range(path, item_name)
325
- elif mod_item.from_parent.is_jac:
326
- mod_node = mod_item.from_mod_path
327
- if mod_node.sub_module and mod_node.sub_module._sym_tab:
328
- for symbol_name, symbol in mod_node.sub_module._sym_tab.tab.items():
329
- if symbol_name == item_name:
330
- return symbol.decl.loc.mod_path, (
331
- symbol.decl.loc.first_line - 1,
332
- symbol.decl.loc.last_line - 1,
333
- )
334
- return None
335
-
336
-
337
- def get_definition_range(
338
- filename: str, name: str
339
- ) -> tuple[str, tuple[int, int]] | None:
340
- """Get the start and end line of a function or class definition in a file."""
341
- import ast
342
-
343
- with open(filename, "r") as file:
344
- source = file.read()
345
-
346
- tree = ast.parse(source)
347
-
348
- for node in ast.walk(tree):
349
- if isinstance(node, (ast.FunctionDef, ast.ClassDef)) and node.name == name:
350
- start_line = node.lineno
351
- end_line = (
352
- node.body[-1].end_lineno
353
- if hasattr(node.body[-1], "end_lineno")
354
- else node.body[-1].lineno
355
- )
356
- if start_line and end_line:
357
- return filename, (start_line - 1, end_line - 1)
358
- elif isinstance(node, ast.Assign):
359
- for target in node.targets:
360
- if isinstance(target, ast.Name) and target.id == name:
361
- start_line = node.lineno
362
- end_line = (
363
- node.end_lineno if hasattr(node, "end_lineno") else node.lineno
364
- )
365
- if start_line and end_line:
366
- return filename, (start_line - 1, end_line - 1)
367
-
368
- return None
369
-
370
-
371
284
  def collect_all_symbols_in_scope(
372
285
  sym_tab: SymbolTable, up_tree: bool = True
373
286
  ) -> list[lspt.CompletionItem]:
@@ -625,3 +538,20 @@ def get_line_of_code(line_number: int, lines: list[str]) -> Optional[tuple[str,
625
538
  else first_non_space
626
539
  )
627
540
  return None
541
+
542
+
543
+ def add_unique_text_edit(
544
+ changes: dict[str, list[lspt.TextEdit]], key: str, new_edit: lspt.TextEdit
545
+ ) -> None:
546
+ """Add a new text edit to the changes dictionary if it is unique."""
547
+ if key not in changes:
548
+ changes[key] = [new_edit]
549
+ else:
550
+ for existing_edit in changes[key]:
551
+ if (
552
+ existing_edit.range.start == new_edit.range.start
553
+ and existing_edit.range.end == new_edit.range.end
554
+ and existing_edit.new_text == new_edit.new_text
555
+ ):
556
+ return
557
+ changes[key].append(new_edit)
jaclang/plugin/builtin.py CHANGED
@@ -2,11 +2,10 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from typing import TYPE_CHECKING
5
+ from typing import Optional
6
6
 
7
- if TYPE_CHECKING:
8
- from typing import Optional
9
- from jaclang.runtimelib.constructs import NodeArchitype
7
+ from jaclang.plugin.feature import JacFeature as Jac
8
+ from jaclang.runtimelib.constructs import Architype, NodeArchitype
10
9
 
11
10
 
12
11
  def dotgen(
@@ -40,3 +39,8 @@ def dotgen(
40
39
  node_limit=node_limit,
41
40
  dot_file=dot_file,
42
41
  )
42
+
43
+
44
+ def jid(obj: Architype) -> str:
45
+ """Get the id of the object."""
46
+ return Jac.object_ref(obj)
jaclang/plugin/default.py CHANGED
@@ -12,6 +12,7 @@ from collections import OrderedDict
12
12
  from dataclasses import field
13
13
  from functools import wraps
14
14
  from typing import Any, Callable, Mapping, Optional, Sequence, Type, Union
15
+ from uuid import UUID
15
16
 
16
17
  import jaclang.compiler.absyntree as ast
17
18
  from jaclang.compiler.constant import EdgeDir, colors
@@ -25,16 +26,14 @@ from jaclang.runtimelib.constructs import (
25
26
  ExecutionContext,
26
27
  GenericEdge,
27
28
  JacTestCheck,
28
- Memory,
29
29
  NodeAnchor,
30
30
  NodeArchitype,
31
- ObjectAnchor,
32
31
  Root,
33
32
  WalkerAnchor,
34
33
  WalkerArchitype,
35
- exec_context,
36
34
  )
37
35
  from jaclang.runtimelib.importer import ImportPathSpec, JacImporter, PythonImporter
36
+ from jaclang.runtimelib.machine import JacMachine, JacProgram
38
37
  from jaclang.runtimelib.utils import traverse_graph
39
38
  from jaclang.plugin.feature import JacFeature as Jac # noqa: I100
40
39
  from jaclang.plugin.spec import P, T
@@ -50,7 +49,6 @@ __all__ = [
50
49
  "hookimpl",
51
50
  "JacTestCheck",
52
51
  "NodeAnchor",
53
- "ObjectAnchor",
54
52
  "WalkerAnchor",
55
53
  "NodeArchitype",
56
54
  "EdgeArchitype",
@@ -69,28 +67,24 @@ class JacFeatureDefaults:
69
67
 
70
68
  @staticmethod
71
69
  @hookimpl
72
- def context(session: str = "") -> ExecutionContext:
73
- """Get the execution context."""
74
- ctx = exec_context.get()
75
- if ctx is None:
76
- ctx = ExecutionContext()
77
- exec_context.set(ctx)
78
- return ctx
70
+ def get_context() -> ExecutionContext:
71
+ """Get current execution context."""
72
+ return ExecutionContext.get()
79
73
 
80
74
  @staticmethod
81
75
  @hookimpl
82
- def reset_context() -> None:
83
- """Reset the execution context."""
84
- ctx = exec_context.get()
85
- if ctx:
86
- ctx.reset()
87
- exec_context.set(None)
76
+ def get_object(id: str) -> Architype | None:
77
+ if id == "root":
78
+ return Jac.get_context().root.architype
79
+ elif obj := Jac.get_context().mem.find_by_id(UUID(id)):
80
+ return obj.architype
81
+
82
+ return None
88
83
 
89
84
  @staticmethod
90
85
  @hookimpl
91
- def memory_hook() -> Memory | None:
92
- """Return the memory hook."""
93
- return Jac.context().mem
86
+ def object_ref(obj: Architype) -> str:
87
+ return obj.__jac__.id.hex
94
88
 
95
89
  @staticmethod
96
90
  @hookimpl
@@ -263,12 +257,18 @@ class JacFeatureDefaults:
263
257
  lng,
264
258
  items,
265
259
  )
260
+
261
+ jac_machine = JacMachine.get(base_path)
262
+ if not jac_machine.jac_program:
263
+ jac_machine.attach_program(JacProgram(mod_bundle=None, bytecode=None))
264
+
266
265
  if lng == "py":
267
- import_result = PythonImporter(Jac.context().jac_machine).run_import(spec)
266
+ import_result = PythonImporter(JacMachine.get()).run_import(spec)
268
267
  else:
269
- import_result = JacImporter(Jac.context().jac_machine).run_import(
268
+ import_result = JacImporter(JacMachine.get()).run_import(
270
269
  spec, reload_module
271
270
  )
271
+
272
272
  return (
273
273
  (import_result.ret_mod,)
274
274
  if absorb or not items
@@ -366,9 +366,9 @@ class JacFeatureDefaults:
366
366
  def spawn_call(op1: Architype, op2: Architype) -> WalkerArchitype:
367
367
  """Jac's spawn operator feature."""
368
368
  if isinstance(op1, WalkerArchitype):
369
- return op1._jac_.spawn_call(op2)
369
+ return op1.__jac__.spawn_call(op2.__jac__)
370
370
  elif isinstance(op2, WalkerArchitype):
371
- return op2._jac_.spawn_call(op1)
371
+ return op2.__jac__.spawn_call(op1.__jac__)
372
372
  else:
373
373
  raise TypeError("Invalid walker object")
374
374
 
@@ -390,7 +390,12 @@ class JacFeatureDefaults:
390
390
  ),
391
391
  ) -> bool:
392
392
  """Jac's ignore stmt feature."""
393
- return walker._jac_.ignore_node(expr)
393
+ if isinstance(walker, WalkerArchitype):
394
+ return walker.__jac__.ignore_node(
395
+ (i.__jac__ for i in expr) if isinstance(expr, list) else [expr.__jac__]
396
+ )
397
+ else:
398
+ raise TypeError("Invalid walker object")
394
399
 
395
400
  @staticmethod
396
401
  @hookimpl
@@ -406,7 +411,9 @@ class JacFeatureDefaults:
406
411
  ) -> bool:
407
412
  """Jac's visit stmt feature."""
408
413
  if isinstance(walker, WalkerArchitype):
409
- return walker._jac_.visit_node(expr)
414
+ return walker.__jac__.visit_node(
415
+ (i.__jac__ for i in expr) if isinstance(expr, list) else [expr.__jac__]
416
+ )
410
417
  else:
411
418
  raise TypeError("Invalid walker object")
412
419
 
@@ -414,7 +421,7 @@ class JacFeatureDefaults:
414
421
  @hookimpl
415
422
  def disengage(walker: WalkerArchitype) -> bool: # noqa: ANN401
416
423
  """Jac's disengage stmt feature."""
417
- walker._jac_.disengage_now()
424
+ walker.__jac__.disengage_now()
418
425
  return True
419
426
 
420
427
  @staticmethod
@@ -437,7 +444,7 @@ class JacFeatureDefaults:
437
444
  if edges_only:
438
445
  connected_edges: list[EdgeArchitype] = []
439
446
  for node in node_obj:
440
- connected_edges += node._jac_.get_edges(
447
+ connected_edges += node.__jac__.get_edges(
441
448
  dir, filter_func, target_obj=targ_obj_set
442
449
  )
443
450
  return list(set(connected_edges))
@@ -445,7 +452,9 @@ class JacFeatureDefaults:
445
452
  connected_nodes: list[NodeArchitype] = []
446
453
  for node in node_obj:
447
454
  connected_nodes.extend(
448
- node._jac_.edges_to_nodes(dir, filter_func, target_obj=targ_obj_set)
455
+ node.__jac__.edges_to_nodes(
456
+ dir, filter_func, target_obj=targ_obj_set
457
+ )
449
458
  )
450
459
  return list(set(connected_nodes))
451
460
 
@@ -454,7 +463,7 @@ class JacFeatureDefaults:
454
463
  def connect(
455
464
  left: NodeArchitype | list[NodeArchitype],
456
465
  right: NodeArchitype | list[NodeArchitype],
457
- edge_spec: Callable[[], EdgeArchitype],
466
+ edge_spec: Callable[[NodeAnchor, NodeAnchor], EdgeArchitype],
458
467
  edges_only: bool,
459
468
  ) -> list[NodeArchitype] | list[EdgeArchitype]:
460
469
  """Jac's connect operator feature.
@@ -464,17 +473,16 @@ class JacFeatureDefaults:
464
473
  left = [left] if isinstance(left, NodeArchitype) else left
465
474
  right = [right] if isinstance(right, NodeArchitype) else right
466
475
  edges = []
467
- for i in left:
468
- for j in right:
469
- conn_edge = edge_spec()
470
- edges.append(conn_edge)
471
- i._jac_.connect_node(j, conn_edge)
472
476
 
473
- if i._jac_.persistent or j._jac_.persistent:
474
- conn_edge.save()
475
- j.save()
476
- i.save()
477
+ root = Jac.get_root().__jac__
477
478
 
479
+ for i in left:
480
+ _left = i.__jac__
481
+ if root.has_connect_access(_left):
482
+ for j in right:
483
+ _right = j.__jac__
484
+ if root.has_connect_access(_right):
485
+ edges.append(edge_spec(_left, _right))
478
486
  return right if not edges_only else edges
479
487
 
480
488
  @staticmethod
@@ -489,26 +497,36 @@ class JacFeatureDefaults:
489
497
  disconnect_occurred = False
490
498
  left = [left] if isinstance(left, NodeArchitype) else left
491
499
  right = [right] if isinstance(right, NodeArchitype) else right
500
+
501
+ root = Jac.get_root().__jac__
502
+
492
503
  for i in left:
493
- for j in right:
494
- edge_list: list[EdgeArchitype] = [*i._jac_.edges]
495
- edge_list = filter_func(edge_list) if filter_func else edge_list
496
- for e in edge_list:
497
- if e._jac_.target and e._jac_.source:
498
- if (
499
- dir in ["OUT", "ANY"] # TODO: Not ideal
500
- and i._jac_.obj == e._jac_.source
501
- and e._jac_.target == j._jac_.obj
502
- ):
503
- e._jac_.detach(i._jac_.obj, e._jac_.target)
504
- disconnect_occurred = True
505
- if (
506
- dir in ["IN", "ANY"]
507
- and i._jac_.obj == e._jac_.target
508
- and e._jac_.source == j._jac_.obj
509
- ):
510
- e._jac_.detach(i._jac_.obj, e._jac_.source)
511
- disconnect_occurred = True
504
+ node = i.__jac__
505
+ for anchor in set(node.edges):
506
+ if (
507
+ (source := anchor.source)
508
+ and (target := anchor.target)
509
+ and (not filter_func or filter_func([anchor.architype]))
510
+ and source.architype
511
+ and target.architype
512
+ ):
513
+ if (
514
+ dir in [EdgeDir.OUT, EdgeDir.ANY]
515
+ and node == source
516
+ and target.architype in right
517
+ and root.has_write_access(target)
518
+ ):
519
+ anchor.destroy() if anchor.persistent else anchor.detach()
520
+ disconnect_occurred = True
521
+ if (
522
+ dir in [EdgeDir.IN, EdgeDir.ANY]
523
+ and node == target
524
+ and source.architype in right
525
+ and root.has_write_access(source)
526
+ ):
527
+ anchor.destroy() if anchor.persistent else anchor.detach()
528
+ disconnect_occurred = True
529
+
512
530
  return disconnect_occurred
513
531
 
514
532
  @staticmethod
@@ -527,7 +545,7 @@ class JacFeatureDefaults:
527
545
  @hookimpl
528
546
  def get_root() -> Root:
529
547
  """Jac's assign comprehension feature."""
530
- return Jac.context().get_root()
548
+ return ExecutionContext.get_root()
531
549
 
532
550
  @staticmethod
533
551
  @hookimpl
@@ -541,19 +559,23 @@ class JacFeatureDefaults:
541
559
  is_undirected: bool,
542
560
  conn_type: Optional[Type[EdgeArchitype] | EdgeArchitype],
543
561
  conn_assign: Optional[tuple[tuple, tuple]],
544
- ) -> Callable[[], EdgeArchitype]:
562
+ ) -> Callable[[NodeAnchor, NodeAnchor], EdgeArchitype]:
545
563
  """Jac's root getter."""
546
564
  conn_type = conn_type if conn_type else GenericEdge
547
565
 
548
- def builder() -> EdgeArchitype:
566
+ def builder(source: NodeAnchor, target: NodeAnchor) -> EdgeArchitype:
549
567
  edge = conn_type() if isinstance(conn_type, type) else conn_type
550
- edge._jac_.is_undirected = is_undirected
568
+ edge.__attach__(source, target, is_undirected)
551
569
  if conn_assign:
552
570
  for fld, val in zip(conn_assign[0], conn_assign[1]):
553
571
  if hasattr(edge, fld):
554
572
  setattr(edge, fld, val)
555
573
  else:
556
574
  raise ValueError(f"Invalid attribute: {fld}")
575
+ if source.persistent or target.persistent:
576
+ edge.__jac__.save()
577
+ target.save()
578
+ source.save()
557
579
  return edge
558
580
 
559
581
  return builder
@@ -895,14 +917,14 @@ class JacBuiltin:
895
917
  for source, target, edge in connections:
896
918
  dot_content += (
897
919
  f"{visited_nodes.index(source)} -> {visited_nodes.index(target)} "
898
- f' [label="{html.escape(str(edge._jac_.obj.__class__.__name__))} "];\n'
920
+ f' [label="{html.escape(str(edge.__jac__.architype))} "];\n'
899
921
  )
900
922
  for node_ in visited_nodes:
901
923
  color = (
902
924
  colors[node_depths[node_]] if node_depths[node_] < 25 else colors[24]
903
925
  )
904
926
  dot_content += (
905
- f'{visited_nodes.index(node_)} [label="{html.escape(str(node_._jac_.obj))}"'
927
+ f'{visited_nodes.index(node_)} [label="{html.escape(str(node_.__jac__.architype))}"'
906
928
  f'fillcolor="{color}"];\n'
907
929
  )
908
930
  if dot_file:
jaclang/plugin/feature.py CHANGED
@@ -8,16 +8,16 @@ from typing import Any, Callable, Mapping, Optional, Sequence, Type, TypeAlias,
8
8
 
9
9
  import jaclang.compiler.absyntree as ast
10
10
  from jaclang.compiler.passes.main.pyast_gen_pass import PyastGenPass
11
- from jaclang.plugin.default import ExecutionContext
12
11
  from jaclang.plugin.spec import JacBuiltin, JacCmdSpec, JacFeatureSpec, P, T
13
12
  from jaclang.runtimelib.constructs import (
14
13
  Architype,
15
14
  EdgeArchitype,
16
- Memory,
15
+ NodeAnchor,
17
16
  NodeArchitype,
18
17
  Root,
19
18
  WalkerArchitype,
20
19
  )
20
+ from jaclang.runtimelib.context import ExecutionContext
21
21
 
22
22
  import pluggy
23
23
 
@@ -42,19 +42,19 @@ class JacFeature:
42
42
  Walker: TypeAlias = WalkerArchitype
43
43
 
44
44
  @staticmethod
45
- def context(session: str = "") -> ExecutionContext:
46
- """Create execution context."""
47
- return pm.hook.context(session=session)
45
+ def get_context() -> ExecutionContext:
46
+ """Get current execution context."""
47
+ return pm.hook.get_context()
48
48
 
49
49
  @staticmethod
50
- def reset_context() -> None:
51
- """Reset execution context."""
52
- return pm.hook.reset_context()
50
+ def get_object(id: str) -> Architype | None:
51
+ """Get object given id."""
52
+ return pm.hook.get_object(id=id)
53
53
 
54
54
  @staticmethod
55
- def memory_hook() -> Memory | None:
56
- """Create memory abstraction."""
57
- return pm.hook.memory_hook()
55
+ def object_ref(obj: Architype) -> str:
56
+ """Get object reference id."""
57
+ return pm.hook.object_ref(obj=obj)
58
58
 
59
59
  @staticmethod
60
60
  def make_architype(
@@ -226,7 +226,7 @@ class JacFeature:
226
226
  def connect(
227
227
  left: NodeArchitype | list[NodeArchitype],
228
228
  right: NodeArchitype | list[NodeArchitype],
229
- edge_spec: Callable[[], EdgeArchitype],
229
+ edge_spec: Callable[[NodeAnchor, NodeAnchor], EdgeArchitype],
230
230
  edges_only: bool = False,
231
231
  ) -> list[NodeArchitype] | list[EdgeArchitype]:
232
232
  """Jac's connect operator feature.
@@ -274,7 +274,7 @@ class JacFeature:
274
274
  is_undirected: bool,
275
275
  conn_type: Optional[Type[EdgeArchitype] | EdgeArchitype],
276
276
  conn_assign: Optional[tuple[tuple, tuple]],
277
- ) -> Callable[[], EdgeArchitype]:
277
+ ) -> Callable[[NodeAnchor, NodeAnchor], EdgeArchitype]:
278
278
  """Jac's root getter."""
279
279
  return pm.hook.build_edge(
280
280
  is_undirected=is_undirected, conn_type=conn_type, conn_assign=conn_assign
jaclang/plugin/spec.py CHANGED
@@ -21,16 +21,15 @@ import jaclang.compiler.absyntree as ast
21
21
  from jaclang.compiler.passes.main.pyast_gen_pass import PyastGenPass
22
22
 
23
23
  if TYPE_CHECKING:
24
- from jaclang.runtimelib.constructs import EdgeArchitype, NodeArchitype
25
24
  from jaclang.plugin.default import (
26
25
  Architype,
27
26
  EdgeDir,
28
- ExecutionContext,
29
27
  WalkerArchitype,
30
28
  Root,
31
29
  DSFunc,
32
30
  )
33
- from jaclang.runtimelib.memory import Memory
31
+ from jaclang.runtimelib.constructs import EdgeArchitype, NodeAnchor, NodeArchitype
32
+ from jaclang.runtimelib.context import ExecutionContext
34
33
 
35
34
  import pluggy
36
35
 
@@ -45,20 +44,20 @@ class JacFeatureSpec:
45
44
 
46
45
  @staticmethod
47
46
  @hookspec(firstresult=True)
48
- def context(session: str = "") -> ExecutionContext:
49
- """Get the execution context."""
47
+ def get_context() -> ExecutionContext:
48
+ """Get current execution context."""
50
49
  raise NotImplementedError
51
50
 
52
51
  @staticmethod
53
52
  @hookspec(firstresult=True)
54
- def reset_context() -> None:
55
- """Reset the execution context."""
53
+ def get_object(id: str) -> Architype | None:
54
+ """Get object given id.."""
56
55
  raise NotImplementedError
57
56
 
58
57
  @staticmethod
59
58
  @hookspec(firstresult=True)
60
- def memory_hook() -> Memory | None:
61
- """Create memory abstraction."""
59
+ def object_ref(obj: Architype) -> str:
60
+ """Get object given id.."""
62
61
  raise NotImplementedError
63
62
 
64
63
  @staticmethod
@@ -224,7 +223,7 @@ class JacFeatureSpec:
224
223
  def connect(
225
224
  left: NodeArchitype | list[NodeArchitype],
226
225
  right: NodeArchitype | list[NodeArchitype],
227
- edge_spec: Callable[[], EdgeArchitype],
226
+ edge_spec: Callable[[NodeAnchor, NodeAnchor], EdgeArchitype],
228
227
  edges_only: bool,
229
228
  ) -> list[NodeArchitype] | list[EdgeArchitype]:
230
229
  """Jac's connect operator feature.
@@ -270,7 +269,7 @@ class JacFeatureSpec:
270
269
  is_undirected: bool,
271
270
  conn_type: Optional[Type[EdgeArchitype] | EdgeArchitype],
272
271
  conn_assign: Optional[tuple[tuple, tuple]],
273
- ) -> Callable[[], EdgeArchitype]:
272
+ ) -> Callable[[NodeAnchor, NodeAnchor], EdgeArchitype]:
274
273
  """Jac's root getter."""
275
274
  raise NotImplementedError
276
275
 
@@ -0,0 +1,82 @@
1
+ import:py from jaclang.runtimelib.architype {Anchor}
2
+ import:py from uuid {UUID}
3
+
4
+ node A {
5
+ has val: int;
6
+ }
7
+
8
+ walker check_node {
9
+ can enter with `root entry {
10
+ visit [-->];
11
+ }
12
+
13
+ can enter2 with A entry {
14
+ print(here);
15
+ }
16
+ }
17
+
18
+ walker update_node {
19
+ has val: int;
20
+
21
+ can enter2 with A entry {
22
+ here.val = self.val;
23
+ }
24
+ }
25
+
26
+ walker create_node {
27
+ has val: int;
28
+
29
+ can enter with `root entry {
30
+ a = A(val=self.val);
31
+ here ++> a;
32
+ print(a.__jac__.id);
33
+ }
34
+ }
35
+
36
+ walker create_other_root {
37
+ can enter with `root entry {
38
+ other_root = `root().__jac__;
39
+ other_root.save();
40
+ print(other_root.id);
41
+ }
42
+ }
43
+
44
+ walker allow_other_root_access {
45
+ has root_id: str, level: int | str = 1, via_all: bool = False;
46
+
47
+ can enter_root with `root entry {
48
+ if self.via_all {
49
+ here.__jac__.unrestrict(self.level);
50
+ } else {
51
+ here.__jac__.allow_root(UUID(self.root_id), self.level);
52
+ }
53
+ }
54
+
55
+ can enter_nested with A entry {
56
+ if self.via_all {
57
+ here.__jac__.unrestrict(self.level);
58
+ } else {
59
+ here.__jac__.allow_root(UUID(self.root_id), self.level);
60
+ }
61
+ }
62
+ }
63
+
64
+ walker disallow_other_root_access {
65
+ has root_id: str, via_all: bool = False;
66
+
67
+ can enter_root with `root entry {
68
+ if self.via_all {
69
+ here.__jac__.restrict();
70
+ } else {
71
+ here.__jac__.disallow_root(UUID(self.root_id));
72
+ }
73
+ }
74
+
75
+ can enter_nested with A entry {
76
+ if self.via_all {
77
+ here.__jac__.restrict();
78
+ } else {
79
+ here.__jac__.disallow_root(UUID(self.root_id));
80
+ }
81
+ }
82
+ }