jaclang 0.8.5__py3-none-any.whl → 0.8.7__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.md +4 -3
- jaclang/cli/cli.py +63 -29
- jaclang/cli/cmdreg.py +1 -140
- jaclang/compiler/passes/main/__init__.py +2 -0
- jaclang/compiler/passes/main/cfg_build_pass.py +21 -1
- jaclang/compiler/passes/main/inheritance_pass.py +8 -1
- jaclang/compiler/passes/main/pyast_gen_pass.py +70 -11
- jaclang/compiler/passes/main/pyast_load_pass.py +14 -20
- jaclang/compiler/passes/main/sym_tab_build_pass.py +4 -0
- jaclang/compiler/passes/main/tests/fixtures/cfg_has_var.jac +12 -0
- jaclang/compiler/passes/main/tests/fixtures/cfg_if_no_else.jac +11 -0
- jaclang/compiler/passes/main/tests/fixtures/cfg_return.jac +9 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_binary_op.jac +21 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_call_expr_class.jac +12 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_cyclic_symbol.jac +4 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_expr_call.jac +9 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_import_missing_module.jac +13 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_imported.jac +2 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_importer.jac +6 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_magic_call.jac +17 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_mod_path.jac +8 -0
- jaclang/compiler/passes/main/tests/fixtures/data_spatial_types.jac +1 -1
- jaclang/compiler/passes/main/tests/fixtures/import_symbol_type_infer.jac +11 -0
- jaclang/compiler/passes/main/tests/fixtures/infer_type_assignment.jac +5 -0
- jaclang/compiler/passes/main/tests/fixtures/member_access_type_inferred.jac +13 -0
- jaclang/compiler/passes/main/tests/fixtures/member_access_type_resolve.jac +11 -0
- jaclang/compiler/passes/main/tests/fixtures/type_annotation_assignment.jac +8 -0
- jaclang/compiler/passes/main/tests/test_cfg_build_pass.py +62 -24
- jaclang/compiler/passes/main/tests/test_checker_pass.py +161 -0
- jaclang/compiler/passes/main/type_checker_pass.py +147 -0
- jaclang/compiler/passes/tool/tests/fixtures/simple_walk_fmt.jac +1 -4
- jaclang/compiler/program.py +17 -3
- jaclang/compiler/type_system/__init__.py +1 -0
- jaclang/compiler/type_system/operations.py +104 -0
- jaclang/compiler/type_system/type_evaluator.py +560 -0
- jaclang/compiler/type_system/type_utils.py +41 -0
- jaclang/compiler/type_system/types.py +240 -0
- jaclang/compiler/unitree.py +15 -9
- jaclang/langserve/dev_engine.jac +645 -0
- jaclang/langserve/dev_server.jac +201 -0
- jaclang/langserve/engine.jac +135 -91
- jaclang/langserve/server.jac +21 -14
- jaclang/langserve/tests/server_test/test_lang_serve.py +2 -5
- jaclang/langserve/tests/test_dev_server.py +80 -0
- jaclang/langserve/tests/test_server.py +9 -2
- jaclang/langserve/utils.jac +44 -48
- jaclang/runtimelib/builtin.py +28 -39
- jaclang/runtimelib/importer.py +1 -1
- jaclang/runtimelib/machine.py +48 -64
- jaclang/runtimelib/memory.py +23 -5
- jaclang/runtimelib/tests/fixtures/savable_object.jac +10 -2
- jaclang/runtimelib/utils.py +13 -6
- jaclang/tests/fixtures/edge_node_walk.jac +1 -1
- jaclang/tests/fixtures/edges_walk.jac +1 -1
- jaclang/tests/fixtures/gendot_bubble_sort.jac +1 -1
- jaclang/tests/fixtures/jac_run_py_bugs.py +18 -0
- jaclang/tests/fixtures/jac_run_py_import.py +13 -0
- jaclang/tests/fixtures/lambda_arg_annotation.jac +15 -0
- jaclang/tests/fixtures/lambda_self.jac +18 -0
- jaclang/tests/fixtures/py_run.jac +8 -0
- jaclang/tests/fixtures/py_run.py +23 -0
- jaclang/tests/fixtures/pyfunc.py +2 -0
- jaclang/tests/test_cli.py +103 -14
- jaclang/tests/test_language.py +10 -4
- jaclang/utils/lang_tools.py +3 -0
- jaclang/utils/module_resolver.py +1 -1
- {jaclang-0.8.5.dist-info → jaclang-0.8.7.dist-info}/METADATA +4 -2
- {jaclang-0.8.5.dist-info → jaclang-0.8.7.dist-info}/RECORD +70 -37
- {jaclang-0.8.5.dist-info → jaclang-0.8.7.dist-info}/WHEEL +1 -1
- {jaclang-0.8.5.dist-info → jaclang-0.8.7.dist-info}/entry_points.txt +0 -0
jaclang/cli/cli.md
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# **Jac Language Command Line Interface (CLI)**
|
|
2
2
|
|
|
3
|
-
Jac Language CLI
|
|
3
|
+
git Jac Language CLI provides a variety of commands to facilitate users. Additionally, Jac language offers users the ability to define custom CLI commands through plugins. This document aims to provide an overview of each command along with clear usage instructions.
|
|
4
4
|
|
|
5
5
|
> [!TIP]
|
|
6
|
-
>
|
|
6
|
+
> Use `jac --help` to see available commands and usage.
|
|
7
7
|
|
|
8
8
|
### Click one of the default commands below and see the usage.
|
|
9
|
-
- [tool](#tool) , [run](#run) , [clean](#clean) , [format](#format) , [check](#check) , [build](#build)
|
|
9
|
+
- [tool](#tool) , [run](#run) , [clean](#clean) , [format](#format) , [check](#check) , [build](#build) , [enter](#enter) , [test](#test)
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
|
|
@@ -36,6 +36,7 @@ Parameters to execute the tool command:
|
|
|
36
36
|
- `sym.`: Generates a dot graph representation of the symbol table for the specified .jac file.
|
|
37
37
|
- `ast`: Displays the Abstract Syntax Tree (AST) of the specified .jac file.
|
|
38
38
|
- `ast.`: Generates a dot graph representation of the AST for the specified .jac file.
|
|
39
|
+
- `cfg.`: Generates a dot graph representatuin of the CFG(Control Flow Graph) for a specific .jac file
|
|
39
40
|
- `pyast`: Generates the Python AST for a .py file or the relevant Python AST for the generated Python code from a .jac file.
|
|
40
41
|
- `py`: Displays the relevant generated Python code for the respective Jac code in a .jac file.
|
|
41
42
|
- `file_path`: Path to the .jac or .py file.
|
jaclang/cli/cli.py
CHANGED
|
@@ -11,22 +11,17 @@ from pathlib import Path
|
|
|
11
11
|
from typing import Optional
|
|
12
12
|
|
|
13
13
|
import jaclang.compiler.unitree as uni
|
|
14
|
-
from jaclang.cli.cmdreg import
|
|
14
|
+
from jaclang.cli.cmdreg import cmd_registry
|
|
15
15
|
from jaclang.compiler.passes.main import PyastBuildPass
|
|
16
16
|
from jaclang.compiler.program import JacProgram
|
|
17
17
|
from jaclang.runtimelib.builtin import printgraph
|
|
18
18
|
from jaclang.runtimelib.constructs import WalkerArchetype
|
|
19
|
-
from jaclang.runtimelib.machine import
|
|
20
|
-
ExecutionContext,
|
|
21
|
-
JacMachine as Jac,
|
|
22
|
-
JacMachineInterface as JacInterface,
|
|
23
|
-
)
|
|
19
|
+
from jaclang.runtimelib.machine import ExecutionContext, JacMachine as Jac
|
|
24
20
|
from jaclang.runtimelib.utils import read_file_with_encoding
|
|
25
21
|
from jaclang.utils.helpers import debugger as db
|
|
26
22
|
from jaclang.utils.lang_tools import AstTool
|
|
27
23
|
|
|
28
|
-
|
|
29
|
-
JacInterface.create_cmd()
|
|
24
|
+
Jac.create_cmd()
|
|
30
25
|
Jac.setup()
|
|
31
26
|
|
|
32
27
|
|
|
@@ -137,9 +132,10 @@ def run(
|
|
|
137
132
|
jac run myprogram.jac --session mysession
|
|
138
133
|
jac run myprogram.jac --no-main
|
|
139
134
|
"""
|
|
140
|
-
# if no session specified, check if it was defined
|
|
135
|
+
# if no session specified, check if it was defined via global CLI args
|
|
141
136
|
# otherwise default to jaclang.session
|
|
142
137
|
base, mod, mach = proc_file_sess(filename, session)
|
|
138
|
+
lng = filename.split(".")[-1]
|
|
143
139
|
Jac.set_base_path(base)
|
|
144
140
|
|
|
145
141
|
if filename.endswith((".jac", ".py")):
|
|
@@ -148,6 +144,7 @@ def run(
|
|
|
148
144
|
target=mod,
|
|
149
145
|
base_path=base,
|
|
150
146
|
override_name="__main__" if main else None,
|
|
147
|
+
lng=lng,
|
|
151
148
|
)
|
|
152
149
|
except Exception as e:
|
|
153
150
|
print(f"Error running {filename}: {e}", file=sys.stderr)
|
|
@@ -159,6 +156,7 @@ def run(
|
|
|
159
156
|
target=mod,
|
|
160
157
|
base_path=base,
|
|
161
158
|
override_name="__main__" if main else None,
|
|
159
|
+
lng=lng,
|
|
162
160
|
)
|
|
163
161
|
except Exception as e:
|
|
164
162
|
print(f"Error running {filename}: {e}", file=sys.stderr)
|
|
@@ -219,7 +217,7 @@ def get_object(filename: str, id: str, session: str = "", main: bool = True) ->
|
|
|
219
217
|
|
|
220
218
|
|
|
221
219
|
@cmd_registry.register
|
|
222
|
-
def build(filename: str) -> None:
|
|
220
|
+
def build(filename: str, typecheck: bool = False) -> None:
|
|
223
221
|
"""Build the specified .jac file.
|
|
224
222
|
|
|
225
223
|
Compiles a Jac source file into a Jac Intermediate Representation (.jir) file,
|
|
@@ -227,21 +225,25 @@ def build(filename: str) -> None:
|
|
|
227
225
|
|
|
228
226
|
Args:
|
|
229
227
|
filename: Path to the .jac file to build
|
|
230
|
-
typecheck: Perform type checking during build (default:
|
|
228
|
+
typecheck: Perform type checking during build (default: False)
|
|
231
229
|
|
|
232
230
|
Examples:
|
|
233
231
|
jac build myprogram.jac
|
|
234
|
-
jac build myprogram.jac --
|
|
232
|
+
jac build myprogram.jac --typecheck
|
|
235
233
|
"""
|
|
236
|
-
if filename.endswith(".jac"):
|
|
237
|
-
(out := JacProgram()).compile(file_path=filename)
|
|
238
|
-
errs = len(out.errors_had)
|
|
239
|
-
warnings = len(out.warnings_had)
|
|
240
|
-
print(f"Errors: {errs}, Warnings: {warnings}")
|
|
241
|
-
with open(filename[:-4] + ".jir", "wb") as f:
|
|
242
|
-
pickle.dump(out, f)
|
|
243
|
-
else:
|
|
234
|
+
if not filename.endswith(".jac"):
|
|
244
235
|
print("Not a .jac file.", file=sys.stderr)
|
|
236
|
+
exit(1)
|
|
237
|
+
(out := JacProgram()).compile(file_path=filename, type_check=typecheck)
|
|
238
|
+
errs = len(out.errors_had)
|
|
239
|
+
warnings = len(out.warnings_had)
|
|
240
|
+
print(f"Errors: {errs}, Warnings: {warnings}")
|
|
241
|
+
|
|
242
|
+
for alrt in out.errors_had + out.warnings_had:
|
|
243
|
+
print(alrt.pretty_print(), file=sys.stderr)
|
|
244
|
+
|
|
245
|
+
with open(filename[:-4] + ".jir", "wb") as f:
|
|
246
|
+
pickle.dump(out, f)
|
|
245
247
|
|
|
246
248
|
|
|
247
249
|
@cmd_registry.register
|
|
@@ -325,6 +327,24 @@ def lsp() -> None:
|
|
|
325
327
|
run_lang_server()
|
|
326
328
|
|
|
327
329
|
|
|
330
|
+
@cmd_registry.register
|
|
331
|
+
def lsp_dev() -> None:
|
|
332
|
+
"""Run Jac Language Server Protocol in Developer Mode.
|
|
333
|
+
|
|
334
|
+
Starts the experimental Jac Language Server with enhanced features
|
|
335
|
+
for development and testing. Used by editor extensions in developer mode.
|
|
336
|
+
|
|
337
|
+
Args:
|
|
338
|
+
This command takes no parameters.
|
|
339
|
+
|
|
340
|
+
Examples:
|
|
341
|
+
jac lsp_dev
|
|
342
|
+
"""
|
|
343
|
+
from jaclang.langserve.dev_server import run_lang_server
|
|
344
|
+
|
|
345
|
+
run_lang_server()
|
|
346
|
+
|
|
347
|
+
|
|
328
348
|
@cmd_registry.register
|
|
329
349
|
def enter(
|
|
330
350
|
filename: str,
|
|
@@ -642,6 +662,14 @@ def start_cli() -> None:
|
|
|
642
662
|
- None
|
|
643
663
|
"""
|
|
644
664
|
parser = cmd_registry.parser
|
|
665
|
+
# Default to `run` when a file is provided without a subcommand
|
|
666
|
+
raw_argv = sys.argv[1:]
|
|
667
|
+
if (
|
|
668
|
+
raw_argv
|
|
669
|
+
and not raw_argv[0].startswith("-")
|
|
670
|
+
and raw_argv[0].lower().endswith((".jac", ".jir", ".py"))
|
|
671
|
+
):
|
|
672
|
+
sys.argv = [sys.argv[0], "run"] + raw_argv
|
|
645
673
|
args = parser.parse_args()
|
|
646
674
|
cmd_registry.args = args
|
|
647
675
|
|
|
@@ -651,16 +679,22 @@ def start_cli() -> None:
|
|
|
651
679
|
print("Jac path:", __file__)
|
|
652
680
|
return
|
|
653
681
|
|
|
682
|
+
if args.command is None:
|
|
683
|
+
parser.print_help()
|
|
684
|
+
return
|
|
685
|
+
|
|
654
686
|
command = cmd_registry.get(args.command)
|
|
655
|
-
if command:
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
687
|
+
if not command:
|
|
688
|
+
print(f"Unknown command: {args.command}", file=sys.stderr)
|
|
689
|
+
parser.print_help()
|
|
690
|
+
return
|
|
691
|
+
|
|
692
|
+
args_dict = vars(args)
|
|
693
|
+
args_dict.pop("command")
|
|
694
|
+
args_dict.pop("version", None)
|
|
695
|
+
ret = command.call(**args_dict)
|
|
696
|
+
if ret:
|
|
697
|
+
print(ret)
|
|
664
698
|
|
|
665
699
|
|
|
666
700
|
if __name__ == "__main__":
|
jaclang/cli/cmdreg.py
CHANGED
|
@@ -3,9 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import argparse
|
|
6
|
-
import cmd
|
|
7
6
|
import inspect
|
|
8
|
-
import pprint
|
|
9
7
|
import re
|
|
10
8
|
from typing import Callable, Dict, Optional
|
|
11
9
|
|
|
@@ -241,141 +239,4 @@ class CommandRegistry:
|
|
|
241
239
|
cmd_registry = CommandRegistry()
|
|
242
240
|
|
|
243
241
|
|
|
244
|
-
|
|
245
|
-
"""Command shell for the command line interface."""
|
|
246
|
-
|
|
247
|
-
intro = "Welcome to the Jac CLI!"
|
|
248
|
-
prompt = "jac> "
|
|
249
|
-
cmd_reg: CommandRegistry
|
|
250
|
-
|
|
251
|
-
def __init__(self, cmd_reg: CommandRegistry) -> None:
|
|
252
|
-
"""Initialize a CommandShell instance."""
|
|
253
|
-
self.cmd_reg = cmd_reg
|
|
254
|
-
super().__init__()
|
|
255
|
-
|
|
256
|
-
def do_exit(self, arg: list) -> bool:
|
|
257
|
-
"""Exit the command shell."""
|
|
258
|
-
return True
|
|
259
|
-
|
|
260
|
-
def default(self, line: str) -> None:
|
|
261
|
-
"""Process the command line input."""
|
|
262
|
-
args = vars(self.cmd_reg.parser.parse_args(line.split()))
|
|
263
|
-
command = self.cmd_reg.get(args["command"])
|
|
264
|
-
if command:
|
|
265
|
-
args.pop("command")
|
|
266
|
-
ret = command.call(**args)
|
|
267
|
-
if ret:
|
|
268
|
-
ret_str = pprint.pformat(ret, indent=2)
|
|
269
|
-
self.stdout.write(f"{ret_str}\n")
|
|
270
|
-
|
|
271
|
-
def do_help(self, arg: str) -> None:
|
|
272
|
-
"""Jac CLI help implementation."""
|
|
273
|
-
|
|
274
|
-
def get_info(name: str, doc: str, args: dict[str, inspect.Parameter]) -> None:
|
|
275
|
-
"""Format and display detailed command information."""
|
|
276
|
-
# Format the command header
|
|
277
|
-
self.stdout.write(f"\n{'=' * 80}\n")
|
|
278
|
-
self.stdout.write(f"COMMAND: {name}\n")
|
|
279
|
-
self.stdout.write(f"{'=' * 80}\n\n")
|
|
280
|
-
|
|
281
|
-
# Format the command description
|
|
282
|
-
doc_lines = doc.strip().split("\n")
|
|
283
|
-
for line in doc_lines:
|
|
284
|
-
self.stdout.write(f"{line}\n")
|
|
285
|
-
|
|
286
|
-
# Format the command arguments
|
|
287
|
-
if args:
|
|
288
|
-
self.stdout.write("\nARGUMENTS:\n")
|
|
289
|
-
for param_name, param in args.items():
|
|
290
|
-
# Get parameter type
|
|
291
|
-
type_name = (
|
|
292
|
-
param.annotation.__name__
|
|
293
|
-
if hasattr(param.annotation, "__name__")
|
|
294
|
-
else str(param.annotation)
|
|
295
|
-
)
|
|
296
|
-
|
|
297
|
-
# Format default value if present
|
|
298
|
-
default_str = ""
|
|
299
|
-
if param.default is not param.empty:
|
|
300
|
-
default_str = f" (default: {param.default})"
|
|
301
|
-
|
|
302
|
-
# Format required/optional status
|
|
303
|
-
req_str = (
|
|
304
|
-
" [required]"
|
|
305
|
-
if param.default is param.empty and param_name != "args"
|
|
306
|
-
else " [optional]"
|
|
307
|
-
)
|
|
308
|
-
|
|
309
|
-
self.stdout.write(
|
|
310
|
-
f" {param_name}: {type_name}{default_str}{req_str}\n"
|
|
311
|
-
)
|
|
312
|
-
else:
|
|
313
|
-
self.stdout.write("\nNo arguments\n")
|
|
314
|
-
|
|
315
|
-
# Format usage examples
|
|
316
|
-
command_parser = self.cmd_reg.sub_parsers.choices[name]
|
|
317
|
-
self.stdout.write(f"\nUSAGE:\n {command_parser.format_usage()[7:]}\n")
|
|
318
|
-
|
|
319
|
-
# Extract and format examples from docstring if present
|
|
320
|
-
if "Examples:" in doc:
|
|
321
|
-
examples_section = doc.split("Examples:")[1].strip()
|
|
322
|
-
example_lines = examples_section.split("\n")
|
|
323
|
-
self.stdout.write("\nEXAMPLES:\n")
|
|
324
|
-
for example in example_lines:
|
|
325
|
-
if example.strip():
|
|
326
|
-
self.stdout.write(f" {example.strip()}\n")
|
|
327
|
-
|
|
328
|
-
self.stdout.write("\n")
|
|
329
|
-
|
|
330
|
-
if arg == "all":
|
|
331
|
-
# Display all commands with their details
|
|
332
|
-
command_details = self.cmd_reg.get_all_commands()
|
|
333
|
-
for name, (doc, args) in sorted(command_details.items()):
|
|
334
|
-
get_info(name, doc, args)
|
|
335
|
-
|
|
336
|
-
elif arg:
|
|
337
|
-
# Display help for a specific command
|
|
338
|
-
command = self.cmd_reg.get(arg)
|
|
339
|
-
if command:
|
|
340
|
-
doc = command.func.__doc__ or "No help available."
|
|
341
|
-
args = command.sig.parameters
|
|
342
|
-
get_info(arg, doc, args)
|
|
343
|
-
else:
|
|
344
|
-
self.stdout.write(f"\nUnknown command: '{arg}'\n")
|
|
345
|
-
self.stdout.write("Type 'help' to see available commands.\n")
|
|
346
|
-
else:
|
|
347
|
-
# Display general help information
|
|
348
|
-
self.stdout.write("\n")
|
|
349
|
-
self.stdout.write("JAC PROGRAMMING LANGUAGE COMMAND LINE INTERFACE\n")
|
|
350
|
-
self.stdout.write("=" * 50 + "\n\n")
|
|
351
|
-
|
|
352
|
-
self.stdout.write("AVAILABLE COMMANDS:\n")
|
|
353
|
-
|
|
354
|
-
# Get all command names and sort them alphabetically
|
|
355
|
-
command_names = sorted(self.cmd_reg.registry.keys())
|
|
356
|
-
|
|
357
|
-
# Get brief descriptions for each command
|
|
358
|
-
command_descriptions: dict[str, str] = {}
|
|
359
|
-
for name in command_names:
|
|
360
|
-
command = self.cmd_reg.get(name)
|
|
361
|
-
if command and command.func.__doc__:
|
|
362
|
-
# Extract the first line of the docstring as a brief description
|
|
363
|
-
brief = command.func.__doc__.split("\n")[0].strip()
|
|
364
|
-
command_descriptions[name] = brief
|
|
365
|
-
else:
|
|
366
|
-
command_descriptions[name] = "No description available"
|
|
367
|
-
|
|
368
|
-
# Display commands with their brief descriptions
|
|
369
|
-
for name in command_names:
|
|
370
|
-
self.stdout.write(
|
|
371
|
-
f" {name.ljust(15)} - {command_descriptions[name]}\n"
|
|
372
|
-
)
|
|
373
|
-
|
|
374
|
-
self.stdout.write("\nFor detailed information on a specific command:\n")
|
|
375
|
-
self.stdout.write(" help <command>\n")
|
|
376
|
-
|
|
377
|
-
self.stdout.write("\nTo see detailed information for all commands:\n")
|
|
378
|
-
self.stdout.write(" help all\n")
|
|
379
|
-
|
|
380
|
-
self.stdout.write("\nTo exit the Jac CLI:\n")
|
|
381
|
-
self.stdout.write(" exit\n\n")
|
|
242
|
+
# Shell mode removed; interactive cmd-based shell is no longer supported.
|
|
@@ -9,6 +9,7 @@ from .def_use_pass import DefUsePass # noqa: I100
|
|
|
9
9
|
from .sem_def_match_pass import SemDefMatchPass # noqa: I100
|
|
10
10
|
from .import_pass import JacImportDepsPass # noqa: I100
|
|
11
11
|
from .def_impl_match_pass import DeclImplMatchPass # noqa: I100
|
|
12
|
+
from .type_checker_pass import TypeCheckPass # noqa: I100
|
|
12
13
|
from .pyast_load_pass import PyastBuildPass # type: ignore # noqa: I100
|
|
13
14
|
from .pyast_gen_pass import PyastGenPass # noqa: I100
|
|
14
15
|
from .pybc_gen_pass import PyBytecodeGenPass # noqa: I100
|
|
@@ -25,6 +26,7 @@ __all__ = [
|
|
|
25
26
|
"JacImportDepsPass",
|
|
26
27
|
"PyImportDepsPass",
|
|
27
28
|
"BinderPass",
|
|
29
|
+
"TypeCheckPass",
|
|
28
30
|
"SymTabBuildPass",
|
|
29
31
|
"SymTabLinkPass",
|
|
30
32
|
"DeclImplMatchPass",
|
|
@@ -152,7 +152,7 @@ class CFGBuildPass(UniPass):
|
|
|
152
152
|
"""Exit BasicBlockStmt nodes."""
|
|
153
153
|
if isinstance(node, uni.UniCFGNode) and not isinstance(node, uni.Semi):
|
|
154
154
|
self.first_exit = True
|
|
155
|
-
if not node.bb_out:
|
|
155
|
+
if not node.bb_out and not isinstance(node, (uni.ReturnStmt, uni.ArchHas)):
|
|
156
156
|
self.to_connect.append(node)
|
|
157
157
|
if (
|
|
158
158
|
isinstance(node, (uni.InForStmt, uni.IterForStmt))
|
|
@@ -176,6 +176,8 @@ class CFGBuildPass(UniPass):
|
|
|
176
176
|
if from_node in self.to_connect:
|
|
177
177
|
self.to_connect.remove(from_node)
|
|
178
178
|
self.ability_stack.pop()
|
|
179
|
+
elif isinstance(node, (uni.IfStmt, uni.ElseIf)) and not node.else_body:
|
|
180
|
+
self.to_connect.append(node)
|
|
179
181
|
|
|
180
182
|
def after_pass(self) -> None:
|
|
181
183
|
"""After pass."""
|
|
@@ -299,3 +301,21 @@ class CoalesceBBPass(UniPass):
|
|
|
299
301
|
|
|
300
302
|
dot += "}\n"
|
|
301
303
|
return dot
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
def cfg_dot_from_file(file_name: str) -> str:
|
|
307
|
+
"""Print the control flow graph."""
|
|
308
|
+
from jaclang.compiler.program import JacProgram
|
|
309
|
+
|
|
310
|
+
with open(file_name, "r") as f:
|
|
311
|
+
file_source = f.read()
|
|
312
|
+
|
|
313
|
+
ir = (prog := JacProgram()).compile(use_str=file_source, file_path=file_name)
|
|
314
|
+
|
|
315
|
+
cfg_pass = CoalesceBBPass(
|
|
316
|
+
ir_in=ir,
|
|
317
|
+
prog=prog,
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
dot = cfg_pass.printgraph_cfg()
|
|
321
|
+
return dot
|
|
@@ -62,9 +62,16 @@ class InheritancePass(Transform[uni.Module, uni.Module]):
|
|
|
62
62
|
|
|
63
63
|
base_class_symbol_table = base_class_symbol.symbol_table
|
|
64
64
|
|
|
65
|
+
# FIXME: If the base class symbol is imported from another module, the symbol table
|
|
66
|
+
# will be None. The imported symbols were ignored and introduced in the typecheck
|
|
67
|
+
# for imported module items. This needs to be investigated to ensure that even imported
|
|
68
|
+
# classes should have a symbol table (unless the module is not parsed and decided not to).
|
|
69
|
+
if base_class_symbol_table is None:
|
|
70
|
+
return
|
|
71
|
+
|
|
65
72
|
if self.is_missing_py_symbol_table(base_class_symbol, base_class_symbol_table):
|
|
66
73
|
return
|
|
67
|
-
|
|
74
|
+
|
|
68
75
|
node.sym_tab.inherit_sym_tab(base_class_symbol_table)
|
|
69
76
|
|
|
70
77
|
def inherit_from_atom_trailer(
|
|
@@ -187,6 +187,15 @@ class PyastGenPass(UniPass):
|
|
|
187
187
|
),
|
|
188
188
|
)
|
|
189
189
|
|
|
190
|
+
def needs_mtllm(self) -> None:
|
|
191
|
+
"""Ensure byLLM is imported only once."""
|
|
192
|
+
self._add_preamble_once(
|
|
193
|
+
self.needs_mtllm.__name__,
|
|
194
|
+
ast3.Import(
|
|
195
|
+
names=[self.sync(ast3.alias(name="byllm"), jac_node=self.ir_out)]
|
|
196
|
+
),
|
|
197
|
+
)
|
|
198
|
+
|
|
190
199
|
def needs_enum(self) -> None:
|
|
191
200
|
"""Ensure Enum utilities are imported only once."""
|
|
192
201
|
self._add_preamble_once(
|
|
@@ -735,17 +744,25 @@ class PyastGenPass(UniPass):
|
|
|
735
744
|
self, model: ast3.expr, caller: ast3.expr, args: ast3.Dict
|
|
736
745
|
) -> ast3.Call:
|
|
737
746
|
"""Reusable method to codegen call_llm(model, caller, args)."""
|
|
738
|
-
|
|
747
|
+
self.needs_mtllm()
|
|
748
|
+
mtir_cls_ast = self.sync(
|
|
749
|
+
ast3.Attribute(
|
|
750
|
+
value=self.sync(ast3.Name(id="byllm", ctx=ast3.Load())),
|
|
751
|
+
attr="MTIR",
|
|
752
|
+
ctx=ast3.Load(),
|
|
753
|
+
)
|
|
754
|
+
)
|
|
755
|
+
mtir_ast = self.sync(
|
|
739
756
|
ast3.Call(
|
|
740
|
-
func=self.
|
|
757
|
+
func=self.sync(
|
|
758
|
+
ast3.Attribute(
|
|
759
|
+
value=mtir_cls_ast,
|
|
760
|
+
attr="factory",
|
|
761
|
+
ctx=ast3.Load(),
|
|
762
|
+
)
|
|
763
|
+
),
|
|
741
764
|
args=[],
|
|
742
765
|
keywords=[
|
|
743
|
-
self.sync(
|
|
744
|
-
ast3.keyword(
|
|
745
|
-
arg="model",
|
|
746
|
-
value=model,
|
|
747
|
-
)
|
|
748
|
-
),
|
|
749
766
|
self.sync(
|
|
750
767
|
ast3.keyword(
|
|
751
768
|
arg="caller",
|
|
@@ -758,6 +775,38 @@ class PyastGenPass(UniPass):
|
|
|
758
775
|
value=args,
|
|
759
776
|
)
|
|
760
777
|
),
|
|
778
|
+
self.sync(
|
|
779
|
+
ast3.keyword(
|
|
780
|
+
arg="call_params",
|
|
781
|
+
value=self.sync(
|
|
782
|
+
ast3.Attribute(
|
|
783
|
+
value=model,
|
|
784
|
+
attr="call_params",
|
|
785
|
+
ctx=ast3.Load(),
|
|
786
|
+
),
|
|
787
|
+
),
|
|
788
|
+
)
|
|
789
|
+
),
|
|
790
|
+
],
|
|
791
|
+
)
|
|
792
|
+
)
|
|
793
|
+
return self.sync(
|
|
794
|
+
ast3.Call(
|
|
795
|
+
func=self.jaclib_obj("call_llm"),
|
|
796
|
+
args=[],
|
|
797
|
+
keywords=[
|
|
798
|
+
self.sync(
|
|
799
|
+
ast3.keyword(
|
|
800
|
+
arg="model",
|
|
801
|
+
value=model,
|
|
802
|
+
)
|
|
803
|
+
),
|
|
804
|
+
self.sync(
|
|
805
|
+
ast3.keyword(
|
|
806
|
+
arg="mtir",
|
|
807
|
+
value=mtir_ast,
|
|
808
|
+
)
|
|
809
|
+
),
|
|
761
810
|
],
|
|
762
811
|
)
|
|
763
812
|
)
|
|
@@ -953,7 +1002,8 @@ class PyastGenPass(UniPass):
|
|
|
953
1002
|
def exit_func_signature(self, node: uni.FuncSignature) -> None:
|
|
954
1003
|
params = (
|
|
955
1004
|
[self.sync(ast3.arg(arg="self", annotation=None))]
|
|
956
|
-
if (abl := node.
|
|
1005
|
+
if (abl := node.parent)
|
|
1006
|
+
and isinstance(abl, uni.Ability)
|
|
957
1007
|
and abl.is_method
|
|
958
1008
|
and not node.is_static
|
|
959
1009
|
and not node.is_in_py_class
|
|
@@ -2074,6 +2124,10 @@ class PyastGenPass(UniPass):
|
|
|
2074
2124
|
]
|
|
2075
2125
|
|
|
2076
2126
|
def exit_lambda_expr(self, node: uni.LambdaExpr) -> None:
|
|
2127
|
+
# Python lambda expressions don't support type annotations
|
|
2128
|
+
if node.signature:
|
|
2129
|
+
self._remove_lambda_param_annotations(node.signature)
|
|
2130
|
+
|
|
2077
2131
|
node.gen.py_ast = [
|
|
2078
2132
|
self.sync(
|
|
2079
2133
|
ast3.Lambda(
|
|
@@ -2095,6 +2149,11 @@ class PyastGenPass(UniPass):
|
|
|
2095
2149
|
)
|
|
2096
2150
|
]
|
|
2097
2151
|
|
|
2152
|
+
def _remove_lambda_param_annotations(self, signature: uni.FuncSignature) -> None:
|
|
2153
|
+
for param in signature.params:
|
|
2154
|
+
if param.gen.py_ast and isinstance(param.gen.py_ast[0], ast3.arg):
|
|
2155
|
+
param.gen.py_ast[0].annotation = None
|
|
2156
|
+
|
|
2098
2157
|
def exit_unary_expr(self, node: uni.UnaryExpr) -> None:
|
|
2099
2158
|
op_cls = UNARY_OP_MAP.get(node.op.name)
|
|
2100
2159
|
if op_cls:
|
|
@@ -2351,7 +2410,7 @@ class PyastGenPass(UniPass):
|
|
|
2351
2410
|
self.sync(
|
|
2352
2411
|
ast3.Attribute(
|
|
2353
2412
|
value=cast(ast3.expr, node.target.gen.py_ast[0]),
|
|
2354
|
-
attr=
|
|
2413
|
+
attr=node.right.sym_name,
|
|
2355
2414
|
ctx=cast(ast3.expr_context, node.right.py_ctx_func()),
|
|
2356
2415
|
)
|
|
2357
2416
|
)
|
|
@@ -2948,7 +3007,7 @@ class PyastGenPass(UniPass):
|
|
|
2948
3007
|
node.gen.py_ast = [self.sync(op_cls())]
|
|
2949
3008
|
|
|
2950
3009
|
def exit_name(self, node: uni.Name) -> None:
|
|
2951
|
-
name = node.sym_name
|
|
3010
|
+
name = node.sym_name
|
|
2952
3011
|
node.gen.py_ast = [self.sync(ast3.Name(id=name, ctx=node.py_ctx_func()))]
|
|
2953
3012
|
|
|
2954
3013
|
def exit_float(self, node: uni.Float) -> None:
|