jaclang 0.7.14__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 (92) 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 +86 -13
  5. jaclang/compiler/jac.lark +4 -3
  6. jaclang/compiler/parser.py +31 -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 +18 -23
  11. jaclang/compiler/passes/main/pyast_gen_pass.py +307 -559
  12. jaclang/compiler/passes/main/pyast_load_pass.py +32 -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/type_check_pass.py +7 -0
  17. jaclang/compiler/passes/tool/jac_formatter_pass.py +124 -86
  18. jaclang/compiler/passes/tool/tests/fixtures/corelib_fmt.jac +0 -1
  19. jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/architype_test.jac +13 -0
  20. jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/comment_alignment.jac +11 -0
  21. jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/comments.jac +13 -0
  22. jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/decorator_stack.jac +37 -0
  23. jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/esc_keywords.jac +5 -0
  24. jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/long_names.jac +19 -0
  25. jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/triple_quoted_string.jac +6 -0
  26. jaclang/compiler/passes/tool/tests/test_jac_format_pass.py +11 -0
  27. jaclang/compiler/passes/tool/tests/test_unparse_validate.py +33 -39
  28. jaclang/compiler/passes/transform.py +4 -0
  29. jaclang/compiler/semtable.py +31 -7
  30. jaclang/compiler/tests/test_importer.py +12 -5
  31. jaclang/langserve/engine.py +65 -118
  32. jaclang/langserve/sem_manager.py +379 -0
  33. jaclang/langserve/server.py +8 -10
  34. jaclang/langserve/tests/fixtures/base_module_structure.jac +27 -6
  35. jaclang/langserve/tests/fixtures/circle.jac +3 -3
  36. jaclang/langserve/tests/fixtures/circle_err.jac +3 -3
  37. jaclang/langserve/tests/fixtures/circle_pure.test.jac +3 -3
  38. jaclang/langserve/tests/fixtures/import_include_statements.jac +1 -1
  39. jaclang/langserve/tests/test_sem_tokens.py +277 -0
  40. jaclang/langserve/tests/test_server.py +72 -16
  41. jaclang/langserve/utils.py +163 -96
  42. jaclang/plugin/builtin.py +1 -1
  43. jaclang/plugin/default.py +212 -24
  44. jaclang/plugin/feature.py +59 -11
  45. jaclang/plugin/spec.py +58 -6
  46. jaclang/{core → runtimelib}/architype.py +1 -1
  47. jaclang/{core → runtimelib}/context.py +8 -1
  48. jaclang/runtimelib/importer.py +361 -0
  49. jaclang/runtimelib/machine.py +94 -0
  50. jaclang/{core → runtimelib}/utils.py +13 -5
  51. jaclang/settings.py +4 -1
  52. jaclang/tests/fixtures/abc.jac +3 -3
  53. jaclang/tests/fixtures/byllmissue.jac +1 -5
  54. jaclang/tests/fixtures/chandra_bugs2.jac +11 -10
  55. jaclang/tests/fixtures/cls_method.jac +41 -0
  56. jaclang/tests/fixtures/dblhello.jac +6 -0
  57. jaclang/tests/fixtures/deep/one_lev.jac +3 -3
  58. jaclang/tests/fixtures/deep/one_lev_dup.jac +2 -3
  59. jaclang/tests/fixtures/deep_import_mods.jac +13 -0
  60. jaclang/tests/fixtures/err.impl.jac +3 -0
  61. jaclang/tests/fixtures/err.jac +4 -2
  62. jaclang/tests/fixtures/err.test.jac +3 -0
  63. jaclang/tests/fixtures/err_runtime.jac +15 -0
  64. jaclang/tests/fixtures/game1.jac +1 -1
  65. jaclang/tests/fixtures/hello.jac +4 -0
  66. jaclang/tests/fixtures/impl_grab.impl.jac +2 -1
  67. jaclang/tests/fixtures/impl_grab.jac +4 -1
  68. jaclang/tests/fixtures/jp_importer_auto.jac +14 -0
  69. jaclang/tests/fixtures/maxfail_run_test.jac +4 -4
  70. jaclang/tests/fixtures/needs_import.jac +2 -2
  71. jaclang/tests/fixtures/pyfunc_2.py +3 -0
  72. jaclang/tests/fixtures/registry.jac +9 -0
  73. jaclang/tests/fixtures/run_test.jac +4 -4
  74. jaclang/tests/fixtures/semstr.jac +1 -4
  75. jaclang/tests/fixtures/simple_archs.jac +1 -1
  76. jaclang/tests/test_cli.py +65 -2
  77. jaclang/tests/test_language.py +74 -7
  78. jaclang/tests/test_reference.py +6 -0
  79. jaclang/utils/helpers.py +45 -21
  80. jaclang/utils/test.py +9 -0
  81. jaclang/utils/treeprinter.py +0 -4
  82. {jaclang-0.7.14.dist-info → jaclang-0.7.16.dist-info}/METADATA +3 -2
  83. {jaclang-0.7.14.dist-info → jaclang-0.7.16.dist-info}/RECORD +89 -74
  84. jaclang/core/importer.py +0 -344
  85. jaclang/tests/fixtures/aott_raise.jac +0 -25
  86. jaclang/tests/fixtures/package_import.jac +0 -6
  87. /jaclang/{core → runtimelib}/__init__.py +0 -0
  88. /jaclang/{core → runtimelib}/constructs.py +0 -0
  89. /jaclang/{core → runtimelib}/memory.py +0 -0
  90. /jaclang/{core → runtimelib}/test.py +0 -0
  91. {jaclang-0.7.14.dist-info → jaclang-0.7.16.dist-info}/WHEEL +0 -0
  92. {jaclang-0.7.14.dist-info → jaclang-0.7.16.dist-info}/entry_points.txt +0 -0
@@ -144,12 +144,15 @@ class PyastBuildPass(Pass[ast.PythonModuleAst]):
144
144
  if sys.version_info >= (3, 12):
145
145
  type_params: list[type_param]
146
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}"
147
152
  name = ast.Name(
148
153
  file_path=self.mod_path,
149
154
  name=Tok.NAME,
150
- value=(
151
- node.name if node.name != "root" else "root_"
152
- ), # root is a reserved keyword
155
+ value=value,
153
156
  line=node.lineno,
154
157
  end_line=node.end_lineno if node.end_lineno else node.lineno,
155
158
  col_start=node.col_offset,
@@ -1875,10 +1878,19 @@ class PyastBuildPass(Pass[ast.PythonModuleAst]):
1875
1878
  id: _Identifier
1876
1879
  ctx: expr_context
1877
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}"
1878
1890
  ret = ast.Name(
1879
1891
  file_path=self.mod_path,
1880
1892
  name=Tok.NAME,
1881
- value=node.id if node.id != "root" else "root_", # reserved word
1893
+ value=value,
1882
1894
  line=node.lineno,
1883
1895
  end_line=node.end_lineno if node.end_lineno else node.lineno,
1884
1896
  col_start=node.col_offset,
@@ -1913,13 +1925,18 @@ class PyastBuildPass(Pass[ast.PythonModuleAst]):
1913
1925
  class Nonlocal(stmt):
1914
1926
  names: list[_Identifier]
1915
1927
  """
1928
+ from jaclang.compiler import TOKEN_MAP
1929
+
1930
+ reserved_keywords = [v for _, v in TOKEN_MAP.items()]
1931
+
1916
1932
  names: list[ast.NameAtom] = []
1917
1933
  for name in node.names:
1934
+ value = name if name not in reserved_keywords else f"<>{name}"
1918
1935
  names.append(
1919
1936
  ast.Name(
1920
1937
  file_path=self.mod_path,
1921
1938
  name=Tok.NAME,
1922
- value=name if name != "root" else "root_",
1939
+ value=value,
1923
1940
  line=node.lineno,
1924
1941
  end_line=node.end_lineno if node.end_lineno else node.lineno,
1925
1942
  col_start=node.col_offset,
@@ -2238,10 +2255,19 @@ class PyastBuildPass(Pass[ast.PythonModuleAst]):
2238
2255
  arg: _Identifier
2239
2256
  annotation: expr | None
2240
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}"
2241
2267
  name = ast.Name(
2242
2268
  file_path=self.mod_path,
2243
2269
  name=Tok.NAME,
2244
- value=node.arg if node.arg != "root" else "root_", # reserved word
2270
+ value=value,
2245
2271
  line=node.lineno,
2246
2272
  end_line=node.end_lineno if node.end_lineno else node.lineno,
2247
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."""
@@ -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))
@@ -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,7 +21,7 @@ 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."""
@@ -49,7 +50,8 @@ class JacFormatPass(Pass):
49
50
 
50
51
  def emit(self, node: ast.AstNode, s: str, strip_mode: bool = True) -> None:
51
52
  """Emit code to node."""
52
- 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
53
55
  if "\n" in node.gen.jac:
54
56
  if strip_mode:
55
57
  node.gen.jac = node.gen.jac.rstrip(" ")
@@ -129,9 +131,15 @@ class JacFormatPass(Pass):
129
131
  else:
130
132
  if isinstance(last_element, ast.Import):
131
133
  self.emit_ln(node, "")
132
- self.emit_ln(node, i.gen.jac)
133
- 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
+ ):
134
140
  self.emit_ln(node, "")
141
+ self.emit_ln(node, i.gen.jac)
142
+
135
143
  if counter <= len(node.body) - 1:
136
144
  if (
137
145
  isinstance(i, ast.Ability)
@@ -142,6 +150,7 @@ class JacFormatPass(Pass):
142
150
  and len(node.body[counter].kid[-1].kid) == 2
143
151
  and len(node.body[counter - 1].kid[-1].kid) == 2
144
152
  )
153
+ and node.gen.jac.endswith("\n")
145
154
  ):
146
155
  self.emit(node, "")
147
156
  else:
@@ -212,7 +221,7 @@ class JacFormatPass(Pass):
212
221
  items: list[T],
213
222
  """
214
223
  prev_token = None
215
- for i, stmt in enumerate(node.kid):
224
+ for stmt in node.kid:
216
225
  if isinstance(node.parent, (ast.EnumDef, ast.Enum)) and stmt.gen.jac == ",":
217
226
  self.indent_level -= 1
218
227
  self.emit_ln(node, f"{stmt.gen.jac}")
@@ -238,42 +247,18 @@ class JacFormatPass(Pass):
238
247
  if stmt.name == Tok.LBRACE:
239
248
  self.emit(node, f" {stmt.value}")
240
249
  elif stmt.name == Tok.RBRACE:
241
- if self.indent_level > 0:
242
- self.indent_level -= 1
243
- if (stmt.parent and stmt.parent.gen.jac.strip() == "{") or (
244
- stmt.parent
245
- and stmt.parent.parent
246
- and isinstance(
247
- stmt.parent.parent,
248
- (ast.ElseIf, ast.IfStmt, ast.IterForStmt, ast.TryStmt),
249
- )
250
- ):
250
+ self.indent_level = max(0, self.indent_level - 1)
251
+ if stmt.parent and stmt.parent.gen.jac.strip() == "{":
251
252
  self.emit(node, f"{stmt.value}")
252
253
  else:
253
- next_kid = (
254
- node.kid[i + 1]
255
- if i < (len(node.kid) - 1)
256
- else ast.EmptyToken()
257
- )
258
- if (
259
- isinstance(next_kid, ast.CommentToken)
260
- and next_kid.is_inline
261
- ):
262
- self.emit(node, f" {stmt.value}")
263
- elif not (node.gen.jac).endswith("\n"):
254
+ if not node.gen.jac.endswith("\n"):
264
255
  self.emit_ln(node, "")
265
- self.emit(node, f"{stmt.value}")
266
- else:
267
- self.emit(node, f"{stmt.value}")
256
+ self.emit(node, f"{stmt.value}")
268
257
  elif isinstance(stmt, ast.CommentToken):
269
258
  if stmt.is_inline:
270
259
  if isinstance(prev_token, ast.Semi) or (
271
260
  isinstance(prev_token, ast.Token)
272
- and prev_token.name
273
- in [
274
- Tok.LBRACE,
275
- Tok.RBRACE,
276
- ]
261
+ and prev_token.name in [Tok.LBRACE, Tok.RBRACE]
277
262
  ):
278
263
  self.indent_level -= 1
279
264
  self.emit(node, f" {stmt.gen.jac}")
@@ -297,48 +282,33 @@ class JacFormatPass(Pass):
297
282
  self.emit(node, stmt.gen.jac)
298
283
  if not stmt.gen.jac.endswith("postinit;"):
299
284
  self.indent_level -= 1
285
+ self.emit_ln(stmt, "")
300
286
  self.emit_ln(node, "")
301
287
  self.indent_level += 1
302
288
  elif stmt.gen.jac == ",":
303
289
  self.emit(node, f"{stmt.value} ")
304
290
  elif stmt.value == "=":
305
291
  self.emit(node, f" {stmt.value} ")
292
+ elif prev_token and prev_token.gen.jac.strip() == "@":
293
+ self.emit_ln(node, stmt.value)
306
294
  else:
307
- self.emit(node, f"{stmt.value}")
295
+ self.emit(node, f"{stmt.gen.jac}")
308
296
  prev_token = stmt
309
297
  continue
310
298
  elif isinstance(stmt, ast.Semi):
311
299
  self.emit(node, stmt.gen.jac)
312
- elif isinstance(prev_token, (ast.HasVar, ast.ArchHas)) and not isinstance(
313
- 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))
314
306
  ):
315
307
  if not isinstance(prev_token.kid[-1], ast.CommentToken):
316
308
  self.indent_level -= 1
317
309
  self.emit_ln(node, "")
318
310
  self.indent_level += 1
319
311
  self.emit(node, stmt.gen.jac)
320
- elif isinstance(prev_token, ast.Ability) and isinstance(
321
- stmt, (ast.Ability, ast.AbilityDef)
322
- ):
323
- if not isinstance(prev_token.kid[-1], ast.CommentToken) and (
324
- stmt.body and not isinstance(stmt.body, ast.FuncCall)
325
- ):
326
- self.indent_level -= 1
327
- self.emit_ln(node, "")
328
- self.indent_level += 1
329
- self.emit(node, f"{stmt.gen.jac}")
330
- elif stmt.body and isinstance(
331
- stmt.body, (ast.FuncCall, ast.EventSignature)
332
- ):
333
- self.indent_level -= 1
334
- self.emit_ln(node, "")
335
- self.indent_level += 1
336
- self.emit(node, stmt.gen.jac)
337
- else:
338
- self.indent_level -= 1
339
- self.emit_ln(node, "")
340
- self.indent_level += 1
341
- self.emit(node, f"{stmt.gen.jac}")
342
312
  else:
343
313
  if prev_token and prev_token.gen.jac.strip() == "{":
344
314
  self.emit_ln(node, "")
@@ -763,7 +733,7 @@ class JacFormatPass(Pass):
763
733
  self.indent_level += indent_val
764
734
  indented = True
765
735
  else:
766
- self.emit(node, f"{j.gen.jac.strip()}")
736
+ self.emit(node, j.gen.jac.lstrip())
767
737
  if indented:
768
738
  self.indent_level -= indent_val
769
739
  else:
@@ -929,6 +899,8 @@ class JacFormatPass(Pass):
929
899
  """Check if the length of the current generated code exceeds the max line length."""
930
900
  if max_line_length == 0:
931
901
  max_line_length = self.MAX_LINE_LENGTH
902
+ # print(content)
903
+ # print(len(content))
932
904
  return len(content) > max_line_length
933
905
 
934
906
  def exit_binary_expr(self, node: ast.BinaryExpr) -> None:
@@ -978,6 +950,7 @@ class JacFormatPass(Pass):
978
950
  self.error(
979
951
  f"Binary operator {node.op.value} not supported in bootstrap Jac"
980
952
  )
953
+ # print(node.gen)
981
954
  if isinstance(
982
955
  node.kid[-1], (ast.Semi, ast.CommentToken)
983
956
  ) and not node.gen.jac.endswith("\n"):
@@ -1110,6 +1083,9 @@ class JacFormatPass(Pass):
1110
1083
  if isinstance(i, ast.CommentToken):
1111
1084
  if i.is_inline:
1112
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)
1113
1089
  else:
1114
1090
  self.emit_ln(node, "")
1115
1091
  self.emit_ln(node, "")
@@ -1340,38 +1316,96 @@ class JacFormatPass(Pass):
1340
1316
  if isinstance(node.kid[-1], (ast.Semi, ast.CommentToken)):
1341
1317
  self.emit_ln(node, "")
1342
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
+
1343
1356
  def exit_assignment(self, node: ast.Assignment) -> None:
1344
1357
  """Sub objects.
1345
1358
 
1346
- target: SubNodeList[AtomType],
1347
- value: Optional[ExprType | YieldStmt],
1348
- type_tag: Optional[SubTag[ExprType]],
1359
+ target: SubNodeList[Expr],
1360
+ value: Optional[Expr | YieldExpr],
1361
+ type_tag: Optional[SubTag[Expr]],
1349
1362
  mutable: bool = True,
1350
- aug_op: Optional[Token] = None
1363
+ aug_op: Optional[Token] = None,
1364
+ semstr: Optional[String] = None,
1365
+ is_enum_stmt: bool = False,
1351
1366
  """
1352
- for i in node.kid:
1353
- if isinstance(i, ast.CommentToken):
1354
- if i.is_inline:
1355
- 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)
1356
1372
  else:
1357
- if i.gen.jac not in [
1373
+ if kid.gen.jac not in [
1358
1374
  "# Update any new user level buddy schedule",
1359
1375
  "# Construct prompt here",
1360
1376
  ]:
1361
1377
  self.emit_ln(node, "")
1362
1378
  self.emit_ln(node, "")
1363
- self.emit_ln(node, i.gen.jac)
1379
+ self.emit_ln(node, kid.gen.jac)
1364
1380
  else:
1365
1381
  self.emit_ln(node, "")
1366
- self.emit(node, i.gen.jac)
1367
- elif isinstance(i, ast.Token) and (
1368
- 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 == ":"
1369
1385
  ):
1370
- self.emit(node, f"{i.gen.jac} ")
1371
- elif isinstance(i, ast.Token) and "=" in i.gen.jac:
1372
- 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)
1373
1406
  else:
1374
- self.emit(node, i.gen.jac)
1407
+ self.emit(node, kid.gen.jac)
1408
+ prev_token = kid
1375
1409
  if isinstance(
1376
1410
  node.kid[-1], (ast.Semi, ast.CommentToken)
1377
1411
  ) and not node.gen.jac.endswith("\n"):
@@ -2386,7 +2420,7 @@ class JacFormatPass(Pass):
2386
2420
  """
2387
2421
  self.emit(node, f"<>{node.value}" if node.is_kwesc else node.value)
2388
2422
 
2389
- def enter_float(self, node: ast.Float) -> None:
2423
+ def exit_float(self, node: ast.Float) -> None:
2390
2424
  """Sub objects.
2391
2425
 
2392
2426
  name: str,
@@ -2399,7 +2433,7 @@ class JacFormatPass(Pass):
2399
2433
  """
2400
2434
  self.emit(node, node.value)
2401
2435
 
2402
- def enter_int(self, node: ast.Int) -> None:
2436
+ def exit_int(self, node: ast.Int) -> None:
2403
2437
  """Sub objects.
2404
2438
 
2405
2439
  name: str,
@@ -2412,7 +2446,7 @@ class JacFormatPass(Pass):
2412
2446
  """
2413
2447
  self.emit(node, node.value)
2414
2448
 
2415
- def enter_string(self, node: ast.String) -> None:
2449
+ def exit_string(self, node: ast.String) -> None:
2416
2450
  """Sub objects.
2417
2451
 
2418
2452
  name: str,
@@ -2424,7 +2458,11 @@ class JacFormatPass(Pass):
2424
2458
  pos_end: int,
2425
2459
  """
2426
2460
  # if string is in docstring format and spans multiple lines turn into the multiple single quoted strings
2427
- 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
+ ):
2428
2466
  string_type = node.value[0:3]
2429
2467
  pure_string = node.value[3:-3]
2430
2468
  lines = pure_string.split("\n")
@@ -2442,14 +2480,14 @@ class JacFormatPass(Pass):
2442
2480
  string_type = node.value[0:3]
2443
2481
  pure_string = node.value[3:-3]
2444
2482
  lines = pure_string.split("\n")
2445
- self.emit(node, string_type)
2446
- for line in lines[:-1]:
2447
- self.emit_ln(node, line)
2448
- 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}")
2449
2487
  else:
2450
2488
  self.emit(node, node.value)
2451
2489
 
2452
- def enter_bool(self, node: ast.Bool) -> None:
2490
+ def exit_bool(self, node: ast.Bool) -> None:
2453
2491
  """Sub objects.
2454
2492
 
2455
2493
  name: str,
@@ -16,7 +16,6 @@ obj Memory {
16
16
  can save_obj(caller_id: UUID, item: Element);
17
17
  can del_obj(caller_id: UUID, item: Element);
18
18
  #* Utility Functions *#
19
-
20
19
  can get_object_distribution -> dict;
21
20
  can get_mem_size -> float;
22
21
  }
@@ -0,0 +1,13 @@
1
+ class Animal {}
2
+
3
+ obj Domesticated {}
4
+
5
+ @print_base_classes
6
+ node Pet :Animal, Domesticated: {}
7
+
8
+ walker Person :Animal: {}
9
+
10
+ walker Feeder :Person: {}
11
+
12
+ @print_base_classes
13
+ walker Zoologist :Feeder: {}
@@ -0,0 +1,11 @@
1
+ obj inner_red {
2
+ has color22: string = 'red',
3
+ # self
4
+ doublepoint11: int = 2,
5
+ doublepoint22: float = 2.0,
6
+ # self
7
+ doublepoint33: int = 2,
8
+ # Comments explaining each attribute
9
+ # color22: represents the color, set to 'red'
10
+ point22: int = 20;
11
+ }
@@ -0,0 +1,13 @@
1
+ with entry:__main__ {
2
+ app = App();
3
+ app.run();
4
+ # ray.init_window(800, 450, "Hello");
5
+ # while not ray.window_should_close() {
6
+ # ray.begin_drawing();
7
+ # ray.clear_background(ray.WHITE);
8
+ # ray.draw_text("Hello world", 190, 200, 20, ray.VIOLET);
9
+ # ray.end_drawing();
10
+ # }
11
+
12
+ # ray.close_window();
13
+ }
@@ -0,0 +1,37 @@
1
+ can star(func: Any) {
2
+ can inner(x: Any) {
3
+ print(("*" * 30));
4
+ func(x);
5
+ print(("*" * 30));
6
+ }
7
+ return inner;
8
+ }
9
+
10
+ can percent(func: Any) {
11
+ can inner(y: Any) {
12
+ print(("%" * 30));
13
+ func(y);
14
+ print(("%" * 30));
15
+ }
16
+ return inner;
17
+ }
18
+
19
+ can percent2(func: Any) {
20
+ can inner(y: Any) {
21
+ print(("-" * 30));
22
+ func(y);
23
+ print(("+" * 30));
24
+ }
25
+ return inner;
26
+ }
27
+
28
+ @star
29
+ @percent
30
+ @percent2
31
+ can printer(msg: Any) {
32
+ print(msg);
33
+ }
34
+
35
+ with entry {
36
+ printer("Hello");
37
+ }
@@ -0,0 +1,5 @@
1
+ with entry {
2
+ <>self = 5;
3
+ <>self += <>self;
4
+ print(<>self);
5
+ }