jaclang 0.0.1__py3-none-any.whl → 0.0.3__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/__init__.py +4 -0
- jaclang/cli/__init__.py +7 -0
- jaclang/cli/cli.jac +46 -0
- jaclang/cli/cmds.jac +14 -0
- jaclang/cli/impl/__init__.py +1 -0
- jaclang/cli/impl/cli_impl.jac +93 -0
- jaclang/cli/impl/cmds_impl.jac +26 -0
- jaclang/core/__init__.py +12 -0
- jaclang/core/impl/__init__.py +1 -0
- jaclang/core/impl/arch_impl.jac +112 -0
- jaclang/core/impl/element_impl.jac +95 -0
- jaclang/core/impl/exec_ctx_impl.jac +17 -0
- jaclang/core/impl/memory_impl.jac +57 -0
- jaclang/core/primitives.jac +104 -0
- jaclang/jac/__init__.py +1 -0
- jaclang/jac/absyntree.py +1787 -0
- jaclang/jac/constant.py +46 -0
- jaclang/jac/importer.py +130 -0
- jaclang/jac/lexer.py +538 -0
- jaclang/jac/parser.py +1474 -0
- jaclang/jac/passes/__init__.py +5 -0
- jaclang/jac/passes/blue/__init__.py +25 -0
- jaclang/jac/passes/blue/ast_build_pass.py +3190 -0
- jaclang/jac/passes/blue/blue_pygen_pass.py +1335 -0
- jaclang/jac/passes/blue/decl_def_match_pass.py +278 -0
- jaclang/jac/passes/blue/import_pass.py +75 -0
- jaclang/jac/passes/blue/sub_node_tab_pass.py +30 -0
- jaclang/jac/passes/blue/tests/__init__.py +1 -0
- jaclang/jac/passes/blue/tests/test_ast_build_pass.py +61 -0
- jaclang/jac/passes/blue/tests/test_blue_pygen_pass.py +117 -0
- jaclang/jac/passes/blue/tests/test_decl_def_match_pass.py +43 -0
- jaclang/jac/passes/blue/tests/test_import_pass.py +18 -0
- jaclang/jac/passes/blue/tests/test_sub_node_pass.py +26 -0
- jaclang/jac/passes/blue/tests/test_type_analyze_pass.py +53 -0
- jaclang/jac/passes/blue/type_analyze_pass.py +731 -0
- jaclang/jac/passes/ir_pass.py +154 -0
- jaclang/jac/passes/purple/__init__.py +17 -0
- jaclang/jac/passes/purple/impl/__init__.py +1 -0
- jaclang/jac/passes/purple/impl/purple_pygen_pass_impl.jac +289 -0
- jaclang/jac/passes/purple/purple_pygen_pass.jac +35 -0
- jaclang/jac/sym_table.py +127 -0
- jaclang/jac/tests/__init__.py +1 -0
- jaclang/jac/tests/fixtures/__init__.py +1 -0
- jaclang/jac/tests/fixtures/activity.py +10 -0
- jaclang/jac/tests/fixtures/fam.jac +68 -0
- jaclang/jac/tests/fixtures/hello_world.jac +5 -0
- jaclang/jac/tests/fixtures/lexer_fam.jac +61 -0
- jaclang/jac/tests/fixtures/stuff.jac +6 -0
- jaclang/jac/tests/test_importer.py +24 -0
- jaclang/jac/tests/test_lexer.py +57 -0
- jaclang/jac/tests/test_parser.py +50 -0
- jaclang/jac/tests/test_utils.py +12 -0
- jaclang/jac/transform.py +63 -0
- jaclang/jac/transpiler.py +69 -0
- jaclang/jac/utils.py +120 -0
- jaclang/utils/__init__.py +1 -0
- jaclang/utils/fstring_parser.py +73 -0
- jaclang/utils/log.py +9 -0
- jaclang/utils/sly/__init__.py +6 -0
- jaclang/utils/sly/docparse.py +62 -0
- jaclang/utils/sly/lex.py +510 -0
- jaclang/utils/sly/yacc.py +2398 -0
- jaclang/utils/test.py +81 -0
- jaclang/utils/tests/__init__.py +1 -0
- jaclang/utils/tests/test_fstring_parser.py +55 -0
- jaclang-0.0.3.dist-info/METADATA +12 -0
- jaclang-0.0.3.dist-info/RECORD +70 -0
- {jaclang-0.0.1.dist-info → jaclang-0.0.3.dist-info}/WHEEL +1 -1
- jaclang-0.0.3.dist-info/entry_points.txt +3 -0
- jaclang-0.0.3.dist-info/top_level.txt +1 -0
- jaclang-0.0.1.dist-info/METADATA +0 -7
- jaclang-0.0.1.dist-info/RECORD +0 -4
- jaclang-0.0.1.dist-info/top_level.txt +0 -1
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
"""Abstract class for IR Passes for Jac."""
|
|
2
|
+
from typing import Optional, TypeVar
|
|
3
|
+
|
|
4
|
+
import jaclang.jac.absyntree as ast
|
|
5
|
+
from jaclang.jac.transform import Transform
|
|
6
|
+
from jaclang.jac.utils import pascal_to_snake
|
|
7
|
+
|
|
8
|
+
T = TypeVar("T", bound=ast.AstNode)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Pass(Transform):
|
|
12
|
+
"""Abstract class for IR passes."""
|
|
13
|
+
|
|
14
|
+
def __init__(
|
|
15
|
+
self,
|
|
16
|
+
mod_path: str,
|
|
17
|
+
input_ir: ast.AstNode,
|
|
18
|
+
base_path: str = "",
|
|
19
|
+
prior: Optional[Transform] = None,
|
|
20
|
+
) -> None:
|
|
21
|
+
"""Initialize parser."""
|
|
22
|
+
self.term_signal = False
|
|
23
|
+
self.prune_signal = False
|
|
24
|
+
self.cur_node = input_ir # tracks current node during traversal
|
|
25
|
+
Transform.__init__(self, mod_path, input_ir, base_path, prior)
|
|
26
|
+
|
|
27
|
+
def before_pass(self) -> None:
|
|
28
|
+
"""Run once before pass."""
|
|
29
|
+
pass
|
|
30
|
+
|
|
31
|
+
def after_pass(self) -> None:
|
|
32
|
+
"""Run once after pass."""
|
|
33
|
+
pass
|
|
34
|
+
|
|
35
|
+
def enter_node(self, node: ast.AstNode) -> None:
|
|
36
|
+
"""Run on entering node."""
|
|
37
|
+
if hasattr(self, f"enter_{pascal_to_snake(type(node).__name__)}"):
|
|
38
|
+
getattr(self, f"enter_{pascal_to_snake(type(node).__name__)}")(node)
|
|
39
|
+
if isinstance(node, ast.Parse) and hasattr(self, f"enter_{node.name}"):
|
|
40
|
+
getattr(self, f"enter_{node.name}")(node)
|
|
41
|
+
|
|
42
|
+
def exit_node(self, node: ast.AstNode) -> None:
|
|
43
|
+
"""Run on exiting node."""
|
|
44
|
+
if hasattr(self, f"exit_{pascal_to_snake(type(node).__name__)}"):
|
|
45
|
+
getattr(self, f"exit_{pascal_to_snake(type(node).__name__)}")(node)
|
|
46
|
+
if isinstance(node, ast.Parse) and hasattr(self, f"exit_{node.name}"):
|
|
47
|
+
getattr(self, f"exit_{node.name}")(node)
|
|
48
|
+
|
|
49
|
+
def terminate(self) -> None:
|
|
50
|
+
"""Terminate traversal."""
|
|
51
|
+
self.term_signal = True
|
|
52
|
+
|
|
53
|
+
def prune(self) -> None:
|
|
54
|
+
"""Prune traversal."""
|
|
55
|
+
self.prune_signal = True
|
|
56
|
+
|
|
57
|
+
def get_all_sub_nodes(
|
|
58
|
+
self, node: ast.AstNode, typ: type[T], brute_force: bool = False
|
|
59
|
+
) -> list[T]:
|
|
60
|
+
"""Get all sub nodes of type."""
|
|
61
|
+
result = []
|
|
62
|
+
# Assumes pass built the sub node table
|
|
63
|
+
if not node:
|
|
64
|
+
return result
|
|
65
|
+
elif len(node._sub_node_tab) and not brute_force:
|
|
66
|
+
result.extend(node._sub_node_tab[typ] if typ in node._sub_node_tab else [])
|
|
67
|
+
elif len(node.kid):
|
|
68
|
+
if not brute_force:
|
|
69
|
+
raise ValueError(f"Node has no sub_node_tab. {node}")
|
|
70
|
+
# Brute force search
|
|
71
|
+
else:
|
|
72
|
+
for i in node.kid:
|
|
73
|
+
if isinstance(i, typ):
|
|
74
|
+
result.append(i)
|
|
75
|
+
result.extend(self.get_all_sub_nodes(i, typ, brute_force))
|
|
76
|
+
return result
|
|
77
|
+
|
|
78
|
+
def recalculate_parents(self, node: ast.AstNode) -> None:
|
|
79
|
+
"""Recalculate parents."""
|
|
80
|
+
if not node:
|
|
81
|
+
return
|
|
82
|
+
for i in node.kid:
|
|
83
|
+
if i:
|
|
84
|
+
i.parent = node
|
|
85
|
+
self.recalculate_parents(i)
|
|
86
|
+
|
|
87
|
+
# Transform Implementations
|
|
88
|
+
# -------------------------
|
|
89
|
+
def transform(self, ir: ast.AstNode) -> ast.AstNode:
|
|
90
|
+
"""Run pass."""
|
|
91
|
+
self.before_pass()
|
|
92
|
+
if not isinstance(ir, ast.AstNode):
|
|
93
|
+
raise ValueError("Current node is not an AstNode.")
|
|
94
|
+
self.traverse(ir)
|
|
95
|
+
self.after_pass()
|
|
96
|
+
# Checks if self.ir is created during traversal
|
|
97
|
+
return self.ir if hasattr(self, "ir") else ir
|
|
98
|
+
|
|
99
|
+
def traverse(self, node: ast.AstNode) -> ast.AstNode:
|
|
100
|
+
"""Traverse tree."""
|
|
101
|
+
if self.term_signal:
|
|
102
|
+
return node
|
|
103
|
+
self.cur_node = node
|
|
104
|
+
self.enter_node(node)
|
|
105
|
+
if not self.prune_signal:
|
|
106
|
+
for i in node.kid:
|
|
107
|
+
if i:
|
|
108
|
+
self.traverse(i)
|
|
109
|
+
else:
|
|
110
|
+
self.prune_signal = False
|
|
111
|
+
self.cur_node = node
|
|
112
|
+
self.exit_node(node)
|
|
113
|
+
return node
|
|
114
|
+
|
|
115
|
+
def update_code_loc(self) -> None:
|
|
116
|
+
"""Update code location."""
|
|
117
|
+
if not isinstance(self.cur_node, ast.AstNode):
|
|
118
|
+
self.ice("Current node is not an AstNode.")
|
|
119
|
+
self.cur_line = self.cur_node.line
|
|
120
|
+
if self.cur_node.mod_link:
|
|
121
|
+
self.rel_mod_path = self.cur_node.mod_link.rel_mod_path
|
|
122
|
+
|
|
123
|
+
def error(self, msg: str) -> None:
|
|
124
|
+
"""Pass Error."""
|
|
125
|
+
self.update_code_loc()
|
|
126
|
+
self.log_error(f"{msg}")
|
|
127
|
+
|
|
128
|
+
def warning(self, msg: str) -> None:
|
|
129
|
+
"""Pass Error."""
|
|
130
|
+
self.update_code_loc()
|
|
131
|
+
self.log_warning(f"{msg}")
|
|
132
|
+
|
|
133
|
+
def ice(self, msg: str) -> None:
|
|
134
|
+
"""Pass Error."""
|
|
135
|
+
if isinstance(self.cur_node, ast.AstNode):
|
|
136
|
+
self.cur_line = self.cur_node.line
|
|
137
|
+
self.log_error(f"ICE: Pass {self.__class__.__name__} - {msg}")
|
|
138
|
+
raise RuntimeError(
|
|
139
|
+
f"Internal Compiler Error: Pass {self.__class__.__name__} - {msg}"
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
class PrinterPass(Pass):
|
|
144
|
+
"""Printer Pass for Jac AST."""
|
|
145
|
+
|
|
146
|
+
def enter_node(self, node: ast.AstNode) -> None:
|
|
147
|
+
"""Run on entering node."""
|
|
148
|
+
print("Entering:", node)
|
|
149
|
+
super().enter_node(node)
|
|
150
|
+
|
|
151
|
+
def exit_node(self, node: ast.AstNode) -> None:
|
|
152
|
+
"""Run on exiting node."""
|
|
153
|
+
super().exit_node(node)
|
|
154
|
+
print("Exiting:", node)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""Collection of purple passes for Jac IR."""
|
|
2
|
+
from jaclang import jac_blue_import
|
|
3
|
+
from jaclang.jac.passes.blue import pass_schedule
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
purple = jac_blue_import("purple_pygen_pass")
|
|
7
|
+
PurplePygenPass = purple.PurplePygenPass # type: ignore
|
|
8
|
+
|
|
9
|
+
__all__ = [
|
|
10
|
+
"PurplePygenPass",
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
pass_schedule = [
|
|
15
|
+
*pass_schedule[:-1],
|
|
16
|
+
PurplePygenPass,
|
|
17
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""For setuptools to find this dir."""
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
"""First Jac pass bootstrapped in Jac"""
|
|
2
|
+
import:py from jaclang.jac.constant, Constants as C, EdgeDir;
|
|
3
|
+
|
|
4
|
+
:object:PurplePygenPass:ability:add_element_import
|
|
5
|
+
(arch: str) {
|
|
6
|
+
(<self>.preamble, f"from jaclang.core import {arch} as _jac_{arch}_")
|
|
7
|
+
|> <self>.emit_ln_unique;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
:object:PurplePygenPass:ability:add_exec_context {
|
|
11
|
+
(<self>.preamble, f"from jaclang.core import exec_ctx as {C.EXEC_CONTEXT}")
|
|
12
|
+
|> <self>.emit_ln_unique;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
:object:PurplePygenPass:ability:add_edge_directions {
|
|
16
|
+
(<self>.preamble, f"from jaclang.jac.constant import EdgeDir as {C.EDGE_DIR}")
|
|
17
|
+
|> <self>.emit_ln_unique;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
:object:PurplePygenPass:ability:needs_jac_import {
|
|
21
|
+
(<self>.preamble, "from jaclang import jac_purple_import as __jac_import__")
|
|
22
|
+
|> <self>.emit_ln_unique;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
"""
|
|
26
|
+
Sub objects.
|
|
27
|
+
|
|
28
|
+
name: Name,
|
|
29
|
+
arch_type: Token,
|
|
30
|
+
doc: Optional[DocString],
|
|
31
|
+
decorators: Optional["Decorators"],
|
|
32
|
+
access: Optional[Token],
|
|
33
|
+
base_classes: "BaseClasses",
|
|
34
|
+
body: Optional["ArchBlock"],
|
|
35
|
+
"""
|
|
36
|
+
:object:PurplePygenPass:ability:exit_architype
|
|
37
|
+
(nd: ast.Architype) {
|
|
38
|
+
|> <self>.add_exec_context;
|
|
39
|
+
if nd.decorators {
|
|
40
|
+
(nd, nd.decorators.meta["py_code"]) |> <self>.emit_ln;
|
|
41
|
+
}
|
|
42
|
+
arch_type=nd.arch_type.name;
|
|
43
|
+
arch_insert = "";
|
|
44
|
+
if arch_type == Tok.KW_OBJECT {
|
|
45
|
+
"Object" |> <self>.add_element_import;
|
|
46
|
+
arch_insert = C.OBJECT_CLASS;
|
|
47
|
+
}
|
|
48
|
+
elif arch_type == Tok.KW_NODE {
|
|
49
|
+
"Node" |> <self>.add_element_import;
|
|
50
|
+
arch_insert = C.NODE_CLASS;
|
|
51
|
+
}
|
|
52
|
+
elif arch_type == Tok.KW_EDGE {
|
|
53
|
+
"Edge" |> <self>.add_element_import;
|
|
54
|
+
arch_insert = C.EDGE_CLASS;
|
|
55
|
+
}
|
|
56
|
+
elif arch_type == Tok.KW_WALKER {
|
|
57
|
+
"Walker" |> <self>.add_element_import;
|
|
58
|
+
arch_insert = C.WALKER_CLASS;
|
|
59
|
+
}
|
|
60
|
+
if nd.base_classes.base_classes |> len {
|
|
61
|
+
(nd, f"class {nd.name.meta['py_code']}"
|
|
62
|
+
f"({nd.base_classes.meta['py_code']}, {arch_insert}):")
|
|
63
|
+
|> <self>.emit_ln;
|
|
64
|
+
} else {
|
|
65
|
+
(nd, f"class {nd.name.meta['py_code']}({arch_insert}):") |> <self>.emit_ln;
|
|
66
|
+
}
|
|
67
|
+
<self>.indent_level += 1;
|
|
68
|
+
if nd.doc {
|
|
69
|
+
(nd, nd.doc.meta["py_code"]) |> <self>.emit_ln; }
|
|
70
|
+
if nd.body {
|
|
71
|
+
(nd, nd.body.meta["py_code"]) |> <self>.emit_ln; }
|
|
72
|
+
else {
|
|
73
|
+
nd.name.meta["py_code"] |> <self>.decl_def_missing; }
|
|
74
|
+
<self>.indent_level -= 1;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
"""Sub objects.
|
|
79
|
+
|
|
80
|
+
name: Name,
|
|
81
|
+
is_func: bool,
|
|
82
|
+
is_async: bool,
|
|
83
|
+
doc: Optional[DocString],
|
|
84
|
+
decorators: Optional["Decorators"],
|
|
85
|
+
access: Optional[Token],
|
|
86
|
+
signature: Optional["FuncSignature | TypeSpec | EventSignature"],
|
|
87
|
+
body: Optional["CodeBlock"],
|
|
88
|
+
arch_attached: Optional["ArchBlock"] = None,
|
|
89
|
+
"""
|
|
90
|
+
:object:PurplePygenPass:ability:exit_ability
|
|
91
|
+
(nd: ast.Ability) {
|
|
92
|
+
"Object" |> <self>.add_element_import;
|
|
93
|
+
if nd.decorators {
|
|
94
|
+
(nd, nd.decorators.meta["py_code"]) |> <self>.emit_ln;
|
|
95
|
+
}
|
|
96
|
+
if nd.signature:>type == ast.EventSignature and nd.arch_attached and
|
|
97
|
+
nd.signature.event.name == Tok.KW_ENTRY {
|
|
98
|
+
type_list = nd.signature.arch_tag_info?.meta?["py_code"] ?: "";
|
|
99
|
+
(nd, f"@{C.OBJECT_CLASS}._jac_on_entry([{type_list.replace('|', ', ')}])") |> <self>.emit_ln;
|
|
100
|
+
}
|
|
101
|
+
elif nd.signature:>type == ast.EventSignature and nd.arch_attached and
|
|
102
|
+
nd.signature.event.name == Tok.KW_EXIT {
|
|
103
|
+
type_list = nd.signature.arch_tag_info?.meta?["py_code"] ?: "";
|
|
104
|
+
(nd, f"@{C.OBJECT_CLASS}._jac_on_exit([{type_list.replace('|', ', ')}])") |> <self>.emit_ln;
|
|
105
|
+
}
|
|
106
|
+
if nd.signature:>type in [ast.FuncSignature, ast.EventSignature] {
|
|
107
|
+
if nd.arch_attached and not nd.is_static {
|
|
108
|
+
(nd, f"def {nd.name.meta['py_code']}(self{nd.signature.meta['py_code']}:")
|
|
109
|
+
|> <self>.emit_ln;
|
|
110
|
+
} else {
|
|
111
|
+
if nd.arch_attached and nd.is_static {
|
|
112
|
+
(nd, f"@classmethod") |> <self>.emit_ln;
|
|
113
|
+
}
|
|
114
|
+
(nd, f"def {nd.name.meta['py_code']}({nd.signature.meta['py_code']}:")
|
|
115
|
+
|> <self>.emit_ln;
|
|
116
|
+
}
|
|
117
|
+
} else {
|
|
118
|
+
if nd.arch_attached {
|
|
119
|
+
(nd, f"def {nd.name.meta['py_code']}(self):") |> <self>.emit_ln;
|
|
120
|
+
} else {
|
|
121
|
+
(nd, f"def {nd.name.meta['py_code']}():") |> <self>.emit_ln;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
<self>.indent_level += 1;
|
|
125
|
+
if nd.doc {
|
|
126
|
+
(nd, nd.doc.meta["py_code"]) |> <self>.emit_ln;
|
|
127
|
+
}
|
|
128
|
+
if nd.body {
|
|
129
|
+
(nd, "try:") |> <self>.emit_ln;
|
|
130
|
+
<self>.indent_level += 1;
|
|
131
|
+
(nd, nd.body.meta["py_code"]) |> <self>.emit;
|
|
132
|
+
<self>.indent_level -= 1;
|
|
133
|
+
(nd) |> <self>.emit_jac_error_handler;
|
|
134
|
+
} else {
|
|
135
|
+
nd.name.meta["py_code"] |> <self>.decl_def_missing;
|
|
136
|
+
}
|
|
137
|
+
<self>.indent_level -= 1;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
"""Sub objects.
|
|
142
|
+
|
|
143
|
+
event: Token,
|
|
144
|
+
arch_tag_info: Optional["TypeList | TypeSpec"],
|
|
145
|
+
return_type: Optional["TypeSpec"],
|
|
146
|
+
"""
|
|
147
|
+
:object:PurplePygenPass:ability:exit_event_signature
|
|
148
|
+
(nd: ast.EventSignature) {
|
|
149
|
+
if (nd.parent|>type) == ast.Ability and nd.parent.arch_attached {
|
|
150
|
+
(nd, ", ") |> <self>.emit;
|
|
151
|
+
}
|
|
152
|
+
if nd.arch_tag_info {
|
|
153
|
+
(nd, f"{C.HERE}: {nd.arch_tag_info.meta['py_code']})")
|
|
154
|
+
|> <self>.emit;
|
|
155
|
+
} else {
|
|
156
|
+
(nd, f"{C.HERE})") |> <self>.emit;
|
|
157
|
+
}
|
|
158
|
+
if nd.return_type {
|
|
159
|
+
(nd, f" -> {nd.return_type.meta['py_code']}") |> <self>.emit;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
"""Sub objects.
|
|
165
|
+
|
|
166
|
+
ctrl: Token,
|
|
167
|
+
"""
|
|
168
|
+
:object:PurplePygenPass:ability:exit_ctrl_stmt
|
|
169
|
+
(nd: ast.CtrlStmt) {
|
|
170
|
+
if nd.ctrl.name == Tok.KW_SKIP {
|
|
171
|
+
(nd, "return") |> <self>.emit_ln;
|
|
172
|
+
} else {
|
|
173
|
+
<super>.exit_ctrl_stmt(nd);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
"""Sub objects.
|
|
179
|
+
|
|
180
|
+
vis_type: Optional[Token],
|
|
181
|
+
target: ExprType,
|
|
182
|
+
else_body: Optional["ElseStmt"],
|
|
183
|
+
from_walker: bool = False,
|
|
184
|
+
"""
|
|
185
|
+
:object:PurplePygenPass:ability:exit_visit_stmt
|
|
186
|
+
(nd: ast.VisitStmt) {
|
|
187
|
+
vis_type = nd.vis_type?.value ?: "";
|
|
188
|
+
loc = f"{'self' if nd.from_walker else C.HERE}";
|
|
189
|
+
(nd, f"if not {loc}.{C.WALKER_VISIT}({nd.target.meta['py_code']}): " )
|
|
190
|
+
|> <self>.emit_ln;
|
|
191
|
+
<self>.indent_level += 1;
|
|
192
|
+
if nd.else_body {
|
|
193
|
+
(nd, nd.else_body.body.meta["py_code"]) |> <self>.emit;
|
|
194
|
+
} else {
|
|
195
|
+
(nd, 'pass') |> <self>.emit_ln;
|
|
196
|
+
}
|
|
197
|
+
<self>.indent_level -= 1;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
"""Sub objects.
|
|
202
|
+
from_walker: bool = False,
|
|
203
|
+
"""
|
|
204
|
+
:object:PurplePygenPass:ability:exit_disengage_stmt
|
|
205
|
+
(nd: ast.VisitStmt) {
|
|
206
|
+
loc = f"{'self' if nd.from_walker else C.HERE}";
|
|
207
|
+
(nd, f"return {loc}.{C.DISENGAGE}()") |> <self>.emit_ln;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
"""Sub objects.
|
|
212
|
+
|
|
213
|
+
left: ExprType,
|
|
214
|
+
right: ExprType,
|
|
215
|
+
op: Token,
|
|
216
|
+
"""
|
|
217
|
+
:object:PurplePygenPass:ability:exit_binary_expr
|
|
218
|
+
(nd: ast.BinaryExpr) {
|
|
219
|
+
if (nd.op|>type) == ast.ConnectOp {
|
|
220
|
+
(nd, f"{nd.left.meta['py_code']}.{C.CONNECT_NODE}({nd.right.meta['py_code']}, {nd.op.meta['py_code']})")
|
|
221
|
+
|> <self>.emit;
|
|
222
|
+
} else {
|
|
223
|
+
<super>.exit_binary_expr(nd);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
"""Sub objects.
|
|
229
|
+
|
|
230
|
+
filter_type: Optional[ExprType],
|
|
231
|
+
filter_cond: Optional[FilterCompr],
|
|
232
|
+
edge_dir: EdgeDir,
|
|
233
|
+
"""
|
|
234
|
+
:object:PurplePygenPass:ability:exit_edge_op_ref
|
|
235
|
+
(nd: ast.EdgeOpRef) {
|
|
236
|
+
edge_dir = f"{C.EDGE_DIR}.IN" if nd.edge_dir == EdgeDir.IN
|
|
237
|
+
else f"{C.EDGE_DIR}.OUT" if nd.edge_dir == EdgeDir.OUT
|
|
238
|
+
else f"{C.EDGE_DIR}.ANY";
|
|
239
|
+
if nd.filter_type and nd.filter_cond {
|
|
240
|
+
(nd, f"[{C.JAC_TMP} for {C.JAC_TMP} in {C.HERE}.{C.EDGES_TO_NODE}({edge_dir})"
|
|
241
|
+
f" if isinstance({C.JAC_TMP}, {nd.filter_type.meta['py_code']})"
|
|
242
|
+
f" and {nd.filter_cond.meta['py_code'].replace(C.PATCH, C.JAC_TMP)}]")
|
|
243
|
+
|> <self>.emit;
|
|
244
|
+
}
|
|
245
|
+
elif nd.filter_type {
|
|
246
|
+
(nd, f"[{C.JAC_TMP} for {C.JAC_TMP} in {C.HERE}.{C.EDGES_TO_NODE}({edge_dir})"
|
|
247
|
+
f" if isinstance({C.JAC_TMP}, {nd.filter_type.meta['py_code']})]")
|
|
248
|
+
|> <self>.emit;
|
|
249
|
+
}
|
|
250
|
+
else {
|
|
251
|
+
(nd, f"{C.HERE}.{C.EDGES_TO_NODE}({edge_dir})")
|
|
252
|
+
|> <self>.emit;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
"""Sub objects.
|
|
258
|
+
|
|
259
|
+
conn_type: Optional[ExprType],
|
|
260
|
+
conn_assign: Optional[AssignmentList],
|
|
261
|
+
edge_dir: EdgeDir,
|
|
262
|
+
"""
|
|
263
|
+
:object:PurplePygenPass:ability:exit_connect_op
|
|
264
|
+
(nd: ast.ConnectOp) {
|
|
265
|
+
"Edge" |> <self>.add_element_import;
|
|
266
|
+
|> <self>.add_edge_directions;
|
|
267
|
+
|
|
268
|
+
if nd.conn_type {
|
|
269
|
+
(nd, f"{nd.conn_type.meta['py_code']}.{C.WITH_DIR}({C.EDGE_DIR}.{nd.edge_dir.name}") |> <self>.emit;
|
|
270
|
+
} else {
|
|
271
|
+
(nd, f"{C.EDGE_CLASS}().{C.WITH_DIR}({C.EDGE_DIR}.{nd.edge_dir.name}") |> <self>.emit;
|
|
272
|
+
}
|
|
273
|
+
if nd.conn_assign {
|
|
274
|
+
(nd, f", {nd.conn_assign.meta['py_code']})") |> <self>.emit;
|
|
275
|
+
} else {
|
|
276
|
+
(nd, ")") |> <self>.emit;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
"""Sub objects.
|
|
281
|
+
|
|
282
|
+
conn_type: Optional[ExprType],
|
|
283
|
+
conn_assign: Optional[AssignmentList],
|
|
284
|
+
edge_dir: EdgeDir,
|
|
285
|
+
"""
|
|
286
|
+
:object:PurplePygenPass:ability:exit_disconnect_op
|
|
287
|
+
(nd: ast.DisconnectOp) {
|
|
288
|
+
|
|
289
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""First Jac pass bootstrapped in Jac"""
|
|
2
|
+
import:py jaclang.jac.absyntree as ast;
|
|
3
|
+
import:py from jaclang.jac.passes.blue, BluePygenPass;
|
|
4
|
+
import:py from jaclang.core, Object, Node, Edge, Walker;
|
|
5
|
+
import:py from jaclang.jac.lexer, Tokens as Tok;
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
include:jac impl.purple_pygen_pass_impl;
|
|
9
|
+
|
|
10
|
+
"""
|
|
11
|
+
This pass leverages data spacial lib to provide code
|
|
12
|
+
gen for full Jac language. It is bootstrapped in Jac blue.
|
|
13
|
+
"""
|
|
14
|
+
object PurplePygenPass:BluePygenPass {
|
|
15
|
+
|
|
16
|
+
#*
|
|
17
|
+
These add functions generate necessary imports
|
|
18
|
+
given code being generated.
|
|
19
|
+
*#
|
|
20
|
+
can add_element_import(arch: str);
|
|
21
|
+
can add_exec_context;
|
|
22
|
+
can add_edge_directions;
|
|
23
|
+
can needs_jac_import;
|
|
24
|
+
|
|
25
|
+
can exit_architype(nd: ast.Architype);
|
|
26
|
+
can exit_ability(nd: ast.Ability);
|
|
27
|
+
can exit_event_signature(nd: ast.EventSignature);
|
|
28
|
+
can exit_ctrl_stmt(nd: ast.CtrlStmt);
|
|
29
|
+
can exit_visit_stmt(nd: ast.VisitStmt);
|
|
30
|
+
can exit_disengage_stmt(nd: ast.DisengageStmt);
|
|
31
|
+
can exit_binary_expr(nd: ast.BinaryExpr);
|
|
32
|
+
can exit_edge_op_ref(nd: ast.EdgeOpRef);
|
|
33
|
+
can exit_connect_op(nd: ast.ConnectOp);
|
|
34
|
+
can exit_disconnect_op(nd: ast.DisconnectOp);
|
|
35
|
+
}
|
jaclang/jac/sym_table.py
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"""Jac Symbol Table."""
|
|
2
|
+
import pprint
|
|
3
|
+
from typing import Optional, TypeVar
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
import jaclang.jac.absyntree as ast
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Symbol:
|
|
10
|
+
"""Symbol."""
|
|
11
|
+
|
|
12
|
+
def __init__(
|
|
13
|
+
self,
|
|
14
|
+
name: str,
|
|
15
|
+
node: Optional[ast.AstNode] = None,
|
|
16
|
+
) -> None:
|
|
17
|
+
"""Initialize."""
|
|
18
|
+
self.name = name
|
|
19
|
+
self.node = node
|
|
20
|
+
|
|
21
|
+
def pretty_print(self) -> str:
|
|
22
|
+
"""Pretty print the symbol."""
|
|
23
|
+
return pprint.pformat(vars(self), indent=2)
|
|
24
|
+
|
|
25
|
+
def __repr__(self) -> str:
|
|
26
|
+
"""Representation for printing Symbols."""
|
|
27
|
+
return f"{self.pretty_print()}"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
T = TypeVar("T", bound=Symbol)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class TypedSymbol(Symbol):
|
|
34
|
+
"""Typed Symbol."""
|
|
35
|
+
|
|
36
|
+
def __init__(
|
|
37
|
+
self,
|
|
38
|
+
name: str,
|
|
39
|
+
typ: type,
|
|
40
|
+
node: ast.AstNode,
|
|
41
|
+
access: Optional[str] = None,
|
|
42
|
+
) -> None:
|
|
43
|
+
"""Initialize."""
|
|
44
|
+
self.typ = typ
|
|
45
|
+
self.access = access
|
|
46
|
+
super().__init__(name, node)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class DefDeclSymbol(Symbol):
|
|
50
|
+
"""DefDecl Symbol."""
|
|
51
|
+
|
|
52
|
+
def __init__(
|
|
53
|
+
self,
|
|
54
|
+
name: str,
|
|
55
|
+
node: Optional[ast.AstNode] = None,
|
|
56
|
+
other_node: Optional[ast.AstNode] = None,
|
|
57
|
+
has_def: bool = False,
|
|
58
|
+
has_decl: bool = False,
|
|
59
|
+
) -> None:
|
|
60
|
+
"""Initialize."""
|
|
61
|
+
self.other_node = other_node
|
|
62
|
+
self.has_def = has_def
|
|
63
|
+
self.has_decl = has_decl
|
|
64
|
+
super().__init__(name, node)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class SymbolTable:
|
|
68
|
+
"""Symbol Table.
|
|
69
|
+
|
|
70
|
+
Functions as a stack by default, can create a separate table in pass
|
|
71
|
+
if keeping track of multiple scopes is necessary.
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
def __init__(
|
|
75
|
+
self,
|
|
76
|
+
scope_name: str = "",
|
|
77
|
+
parent: Optional["SymbolTable"] = None,
|
|
78
|
+
) -> None:
|
|
79
|
+
"""Initialize."""
|
|
80
|
+
self.scope_name = scope_name
|
|
81
|
+
self.parent = parent
|
|
82
|
+
self.tab: dict = {}
|
|
83
|
+
|
|
84
|
+
def lookup(self, name: str, deep: bool = True) -> Optional[T]:
|
|
85
|
+
"""Lookup a variable in the symbol table."""
|
|
86
|
+
if name in self.tab:
|
|
87
|
+
return self.tab[name]
|
|
88
|
+
if deep and self.parent:
|
|
89
|
+
return self.parent.lookup(name, deep)
|
|
90
|
+
return None
|
|
91
|
+
|
|
92
|
+
def set(self, sym: T, fresh_only: bool = False) -> bool:
|
|
93
|
+
"""Set a variable in the symbol table."""
|
|
94
|
+
if fresh_only and sym.name in self.tab:
|
|
95
|
+
return False
|
|
96
|
+
self.tab[sym.name] = sym
|
|
97
|
+
return True
|
|
98
|
+
|
|
99
|
+
def update(self, sym: T, deep: bool = False) -> bool:
|
|
100
|
+
"""Set a variable in the symbol table."""
|
|
101
|
+
if sym.name in self.tab:
|
|
102
|
+
self.tab[sym.name] = T
|
|
103
|
+
return True
|
|
104
|
+
elif deep and self.parent:
|
|
105
|
+
return self.parent.update(sym)
|
|
106
|
+
return False
|
|
107
|
+
|
|
108
|
+
def push(self, scope_name: str = "") -> "SymbolTable":
|
|
109
|
+
"""Push a new scope onto the symbol table."""
|
|
110
|
+
return SymbolTable(scope_name, self)
|
|
111
|
+
|
|
112
|
+
def pop(self) -> "SymbolTable":
|
|
113
|
+
"""Pop the current scope off the symbol table."""
|
|
114
|
+
return self.parent if self.parent else self
|
|
115
|
+
|
|
116
|
+
def pretty_print(self) -> str:
|
|
117
|
+
"""Pretty print the symbol table and return the result as a string."""
|
|
118
|
+
output = f"Scope: {self.scope_name}\n"
|
|
119
|
+
output += pprint.pformat(self.tab)
|
|
120
|
+
if self.parent:
|
|
121
|
+
output += f"\nParent Scope: {self.parent.scope_name}\n"
|
|
122
|
+
output += self.parent.pretty_print()
|
|
123
|
+
return output
|
|
124
|
+
|
|
125
|
+
def __repr__(self) -> str:
|
|
126
|
+
"""Return the symbol table as a string."""
|
|
127
|
+
return self.pretty_print()
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Tests for Jac tools."""
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Various fixtures for testing"""
|