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
@@ -3,6 +3,11 @@
3
3
  PyrightReference: packages/pyright-internal/src/analyzer/typeUtils.ts
4
4
  """
5
5
 
6
+ from dataclasses import dataclass
7
+ from enum import Enum
8
+
9
+ import jaclang.compiler.unitree as uni
10
+ from jaclang.compiler.constant import Tokens as Tok
6
11
  from jaclang.compiler.unitree import Symbol
7
12
 
8
13
  from . import types
@@ -39,3 +44,244 @@ class ClassMember:
39
44
  # True if member lookup skipped an undeclared (inferred) type
40
45
  # in a subclass before finding a declared type in a base class
41
46
  self.skipped_undeclared_type = False
47
+
48
+
49
+ def compute_mro_linearization(cls: types.ClassType) -> None:
50
+ """Compute the method resolution order (MRO) for a class type.
51
+
52
+ This uses the C3 linearization algorithm to compute the MRO.
53
+ See https://www.python.org/download/releases/2.3/mro/
54
+ """
55
+ if cls.shared.mro:
56
+ return
57
+
58
+ # FIXME: This is an ad-hoc implementation to make the MRO works
59
+ # and it'll cover the 90% or more of the user cases.
60
+
61
+ # Computer MRO for base classes first
62
+ cls.shared.mro.append(cls)
63
+ for base in cls.shared.base_classes:
64
+ if isinstance(base, types.ClassType):
65
+ compute_mro_linearization(base)
66
+
67
+ # Then add base classes and their MROs
68
+ for base in cls.shared.base_classes:
69
+ if isinstance(base, types.ClassType):
70
+ for mro_cls in base.shared.mro:
71
+ if mro_cls not in cls.shared.mro:
72
+ cls.shared.mro.append(mro_cls)
73
+
74
+
75
+ # In pyright, this class lives in the parameterUtils.ts however we're
76
+ # putting it here for now and if the scale of the code grows we can
77
+ # split it into a separate file.
78
+ class ParamAssignmentTracker:
79
+ """Tracks parameter assignments for function calls.
80
+
81
+ This class helps in tracking which parameters have been matched
82
+ with arguments in a function call. It supports positional, named,
83
+ *args, and **kwargs arguments.
84
+ """
85
+
86
+ def __init__(self, params: list[types.Parameter]) -> None:
87
+ """Initialize obviously."""
88
+ self.params = params
89
+ self.curr_param_idx = 0
90
+ self.matched_params: set[types.Parameter] = set()
91
+
92
+ # Quick access to vararg and kwarg parameters
93
+ self.varargs: types.Parameter | None = None
94
+ self.kwargs: types.Parameter | None = None
95
+
96
+ # "Cache" vararg and kwarg parameters for quick access
97
+ for param in self.params:
98
+ if param.param_kind == types.ParamKind.VARARG:
99
+ self.varargs = param
100
+ elif param.param_kind == types.ParamKind.KWARG:
101
+ self.kwargs = param
102
+
103
+ def lookup_named_parameter(self, param_name: str) -> types.Parameter | None:
104
+ """Lookup a named parameter by name and if any match is found we mark it as such."""
105
+ for param in self.params:
106
+ # User try to pass a positional only parameter with a name, should be an error.
107
+ if (param.param_kind == types.ParamKind.POSONLY) and (
108
+ param_name == param.name
109
+ ):
110
+ self.matched_params.add(param)
111
+ raise Exception(
112
+ f"Positional only parameter '{param.name}' cannot be matched with a named argument"
113
+ )
114
+ if param.param_kind not in (types.ParamKind.NORMAL, types.ParamKind.KWONLY):
115
+ continue
116
+ if param.name == param_name:
117
+ if param in self.matched_params:
118
+ raise Exception(f"Parameter '{param.name}' already matched")
119
+ self.matched_params.add(param)
120
+ return param
121
+ # If we reached here, there is no named parameter to match with
122
+ # however if we have **kwargs, we can match it with that, if that
123
+ # also None, it'll return None indicating no match.
124
+ return self.kwargs
125
+
126
+ def _mark_all_named_params_as_matched(self) -> None:
127
+ """Mark all named parameters as matched."""
128
+ for param in self.params:
129
+ if param.param_kind in (
130
+ types.ParamKind.NORMAL,
131
+ types.ParamKind.KWONLY,
132
+ types.ParamKind.KWARG,
133
+ ):
134
+ self.matched_params.add(param)
135
+
136
+ def _mark_all_positional_params_as_matched(self) -> None:
137
+ """Mark all positional parameters as matched."""
138
+ for param in self.params:
139
+ if param.param_kind in (
140
+ types.ParamKind.POSONLY,
141
+ types.ParamKind.NORMAL,
142
+ types.ParamKind.VARARG,
143
+ ):
144
+ self.matched_params.add(param)
145
+ self.curr_param_idx = -1
146
+
147
+ def match_named_argument(self, arg: uni.KWPair) -> types.Parameter | None:
148
+ """Match a named argument to a parameter."""
149
+ if arg.key is None:
150
+ # **{} <- This can be matched with any named parameter
151
+ # in the current parameter list.
152
+ self._mark_all_named_params_as_matched()
153
+ return None
154
+ else:
155
+ if param := self.lookup_named_parameter(arg.key.sym_name):
156
+ return param
157
+ raise Exception(
158
+ f"Named argument '{arg.key.sym_name}' does not match any parameter"
159
+ )
160
+
161
+ def match_positional_argument(self, arg: uni.Expr) -> types.Parameter | None:
162
+ """Match a positional argument to a parameter."""
163
+ # NOTE: The curr_param_idx can be -1 only when a unpack (*expr) is passed
164
+ # at that point there is no reliable way to match the remaining positional
165
+ # arguments after the unpack, so we either match with *args or not match with
166
+ # anything.
167
+ if self.curr_param_idx == -1:
168
+ return self.varargs
169
+
170
+ if isinstance(arg, uni.UnaryExpr) and arg.op.value == Tok.STAR_MUL:
171
+ self._mark_all_positional_params_as_matched()
172
+ return None
173
+ else:
174
+ if self.curr_param_idx < len(self.params):
175
+ param = self.params[self.curr_param_idx]
176
+ if param.param_kind == types.ParamKind.VARARG:
177
+ self.matched_params.add(param)
178
+ return param
179
+ if param.param_kind in (
180
+ types.ParamKind.NORMAL,
181
+ types.ParamKind.POSONLY,
182
+ ):
183
+ self.curr_param_idx += 1
184
+ self.matched_params.add(param)
185
+ return param
186
+
187
+ # If we reached here, there is no parameter to match with
188
+ raise Exception("Too many positional arguments")
189
+
190
+ def get_unmatched_required_params(self) -> list[types.Parameter]:
191
+ """Check if there are any unmatched required parameters."""
192
+ ret: list[types.Parameter] = []
193
+ for param in self.params:
194
+ if (
195
+ param not in self.matched_params
196
+ and param.default_value is None
197
+ and param.param_kind
198
+ not in (types.ParamKind.VARARG, types.ParamKind.KWARG)
199
+ ):
200
+ ret.append(param)
201
+ return ret
202
+
203
+
204
+ # -----------------------------------------------------------------------------
205
+ # Completion item utils
206
+ # -----------------------------------------------------------------------------
207
+
208
+
209
+ @dataclass
210
+ class CompletionItem:
211
+ """A completion item."""
212
+
213
+ label: str
214
+ kind: int
215
+ detail: str | None = None
216
+
217
+
218
+ class CompletionItemKind(int, Enum):
219
+ """The kind of a completion entry."""
220
+
221
+ Text = 1
222
+ Method = 2
223
+ Function = 3
224
+ Constructor = 4
225
+ Field = 5
226
+ Variable = 6
227
+ Class = 7
228
+ Interface = 8
229
+ Module = 9
230
+ Property = 10
231
+ Unit = 11
232
+ Value = 12
233
+ Enum = 13
234
+ Keyword = 14
235
+ Snippet = 15
236
+ Color = 16
237
+ File = 17
238
+ Reference = 18
239
+ Folder = 19
240
+ EnumMember = 20
241
+ Constant = 21
242
+ Struct = 22
243
+ Event = 23
244
+ Operator = 24
245
+ TypeParameter = 25
246
+
247
+
248
+ def completion_kind_from_sym(sym: Symbol) -> int:
249
+ """Get the completion item kind from a symbol."""
250
+ match sym.decl.name_of:
251
+ case uni.ModulePath():
252
+ return CompletionItemKind.Module
253
+ case uni.Ability():
254
+ return CompletionItemKind.Function
255
+ case uni.Archetype():
256
+ return CompletionItemKind.Class
257
+ case uni.Enum():
258
+ return CompletionItemKind.Enum
259
+ case uni.HasVar():
260
+ return CompletionItemKind.Variable
261
+ return CompletionItemKind.Text
262
+
263
+
264
+ def get_completion_items(ty: types.TypeBase | uni.UniScopeNode) -> list[CompletionItem]:
265
+ """Return a list of completion items for the type."""
266
+ ret = []
267
+
268
+ if isinstance(ty, uni.UniScopeNode):
269
+ scope = ty
270
+ while scope:
271
+ for name, sym in scope.names_in_scope.items():
272
+ kind = completion_kind_from_sym(sym)
273
+ ret.append(CompletionItem(label=name, kind=kind))
274
+ scope = scope.parent_scope
275
+
276
+ elif isinstance(ty, types.ClassType):
277
+ for cls in ty.shared.mro:
278
+ for name, sym in cls.shared.symbol_table.names_in_scope.items():
279
+ kind = completion_kind_from_sym(sym)
280
+ ret.append(CompletionItem(label=name, kind=kind))
281
+
282
+ elif isinstance(ty, types.ModuleType):
283
+ for name, sym in ty.symbol_table.names_in_scope.items():
284
+ kind = completion_kind_from_sym(sym)
285
+ ret.append(CompletionItem(label=name, kind=kind))
286
+
287
+ return ret
@@ -9,7 +9,7 @@ from pathlib import Path
9
9
  from typing import ClassVar, TYPE_CHECKING
10
10
 
11
11
  if TYPE_CHECKING:
12
- from jaclang.compiler.unitree import Symbol, UniScopeNode as SymbolTable
12
+ from jaclang.compiler.unitree import Expr, Symbol, UniScopeNode as SymbolTable
13
13
 
14
14
 
15
15
  class TypeCategory(IntEnum):
@@ -163,11 +163,25 @@ class ClassType(TypeBase):
163
163
  class_name: str,
164
164
  symbol_table: SymbolTable,
165
165
  base_classes: list[TypeBase] | None = None,
166
+ is_builtin_class: bool = False,
166
167
  ) -> None:
167
168
  """Initialize obviously."""
168
169
  self.class_name = class_name
169
170
  self.symbol_table = symbol_table
170
171
  self.base_classes = base_classes or []
172
+ self.mro: list[ClassType] = []
173
+
174
+ # In pyright classes have ClassTypeFlags to indicate if it's builtin
175
+ # along with other information, I'm adding only the builtin flag for now.
176
+ # add the other flags if needed in the future with a bitmask enum.
177
+ #
178
+ # export const enum ClassTypeFlags {
179
+ # ...
180
+ # // Class is defined in the "builtins" or "typing" file.
181
+ # BuiltIn = 1 << 0,
182
+ # ...
183
+ #
184
+ self.is_builtin_class = is_builtin_class
171
185
 
172
186
  def __init__(
173
187
  self,
@@ -178,6 +192,10 @@ class ClassType(TypeBase):
178
192
  super().__init__(flags=flags)
179
193
  self.shared = shared
180
194
 
195
+ def __str__(self) -> str:
196
+ """Return a string representation of the class type."""
197
+ return f"<class {self.shared.class_name}>"
198
+
181
199
  def clone_as_instance(self) -> "ClassType":
182
200
  """Clone this class type as an instance type."""
183
201
  if self.is_instance():
@@ -196,19 +214,57 @@ class ClassType(TypeBase):
196
214
  """Lookup a member in the class type."""
197
215
  return self.shared.symbol_table.lookup(member, deep=True)
198
216
 
217
+ def is_builtin(self, class_name: str | None = None) -> bool:
218
+ """
219
+ Return true if this class is a builtin class.
220
+
221
+ If class_name is provided, also check if the class name matches.
222
+ """
223
+ if not self.shared.is_builtin_class:
224
+ return False
225
+ if class_name is not None:
226
+ return self.shared.class_name == class_name
227
+ return True
228
+
229
+
230
+ class ParamKind(IntEnum):
231
+ """Enumeration of parameter kinds."""
232
+
233
+ POSONLY = 0
234
+ NORMAL = 1
235
+ VARARG = 2
236
+ KWONLY = 3
237
+ KWARG = 4
238
+
199
239
 
200
240
  class Parameter:
201
241
  """Represents a function parameter."""
202
242
 
203
243
  def __init__(
204
- self, name: str, category: ParameterCategory, param_type: TypeBase | None
244
+ self,
245
+ name: str,
246
+ category: ParameterCategory,
247
+ param_type: TypeBase | None,
248
+ default_value: Expr | None = None,
249
+ is_self: bool = False,
250
+ param_kind: ParamKind = ParamKind.NORMAL,
205
251
  ) -> None:
206
252
  """Initialize obviously."""
207
253
  super().__init__()
208
254
  self.name = name
209
255
  self.category = category
256
+ self.default_value = default_value
210
257
  self.param_type = param_type
211
258
 
259
+ # This will set to true if the parameter is `self`. In jaclang self
260
+ # in the context of obj, node, edge, walker methods is not required to
261
+ # be explicitly defined. However, in the `class` methods it should be
262
+ # explicitly defined.
263
+ self.is_self = is_self
264
+
265
+ # Kind is wheather it's normal, posonly, vararg, kwonly, kwarg.
266
+ self.param_kind: ParamKind = param_kind
267
+
212
268
 
213
269
  class FunctionType(TypeBase):
214
270
  """Represents a function type."""