Skip to content

Commit c11c2e4

Browse files
Merge pull request #31 from MarcellPerger1/start-ast
Refactor CST nodes
2 parents d0cb2f1 + 01ce21c commit c11c2e4

File tree

10 files changed

+183
-13
lines changed

10 files changed

+183
-13
lines changed

main.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from util import readfile
55
from parser.cst.treegen import TreeGen
66
from parser.lexer import Tokenizer, print_tokens
7-
from parser.cst.tree_print import tprint
7+
from parser.trees.tree_print import tprint
88

99

1010
def make_tree(src: str):

parser/cst/nodes.py

Lines changed: 159 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
from __future__ import annotations
22

3-
from .named_node import (NamedLeafCls, NamedNodeCls, NamedSizedNodeCls,
4-
register_corresponding_token)
3+
from typing import cast
4+
5+
from util import checked_cast
6+
from ..trees.named_node import (NamedLeafCls, NamedNodeCls, NamedSizedNodeCls,
7+
register_corresponding_token)
58

69
__all__ = [
710
"NumberNode", "StringNode", "AnyNameLeaf", "IdentNode", "AttrNameNode",
@@ -16,6 +19,10 @@
1619
class ProgramNode(NamedNodeCls):
1720
name = 'program' # Varargs
1821

22+
@property
23+
def statements(self):
24+
return self.children
25+
1926

2027
# region ---- Expressions ----
2128
@register_corresponding_token
@@ -45,30 +52,66 @@ class AttrNameNode(AnyNameLeaf):
4552
class AutocatNode(NamedNodeCls):
4653
name = 'autocat' # Note: this is varargs, unlike regular concat
4754

55+
@property
56+
def parts(self) -> list[StringNode]:
57+
return cast(list[StringNode], self.children)
58+
4859

4960
class GetattrNode(NamedSizedNodeCls):
5061
name = 'getattr'
5162
size = 2
5263

64+
@property
65+
def target(self):
66+
return self.children[0]
67+
68+
@property
69+
def attr(self):
70+
return self.children[1]
71+
5372

5473
class GetitemNode(NamedSizedNodeCls):
5574
name = 'getitem'
5675
size = 2
5776

77+
@property
78+
def target(self):
79+
return self.children[0]
80+
81+
@property
82+
def item(self):
83+
return checked_cast(AttrNameNode, self.children[1])
84+
5885

5986
class ParenNode(NamedSizedNodeCls):
6087
name = 'paren'
6188
size = 1
6289

90+
@property
91+
def contents(self):
92+
return self.children[0]
93+
6394

6495
class CallNode(NamedSizedNodeCls):
6596
name = 'call'
6697
size = 2
6798

99+
@property
100+
def target(self):
101+
return self.children[0]
102+
103+
@property
104+
def arglist(self) -> CallArgs:
105+
return checked_cast(CallArgs, self.children[1])
106+
68107

69108
class CallArgs(NamedNodeCls):
70109
name = 'call_args' # Varargs
71110

111+
@property
112+
def args(self):
113+
return self.children
114+
72115

73116
class OperatorNode(NamedNodeCls):
74117
pass
@@ -77,6 +120,10 @@ class OperatorNode(NamedNodeCls):
77120
class UnaryOpNode(NamedSizedNodeCls, OperatorNode):
78121
size = 1
79122

123+
@property
124+
def operand(self):
125+
return self.children[0]
126+
80127

81128
@register_corresponding_token('+', arity=1)
82129
@register_corresponding_token()
@@ -98,6 +145,14 @@ class NotNode(UnaryOpNode):
98145
class BinOpNode(NamedSizedNodeCls, OperatorNode):
99146
size = 2
100147

148+
@property
149+
def left(self):
150+
return self.children[0]
151+
152+
@property
153+
def right(self):
154+
return self.children[1]
155+
101156

102157
@register_corresponding_token
103158
class AddNode(BinOpNode):
@@ -188,25 +243,61 @@ class NopNode(NamedLeafCls):
188243
class BlockNode(NamedNodeCls):
189244
name = 'block' # Varargs
190245

246+
@property
247+
def statements(self):
248+
return self.children
249+
191250

192251
class ConditionalBlock(NamedNodeCls): # Varargs: if, *elseif, else
193252
name = 'conditional'
194253

254+
@property
255+
def if_block(self):
256+
return checked_cast(IfBlock, self.children[0])
257+
258+
@property
259+
def elseif_blocks(self):
260+
return cast(list[ElseIfBlock], self.children[1:-1])
261+
262+
@property
263+
def else_block(self) -> ElseBlock | NullElseBlock:
264+
return checked_cast(ElseBlock | NullElseBlock, self.children[-1])
265+
195266

196267
class IfBlock(NamedSizedNodeCls):
197268
name = 'if'
198269
size = 2 # condition, block
199270

271+
@property
272+
def cond(self):
273+
return self.children[0]
274+
275+
@property
276+
def block(self):
277+
return checked_cast(BlockNode, self.children[1])
278+
200279

201280
class ElseIfBlock(NamedSizedNodeCls):
202281
name = 'elseif'
203282
size = 2
204283

284+
@property
285+
def cond(self):
286+
return self.children[0]
287+
288+
@property
289+
def block(self):
290+
return checked_cast(BlockNode, self.children[1])
291+
205292

206293
class ElseBlock(NamedSizedNodeCls):
207294
name = 'else'
208295
size = 1 # just the BlockNode
209296

297+
@property
298+
def block(self):
299+
return checked_cast(BlockNode, self.children[0])
300+
210301

211302
class NullElseBlock(NamedLeafCls):
212303
name = 'else_null'
@@ -217,42 +308,106 @@ class WhileBlock(NamedSizedNodeCls):
217308
name = 'while'
218309
size = 2
219310

311+
@property
312+
def cond(self):
313+
return self.children[0]
314+
315+
@property
316+
def block(self):
317+
return checked_cast(BlockNode, self.children[1])
318+
220319

221320
class RepeatBlock(NamedSizedNodeCls):
222321
name = 'repeat'
223322
size = 2
224323

324+
@property
325+
def count(self):
326+
return self.children[0]
327+
328+
@property
329+
def block(self):
330+
return checked_cast(BlockNode, self.children[1])
331+
225332

226333
class DefineNode(NamedSizedNodeCls):
227334
name = 'def'
228335
size = 3 # name, args_decl, block
229336

337+
@property
338+
def ident(self):
339+
return checked_cast(IdentNode, self.children[0])
340+
341+
@property
342+
def args_decl(self):
343+
return checked_cast(ArgsDeclNode, self.children[1])
344+
345+
@property
346+
def block(self):
347+
return checked_cast(BlockNode, self.children[1])
348+
230349

231350
class ArgsDeclNode(NamedNodeCls):
232351
name = 'args_decl' # Varargs
233352

353+
@property
354+
def decls(self):
355+
return cast(list[ArgDeclNode], self.children)
356+
234357

235358
class ArgDeclNode(NamedSizedNodeCls):
236359
name = 'arg_decl'
237360
size = 2 # type and name
238361

362+
@property
363+
def type(self):
364+
return checked_cast(IdentNode, self.children[0])
365+
366+
@property
367+
def ident(self):
368+
return checked_cast(IdentNode, self.children[1])
369+
# endregion
370+
239371

240372
class DeclItemNode(NamedNodeCls):
241373
name = 'decl_item' # 1 or 2 (name and optional value)
242-
# endregion
374+
375+
@property
376+
def ident(self):
377+
return checked_cast(IdentNode, self.children[0])
378+
379+
@property
380+
def value(self):
381+
if len(self.children) <= 1:
382+
return None
383+
return self.children[1]
243384

244385

245386
class LetNode(NamedNodeCls):
246387
name = 'let_decl' # Varargs
247388

389+
@property
390+
def decls(self):
391+
return cast(list[DeclItemNode], self.children)
392+
248393

249394
class GlobalNode(NamedNodeCls):
250395
name = 'global_decl' # Varargs
251396

397+
@property
398+
def decls(self):
399+
return cast(list[DeclItemNode], self.children)
400+
252401

253402
# region ---- Assignment-ops ----
254403
class AssignOpNode(BinOpNode):
255-
pass
404+
@property
405+
def target(self):
406+
return self.children[0]
407+
408+
@property
409+
def source(self):
410+
return self.children[1]
256411

257412

258413
@register_corresponding_token

parser/cst/treegen.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@
44

55
from .nodes import *
66
from .token_matcher import OpM, KwdM, Matcher, PatternT
7-
from .named_node import AnyNamedNode, node_from_token, node_cls_from_name
8-
from .base_node import AnyNode, Node
97
from ..error import BaseParseError, BaseLocatedError
108
from ..lexer import Tokenizer
119
from ..operators import UNARY_OPS, COMPARISONS, ASSIGN_OPS
1210
from ..str_region import StrRegion
1311
from ..tokens import *
12+
from ..trees.base_node import AnyNode, Node
13+
from ..trees.named_node import AnyNamedNode, node_from_token, node_cls_from_name
1414

1515
DT = TypeVar('DT')
1616

parser/trees/__init__.py

Whitespace-only changes.
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@
44
from typing import TYPE_CHECKING, overload, Sequence, cast, Literal
55

66
from .base_node import Leaf, AnyNode, Node
7-
from ..str_region import StrRegion
7+
from parser.str_region import StrRegion
88

99
if TYPE_CHECKING:
10-
from ..tokens import Token
10+
from parser.tokens import Token
1111

1212

1313
# NOTE: these classes only follow the Liskov substitution principle on
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from typing import IO, Sequence
66

77
from .base_node import Leaf, AnyNode, Node
8-
from ..str_region import StrRegion
8+
from parser.str_region import StrRegion
99

1010
__all__ = [
1111
'TreePrinter', 'tree_print', 'tree_format', 'tprint', 'tformat'

test/common.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
from enum import IntFlag, Enum
66
from typing import Sequence, TypeVar
77

8-
from parser.cst.base_node import Leaf, AnyNode, Node
9-
from parser.cst.tree_print import tformat
8+
from parser.trees.base_node import Leaf, AnyNode, Node
9+
from parser.trees.tree_print import tformat
1010
from parser.cst.treegen import TreeGen, CstParseError
1111
from parser.error import BaseParseError
1212
from parser.lexer import Tokenizer

test/test_node.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from parser.str_region import StrRegion
44
from parser.tokens import NumberToken, IdentNameToken
5-
from parser.cst.named_node import node_from_token
5+
from parser.trees.named_node import node_from_token
66
from parser.cst.nodes import NumberNode, IdentNode
77

88

util/__init__.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,28 @@
11
from __future__ import annotations
22

33
from os import PathLike
4+
from typing import TypeVar, Any, overload
45

56
from .simple_process_pool import *
67
from .timeouts import *
78

9+
T = TypeVar('T')
10+
U = TypeVar('U')
11+
812

913
def readfile(path: int | str | bytes | PathLike[str] | PathLike[bytes],
1014
encoding='utf-8', errors: str | None = None,
1115
newline: str | None = None):
1216
with open(path, encoding=encoding, errors=errors, newline=newline, ) as f:
1317
return f.read()
18+
19+
20+
@overload
21+
def checked_cast(typ: type[T], val: Any) -> T: ...
22+
@overload
23+
def checked_cast(typ: type[T | U], val: Any) -> T | U: ...
24+
25+
26+
def checked_cast(typ: type[T], val: Any) -> T:
27+
assert isinstance(val, typ)
28+
return val

0 commit comments

Comments
 (0)