jaclang 0.8.8__py3-none-any.whl → 0.8.9__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 +64 -2
- jaclang/compiler/constant.py +6 -1
- jaclang/compiler/jac.lark +34 -41
- jaclang/compiler/larkparse/jac_parser.py +2 -2
- jaclang/compiler/parser.py +143 -27
- jaclang/compiler/passes/main/def_use_pass.py +1 -0
- jaclang/compiler/passes/main/pyast_gen_pass.py +151 -83
- jaclang/compiler/passes/main/pyast_load_pass.py +2 -0
- jaclang/compiler/passes/main/tests/test_checker_pass.py +0 -1
- jaclang/compiler/passes/main/tests/test_predynamo_pass.py +13 -14
- jaclang/compiler/passes/tool/doc_ir_gen_pass.py +104 -20
- jaclang/compiler/passes/tool/jac_formatter_pass.py +2 -2
- jaclang/compiler/passes/tool/tests/fixtures/import_fmt.jac +7 -1
- jaclang/compiler/passes/tool/tests/fixtures/tagbreak.jac +135 -29
- jaclang/compiler/program.py +6 -2
- jaclang/compiler/type_system/type_evaluator.jac +959 -0
- jaclang/compiler/unitree.py +23 -13
- jaclang/lib.py +17 -0
- jaclang/runtimelib/archetype.py +25 -25
- jaclang/runtimelib/constructs.py +2 -2
- jaclang/runtimelib/machine.py +57 -47
- jaclang/settings.py +1 -2
- jaclang/tests/fixtures/attr_pattern_case.jac +18 -0
- jaclang/tests/fixtures/funccall_genexpr.jac +7 -0
- jaclang/tests/fixtures/funccall_genexpr.py +5 -0
- jaclang/tests/fixtures/py2jac_empty.py +0 -0
- jaclang/tests/test_cli.py +134 -18
- jaclang/tests/test_language.py +146 -29
- jaclang/tests/test_reference.py +3 -1
- jaclang/utils/helpers.py +20 -4
- jaclang/utils/tests/test_lang_tools.py +4 -15
- {jaclang-0.8.8.dist-info → jaclang-0.8.9.dist-info}/METADATA +1 -1
- {jaclang-0.8.8.dist-info → jaclang-0.8.9.dist-info}/RECORD +35 -30
- jaclang/compiler/type_system/type_evaluator.py +0 -844
- {jaclang-0.8.8.dist-info → jaclang-0.8.9.dist-info}/WHEEL +0 -0
- {jaclang-0.8.8.dist-info → jaclang-0.8.9.dist-info}/entry_points.txt +0 -0
jaclang/compiler/parser.py
CHANGED
|
@@ -108,9 +108,14 @@ class JacParser(Transform[uni.Source, uni.Module]):
|
|
|
108
108
|
|
|
109
109
|
def feed_current_token(iparser: jl.InteractiveParser, tok: jl.Token) -> bool:
|
|
110
110
|
"""Feed the current token to the parser."""
|
|
111
|
+
max_attempts = 100 # Prevent infinite loops
|
|
112
|
+
attempts = 0
|
|
111
113
|
while tok.type not in iparser.accepts():
|
|
114
|
+
if attempts >= max_attempts:
|
|
115
|
+
return False # Give up after too many attempts
|
|
112
116
|
if not try_feed_missing_token(iparser):
|
|
113
117
|
return False
|
|
118
|
+
attempts += 1
|
|
114
119
|
iparser.feed_token(tok)
|
|
115
120
|
return True
|
|
116
121
|
|
|
@@ -650,11 +655,12 @@ class JacParser(Transform[uni.Source, uni.Module]):
|
|
|
650
655
|
def sem_def(self, _: None) -> uni.SemDef:
|
|
651
656
|
"""Grammar rule.
|
|
652
657
|
|
|
653
|
-
sem_def: KW_SEM dotted_name EQ
|
|
658
|
+
sem_def: KW_SEM dotted_name (EQ | KW_IS) STRING SEMI
|
|
654
659
|
"""
|
|
655
660
|
self.consume_token(Tok.KW_SEM)
|
|
656
661
|
target = self.extract_from_list(self.consume(list), uni.NameAtom)
|
|
657
|
-
self.
|
|
662
|
+
if not self.match_token(Tok.KW_IS):
|
|
663
|
+
self.consume_token(Tok.EQ)
|
|
658
664
|
value = self.consume(uni.String)
|
|
659
665
|
self.consume_token(Tok.SEMI)
|
|
660
666
|
return uni.SemDef(
|
|
@@ -1705,15 +1711,18 @@ class JacParser(Transform[uni.Source, uni.Module]):
|
|
|
1705
1711
|
lambda_expr: KW_LAMBDA func_decl_params? (RETURN_HINT expression)? COLON expression
|
|
1706
1712
|
"""
|
|
1707
1713
|
return_type: uni.Expr | None = None
|
|
1714
|
+
return_hint_tok: uni.Token | None = None
|
|
1708
1715
|
sig_kid: list[uni.UniNode] = []
|
|
1709
1716
|
self.consume_token(Tok.KW_LAMBDA)
|
|
1710
1717
|
params = self.match(list)
|
|
1711
|
-
if self.match_token(Tok.RETURN_HINT):
|
|
1718
|
+
if return_hint_tok := self.match_token(Tok.RETURN_HINT):
|
|
1712
1719
|
return_type = self.consume(uni.Expr)
|
|
1713
1720
|
self.consume_token(Tok.COLON)
|
|
1714
1721
|
body = self.consume(uni.Expr)
|
|
1715
1722
|
if params:
|
|
1716
1723
|
sig_kid.extend(params)
|
|
1724
|
+
if return_hint_tok:
|
|
1725
|
+
sig_kid.append(return_hint_tok)
|
|
1717
1726
|
if return_type:
|
|
1718
1727
|
sig_kid.append(return_type)
|
|
1719
1728
|
signature = (
|
|
@@ -1731,7 +1740,11 @@ class JacParser(Transform[uni.Source, uni.Module]):
|
|
|
1731
1740
|
if params or return_type
|
|
1732
1741
|
else None
|
|
1733
1742
|
)
|
|
1734
|
-
new_kid = [
|
|
1743
|
+
new_kid = [
|
|
1744
|
+
i
|
|
1745
|
+
for i in self.cur_nodes
|
|
1746
|
+
if i != params and i != return_type and i != return_hint_tok
|
|
1747
|
+
]
|
|
1735
1748
|
new_kid.insert(1, signature) if signature else None
|
|
1736
1749
|
return uni.LambdaExpr(
|
|
1737
1750
|
signature=signature,
|
|
@@ -1965,25 +1978,31 @@ class JacParser(Transform[uni.Source, uni.Module]):
|
|
|
1965
1978
|
)
|
|
1966
1979
|
return self._binary_expr_unwind(self.cur_nodes)
|
|
1967
1980
|
|
|
1981
|
+
def await_expr(self, _: None) -> uni.Expr:
|
|
1982
|
+
"""Grammar rule.
|
|
1983
|
+
|
|
1984
|
+
await_expr: KW_AWAIT? pipe_call
|
|
1985
|
+
"""
|
|
1986
|
+
if self.match_token(Tok.KW_AWAIT):
|
|
1987
|
+
operand = self.consume(uni.Expr)
|
|
1988
|
+
return uni.AwaitExpr(
|
|
1989
|
+
target=operand,
|
|
1990
|
+
kid=self.cur_nodes,
|
|
1991
|
+
)
|
|
1992
|
+
return self._binary_expr_unwind(self.cur_nodes)
|
|
1993
|
+
|
|
1968
1994
|
def pipe_call(self, _: None) -> uni.Expr:
|
|
1969
1995
|
"""Grammar rule.
|
|
1970
1996
|
|
|
1971
|
-
pipe_call: (PIPE_FWD | A_PIPE_FWD | KW_SPAWN
|
|
1997
|
+
pipe_call: (PIPE_FWD | A_PIPE_FWD | KW_SPAWN)? atomic_chain
|
|
1972
1998
|
"""
|
|
1973
|
-
if len(self.cur_nodes) == 2:
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
elif op := self.match(uni.Token):
|
|
1981
|
-
operand = self.consume(uni.Expr)
|
|
1982
|
-
return uni.UnaryExpr(
|
|
1983
|
-
op=op,
|
|
1984
|
-
operand=operand,
|
|
1985
|
-
kid=self.cur_nodes,
|
|
1986
|
-
)
|
|
1999
|
+
if len(self.cur_nodes) == 2 and (op := self.match(uni.Token)):
|
|
2000
|
+
operand = self.consume(uni.Expr)
|
|
2001
|
+
return uni.UnaryExpr(
|
|
2002
|
+
op=op,
|
|
2003
|
+
operand=operand,
|
|
2004
|
+
kid=self.cur_nodes,
|
|
2005
|
+
)
|
|
1987
2006
|
return self._binary_expr_unwind(self.cur_nodes)
|
|
1988
2007
|
|
|
1989
2008
|
def aug_op(self, _: None) -> uni.Token:
|
|
@@ -1991,7 +2010,6 @@ class JacParser(Transform[uni.Source, uni.Module]):
|
|
|
1991
2010
|
|
|
1992
2011
|
aug_op: RSHIFT_EQ
|
|
1993
2012
|
| LSHIFT_EQ
|
|
1994
|
-
| BW_NOT_EQ
|
|
1995
2013
|
| BW_XOR_EQ
|
|
1996
2014
|
| BW_OR_EQ
|
|
1997
2015
|
| BW_AND_EQ
|
|
@@ -2045,6 +2063,14 @@ class JacParser(Transform[uni.Source, uni.Module]):
|
|
|
2045
2063
|
atomic_call: atomic_chain LPAREN param_list? by_llm? RPAREN
|
|
2046
2064
|
"""
|
|
2047
2065
|
target = self.consume(uni.Expr)
|
|
2066
|
+
gencompr = self.match(uni.GenCompr)
|
|
2067
|
+
if gencompr:
|
|
2068
|
+
return uni.FuncCall(
|
|
2069
|
+
target=target,
|
|
2070
|
+
params=[gencompr],
|
|
2071
|
+
genai_call=None,
|
|
2072
|
+
kid=self.cur_nodes,
|
|
2073
|
+
)
|
|
2048
2074
|
self.consume_token(Tok.LPAREN)
|
|
2049
2075
|
params_sn = self.match(list)
|
|
2050
2076
|
genai_call = self.match(uni.Expr)
|
|
@@ -2188,10 +2214,22 @@ class JacParser(Transform[uni.Source, uni.Module]):
|
|
|
2188
2214
|
|
|
2189
2215
|
fstring: FSTR_START fstr_parts FSTR_END
|
|
2190
2216
|
| FSTR_SQ_START fstr_sq_parts FSTR_SQ_END
|
|
2217
|
+
| FSTR_TRIPLE_START fstr_triple_parts FSTR_TRIPLE_END
|
|
2218
|
+
| FSTR_SQ_TRIPLE_START fstr_sq_triple_parts FSTR_SQ_TRIPLE_END
|
|
2191
2219
|
"""
|
|
2192
|
-
|
|
2220
|
+
(
|
|
2221
|
+
self.match_token(Tok.FSTR_TRIPLE_START)
|
|
2222
|
+
or self.match_token(Tok.FSTR_SQ_TRIPLE_START)
|
|
2223
|
+
or self.match_token(Tok.FSTR_START)
|
|
2224
|
+
or self.consume_token(Tok.FSTR_SQ_START)
|
|
2225
|
+
)
|
|
2193
2226
|
target = self.match(list)
|
|
2194
|
-
|
|
2227
|
+
(
|
|
2228
|
+
self.match_token(Tok.FSTR_TRIPLE_END)
|
|
2229
|
+
or self.match_token(Tok.FSTR_SQ_TRIPLE_END)
|
|
2230
|
+
or self.match_token(Tok.FSTR_END)
|
|
2231
|
+
or self.consume_token(Tok.FSTR_SQ_END)
|
|
2232
|
+
)
|
|
2195
2233
|
return uni.FString(
|
|
2196
2234
|
parts=(
|
|
2197
2235
|
self.extract_from_list(target, (uni.String, uni.ExprStmt))
|
|
@@ -2239,6 +2277,44 @@ class JacParser(Transform[uni.Source, uni.Module]):
|
|
|
2239
2277
|
]
|
|
2240
2278
|
return valid_parts
|
|
2241
2279
|
|
|
2280
|
+
def fstr_triple_parts(self, _: None) -> list[uni.UniNode]:
|
|
2281
|
+
"""Grammar rule.
|
|
2282
|
+
|
|
2283
|
+
fstr_triple_parts: (FSTR_TRIPLE_PIECE | FSTR_BESC | LBRACE expression RBRACE )*
|
|
2284
|
+
"""
|
|
2285
|
+
valid_parts: list[uni.UniNode] = [
|
|
2286
|
+
(
|
|
2287
|
+
i
|
|
2288
|
+
if isinstance(i, uni.String)
|
|
2289
|
+
else (
|
|
2290
|
+
uni.ExprStmt(expr=i, in_fstring=True, kid=[i])
|
|
2291
|
+
if isinstance(i, uni.Expr)
|
|
2292
|
+
else i
|
|
2293
|
+
)
|
|
2294
|
+
)
|
|
2295
|
+
for i in self.cur_nodes
|
|
2296
|
+
]
|
|
2297
|
+
return valid_parts
|
|
2298
|
+
|
|
2299
|
+
def fstr_sq_triple_parts(self, _: None) -> list[uni.UniNode]:
|
|
2300
|
+
"""Grammar rule.
|
|
2301
|
+
|
|
2302
|
+
fstr_sq_triple_parts: (FSTR_SQ_TRIPLE_PIECE | FSTR_BESC | LBRACE expression RBRACE )*
|
|
2303
|
+
"""
|
|
2304
|
+
valid_parts: list[uni.UniNode] = [
|
|
2305
|
+
(
|
|
2306
|
+
i
|
|
2307
|
+
if isinstance(i, uni.String)
|
|
2308
|
+
else (
|
|
2309
|
+
uni.ExprStmt(expr=i, in_fstring=True, kid=[i])
|
|
2310
|
+
if isinstance(i, uni.Expr)
|
|
2311
|
+
else i
|
|
2312
|
+
)
|
|
2313
|
+
)
|
|
2314
|
+
for i in self.cur_nodes
|
|
2315
|
+
]
|
|
2316
|
+
return valid_parts
|
|
2317
|
+
|
|
2242
2318
|
def list_val(self, _: None) -> uni.ListVal:
|
|
2243
2319
|
"""Grammar rule.
|
|
2244
2320
|
|
|
@@ -2468,7 +2544,7 @@ class JacParser(Transform[uni.Source, uni.Module]):
|
|
|
2468
2544
|
def assignment_list(self, _: None) -> list[uni.UniNode]:
|
|
2469
2545
|
"""Grammar rule.
|
|
2470
2546
|
|
|
2471
|
-
assignment_list: (
|
|
2547
|
+
assignment_list: (assignment | named_ref) (COMMA (assignment | named_ref))* COMMA?
|
|
2472
2548
|
"""
|
|
2473
2549
|
|
|
2474
2550
|
def name_to_assign(name_consume: uni.NameAtom) -> uni.Assignment:
|
|
@@ -2476,15 +2552,23 @@ class JacParser(Transform[uni.Source, uni.Module]):
|
|
|
2476
2552
|
target=[name_consume], value=None, type_tag=None, kid=[name_consume]
|
|
2477
2553
|
)
|
|
2478
2554
|
|
|
2479
|
-
|
|
2480
|
-
self.consume_token(Tok.COMMA)
|
|
2555
|
+
# Match first (assignment | named_ref)
|
|
2481
2556
|
if self.match(uni.Assignment):
|
|
2482
2557
|
pass
|
|
2483
2558
|
elif name_consume := self.match(uni.NameAtom):
|
|
2484
2559
|
self.cur_nodes[self.node_idx - 1] = name_to_assign(name_consume)
|
|
2485
2560
|
else:
|
|
2486
|
-
|
|
2487
|
-
|
|
2561
|
+
raise self.ice()
|
|
2562
|
+
|
|
2563
|
+
# Match (COMMA (assignment | named_ref))* COMMA?
|
|
2564
|
+
while self.match_token(Tok.COMMA):
|
|
2565
|
+
if self.match(uni.Assignment):
|
|
2566
|
+
pass
|
|
2567
|
+
elif name_consume := self.match(uni.NameAtom):
|
|
2568
|
+
self.cur_nodes[self.node_idx - 1] = name_to_assign(name_consume)
|
|
2569
|
+
else:
|
|
2570
|
+
break # trailing comma
|
|
2571
|
+
|
|
2488
2572
|
return self.flat_cur_nodes
|
|
2489
2573
|
|
|
2490
2574
|
def type_ref(self, kid: list[uni.UniNode]) -> uni.TypeRef:
|
|
@@ -2946,6 +3030,36 @@ class JacParser(Transform[uni.Source, uni.Module]):
|
|
|
2946
3030
|
value = self.consume(uni.MatchPattern)
|
|
2947
3031
|
return uni.MatchKVPair(key=pattern, value=value, kid=self.cur_nodes)
|
|
2948
3032
|
|
|
3033
|
+
def attr_pattern(self, _: None) -> uni.MatchValue:
|
|
3034
|
+
"""Grammar rule.
|
|
3035
|
+
|
|
3036
|
+
attr_pattern: NAME (DOT NAME)+
|
|
3037
|
+
"""
|
|
3038
|
+
name_idx = 0
|
|
3039
|
+
cur_element = self.consume(uni.NameAtom)
|
|
3040
|
+
name_idx += 1
|
|
3041
|
+
trailer: uni.AtomTrailer | None = None
|
|
3042
|
+
while dot := self.match_token(Tok.DOT):
|
|
3043
|
+
target = trailer if trailer else cur_element
|
|
3044
|
+
right = self.consume(uni.NameAtom)
|
|
3045
|
+
name_idx += 2
|
|
3046
|
+
trailer = uni.AtomTrailer(
|
|
3047
|
+
target=target,
|
|
3048
|
+
right=right,
|
|
3049
|
+
is_attr=True,
|
|
3050
|
+
is_null_ok=False,
|
|
3051
|
+
kid=[target, dot, right],
|
|
3052
|
+
)
|
|
3053
|
+
name = trailer if trailer else cur_element
|
|
3054
|
+
if not isinstance(name, (uni.NameAtom, uni.AtomTrailer)):
|
|
3055
|
+
raise TypeError(
|
|
3056
|
+
f"Expected name to be either NameAtom or AtomTrailer, got {type(name)}"
|
|
3057
|
+
)
|
|
3058
|
+
return uni.MatchValue(
|
|
3059
|
+
value=name,
|
|
3060
|
+
kid=[name, *self.flat_cur_nodes[name_idx:]],
|
|
3061
|
+
)
|
|
3062
|
+
|
|
2949
3063
|
def class_pattern(self, _: None) -> uni.MatchArch:
|
|
2950
3064
|
"""Grammar rule.
|
|
2951
3065
|
|
|
@@ -3052,6 +3166,8 @@ class JacParser(Transform[uni.Source, uni.Module]):
|
|
|
3052
3166
|
Tok.FSTR_BESC,
|
|
3053
3167
|
Tok.FSTR_PIECE,
|
|
3054
3168
|
Tok.FSTR_SQ_PIECE,
|
|
3169
|
+
Tok.FSTR_TRIPLE_PIECE,
|
|
3170
|
+
Tok.FSTR_SQ_TRIPLE_PIECE,
|
|
3055
3171
|
]:
|
|
3056
3172
|
ret_type = uni.String
|
|
3057
3173
|
if token.type == Tok.FSTR_BESC:
|
|
@@ -64,7 +64,6 @@ TOKEN_AST_MAP: dict[Tok, type[ast3.AST]] = {
|
|
|
64
64
|
Tok.MINUS: ast3.Sub,
|
|
65
65
|
Tok.SUB_EQ: ast3.Sub,
|
|
66
66
|
Tok.BW_NOT: ast3.Invert,
|
|
67
|
-
Tok.BW_NOT_EQ: ast3.Invert,
|
|
68
67
|
Tok.NOT: ast3.Not,
|
|
69
68
|
Tok.EQ: ast3.NotEq,
|
|
70
69
|
Tok.EE: ast3.Eq,
|
|
@@ -92,11 +91,34 @@ UNARY_OP_MAP: dict[Tok, type[ast3.unaryop]] = {
|
|
|
92
91
|
class PyastGenPass(UniPass):
|
|
93
92
|
"""Jac blue transpilation to python pass."""
|
|
94
93
|
|
|
94
|
+
# Builtins that should be imported from jaclang.runtimelib.builtin
|
|
95
|
+
KNOWN_BUILTINS = {
|
|
96
|
+
"abstractmethod",
|
|
97
|
+
"ClassVar",
|
|
98
|
+
"override",
|
|
99
|
+
"printgraph",
|
|
100
|
+
"jid",
|
|
101
|
+
"jobj",
|
|
102
|
+
"grant",
|
|
103
|
+
"revoke",
|
|
104
|
+
"allroots",
|
|
105
|
+
"save",
|
|
106
|
+
"commit",
|
|
107
|
+
"NoPerm",
|
|
108
|
+
"ReadPerm",
|
|
109
|
+
"ConnectPerm",
|
|
110
|
+
"WritePerm",
|
|
111
|
+
}
|
|
112
|
+
|
|
95
113
|
def before_pass(self) -> None:
|
|
114
|
+
self.child_passes: list[PyastGenPass] = []
|
|
96
115
|
for i in self.ir_in.impl_mod + self.ir_in.test_mod:
|
|
97
|
-
PyastGenPass(ir_in=i, prog=self.prog)
|
|
116
|
+
child_pass = PyastGenPass(ir_in=i, prog=self.prog)
|
|
117
|
+
self.child_passes.append(child_pass)
|
|
98
118
|
self.debuginfo: dict[str, list[str]] = {"jac_mods": []}
|
|
99
119
|
self.already_added: list[str] = []
|
|
120
|
+
self.jaclib_imports: set[str] = set() # Track individual jaclib imports
|
|
121
|
+
self.builtin_imports: set[str] = set() # Track individual builtin imports
|
|
100
122
|
self.preamble: list[ast3.AST] = [
|
|
101
123
|
self.sync(
|
|
102
124
|
ast3.ImportFrom(
|
|
@@ -106,40 +128,6 @@ class PyastGenPass(UniPass):
|
|
|
106
128
|
),
|
|
107
129
|
jac_node=self.ir_out,
|
|
108
130
|
),
|
|
109
|
-
(
|
|
110
|
-
self.sync(
|
|
111
|
-
ast3.ImportFrom(
|
|
112
|
-
module="jaclang.runtimelib.builtin",
|
|
113
|
-
names=[
|
|
114
|
-
self.sync(
|
|
115
|
-
ast3.alias(
|
|
116
|
-
name="*",
|
|
117
|
-
asname=None,
|
|
118
|
-
)
|
|
119
|
-
)
|
|
120
|
-
],
|
|
121
|
-
level=0,
|
|
122
|
-
),
|
|
123
|
-
jac_node=self.ir_out,
|
|
124
|
-
)
|
|
125
|
-
),
|
|
126
|
-
(
|
|
127
|
-
self.sync(
|
|
128
|
-
ast3.ImportFrom(
|
|
129
|
-
module="jaclang",
|
|
130
|
-
names=[
|
|
131
|
-
self.sync(
|
|
132
|
-
ast3.alias(
|
|
133
|
-
name="JacMachineInterface",
|
|
134
|
-
asname=settings.pyout_jaclib_alias,
|
|
135
|
-
)
|
|
136
|
-
),
|
|
137
|
-
],
|
|
138
|
-
level=0,
|
|
139
|
-
),
|
|
140
|
-
jac_node=self.ir_out,
|
|
141
|
-
)
|
|
142
|
-
),
|
|
143
131
|
]
|
|
144
132
|
|
|
145
133
|
def enter_node(self, node: uni.UniNode) -> None:
|
|
@@ -161,15 +149,31 @@ class PyastGenPass(UniPass):
|
|
|
161
149
|
|
|
162
150
|
def jaclib_obj(self, obj_name: str) -> ast3.Name | ast3.Attribute:
|
|
163
151
|
"""Return the object from jaclib as ast node based on the import config."""
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
152
|
+
if settings.library_mode:
|
|
153
|
+
self.jaclib_imports.add(obj_name)
|
|
154
|
+
return self.sync(ast3.Name(id=obj_name, ctx=ast3.Load()))
|
|
155
|
+
else:
|
|
156
|
+
self.needs_jaclib()
|
|
157
|
+
return self.sync(
|
|
158
|
+
ast3.Attribute(
|
|
159
|
+
value=self.sync(
|
|
160
|
+
ast3.Name(id=settings.pyout_jaclib_alias, ctx=ast3.Load())
|
|
161
|
+
),
|
|
162
|
+
attr=obj_name,
|
|
163
|
+
ctx=ast3.Load(),
|
|
164
|
+
)
|
|
171
165
|
)
|
|
172
|
-
|
|
166
|
+
|
|
167
|
+
def builtin_name(self, name: str) -> ast3.Name:
|
|
168
|
+
"""Return a builtin name and track it for importing.
|
|
169
|
+
|
|
170
|
+
Note: Some names like 'Enum' are provided by other imports (e.g., needs_enum)
|
|
171
|
+
and should not be added to builtin_imports.
|
|
172
|
+
"""
|
|
173
|
+
# Enum is imported via needs_enum, not from builtins
|
|
174
|
+
if name not in ["Enum"]:
|
|
175
|
+
self.builtin_imports.add(name)
|
|
176
|
+
return self.sync(ast3.Name(id=name, ctx=ast3.Load()))
|
|
173
177
|
|
|
174
178
|
def _add_preamble_once(self, key: str, node: ast3.AST) -> None:
|
|
175
179
|
"""Append an import statement to the preamble once."""
|
|
@@ -187,15 +191,6 @@ class PyastGenPass(UniPass):
|
|
|
187
191
|
),
|
|
188
192
|
)
|
|
189
193
|
|
|
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
|
-
|
|
199
194
|
def needs_enum(self) -> None:
|
|
200
195
|
"""Ensure Enum utilities are imported only once."""
|
|
201
196
|
self._add_preamble_once(
|
|
@@ -221,6 +216,24 @@ class PyastGenPass(UniPass):
|
|
|
221
216
|
),
|
|
222
217
|
)
|
|
223
218
|
|
|
219
|
+
def needs_jaclib(self) -> None:
|
|
220
|
+
"""Ensure JacMachineInterface is imported only once."""
|
|
221
|
+
self._add_preamble_once(
|
|
222
|
+
self.needs_jaclib.__name__,
|
|
223
|
+
ast3.ImportFrom(
|
|
224
|
+
module="jaclang",
|
|
225
|
+
names=[
|
|
226
|
+
self.sync(
|
|
227
|
+
ast3.alias(
|
|
228
|
+
name="JacMachineInterface",
|
|
229
|
+
asname=settings.pyout_jaclib_alias,
|
|
230
|
+
)
|
|
231
|
+
)
|
|
232
|
+
],
|
|
233
|
+
level=0,
|
|
234
|
+
),
|
|
235
|
+
)
|
|
236
|
+
|
|
224
237
|
def _get_sem_decorator(self, node: uni.UniNode) -> ast3.Call | None:
|
|
225
238
|
"""Create a semstring decorator for the given semantic strings.
|
|
226
239
|
|
|
@@ -283,7 +296,10 @@ class PyastGenPass(UniPass):
|
|
|
283
296
|
else {}
|
|
284
297
|
)
|
|
285
298
|
|
|
286
|
-
if
|
|
299
|
+
# Only add sem decorator if there's actual semantic content
|
|
300
|
+
if not semstr and (
|
|
301
|
+
not inner_semstr or all(not v for v in inner_semstr.values())
|
|
302
|
+
):
|
|
287
303
|
return None
|
|
288
304
|
|
|
289
305
|
return self.sync(
|
|
@@ -417,6 +433,48 @@ class PyastGenPass(UniPass):
|
|
|
417
433
|
node.gen.py_ast = node.tag.gen.py_ast
|
|
418
434
|
|
|
419
435
|
def exit_module(self, node: uni.Module) -> None:
|
|
436
|
+
# Check if any child passes (impl_mod or test_mod) needed jaclib
|
|
437
|
+
for child_pass in self.child_passes:
|
|
438
|
+
if "needs_jaclib" in child_pass.already_added:
|
|
439
|
+
self.needs_jaclib()
|
|
440
|
+
break
|
|
441
|
+
# Merge jaclib and builtin imports from child passes
|
|
442
|
+
if settings.library_mode:
|
|
443
|
+
self.jaclib_imports.update(child_pass.jaclib_imports)
|
|
444
|
+
self.builtin_imports.update(child_pass.builtin_imports)
|
|
445
|
+
|
|
446
|
+
# Add builtin imports if any were used
|
|
447
|
+
if self.builtin_imports:
|
|
448
|
+
self.preamble.append(
|
|
449
|
+
self.sync(
|
|
450
|
+
ast3.ImportFrom(
|
|
451
|
+
module="jaclang.runtimelib.builtin",
|
|
452
|
+
names=[
|
|
453
|
+
self.sync(ast3.alias(name=name, asname=None))
|
|
454
|
+
for name in sorted(self.builtin_imports)
|
|
455
|
+
],
|
|
456
|
+
level=0,
|
|
457
|
+
),
|
|
458
|
+
jac_node=self.ir_out,
|
|
459
|
+
)
|
|
460
|
+
)
|
|
461
|
+
|
|
462
|
+
# Add library mode imports at the end of preamble
|
|
463
|
+
if settings.library_mode and self.jaclib_imports:
|
|
464
|
+
self.preamble.append(
|
|
465
|
+
self.sync(
|
|
466
|
+
ast3.ImportFrom(
|
|
467
|
+
module="jaclang.lib",
|
|
468
|
+
names=[
|
|
469
|
+
self.sync(ast3.alias(name=name, asname=None))
|
|
470
|
+
for name in sorted(self.jaclib_imports)
|
|
471
|
+
],
|
|
472
|
+
level=0,
|
|
473
|
+
),
|
|
474
|
+
jac_node=self.ir_out,
|
|
475
|
+
)
|
|
476
|
+
)
|
|
477
|
+
|
|
420
478
|
clean_body = [i for i in node.body if not isinstance(i, uni.ImplDef)]
|
|
421
479
|
pre_body: list[uni.UniNode] = []
|
|
422
480
|
for pbody in node.impl_mod:
|
|
@@ -722,7 +780,7 @@ class PyastGenPass(UniPass):
|
|
|
722
780
|
decorators.append(sem_decorator)
|
|
723
781
|
|
|
724
782
|
base_classes = [cast(ast3.expr, i.gen.py_ast[0]) for i in node.base_classes]
|
|
725
|
-
base_classes.append(self.
|
|
783
|
+
base_classes.append(self.builtin_name("Enum"))
|
|
726
784
|
node.gen.py_ast = [
|
|
727
785
|
self.sync(
|
|
728
786
|
ast3.ClassDef(
|
|
@@ -744,23 +802,9 @@ class PyastGenPass(UniPass):
|
|
|
744
802
|
self, model: ast3.expr, caller: ast3.expr, args: ast3.Dict
|
|
745
803
|
) -> ast3.Call:
|
|
746
804
|
"""Reusable method to codegen call_llm(model, caller, args)."""
|
|
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
805
|
mtir_ast = self.sync(
|
|
756
806
|
ast3.Call(
|
|
757
|
-
func=self.
|
|
758
|
-
ast3.Attribute(
|
|
759
|
-
value=mtir_cls_ast,
|
|
760
|
-
attr="factory",
|
|
761
|
-
ctx=ast3.Load(),
|
|
762
|
-
)
|
|
763
|
-
),
|
|
807
|
+
func=self.jaclib_obj("get_mtir"),
|
|
764
808
|
args=[],
|
|
765
809
|
keywords=[
|
|
766
810
|
self.sync(
|
|
@@ -928,7 +972,9 @@ class PyastGenPass(UniPass):
|
|
|
928
972
|
if isinstance(node.signature, uni.EventSignature):
|
|
929
973
|
decorator_list.append(
|
|
930
974
|
self.jaclib_obj(
|
|
931
|
-
"
|
|
975
|
+
"on_entry"
|
|
976
|
+
if node.signature.event.name == Tok.KW_ENTRY
|
|
977
|
+
else "on_exit"
|
|
932
978
|
)
|
|
933
979
|
)
|
|
934
980
|
|
|
@@ -943,11 +989,9 @@ class PyastGenPass(UniPass):
|
|
|
943
989
|
)
|
|
944
990
|
)
|
|
945
991
|
if node.is_abstract:
|
|
946
|
-
decorator_list.append(
|
|
947
|
-
self.sync(ast3.Name(id="abstractmethod", ctx=ast3.Load()))
|
|
948
|
-
)
|
|
992
|
+
decorator_list.append(self.builtin_name("abstractmethod"))
|
|
949
993
|
if node.is_override:
|
|
950
|
-
decorator_list.append(self.
|
|
994
|
+
decorator_list.append(self.builtin_name("override"))
|
|
951
995
|
if node.is_static:
|
|
952
996
|
decorator_list.insert(
|
|
953
997
|
0, self.sync(ast3.Name(id="staticmethod", ctx=ast3.Load()))
|
|
@@ -1145,7 +1189,7 @@ class PyastGenPass(UniPass):
|
|
|
1145
1189
|
elif is_static_var:
|
|
1146
1190
|
annotation = self.sync(
|
|
1147
1191
|
ast3.Subscript(
|
|
1148
|
-
value=self.
|
|
1192
|
+
value=self.builtin_name("ClassVar"),
|
|
1149
1193
|
slice=cast(ast3.expr, annotation),
|
|
1150
1194
|
ctx=ast3.Load(),
|
|
1151
1195
|
)
|
|
@@ -1222,8 +1266,29 @@ class PyastGenPass(UniPass):
|
|
|
1222
1266
|
]
|
|
1223
1267
|
|
|
1224
1268
|
def exit_typed_ctx_block(self, node: uni.TypedCtxBlock) -> None:
|
|
1225
|
-
|
|
1226
|
-
|
|
1269
|
+
loc = self.sync(
|
|
1270
|
+
ast3.Name(id=Con.HERE.value, ctx=ast3.Load())
|
|
1271
|
+
if node.from_walker
|
|
1272
|
+
else ast3.Name(id=Con.VISITOR.value, ctx=ast3.Load())
|
|
1273
|
+
)
|
|
1274
|
+
node.gen.py_ast = [
|
|
1275
|
+
self.sync(
|
|
1276
|
+
ast3.If(
|
|
1277
|
+
test=self.sync(
|
|
1278
|
+
ast3.Call(
|
|
1279
|
+
func=self.sync(ast3.Name(id="isinstance", ctx=ast3.Load())),
|
|
1280
|
+
args=[
|
|
1281
|
+
loc,
|
|
1282
|
+
cast(ast3.expr, node.type_ctx.gen.py_ast[0]),
|
|
1283
|
+
],
|
|
1284
|
+
keywords=[],
|
|
1285
|
+
)
|
|
1286
|
+
),
|
|
1287
|
+
body=cast(list[ast3.stmt], self.resolve_stmt_block(node.body)),
|
|
1288
|
+
orelse=[],
|
|
1289
|
+
)
|
|
1290
|
+
)
|
|
1291
|
+
]
|
|
1227
1292
|
|
|
1228
1293
|
def exit_if_stmt(self, node: uni.IfStmt) -> None:
|
|
1229
1294
|
node.gen.py_ast = [
|
|
@@ -1999,7 +2064,7 @@ class PyastGenPass(UniPass):
|
|
|
1999
2064
|
keywords.append(
|
|
2000
2065
|
self.sync(
|
|
2001
2066
|
ast3.keyword(
|
|
2002
|
-
arg="
|
|
2067
|
+
arg="filter_on",
|
|
2003
2068
|
value=cast(
|
|
2004
2069
|
ast3.expr,
|
|
2005
2070
|
node.op.edge_spec.filter_cond.gen.py_ast[0],
|
|
@@ -2206,7 +2271,7 @@ class PyastGenPass(UniPass):
|
|
|
2206
2271
|
node.gen.py_ast = [
|
|
2207
2272
|
self.sync(
|
|
2208
2273
|
ast3.Call(
|
|
2209
|
-
func=self.
|
|
2274
|
+
func=self.builtin_name("jobj"),
|
|
2210
2275
|
args=[],
|
|
2211
2276
|
keywords=[
|
|
2212
2277
|
self.sync(
|
|
@@ -2431,7 +2496,7 @@ class PyastGenPass(UniPass):
|
|
|
2431
2496
|
node.gen.py_ast = [
|
|
2432
2497
|
self.sync(
|
|
2433
2498
|
ast3.Call(
|
|
2434
|
-
func=self.jaclib_obj("
|
|
2499
|
+
func=self.jaclib_obj("filter_on"),
|
|
2435
2500
|
args=[],
|
|
2436
2501
|
keywords=[
|
|
2437
2502
|
self.sync(
|
|
@@ -2454,7 +2519,7 @@ class PyastGenPass(UniPass):
|
|
|
2454
2519
|
node.gen.py_ast = [
|
|
2455
2520
|
self.sync(
|
|
2456
2521
|
ast3.Call(
|
|
2457
|
-
func=self.jaclib_obj("
|
|
2522
|
+
func=self.jaclib_obj("assign_all"),
|
|
2458
2523
|
args=cast(
|
|
2459
2524
|
list[ast3.expr],
|
|
2460
2525
|
[node.target.gen.py_ast[0], node.right.gen.py_ast[0]],
|
|
@@ -2685,7 +2750,7 @@ class PyastGenPass(UniPass):
|
|
|
2685
2750
|
func=self.sync(
|
|
2686
2751
|
ast3.Attribute(
|
|
2687
2752
|
value=pynode,
|
|
2688
|
-
attr=f"
|
|
2753
|
+
attr=f"edge_{cur.edge_dir.name.lower()}",
|
|
2689
2754
|
ctx=ast3.Load(),
|
|
2690
2755
|
)
|
|
2691
2756
|
),
|
|
@@ -3026,6 +3091,9 @@ class PyastGenPass(UniPass):
|
|
|
3026
3091
|
|
|
3027
3092
|
def exit_name(self, node: uni.Name) -> None:
|
|
3028
3093
|
name = node.sym_name
|
|
3094
|
+
# Track if this name is a known builtin
|
|
3095
|
+
if name in self.KNOWN_BUILTINS:
|
|
3096
|
+
self.builtin_imports.add(name)
|
|
3029
3097
|
node.gen.py_ast = [self.sync(ast3.Name(id=name, ctx=node.py_ctx_func()))]
|
|
3030
3098
|
|
|
3031
3099
|
def exit_float(self, node: uni.Float) -> None:
|
|
@@ -96,6 +96,8 @@ class PyastBuildPass(Transform[uni.PythonModuleAst, uni.Module]):
|
|
|
96
96
|
body: list[stmt]
|
|
97
97
|
type_ignores: list[TypeIgnore]
|
|
98
98
|
"""
|
|
99
|
+
if not node.body:
|
|
100
|
+
return uni.Module.make_stub(inject_src=self.ir_in)
|
|
99
101
|
elements: list[uni.UniNode] = [self.convert(i) for i in node.body]
|
|
100
102
|
elements[0] = (
|
|
101
103
|
elements[0].expr
|
|
@@ -175,7 +175,6 @@ class TypeCheckerPassTests(TestCase):
|
|
|
175
175
|
""", program.errors_had[0].pretty_print())
|
|
176
176
|
|
|
177
177
|
def test_param_arg_match(self) -> None:
|
|
178
|
-
path = self.fixture_abs_path("checker_param_types.jac")
|
|
179
178
|
program = JacProgram()
|
|
180
179
|
path = self.fixture_abs_path("checker_arg_param_match.jac")
|
|
181
180
|
mod = program.compile(path)
|