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.
- jaclang/cli/cli.py +5 -7
- jaclang/compiler/absyntree.py +1 -1
- jaclang/compiler/jac.lark +14 -14
- jaclang/compiler/parser.py +71 -59
- jaclang/compiler/passes/ir_pass.py +2 -0
- jaclang/compiler/passes/main/__init__.py +1 -1
- jaclang/compiler/passes/main/fuse_typeinfo_pass.py +50 -13
- jaclang/compiler/passes/main/import_pass.py +29 -1
- jaclang/compiler/passes/main/py_collect_dep_pass.py +8 -0
- jaclang/compiler/passes/main/pyast_gen_pass.py +25 -0
- jaclang/compiler/passes/main/registry_pass.py +4 -0
- jaclang/compiler/passes/main/sym_tab_build_pass.py +0 -18
- jaclang/compiler/passes/main/tests/fixtures/mod_type_assign.jac +7 -0
- jaclang/compiler/passes/main/tests/fixtures/pygame_mock/__init__.pyi +3 -0
- jaclang/compiler/passes/main/tests/test_import_pass.py +10 -10
- jaclang/compiler/passes/main/tests/test_type_check_pass.py +1 -1
- jaclang/compiler/passes/main/tests/test_typeinfo_pass.py +23 -1
- jaclang/compiler/passes/main/type_check_pass.py +4 -4
- jaclang/compiler/passes/tool/jac_formatter_pass.py +63 -31
- jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/line_spacing.jac +57 -0
- jaclang/compiler/semtable.py +4 -4
- jaclang/compiler/symtable.py +5 -0
- jaclang/langserve/engine.py +92 -22
- jaclang/langserve/tests/fixtures/import_include_statements.jac +3 -3
- jaclang/langserve/tests/test_server.py +11 -7
- jaclang/langserve/utils.py +11 -213
- jaclang/plugin/builtin.py +8 -4
- jaclang/plugin/default.py +17 -3
- jaclang/plugin/feature.py +10 -0
- jaclang/plugin/spec.py +12 -0
- jaclang/plugin/tests/test_jaseci.py +23 -4
- jaclang/runtimelib/architype.py +20 -2
- jaclang/runtimelib/importer.py +4 -0
- jaclang/runtimelib/machine.py +92 -4
- jaclang/runtimelib/memory.py +7 -7
- jaclang/settings.py +4 -0
- jaclang/tests/fixtures/bar.jac +1 -1
- jaclang/tests/fixtures/builtin_dotgen.jac +1 -0
- jaclang/tests/fixtures/builtins_test.jac +16 -0
- jaclang/tests/fixtures/dynamic_architype.jac +34 -0
- jaclang/tests/fixtures/entry_exit.jac +36 -0
- jaclang/tests/fixtures/foo.jac +0 -1
- jaclang/tests/fixtures/match_multi_ex.jac +12 -0
- jaclang/tests/fixtures/objref.jac +12 -0
- jaclang/tests/fixtures/trailing_comma.jac +88 -0
- jaclang/tests/fixtures/walker_update.jac +19 -0
- jaclang/tests/test_cli.py +29 -2
- jaclang/tests/test_language.py +144 -4
- jaclang/utils/treeprinter.py +1 -1
- {jaclang-0.7.17.dist-info → jaclang-0.7.21.dist-info}/METADATA +6 -2
- {jaclang-0.7.17.dist-info → jaclang-0.7.21.dist-info}/RECORD +53 -43
- {jaclang-0.7.17.dist-info → jaclang-0.7.21.dist-info}/WHEEL +0 -0
- {jaclang-0.7.17.dist-info → jaclang-0.7.21.dist-info}/entry_points.txt +0 -0
jaclang/langserve/utils.py
CHANGED
|
@@ -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
|
|
5
|
+
from typing import Optional
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
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
|
|
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.
|
|
341
|
-
self.
|
|
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.
|
|
439
|
-
self.
|
|
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
|
jaclang/runtimelib/architype.py
CHANGED
|
@@ -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}")
|
jaclang/runtimelib/importer.py
CHANGED
|
@@ -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:
|
jaclang/runtimelib/machine.py
CHANGED
|
@@ -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
|
|
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
|
|
jaclang/runtimelib/memory.py
CHANGED
|
@@ -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[
|
|
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
|
-
|
|
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
|
|
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(
|
|
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
jaclang/tests/fixtures/bar.jac
CHANGED