samlang 0.1.0__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.
- samlang/__init__.py +6 -0
- samlang/__main__.py +8 -0
- samlang/ast_nodes.py +641 -0
- samlang/cli.py +84 -0
- samlang/environment.py +386 -0
- samlang/errors.py +74 -0
- samlang/interpreter.py +1791 -0
- samlang/lexer.py +386 -0
- samlang/lsp/__init__.py +8 -0
- samlang/lsp/__main__.py +15 -0
- samlang/lsp/analyzer.py +602 -0
- samlang/lsp/protocol.py +80 -0
- samlang/lsp/server.py +166 -0
- samlang/parser.py +1968 -0
- samlang/runner.py +56 -0
- samlang/stdlib.py +73 -0
- samlang/tokens.py +136 -0
- samlang-0.1.0.dist-info/METADATA +306 -0
- samlang-0.1.0.dist-info/RECORD +23 -0
- samlang-0.1.0.dist-info/WHEEL +5 -0
- samlang-0.1.0.dist-info/entry_points.txt +2 -0
- samlang-0.1.0.dist-info/licenses/LICENSE +51 -0
- samlang-0.1.0.dist-info/top_level.txt +1 -0
samlang/__init__.py
ADDED
samlang/__main__.py
ADDED
samlang/ast_nodes.py
ADDED
|
@@ -0,0 +1,641 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Abstract Syntax Tree (AST) Nodes for SamLang.
|
|
3
|
+
Author: Samrane Ahmed
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from typing import List, Optional, Tuple
|
|
7
|
+
from samlang.tokens import Token, Position
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class ASTNode:
|
|
11
|
+
"""Base class for all Abstract Syntax Tree nodes in SamLang."""
|
|
12
|
+
def __init__(self, pos_start: Optional[Position] = None, pos_end: Optional[Position] = None):
|
|
13
|
+
self.pos_start: Optional[Position] = pos_start
|
|
14
|
+
self.pos_end: Optional[Position] = pos_end
|
|
15
|
+
|
|
16
|
+
def __repr__(self) -> str:
|
|
17
|
+
return f"{self.__class__.__name__}()"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class NumberNode(ASTNode):
|
|
21
|
+
"""Represents numeric literals (integers and floats)."""
|
|
22
|
+
def __init__(self, tok: Token):
|
|
23
|
+
super().__init__(tok.pos_start, tok.pos_end)
|
|
24
|
+
self.tok: Token = tok
|
|
25
|
+
self.value: float = tok.value
|
|
26
|
+
|
|
27
|
+
def __repr__(self) -> str:
|
|
28
|
+
return f"NumberNode({self.value})"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class StringNode(ASTNode):
|
|
32
|
+
"""Represents string literals."""
|
|
33
|
+
def __init__(self, tok: Token):
|
|
34
|
+
super().__init__(tok.pos_start, tok.pos_end)
|
|
35
|
+
self.tok: Token = tok
|
|
36
|
+
self.value: str = tok.value
|
|
37
|
+
|
|
38
|
+
def __repr__(self) -> str:
|
|
39
|
+
return f"StringNode({repr(self.value)})"
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class VarAccessNode(ASTNode):
|
|
43
|
+
"""Represents reading a variable value."""
|
|
44
|
+
def __init__(self, var_name_tok: Token):
|
|
45
|
+
super().__init__(var_name_tok.pos_start, var_name_tok.pos_end)
|
|
46
|
+
self.var_name_tok: Token = var_name_tok
|
|
47
|
+
self.var_name: str = var_name_tok.value
|
|
48
|
+
|
|
49
|
+
def __repr__(self) -> str:
|
|
50
|
+
return f"VarAccessNode({self.var_name})"
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class VarAssignNode(ASTNode):
|
|
54
|
+
"""Represents assigning a value to a variable (let x = expr or x = expr)."""
|
|
55
|
+
def __init__(self, var_name_tok: Token, value_node: ASTNode):
|
|
56
|
+
super().__init__(var_name_tok.pos_start, value_node.pos_end)
|
|
57
|
+
self.var_name_tok: Token = var_name_tok
|
|
58
|
+
self.var_name: str = var_name_tok.value
|
|
59
|
+
self.value_node: ASTNode = value_node
|
|
60
|
+
|
|
61
|
+
def __repr__(self) -> str:
|
|
62
|
+
return f"VarAssignNode({self.var_name} = {self.value_node})"
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class BinOpNode(ASTNode):
|
|
66
|
+
"""Represents binary operations (e.g. left + right, left * right)."""
|
|
67
|
+
def __init__(self, left_node: ASTNode, op_tok: Token, right_node: ASTNode):
|
|
68
|
+
super().__init__(left_node.pos_start, right_node.pos_end)
|
|
69
|
+
self.left_node: ASTNode = left_node
|
|
70
|
+
self.op_tok: Token = op_tok
|
|
71
|
+
self.right_node: ASTNode = right_node
|
|
72
|
+
|
|
73
|
+
def __repr__(self) -> str:
|
|
74
|
+
return f"BinOpNode({self.left_node} {self.op_tok.type} {self.right_node})"
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class UnaryOpNode(ASTNode):
|
|
78
|
+
"""Represents unary operations (e.g. -x, +x)."""
|
|
79
|
+
def __init__(self, op_tok: Token, node: ASTNode):
|
|
80
|
+
super().__init__(op_tok.pos_start, node.pos_end)
|
|
81
|
+
self.op_tok: Token = op_tok
|
|
82
|
+
self.node: ASTNode = node
|
|
83
|
+
|
|
84
|
+
def __repr__(self) -> str:
|
|
85
|
+
return f"UnaryOpNode({self.op_tok.type} {self.node})"
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class PrintNode(ASTNode):
|
|
89
|
+
"""Represents the built-in print, println, and echo statements."""
|
|
90
|
+
def __init__(self, expr_nodes: List[ASTNode], newline: bool = True, pos_start: Optional[Position] = None, pos_end: Optional[Position] = None):
|
|
91
|
+
super().__init__(pos_start, pos_end)
|
|
92
|
+
self.expr_nodes: List[ASTNode] = expr_nodes
|
|
93
|
+
self.newline: bool = newline
|
|
94
|
+
|
|
95
|
+
def __repr__(self) -> str:
|
|
96
|
+
return f"PrintNode({self.expr_nodes}, newline={self.newline})"
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class BooleanNode(ASTNode):
|
|
100
|
+
"""Represents boolean literals (true and false)."""
|
|
101
|
+
def __init__(self, tok: Token):
|
|
102
|
+
super().__init__(tok.pos_start, tok.pos_end)
|
|
103
|
+
self.tok: Token = tok
|
|
104
|
+
self.value: bool = bool(tok.value)
|
|
105
|
+
|
|
106
|
+
def __repr__(self) -> str:
|
|
107
|
+
return f"BooleanNode({self.value})"
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class NullNode(ASTNode):
|
|
111
|
+
"""Represents the null / nil literal."""
|
|
112
|
+
def __init__(self, tok: Token):
|
|
113
|
+
super().__init__(tok.pos_start, tok.pos_end)
|
|
114
|
+
self.tok: Token = tok
|
|
115
|
+
self.value = None
|
|
116
|
+
|
|
117
|
+
def __repr__(self) -> str:
|
|
118
|
+
return "NullNode()"
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
class BlockNode(ASTNode):
|
|
122
|
+
"""Represents a scoped block of statements { ... }."""
|
|
123
|
+
def __init__(self, statements: List[ASTNode], pos_start: Optional[Position] = None, pos_end: Optional[Position] = None):
|
|
124
|
+
super().__init__(pos_start, pos_end)
|
|
125
|
+
self.statements: List[ASTNode] = statements
|
|
126
|
+
|
|
127
|
+
def __repr__(self) -> str:
|
|
128
|
+
return f"BlockNode({len(self.statements)} stmts)"
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
class FuncDefNode(ASTNode):
|
|
132
|
+
"""Represents a function definition: func name(arg1, arg2) { body }."""
|
|
133
|
+
def __init__(self, func_name_tok: Token, arg_name_toks: List[Token], body_node: ASTNode):
|
|
134
|
+
super().__init__(func_name_tok.pos_start, body_node.pos_end)
|
|
135
|
+
self.func_name_tok: Token = func_name_tok
|
|
136
|
+
self.func_name: str = func_name_tok.value
|
|
137
|
+
self.arg_name_toks: List[Token] = arg_name_toks
|
|
138
|
+
self.arg_names: List[str] = [tok.value for tok in arg_name_toks]
|
|
139
|
+
self.body_node: ASTNode = body_node
|
|
140
|
+
|
|
141
|
+
def __repr__(self) -> str:
|
|
142
|
+
return f"FuncDefNode({self.func_name}({', '.join(self.arg_names)}))"
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
class FuncCallNode(ASTNode):
|
|
146
|
+
"""Represents a function call: func_name(expr1, expr2)."""
|
|
147
|
+
def __init__(self, call_node: ASTNode, arg_nodes: List[ASTNode], pos_end: Optional[Position] = None):
|
|
148
|
+
super().__init__(call_node.pos_start, pos_end or call_node.pos_end)
|
|
149
|
+
self.call_node: ASTNode = call_node
|
|
150
|
+
self.arg_nodes: List[ASTNode] = arg_nodes
|
|
151
|
+
|
|
152
|
+
def __repr__(self) -> str:
|
|
153
|
+
return f"FuncCallNode({self.call_node}({len(self.arg_nodes)} args))"
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
class ReturnNode(ASTNode):
|
|
157
|
+
"""Represents a return statement: return expr;."""
|
|
158
|
+
def __init__(self, value_node: Optional[ASTNode], pos_start: Position, pos_end: Optional[Position] = None):
|
|
159
|
+
super().__init__(pos_start, pos_end or (value_node.pos_end if value_node else pos_start))
|
|
160
|
+
self.value_node: Optional[ASTNode] = value_node
|
|
161
|
+
|
|
162
|
+
def __repr__(self) -> str:
|
|
163
|
+
return f"ReturnNode({self.value_node})"
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
class IfNode(ASTNode):
|
|
167
|
+
"""Represents conditional branching: if cond { } elif cond { } else { }."""
|
|
168
|
+
def __init__(self, cases: List[Tuple[ASTNode, ASTNode]], else_case: Optional[ASTNode] = None, pos_start: Optional[Position] = None, pos_end: Optional[Position] = None):
|
|
169
|
+
super().__init__(pos_start, pos_end)
|
|
170
|
+
self.cases: List[Tuple[ASTNode, ASTNode]] = cases
|
|
171
|
+
self.else_case: Optional[ASTNode] = else_case
|
|
172
|
+
|
|
173
|
+
def __repr__(self) -> str:
|
|
174
|
+
return f"IfNode({len(self.cases)} cases, has_else={self.else_case is not None})"
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
class WhileNode(ASTNode):
|
|
178
|
+
"""Represents while loops: while cond { body }."""
|
|
179
|
+
def __init__(self, condition_node: ASTNode, body_node: ASTNode, pos_start: Optional[Position] = None, pos_end: Optional[Position] = None):
|
|
180
|
+
super().__init__(pos_start, pos_end or body_node.pos_end)
|
|
181
|
+
self.condition_node: ASTNode = condition_node
|
|
182
|
+
self.body_node: ASTNode = body_node
|
|
183
|
+
|
|
184
|
+
def __repr__(self) -> str:
|
|
185
|
+
return f"WhileNode({self.condition_node})"
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
class ForNode(ASTNode):
|
|
189
|
+
"""Represents a C-style for loop: for (init; cond; step) { body }."""
|
|
190
|
+
def __init__(self, init_node: Optional[ASTNode], cond_node: Optional[ASTNode], step_node: Optional[ASTNode], body_node: ASTNode, pos_start: Optional[Position] = None, pos_end: Optional[Position] = None):
|
|
191
|
+
super().__init__(pos_start, pos_end or body_node.pos_end)
|
|
192
|
+
self.init_node: Optional[ASTNode] = init_node
|
|
193
|
+
self.cond_node: Optional[ASTNode] = cond_node
|
|
194
|
+
self.step_node: Optional[ASTNode] = step_node
|
|
195
|
+
self.body_node: ASTNode = body_node
|
|
196
|
+
|
|
197
|
+
def __repr__(self) -> str:
|
|
198
|
+
return f"ForNode({self.init_node}; {self.cond_node}; {self.step_node})"
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
class ForRangeNode(ASTNode):
|
|
202
|
+
"""Represents a range-based for loop: for var in range(...) { body }."""
|
|
203
|
+
def __init__(self, var_name_tok: Token, start_node: ASTNode, stop_node: ASTNode, step_node: ASTNode, body_node: ASTNode, pos_start: Optional[Position] = None, pos_end: Optional[Position] = None):
|
|
204
|
+
super().__init__(pos_start, pos_end or body_node.pos_end)
|
|
205
|
+
self.var_name_tok: Token = var_name_tok
|
|
206
|
+
self.var_name: str = var_name_tok.value
|
|
207
|
+
self.start_node: ASTNode = start_node
|
|
208
|
+
self.stop_node: ASTNode = stop_node
|
|
209
|
+
self.step_node: ASTNode = step_node
|
|
210
|
+
self.body_node: ASTNode = body_node
|
|
211
|
+
|
|
212
|
+
def __repr__(self) -> str:
|
|
213
|
+
return f"ForRangeNode({self.var_name} in range({self.start_node}, {self.stop_node}, {self.step_node}))"
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
class DoWhileNode(ASTNode):
|
|
217
|
+
"""Represents a do-while loop: do { body } while (cond);."""
|
|
218
|
+
def __init__(self, body_node: ASTNode, condition_node: ASTNode, pos_start: Optional[Position] = None, pos_end: Optional[Position] = None):
|
|
219
|
+
super().__init__(pos_start, pos_end or condition_node.pos_end)
|
|
220
|
+
self.body_node: ASTNode = body_node
|
|
221
|
+
self.condition_node: ASTNode = condition_node
|
|
222
|
+
|
|
223
|
+
def __repr__(self) -> str:
|
|
224
|
+
return f"DoWhileNode(while {self.condition_node})"
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
class ConstAssignNode(ASTNode):
|
|
228
|
+
"""Represents constant definition: const NAME = expr;."""
|
|
229
|
+
def __init__(self, var_name_tok: Token, value_node: ASTNode):
|
|
230
|
+
super().__init__(var_name_tok.pos_start, value_node.pos_end)
|
|
231
|
+
self.var_name_tok: Token = var_name_tok
|
|
232
|
+
self.var_name: str = var_name_tok.value
|
|
233
|
+
self.value_node: ASTNode = value_node
|
|
234
|
+
|
|
235
|
+
def __repr__(self) -> str:
|
|
236
|
+
return f"ConstAssignNode(const {self.var_name} = {self.value_node})"
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
class IncDecNode(ASTNode):
|
|
240
|
+
"""Represents increment or decrement: x++, x--, ++x, --x."""
|
|
241
|
+
def __init__(self, var_name_tok: Token, op_tok: Token, is_prefix: bool = False):
|
|
242
|
+
pos_start = op_tok.pos_start if is_prefix else var_name_tok.pos_start
|
|
243
|
+
pos_end = var_name_tok.pos_end if is_prefix else op_tok.pos_end
|
|
244
|
+
super().__init__(pos_start, pos_end)
|
|
245
|
+
self.var_name_tok: Token = var_name_tok
|
|
246
|
+
self.var_name: str = var_name_tok.value
|
|
247
|
+
self.op_tok: Token = op_tok
|
|
248
|
+
self.is_prefix: bool = is_prefix
|
|
249
|
+
|
|
250
|
+
def __repr__(self) -> str:
|
|
251
|
+
prefix = "prefix" if self.is_prefix else "postfix"
|
|
252
|
+
return f"IncDecNode({prefix} {self.var_name}{self.op_tok.type})"
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
class CompoundAssignNode(ASTNode):
|
|
256
|
+
"""Represents compound assignment: x += 5, x -= 2, x *= 3, etc."""
|
|
257
|
+
def __init__(self, var_name_tok: Token, op_tok: Token, value_node: ASTNode):
|
|
258
|
+
super().__init__(var_name_tok.pos_start, value_node.pos_end)
|
|
259
|
+
self.var_name_tok: Token = var_name_tok
|
|
260
|
+
self.var_name: str = var_name_tok.value
|
|
261
|
+
self.op_tok: Token = op_tok
|
|
262
|
+
self.value_node: ASTNode = value_node
|
|
263
|
+
|
|
264
|
+
def __repr__(self) -> str:
|
|
265
|
+
return f"CompoundAssignNode({self.var_name} {self.op_tok.type} {self.value_node})"
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
class RepeatNode(ASTNode):
|
|
269
|
+
"""Represents a repeat loop: repeat count { body }."""
|
|
270
|
+
def __init__(self, count_node: ASTNode, body_node: ASTNode, pos_start: Optional[Position] = None, pos_end: Optional[Position] = None):
|
|
271
|
+
super().__init__(pos_start, pos_end or body_node.pos_end)
|
|
272
|
+
self.count_node: ASTNode = count_node
|
|
273
|
+
self.body_node: ASTNode = body_node
|
|
274
|
+
|
|
275
|
+
def __repr__(self) -> str:
|
|
276
|
+
return f"RepeatNode({self.count_node})"
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
class ListNode(ASTNode):
|
|
280
|
+
"""Represents a list literal: [elem1, elem2, ...]."""
|
|
281
|
+
def __init__(self, element_nodes: List[ASTNode], pos_start: Optional[Position] = None, pos_end: Optional[Position] = None):
|
|
282
|
+
super().__init__(pos_start, pos_end)
|
|
283
|
+
self.element_nodes: List[ASTNode] = element_nodes
|
|
284
|
+
|
|
285
|
+
def __repr__(self) -> str:
|
|
286
|
+
return f"ListNode({self.element_nodes})"
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
class IndexAccessNode(ASTNode):
|
|
290
|
+
"""Represents indexing into a collection: target[index]."""
|
|
291
|
+
def __init__(self, target_node: ASTNode, index_node: ASTNode, pos_start: Optional[Position] = None, pos_end: Optional[Position] = None):
|
|
292
|
+
super().__init__(pos_start or target_node.pos_start, pos_end or index_node.pos_end)
|
|
293
|
+
self.target_node: ASTNode = target_node
|
|
294
|
+
self.index_node: ASTNode = index_node
|
|
295
|
+
|
|
296
|
+
def __repr__(self) -> str:
|
|
297
|
+
return f"IndexAccessNode({self.target_node}[{self.index_node}])"
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
class IndexAssignNode(ASTNode):
|
|
301
|
+
"""Represents assigning to an index: target[index] = val or target[index] += val."""
|
|
302
|
+
def __init__(self, target_node: ASTNode, index_node: ASTNode, op_tok: Token, value_node: ASTNode, pos_start: Optional[Position] = None, pos_end: Optional[Position] = None):
|
|
303
|
+
super().__init__(pos_start or target_node.pos_start, pos_end or value_node.pos_end)
|
|
304
|
+
self.target_node: ASTNode = target_node
|
|
305
|
+
self.index_node: ASTNode = index_node
|
|
306
|
+
self.op_tok: Token = op_tok
|
|
307
|
+
self.value_node: ASTNode = value_node
|
|
308
|
+
|
|
309
|
+
def __repr__(self) -> str:
|
|
310
|
+
return f"IndexAssignNode({self.target_node}[{self.index_node}] {self.op_tok.type} {self.value_node})"
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
class ForInNode(ASTNode):
|
|
314
|
+
"""Represents for-in collection loop: for item in list { } or for i, item in list { }."""
|
|
315
|
+
def __init__(self, item_var_tok: Token, index_var_tok: Optional[Token], iterable_node: ASTNode, body_node: ASTNode, pos_start: Optional[Position] = None, pos_end: Optional[Position] = None):
|
|
316
|
+
super().__init__(pos_start, pos_end or body_node.pos_end)
|
|
317
|
+
self.item_var_tok: Token = item_var_tok
|
|
318
|
+
self.item_var: str = item_var_tok.value
|
|
319
|
+
self.index_var_tok: Optional[Token] = index_var_tok
|
|
320
|
+
self.index_var: Optional[str] = index_var_tok.value if index_var_tok else None
|
|
321
|
+
self.iterable_node: ASTNode = iterable_node
|
|
322
|
+
self.body_node: ASTNode = body_node
|
|
323
|
+
|
|
324
|
+
def __repr__(self) -> str:
|
|
325
|
+
idx_str = f"{self.index_var}, " if self.index_var else ""
|
|
326
|
+
return f"ForInNode({idx_str}{self.item_var} in {self.iterable_node})"
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
class TernaryNode(ASTNode):
|
|
330
|
+
"""Represents ternary conditional operator: cond ? true_expr : false_expr."""
|
|
331
|
+
def __init__(self, condition_node: ASTNode, true_node: ASTNode, false_node: ASTNode, pos_start: Optional[Position] = None, pos_end: Optional[Position] = None):
|
|
332
|
+
super().__init__(pos_start or condition_node.pos_start, pos_end or false_node.pos_end)
|
|
333
|
+
self.condition_node: ASTNode = condition_node
|
|
334
|
+
self.true_node: ASTNode = true_node
|
|
335
|
+
self.false_node: ASTNode = false_node
|
|
336
|
+
|
|
337
|
+
def __repr__(self) -> str:
|
|
338
|
+
return f"TernaryNode({self.condition_node} ? {self.true_node} : {self.false_node})"
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
class DictNode(ASTNode):
|
|
342
|
+
"""Represents a dictionary / hashmap literal: { key: value, ... }."""
|
|
343
|
+
def __init__(self, key_value_pairs: List[Tuple[ASTNode, ASTNode]], pos_start: Optional[Position] = None, pos_end: Optional[Position] = None):
|
|
344
|
+
super().__init__(pos_start, pos_end)
|
|
345
|
+
self.key_value_pairs: List[Tuple[ASTNode, ASTNode]] = key_value_pairs
|
|
346
|
+
|
|
347
|
+
def __repr__(self) -> str:
|
|
348
|
+
return f"DictNode({len(self.key_value_pairs)} pairs)"
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
class StatementsNode(ASTNode):
|
|
352
|
+
"""Represents a list of sequential statements in a script."""
|
|
353
|
+
def __init__(self, statements: List[ASTNode], pos_start: Optional[Position] = None, pos_end: Optional[Position] = None):
|
|
354
|
+
super().__init__(pos_start, pos_end)
|
|
355
|
+
self.statements: List[ASTNode] = statements
|
|
356
|
+
|
|
357
|
+
def __repr__(self) -> str:
|
|
358
|
+
return f"StatementsNode({len(self.statements)} stmts)"
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
class ImportNode(ASTNode):
|
|
362
|
+
"""
|
|
363
|
+
Represents an import statement:
|
|
364
|
+
- import "path/file.sl" [as alias]
|
|
365
|
+
- import module_name [as alias]
|
|
366
|
+
- import { item1, item2 as alias2 } from "path/file.sl"
|
|
367
|
+
- import item1, item2 from "path/file.sl"
|
|
368
|
+
- import * from "path/file.sl"
|
|
369
|
+
- from "path/file.sl" import item1, item2
|
|
370
|
+
- from "path/file.sl" import *
|
|
371
|
+
"""
|
|
372
|
+
def __init__(
|
|
373
|
+
self,
|
|
374
|
+
module_path: str,
|
|
375
|
+
alias: Optional[str] = None,
|
|
376
|
+
items: Optional[List[Tuple[str, Optional[str]]]] = None,
|
|
377
|
+
is_wildcard: bool = False,
|
|
378
|
+
pos_start: Optional[Position] = None,
|
|
379
|
+
pos_end: Optional[Position] = None
|
|
380
|
+
):
|
|
381
|
+
super().__init__(pos_start, pos_end)
|
|
382
|
+
self.module_path: str = module_path
|
|
383
|
+
self.alias: Optional[str] = alias
|
|
384
|
+
self.items: Optional[List[Tuple[str, Optional[str]]]] = items
|
|
385
|
+
self.is_wildcard: bool = is_wildcard
|
|
386
|
+
|
|
387
|
+
def __repr__(self) -> str:
|
|
388
|
+
if self.is_wildcard:
|
|
389
|
+
return f"ImportNode(* from {self.module_path})"
|
|
390
|
+
if self.items:
|
|
391
|
+
items_str = ", ".join(f"{orig} as {al}" if al else orig for orig, al in self.items)
|
|
392
|
+
return f"ImportNode({{{items_str}}} from {self.module_path})"
|
|
393
|
+
if self.alias:
|
|
394
|
+
return f"ImportNode({self.module_path} as {self.alias})"
|
|
395
|
+
return f"ImportNode({self.module_path})"
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
class ExportNode(ASTNode):
|
|
399
|
+
"""Represents an export statement: export func foo() ... or export const X = 1;"""
|
|
400
|
+
def __init__(self, statement_node: ASTNode, pos_start: Optional[Position] = None, pos_end: Optional[Position] = None):
|
|
401
|
+
super().__init__(pos_start or statement_node.pos_start, pos_end or statement_node.pos_end)
|
|
402
|
+
self.statement_node: ASTNode = statement_node
|
|
403
|
+
|
|
404
|
+
def __repr__(self) -> str:
|
|
405
|
+
return f"ExportNode({self.statement_node})"
|
|
406
|
+
|
|
407
|
+
|
|
408
|
+
class BreakNode(ASTNode):
|
|
409
|
+
"""Represents a break statement inside loops."""
|
|
410
|
+
def __init__(self, pos_start: Optional[Position] = None, pos_end: Optional[Position] = None):
|
|
411
|
+
super().__init__(pos_start, pos_end)
|
|
412
|
+
|
|
413
|
+
def __repr__(self) -> str:
|
|
414
|
+
return "BreakNode()"
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
class ContinueNode(ASTNode):
|
|
418
|
+
"""Represents a continue statement inside loops."""
|
|
419
|
+
def __init__(self, pos_start: Optional[Position] = None, pos_end: Optional[Position] = None):
|
|
420
|
+
super().__init__(pos_start, pos_end)
|
|
421
|
+
|
|
422
|
+
def __repr__(self) -> str:
|
|
423
|
+
return "ContinueNode()"
|
|
424
|
+
|
|
425
|
+
|
|
426
|
+
class TryCatchNode(ASTNode):
|
|
427
|
+
"""Represents try-catch error handling: try { ... } catch (err) { ... } [finally { ... }]"""
|
|
428
|
+
def __init__(
|
|
429
|
+
self,
|
|
430
|
+
try_body: ASTNode,
|
|
431
|
+
catch_var_tok: Optional[Token],
|
|
432
|
+
catch_body: ASTNode,
|
|
433
|
+
finally_body: Optional[ASTNode] = None,
|
|
434
|
+
pos_start: Optional[Position] = None,
|
|
435
|
+
pos_end: Optional[Position] = None
|
|
436
|
+
):
|
|
437
|
+
super().__init__(pos_start, pos_end)
|
|
438
|
+
self.try_body: ASTNode = try_body
|
|
439
|
+
self.catch_var: Optional[str] = catch_var_tok.value if catch_var_tok else None
|
|
440
|
+
self.catch_body: ASTNode = catch_body
|
|
441
|
+
self.finally_body: Optional[ASTNode] = finally_body
|
|
442
|
+
|
|
443
|
+
def __repr__(self) -> str:
|
|
444
|
+
var_str = f"({self.catch_var})" if self.catch_var else ""
|
|
445
|
+
return f"TryCatchNode(try {self.try_body} catch{var_str} {self.catch_body})"
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
class LambdaNode(ASTNode):
|
|
449
|
+
"""Represents an anonymous / arrow function: (x, y) => expr or (x) => { ... }."""
|
|
450
|
+
def __init__(self, arg_names: List[str], body_node: ASTNode, pos_start: Optional[Position] = None, pos_end: Optional[Position] = None):
|
|
451
|
+
super().__init__(pos_start, pos_end)
|
|
452
|
+
self.arg_names: List[str] = arg_names
|
|
453
|
+
self.body_node: ASTNode = body_node
|
|
454
|
+
|
|
455
|
+
def __repr__(self) -> str:
|
|
456
|
+
return f"LambdaNode(({', '.join(self.arg_names)}) => {self.body_node})"
|
|
457
|
+
|
|
458
|
+
|
|
459
|
+
class PropertyDefNode(ASTNode):
|
|
460
|
+
"""Represents a property: let Name { get; set; } [= default]."""
|
|
461
|
+
def __init__(
|
|
462
|
+
self,
|
|
463
|
+
name_tok: Token,
|
|
464
|
+
has_get: bool = True,
|
|
465
|
+
has_set: bool = True,
|
|
466
|
+
default_node: Optional[ASTNode] = None,
|
|
467
|
+
pos_start: Optional[Position] = None,
|
|
468
|
+
pos_end: Optional[Position] = None
|
|
469
|
+
):
|
|
470
|
+
super().__init__(pos_start or name_tok.pos_start, pos_end or (default_node.pos_end if default_node else name_tok.pos_end))
|
|
471
|
+
self.name_tok: Token = name_tok
|
|
472
|
+
self.name: str = name_tok.value
|
|
473
|
+
self.has_get: bool = has_get
|
|
474
|
+
self.has_set: bool = has_set
|
|
475
|
+
self.default_node: Optional[ASTNode] = default_node
|
|
476
|
+
|
|
477
|
+
def __repr__(self) -> str:
|
|
478
|
+
gs = f"{{ {'get; ' if self.has_get else ''}{'set; ' if self.has_set else ''}}}"
|
|
479
|
+
return f"PropertyDefNode({self.name} {gs})"
|
|
480
|
+
|
|
481
|
+
|
|
482
|
+
class MethodDefNode(ASTNode):
|
|
483
|
+
"""Represents a class method or constructor with virtual/override/static modifiers."""
|
|
484
|
+
def __init__(
|
|
485
|
+
self,
|
|
486
|
+
name_tok: Token,
|
|
487
|
+
arg_name_toks: List[Token],
|
|
488
|
+
body_node: ASTNode,
|
|
489
|
+
is_virtual: bool = False,
|
|
490
|
+
is_override: bool = False,
|
|
491
|
+
is_static: bool = False,
|
|
492
|
+
pos_start: Optional[Position] = None,
|
|
493
|
+
pos_end: Optional[Position] = None
|
|
494
|
+
):
|
|
495
|
+
super().__init__(pos_start or name_tok.pos_start, pos_end or body_node.pos_end)
|
|
496
|
+
self.name_tok: Token = name_tok
|
|
497
|
+
self.name: str = name_tok.value
|
|
498
|
+
self.arg_name_toks: List[Token] = arg_name_toks
|
|
499
|
+
self.body_node: ASTNode = body_node
|
|
500
|
+
self.is_virtual: bool = is_virtual
|
|
501
|
+
self.is_override: bool = is_override
|
|
502
|
+
self.is_static: bool = is_static
|
|
503
|
+
|
|
504
|
+
def __repr__(self) -> str:
|
|
505
|
+
mods = []
|
|
506
|
+
if self.is_virtual: mods.append("virtuel")
|
|
507
|
+
if self.is_override: mods.append("override")
|
|
508
|
+
if self.is_static: mods.append("static")
|
|
509
|
+
mod_str = (" " + " ".join(mods)) if mods else ""
|
|
510
|
+
return f"MethodDefNode(func{mod_str} {self.name}(...))"
|
|
511
|
+
|
|
512
|
+
|
|
513
|
+
class ClassDefNode(ASTNode):
|
|
514
|
+
"""Represents a class definition: class Name [: Parent] { ... }."""
|
|
515
|
+
def __init__(
|
|
516
|
+
self,
|
|
517
|
+
name_tok: Token,
|
|
518
|
+
parent_tok: Optional[Token],
|
|
519
|
+
properties: List[PropertyDefNode],
|
|
520
|
+
methods: List[MethodDefNode],
|
|
521
|
+
constructor: Optional[MethodDefNode] = None,
|
|
522
|
+
pos_start: Optional[Position] = None,
|
|
523
|
+
pos_end: Optional[Position] = None
|
|
524
|
+
):
|
|
525
|
+
super().__init__(pos_start or name_tok.pos_start, pos_end or name_tok.pos_end)
|
|
526
|
+
self.name_tok: Token = name_tok
|
|
527
|
+
self.name: str = name_tok.value
|
|
528
|
+
self.parent_tok: Optional[Token] = parent_tok
|
|
529
|
+
self.parent_name: Optional[str] = parent_tok.value if parent_tok else None
|
|
530
|
+
self.properties: List[PropertyDefNode] = properties
|
|
531
|
+
self.methods: List[MethodDefNode] = methods
|
|
532
|
+
self.constructor: Optional[MethodDefNode] = constructor
|
|
533
|
+
|
|
534
|
+
def __repr__(self) -> str:
|
|
535
|
+
parent_str = f" : {self.parent_name}" if self.parent_name else ""
|
|
536
|
+
return f"ClassDefNode({self.name}{parent_str})"
|
|
537
|
+
|
|
538
|
+
|
|
539
|
+
class NewNode(ASTNode):
|
|
540
|
+
"""Represents object instantiation: new ClassName(args)."""
|
|
541
|
+
def __init__(
|
|
542
|
+
self,
|
|
543
|
+
class_node: ASTNode,
|
|
544
|
+
arg_nodes: List[ASTNode],
|
|
545
|
+
pos_start: Optional[Position] = None,
|
|
546
|
+
pos_end: Optional[Position] = None
|
|
547
|
+
):
|
|
548
|
+
super().__init__(pos_start or class_node.pos_start, pos_end or class_node.pos_end)
|
|
549
|
+
self.class_node: ASTNode = class_node
|
|
550
|
+
self.arg_nodes: List[ASTNode] = arg_nodes
|
|
551
|
+
|
|
552
|
+
def __repr__(self) -> str:
|
|
553
|
+
return f"NewNode(new {self.class_node}({len(self.arg_nodes)} args))"
|
|
554
|
+
|
|
555
|
+
|
|
556
|
+
class BaseCallNode(ASTNode):
|
|
557
|
+
"""Represents calling parent constructor: base(args)."""
|
|
558
|
+
def __init__(
|
|
559
|
+
self,
|
|
560
|
+
arg_nodes: List[ASTNode],
|
|
561
|
+
pos_start: Optional[Position] = None,
|
|
562
|
+
pos_end: Optional[Position] = None
|
|
563
|
+
):
|
|
564
|
+
super().__init__(pos_start, pos_end)
|
|
565
|
+
self.arg_nodes: List[ASTNode] = arg_nodes
|
|
566
|
+
|
|
567
|
+
def __repr__(self) -> str:
|
|
568
|
+
return f"BaseCallNode(base({len(self.arg_nodes)} args))"
|
|
569
|
+
|
|
570
|
+
|
|
571
|
+
class BaseAccessNode(ASTNode):
|
|
572
|
+
"""Represents accessing parent member: base.member."""
|
|
573
|
+
def __init__(
|
|
574
|
+
self,
|
|
575
|
+
member_tok: Token,
|
|
576
|
+
pos_start: Optional[Position] = None,
|
|
577
|
+
pos_end: Optional[Position] = None
|
|
578
|
+
):
|
|
579
|
+
super().__init__(pos_start, pos_end or member_tok.pos_end)
|
|
580
|
+
self.member_tok: Token = member_tok
|
|
581
|
+
self.member_name: str = member_tok.value
|
|
582
|
+
|
|
583
|
+
def __repr__(self) -> str:
|
|
584
|
+
return f"BaseAccessNode(base.{self.member_name})"
|
|
585
|
+
|
|
586
|
+
|
|
587
|
+
class ThisNode(ASTNode):
|
|
588
|
+
"""Represents 'this' or 'self' reference to current instance."""
|
|
589
|
+
def __init__(self, tok: Token):
|
|
590
|
+
super().__init__(tok.pos_start, tok.pos_end)
|
|
591
|
+
self.tok: Token = tok
|
|
592
|
+
self.name: str = tok.value
|
|
593
|
+
|
|
594
|
+
def __repr__(self) -> str:
|
|
595
|
+
return f"ThisNode({self.name})"
|
|
596
|
+
|
|
597
|
+
|
|
598
|
+
class TypedVarAssignNode(ASTNode):
|
|
599
|
+
"""Represents explicit typed variable declaration: Person s = new Student()."""
|
|
600
|
+
def __init__(
|
|
601
|
+
self,
|
|
602
|
+
type_tok: Token,
|
|
603
|
+
var_name_tok: Token,
|
|
604
|
+
value_node: ASTNode,
|
|
605
|
+
pos_start: Optional[Position] = None,
|
|
606
|
+
pos_end: Optional[Position] = None
|
|
607
|
+
):
|
|
608
|
+
super().__init__(pos_start or type_tok.pos_start, pos_end or value_node.pos_end)
|
|
609
|
+
self.type_tok: Token = type_tok
|
|
610
|
+
self.type_name: str = type_tok.value
|
|
611
|
+
self.var_name_tok: Token = var_name_tok
|
|
612
|
+
self.var_name: str = var_name_tok.value
|
|
613
|
+
self.value_node: ASTNode = value_node
|
|
614
|
+
|
|
615
|
+
def __repr__(self) -> str:
|
|
616
|
+
return f"TypedVarAssignNode({self.type_name} {self.var_name} = {self.value_node})"
|
|
617
|
+
|
|
618
|
+
|
|
619
|
+
class MemberAssignNode(ASTNode):
|
|
620
|
+
"""Represents assignment to an object property: obj.prop = value."""
|
|
621
|
+
def __init__(
|
|
622
|
+
self,
|
|
623
|
+
target_node: ASTNode,
|
|
624
|
+
member_tok: Token,
|
|
625
|
+
value_node: ASTNode,
|
|
626
|
+
pos_start: Optional[Position] = None,
|
|
627
|
+
pos_end: Optional[Position] = None
|
|
628
|
+
):
|
|
629
|
+
super().__init__(pos_start or target_node.pos_start, pos_end or value_node.pos_end)
|
|
630
|
+
self.target_node: ASTNode = target_node
|
|
631
|
+
self.member_tok: Token = member_tok
|
|
632
|
+
self.member_name: str = member_tok.value
|
|
633
|
+
self.value_node: ASTNode = value_node
|
|
634
|
+
|
|
635
|
+
def __repr__(self) -> str:
|
|
636
|
+
return f"MemberAssignNode({self.target_node}.{self.member_name} = {self.value_node})"
|
|
637
|
+
|
|
638
|
+
|
|
639
|
+
|
|
640
|
+
|
|
641
|
+
|