jaclang 0.8.9__py3-none-any.whl → 0.8.10__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of jaclang might be problematic. Click here for more details.
- jaclang/cli/cli.py +147 -25
- jaclang/cli/cmdreg.py +144 -8
- jaclang/compiler/__init__.py +6 -1
- jaclang/compiler/codeinfo.py +16 -1
- jaclang/compiler/constant.py +33 -13
- jaclang/compiler/jac.lark +130 -31
- jaclang/compiler/larkparse/jac_parser.py +2 -2
- jaclang/compiler/parser.py +567 -176
- jaclang/compiler/passes/__init__.py +2 -1
- jaclang/compiler/passes/ast_gen/__init__.py +5 -0
- jaclang/compiler/passes/ast_gen/base_ast_gen_pass.py +54 -0
- jaclang/compiler/passes/ast_gen/jsx_processor.py +344 -0
- jaclang/compiler/passes/ecmascript/__init__.py +25 -0
- jaclang/compiler/passes/ecmascript/es_unparse.py +576 -0
- jaclang/compiler/passes/ecmascript/esast_gen_pass.py +2068 -0
- jaclang/compiler/passes/ecmascript/estree.py +972 -0
- jaclang/compiler/passes/ecmascript/tests/__init__.py +1 -0
- jaclang/compiler/passes/ecmascript/tests/fixtures/advanced_language_features.jac +170 -0
- jaclang/compiler/passes/ecmascript/tests/fixtures/class_separate_impl.impl.jac +30 -0
- jaclang/compiler/passes/ecmascript/tests/fixtures/class_separate_impl.jac +14 -0
- jaclang/compiler/passes/ecmascript/tests/fixtures/client_jsx.jac +89 -0
- jaclang/compiler/passes/ecmascript/tests/fixtures/core_language_features.jac +195 -0
- jaclang/compiler/passes/ecmascript/tests/test_esast_gen_pass.py +167 -0
- jaclang/compiler/passes/ecmascript/tests/test_js_generation.py +239 -0
- jaclang/compiler/passes/main/__init__.py +0 -3
- jaclang/compiler/passes/main/annex_pass.py +23 -1
- jaclang/compiler/passes/main/pyast_gen_pass.py +324 -234
- jaclang/compiler/passes/main/pyast_load_pass.py +46 -11
- jaclang/compiler/passes/main/pyjac_ast_link_pass.py +2 -0
- jaclang/compiler/passes/main/sym_tab_build_pass.py +18 -1
- jaclang/compiler/passes/main/tests/fixtures/autoimpl.cl.jac +7 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_arity.jac +3 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_class_construct.jac +33 -0
- jaclang/compiler/passes/main/tests/fixtures/defuse_modpath.jac +7 -0
- jaclang/compiler/passes/main/tests/fixtures/member_access_type_resolve.jac +2 -1
- jaclang/compiler/passes/main/tests/test_checker_pass.py +31 -2
- jaclang/compiler/passes/main/tests/test_def_use_pass.py +12 -0
- jaclang/compiler/passes/main/tests/test_import_pass.py +23 -4
- jaclang/compiler/passes/main/tests/test_pyast_gen_pass.py +25 -0
- jaclang/compiler/passes/main/type_checker_pass.py +7 -0
- jaclang/compiler/passes/tool/doc_ir_gen_pass.py +115 -0
- jaclang/compiler/passes/tool/fuse_comments_pass.py +1 -10
- jaclang/compiler/passes/tool/tests/test_jac_format_pass.py +4 -1
- jaclang/compiler/passes/transform.py +9 -1
- jaclang/compiler/passes/uni_pass.py +5 -7
- jaclang/compiler/program.py +22 -25
- jaclang/compiler/tests/test_client_codegen.py +113 -0
- jaclang/compiler/tests/test_importer.py +12 -10
- jaclang/compiler/tests/test_parser.py +249 -3
- jaclang/compiler/type_system/type_evaluator.jac +169 -50
- jaclang/compiler/type_system/type_utils.py +1 -1
- jaclang/compiler/type_system/types.py +6 -0
- jaclang/compiler/unitree.py +430 -84
- jaclang/langserve/engine.jac +224 -288
- jaclang/langserve/sem_manager.jac +12 -8
- jaclang/langserve/server.jac +48 -48
- jaclang/langserve/tests/fixtures/greet.py +17 -0
- jaclang/langserve/tests/fixtures/md_path.jac +22 -0
- jaclang/langserve/tests/fixtures/user.jac +15 -0
- jaclang/langserve/tests/test_server.py +66 -371
- jaclang/lib.py +1 -1
- jaclang/runtimelib/client_bundle.py +169 -0
- jaclang/runtimelib/client_runtime.jac +586 -0
- jaclang/runtimelib/constructs.py +2 -0
- jaclang/runtimelib/machine.py +259 -100
- jaclang/runtimelib/meta_importer.py +111 -22
- jaclang/runtimelib/mtp.py +15 -0
- jaclang/runtimelib/server.py +1089 -0
- jaclang/runtimelib/tests/fixtures/client_app.jac +18 -0
- jaclang/runtimelib/tests/fixtures/custom_access_validation.jac +1 -1
- jaclang/runtimelib/tests/fixtures/savable_object.jac +4 -5
- jaclang/runtimelib/tests/fixtures/serve_api.jac +75 -0
- jaclang/runtimelib/tests/test_client_bundle.py +55 -0
- jaclang/runtimelib/tests/test_client_render.py +63 -0
- jaclang/runtimelib/tests/test_serve.py +1069 -0
- jaclang/settings.py +0 -2
- jaclang/tests/fixtures/iife_functions.jac +142 -0
- jaclang/tests/fixtures/iife_functions_client.jac +143 -0
- jaclang/tests/fixtures/multistatement_lambda.jac +116 -0
- jaclang/tests/fixtures/multistatement_lambda_client.jac +113 -0
- jaclang/tests/fixtures/needs_import_dup.jac +6 -4
- jaclang/tests/fixtures/py_run.py +7 -5
- jaclang/tests/fixtures/pyfunc_fstr.py +2 -2
- jaclang/tests/fixtures/simple_lambda_test.jac +12 -0
- jaclang/tests/test_cli.py +1 -1
- jaclang/tests/test_language.py +10 -39
- jaclang/tests/test_reference.py +17 -2
- jaclang/utils/NonGPT.py +375 -0
- jaclang/utils/helpers.py +44 -16
- jaclang/utils/lang_tools.py +31 -4
- jaclang/utils/tests/test_lang_tools.py +1 -1
- jaclang/utils/treeprinter.py +8 -3
- {jaclang-0.8.9.dist-info → jaclang-0.8.10.dist-info}/METADATA +3 -3
- {jaclang-0.8.9.dist-info → jaclang-0.8.10.dist-info}/RECORD +96 -66
- jaclang/compiler/passes/main/binder_pass.py +0 -594
- jaclang/compiler/passes/main/tests/fixtures/sym_binder.jac +0 -47
- jaclang/compiler/passes/main/tests/test_binder_pass.py +0 -111
- jaclang/langserve/tests/session.jac +0 -294
- jaclang/langserve/tests/test_dev_server.py +0 -80
- jaclang/runtimelib/importer.py +0 -351
- jaclang/tests/test_typecheck.py +0 -542
- {jaclang-0.8.9.dist-info → jaclang-0.8.10.dist-info}/WHEEL +0 -0
- {jaclang-0.8.9.dist-info → jaclang-0.8.10.dist-info}/entry_points.txt +0 -0
|
@@ -15,7 +15,7 @@ import from typing { Callable, TYPE_CHECKING, cast }
|
|
|
15
15
|
|
|
16
16
|
import jaclang.compiler.unitree as uni;
|
|
17
17
|
import from jaclang.compiler { TOKEN_MAP }
|
|
18
|
-
import from jaclang.compiler.constant { Tokens as Tok }
|
|
18
|
+
import from jaclang.compiler.constant { SymbolType, Tokens as Tok }
|
|
19
19
|
import from jaclang.compiler.passes.main.pyast_load_pass { PyastBuildPass }
|
|
20
20
|
import from jaclang.compiler.passes.main.sym_tab_build_pass { SymTabBuildPass }
|
|
21
21
|
import from jaclang.compiler.type_system { types }
|
|
@@ -112,9 +112,11 @@ class TypeEvaluator {
|
|
|
112
112
|
def __init__(self: TypeEvaluator, program: "JacProgram") -> None {
|
|
113
113
|
self.program = program;
|
|
114
114
|
self.symbol_resolution_stack: list[SymbolResolutionStackEntry] = [];
|
|
115
|
+
self.prefetch: PrefetchedTypes | None = None;
|
|
115
116
|
self.builtins_module = self._load_builtins_stub_module();
|
|
116
|
-
self.prefetch = self._prefetch_types();
|
|
117
117
|
self.diagnostic_callback: DiagnosticCallback | None = None;
|
|
118
|
+
|
|
119
|
+
self._prefetch_types();
|
|
118
120
|
}
|
|
119
121
|
|
|
120
122
|
"""Load and return the builtins stub module."""
|
|
@@ -147,7 +149,7 @@ class TypeEvaluator {
|
|
|
147
149
|
|
|
148
150
|
"""Return the prefetched types for the type evaluator."""
|
|
149
151
|
def _prefetch_types(self: TypeEvaluator) -> "PrefetchedTypes" {
|
|
150
|
-
|
|
152
|
+
self.prefetch = PrefetchedTypes(
|
|
151
153
|
# TODO: Pyright first try load NoneType from typeshed and if it cannot
|
|
152
154
|
# then it set to unknown type.
|
|
153
155
|
none_type_class=types.UnknownType(),
|
|
@@ -267,23 +269,41 @@ class TypeEvaluator {
|
|
|
267
269
|
|
|
268
270
|
"""Return the effective type of the module."""
|
|
269
271
|
def get_type_of_module(self: TypeEvaluator, node_: uni.ModulePath) -> types.TypeBase {
|
|
270
|
-
if node_.
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
return node_.name_spec.type;
|
|
276
|
-
}
|
|
272
|
+
if node_.path {
|
|
273
|
+
# If the type is already computed, return it.
|
|
274
|
+
if node_.path[-1].type {
|
|
275
|
+
return node_.path[-1].type;
|
|
276
|
+
}
|
|
277
277
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
278
|
+
for (idx, npath) in enumerate(node_.path) {
|
|
279
|
+
mod_path = node_.resolve_relative_path_list()[idx];
|
|
280
|
+
|
|
281
|
+
# If the path doesn't exist, return unknown type.
|
|
282
|
+
# eg: import from ..mod { item } # <-- Here ..mod might not exist.
|
|
283
|
+
if not Path(mod_path).exists() {
|
|
284
|
+
npath.type = types.UnknownType();
|
|
285
|
+
npath._sym_category = SymbolType.MODULE;
|
|
286
|
+
self.add_diagnostic(npath, 'Module not found', warning=True);
|
|
287
|
+
return npath.type;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
mod: uni.Module = self._import_module_from_path(mod_path);
|
|
291
|
+
mod_type = types.ModuleType(
|
|
292
|
+
mod_name=npath.value,
|
|
293
|
+
file_uri=Path(mod_path).resolve(),
|
|
294
|
+
symbol_table=mod
|
|
295
|
+
);
|
|
296
|
+
npath.type = mod_type;
|
|
297
|
+
npath._sym_category = SymbolType.MODULE;
|
|
298
|
+
}
|
|
299
|
+
if node_.alias {
|
|
300
|
+
node_.alias.type = node_.path[-1].type;
|
|
301
|
+
node_.alias._sym_category = SymbolType.MODULE;
|
|
302
|
+
}
|
|
303
|
+
return node_.path[-1].type;
|
|
304
|
+
}
|
|
305
|
+
return types.UnknownType();
|
|
284
306
|
|
|
285
|
-
node_.name_spec.type = mod_type;
|
|
286
|
-
return mod_type;
|
|
287
307
|
}
|
|
288
308
|
|
|
289
309
|
"""Return the effective type of the module item."""
|
|
@@ -291,12 +311,13 @@ class TypeEvaluator {
|
|
|
291
311
|
# Module item can be both a module or a member of a module.
|
|
292
312
|
# import from .. { mod } # <-- Here mod is not a member but a module itself.
|
|
293
313
|
# import from mod { item } # <-- Here item is not a module but a member of mod.
|
|
294
|
-
if node_.
|
|
295
|
-
return node_.
|
|
314
|
+
if node_.name.type is not None {
|
|
315
|
+
return node_.name.type;
|
|
296
316
|
}
|
|
297
317
|
|
|
298
318
|
import_node = node_.parent_of_type(uni.Import);
|
|
299
|
-
|
|
319
|
+
assert import_node.from_loc is not None;
|
|
320
|
+
if isinstance(self.get_type_of_module(import_node.from_loc), types.ModuleType){
|
|
300
321
|
|
|
301
322
|
from_path = Path(import_node.from_loc.resolve_relative_path());
|
|
302
323
|
is_dir = from_path.is_dir() or (from_path.stem == "__init__");
|
|
@@ -309,12 +330,12 @@ class TypeEvaluator {
|
|
|
309
330
|
if (path := (mod_dir / (node_.name.value + ext)).resolve()).exists() {
|
|
310
331
|
mod = self._import_module_from_path(str(path));
|
|
311
332
|
mod_type = types.ModuleType(
|
|
312
|
-
mod_name=node_.
|
|
333
|
+
mod_name=node_.name.sym_name,
|
|
313
334
|
file_uri=path,
|
|
314
335
|
symbol_table=mod,
|
|
315
336
|
);
|
|
316
337
|
# Cache the type.
|
|
317
|
-
node_.
|
|
338
|
+
node_.name.type = mod_type;
|
|
318
339
|
|
|
319
340
|
# FIXME: goto definition works on imported symbol by checking if it's a MODULE
|
|
320
341
|
# type and in that case it'll call resolve_relative_path on the parent node of
|
|
@@ -323,8 +344,8 @@ class TypeEvaluator {
|
|
|
323
344
|
# below should work but because of the above assumption (should be a mod path)
|
|
324
345
|
# it won't, This needs to be discussed.
|
|
325
346
|
#
|
|
326
|
-
|
|
327
|
-
return
|
|
347
|
+
node_.name._sym_category = SymbolType.MODULE;
|
|
348
|
+
return mod_type;
|
|
328
349
|
}
|
|
329
350
|
}
|
|
330
351
|
}
|
|
@@ -333,18 +354,18 @@ class TypeEvaluator {
|
|
|
333
354
|
else {
|
|
334
355
|
mod_type = self.get_type_of_module(import_node.from_loc);
|
|
335
356
|
if not isinstance(mod_type, types.ModuleType) {
|
|
336
|
-
node_.
|
|
357
|
+
node_.name.type = types.UnknownType();
|
|
337
358
|
# TODO: Add diagnostic that from_loc is not accessible.
|
|
338
359
|
# Eg: 'Import "scipy" could not be resolved'
|
|
339
|
-
return node_.
|
|
360
|
+
return node_.name.type;
|
|
340
361
|
}
|
|
341
362
|
if sym := mod_type.symbol_table.lookup(node_.name.value, deep=True) {
|
|
342
363
|
node_.name.sym = sym;
|
|
343
364
|
if node_.alias {
|
|
344
365
|
node_.alias.sym = sym;
|
|
345
366
|
}
|
|
346
|
-
node_.
|
|
347
|
-
return node_.
|
|
367
|
+
node_.name.type = self.get_type_of_symbol(sym);
|
|
368
|
+
return node_.name.type;
|
|
348
369
|
}
|
|
349
370
|
}
|
|
350
371
|
}
|
|
@@ -366,12 +387,18 @@ class TypeEvaluator {
|
|
|
366
387
|
}
|
|
367
388
|
is_builtin_class = node_.find_parent_of_type(uni.Module) == self.builtins_module;
|
|
368
389
|
|
|
390
|
+
# NOTE: obj, walker, node, edge are dataclass with symbol type as such but
|
|
391
|
+
# other classes are with symbol type TYPE.
|
|
392
|
+
# TODO: We need to handle @dataclass decorator as well.
|
|
393
|
+
is_data_class = node_.sym_category != SymbolType.TYPE;
|
|
394
|
+
|
|
369
395
|
cls_type = types.ClassType(
|
|
370
396
|
types.ClassType.ClassDetailsShared(
|
|
371
397
|
class_name=node_.name_spec.sym_name,
|
|
372
398
|
symbol_table=node_,
|
|
373
399
|
base_classes=base_classes,
|
|
374
400
|
is_builtin_class=is_builtin_class,
|
|
401
|
+
is_data_class=is_data_class,
|
|
375
402
|
),
|
|
376
403
|
flags=types.TypeFlags.Instantiable,
|
|
377
404
|
);
|
|
@@ -600,12 +627,6 @@ class TypeEvaluator {
|
|
|
600
627
|
def _get_type_of_symbol(self: TypeEvaluator, symbol: uni.Symbol) -> TypeBase {
|
|
601
628
|
node_ = symbol.decl.name_of;
|
|
602
629
|
match node_ {
|
|
603
|
-
case uni.ModulePath():
|
|
604
|
-
return self.get_type_of_module(node_);
|
|
605
|
-
|
|
606
|
-
case uni.ModuleItem():
|
|
607
|
-
return self.get_type_of_module_item(node_);
|
|
608
|
-
|
|
609
630
|
case uni.Archetype():
|
|
610
631
|
return self.get_type_of_class(node_);
|
|
611
632
|
|
|
@@ -635,6 +656,12 @@ class TypeEvaluator {
|
|
|
635
656
|
}
|
|
636
657
|
}
|
|
637
658
|
}
|
|
659
|
+
if isinstance(node_.parent, uni.ModulePath) {
|
|
660
|
+
return self.get_type_of_module(node_.parent);
|
|
661
|
+
}
|
|
662
|
+
if isinstance(node_.parent, uni.ModuleItem) {
|
|
663
|
+
return self.get_type_of_module_item(node_.parent);
|
|
664
|
+
}
|
|
638
665
|
|
|
639
666
|
case uni.HasVar():
|
|
640
667
|
if node_.type_tag is not None {
|
|
@@ -657,14 +684,30 @@ class TypeEvaluator {
|
|
|
657
684
|
def _get_type_of_expression_core(self: TypeEvaluator, expr: uni.Expr) -> TypeBase {
|
|
658
685
|
match expr {
|
|
659
686
|
|
|
687
|
+
# ----------------------------------------------------------------------
|
|
688
|
+
# Prefetched types: These types may not have prefetched and we need to
|
|
689
|
+
# check if they are available, if the're not, we're inside builtins or
|
|
690
|
+
# typeshed module itself and in that case we can just return unknown type.
|
|
691
|
+
# ----------------------------------------------------------------------
|
|
692
|
+
|
|
660
693
|
case uni.String() | uni.MultiString():
|
|
661
|
-
|
|
694
|
+
if self.prefetch {
|
|
695
|
+
return self._convert_to_instance(self.get_type_of_string(expr));
|
|
696
|
+
}
|
|
662
697
|
|
|
663
698
|
case uni.Int():
|
|
664
|
-
|
|
699
|
+
if self.prefetch {
|
|
700
|
+
return self._convert_to_instance(self.get_type_of_int(expr));
|
|
701
|
+
}
|
|
665
702
|
|
|
666
703
|
case uni.Float():
|
|
667
|
-
|
|
704
|
+
if self.prefetch {
|
|
705
|
+
return self._convert_to_instance(self.get_type_of_float(expr));
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
# ----------------------------------------------------------------------
|
|
709
|
+
# End of prefetched types
|
|
710
|
+
# ----------------------------------------------------------------------
|
|
668
711
|
|
|
669
712
|
case uni.AtomTrailer():
|
|
670
713
|
# NOTE: Pyright is using CFG to figure out the member type by narrowing the base
|
|
@@ -679,8 +722,7 @@ class TypeEvaluator {
|
|
|
679
722
|
if sym := base_type.symbol_table.lookup(
|
|
680
723
|
expr.right.value, deep=True
|
|
681
724
|
) {
|
|
682
|
-
expr.right
|
|
683
|
-
return self.get_type_of_symbol(sym);
|
|
725
|
+
return self._set_symbol_to_expr(expr.right, sym);
|
|
684
726
|
}
|
|
685
727
|
return types.UnknownType();
|
|
686
728
|
}
|
|
@@ -690,8 +732,7 @@ class TypeEvaluator {
|
|
|
690
732
|
if member := self._lookup_class_member(
|
|
691
733
|
base_type, expr.right.value
|
|
692
734
|
) {
|
|
693
|
-
expr.right
|
|
694
|
-
return self.get_type_of_symbol(member.symbol);
|
|
735
|
+
return self._set_symbol_to_expr(expr.right, member.symbol);
|
|
695
736
|
}
|
|
696
737
|
return types.UnknownType();
|
|
697
738
|
}
|
|
@@ -701,8 +742,7 @@ class TypeEvaluator {
|
|
|
701
742
|
if member := self._lookup_object_member(
|
|
702
743
|
base_type, expr.right.value
|
|
703
744
|
) {
|
|
704
|
-
expr.right
|
|
705
|
-
return self.get_type_of_symbol(member.symbol);
|
|
745
|
+
return self._set_symbol_to_expr(expr.right, member.symbol);
|
|
706
746
|
}
|
|
707
747
|
return types.UnknownType();
|
|
708
748
|
}
|
|
@@ -734,7 +774,7 @@ class TypeEvaluator {
|
|
|
734
774
|
}
|
|
735
775
|
|
|
736
776
|
if symbol := expr.sym_tab.lookup(expr.value, deep=True) {
|
|
737
|
-
expr.sym = symbol;
|
|
777
|
+
expr.sym = self.resolve_imported_symbols(symbol);
|
|
738
778
|
return self.get_type_of_symbol(symbol);
|
|
739
779
|
}
|
|
740
780
|
}
|
|
@@ -747,6 +787,26 @@ class TypeEvaluator {
|
|
|
747
787
|
# Helper functions
|
|
748
788
|
# -----------------------------------------------------------------------------
|
|
749
789
|
|
|
790
|
+
"""Resolve the imported symbols to the actual symbol."""
|
|
791
|
+
def resolve_imported_symbols(self: TypeEvaluator, sym: uni.Symbol) -> uni.Symbol {
|
|
792
|
+
if (
|
|
793
|
+
isinstance(sym.decl, uni.Name) and sym.decl.find_parent_of_type(uni.ModuleItem)
|
|
794
|
+
) {
|
|
795
|
+
# If the symbol is from a module item, we need to resolve it to the actual symbol.
|
|
796
|
+
mod_item = sym.decl.find_parent_of_type(uni.ModuleItem);
|
|
797
|
+
assert (mod_item is not None);
|
|
798
|
+
self.get_type_of_module(mod_item.from_mod_path);
|
|
799
|
+
return (mod_item.name.sym or sym);
|
|
800
|
+
}
|
|
801
|
+
return sym;
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
def _set_symbol_to_expr(self: TypeEvaluator, expr: uni.Expr, sym: uni.Symbol) -> TypeBase {
|
|
805
|
+
expr.sym = sym;
|
|
806
|
+
expr.type = self.get_type_of_symbol(sym);
|
|
807
|
+
return expr.type;
|
|
808
|
+
}
|
|
809
|
+
|
|
750
810
|
"""Check if the expression is Name that is 'self' and in the method context."""
|
|
751
811
|
def _is_expr_self(self: TypeEvaluator, expr: uni.Expr) -> bool {
|
|
752
812
|
if (
|
|
@@ -812,7 +872,6 @@ class TypeEvaluator {
|
|
|
812
872
|
|
|
813
873
|
"""Lookup the class member type."""
|
|
814
874
|
def _lookup_class_member(self: TypeEvaluator, base_type: types.ClassType, member: str) -> type_utils.ClassMember | None {
|
|
815
|
-
assert self.prefetch.int_class is not None;
|
|
816
875
|
# FIXME: Pyright's way: Implement class member iterator (based on mro and the multiple inheritance)
|
|
817
876
|
# return the first found member from the iterator.
|
|
818
877
|
|
|
@@ -908,6 +967,8 @@ class TypeEvaluator {
|
|
|
908
967
|
"""
|
|
909
968
|
def validate_call_args(self: TypeEvaluator, expr: uni.FuncCall) -> TypeBase {
|
|
910
969
|
caller_type = self.get_type_of_expression(expr.target);
|
|
970
|
+
|
|
971
|
+
# 1. Call to a function.
|
|
911
972
|
if isinstance(caller_type, types.FunctionType) {
|
|
912
973
|
arg_param_match = self.match_args_to_params(expr, caller_type);
|
|
913
974
|
if not arg_param_match.argument_errors {
|
|
@@ -916,14 +977,34 @@ class TypeEvaluator {
|
|
|
916
977
|
return caller_type.return_type or types.UnknownType();
|
|
917
978
|
}
|
|
918
979
|
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
and caller_type.is_instantiable_class()
|
|
922
|
-
) {
|
|
980
|
+
# 2. Call to an instantiable class.
|
|
981
|
+
if (isinstance(caller_type, types.ClassType) and caller_type.is_instantiable_class()) {
|
|
923
982
|
# TODO: validate args for __init__()
|
|
983
|
+
|
|
984
|
+
# If it has __init__ method, try to call it.
|
|
985
|
+
if init_method := self._lookup_class_member(caller_type, "__init__") {
|
|
986
|
+
init_fn_type = self.get_type_of_symbol(init_method.symbol);
|
|
987
|
+
if isinstance(init_fn_type, types.FunctionType) {
|
|
988
|
+
arg_param_match = self.match_args_to_params(expr, init_fn_type);
|
|
989
|
+
if not arg_param_match.argument_errors {
|
|
990
|
+
self.validate_arg_types(arg_param_match);
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
# If it's a data class, create the __init__ method and validate.
|
|
996
|
+
elif caller_type.is_data_class() {
|
|
997
|
+
init_fn_type = self._create_dataclass_init_method(caller_type);
|
|
998
|
+
arg_param_match = self.match_args_to_params(expr, init_fn_type);
|
|
999
|
+
if not arg_param_match.argument_errors {
|
|
1000
|
+
self.validate_arg_types(arg_param_match);
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
|
|
924
1004
|
return caller_type.clone_as_instance();
|
|
925
1005
|
}
|
|
926
1006
|
|
|
1007
|
+
# 3. Call to a callable object (__call__).
|
|
927
1008
|
if caller_type.is_class_instance() {
|
|
928
1009
|
# TODO: validate args.
|
|
929
1010
|
magic_call_ret = self.get_type_of_magic_method_call(caller_type, "__call__");
|
|
@@ -956,4 +1037,42 @@ class TypeEvaluator {
|
|
|
956
1037
|
}
|
|
957
1038
|
}
|
|
958
1039
|
|
|
1040
|
+
def _create_dataclass_init_method(self: TypeEvaluator, class_type: types.ClassType) -> types.FunctionType {
|
|
1041
|
+
|
|
1042
|
+
parameters: list[types.Parameter] = [];
|
|
1043
|
+
|
|
1044
|
+
# Iterate over the has vars and create parameters.
|
|
1045
|
+
for has_var in class_type.shared.symbol_table.get_has_vars() {
|
|
1046
|
+
|
|
1047
|
+
# Get parameter type.
|
|
1048
|
+
param_type: TypeBase | None = None;
|
|
1049
|
+
if has_var.type_tag {
|
|
1050
|
+
var_type_expr = has_var.type_tag.tag;
|
|
1051
|
+
type_cls = self.get_type_of_expression(var_type_expr);
|
|
1052
|
+
param_type = self._convert_to_instance(type_cls);
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
# Create parameter.
|
|
1056
|
+
parameters.append(
|
|
1057
|
+
types.Parameter(
|
|
1058
|
+
name=has_var.name.value,
|
|
1059
|
+
category=types.ParameterCategory.Positional,
|
|
1060
|
+
param_type=param_type,
|
|
1061
|
+
default_value=has_var.value,
|
|
1062
|
+
is_self=False,
|
|
1063
|
+
param_kind=types.ParamKind.NORMAL,
|
|
1064
|
+
)
|
|
1065
|
+
);
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
# Create and return the __init__ method type.
|
|
1069
|
+
return types.FunctionType(
|
|
1070
|
+
func_name="__init__",
|
|
1071
|
+
return_type=types.UnknownType(),
|
|
1072
|
+
parameters=parameters,
|
|
1073
|
+
);
|
|
1074
|
+
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
|
|
959
1078
|
}
|
|
@@ -167,7 +167,7 @@ class ParamAssignmentTracker:
|
|
|
167
167
|
if self.curr_param_idx == -1:
|
|
168
168
|
return self.varargs
|
|
169
169
|
|
|
170
|
-
if isinstance(arg, uni.UnaryExpr) and arg.op.
|
|
170
|
+
if isinstance(arg, uni.UnaryExpr) and arg.op.name == Tok.STAR_MUL:
|
|
171
171
|
self._mark_all_positional_params_as_matched()
|
|
172
172
|
return None
|
|
173
173
|
else:
|
|
@@ -164,6 +164,7 @@ class ClassType(TypeBase):
|
|
|
164
164
|
symbol_table: SymbolTable,
|
|
165
165
|
base_classes: list[TypeBase] | None = None,
|
|
166
166
|
is_builtin_class: bool = False,
|
|
167
|
+
is_data_class: bool = False,
|
|
167
168
|
) -> None:
|
|
168
169
|
"""Initialize obviously."""
|
|
169
170
|
self.class_name = class_name
|
|
@@ -182,6 +183,7 @@ class ClassType(TypeBase):
|
|
|
182
183
|
# ...
|
|
183
184
|
#
|
|
184
185
|
self.is_builtin_class = is_builtin_class
|
|
186
|
+
self.is_data_class = is_data_class
|
|
185
187
|
|
|
186
188
|
def __init__(
|
|
187
189
|
self,
|
|
@@ -226,6 +228,10 @@ class ClassType(TypeBase):
|
|
|
226
228
|
return self.shared.class_name == class_name
|
|
227
229
|
return True
|
|
228
230
|
|
|
231
|
+
def is_data_class(self) -> bool:
|
|
232
|
+
"""Return true if this class is a data class."""
|
|
233
|
+
return self.shared.is_data_class
|
|
234
|
+
|
|
229
235
|
|
|
230
236
|
class ParamKind(IntEnum):
|
|
231
237
|
"""Enumeration of parameter kinds."""
|