jaclang 0.7.13__py3-none-any.whl → 0.7.16__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 (96) hide show
  1. jaclang/cli/cli.py +15 -10
  2. jaclang/cli/cmdreg.py +9 -12
  3. jaclang/compiler/__init__.py +19 -53
  4. jaclang/compiler/absyntree.py +95 -17
  5. jaclang/compiler/jac.lark +4 -3
  6. jaclang/compiler/parser.py +35 -23
  7. jaclang/compiler/passes/ir_pass.py +4 -13
  8. jaclang/compiler/passes/main/access_modifier_pass.py +1 -1
  9. jaclang/compiler/passes/main/fuse_typeinfo_pass.py +4 -5
  10. jaclang/compiler/passes/main/import_pass.py +19 -23
  11. jaclang/compiler/passes/main/pyast_gen_pass.py +308 -567
  12. jaclang/compiler/passes/main/pyast_load_pass.py +33 -6
  13. jaclang/compiler/passes/main/registry_pass.py +37 -3
  14. jaclang/compiler/passes/main/sym_tab_build_pass.py +1 -1
  15. jaclang/compiler/passes/main/tests/__init__.py +1 -1
  16. jaclang/compiler/passes/main/tests/test_import_pass.py +5 -1
  17. jaclang/compiler/passes/main/type_check_pass.py +7 -0
  18. jaclang/compiler/passes/tool/fuse_comments_pass.py +14 -2
  19. jaclang/compiler/passes/tool/jac_formatter_pass.py +144 -94
  20. jaclang/compiler/passes/tool/tests/fixtures/corelib_fmt.jac +0 -1
  21. jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/architype_test.jac +13 -0
  22. jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/comment_alignment.jac +11 -0
  23. jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/comments.jac +13 -0
  24. jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/decorator_stack.jac +37 -0
  25. jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/esc_keywords.jac +5 -0
  26. jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/long_names.jac +19 -0
  27. jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/triple_quoted_string.jac +6 -0
  28. jaclang/compiler/passes/tool/tests/test_jac_format_pass.py +11 -0
  29. jaclang/compiler/passes/tool/tests/test_unparse_validate.py +33 -39
  30. jaclang/compiler/passes/transform.py +4 -0
  31. jaclang/compiler/semtable.py +31 -7
  32. jaclang/compiler/tests/test_importer.py +12 -5
  33. jaclang/langserve/engine.py +82 -143
  34. jaclang/langserve/sem_manager.py +379 -0
  35. jaclang/langserve/server.py +8 -10
  36. jaclang/langserve/tests/fixtures/base_module_structure.jac +27 -6
  37. jaclang/langserve/tests/fixtures/circle.jac +3 -3
  38. jaclang/langserve/tests/fixtures/circle_err.jac +3 -3
  39. jaclang/langserve/tests/fixtures/circle_pure.test.jac +3 -3
  40. jaclang/langserve/tests/fixtures/import_include_statements.jac +1 -1
  41. jaclang/langserve/tests/test_sem_tokens.py +277 -0
  42. jaclang/langserve/tests/test_server.py +96 -16
  43. jaclang/langserve/utils.py +163 -96
  44. jaclang/plugin/builtin.py +1 -1
  45. jaclang/plugin/default.py +214 -24
  46. jaclang/plugin/feature.py +59 -11
  47. jaclang/plugin/spec.py +58 -6
  48. jaclang/{core → runtimelib}/architype.py +1 -1
  49. jaclang/{core → runtimelib}/context.py +8 -1
  50. jaclang/runtimelib/importer.py +361 -0
  51. jaclang/runtimelib/machine.py +94 -0
  52. jaclang/{core → runtimelib}/utils.py +13 -5
  53. jaclang/settings.py +4 -1
  54. jaclang/tests/fixtures/abc.jac +3 -3
  55. jaclang/tests/fixtures/blankwithentry.jac +3 -0
  56. jaclang/tests/fixtures/byllmissue.jac +1 -5
  57. jaclang/tests/fixtures/chandra_bugs2.jac +11 -10
  58. jaclang/tests/fixtures/cls_method.jac +41 -0
  59. jaclang/tests/fixtures/dblhello.jac +6 -0
  60. jaclang/tests/fixtures/deep/one_lev.jac +3 -3
  61. jaclang/tests/fixtures/deep/one_lev_dup.jac +2 -3
  62. jaclang/tests/fixtures/deep_import_mods.jac +13 -0
  63. jaclang/tests/fixtures/err.impl.jac +3 -0
  64. jaclang/tests/fixtures/err.jac +4 -2
  65. jaclang/tests/fixtures/err.test.jac +3 -0
  66. jaclang/tests/fixtures/err_runtime.jac +15 -0
  67. jaclang/tests/fixtures/game1.jac +1 -1
  68. jaclang/tests/fixtures/hello.jac +4 -0
  69. jaclang/tests/fixtures/impl_grab.impl.jac +2 -1
  70. jaclang/tests/fixtures/impl_grab.jac +4 -1
  71. jaclang/tests/fixtures/jp_importer_auto.jac +14 -0
  72. jaclang/tests/fixtures/maxfail_run_test.jac +4 -4
  73. jaclang/tests/fixtures/needs_import.jac +2 -2
  74. jaclang/tests/fixtures/pyfunc_2.py +3 -0
  75. jaclang/tests/fixtures/registry.jac +9 -0
  76. jaclang/tests/fixtures/run_test.jac +4 -4
  77. jaclang/tests/fixtures/semstr.jac +1 -4
  78. jaclang/tests/fixtures/simple_archs.jac +1 -1
  79. jaclang/tests/test_cli.py +65 -2
  80. jaclang/tests/test_language.py +83 -7
  81. jaclang/tests/test_man_code.py +17 -0
  82. jaclang/tests/test_reference.py +6 -0
  83. jaclang/utils/helpers.py +45 -21
  84. jaclang/utils/test.py +9 -0
  85. jaclang/utils/treeprinter.py +0 -4
  86. {jaclang-0.7.13.dist-info → jaclang-0.7.16.dist-info}/METADATA +3 -2
  87. {jaclang-0.7.13.dist-info → jaclang-0.7.16.dist-info}/RECORD +93 -77
  88. jaclang/core/importer.py +0 -344
  89. jaclang/tests/fixtures/aott_raise.jac +0 -25
  90. jaclang/tests/fixtures/package_import.jac +0 -6
  91. /jaclang/{core → runtimelib}/__init__.py +0 -0
  92. /jaclang/{core → runtimelib}/constructs.py +0 -0
  93. /jaclang/{core → runtimelib}/memory.py +0 -0
  94. /jaclang/{core → runtimelib}/test.py +0 -0
  95. {jaclang-0.7.13.dist-info → jaclang-0.7.16.dist-info}/WHEEL +0 -0
  96. {jaclang-0.7.13.dist-info → jaclang-0.7.16.dist-info}/entry_points.txt +0 -0
@@ -124,6 +124,7 @@ class PyastBuildPass(Pass[ast.PythonModuleAst]):
124
124
  source=ast.JacSource("", mod_path=self.mod_path),
125
125
  doc=doc_str,
126
126
  body=valid[1:] if valid and isinstance(valid[0], ast.String) else valid,
127
+ terminals=[],
127
128
  is_imported=False,
128
129
  kid=valid,
129
130
  )
@@ -143,12 +144,15 @@ class PyastBuildPass(Pass[ast.PythonModuleAst]):
143
144
  if sys.version_info >= (3, 12):
144
145
  type_params: list[type_param]
145
146
  """
147
+ from jaclang.compiler import TOKEN_MAP
148
+
149
+ reserved_keywords = [v for _, v in TOKEN_MAP.items()]
150
+
151
+ value = node.name if node.name not in reserved_keywords else f"<>{node.name}"
146
152
  name = ast.Name(
147
153
  file_path=self.mod_path,
148
154
  name=Tok.NAME,
149
- value=(
150
- node.name if node.name != "root" else "root_"
151
- ), # root is a reserved keyword
155
+ value=value,
152
156
  line=node.lineno,
153
157
  end_line=node.end_lineno if node.end_lineno else node.lineno,
154
158
  col_start=node.col_offset,
@@ -1874,10 +1878,19 @@ class PyastBuildPass(Pass[ast.PythonModuleAst]):
1874
1878
  id: _Identifier
1875
1879
  ctx: expr_context
1876
1880
  """
1881
+ from jaclang.compiler import TOKEN_MAP
1882
+
1883
+ reserved_keywords = [
1884
+ v
1885
+ for _, v in TOKEN_MAP.items()
1886
+ if v not in ["float", "int", "str", "bool", "self"]
1887
+ ]
1888
+
1889
+ value = node.id if node.id not in reserved_keywords else f"<>{node.id}"
1877
1890
  ret = ast.Name(
1878
1891
  file_path=self.mod_path,
1879
1892
  name=Tok.NAME,
1880
- value=node.id if node.id != "root" else "root_", # reserved word
1893
+ value=value,
1881
1894
  line=node.lineno,
1882
1895
  end_line=node.end_lineno if node.end_lineno else node.lineno,
1883
1896
  col_start=node.col_offset,
@@ -1912,13 +1925,18 @@ class PyastBuildPass(Pass[ast.PythonModuleAst]):
1912
1925
  class Nonlocal(stmt):
1913
1926
  names: list[_Identifier]
1914
1927
  """
1928
+ from jaclang.compiler import TOKEN_MAP
1929
+
1930
+ reserved_keywords = [v for _, v in TOKEN_MAP.items()]
1931
+
1915
1932
  names: list[ast.NameAtom] = []
1916
1933
  for name in node.names:
1934
+ value = name if name not in reserved_keywords else f"<>{name}"
1917
1935
  names.append(
1918
1936
  ast.Name(
1919
1937
  file_path=self.mod_path,
1920
1938
  name=Tok.NAME,
1921
- value=name if name != "root" else "root_",
1939
+ value=value,
1922
1940
  line=node.lineno,
1923
1941
  end_line=node.end_lineno if node.end_lineno else node.lineno,
1924
1942
  col_start=node.col_offset,
@@ -2237,10 +2255,19 @@ class PyastBuildPass(Pass[ast.PythonModuleAst]):
2237
2255
  arg: _Identifier
2238
2256
  annotation: expr | None
2239
2257
  """
2258
+ from jaclang.compiler import TOKEN_MAP
2259
+
2260
+ reserved_keywords = [
2261
+ v
2262
+ for _, v in TOKEN_MAP.items()
2263
+ if v not in ["float", "int", "str", "bool", "self"]
2264
+ ]
2265
+
2266
+ value = node.arg if node.arg not in reserved_keywords else f"<>{node.arg}"
2240
2267
  name = ast.Name(
2241
2268
  file_path=self.mod_path,
2242
2269
  name=Tok.NAME,
2243
- value=node.arg if node.arg != "root" else "root_", # reserved word
2270
+ value=value,
2244
2271
  line=node.lineno,
2245
2272
  end_line=node.end_lineno if node.end_lineno else node.lineno,
2246
2273
  col_start=node.col_offset,
@@ -13,7 +13,7 @@ import jaclang.compiler.absyntree as ast
13
13
  from jaclang.compiler.constant import Constants as Con
14
14
  from jaclang.compiler.passes import Pass
15
15
  from jaclang.compiler.semtable import SemInfo, SemRegistry
16
- from jaclang.core.utils import get_sem_scope
16
+ from jaclang.runtimelib.utils import get_sem_scope
17
17
 
18
18
 
19
19
  class RegistryPass(Pass):
@@ -46,6 +46,7 @@ class RegistryPass(Pass):
46
46
  """Save architype information."""
47
47
  scope = get_sem_scope(node)
48
48
  seminfo = SemInfo(
49
+ node,
49
50
  node.name.value,
50
51
  node.arch_type.value,
51
52
  node.semstr.lit_value if node.semstr else "",
@@ -61,7 +62,7 @@ class RegistryPass(Pass):
61
62
  """Save enum information."""
62
63
  scope = get_sem_scope(node)
63
64
  seminfo = SemInfo(
64
- node.name.value, "Enum", node.semstr.lit_value if node.semstr else ""
65
+ node, node.name.value, "Enum", node.semstr.lit_value if node.semstr else ""
65
66
  )
66
67
  if (
67
68
  len(self.modules_visited)
@@ -77,6 +78,38 @@ class RegistryPass(Pass):
77
78
  )
78
79
  scope = get_sem_scope(node)
79
80
  seminfo = SemInfo(
81
+ node,
82
+ node.name.value,
83
+ extracted_type,
84
+ node.semstr.lit_value if node.semstr else "",
85
+ )
86
+ if len(self.modules_visited) and self.modules_visited[-1].registry:
87
+ self.modules_visited[-1].registry.add(scope, seminfo)
88
+
89
+ def exit_ability(self, node: ast.Ability) -> None:
90
+ """Save ability information."""
91
+ scope = get_sem_scope(node)
92
+ seminfo = SemInfo(
93
+ node,
94
+ node.name_ref.sym_name,
95
+ "Ability",
96
+ node.semstr.lit_value if node.semstr else "",
97
+ )
98
+ if len(self.modules_visited) and self.modules_visited[-1].registry:
99
+ (
100
+ self.modules_visited[-1].registry.add(scope.parent, seminfo)
101
+ if scope.parent
102
+ else None
103
+ )
104
+
105
+ def exit_param_var(self, node: ast.ParamVar) -> None:
106
+ """Save param information."""
107
+ scope = get_sem_scope(node)
108
+ extracted_type = (
109
+ "".join(self.extract_type(node.type_tag.tag)) if node.type_tag else None
110
+ )
111
+ seminfo = SemInfo(
112
+ node,
80
113
  node.name.value,
81
114
  extracted_type,
82
115
  node.semstr.lit_value if node.semstr else "",
@@ -94,6 +127,7 @@ class RegistryPass(Pass):
94
127
  )
95
128
  scope = get_sem_scope(node)
96
129
  seminfo = SemInfo(
130
+ node,
97
131
  (
98
132
  node.target.items[0].value
99
133
  if isinstance(node.target.items[0], ast.Name)
@@ -113,7 +147,7 @@ class RegistryPass(Pass):
113
147
  and node.parent.parent.__class__.__name__ == "Enum"
114
148
  ):
115
149
  scope = get_sem_scope(node)
116
- seminfo = SemInfo(node.value, None, "")
150
+ seminfo = SemInfo(node, node.value, None, "")
117
151
  if len(self.modules_visited) and self.modules_visited[-1].registry:
118
152
  self.modules_visited[-1].registry.add(scope, seminfo)
119
153
 
@@ -193,7 +193,7 @@ class SymTabBuildPass(Pass):
193
193
  if not node.is_absorb:
194
194
  for i in node.items.items:
195
195
  i.sym_tab.def_insert(i, single_decl="import item")
196
- elif node.is_absorb and node.hint.tag.value == "jac":
196
+ elif node.is_absorb and node.is_jac:
197
197
  source = node.items.items[0]
198
198
  if not isinstance(source, ast.ModulePath) or not source.sub_module:
199
199
  self.error(
@@ -1 +1 @@
1
- """Tests for Jac passes."""
1
+ """Various tests for Jac passes."""
@@ -47,8 +47,12 @@ class ImportPassPassTests(TestCase):
47
47
  state = jac_file_to_pass(
48
48
  self.fixture_abs_path("incautoimpl.jac"), JacImportPass
49
49
  )
50
+ count = 0
50
51
  for i in state.ir.get_all_sub_nodes(ast.Module):
51
- self.assertEqual(i.annexable_by, self.fixture_abs_path("autoimpl.jac"))
52
+ if i.name != "autoimpl":
53
+ count += 1
54
+ self.assertEqual(i.annexable_by, self.fixture_abs_path("autoimpl.jac"))
55
+ self.assertEqual(count, 3)
52
56
 
53
57
  def test_py_resolve_list(self) -> None:
54
58
  """Basic test for pass."""
@@ -49,7 +49,14 @@ class JacTypeCheckPass(Pass):
49
49
  """Call mypy APIs to implement type checking in Jac."""
50
50
  # Creating mypy api objects
51
51
  options = myab.myb.Options()
52
+ options.ignore_missing_imports = True
52
53
  options.cache_dir = Con.JAC_MYPY_CACHE
54
+ options.mypy_path = [
55
+ str(
56
+ pathlib.Path(os.path.dirname(__file__)).parent.parent.parent.parent
57
+ / "stubs"
58
+ )
59
+ ]
53
60
  errors = myab.Errors(self, options)
54
61
  fs_cache = myab.FileSystemCache()
55
62
  search_paths = myab.compute_search_paths([], options, str(self.__path))
@@ -27,7 +27,12 @@ class FuseCommentsPass(Pass):
27
27
  """Insert comment tokens into all_tokens."""
28
28
  comment_stream = iter(self.comments) # Iterator for comments
29
29
  code_stream = iter(self.all_tokens) # Iterator for code tokens
30
- new_stream: list[ast.AstNode] = [] # New stream to hold ordered tokens
30
+ new_stream: list[ast.Token] = [] # New stream to hold ordered tokens
31
+
32
+ if not isinstance(self.ir, ast.Module):
33
+ raise self.ice(
34
+ f"FuseCommentsPass can only be run on a Module, not a {type(self.ir)}"
35
+ )
31
36
 
32
37
  try:
33
38
  next_comment = next(comment_stream) # Get the first comment
@@ -39,12 +44,20 @@ class FuseCommentsPass(Pass):
39
44
  except StopIteration:
40
45
  next_code = None
41
46
 
47
+ if next_comment and (not next_code or is_comment_next(next_comment, next_code)):
48
+ self.ir.terminals.insert(0, next_comment)
49
+
42
50
  while next_comment or next_code:
43
51
  if next_comment and (
44
52
  not next_code or is_comment_next(next_comment, next_code)
45
53
  ):
46
54
  # Add the comment to the new stream
55
+ last_tok = new_stream[-1] if len(new_stream) else None
47
56
  new_stream.append(next_comment)
57
+ if last_tok:
58
+ self.ir.terminals.insert(
59
+ self.ir.terminals.index(last_tok) + 1, next_comment
60
+ )
48
61
  try:
49
62
  next_comment = next(comment_stream)
50
63
  except StopIteration:
@@ -70,7 +83,6 @@ class FuseCommentsPass(Pass):
70
83
  parent_kids.insert(insert_index, token)
71
84
  prev_token.parent.set_kids(parent_kids)
72
85
  else:
73
- prev_token.pp()
74
86
  raise self.ice(
75
87
  "Token without parent in AST should be impossible"
76
88
  )
@@ -10,6 +10,7 @@ import jaclang.compiler.absyntree as ast
10
10
  from jaclang.compiler.absyntree import AstNode
11
11
  from jaclang.compiler.constant import Tokens as Tok
12
12
  from jaclang.compiler.passes import Pass
13
+ from jaclang.settings import settings
13
14
 
14
15
 
15
16
  class JacFormatPass(Pass):
@@ -20,20 +21,37 @@ class JacFormatPass(Pass):
20
21
  self.comments: list[ast.CommentToken] = []
21
22
  self.indent_size = 4
22
23
  self.indent_level = 0
23
- self.MAX_LINE_LENGTH = 44
24
+ self.MAX_LINE_LENGTH = int(float(settings.max_line_length) / 2)
24
25
 
25
26
  def enter_node(self, node: ast.AstNode) -> None:
26
27
  """Enter node."""
27
28
  node.gen.jac = ""
28
29
  super().enter_node(node)
29
30
 
31
+ def token_before(self, node: ast.Token) -> Optional[ast.Token]:
32
+ """Token before."""
33
+ if not isinstance(self.ir, ast.Module):
34
+ raise self.ice("IR must be module. Impossible")
35
+ if self.ir.terminals.index(node) == 0:
36
+ return None
37
+ return self.ir.terminals[self.ir.terminals.index(node) - 1]
38
+
39
+ def token_after(self, node: ast.Token) -> Optional[ast.Token]:
40
+ """Token after."""
41
+ if not isinstance(self.ir, ast.Module):
42
+ raise self.ice("IR must be module. Impossible")
43
+ if self.ir.terminals.index(node) == len(self.ir.terminals) - 1:
44
+ return None
45
+ return self.ir.terminals[self.ir.terminals.index(node) + 1]
46
+
30
47
  def indent_str(self) -> str:
31
48
  """Return string for indent."""
32
49
  return " " * self.indent_size * self.indent_level
33
50
 
34
51
  def emit(self, node: ast.AstNode, s: str, strip_mode: bool = True) -> None:
35
52
  """Emit code to node."""
36
- node.gen.jac += self.indent_str() + s.replace("\n", "\n" + self.indent_str())
53
+ indented_str = re.sub(r"\n(?!\n)", f"\n{self.indent_str()}", s)
54
+ node.gen.jac += self.indent_str() + indented_str
37
55
  if "\n" in node.gen.jac:
38
56
  if strip_mode:
39
57
  node.gen.jac = node.gen.jac.rstrip(" ")
@@ -113,9 +131,15 @@ class JacFormatPass(Pass):
113
131
  else:
114
132
  if isinstance(last_element, ast.Import):
115
133
  self.emit_ln(node, "")
116
- self.emit_ln(node, i.gen.jac)
117
- if not node.gen.jac.endswith("\n"):
134
+ if last_element and (
135
+ isinstance(i, ast.Architype)
136
+ and isinstance(last_element, ast.Architype)
137
+ and i.loc.first_line - last_element.loc.last_line == 2
138
+ and not node.gen.jac.endswith("\n\n")
139
+ ):
118
140
  self.emit_ln(node, "")
141
+ self.emit_ln(node, i.gen.jac)
142
+
119
143
  if counter <= len(node.body) - 1:
120
144
  if (
121
145
  isinstance(i, ast.Ability)
@@ -126,6 +150,7 @@ class JacFormatPass(Pass):
126
150
  and len(node.body[counter].kid[-1].kid) == 2
127
151
  and len(node.body[counter - 1].kid[-1].kid) == 2
128
152
  )
153
+ and node.gen.jac.endswith("\n")
129
154
  ):
130
155
  self.emit(node, "")
131
156
  else:
@@ -196,7 +221,7 @@ class JacFormatPass(Pass):
196
221
  items: list[T],
197
222
  """
198
223
  prev_token = None
199
- for i, stmt in enumerate(node.kid):
224
+ for stmt in node.kid:
200
225
  if isinstance(node.parent, (ast.EnumDef, ast.Enum)) and stmt.gen.jac == ",":
201
226
  self.indent_level -= 1
202
227
  self.emit_ln(node, f"{stmt.gen.jac}")
@@ -220,52 +245,20 @@ class JacFormatPass(Pass):
220
245
  self.emit_ln(node, "")
221
246
  self.indent_level += 1
222
247
  if stmt.name == Tok.LBRACE:
223
- next_kid = node.kid[i + 1]
224
- if isinstance(next_kid, ast.CommentToken) and next_kid.is_inline:
225
- self.emit(node, f" {stmt.value}")
226
- else:
227
- self.emit(node, f" {stmt.value}")
248
+ self.emit(node, f" {stmt.value}")
228
249
  elif stmt.name == Tok.RBRACE:
229
- if self.indent_level > 0:
230
- self.indent_level -= 1
250
+ self.indent_level = max(0, self.indent_level - 1)
231
251
  if stmt.parent and stmt.parent.gen.jac.strip() == "{":
232
- self.emit_ln(node, stmt.gen.jac.strip())
233
- elif (
234
- stmt.parent
235
- and stmt.parent.parent
236
- and isinstance(
237
- stmt.parent.parent,
238
- (ast.ElseIf, ast.IfStmt, ast.IterForStmt, ast.TryStmt),
239
- )
240
- ):
241
252
  self.emit(node, f"{stmt.value}")
242
253
  else:
243
- next_kid = (
244
- node.kid[i + 1]
245
- if i < (len(node.kid) - 1)
246
- else ast.EmptyToken()
247
- )
248
- if (
249
- isinstance(next_kid, ast.CommentToken)
250
- and next_kid.is_inline
251
- ):
252
- self.emit(node, f" {stmt.value}")
253
- elif not (node.gen.jac).endswith("\n"):
254
- self.indent_level -= 1
254
+ if not node.gen.jac.endswith("\n"):
255
255
  self.emit_ln(node, "")
256
- self.indent_level += 1
257
- self.emit(node, f"{stmt.value}")
258
- else:
259
- self.emit(node, f"{stmt.value}")
256
+ self.emit(node, f"{stmt.value}")
260
257
  elif isinstance(stmt, ast.CommentToken):
261
258
  if stmt.is_inline:
262
259
  if isinstance(prev_token, ast.Semi) or (
263
260
  isinstance(prev_token, ast.Token)
264
- and prev_token.name
265
- in [
266
- Tok.LBRACE,
267
- Tok.RBRACE,
268
- ]
261
+ and prev_token.name in [Tok.LBRACE, Tok.RBRACE]
269
262
  ):
270
263
  self.indent_level -= 1
271
264
  self.emit(node, f" {stmt.gen.jac}")
@@ -289,48 +282,33 @@ class JacFormatPass(Pass):
289
282
  self.emit(node, stmt.gen.jac)
290
283
  if not stmt.gen.jac.endswith("postinit;"):
291
284
  self.indent_level -= 1
285
+ self.emit_ln(stmt, "")
292
286
  self.emit_ln(node, "")
293
287
  self.indent_level += 1
294
288
  elif stmt.gen.jac == ",":
295
289
  self.emit(node, f"{stmt.value} ")
296
290
  elif stmt.value == "=":
297
291
  self.emit(node, f" {stmt.value} ")
292
+ elif prev_token and prev_token.gen.jac.strip() == "@":
293
+ self.emit_ln(node, stmt.value)
298
294
  else:
299
- self.emit(node, f"{stmt.value}")
295
+ self.emit(node, f"{stmt.gen.jac}")
300
296
  prev_token = stmt
301
297
  continue
302
298
  elif isinstance(stmt, ast.Semi):
303
299
  self.emit(node, stmt.gen.jac)
304
- elif isinstance(prev_token, (ast.HasVar, ast.ArchHas)) and not isinstance(
305
- stmt, (ast.HasVar, ast.ArchHas)
300
+ elif (
301
+ isinstance(prev_token, (ast.HasVar, ast.ArchHas))
302
+ and not isinstance(stmt, (ast.HasVar, ast.ArchHas))
303
+ ) or (
304
+ isinstance(prev_token, ast.Ability)
305
+ and isinstance(stmt, (ast.Ability, ast.AbilityDef))
306
306
  ):
307
307
  if not isinstance(prev_token.kid[-1], ast.CommentToken):
308
308
  self.indent_level -= 1
309
309
  self.emit_ln(node, "")
310
310
  self.indent_level += 1
311
311
  self.emit(node, stmt.gen.jac)
312
- elif isinstance(prev_token, ast.Ability) and isinstance(
313
- stmt, (ast.Ability, ast.AbilityDef)
314
- ):
315
- if not isinstance(prev_token.kid[-1], ast.CommentToken) and (
316
- stmt.body and not isinstance(stmt.body, ast.FuncCall)
317
- ):
318
- self.indent_level -= 1
319
- self.emit_ln(node, "")
320
- self.indent_level += 1
321
- self.emit(node, f"{stmt.gen.jac}")
322
- elif stmt.body and isinstance(
323
- stmt.body, (ast.FuncCall, ast.EventSignature)
324
- ):
325
- self.indent_level -= 1
326
- self.emit_ln(node, "")
327
- self.indent_level += 1
328
- self.emit(node, stmt.gen.jac)
329
- else:
330
- self.indent_level -= 1
331
- self.emit_ln(node, "")
332
- self.indent_level += 1
333
- self.emit(node, f"{stmt.gen.jac}")
334
312
  else:
335
313
  if prev_token and prev_token.gen.jac.strip() == "{":
336
314
  self.emit_ln(node, "")
@@ -755,7 +733,7 @@ class JacFormatPass(Pass):
755
733
  self.indent_level += indent_val
756
734
  indented = True
757
735
  else:
758
- self.emit(node, f"{j.gen.jac.strip()}")
736
+ self.emit(node, j.gen.jac.lstrip())
759
737
  if indented:
760
738
  self.indent_level -= indent_val
761
739
  else:
@@ -921,6 +899,8 @@ class JacFormatPass(Pass):
921
899
  """Check if the length of the current generated code exceeds the max line length."""
922
900
  if max_line_length == 0:
923
901
  max_line_length = self.MAX_LINE_LENGTH
902
+ # print(content)
903
+ # print(len(content))
924
904
  return len(content) > max_line_length
925
905
 
926
906
  def exit_binary_expr(self, node: ast.BinaryExpr) -> None:
@@ -970,6 +950,7 @@ class JacFormatPass(Pass):
970
950
  self.error(
971
951
  f"Binary operator {node.op.value} not supported in bootstrap Jac"
972
952
  )
953
+ # print(node.gen)
973
954
  if isinstance(
974
955
  node.kid[-1], (ast.Semi, ast.CommentToken)
975
956
  ) and not node.gen.jac.endswith("\n"):
@@ -1102,6 +1083,9 @@ class JacFormatPass(Pass):
1102
1083
  if isinstance(i, ast.CommentToken):
1103
1084
  if i.is_inline:
1104
1085
  self.emit(node, f" {i.gen.jac}")
1086
+ elif (tok := self.token_before(i)) and (i.line_no - tok.line_no == 1):
1087
+ self.emit_ln(node, "")
1088
+ self.emit(node, i.gen.jac)
1105
1089
  else:
1106
1090
  self.emit_ln(node, "")
1107
1091
  self.emit_ln(node, "")
@@ -1332,38 +1316,96 @@ class JacFormatPass(Pass):
1332
1316
  if isinstance(node.kid[-1], (ast.Semi, ast.CommentToken)):
1333
1317
  self.emit_ln(node, "")
1334
1318
 
1319
+ def handle_long_assignment(self, node: ast.Assignment, kid: ast.AstNode) -> None:
1320
+ """Handle long assignment lines."""
1321
+ parts = re.split(r"(=)", kid.gen.jac)
1322
+ first_part = parts.pop(0).strip()
1323
+ self.emit_ln(
1324
+ node, f"{first_part} {parts.pop(0).strip()} {parts.pop(0).strip()}"
1325
+ )
1326
+ for j in range(0, len(parts) - 1, 2):
1327
+ op = parts[j]
1328
+ var = parts[j + 1].strip() if j + 1 < len(parts) else ""
1329
+ if var:
1330
+ self.indent_level += 1
1331
+ self.emit(node, f"{op} {var}")
1332
+ self.indent_level -= 1
1333
+ self.emit_ln(node, "")
1334
+ else:
1335
+ self.indent_level += 1
1336
+ self.emit(node, op)
1337
+ self.indent_level -= 1
1338
+
1339
+ def handle_long_expression(self, node: ast.AstNode, kid: ast.AstNode) -> None:
1340
+ """Handle long expressions with multiple operators."""
1341
+ parts = re.split(r"(\+|\-|\*|\/)", kid.gen.jac)
1342
+ self.emit_ln(node, f"{parts.pop(0).strip()}")
1343
+ for j in range(0, len(parts) - 1, 2):
1344
+ op = parts[j]
1345
+ var = parts[j + 1].strip() if j + 1 < len(parts) else ""
1346
+ if j < len(parts) - 2:
1347
+ self.indent_level += 1
1348
+ self.emit(node, f"{op} {var}")
1349
+ self.indent_level -= 1
1350
+ self.emit_ln(node, "")
1351
+ else:
1352
+ self.indent_level += 1
1353
+ self.emit(node, f"{op} {var}")
1354
+ self.indent_level -= 1
1355
+
1335
1356
  def exit_assignment(self, node: ast.Assignment) -> None:
1336
1357
  """Sub objects.
1337
1358
 
1338
- target: SubNodeList[AtomType],
1339
- value: Optional[ExprType | YieldStmt],
1340
- type_tag: Optional[SubTag[ExprType]],
1359
+ target: SubNodeList[Expr],
1360
+ value: Optional[Expr | YieldExpr],
1361
+ type_tag: Optional[SubTag[Expr]],
1341
1362
  mutable: bool = True,
1342
- aug_op: Optional[Token] = None
1363
+ aug_op: Optional[Token] = None,
1364
+ semstr: Optional[String] = None,
1365
+ is_enum_stmt: bool = False,
1343
1366
  """
1344
- for i in node.kid:
1345
- if isinstance(i, ast.CommentToken):
1346
- if i.is_inline:
1347
- self.emit(node, i.gen.jac)
1367
+ prev_token = None
1368
+ for kid in node.kid:
1369
+ if isinstance(kid, ast.CommentToken):
1370
+ if kid.is_inline:
1371
+ self.emit(node, kid.gen.jac)
1348
1372
  else:
1349
- if i.gen.jac not in [
1373
+ if kid.gen.jac not in [
1350
1374
  "# Update any new user level buddy schedule",
1351
1375
  "# Construct prompt here",
1352
1376
  ]:
1353
1377
  self.emit_ln(node, "")
1354
1378
  self.emit_ln(node, "")
1355
- self.emit_ln(node, i.gen.jac)
1379
+ self.emit_ln(node, kid.gen.jac)
1356
1380
  else:
1357
1381
  self.emit_ln(node, "")
1358
- self.emit(node, i.gen.jac)
1359
- elif isinstance(i, ast.Token) and (
1360
- i.name == Tok.KW_LET or i.gen.jac == ":"
1382
+ self.emit(node, kid.gen.jac)
1383
+ elif isinstance(kid, ast.Token) and (
1384
+ kid.name == Tok.KW_LET or kid.gen.jac == ":"
1361
1385
  ):
1362
- self.emit(node, f"{i.gen.jac} ")
1363
- elif isinstance(i, ast.Token) and "=" in i.gen.jac:
1364
- self.emit(node, f" {i.gen.jac} ")
1386
+ self.emit(node, f"{kid.gen.jac} ")
1387
+ elif isinstance(kid, ast.Token) and "=" in kid.gen.jac:
1388
+ self.emit(node, f" {kid.gen.jac} ")
1389
+ elif (
1390
+ "=" in kid.gen.jac
1391
+ and self.is_line_break_needed(
1392
+ kid.gen.jac, max_line_length=self.MAX_LINE_LENGTH * 2
1393
+ )
1394
+ and "\n" not in kid.gen.jac
1395
+ ):
1396
+ self.handle_long_assignment(node, kid)
1397
+ elif (
1398
+ prev_token
1399
+ and "=" in prev_token.gen.jac
1400
+ and self.is_line_break_needed(
1401
+ kid.gen.jac, max_line_length=self.MAX_LINE_LENGTH * 2
1402
+ )
1403
+ and "\n" not in kid.gen.jac
1404
+ ):
1405
+ self.handle_long_expression(node, kid)
1365
1406
  else:
1366
- self.emit(node, i.gen.jac)
1407
+ self.emit(node, kid.gen.jac)
1408
+ prev_token = kid
1367
1409
  if isinstance(
1368
1410
  node.kid[-1], (ast.Semi, ast.CommentToken)
1369
1411
  ) and not node.gen.jac.endswith("\n"):
@@ -1392,8 +1434,12 @@ class JacFormatPass(Pass):
1392
1434
  self.emit(node, i.gen.jac)
1393
1435
  if isinstance(prev_token, ast.Semi):
1394
1436
  self.emit_ln(node, "")
1437
+ elif (tok := self.token_before(i)) and (i.line_no - tok.line_no > 1):
1438
+ self.emit_ln(node, "")
1439
+ self.emit_ln(node, i.gen.jac)
1395
1440
  else:
1396
1441
  self.emit_ln(node, i.gen.jac)
1442
+ self.emit_ln(node, "")
1397
1443
  elif isinstance(i, ast.Semi):
1398
1444
  self.emit(node, f"{i.gen.jac} ")
1399
1445
  elif isinstance(i, ast.SubNodeList) and i.gen.jac.startswith("@"):
@@ -2374,7 +2420,7 @@ class JacFormatPass(Pass):
2374
2420
  """
2375
2421
  self.emit(node, f"<>{node.value}" if node.is_kwesc else node.value)
2376
2422
 
2377
- def enter_float(self, node: ast.Float) -> None:
2423
+ def exit_float(self, node: ast.Float) -> None:
2378
2424
  """Sub objects.
2379
2425
 
2380
2426
  name: str,
@@ -2387,7 +2433,7 @@ class JacFormatPass(Pass):
2387
2433
  """
2388
2434
  self.emit(node, node.value)
2389
2435
 
2390
- def enter_int(self, node: ast.Int) -> None:
2436
+ def exit_int(self, node: ast.Int) -> None:
2391
2437
  """Sub objects.
2392
2438
 
2393
2439
  name: str,
@@ -2400,7 +2446,7 @@ class JacFormatPass(Pass):
2400
2446
  """
2401
2447
  self.emit(node, node.value)
2402
2448
 
2403
- def enter_string(self, node: ast.String) -> None:
2449
+ def exit_string(self, node: ast.String) -> None:
2404
2450
  """Sub objects.
2405
2451
 
2406
2452
  name: str,
@@ -2412,7 +2458,11 @@ class JacFormatPass(Pass):
2412
2458
  pos_end: int,
2413
2459
  """
2414
2460
  # if string is in docstring format and spans multiple lines turn into the multiple single quoted strings
2415
- if "\n" in node.value and node.parent and isinstance(node.parent, ast.Expr):
2461
+ if "\n" in node.value and (
2462
+ node.parent
2463
+ and isinstance(node.parent, ast.Expr)
2464
+ and not isinstance(node.parent, ast.MultiString)
2465
+ ):
2416
2466
  string_type = node.value[0:3]
2417
2467
  pure_string = node.value[3:-3]
2418
2468
  lines = pure_string.split("\n")
@@ -2430,14 +2480,14 @@ class JacFormatPass(Pass):
2430
2480
  string_type = node.value[0:3]
2431
2481
  pure_string = node.value[3:-3]
2432
2482
  lines = pure_string.split("\n")
2433
- self.emit(node, string_type)
2434
- for line in lines[:-1]:
2435
- self.emit_ln(node, line)
2436
- self.emit_ln(node, f"{lines[-1]}{string_type}")
2483
+ self.emit_ln(node, f"{string_type}{lines[0].lstrip()}")
2484
+ for line in lines[1:-1]:
2485
+ self.emit_ln(node, line.lstrip())
2486
+ self.emit(node, f"{lines[-1].lstrip()}{string_type}")
2437
2487
  else:
2438
2488
  self.emit(node, node.value)
2439
2489
 
2440
- def enter_bool(self, node: ast.Bool) -> None:
2490
+ def exit_bool(self, node: ast.Bool) -> None:
2441
2491
  """Sub objects.
2442
2492
 
2443
2493
  name: str,