jaclang 0.8.7__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.

Files changed (99) hide show
  1. jaclang/cli/cli.py +77 -29
  2. jaclang/cli/cmdreg.py +44 -0
  3. jaclang/compiler/constant.py +6 -2
  4. jaclang/compiler/jac.lark +37 -47
  5. jaclang/compiler/larkparse/jac_parser.py +2 -2
  6. jaclang/compiler/parser.py +356 -61
  7. jaclang/compiler/passes/main/__init__.py +2 -4
  8. jaclang/compiler/passes/main/def_use_pass.py +1 -4
  9. jaclang/compiler/passes/main/predynamo_pass.py +221 -0
  10. jaclang/compiler/passes/main/pyast_gen_pass.py +221 -135
  11. jaclang/compiler/passes/main/pyast_load_pass.py +54 -20
  12. jaclang/compiler/passes/main/sym_tab_build_pass.py +1 -1
  13. jaclang/compiler/passes/main/tests/fixtures/checker/import_sym.jac +2 -0
  14. jaclang/compiler/passes/main/tests/fixtures/checker/import_sym_test.jac +6 -0
  15. jaclang/compiler/passes/main/tests/fixtures/checker/imported_sym.jac +5 -0
  16. jaclang/compiler/passes/main/tests/fixtures/checker_arg_param_match.jac +37 -0
  17. jaclang/compiler/passes/main/tests/fixtures/checker_arity.jac +18 -0
  18. jaclang/compiler/passes/main/tests/fixtures/checker_cat_is_animal.jac +18 -0
  19. jaclang/compiler/passes/main/tests/fixtures/checker_float.jac +7 -0
  20. jaclang/compiler/passes/main/tests/fixtures/checker_param_types.jac +11 -0
  21. jaclang/compiler/passes/main/tests/fixtures/checker_self_type.jac +9 -0
  22. jaclang/compiler/passes/main/tests/fixtures/checker_sym_inherit.jac +42 -0
  23. jaclang/compiler/passes/main/tests/fixtures/predynamo_fix3.jac +43 -0
  24. jaclang/compiler/passes/main/tests/fixtures/predynamo_where_assign.jac +13 -0
  25. jaclang/compiler/passes/main/tests/fixtures/predynamo_where_return.jac +11 -0
  26. jaclang/compiler/passes/main/tests/test_checker_pass.py +190 -0
  27. jaclang/compiler/passes/main/tests/test_predynamo_pass.py +56 -0
  28. jaclang/compiler/passes/main/type_checker_pass.py +29 -73
  29. jaclang/compiler/passes/tool/doc_ir_gen_pass.py +302 -58
  30. jaclang/compiler/passes/tool/jac_formatter_pass.py +119 -69
  31. jaclang/compiler/passes/tool/tests/fixtures/corelib_fmt.jac +3 -3
  32. jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/triple_quoted_string.jac +4 -5
  33. jaclang/compiler/passes/tool/tests/fixtures/import_fmt.jac +7 -1
  34. jaclang/compiler/passes/tool/tests/fixtures/tagbreak.jac +276 -10
  35. jaclang/compiler/passes/transform.py +12 -8
  36. jaclang/compiler/program.py +19 -7
  37. jaclang/compiler/tests/fixtures/jac_import_py_files.py +4 -0
  38. jaclang/compiler/tests/fixtures/jac_module.jac +3 -0
  39. jaclang/compiler/tests/fixtures/multiple_syntax_errors.jac +10 -0
  40. jaclang/compiler/tests/fixtures/python_module.py +1 -0
  41. jaclang/compiler/tests/test_importer.py +39 -0
  42. jaclang/compiler/tests/test_parser.py +49 -0
  43. jaclang/compiler/type_system/type_evaluator.jac +959 -0
  44. jaclang/compiler/type_system/type_utils.py +246 -0
  45. jaclang/compiler/type_system/types.py +58 -2
  46. jaclang/compiler/unitree.py +102 -107
  47. jaclang/langserve/engine.jac +138 -159
  48. jaclang/langserve/server.jac +25 -1
  49. jaclang/langserve/tests/fixtures/circle.jac +3 -3
  50. jaclang/langserve/tests/fixtures/circle_err.jac +3 -3
  51. jaclang/langserve/tests/fixtures/circle_pure.test.jac +3 -3
  52. jaclang/langserve/tests/fixtures/completion_test_err.jac +10 -0
  53. jaclang/langserve/tests/server_test/circle_template.jac +80 -0
  54. jaclang/langserve/tests/server_test/glob_template.jac +4 -0
  55. jaclang/langserve/tests/server_test/test_lang_serve.py +154 -309
  56. jaclang/langserve/tests/server_test/utils.py +153 -116
  57. jaclang/langserve/tests/test_server.py +21 -84
  58. jaclang/langserve/utils.jac +12 -15
  59. jaclang/lib.py +17 -0
  60. jaclang/runtimelib/archetype.py +25 -25
  61. jaclang/runtimelib/constructs.py +2 -2
  62. jaclang/runtimelib/machine.py +63 -46
  63. jaclang/runtimelib/meta_importer.py +27 -1
  64. jaclang/runtimelib/tests/fixtures/custom_access_validation.jac +1 -1
  65. jaclang/runtimelib/tests/fixtures/savable_object.jac +2 -2
  66. jaclang/settings.py +19 -16
  67. jaclang/tests/fixtures/abc_check.jac +3 -3
  68. jaclang/tests/fixtures/arch_rel_import_creation.jac +12 -12
  69. jaclang/tests/fixtures/attr_pattern_case.jac +18 -0
  70. jaclang/tests/fixtures/chandra_bugs2.jac +3 -3
  71. jaclang/tests/fixtures/create_dynamic_archetype.jac +13 -13
  72. jaclang/tests/fixtures/funccall_genexpr.jac +7 -0
  73. jaclang/tests/fixtures/funccall_genexpr.py +5 -0
  74. jaclang/tests/fixtures/maxfail_run_test.jac +4 -4
  75. jaclang/tests/fixtures/params/param_syntax_err.jac +9 -0
  76. jaclang/tests/fixtures/params/test_complex_params.jac +42 -0
  77. jaclang/tests/fixtures/params/test_failing_kwonly.jac +207 -0
  78. jaclang/tests/fixtures/params/test_failing_posonly.jac +116 -0
  79. jaclang/tests/fixtures/params/test_failing_varargs.jac +300 -0
  80. jaclang/tests/fixtures/params/test_kwonly_params.jac +29 -0
  81. jaclang/tests/fixtures/py2jac_params.py +8 -0
  82. jaclang/tests/fixtures/run_test.jac +4 -4
  83. jaclang/tests/test_cli.py +159 -7
  84. jaclang/tests/test_language.py +213 -38
  85. jaclang/tests/test_reference.py +3 -1
  86. jaclang/utils/helpers.py +67 -6
  87. jaclang/utils/module_resolver.py +10 -0
  88. jaclang/utils/test.py +8 -0
  89. jaclang/utils/tests/test_lang_tools.py +4 -15
  90. jaclang/utils/treeprinter.py +0 -18
  91. {jaclang-0.8.7.dist-info → jaclang-0.8.9.dist-info}/METADATA +1 -2
  92. {jaclang-0.8.7.dist-info → jaclang-0.8.9.dist-info}/RECORD +95 -65
  93. {jaclang-0.8.7.dist-info → jaclang-0.8.9.dist-info}/WHEEL +1 -1
  94. jaclang/compiler/passes/main/inheritance_pass.py +0 -131
  95. jaclang/compiler/type_system/type_evaluator.py +0 -560
  96. jaclang/langserve/dev_engine.jac +0 -645
  97. jaclang/langserve/dev_server.jac +0 -201
  98. /jaclang/{langserve/tests/server_test/code_test.py → tests/fixtures/py2jac_empty.py} +0 -0
  99. {jaclang-0.8.7.dist-info → jaclang-0.8.9.dist-info}/entry_points.txt +0 -0
@@ -5,12 +5,14 @@ from __future__ import annotations
5
5
  import keyword
6
6
  import logging
7
7
  import os
8
+ import sys
8
9
  from typing import Callable, Sequence, TYPE_CHECKING, TypeAlias, TypeVar, cast
9
10
 
10
11
  import jaclang.compiler.unitree as uni
11
- from jaclang.compiler import jac_lark as jl
12
+ from jaclang.compiler import TOKEN_MAP, jac_lark as jl
12
13
  from jaclang.compiler.constant import EdgeDir, Tokens as Tok
13
14
  from jaclang.compiler.passes.main import Transform
15
+ from jaclang.utils.helpers import ANSIColors
14
16
  from jaclang.vendor.lark import Lark, Transformer, Tree, logger
15
17
 
16
18
  if TYPE_CHECKING:
@@ -39,22 +41,17 @@ class JacParser(Transform[uni.Source, uni.Module]):
39
41
  tree, comments = JacParser.parse(ir_in.value, on_error=self.error_callback)
40
42
  mod = JacParser.TreeToAST(parser=self).transform(tree)
41
43
  ir_in.comments = [self.proc_comment(i, mod) for i in comments]
42
- if isinstance(mod, uni.Module):
43
- self.ir_out = mod
44
- return mod
45
- else:
44
+ if not isinstance(mod, uni.Module):
46
45
  raise self.ice()
46
+ if len(self.errors_had) != 0:
47
+ mod.has_syntax_errors = True
48
+ self.report_errors()
49
+ self.ir_out = mod
50
+ return mod
47
51
  except jl.UnexpectedInput as e:
48
- catch_error = uni.EmptyToken()
49
- catch_error.orig_src = ir_in
50
- catch_error.line_no = e.line
51
- catch_error.end_line = e.line
52
- catch_error.c_start = e.column
53
- catch_error.c_end = e.column + 1
54
- catch_error.pos_start = e.pos_in_stream or 0
55
- catch_error.pos_end = catch_error.pos_start + 1
52
+ catch_error = self.error_to_token(e)
53
+ error_msg = self.error_to_message(e)
56
54
 
57
- error_msg = "Syntax Error"
58
55
  if len(e.args) >= 1 and isinstance(e.args[0], str):
59
56
  error_msg += e.args[0]
60
57
  self.log_error(error_msg, node_override=catch_error)
@@ -62,7 +59,12 @@ class JacParser(Transform[uni.Source, uni.Module]):
62
59
  except Exception as e:
63
60
  raise e
64
61
 
65
- return uni.Module.make_stub(inject_src=ir_in)
62
+ # If we reach here, there was a syntax error, mark the module as such
63
+ # and report errors.
64
+ self.report_errors()
65
+ mod = uni.Module.make_stub(inject_src=ir_in)
66
+ mod.has_syntax_errors = True
67
+ return mod
66
68
 
67
69
  @staticmethod
68
70
  def proc_comment(token: jl.Token, mod: uni.UniNode) -> uni.CommentToken:
@@ -80,10 +82,114 @@ class JacParser(Transform[uni.Source, uni.Module]):
80
82
  kid=[],
81
83
  )
82
84
 
85
+ _MISSING_TOKENS = [
86
+ Tok.SEMI,
87
+ Tok.COMMA,
88
+ Tok.COLON,
89
+ Tok.RPAREN,
90
+ Tok.RBRACE,
91
+ Tok.RSQUARE,
92
+ Tok.RETURN_HINT,
93
+ ]
94
+
83
95
  def error_callback(self, e: jl.UnexpectedInput) -> bool:
84
96
  """Handle error."""
97
+ iparser = e.interactive_parser
98
+
99
+ def try_feed_missing_token(iparser: jl.InteractiveParser) -> Tok | None:
100
+ """Feed a missing token to the parser."""
101
+ # If any of the below token is missing, insert them and continue parsing.
102
+ accepts = iparser.accepts()
103
+ for tok in JacParser._MISSING_TOKENS:
104
+ if tok.name in accepts:
105
+ iparser.feed_token(jl.Token(tok.name, TOKEN_MAP[tok.name]))
106
+ return tok
107
+ return None
108
+
109
+ def feed_current_token(iparser: jl.InteractiveParser, tok: jl.Token) -> bool:
110
+ """Feed the current token to the parser."""
111
+ max_attempts = 100 # Prevent infinite loops
112
+ attempts = 0
113
+ while tok.type not in iparser.accepts():
114
+ if attempts >= max_attempts:
115
+ return False # Give up after too many attempts
116
+ if not try_feed_missing_token(iparser):
117
+ return False
118
+ attempts += 1
119
+ iparser.feed_token(tok)
120
+ return True
121
+
122
+ if isinstance(e, jl.UnexpectedToken):
123
+ # If last token is DOT and we expect a NAME, insert a NAME token
124
+ last_tok: jl.Token | None = (
125
+ e.token_history[-1]
126
+ if e.token_history and len(e.token_history) >= 1
127
+ else None
128
+ )
129
+ if (
130
+ last_tok
131
+ and last_tok.type == Tok.DOT.name
132
+ and (Tok.NAME.name in e.accepts)
133
+ ):
134
+ self.log_error("Incomplete member access", self.error_to_token(e))
135
+ iparser.feed_token(jl.Token(Tok.NAME.name, "recover_name_token"))
136
+ return feed_current_token(iparser, e.token)
137
+
138
+ # We're calling try_feed_missing_token twice here because the first missing
139
+ # will be reported as such and we don't for the consequent missing token.
140
+ if tk := try_feed_missing_token(iparser):
141
+ self.log_error(f"Missing {tk.name}", self.error_to_token(e))
142
+ return feed_current_token(iparser, e.token)
143
+
144
+ # Ignore unexpected tokens and continue parsing till we reach a known state.
145
+ self.log_error(
146
+ f"Unexpected token '{e.token.value}'", self.error_to_token(e)
147
+ )
148
+ return True
149
+
85
150
  return False
86
151
 
152
+ def error_to_message(self, e: jl.UnexpectedInput) -> str:
153
+ """Return an error message based on the exception."""
154
+ # TODO: Match more specific errors with lark's example based matching.
155
+ # Reference: https://github.com/lark-parser/lark/blob/master/examples/advanced/error_reporting_lalr.py
156
+ # e.match_examples()
157
+ if isinstance(e, jl.UnexpectedToken):
158
+ return f"Unexpected token '{e.token.value}'"
159
+ return "Syntax Error"
160
+
161
+ def error_to_token(self, e: jl.UnexpectedInput) -> uni.Token:
162
+ """Convert error to token."""
163
+ catch_error = uni.EmptyToken()
164
+ catch_error.orig_src = self.ir_in
165
+ catch_error.line_no = e.line
166
+ catch_error.end_line = e.line
167
+ catch_error.c_start = e.column
168
+ catch_error.pos_start = e.pos_in_stream or 0
169
+ if isinstance(e, jl.UnexpectedToken) and e.token:
170
+ catch_error.c_end = e.token.end_column or (e.column + 1)
171
+ catch_error.pos_end = e.token.end_pos or (catch_error.pos_start + 1)
172
+ else:
173
+ catch_error.c_end = e.column + 1
174
+ catch_error.pos_end = catch_error.pos_start + 1
175
+ return catch_error
176
+
177
+ def report_errors(self, *, colors: bool = True) -> None:
178
+ """Report errors to the user."""
179
+ # TODO: Write a better IO system.
180
+ # NOTE: Currently it writes all the errors to stderr cause LSP JsonRPC uses stdout for IPC.
181
+ if not sys.stderr.isatty():
182
+ # FIXME: If we're outputting to a file (pipe, redirection, etc) other
183
+ # than a terminal we disable colors however we should be able to force
184
+ # colors with a configuration.
185
+ colors = False
186
+ for alrt in self.errors_had:
187
+ error_label = (
188
+ "Error:" if not colors else f"{ANSIColors.RED}Error:{ANSIColors.END}"
189
+ )
190
+ print(error_label, end=" ", file=sys.stderr)
191
+ print(alrt.pretty_print(colors=colors), file=sys.stderr)
192
+
87
193
  @staticmethod
88
194
  def _comment_callback(comment: jl.Token) -> None:
89
195
  JacParser.comment_cache.append(comment)
@@ -549,11 +655,12 @@ class JacParser(Transform[uni.Source, uni.Module]):
549
655
  def sem_def(self, _: None) -> uni.SemDef:
550
656
  """Grammar rule.
551
657
 
552
- sem_def: KW_SEM dotted_name EQ multistring SEMI
658
+ sem_def: KW_SEM dotted_name (EQ | KW_IS) STRING SEMI
553
659
  """
554
660
  self.consume_token(Tok.KW_SEM)
555
661
  target = self.extract_from_list(self.consume(list), uni.NameAtom)
556
- self.consume_token(Tok.EQ)
662
+ if not self.match_token(Tok.KW_IS):
663
+ self.consume_token(Tok.EQ)
557
664
  value = self.consume(uni.String)
558
665
  self.consume_token(Tok.SEMI)
559
666
  return uni.SemDef(
@@ -848,25 +955,104 @@ class JacParser(Transform[uni.Source, uni.Module]):
848
955
  if self.match_token(Tok.RETURN_HINT):
849
956
  return_spec = self.consume(uni.Expr)
850
957
  return uni.FuncSignature(
958
+ posonly_params=[],
851
959
  params=[],
960
+ varargs=None,
961
+ kwonlyargs=[],
962
+ kwargs=None,
852
963
  return_type=return_spec,
853
964
  kid=self.flat_cur_nodes,
854
965
  )
855
966
  # Otherwise, parse the traditional parameter list form
856
967
  else:
857
968
  self.consume_token(Tok.LPAREN)
858
- params = self.match(list)
969
+ all_params = self.match(list) or []
970
+ posonly_params, params, varargs, kwonlyargs, kwargs = (
971
+ self._parse_parameter_categories(all_params)
972
+ )
859
973
  self.consume_token(Tok.RPAREN)
860
974
  if self.match_token(Tok.RETURN_HINT):
861
975
  return_spec = self.consume(uni.Expr)
862
976
  return uni.FuncSignature(
863
- params=(
864
- self.extract_from_list(params, uni.ParamVar) if params else []
865
- ),
977
+ posonly_params=posonly_params,
978
+ params=params,
979
+ varargs=varargs,
980
+ kwonlyargs=kwonlyargs,
981
+ kwargs=kwargs,
866
982
  return_type=return_spec,
867
983
  kid=self.flat_cur_nodes,
868
984
  )
869
985
 
986
+ def _parse_parameter_categories(self, all_params: list[uni.UniNode]) -> tuple[
987
+ list[uni.ParamVar],
988
+ list[uni.ParamVar],
989
+ uni.ParamVar | None,
990
+ list[uni.ParamVar],
991
+ uni.ParamVar | None,
992
+ ]:
993
+ posonly_params = []
994
+ params = []
995
+ varargs = None
996
+ kwonlyargs = []
997
+ kwargs = None
998
+
999
+ # Initial state determination
1000
+ cur_state = "positional"
1001
+ for param in all_params:
1002
+ if isinstance(param, uni.Token) and param.name == Tok.DIV:
1003
+ cur_state = "posonly"
1004
+ break
1005
+
1006
+ for cur_nd in all_params:
1007
+ cur_state = self._update_parameter_state(cur_nd, cur_state)
1008
+ if isinstance(cur_nd, uni.ParamVar):
1009
+ if cur_state == "positional":
1010
+ cur_nd.param_kind = uni.ParamKind.NORMAL
1011
+ params.append(cur_nd)
1012
+ elif cur_state == "posonly":
1013
+ cur_nd.param_kind = uni.ParamKind.POSONLY
1014
+ posonly_params.append(cur_nd)
1015
+ elif cur_state == "varargs":
1016
+ cur_nd.param_kind = uni.ParamKind.VARARG
1017
+ varargs = cur_nd
1018
+ cur_state = "keyword_only"
1019
+ elif cur_state == "keyword_only":
1020
+ cur_nd.param_kind = uni.ParamKind.KWONLY
1021
+ kwonlyargs.append(cur_nd)
1022
+ elif cur_state == "kwargs":
1023
+ cur_nd.param_kind = uni.ParamKind.KWARG
1024
+ kwargs = cur_nd
1025
+ else:
1026
+ raise self.ice()
1027
+
1028
+ return posonly_params, params, varargs, kwonlyargs, kwargs
1029
+
1030
+ def _update_parameter_state(self, cur_nd: uni.UniNode, cur_state: str) -> str:
1031
+ if isinstance(cur_nd, uni.Token):
1032
+ if cur_nd.name == Tok.DIV:
1033
+ if cur_state in ["keyword_only", "kwargs", "positional"]:
1034
+ self.parse_ref.log_error(
1035
+ "Invalid syntax in function parameters: '/' cannot appear after '*' or '**'.",
1036
+ node_override=cur_nd,
1037
+ )
1038
+ return "positional"
1039
+ elif cur_nd.name == Tok.STAR_MUL:
1040
+ if cur_state in ["keyword_only", "kwargs"]:
1041
+ self.parse_ref.log_error(
1042
+ "Invalid syntax in function parameters: '*' cannot appear after '**'.",
1043
+ node_override=cur_nd,
1044
+ )
1045
+ return "keyword_only"
1046
+ elif cur_nd.name == Tok.COMMA:
1047
+ return cur_state
1048
+
1049
+ elif isinstance(cur_nd, uni.ParamVar):
1050
+ if cur_nd.is_vararg:
1051
+ return "varargs"
1052
+ if cur_nd.is_kwargs:
1053
+ return "kwargs"
1054
+ return cur_state
1055
+
870
1056
  def func_decl_params(self, _: None) -> list[uni.UniNode]:
871
1057
  """Grammar rule.
872
1058
 
@@ -874,11 +1060,17 @@ class JacParser(Transform[uni.Source, uni.Module]):
874
1060
  """
875
1061
  return self.cur_nodes
876
1062
 
877
- def param_var(self, _: None) -> uni.ParamVar:
1063
+ def param_var(self, _: None) -> uni.ParamVar | uni.Token:
878
1064
  """Grammar rule.
879
1065
 
880
- param_var: (STAR_POW | STAR_MUL)? NAME type_tag (EQ expression)?
1066
+ param_var: (STAR_POW | STAR_MUL)? named_ref type_tag (EQ expression)?
1067
+ | DIV
1068
+ | STAR_MUL
881
1069
  """
1070
+ if len(self.cur_nodes) == 1 and (
1071
+ star_only := self.match_token(Tok.DIV) or self.match_token(Tok.STAR_MUL)
1072
+ ):
1073
+ return star_only
882
1074
  star = self.match_token(Tok.STAR_POW) or self.match_token(Tok.STAR_MUL)
883
1075
  name = self.consume(uni.Name)
884
1076
  type_tag = self.consume(uni.SubTag)
@@ -1028,7 +1220,6 @@ class JacParser(Transform[uni.Source, uni.Module]):
1028
1220
  | (yield_expr | KW_YIELD) SEMI
1029
1221
  | raise_stmt SEMI
1030
1222
  | assert_stmt SEMI
1031
- | check_stmt SEMI
1032
1223
  | assignment SEMI
1033
1224
  | delete_stmt SEMI
1034
1225
  | report_stmt SEMI
@@ -1307,18 +1498,6 @@ class JacParser(Transform[uni.Source, uni.Module]):
1307
1498
  kid=self.cur_nodes,
1308
1499
  )
1309
1500
 
1310
- def check_stmt(self, _: None) -> uni.CheckStmt:
1311
- """Grammar rule.
1312
-
1313
- check_stmt: KW_CHECK expression
1314
- """
1315
- self.consume_token(Tok.KW_CHECK)
1316
- target = self.consume(uni.Expr)
1317
- return uni.CheckStmt(
1318
- target=target,
1319
- kid=self.cur_nodes,
1320
- )
1321
-
1322
1501
  def ctrl_stmt(self, _: None) -> uni.CtrlStmt | uni.DisengageStmt:
1323
1502
  """Grammar rule.
1324
1503
 
@@ -1532,29 +1711,40 @@ class JacParser(Transform[uni.Source, uni.Module]):
1532
1711
  lambda_expr: KW_LAMBDA func_decl_params? (RETURN_HINT expression)? COLON expression
1533
1712
  """
1534
1713
  return_type: uni.Expr | None = None
1714
+ return_hint_tok: uni.Token | None = None
1535
1715
  sig_kid: list[uni.UniNode] = []
1536
1716
  self.consume_token(Tok.KW_LAMBDA)
1537
1717
  params = self.match(list)
1538
- if self.match_token(Tok.RETURN_HINT):
1718
+ if return_hint_tok := self.match_token(Tok.RETURN_HINT):
1539
1719
  return_type = self.consume(uni.Expr)
1540
1720
  self.consume_token(Tok.COLON)
1541
1721
  body = self.consume(uni.Expr)
1542
1722
  if params:
1543
1723
  sig_kid.extend(params)
1724
+ if return_hint_tok:
1725
+ sig_kid.append(return_hint_tok)
1544
1726
  if return_type:
1545
1727
  sig_kid.append(return_type)
1546
1728
  signature = (
1547
1729
  uni.FuncSignature(
1730
+ posonly_params=[],
1548
1731
  params=(
1549
1732
  self.extract_from_list(params, uni.ParamVar) if params else []
1550
1733
  ),
1734
+ varargs=None,
1735
+ kwonlyargs=[],
1736
+ kwargs=None,
1551
1737
  return_type=return_type,
1552
1738
  kid=sig_kid,
1553
1739
  )
1554
1740
  if params or return_type
1555
1741
  else None
1556
1742
  )
1557
- new_kid = [i for i in self.cur_nodes if i != params and i != return_type]
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
+ ]
1558
1748
  new_kid.insert(1, signature) if signature else None
1559
1749
  return uni.LambdaExpr(
1560
1750
  signature=signature,
@@ -1788,25 +1978,31 @@ class JacParser(Transform[uni.Source, uni.Module]):
1788
1978
  )
1789
1979
  return self._binary_expr_unwind(self.cur_nodes)
1790
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
+
1791
1994
  def pipe_call(self, _: None) -> uni.Expr:
1792
1995
  """Grammar rule.
1793
1996
 
1794
- pipe_call: (PIPE_FWD | A_PIPE_FWD | KW_SPAWN | KW_AWAIT)? atomic_chain
1997
+ pipe_call: (PIPE_FWD | A_PIPE_FWD | KW_SPAWN)? atomic_chain
1795
1998
  """
1796
- if len(self.cur_nodes) == 2:
1797
- if self.match_token(Tok.KW_AWAIT):
1798
- target = self.consume(uni.Expr)
1799
- return uni.AwaitExpr(
1800
- target=target,
1801
- kid=self.cur_nodes,
1802
- )
1803
- elif op := self.match(uni.Token):
1804
- operand = self.consume(uni.Expr)
1805
- return uni.UnaryExpr(
1806
- op=op,
1807
- operand=operand,
1808
- kid=self.cur_nodes,
1809
- )
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
+ )
1810
2006
  return self._binary_expr_unwind(self.cur_nodes)
1811
2007
 
1812
2008
  def aug_op(self, _: None) -> uni.Token:
@@ -1814,7 +2010,6 @@ class JacParser(Transform[uni.Source, uni.Module]):
1814
2010
 
1815
2011
  aug_op: RSHIFT_EQ
1816
2012
  | LSHIFT_EQ
1817
- | BW_NOT_EQ
1818
2013
  | BW_XOR_EQ
1819
2014
  | BW_OR_EQ
1820
2015
  | BW_AND_EQ
@@ -1868,6 +2063,14 @@ class JacParser(Transform[uni.Source, uni.Module]):
1868
2063
  atomic_call: atomic_chain LPAREN param_list? by_llm? RPAREN
1869
2064
  """
1870
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
+ )
1871
2074
  self.consume_token(Tok.LPAREN)
1872
2075
  params_sn = self.match(list)
1873
2076
  genai_call = self.match(uni.Expr)
@@ -2011,10 +2214,22 @@ class JacParser(Transform[uni.Source, uni.Module]):
2011
2214
 
2012
2215
  fstring: FSTR_START fstr_parts FSTR_END
2013
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
2014
2219
  """
2015
- self.match_token(Tok.FSTR_START) or self.consume_token(Tok.FSTR_SQ_START)
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
+ )
2016
2226
  target = self.match(list)
2017
- self.match_token(Tok.FSTR_END) or self.consume_token(Tok.FSTR_SQ_END)
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
+ )
2018
2233
  return uni.FString(
2019
2234
  parts=(
2020
2235
  self.extract_from_list(target, (uni.String, uni.ExprStmt))
@@ -2062,6 +2277,44 @@ class JacParser(Transform[uni.Source, uni.Module]):
2062
2277
  ]
2063
2278
  return valid_parts
2064
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
+
2065
2318
  def list_val(self, _: None) -> uni.ListVal:
2066
2319
  """Grammar rule.
2067
2320
 
@@ -2291,7 +2544,7 @@ class JacParser(Transform[uni.Source, uni.Module]):
2291
2544
  def assignment_list(self, _: None) -> list[uni.UniNode]:
2292
2545
  """Grammar rule.
2293
2546
 
2294
- assignment_list: (assignment_list COMMA)? (assignment | NAME)
2547
+ assignment_list: (assignment | named_ref) (COMMA (assignment | named_ref))* COMMA?
2295
2548
  """
2296
2549
 
2297
2550
  def name_to_assign(name_consume: uni.NameAtom) -> uni.Assignment:
@@ -2299,15 +2552,23 @@ class JacParser(Transform[uni.Source, uni.Module]):
2299
2552
  target=[name_consume], value=None, type_tag=None, kid=[name_consume]
2300
2553
  )
2301
2554
 
2302
- if self.match(list):
2303
- self.consume_token(Tok.COMMA)
2555
+ # Match first (assignment | named_ref)
2304
2556
  if self.match(uni.Assignment):
2305
2557
  pass
2306
2558
  elif name_consume := self.match(uni.NameAtom):
2307
2559
  self.cur_nodes[self.node_idx - 1] = name_to_assign(name_consume)
2308
2560
  else:
2309
- assign = self.consume(uni.Assignment)
2310
- self.cur_nodes[self.node_idx - 1] = assign
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
+
2311
2572
  return self.flat_cur_nodes
2312
2573
 
2313
2574
  def type_ref(self, kid: list[uni.UniNode]) -> uni.TypeRef:
@@ -2328,6 +2589,7 @@ class JacParser(Transform[uni.Source, uni.Module]):
2328
2589
  LSQUARE (KW_NODE| KW_EDGE)? expression? (edge_op_ref (filter_compr | expression)?)+ RSQUARE
2329
2590
  """
2330
2591
  self.consume_token(Tok.LSQUARE)
2592
+ is_async = bool(self.match_token(Tok.KW_ASYNC))
2331
2593
  edges_only = bool(self.match_token(Tok.KW_EDGE))
2332
2594
  self.match_token(Tok.KW_NODE)
2333
2595
  valid_chain = []
@@ -2338,6 +2600,7 @@ class JacParser(Transform[uni.Source, uni.Module]):
2338
2600
  return uni.EdgeRefTrailer(
2339
2601
  chain=valid_chain,
2340
2602
  edges_only=edges_only,
2603
+ is_async=is_async,
2341
2604
  kid=self.cur_nodes,
2342
2605
  )
2343
2606
 
@@ -2767,6 +3030,36 @@ class JacParser(Transform[uni.Source, uni.Module]):
2767
3030
  value = self.consume(uni.MatchPattern)
2768
3031
  return uni.MatchKVPair(key=pattern, value=value, kid=self.cur_nodes)
2769
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
+
2770
3063
  def class_pattern(self, _: None) -> uni.MatchArch:
2771
3064
  """Grammar rule.
2772
3065
 
@@ -2873,6 +3166,8 @@ class JacParser(Transform[uni.Source, uni.Module]):
2873
3166
  Tok.FSTR_BESC,
2874
3167
  Tok.FSTR_PIECE,
2875
3168
  Tok.FSTR_SQ_PIECE,
3169
+ Tok.FSTR_TRIPLE_PIECE,
3170
+ Tok.FSTR_SQ_TRIPLE_PIECE,
2876
3171
  ]:
2877
3172
  ret_type = uni.String
2878
3173
  if token.type == Tok.FSTR_BESC:
@@ -4,7 +4,6 @@ from ..transform import Alert, Transform # noqa: I100
4
4
  from .annex_pass import JacAnnexPass # noqa: I100
5
5
  from .binder_pass import BinderPass # noqa: I100
6
6
  from .sym_tab_build_pass import SymTabBuildPass, UniPass # noqa: I100
7
- from .sym_tab_link_pass import SymTabLinkPass # noqa: I100
8
7
  from .def_use_pass import DefUsePass # noqa: I100
9
8
  from .sem_def_match_pass import SemDefMatchPass # noqa: I100
10
9
  from .import_pass import JacImportDepsPass # noqa: I100
@@ -12,10 +11,10 @@ from .def_impl_match_pass import DeclImplMatchPass # noqa: I100
12
11
  from .type_checker_pass import TypeCheckPass # noqa: I100
13
12
  from .pyast_load_pass import PyastBuildPass # type: ignore # noqa: I100
14
13
  from .pyast_gen_pass import PyastGenPass # noqa: I100
14
+ from .predynamo_pass import PreDynamoPass # noqa: I100
15
15
  from .pybc_gen_pass import PyBytecodeGenPass # noqa: I100
16
16
  from .cfg_build_pass import CFGBuildPass # noqa: I100
17
17
  from .pyjac_ast_link_pass import PyJacAstLinkPass # noqa: I100
18
- from .inheritance_pass import InheritancePass # noqa: I100
19
18
 
20
19
 
21
20
  __all__ = [
@@ -28,14 +27,13 @@ __all__ = [
28
27
  "BinderPass",
29
28
  "TypeCheckPass",
30
29
  "SymTabBuildPass",
31
- "SymTabLinkPass",
32
30
  "DeclImplMatchPass",
33
31
  "DefUsePass",
34
32
  "SemDefMatchPass",
35
33
  "PyastBuildPass",
36
34
  "PyastGenPass",
35
+ "PreDynamoPass",
37
36
  "PyBytecodeGenPass",
38
37
  "CFGBuildPass",
39
38
  "PyJacAstLinkPass",
40
- "InheritancePass",
41
39
  ]
@@ -30,7 +30,6 @@ class DefUsePass(UniPass):
30
30
  """Jac Ast build pass."""
31
31
 
32
32
  def enter_archetype(self, node: uni.Archetype) -> None:
33
- node.sym_tab.inherit_baseclasses_sym(node)
34
33
 
35
34
  def inform_from_walker(node: uni.UniNode) -> None:
36
35
  for i in (
@@ -38,6 +37,7 @@ class DefUsePass(UniPass):
38
37
  + node.get_all_sub_nodes(uni.DisengageStmt)
39
38
  + node.get_all_sub_nodes(uni.EdgeOpRef)
40
39
  + node.get_all_sub_nodes(uni.EventSignature)
40
+ + node.get_all_sub_nodes(uni.TypedCtxBlock)
41
41
  ):
42
42
  i.from_walker = True
43
43
 
@@ -47,9 +47,6 @@ class DefUsePass(UniPass):
47
47
  if isinstance(i.body, uni.ImplDef):
48
48
  inform_from_walker(i.body)
49
49
 
50
- def enter_enum(self, node: uni.Enum) -> None:
51
- node.sym_tab.inherit_baseclasses_sym(node)
52
-
53
50
  def enter_type_ref(self, node: uni.TypeRef) -> None:
54
51
  node.sym_tab.use_lookup(node)
55
52