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.
- jaclang/cli/cli.py +140 -77
- jaclang/compiler/absyntree.py +9 -4
- jaclang/compiler/constant.py +8 -8
- jaclang/compiler/parser.py +10 -2
- jaclang/compiler/passes/main/__init__.py +1 -1
- jaclang/compiler/passes/main/access_modifier_pass.py +96 -147
- jaclang/compiler/passes/main/fuse_typeinfo_pass.py +152 -50
- jaclang/compiler/passes/main/import_pass.py +88 -59
- jaclang/compiler/passes/main/py_collect_dep_pass.py +70 -0
- jaclang/compiler/passes/main/pyast_gen_pass.py +46 -6
- jaclang/compiler/passes/main/pyast_load_pass.py +1 -0
- jaclang/compiler/passes/main/pyjac_ast_link_pass.py +7 -0
- jaclang/compiler/passes/main/schedules.py +9 -2
- jaclang/compiler/passes/main/sym_tab_build_pass.py +9 -5
- jaclang/compiler/passes/main/tests/fixtures/autoimpl.empty.impl.jac +0 -0
- jaclang/compiler/passes/main/tests/fixtures/autoimpl.jac +1 -1
- jaclang/compiler/passes/main/tests/fixtures/py_imp_test.jac +29 -0
- jaclang/compiler/passes/main/tests/fixtures/pygame_mock/__init__.py +3 -0
- jaclang/compiler/passes/main/tests/fixtures/pygame_mock/color.py +3 -0
- jaclang/compiler/passes/main/tests/fixtures/pygame_mock/constants.py +5 -0
- jaclang/compiler/passes/main/tests/fixtures/pygame_mock/display.py +2 -0
- jaclang/compiler/passes/main/tests/test_import_pass.py +72 -13
- jaclang/compiler/passes/main/type_check_pass.py +15 -5
- jaclang/compiler/passes/tool/jac_formatter_pass.py +13 -3
- jaclang/compiler/passes/tool/tests/fixtures/corelib.jac +37 -41
- jaclang/compiler/passes/tool/tests/fixtures/corelib_fmt.jac +37 -41
- jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/access_mod_check.jac +27 -0
- jaclang/compiler/passes/utils/mypy_ast_build.py +45 -0
- jaclang/compiler/symtable.py +16 -11
- jaclang/compiler/tests/test_importer.py +17 -9
- jaclang/langserve/engine.py +64 -16
- jaclang/langserve/server.py +16 -1
- jaclang/langserve/tests/fixtures/import_include_statements.jac +3 -3
- jaclang/langserve/tests/fixtures/rename.jac +30 -0
- jaclang/langserve/tests/test_server.py +224 -6
- jaclang/langserve/utils.py +28 -98
- jaclang/plugin/builtin.py +8 -4
- jaclang/plugin/default.py +86 -64
- jaclang/plugin/feature.py +13 -13
- jaclang/plugin/spec.py +10 -11
- jaclang/plugin/tests/fixtures/other_root_access.jac +82 -0
- jaclang/plugin/tests/test_jaseci.py +414 -42
- jaclang/runtimelib/architype.py +481 -333
- jaclang/runtimelib/constructs.py +5 -8
- jaclang/runtimelib/context.py +89 -69
- jaclang/runtimelib/importer.py +16 -15
- jaclang/runtimelib/machine.py +66 -2
- jaclang/runtimelib/memory.py +134 -75
- jaclang/runtimelib/utils.py +17 -10
- jaclang/settings.py +2 -4
- jaclang/tests/fixtures/access_checker.jac +12 -17
- jaclang/tests/fixtures/access_modifier.jac +88 -33
- jaclang/tests/fixtures/baddy.jac +3 -0
- jaclang/tests/fixtures/bar.jac +34 -0
- jaclang/tests/fixtures/builtin_dotgen.jac +1 -0
- jaclang/tests/fixtures/edge_node_walk.jac +1 -1
- jaclang/tests/fixtures/edge_ops.jac +1 -1
- jaclang/tests/fixtures/edges_walk.jac +1 -1
- jaclang/tests/fixtures/foo.jac +43 -0
- jaclang/tests/fixtures/game1.jac +1 -1
- jaclang/tests/fixtures/gendot_bubble_sort.jac +1 -1
- jaclang/tests/fixtures/import.jac +9 -0
- jaclang/tests/fixtures/index_slice.jac +30 -0
- jaclang/tests/fixtures/objref.jac +12 -0
- jaclang/tests/fixtures/pyfunc_1.py +1 -1
- jaclang/tests/fixtures/pyfunc_2.py +2 -2
- jaclang/tests/fixtures/pygame_mock/__init__.py +3 -0
- jaclang/tests/fixtures/pygame_mock/color.py +3 -0
- jaclang/tests/fixtures/pygame_mock/constants.py +5 -0
- jaclang/tests/fixtures/pygame_mock/display.py +2 -0
- jaclang/tests/fixtures/pygame_mock/inner/__init__.py +0 -0
- jaclang/tests/fixtures/pygame_mock/inner/iner_mod.py +2 -0
- jaclang/tests/test_cli.py +49 -6
- jaclang/tests/test_language.py +126 -80
- jaclang/tests/test_reference.py +2 -9
- jaclang/utils/treeprinter.py +30 -3
- {jaclang-0.7.16.dist-info → jaclang-0.7.18.dist-info}/METADATA +3 -1
- {jaclang-0.7.16.dist-info → jaclang-0.7.18.dist-info}/RECORD +81 -59
- /jaclang/tests/fixtures/{err.test.jac → baddy.test.jac} +0 -0
- {jaclang-0.7.16.dist-info → jaclang-0.7.18.dist-info}/WHEEL +0 -0
- {jaclang-0.7.16.dist-info → jaclang-0.7.18.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,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
|
|
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,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
|
|
73
|
-
"""Get
|
|
74
|
-
|
|
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
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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
|
|
92
|
-
|
|
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(
|
|
266
|
+
import_result = PythonImporter(JacMachine.get()).run_import(spec)
|
|
268
267
|
else:
|
|
269
|
-
import_result = JacImporter(
|
|
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.
|
|
369
|
+
return op1.__jac__.spawn_call(op2.__jac__)
|
|
370
370
|
elif isinstance(op2, WalkerArchitype):
|
|
371
|
-
return op2.
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
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
|
|
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.
|
|
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.
|
|
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_.
|
|
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
|
-
|
|
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
|
|
46
|
-
"""
|
|
47
|
-
return pm.hook.
|
|
45
|
+
def get_context() -> ExecutionContext:
|
|
46
|
+
"""Get current execution context."""
|
|
47
|
+
return pm.hook.get_context()
|
|
48
48
|
|
|
49
49
|
@staticmethod
|
|
50
|
-
def
|
|
51
|
-
"""
|
|
52
|
-
return pm.hook.
|
|
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
|
|
56
|
-
"""
|
|
57
|
-
return pm.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.
|
|
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
|
|
49
|
-
"""Get
|
|
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
|
|
55
|
-
"""
|
|
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
|
|
61
|
-
"""
|
|
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
|
+
}
|