jaclang 0.7.17__py3-none-any.whl → 0.7.21__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 (53) hide show
  1. jaclang/cli/cli.py +5 -7
  2. jaclang/compiler/absyntree.py +1 -1
  3. jaclang/compiler/jac.lark +14 -14
  4. jaclang/compiler/parser.py +71 -59
  5. jaclang/compiler/passes/ir_pass.py +2 -0
  6. jaclang/compiler/passes/main/__init__.py +1 -1
  7. jaclang/compiler/passes/main/fuse_typeinfo_pass.py +50 -13
  8. jaclang/compiler/passes/main/import_pass.py +29 -1
  9. jaclang/compiler/passes/main/py_collect_dep_pass.py +8 -0
  10. jaclang/compiler/passes/main/pyast_gen_pass.py +25 -0
  11. jaclang/compiler/passes/main/registry_pass.py +4 -0
  12. jaclang/compiler/passes/main/sym_tab_build_pass.py +0 -18
  13. jaclang/compiler/passes/main/tests/fixtures/mod_type_assign.jac +7 -0
  14. jaclang/compiler/passes/main/tests/fixtures/pygame_mock/__init__.pyi +3 -0
  15. jaclang/compiler/passes/main/tests/test_import_pass.py +10 -10
  16. jaclang/compiler/passes/main/tests/test_type_check_pass.py +1 -1
  17. jaclang/compiler/passes/main/tests/test_typeinfo_pass.py +23 -1
  18. jaclang/compiler/passes/main/type_check_pass.py +4 -4
  19. jaclang/compiler/passes/tool/jac_formatter_pass.py +63 -31
  20. jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/line_spacing.jac +57 -0
  21. jaclang/compiler/semtable.py +4 -4
  22. jaclang/compiler/symtable.py +5 -0
  23. jaclang/langserve/engine.py +92 -22
  24. jaclang/langserve/tests/fixtures/import_include_statements.jac +3 -3
  25. jaclang/langserve/tests/test_server.py +11 -7
  26. jaclang/langserve/utils.py +11 -213
  27. jaclang/plugin/builtin.py +8 -4
  28. jaclang/plugin/default.py +17 -3
  29. jaclang/plugin/feature.py +10 -0
  30. jaclang/plugin/spec.py +12 -0
  31. jaclang/plugin/tests/test_jaseci.py +23 -4
  32. jaclang/runtimelib/architype.py +20 -2
  33. jaclang/runtimelib/importer.py +4 -0
  34. jaclang/runtimelib/machine.py +92 -4
  35. jaclang/runtimelib/memory.py +7 -7
  36. jaclang/settings.py +4 -0
  37. jaclang/tests/fixtures/bar.jac +1 -1
  38. jaclang/tests/fixtures/builtin_dotgen.jac +1 -0
  39. jaclang/tests/fixtures/builtins_test.jac +16 -0
  40. jaclang/tests/fixtures/dynamic_architype.jac +34 -0
  41. jaclang/tests/fixtures/entry_exit.jac +36 -0
  42. jaclang/tests/fixtures/foo.jac +0 -1
  43. jaclang/tests/fixtures/match_multi_ex.jac +12 -0
  44. jaclang/tests/fixtures/objref.jac +12 -0
  45. jaclang/tests/fixtures/trailing_comma.jac +88 -0
  46. jaclang/tests/fixtures/walker_update.jac +19 -0
  47. jaclang/tests/test_cli.py +29 -2
  48. jaclang/tests/test_language.py +144 -4
  49. jaclang/utils/treeprinter.py +1 -1
  50. {jaclang-0.7.17.dist-info → jaclang-0.7.21.dist-info}/METADATA +6 -2
  51. {jaclang-0.7.17.dist-info → jaclang-0.7.21.dist-info}/RECORD +53 -43
  52. {jaclang-0.7.17.dist-info → jaclang-0.7.21.dist-info}/WHEEL +0 -0
  53. {jaclang-0.7.17.dist-info → jaclang-0.7.21.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,103 +281,6 @@ def label_map(sub_tab: SymbolType) -> lspt.CompletionItemKind:
273
281
  )
274
282
 
275
283
 
276
- def get_mod_path(
277
- mod_path: ast.ModulePath, name_node: ast.Name
278
- ) -> str | None: # TODO: This should go away
279
- """Get path for a module import name."""
280
- ret_target = None
281
- if mod_path.parent and (
282
- (
283
- isinstance(mod_path.parent.parent, ast.Import)
284
- and mod_path.parent.parent.is_py
285
- )
286
- or (
287
- isinstance(mod_path.parent, ast.Import)
288
- and mod_path.parent.from_loc
289
- and mod_path.parent.is_py
290
- )
291
- ):
292
- if mod_path.path and name_node in mod_path.path:
293
- temporary_path_str = ("." * mod_path.level) + ".".join(
294
- [p.value for p in mod_path.path[: mod_path.path.index(name_node) + 1]]
295
- if mod_path.path
296
- else ""
297
- )
298
- else:
299
- temporary_path_str = mod_path.dot_path_str
300
- sys.path.append(os.path.dirname(mod_path.loc.mod_path))
301
- spec = importlib.util.find_spec(temporary_path_str)
302
- sys.path.remove(os.path.dirname(mod_path.loc.mod_path))
303
- if spec and spec.origin and spec.origin.endswith(".py"):
304
- ret_target = spec.origin
305
- elif mod_path.parent and (
306
- (
307
- isinstance(mod_path.parent.parent, ast.Import)
308
- and mod_path.parent.parent.is_jac
309
- )
310
- or (
311
- isinstance(mod_path.parent, ast.Import)
312
- and mod_path.parent.from_loc
313
- and mod_path.parent.is_jac
314
- )
315
- ):
316
- ret_target = mod_path.resolve_relative_path()
317
- return ret_target
318
-
319
-
320
- def get_item_path(mod_item: ast.ModuleItem) -> tuple[str, tuple[int, int]] | None:
321
- """Get path."""
322
- item_name = mod_item.name.value
323
- if mod_item.from_parent.is_py and mod_item.from_parent.from_loc:
324
- path = get_mod_path(mod_item.from_parent.from_loc, mod_item.name)
325
- if path:
326
- return get_definition_range(path, item_name)
327
- elif mod_item.from_parent.is_jac:
328
- mod_node = mod_item.from_mod_path
329
- if mod_node.sub_module and mod_node.sub_module._sym_tab:
330
- for symbol_name, symbol in mod_node.sub_module._sym_tab.tab.items():
331
- if symbol_name == item_name:
332
- return symbol.decl.loc.mod_path, (
333
- symbol.decl.loc.first_line - 1,
334
- symbol.decl.loc.last_line - 1,
335
- )
336
- return None
337
-
338
-
339
- def get_definition_range(
340
- filename: str, name: str
341
- ) -> tuple[str, tuple[int, int]] | None:
342
- """Get the start and end line of a function or class definition in a file."""
343
- import ast
344
-
345
- with open(filename, "r") as file:
346
- source = file.read()
347
-
348
- tree = ast.parse(source)
349
-
350
- for node in ast.walk(tree):
351
- if isinstance(node, (ast.FunctionDef, ast.ClassDef)) and node.name == name:
352
- start_line = node.lineno
353
- end_line = (
354
- node.body[-1].end_lineno
355
- if hasattr(node.body[-1], "end_lineno")
356
- else node.body[-1].lineno
357
- )
358
- if start_line and end_line:
359
- return filename, (start_line - 1, end_line - 1)
360
- elif isinstance(node, ast.Assign):
361
- for target in node.targets:
362
- if isinstance(target, ast.Name) and target.id == name:
363
- start_line = node.lineno
364
- end_line = (
365
- node.end_lineno if hasattr(node, "end_lineno") else node.lineno
366
- )
367
- if start_line and end_line:
368
- return filename, (start_line - 1, end_line - 1)
369
-
370
- return None
371
-
372
-
373
284
  def collect_all_symbols_in_scope(
374
285
  sym_tab: SymbolTable, up_tree: bool = True
375
286
  ) -> list[lspt.CompletionItem]:
@@ -421,119 +332,6 @@ def parse_symbol_path(text: str, dot_position: int) -> list[str]:
421
332
  return all_words
422
333
 
423
334
 
424
- def resolve_symbol_path(sym_name: str, node_tab: SymbolTable) -> str:
425
- """Resolve symbol path."""
426
- visited = set()
427
- current_tab: Optional[SymbolTable] = node_tab
428
-
429
- while current_tab is not None and current_tab not in visited:
430
- visited.add(current_tab)
431
- for name, symbol in current_tab.tab.items():
432
- if name not in dir(builtins) and name == sym_name:
433
- path = symbol.defn[0]._sym_type
434
- if symbol.sym_type == SymbolType.ENUM_ARCH:
435
- if isinstance(current_tab.owner, ast.Module):
436
- return current_tab.owner.name + "." + sym_name
437
- elif isinstance(current_tab.owner, ast.AstSymbolNode):
438
- return current_tab.owner.name_spec._sym_type + "." + sym_name
439
- return path
440
- current_tab = current_tab.parent if current_tab.parent != current_tab else None
441
- return ""
442
-
443
-
444
- def find_symbol_table(path: str, current_tab: Optional[SymbolTable]) -> SymbolTable:
445
- """Find symbol table."""
446
- path = path.lstrip(".")
447
- current_table = current_tab
448
- if current_table:
449
- for segment in path.split("."):
450
- current_table = next(
451
- (
452
- child_table
453
- for child_table in current_table.kid
454
- if child_table.name == segment
455
- ),
456
- current_table,
457
- )
458
- if current_table:
459
- return current_table
460
- raise ValueError(f"Symbol table not found for path {path}")
461
-
462
-
463
- def resolve_completion_symbol_table(
464
- mod_tab: SymbolTable,
465
- current_symbol_path: list[str],
466
- current_tab: Optional[SymbolTable],
467
- ) -> list[lspt.CompletionItem]:
468
- """Resolve symbol table for completion items."""
469
- current_symbol_table = mod_tab
470
- for obj in current_symbol_path:
471
- if obj == "self":
472
- try:
473
- try:
474
- is_abilitydef = (
475
- mod_tab.owner
476
- if isinstance(mod_tab.owner, ast.AbilityDef)
477
- else mod_tab.owner.parent_of_type(ast.AbilityDef)
478
- )
479
- archi_owner = (
480
- (is_abilitydef.decl_link.parent_of_type(ast.Architype))
481
- if is_abilitydef.decl_link
482
- else None
483
- )
484
- current_symbol_table = (
485
- archi_owner._sym_tab
486
- if archi_owner and archi_owner._sym_tab
487
- else mod_tab
488
- )
489
- continue
490
-
491
- except ValueError:
492
- pass
493
- archi_owner = mod_tab.owner.parent_of_type(ast.Architype)
494
- current_symbol_table = (
495
- archi_owner._sym_tab
496
- if archi_owner and archi_owner._sym_tab
497
- else mod_tab
498
- )
499
- except ValueError:
500
- pass
501
- else:
502
- path: str = resolve_symbol_path(obj, current_symbol_table)
503
- if path:
504
- current_symbol_table = find_symbol_table(path, current_tab)
505
- else:
506
- if (
507
- isinstance(current_symbol_table.owner, ast.Architype)
508
- and current_symbol_table.owner.base_classes
509
- ):
510
- for base_name in current_symbol_table.owner.base_classes.items:
511
- if isinstance(base_name, ast.Name) and base_name.sym:
512
- path = base_name.sym.sym_dotted_name + "." + obj
513
- current_symbol_table = find_symbol_table(path, current_tab)
514
- if (
515
- isinstance(current_symbol_table.owner, ast.Architype)
516
- and current_symbol_table.owner.base_classes
517
- ):
518
- base = []
519
- for base_name in current_symbol_table.owner.base_classes.items:
520
- if isinstance(base_name, ast.Name) and base_name.sym:
521
- base.append(base_name.sym.sym_dotted_name)
522
- for base_ in base:
523
- completion_items = collect_all_symbols_in_scope(
524
- find_symbol_table(base_, current_tab),
525
- up_tree=False,
526
- )
527
- else:
528
- completion_items = []
529
- if isinstance(current_symbol_table.owner, (ast.Ability, ast.AbilityDef)):
530
- return completion_items
531
- completion_items.extend(
532
- collect_all_symbols_in_scope(current_symbol_table, up_tree=False)
533
- )
534
- return completion_items
535
-
536
-
537
335
  def get_token_start(
538
336
  token_index: int | None, sem_tokens: list[int]
539
337
  ) -> tuple[int, int, int]:
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,13 +12,13 @@ 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
18
19
  from jaclang.compiler.passes.main.pyast_gen_pass import PyastGenPass
19
20
  from jaclang.compiler.semtable import SemInfo, SemRegistry, SemScope
20
21
  from jaclang.runtimelib.constructs import (
21
- Anchor,
22
22
  Architype,
23
23
  DSFunc,
24
24
  EdgeAnchor,
@@ -44,7 +44,6 @@ import pluggy
44
44
  hookimpl = pluggy.HookimplMarker("jac")
45
45
 
46
46
  __all__ = [
47
- "Anchor",
48
47
  "EdgeAnchor",
49
48
  "GenericEdge",
50
49
  "hookimpl",
@@ -72,6 +71,21 @@ class JacFeatureDefaults:
72
71
  """Get current execution context."""
73
72
  return ExecutionContext.get()
74
73
 
74
+ @staticmethod
75
+ @hookimpl
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
83
+
84
+ @staticmethod
85
+ @hookimpl
86
+ def object_ref(obj: Architype) -> str:
87
+ return obj.__jac__.id.hex
88
+
75
89
  @staticmethod
76
90
  @hookimpl
77
91
  def make_architype(
@@ -903,7 +917,7 @@ class JacBuiltin:
903
917
  for source, target, edge in connections:
904
918
  dot_content += (
905
919
  f"{visited_nodes.index(source)} -> {visited_nodes.index(target)} "
906
- f' [label="{html.escape(str(edge.__jac__.architype.__class__.__name__))} "];\n'
920
+ f' [label="{html.escape(str(edge.__jac__.architype))} "];\n'
907
921
  )
908
922
  for node_ in visited_nodes:
909
923
  color = (
jaclang/plugin/feature.py CHANGED
@@ -46,6 +46,16 @@ class JacFeature:
46
46
  """Get current execution context."""
47
47
  return pm.hook.get_context()
48
48
 
49
+ @staticmethod
50
+ def get_object(id: str) -> Architype | None:
51
+ """Get object given id."""
52
+ return pm.hook.get_object(id=id)
53
+
54
+ @staticmethod
55
+ def object_ref(obj: Architype) -> str:
56
+ """Get object reference id."""
57
+ return pm.hook.object_ref(obj=obj)
58
+
49
59
  @staticmethod
50
60
  def make_architype(
51
61
  cls: type,
jaclang/plugin/spec.py CHANGED
@@ -48,6 +48,18 @@ class JacFeatureSpec:
48
48
  """Get current execution context."""
49
49
  raise NotImplementedError
50
50
 
51
+ @staticmethod
52
+ @hookspec(firstresult=True)
53
+ def get_object(id: str) -> Architype | None:
54
+ """Get object given id.."""
55
+ raise NotImplementedError
56
+
57
+ @staticmethod
58
+ @hookspec(firstresult=True)
59
+ def object_ref(obj: Architype) -> str:
60
+ """Get object given id.."""
61
+ raise NotImplementedError
62
+
51
63
  @staticmethod
52
64
  @hookspec(firstresult=True)
53
65
  def make_architype(
@@ -337,8 +337,8 @@ class TestJaseciPlugin(TestCase):
337
337
 
338
338
  # --------- NO UPDATE SHOULD HAPPEN -------- #
339
339
 
340
- self.assertTrue(archs[0], "A(val=2)")
341
- self.assertTrue(archs[1], "A(val=1)")
340
+ self.assertEqual(archs[0], "A(val=2)")
341
+ self.assertEqual(archs[1], "A(val=1)")
342
342
 
343
343
  self._output2buffer()
344
344
  cli.enter(
@@ -435,8 +435,8 @@ class TestJaseciPlugin(TestCase):
435
435
 
436
436
  # --------- UPDATE SHOULD HAPPEN -------- #
437
437
 
438
- self.assertTrue(archs[0], "A(val=20)")
439
- self.assertTrue(archs[1], "A(val=10)")
438
+ self.assertEqual(archs[0], "A(val=20)")
439
+ self.assertEqual(archs[1], "A(val=10)")
440
440
 
441
441
  self._output2buffer()
442
442
  cli.enter(
@@ -474,6 +474,25 @@ class TestJaseciPlugin(TestCase):
474
474
  )
475
475
  self.assertFalse(self.capturedOutput.getvalue().strip())
476
476
 
477
+ # --------- ROOTS RESET OWN NODE -------- #
478
+
479
+ cli.enter(
480
+ filename=self.fixture_abs_path("other_root_access.jac"),
481
+ entrypoint="update_node",
482
+ args=[1],
483
+ session=session,
484
+ root=self.roots[0],
485
+ node=self.nodes[0],
486
+ )
487
+ cli.enter(
488
+ filename=self.fixture_abs_path("other_root_access.jac"),
489
+ entrypoint="update_node",
490
+ args=[2],
491
+ session=session,
492
+ root=self.roots[1],
493
+ node=self.nodes[1],
494
+ )
495
+
477
496
  def test_other_root_access(self) -> None:
478
497
  """Test filtering on node, then visit."""
479
498
  global session
@@ -526,6 +526,14 @@ class WalkerAnchor(Anchor):
526
526
  if walker := self.architype:
527
527
  self.path = []
528
528
  self.next = [node]
529
+ if self.next:
530
+ current_node = self.next[-1].architype
531
+ for i in walker._jac_entry_funcs_:
532
+ if not i.trigger:
533
+ if i.func:
534
+ i.func(walker, current_node)
535
+ else:
536
+ raise ValueError(f"No function {i.name} to call.")
529
537
  while len(self.next):
530
538
  if current_node := self.next.pop(0).architype:
531
539
  for i in current_node._jac_entry_funcs_:
@@ -538,16 +546,20 @@ class WalkerAnchor(Anchor):
538
546
  return walker
539
547
  for i in walker._jac_entry_funcs_:
540
548
  if not i.trigger or isinstance(current_node, i.trigger):
541
- if i.func:
549
+ if i.func and i.trigger:
542
550
  i.func(walker, current_node)
551
+ elif not i.trigger:
552
+ continue
543
553
  else:
544
554
  raise ValueError(f"No function {i.name} to call.")
545
555
  if self.disengaged:
546
556
  return walker
547
557
  for i in walker._jac_exit_funcs_:
548
558
  if not i.trigger or isinstance(current_node, i.trigger):
549
- if i.func:
559
+ if i.func and i.trigger:
550
560
  i.func(walker, current_node)
561
+ elif not i.trigger:
562
+ continue
551
563
  else:
552
564
  raise ValueError(f"No function {i.name} to call.")
553
565
  if self.disengaged:
@@ -560,6 +572,12 @@ class WalkerAnchor(Anchor):
560
572
  raise ValueError(f"No function {i.name} to call.")
561
573
  if self.disengaged:
562
574
  return walker
575
+ for i in walker._jac_exit_funcs_:
576
+ if not i.trigger:
577
+ if i.func:
578
+ i.func(walker, current_node)
579
+ else:
580
+ raise ValueError(f"No function {i.name} to call.")
563
581
  self.ignores = []
564
582
  return walker
565
583
  raise Exception(f"Invalid Reference {self.id}")
@@ -3,6 +3,7 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import importlib
6
+ import importlib.util
6
7
  import os
7
8
  import sys
8
9
  import types
@@ -194,6 +195,8 @@ class PythonImporter(Importer):
194
195
  loaded_items: list = []
195
196
  if spec.target.startswith("."):
196
197
  spec.target = spec.target.lstrip(".")
198
+ if len(spec.target.split(".")) > 1:
199
+ spec.target = spec.target.split(".")[-1]
197
200
  full_target = path.normpath(path.join(spec.caller_dir, spec.target))
198
201
  imp_spec = importlib.util.spec_from_file_location(
199
202
  spec.target, full_target + ".py"
@@ -339,6 +342,7 @@ class JacImporter(Importer):
339
342
  spec.full_target,
340
343
  caller_dir=spec.caller_dir,
341
344
  cachable=spec.cachable,
345
+ reload=reload if reload else False,
342
346
  )
343
347
  try:
344
348
  if not codeobj:
@@ -6,12 +6,17 @@ import os
6
6
  import sys
7
7
  import types
8
8
  from contextvars import ContextVar
9
- from typing import Optional
9
+ from typing import Optional, Union
10
10
 
11
11
  from jaclang.compiler.absyntree import Module
12
12
  from jaclang.compiler.compile import compile_jac
13
13
  from jaclang.compiler.constant import Constants as Con
14
- from jaclang.runtimelib.architype import EdgeArchitype, NodeArchitype, WalkerArchitype
14
+ from jaclang.runtimelib.architype import (
15
+ Architype,
16
+ EdgeArchitype,
17
+ NodeArchitype,
18
+ WalkerArchitype,
19
+ )
15
20
  from jaclang.utils.log import logging
16
21
 
17
22
 
@@ -55,11 +60,12 @@ class JacMachine:
55
60
  full_target: str,
56
61
  caller_dir: str,
57
62
  cachable: bool = True,
63
+ reload: bool = False,
58
64
  ) -> Optional[types.CodeType]:
59
65
  """Retrieve bytecode from the attached JacProgram."""
60
66
  if self.jac_program:
61
67
  return self.jac_program.get_bytecode(
62
- module_name, full_target, caller_dir, cachable
68
+ module_name, full_target, caller_dir, cachable, reload=reload
63
69
  )
64
70
  return None
65
71
 
@@ -105,6 +111,87 @@ class JacMachine:
105
111
  return nodes
106
112
  return []
107
113
 
114
+ def update_walker(
115
+ self, module_name: str, items: Optional[dict[str, Union[str, Optional[str]]]]
116
+ ) -> tuple[types.ModuleType, ...]:
117
+ """Reimport the module."""
118
+ from .importer import JacImporter, ImportPathSpec
119
+
120
+ if module_name in self.loaded_modules:
121
+ try:
122
+ old_module = self.loaded_modules[module_name]
123
+ importer = JacImporter(self)
124
+ spec = ImportPathSpec(
125
+ target=module_name,
126
+ base_path=self.base_path,
127
+ absorb=False,
128
+ cachable=True,
129
+ mdl_alias=None,
130
+ override_name=None,
131
+ lng="jac",
132
+ items=items,
133
+ )
134
+ import_result = importer.run_import(spec, reload=True)
135
+ ret_items = []
136
+ if items:
137
+ for item_name in items:
138
+ if hasattr(old_module, item_name):
139
+ new_attr = getattr(import_result.ret_mod, item_name, None)
140
+ if new_attr:
141
+ ret_items.append(new_attr)
142
+ setattr(
143
+ old_module,
144
+ item_name,
145
+ new_attr,
146
+ )
147
+ return (old_module,) if not items else tuple(ret_items)
148
+ except Exception as e:
149
+ logger.error(f"Failed to update module {module_name}: {e}")
150
+ else:
151
+ logger.warning(f"Module {module_name} not found in loaded modules.")
152
+ return ()
153
+
154
+ def spawn_node(
155
+ self,
156
+ node_name: str,
157
+ attributes: Optional[dict] = None,
158
+ module_name: str = "__main__",
159
+ ) -> NodeArchitype:
160
+ """Spawn a node instance of the given node_name with attributes."""
161
+ node_class = self.get_architype(module_name, node_name)
162
+ if isinstance(node_class, type) and issubclass(node_class, NodeArchitype):
163
+ if attributes is None:
164
+ attributes = {}
165
+ node_instance = node_class(**attributes)
166
+ return node_instance
167
+ else:
168
+ raise ValueError(f"Node {node_name} not found.")
169
+
170
+ def spawn_walker(
171
+ self,
172
+ walker_name: str,
173
+ attributes: Optional[dict] = None,
174
+ module_name: str = "__main__",
175
+ ) -> WalkerArchitype:
176
+ """Spawn a walker instance of the given walker_name."""
177
+ walker_class = self.get_architype(module_name, walker_name)
178
+ if isinstance(walker_class, type) and issubclass(walker_class, WalkerArchitype):
179
+ if attributes is None:
180
+ attributes = {}
181
+ walker_instance = walker_class(**attributes)
182
+ return walker_instance
183
+ else:
184
+ raise ValueError(f"Walker {walker_name} not found.")
185
+
186
+ def get_architype(
187
+ self, module_name: str, architype_name: str
188
+ ) -> Optional[Architype]:
189
+ """Retrieve an architype class from a module."""
190
+ module = self.loaded_modules.get(module_name)
191
+ if module:
192
+ return getattr(module, architype_name, None)
193
+ return None
194
+
108
195
  @staticmethod
109
196
  def get(base_path: str = "") -> "JacMachine":
110
197
  """Get current jac machine."""
@@ -134,6 +221,7 @@ class JacProgram:
134
221
  full_target: str,
135
222
  caller_dir: str,
136
223
  cachable: bool = True,
224
+ reload: bool = False,
137
225
  ) -> Optional[types.CodeType]:
138
226
  """Get the bytecode for a specific module."""
139
227
  if self.mod_bundle and isinstance(self.mod_bundle, Module):
@@ -141,7 +229,7 @@ class JacProgram:
141
229
  return marshal.loads(codeobj) if isinstance(codeobj, bytes) else None
142
230
  gen_dir = os.path.join(caller_dir, Con.JAC_GEN_DIR)
143
231
  pyc_file_path = os.path.join(gen_dir, module_name + ".jbc")
144
- if cachable and os.path.exists(pyc_file_path):
232
+ if cachable and os.path.exists(pyc_file_path) and not reload:
145
233
  with open(pyc_file_path, "rb") as f:
146
234
  return marshal.load(f)
147
235
 
@@ -18,7 +18,7 @@ class Memory(Generic[ID, TANCH]):
18
18
  """Generic Memory Handler."""
19
19
 
20
20
  __mem__: dict[ID, TANCH] = field(default_factory=dict)
21
- __gc__: set[ID] = field(default_factory=set)
21
+ __gc__: set[TANCH] = field(default_factory=set)
22
22
 
23
23
  def close(self) -> None:
24
24
  """Close memory handler."""
@@ -62,8 +62,8 @@ class Memory(Generic[ID, TANCH]):
62
62
  ids = [ids]
63
63
 
64
64
  for id in ids:
65
- self.__mem__.pop(id, None)
66
- self.__gc__.add(id)
65
+ if anchor := self.__mem__.pop(id, None):
66
+ self.__gc__.add(anchor)
67
67
 
68
68
 
69
69
  @dataclass
@@ -84,9 +84,9 @@ class ShelfStorage(Memory[UUID, Anchor]):
84
84
 
85
85
  root = Jac.get_root().__jac__
86
86
 
87
- for id in self.__gc__:
88
- self.__shelf__.pop(str(id), None)
89
- self.__mem__.pop(id, None)
87
+ for anchor in self.__gc__:
88
+ self.__shelf__.pop(str(anchor.id), None)
89
+ self.__mem__.pop(anchor.id, None)
90
90
 
91
91
  for d in self.__mem__.values():
92
92
  if d.persistent and d.hash != hash(dumps(d)):
@@ -106,7 +106,7 @@ class ShelfStorage(Memory[UUID, Anchor]):
106
106
  if root.has_write_access(d):
107
107
  if hash(dumps(p_d.access)) != hash(dumps(d.access)):
108
108
  p_d.access = d.access
109
- if hash(dumps(d.architype)) != hash(dumps(d.architype)):
109
+ if hash(dumps(p_d.architype)) != hash(dumps(d.architype)):
110
110
  p_d.architype = d.architype
111
111
 
112
112
  self.__shelf__[_id] = p_d
jaclang/settings.py CHANGED
@@ -17,6 +17,10 @@ class Settings:
17
17
  collect_py_dep_debug: bool = False
18
18
  print_py_raised_ast: bool = False
19
19
 
20
+ # Compiler configuration
21
+ disable_mtllm: bool = False
22
+ ignore_test_annex: bool = False
23
+
20
24
  # Formatter configuration
21
25
  max_line_length: int = 88
22
26
 
@@ -31,4 +31,4 @@ walker bar_walk {
31
31
  disengage;
32
32
  }
33
33
  }
34
- }
34
+ }