jaclang 0.7.27__py3-none-any.whl → 0.7.30__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 (56) hide show
  1. jaclang/cli/cli.py +3 -0
  2. jaclang/compiler/__init__.py +1 -1
  3. jaclang/compiler/absyntree.py +30 -5
  4. jaclang/compiler/compile.py +1 -1
  5. jaclang/compiler/constant.py +0 -1
  6. jaclang/compiler/jac.lark +1 -5
  7. jaclang/compiler/parser.py +2 -10
  8. jaclang/compiler/passes/main/__init__.py +1 -1
  9. jaclang/compiler/passes/main/access_modifier_pass.py +1 -1
  10. jaclang/compiler/passes/main/fuse_typeinfo_pass.py +62 -33
  11. jaclang/compiler/passes/main/import_pass.py +285 -64
  12. jaclang/compiler/passes/main/inheritance_pass.py +103 -0
  13. jaclang/compiler/passes/main/py_collect_dep_pass.py +5 -5
  14. jaclang/compiler/passes/main/pyast_gen_pass.py +0 -19
  15. jaclang/compiler/passes/main/pyast_load_pass.py +1 -1
  16. jaclang/compiler/passes/main/schedules.py +2 -0
  17. jaclang/compiler/passes/main/sym_tab_build_pass.py +17 -0
  18. jaclang/compiler/passes/main/tests/fixtures/data_spatial_types.jac +130 -0
  19. jaclang/compiler/passes/main/tests/fixtures/pygame_mock/__init__.py +3 -3
  20. jaclang/compiler/passes/main/tests/fixtures/pygame_mock/__init__.pyi +3 -3
  21. jaclang/compiler/passes/main/tests/test_import_pass.py +16 -17
  22. jaclang/compiler/passes/main/tests/test_type_check_pass.py +24 -0
  23. jaclang/compiler/passes/main/type_check_pass.py +3 -2
  24. jaclang/compiler/passes/tool/jac_formatter_pass.py +0 -1
  25. jaclang/compiler/py_info.py +22 -0
  26. jaclang/compiler/symtable.py +9 -2
  27. jaclang/compiler/tests/test_importer.py +45 -1
  28. jaclang/langserve/tests/test_server.py +2 -2
  29. jaclang/plugin/default.py +86 -62
  30. jaclang/plugin/feature.py +2 -5
  31. jaclang/plugin/spec.py +1 -6
  32. jaclang/runtimelib/architype.py +20 -16
  33. jaclang/runtimelib/importer.py +26 -3
  34. jaclang/runtimelib/machine.py +2 -2
  35. jaclang/runtimelib/test.py +59 -4
  36. jaclang/runtimelib/utils.py +15 -0
  37. jaclang/settings.py +3 -0
  38. jaclang/tests/fixtures/base_class1.jac +11 -0
  39. jaclang/tests/fixtures/base_class2.jac +11 -0
  40. jaclang/tests/fixtures/import_all.jac +7 -0
  41. jaclang/tests/fixtures/import_all_py.py +8 -0
  42. jaclang/tests/fixtures/jactest_imported.jac +6 -0
  43. jaclang/tests/fixtures/jactest_main.jac +22 -0
  44. jaclang/tests/fixtures/multi_dim_array_split.jac +2 -6
  45. jaclang/tests/fixtures/test_py.py +12 -0
  46. jaclang/tests/fixtures/visit_sequence.jac +50 -0
  47. jaclang/tests/test_cli.py +83 -1
  48. jaclang/tests/test_language.py +24 -9
  49. jaclang/utils/helpers.py +9 -1
  50. jaclang/utils/test.py +2 -2
  51. jaclang/utils/tests/test_lang_tools.py +4 -2
  52. jaclang/utils/treeprinter.py +6 -3
  53. {jaclang-0.7.27.dist-info → jaclang-0.7.30.dist-info}/METADATA +3 -3
  54. {jaclang-0.7.27.dist-info → jaclang-0.7.30.dist-info}/RECORD +56 -45
  55. {jaclang-0.7.27.dist-info → jaclang-0.7.30.dist-info}/WHEEL +1 -1
  56. {jaclang-0.7.27.dist-info → jaclang-0.7.30.dist-info}/entry_points.txt +0 -0
jaclang/cli/cli.py CHANGED
@@ -307,6 +307,7 @@ def enter(
307
307
  @cmd_registry.register
308
308
  def test(
309
309
  filepath: str,
310
+ test_name: str = "",
310
311
  filter: str = "",
311
312
  xit: bool = False,
312
313
  maxfail: int = None, # type:ignore
@@ -316,6 +317,7 @@ def test(
316
317
  """Run the test suite in the specified .jac file.
317
318
 
318
319
  :param filepath: Path/to/file.jac
320
+ :param test_name: Run a specific test.
319
321
  :param filter: Filter the files using Unix shell style conventions.
320
322
  :param xit(exit): Stop(exit) running tests as soon as finds an error.
321
323
  :param maxfail: Stop running tests after n failures.
@@ -328,6 +330,7 @@ def test(
328
330
 
329
331
  failcount = Jac.run_test(
330
332
  filepath=filepath,
333
+ func_name=("test_" + test_name) if test_name else None,
331
334
  filter=filter,
332
335
  xit=xit,
333
336
  maxfail=maxfail,
@@ -55,7 +55,7 @@ TOKEN_MAP.update(
55
55
  "CARROW_L": "<++", "CARROW_R": "++>", "GLOBAL_OP": ":global:",
56
56
  "NONLOCAL_OP": ":nonlocal:", "WALKER_OP": ":walker:", "NODE_OP": ":node:",
57
57
  "EDGE_OP": ":edge:", "CLASS_OP": ":class:", "OBJECT_OP": ":obj:",
58
- "TYPE_OP": "`", "ABILITY_OP": ":can:", "ELVIS_OP": "?:", "NULL_OK": "?",
58
+ "TYPE_OP": "`", "ABILITY_OP": ":can:", "NULL_OK": "?",
59
59
  "KW_OR": "|", "ARROW_BI": "<-->", "ARROW_L": "<--",
60
60
  "ARROW_R": "-->", "ARROW_L_P1": "<-:", "ARROW_R_P2": ":->",
61
61
  "ARROW_L_P2": ":-", "ARROW_R_P1": "-:", "CARROW_BI": "<++>",
@@ -30,6 +30,7 @@ from jaclang.compiler.constant import (
30
30
  SymbolType,
31
31
  )
32
32
  from jaclang.compiler.constant import DELIM_MAP, SymbolAccess, Tokens as Tok
33
+ from jaclang.compiler.py_info import PyInfo
33
34
  from jaclang.compiler.semtable import SemRegistry
34
35
  from jaclang.utils.treeprinter import dotgen_ast_tree, print_ast_tree
35
36
 
@@ -636,11 +637,10 @@ class Module(AstDocNode):
636
637
  self.impl_mod: list[Module] = []
637
638
  self.test_mod: list[Module] = []
638
639
  self.mod_deps: dict[str, Module] = {}
639
- self.py_mod_dep_map: dict[str, str] = {}
640
- self.py_raise_map: dict[str, str] = {}
641
640
  self.registry = registry
642
641
  self.terminals: list[Token] = terminals
643
- self.is_raised_from_py: bool = False
642
+ self.py_info: PyInfo = PyInfo()
643
+
644
644
  AstNode.__init__(self, kid=kid)
645
645
  AstDocNode.__init__(self, doc=doc)
646
646
 
@@ -693,6 +693,21 @@ class Module(AstDocNode):
693
693
  super().unparse()
694
694
  return self.format()
695
695
 
696
+ @staticmethod
697
+ def get_href_path(node: AstNode) -> str:
698
+ """Return the full path of the module that contains this node."""
699
+ parent = node.find_parent_of_type(Module)
700
+ mod_list: list[Module | Architype] = []
701
+ if isinstance(node, (Module, Architype)):
702
+ mod_list.append(node)
703
+ while parent is not None:
704
+ mod_list.append(parent)
705
+ parent = parent.find_parent_of_type(Module)
706
+ mod_list.reverse()
707
+ return ".".join(
708
+ p.name if isinstance(p, Module) else p.name.sym_name for p in mod_list
709
+ )
710
+
696
711
 
697
712
  class GlobalVars(ElementStmt, AstAccessNode):
698
713
  """GlobalVars node type for Jac Ast."""
@@ -998,8 +1013,9 @@ class ModulePath(AstSymbolNode):
998
1013
  target = self.dot_path_str
999
1014
  if target_item:
1000
1015
  target += f".{target_item}"
1001
- base_path = os.path.dirname(self.loc.mod_path)
1002
- base_path = base_path if base_path else os.getcwd()
1016
+ base_path = (
1017
+ os.getenv("JACPATH") or os.path.dirname(self.loc.mod_path) or os.getcwd()
1018
+ )
1003
1019
  parts = target.split(".")
1004
1020
  traversal_levels = self.level - 1 if self.level > 0 else 0
1005
1021
  actual_parts = parts[traversal_levels:]
@@ -1011,6 +1027,15 @@ class ModulePath(AstSymbolNode):
1011
1027
  if os.path.exists(relative_path + ".jac")
1012
1028
  else relative_path
1013
1029
  )
1030
+ jacpath = os.getenv("JACPATH")
1031
+ if not os.path.exists(relative_path) and jacpath:
1032
+ name_to_find = actual_parts[-1] + ".jac"
1033
+
1034
+ # Walk through the single path in JACPATH
1035
+ for root, _, files in os.walk(jacpath):
1036
+ if name_to_find in files:
1037
+ relative_path = os.path.join(root, name_to_find)
1038
+ break
1014
1039
  return relative_path
1015
1040
 
1016
1041
  def normalize(self, deep: bool = False) -> bool:
@@ -31,7 +31,7 @@ def jac_file_to_pass(
31
31
  schedule: list[Type[Pass]] = pass_schedule,
32
32
  ) -> Pass:
33
33
  """Convert a Jac file to an AST."""
34
- with open(file_path) as file:
34
+ with open(file_path, "r", encoding="utf-8") as file:
35
35
  return jac_str_to_pass(
36
36
  jac_str=file.read(),
37
37
  file_path=file_path,
@@ -315,7 +315,6 @@ class Tokens(str, Enum):
315
315
  ABILITY_OP = "ABILITY_OP"
316
316
  A_PIPE_FWD = "A_PIPE_FWD"
317
317
  A_PIPE_BKWD = "A_PIPE_BKWD"
318
- ELVIS_OP = "ELVIS_OP"
319
318
  RETURN_HINT = "RETURN_HINT"
320
319
  NULL_OK = "NULL_OK"
321
320
  DECOR_OP = "DECOR_OP"
jaclang/compiler/jac.lark CHANGED
@@ -298,10 +298,7 @@ lambda_expr: KW_WITH func_decl_params? (RETURN_HINT expression)? KW_CAN expressi
298
298
  pipe: (pipe PIPE_FWD)? pipe_back
299
299
 
300
300
  // Pipe back expressions
301
- pipe_back: (pipe_back PIPE_BKWD)? elvis_check
302
-
303
- // Elvis expressions
304
- elvis_check: (elvis_check ELVIS_OP)? bitwise_or
301
+ pipe_back: (pipe_back PIPE_BKWD)? bitwise_or
305
302
 
306
303
  // Bitwise expressions
307
304
  bitwise_or: (bitwise_or BW_OR)? bitwise_xor
@@ -599,7 +596,6 @@ PIPE_BKWD: /<\|/
599
596
  DOT_FWD: /\.>/
600
597
  DOT_BKWD: /<\./
601
598
  RETURN_HINT: /->/
602
- ELVIS_OP: /\?:/
603
599
  NULL_OK: /\?/
604
600
  MATMUL_EQ: /@=/
605
601
  DECOR_OP: /@/
@@ -2037,16 +2037,8 @@ class JacParser(Pass):
2037
2037
  def pipe_back(self, kid: list[ast.AstNode]) -> ast.Expr:
2038
2038
  """Grammar rule.
2039
2039
 
2040
- pipe_back: elvis_check PIPE_BKWD pipe_back
2041
- | elvis_check
2042
- """
2043
- return self.binary_expr_unwind(kid)
2044
-
2045
- def elvis_check(self, kid: list[ast.AstNode]) -> ast.Expr:
2046
- """Grammar rule.
2047
-
2048
- elvis_check: bitwise_or ELVIS_OP elvis_check
2049
- | bitwise_or
2040
+ pipe_back: bitwise_or PIPE_BKWD pipe_back
2041
+ | bitwise_or
2050
2042
  """
2051
2043
  return self.binary_expr_unwind(kid)
2052
2044
 
@@ -2,9 +2,9 @@
2
2
 
3
3
  from .sub_node_tab_pass import SubNodeTabPass
4
4
  from .sym_tab_build_pass import SymTabBuildPass # noqa: I100
5
+ from .def_use_pass import DefUsePass # noqa: I100
5
6
  from .import_pass import JacImportPass, PyImportPass # noqa: I100
6
7
  from .def_impl_match_pass import DeclImplMatchPass # noqa: I100
7
- from .def_use_pass import DefUsePass # noqa: I100
8
8
  from .pyout_pass import PyOutPass # noqa: I100
9
9
  from .pyast_load_pass import PyastBuildPass # type: ignore # noqa: I100
10
10
  from .pyast_gen_pass import PyastGenPass # noqa: I100
@@ -45,7 +45,7 @@ class AccessCheckPass(Pass):
45
45
  settings.lsp_debug
46
46
  and isinstance(node, ast.NameAtom)
47
47
  and not node.sym
48
- and not node.parent_of_type(ast.Module).is_raised_from_py
48
+ and not node.parent_of_type(ast.Module).py_info.is_raised_from_py
49
49
  and not (
50
50
  node.sym_name == "py"
51
51
  and node.parent
@@ -10,6 +10,7 @@ import re
10
10
  from typing import Callable, Optional, TypeVar
11
11
 
12
12
  import jaclang.compiler.absyntree as ast
13
+ from jaclang.compiler.constant import Constants, Tokens
13
14
  from jaclang.compiler.passes import Pass
14
15
  from jaclang.compiler.symtable import SymbolTable
15
16
  from jaclang.settings import settings
@@ -76,35 +77,6 @@ class FuseTypeInfoPass(Pass):
76
77
  if typ_sym_table != self.ir.sym_tab:
77
78
  node.name_spec.type_sym_tab = typ_sym_table
78
79
 
79
- def __collect_python_dependencies(self, node: ast.AstNode) -> None:
80
- assert isinstance(node, ast.AstSymbolNode)
81
- assert isinstance(self.ir, ast.Module)
82
-
83
- mypy_node = node.gen.mypy_ast[0]
84
-
85
- if isinstance(mypy_node, MypyNodes.RefExpr) and mypy_node.node:
86
- node_full_name = mypy_node.node.fullname
87
- if "." in node_full_name:
88
- mod_name = node_full_name[: node_full_name.rindex(".")]
89
- else:
90
- mod_name = node_full_name
91
-
92
- if mod_name not in self.ir.py_mod_dep_map:
93
- self.__debug_print(
94
- f"Can't find a python file associated with {type(node)}::{node.loc}"
95
- )
96
- return
97
-
98
- mode_path = self.ir.py_mod_dep_map[mod_name]
99
- if mode_path.endswith(".jac"):
100
- return
101
-
102
- self.ir.py_raise_map[mod_name] = mode_path
103
- else:
104
- self.__debug_print(
105
- f"Collect python dependencies is not supported in {type(node)}::{node.loc}"
106
- )
107
-
108
80
  @staticmethod
109
81
  def __handle_node(
110
82
  func: Callable[[FuseTypeInfoPass, T], None]
@@ -126,7 +98,6 @@ class FuseTypeInfoPass(Pass):
126
98
  if len(node.gen.mypy_ast) == 1:
127
99
  func(self, node)
128
100
  self.__set_type_sym_table_link(node)
129
- self.__collect_python_dependencies(node)
130
101
 
131
102
  # Jac node has multiple mypy nodes linked to it
132
103
  elif len(node.gen.mypy_ast) > 1:
@@ -150,7 +121,6 @@ class FuseTypeInfoPass(Pass):
150
121
  )
151
122
  func(self, node)
152
123
  self.__set_type_sym_table_link(node)
153
- self.__collect_python_dependencies(node)
154
124
 
155
125
  # Special handing for BuiltinType
156
126
  elif isinstance(node, ast.BuiltinType):
@@ -266,6 +236,43 @@ class FuseTypeInfoPass(Pass):
266
236
  if len(node.gen.mypy_ast) == 0:
267
237
  return
268
238
 
239
+ # No need to run this handling in case of a previous type
240
+ # was set during the pass, if not then we hope that the
241
+ # mypy node has a type associated to it
242
+ if node.expr_type != "NoType":
243
+ return
244
+
245
+ # Check if the expression is a data spatial expression
246
+ # Support disconnectOp
247
+ if isinstance(node, ast.BinaryExpr):
248
+ if isinstance(node.op, ast.DisconnectOp):
249
+ node.expr_type = "builtins.bool"
250
+ return
251
+
252
+ # Support spwan and connectOp
253
+ elif (
254
+ isinstance(node.op, ast.ConnectOp)
255
+ or node.op.name == Tokens.KW_SPAWN.value
256
+ ):
257
+ if node.gen.mypy_ast[-1] in self.node_type_hash:
258
+ node.expr_type = (
259
+ self.__call_type_handler(
260
+ self.node_type_hash[node.gen.mypy_ast[-1]]
261
+ )
262
+ or node.expr_type
263
+ )
264
+ return
265
+
266
+ if isinstance(node, ast.EdgeRefTrailer) and any(
267
+ isinstance(k, ast.FilterCompr) for k in node.kid
268
+ ):
269
+ if node.gen.mypy_ast[-1] in self.node_type_hash:
270
+ node.expr_type = (
271
+ self.__call_type_handler(self.node_type_hash[node.gen.mypy_ast[-1]])
272
+ or node.expr_type
273
+ )
274
+ return
275
+
269
276
  # If the corrosponding mypy ast node type has stored here, get the values.
270
277
  mypy_node = node.gen.mypy_ast[0]
271
278
  if mypy_node in self.node_type_hash:
@@ -427,7 +434,14 @@ class FuseTypeInfoPass(Pass):
427
434
  @__handle_node
428
435
  def enter_special_var_ref(self, node: ast.SpecialVarRef) -> None:
429
436
  """Pass handler for SpecialVarRef nodes."""
430
- return self.enter_name(node)
437
+ if node.py_resolve_name() == Constants.ROOT:
438
+ if node.gen.mypy_ast[-1] in self.node_type_hash:
439
+ node.name_spec.expr_type = (
440
+ self.__call_type_handler(self.node_type_hash[node.gen.mypy_ast[-1]])
441
+ or node.name_spec.expr_type
442
+ )
443
+ else:
444
+ self.enter_name(node)
431
445
 
432
446
  @__handle_node
433
447
  def enter_edge_op_ref(self, node: ast.EdgeOpRef) -> None:
@@ -491,7 +505,7 @@ class FuseTypeInfoPass(Pass):
491
505
  self, mypy_type: MypyTypes.Overloaded
492
506
  ) -> Optional[str]:
493
507
  """Get type info from mypy type Overloaded."""
494
- return self.__call_type_handler(mypy_type.items[0])
508
+ return self.__call_type_handler(mypy_type.items[-1])
495
509
 
496
510
  def get_type_from_none_type(self, mypy_type: MypyTypes.NoneType) -> Optional[str]:
497
511
  """Get type info from mypy type NoneType."""
@@ -509,6 +523,12 @@ class FuseTypeInfoPass(Pass):
509
523
  """Get type info from mypy type TypeType."""
510
524
  return str(mypy_type.item)
511
525
 
526
+ def get_type_from_type_var_type(
527
+ self, mypy_type: MypyTypes.TypeVarType
528
+ ) -> Optional[str]:
529
+ """Get type info from mypy type TypeType."""
530
+ return str(mypy_type.name)
531
+
512
532
  def exit_assignment(self, node: ast.Assignment) -> None:
513
533
  """Add new symbols in the symbol table in case of self."""
514
534
  # This will fix adding new items to the class through self
@@ -590,6 +610,7 @@ class FuseTypeInfoPass(Pass):
590
610
  # right index slice is a range then it's type is the same as left
591
611
  if right.is_range:
592
612
  right.expr_type = left.expr_type
613
+ right.parent_of_type(ast.AtomTrailer).expr_type = node_type
593
614
  continue
594
615
 
595
616
  # left type is a dictionary
@@ -603,6 +624,7 @@ class FuseTypeInfoPass(Pass):
603
624
  continue
604
625
 
605
626
  right.expr_type = node_type
627
+ right.parent_of_type(ast.AtomTrailer).expr_type = node_type
606
628
 
607
629
  # Getting the correct symbol table and link it
608
630
  type_symtab: Optional[SymbolTable] = self.ir.sym_tab
@@ -621,6 +643,13 @@ class FuseTypeInfoPass(Pass):
621
643
  right.type_sym_tab = type_symtab
622
644
 
623
645
  else:
646
+ # Fix the symbolTable linking in case of type annotations
647
+ if left.type_sym_tab is None and isinstance(node.parent, ast.SubTag):
648
+ assert isinstance(left, ast.AstSymbolNode)
649
+ left.name_spec.type_sym_tab = self.ir.sym_tab.find_scope(
650
+ left.sym_name
651
+ )
652
+
624
653
  if left.type_sym_tab:
625
654
  right.name_spec.sym = left.type_sym_tab.lookup(right.sym_name)
626
655
  if right.name_spec.sym: