jaclang 0.7.18__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 +2 -2
- 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/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 +61 -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 +68 -7
- jaclang/langserve/tests/test_server.py +3 -3
- jaclang/langserve/utils.py +0 -113
- jaclang/plugin/tests/test_jaseci.py +23 -4
- jaclang/runtimelib/architype.py +20 -2
- jaclang/runtimelib/importer.py +3 -0
- jaclang/runtimelib/machine.py +92 -4
- jaclang/runtimelib/memory.py +1 -1
- jaclang/settings.py +4 -0
- jaclang/tests/fixtures/bar.jac +1 -1
- 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/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 +131 -2
- jaclang/utils/treeprinter.py +1 -1
- {jaclang-0.7.18.dist-info → jaclang-0.7.21.dist-info}/METADATA +4 -2
- {jaclang-0.7.18.dist-info → jaclang-0.7.21.dist-info}/RECORD +45 -36
- {jaclang-0.7.18.dist-info → jaclang-0.7.21.dist-info}/WHEEL +0 -0
- {jaclang-0.7.18.dist-info → jaclang-0.7.21.dist-info}/entry_points.txt +0 -0
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
|
@@ -195,6 +195,8 @@ class PythonImporter(Importer):
|
|
|
195
195
|
loaded_items: list = []
|
|
196
196
|
if spec.target.startswith("."):
|
|
197
197
|
spec.target = spec.target.lstrip(".")
|
|
198
|
+
if len(spec.target.split(".")) > 1:
|
|
199
|
+
spec.target = spec.target.split(".")[-1]
|
|
198
200
|
full_target = path.normpath(path.join(spec.caller_dir, spec.target))
|
|
199
201
|
imp_spec = importlib.util.spec_from_file_location(
|
|
200
202
|
spec.target, full_target + ".py"
|
|
@@ -340,6 +342,7 @@ class JacImporter(Importer):
|
|
|
340
342
|
spec.full_target,
|
|
341
343
|
caller_dir=spec.caller_dir,
|
|
342
344
|
cachable=spec.cachable,
|
|
345
|
+
reload=reload if reload else False,
|
|
343
346
|
)
|
|
344
347
|
try:
|
|
345
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
|
@@ -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
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import:py from jaclang.runtimelib.machine {JacMachine}
|
|
2
|
+
import:jac from bar {Item}
|
|
3
|
+
|
|
4
|
+
node test_node {
|
|
5
|
+
has value:int;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
walker test_walker {
|
|
9
|
+
can visit_nodes with `root entry {
|
|
10
|
+
visit [-->];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
can print_value with test_node | Item entry {
|
|
14
|
+
print("Value:", f'{here.value}');
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
walker child_walker :test_walker: {}
|
|
19
|
+
|
|
20
|
+
with entry {
|
|
21
|
+
for value in range(1, 4) {
|
|
22
|
+
root ++> test_node(value=value);
|
|
23
|
+
root ++> Item(value=value);
|
|
24
|
+
}
|
|
25
|
+
node_obj = JacMachine.get().spawn_node(node_name='test_node', attributes={'value': 0}, module_name='__main__');
|
|
26
|
+
walker_obj = JacMachine.get().spawn_walker(walker_name='child_walker', module_name='__main__');
|
|
27
|
+
external_node = JacMachine.get().spawn_node(node_name='Item', attributes={'value': 0}, module_name='bar');
|
|
28
|
+
root ++> external_node;
|
|
29
|
+
root ++> node_obj;
|
|
30
|
+
print("Spawned Node:", node_obj);
|
|
31
|
+
print("Spawned Walker:", walker_obj);
|
|
32
|
+
print("Spawned External node:", external_node);
|
|
33
|
+
root spawn walker_obj;
|
|
34
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
node test_node {
|
|
2
|
+
has value: int;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
walker test_walker {
|
|
6
|
+
has visited_nodes: list = [];
|
|
7
|
+
has entry_count: int = 0;
|
|
8
|
+
has exit_count: int = 0;
|
|
9
|
+
|
|
10
|
+
can traverse with `root entry {
|
|
11
|
+
visit [-->](`?test_node);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
can log_entry with entry {
|
|
15
|
+
print("Entering at the beginning of walker: ",here);
|
|
16
|
+
self.entry_count += 1;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
can log_visit with test_node exit {
|
|
20
|
+
print("Visiting node : ", here);
|
|
21
|
+
self.visited_nodes.append(here);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
can log_exit with exit {
|
|
25
|
+
print("Exiting at the end of walker: ",here);
|
|
26
|
+
self.exit_count += 1;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
with entry {
|
|
31
|
+
for i in range(10) {
|
|
32
|
+
root ++> (next:=test_node(value=i));
|
|
33
|
+
}
|
|
34
|
+
wlk_obj = root spawn test_walker();
|
|
35
|
+
print(wlk_obj);
|
|
36
|
+
}
|
jaclang/tests/fixtures/foo.jac
CHANGED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This test file is to ensure the valid syntax of jac lang.
|
|
3
|
+
Add new jac syntax here to test if it compile without any issue.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
# Import statement without trailing comma.
|
|
8
|
+
import:py from time { sleep, timezone, tzname }
|
|
9
|
+
# Import statement with trailing comma.
|
|
10
|
+
import:py from os { path, getenv, getpid, }
|
|
11
|
+
|
|
12
|
+
enum WithoutTrailComma {
|
|
13
|
+
FOO = "FOO",
|
|
14
|
+
BAR = "BAR"
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
enum WithTrailComma {
|
|
18
|
+
FOO = "FOO",
|
|
19
|
+
BAR = "BAR",
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
can without_trail_comma(a: int, b: int) {}
|
|
23
|
+
|
|
24
|
+
can with_trail_comma(a: int, b: int, c: int, ) {}
|
|
25
|
+
|
|
26
|
+
with entry {
|
|
27
|
+
|
|
28
|
+
dict_without_trail_comma = {"key": "value"};
|
|
29
|
+
dict_with_trail_comma = {"key": "val",};
|
|
30
|
+
|
|
31
|
+
list_without_trail_comma = ["foo", "bar"];
|
|
32
|
+
list_with_trail_comma = ["foo", "bar",];
|
|
33
|
+
|
|
34
|
+
set_without_trail_comma = {"foo", "bar"};
|
|
35
|
+
set_with_trail_comma = {"foo", "bar"};
|
|
36
|
+
obj foo11 {
|
|
37
|
+
has val: int,
|
|
38
|
+
val2: int = 9,
|
|
39
|
+
val3: int = 8,
|
|
40
|
+
val4: int = 8,
|
|
41
|
+
val5: int = 8;
|
|
42
|
+
}
|
|
43
|
+
empty_tuple = ();
|
|
44
|
+
single_tuple = ('single', );
|
|
45
|
+
|
|
46
|
+
tuple_without_trail_comma = ("foo", "bar");
|
|
47
|
+
tuple_with_trail_comma = ("foo", "bar", );
|
|
48
|
+
|
|
49
|
+
mixed_tuple = (1, 'two', 3.0, False);
|
|
50
|
+
mixed_tuple_with_trail_comma = (1, 'two', 3.0, False, );
|
|
51
|
+
|
|
52
|
+
nested_tuple = (1, (2, 3), 'four');
|
|
53
|
+
nested_tuple_with_trail_comma = (1, (2, 3), 'four', );
|
|
54
|
+
|
|
55
|
+
deeply_nested_tuple = (1, (2, (3, (4, 5))));
|
|
56
|
+
deeply_nested_tuple_with_trail_comma = (1, (2, (3, (4, 5))), );
|
|
57
|
+
|
|
58
|
+
tuple1 = ("foo", );
|
|
59
|
+
tuple1_2 = ("foo", "bar", "baz");
|
|
60
|
+
tuple_2_2 = ("foo", "bar", "baz", );
|
|
61
|
+
|
|
62
|
+
foo_instance1 = foo11(val=90);
|
|
63
|
+
foo_instance1_2 = foo11(val=90, );
|
|
64
|
+
|
|
65
|
+
foo_instance3 = foo11(2, 34, val3=90);
|
|
66
|
+
foo_instance3_2 = foo11(2, 34, val3=90, );
|
|
67
|
+
|
|
68
|
+
foo_instance4 = foo11(2, 34, val3=90, val4=90);
|
|
69
|
+
foo_instance4_2 = foo11(2, 34, val3=90, val4=90, );
|
|
70
|
+
|
|
71
|
+
func_tuple = (foo_instance1, foo_instance3_2, len(tuple1_2));
|
|
72
|
+
func_tuple_with_trail_comma = (foo_instance1, foo_instance3_2, len(tuple1_2), );
|
|
73
|
+
|
|
74
|
+
unpack_tuple = (1, 2, 3);
|
|
75
|
+
(a, b, c, ) = unpack_tuple;
|
|
76
|
+
values = (a, b, c);
|
|
77
|
+
|
|
78
|
+
(1, 2);
|
|
79
|
+
(1, 2, );
|
|
80
|
+
(1, 2, 3, 4);
|
|
81
|
+
(1, 2, 3, 4, );
|
|
82
|
+
|
|
83
|
+
val_f = (foo_instance3_2.val, foo_instance3_2.val2, foo_instance3_2.val3);
|
|
84
|
+
expression_tuple = (1 + 2, len("abc"), foo_instance1.val * 2);
|
|
85
|
+
tuple_with_list = (1, [2, 3], 4);
|
|
86
|
+
print("Code compiled and ran successfully!");
|
|
87
|
+
|
|
88
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import:jac from bar { bar_walk }
|
|
2
|
+
import:py from jaclang.runtimelib.machine { JacMachine }
|
|
3
|
+
import:py os;
|
|
4
|
+
|
|
5
|
+
can update_bar_walker {
|
|
6
|
+
"Updating bar.jac with new behavior." |> print;
|
|
7
|
+
(bar_walk_new, ) = JacMachine.get().update_walker(
|
|
8
|
+
"bar",
|
|
9
|
+
items={'bar_walk': None}
|
|
10
|
+
);
|
|
11
|
+
"Running bar_walk after update..." |> print;
|
|
12
|
+
root spawn bar_walk_new();
|
|
13
|
+
print(f"bar_walk: {bar_walk_new.__dict__}");
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
with entry {
|
|
18
|
+
update_bar_walker();
|
|
19
|
+
}
|
jaclang/tests/test_cli.py
CHANGED
|
@@ -190,6 +190,33 @@ class JacCliTests(TestCase):
|
|
|
190
190
|
r"8\:37 \- 8:44.*ModuleItem - display - abs_path\:.*fixtures/pygame_mock/display.py",
|
|
191
191
|
)
|
|
192
192
|
|
|
193
|
+
def test_builtins_loading(self) -> None:
|
|
194
|
+
"""Testing for print AstTool."""
|
|
195
|
+
from jaclang.settings import settings
|
|
196
|
+
|
|
197
|
+
settings.ast_symbol_info_detailed = True
|
|
198
|
+
captured_output = io.StringIO()
|
|
199
|
+
sys.stdout = captured_output
|
|
200
|
+
|
|
201
|
+
cli.tool("ir", ["ast", f"{self.fixture_abs_path('builtins_test.jac')}"])
|
|
202
|
+
|
|
203
|
+
sys.stdout = sys.__stdout__
|
|
204
|
+
stdout_value = captured_output.getvalue()
|
|
205
|
+
settings.ast_symbol_info_detailed = False
|
|
206
|
+
|
|
207
|
+
self.assertRegex(
|
|
208
|
+
stdout_value,
|
|
209
|
+
r"2\:8 \- 2\:12.*BuiltinType - list - .*SymbolPath: builtins_test.builtins.list",
|
|
210
|
+
)
|
|
211
|
+
self.assertRegex(
|
|
212
|
+
stdout_value,
|
|
213
|
+
r"15\:5 \- 15\:8.*Name - dir - .*SymbolPath: builtins_test.builtins.dir",
|
|
214
|
+
)
|
|
215
|
+
self.assertRegex(
|
|
216
|
+
stdout_value,
|
|
217
|
+
r"13\:12 \- 13\:18.*Name - append - .*SymbolPath: builtins_test.builtins.list.append",
|
|
218
|
+
)
|
|
219
|
+
|
|
193
220
|
def test_ast_dotgen(self) -> None:
|
|
194
221
|
"""Testing for print AstTool."""
|
|
195
222
|
captured_output = io.StringIO()
|
|
@@ -218,8 +245,8 @@ class JacCliTests(TestCase):
|
|
|
218
245
|
sys.stdout = sys.__stdout__
|
|
219
246
|
stdout_value = captured_output.getvalue()
|
|
220
247
|
self.assertEqual(stdout_value.count("type_info.ServerWrapper"), 7)
|
|
221
|
-
self.assertEqual(stdout_value.count("builtins.int"),
|
|
222
|
-
self.assertEqual(stdout_value.count("builtins.str"),
|
|
248
|
+
self.assertEqual(stdout_value.count("builtins.int"), 3)
|
|
249
|
+
self.assertEqual(stdout_value.count("builtins.str"), 10)
|
|
223
250
|
|
|
224
251
|
def test_build_and_run(self) -> None:
|
|
225
252
|
"""Testing for print AstTool."""
|
jaclang/tests/test_language.py
CHANGED
|
@@ -522,7 +522,7 @@ class JacLanguageTests(TestCase):
|
|
|
522
522
|
self.assertIn("can greet2(**kwargs: Any) {", output)
|
|
523
523
|
self.assertIn("squares_dict = {x: (x ** 2) for x in numbers};", output)
|
|
524
524
|
self.assertIn(
|
|
525
|
-
'\n\n@ my_decorator \n can say_hello() {\n """Say hello""" ;
|
|
525
|
+
'\n\n@ my_decorator \n can say_hello() {\n\n """Say hello""" ;', output
|
|
526
526
|
)
|
|
527
527
|
|
|
528
528
|
def test_needs_import_2(self) -> None:
|
|
@@ -568,7 +568,7 @@ class JacLanguageTests(TestCase):
|
|
|
568
568
|
py_ast.parse(f.read()), mod_path=py_out_path
|
|
569
569
|
),
|
|
570
570
|
).ir.unparse()
|
|
571
|
-
self.assertIn("class X {\n with entry {\n a_b = 67;", output)
|
|
571
|
+
self.assertIn("class X {\n with entry {\n\n a_b = 67;", output)
|
|
572
572
|
self.assertIn("br = b'Hello\\\\\\\\nWorld'", output)
|
|
573
573
|
self.assertIn("class Circle {\n can init(radius: float", output)
|
|
574
574
|
self.assertIn("<>node = 90; \n print(<>node) ;\n}\n", output)
|
|
@@ -677,6 +677,15 @@ class JacLanguageTests(TestCase):
|
|
|
677
677
|
self.assertIn("1", stdout_value[0])
|
|
678
678
|
self.assertIn("[2, 3, 4]", stdout_value[1])
|
|
679
679
|
|
|
680
|
+
def test_trailing_comma(self) -> None:
|
|
681
|
+
"""Test trailing comma."""
|
|
682
|
+
captured_output = io.StringIO()
|
|
683
|
+
sys.stdout = captured_output
|
|
684
|
+
jac_import("trailing_comma", base_path=self.fixture_abs_path("./"))
|
|
685
|
+
sys.stdout = sys.__stdout__
|
|
686
|
+
stdout_value = captured_output.getvalue()
|
|
687
|
+
self.assertIn("Code compiled and ran successfully!", stdout_value)
|
|
688
|
+
|
|
680
689
|
def test_try_finally(self) -> None:
|
|
681
690
|
"""Test try finally."""
|
|
682
691
|
captured_output = io.StringIO()
|
|
@@ -952,6 +961,103 @@ class JacLanguageTests(TestCase):
|
|
|
952
961
|
self.assertIn("Item value: 0", stdout_value)
|
|
953
962
|
self.assertIn("Created 5 items.", stdout_value)
|
|
954
963
|
|
|
964
|
+
def test_walker_dynamic_update(self) -> None:
|
|
965
|
+
"""Test dynamic update of a walker during runtime."""
|
|
966
|
+
session = self.fixture_abs_path("bar_walk.session")
|
|
967
|
+
bar_file_path = self.fixture_abs_path("bar.jac")
|
|
968
|
+
update_file_path = self.fixture_abs_path("walker_update.jac")
|
|
969
|
+
captured_output = io.StringIO()
|
|
970
|
+
sys.stdout = captured_output
|
|
971
|
+
cli.enter(
|
|
972
|
+
filename=bar_file_path,
|
|
973
|
+
session=session,
|
|
974
|
+
entrypoint="bar_walk",
|
|
975
|
+
args=[],
|
|
976
|
+
)
|
|
977
|
+
sys.stdout = sys.__stdout__
|
|
978
|
+
stdout_value = captured_output.getvalue()
|
|
979
|
+
expected_output = "Created 5 items."
|
|
980
|
+
self.assertIn(expected_output, stdout_value.split("\n"))
|
|
981
|
+
# Define the new behavior to be added
|
|
982
|
+
new_behavior = """
|
|
983
|
+
# New behavior added during runtime
|
|
984
|
+
can end with `root exit {
|
|
985
|
+
"bar_walk has been updated with new behavior!" |> print;
|
|
986
|
+
disengage;
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
"""
|
|
990
|
+
|
|
991
|
+
# Backup the original file content
|
|
992
|
+
with open(bar_file_path, "r") as bar_file:
|
|
993
|
+
original_content = bar_file.read()
|
|
994
|
+
|
|
995
|
+
# Update the bar.jac file with new behavior
|
|
996
|
+
with open(bar_file_path, "r+") as bar_file:
|
|
997
|
+
content = bar_file.read()
|
|
998
|
+
last_brace_index = content.rfind("}")
|
|
999
|
+
if last_brace_index != -1:
|
|
1000
|
+
updated_content = content[:last_brace_index] + new_behavior
|
|
1001
|
+
bar_file.seek(0)
|
|
1002
|
+
bar_file.write(updated_content)
|
|
1003
|
+
bar_file.truncate()
|
|
1004
|
+
|
|
1005
|
+
captured_output = io.StringIO()
|
|
1006
|
+
sys.stdout = captured_output
|
|
1007
|
+
|
|
1008
|
+
try:
|
|
1009
|
+
cli.run(
|
|
1010
|
+
filename=update_file_path,
|
|
1011
|
+
)
|
|
1012
|
+
sys.stdout = sys.__stdout__
|
|
1013
|
+
stdout_value = captured_output.getvalue()
|
|
1014
|
+
expected_output = "bar_walk has been updated with new behavior!"
|
|
1015
|
+
self.assertIn(expected_output, stdout_value.split("\n"))
|
|
1016
|
+
finally:
|
|
1017
|
+
# Restore the original content of bar.jac
|
|
1018
|
+
with open(bar_file_path, "w") as bar_file:
|
|
1019
|
+
bar_file.write(original_content)
|
|
1020
|
+
|
|
1021
|
+
def test_dynamic_spawn_architype(self) -> None:
|
|
1022
|
+
"""Test that the walker and node can be spawned and behaves as expected."""
|
|
1023
|
+
captured_output = io.StringIO()
|
|
1024
|
+
sys.stdout = captured_output
|
|
1025
|
+
cli.run(self.fixture_abs_path("dynamic_architype.jac"))
|
|
1026
|
+
|
|
1027
|
+
output = captured_output.getvalue().strip()
|
|
1028
|
+
output_lines = output.split("\n")
|
|
1029
|
+
|
|
1030
|
+
# Expected outputs for spawned entities
|
|
1031
|
+
expected_spawned_node = "Spawned Node:"
|
|
1032
|
+
expected_spawned_walker = "Spawned Walker:"
|
|
1033
|
+
expected_spawned_external_node = "Spawned External node:"
|
|
1034
|
+
|
|
1035
|
+
# Check for the spawned messages
|
|
1036
|
+
self.assertTrue(
|
|
1037
|
+
any(expected_spawned_node in line for line in output_lines),
|
|
1038
|
+
f"Expected '{expected_spawned_node}' in output.",
|
|
1039
|
+
)
|
|
1040
|
+
self.assertTrue(
|
|
1041
|
+
any(expected_spawned_walker in line for line in output_lines),
|
|
1042
|
+
f"Expected '{expected_spawned_walker}' in output.",
|
|
1043
|
+
)
|
|
1044
|
+
self.assertTrue(
|
|
1045
|
+
any(expected_spawned_external_node in line for line in output_lines),
|
|
1046
|
+
f"Expected '{expected_spawned_external_node}' in output.",
|
|
1047
|
+
)
|
|
1048
|
+
|
|
1049
|
+
# Expected values from the walker traversal
|
|
1050
|
+
expected_values = ["Value: 0", "Value: 1", "Value: 2", "Value: 3"]
|
|
1051
|
+
|
|
1052
|
+
# Each expected value should appear twice (once for test_node, once for Item)
|
|
1053
|
+
for val in expected_values:
|
|
1054
|
+
occurrences = [line for line in output_lines if line.strip() == val]
|
|
1055
|
+
self.assertEqual(
|
|
1056
|
+
len(occurrences),
|
|
1057
|
+
2,
|
|
1058
|
+
f"Expected '{val}' to appear 2 times, but found {len(occurrences)}.",
|
|
1059
|
+
)
|
|
1060
|
+
|
|
955
1061
|
def test_object_ref_interface(self) -> None:
|
|
956
1062
|
"""Test class method output."""
|
|
957
1063
|
captured_output = io.StringIO()
|
|
@@ -962,3 +1068,26 @@ class JacLanguageTests(TestCase):
|
|
|
962
1068
|
self.assertEqual(len(stdout_value[0]), 32)
|
|
963
1069
|
self.assertEqual("MyNode(value=0)", stdout_value[1])
|
|
964
1070
|
self.assertEqual("valid: True", stdout_value[2])
|
|
1071
|
+
|
|
1072
|
+
def test_match_multi_ex(self) -> None:
|
|
1073
|
+
"""Test match case with multiple expressions."""
|
|
1074
|
+
captured_output = io.StringIO()
|
|
1075
|
+
sys.stdout = captured_output
|
|
1076
|
+
jac_import("match_multi_ex", base_path=self.fixture_abs_path("./"))
|
|
1077
|
+
sys.stdout = sys.__stdout__
|
|
1078
|
+
stdout_value = captured_output.getvalue().split("\n")
|
|
1079
|
+
self.assertEqual("Ten", stdout_value[0])
|
|
1080
|
+
self.assertEqual("ten", stdout_value[1])
|
|
1081
|
+
|
|
1082
|
+
def test_entry_exit(self) -> None:
|
|
1083
|
+
"""Test entry and exit behavior of walker."""
|
|
1084
|
+
captured_output = io.StringIO()
|
|
1085
|
+
sys.stdout = captured_output
|
|
1086
|
+
jac_import("entry_exit", base_path=self.fixture_abs_path("./"))
|
|
1087
|
+
sys.stdout = sys.__stdout__
|
|
1088
|
+
stdout_value = captured_output.getvalue().split("\n")
|
|
1089
|
+
self.assertIn("Entering at the beginning of walker: Root()", stdout_value[0])
|
|
1090
|
+
self.assertIn("entry_count=1, exit_count=1", str(stdout_value[12]))
|
|
1091
|
+
self.assertIn(
|
|
1092
|
+
"Exiting at the end of walker: test_node(value=", stdout_value[11]
|
|
1093
|
+
)
|
jaclang/utils/treeprinter.py
CHANGED
|
@@ -113,7 +113,7 @@ def print_ast_tree(
|
|
|
113
113
|
if node.sym
|
|
114
114
|
else "<No Symbol is associated with this node>"
|
|
115
115
|
)
|
|
116
|
-
out += f" SymbolPath: {symbol}"
|
|
116
|
+
out += f", SymbolPath: {symbol}"
|
|
117
117
|
return out
|
|
118
118
|
elif isinstance(node, Token):
|
|
119
119
|
return f"{node.__class__.__name__} - {node.value}, {access}"
|