-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgenerate_ast.py
125 lines (98 loc) · 3.29 KB
/
generate_ast.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
class ASTGenerator:
EXPRESSIONS_PATH = 'hyde/expressions.py'
STATEMENTS_PATH = 'hyde/statements.py'
EXPRESSIONS = {
'Expression': None,
'Assign': ['name', 'value'],
'Binary': ['left', 'operator', 'right'],
'Call': ['callee', 'paren', 'arguments'],
'Get': ['object', 'name'],
'Grouping': ['expression'],
'Literal': ['value', 'token'],
'Logical': ['left', 'operator', 'right'],
'Set': ['object', 'name', 'value'],
'Super': ['keyword', 'method'],
'This': ['keyword'],
'Unary': ['operator', 'right'],
'Variable': ['name']
}
STATEMENTS = {
# class, if, return, and while are reserved words in Python
'Statement': None,
'Block': ['statements'],
'ClassDef': ['name', 'superclass', 'methods'],
'Expression': ['expression'],
'Function': ['name', 'params', 'body'],
'IfStmt': ['condition', 'then_branch', 'else_branch'],
'Load': ['path'],
'Print': ['expression'],
'ReturnStmt': ['keyword', 'value'],
'Var': ['name', 'initializer'],
'WhileStmt': ['condition', 'body']
}
@classmethod
def define_expressions(cls):
ASTGenerator.define_ast(cls.EXPRESSIONS, cls.EXPRESSIONS_PATH)
@classmethod
def define_statements(cls):
ASTGenerator.define_ast(cls.STATEMENTS, cls.STATEMENTS_PATH)
@classmethod
def define_ast(cls, data, output_path):
lines = []
parent_class = list(data.keys())[0]
for class_name in data:
attributes = data[class_name]
initializer = ASTGenerator.generate_initializer(attributes)
class_definition = f'{class_name}({parent_class})' if class_name != parent_class else class_name
klass = f'''\
class {class_definition}:
{initializer}
'''
lines.append(klass)
visitor_class = ASTGenerator.generate_visitor_class(data, without = [parent_class])
lines.append(visitor_class)
code = '\n'.join(lines)
ASTGenerator.write(output_path, code)
@classmethod
def generate_initializer(cls, attributes):
if attributes is None:
return f'''\
pass
'''
param_list = ', '.join(attributes)
initializer_attributes = ''
for attribute in attributes:
initializer_attributes += f'''\
self.{attribute} = {attribute}
'''
initializer = f'''\
def __init__(self, {param_list}):
{initializer_attributes}'''
return initializer
@classmethod
def generate_visitor_class(cls, other_classes, without = []):
base = '''\
class Visitor:
# Base methods to be overridden in child classes
'''
method_count = 0
for klass in other_classes:
if klass in without:
continue
method = f'''\
def visit_{klass.lower()}(self, {klass.lower()}):
raise NotImplementedError('visit_{klass.lower()}')
'''
base += method
method_count += 1
if method_count == 0:
base += '''\
pass
'''
return base
@classmethod
def write(cls, path, code):
with open(path, 'w') as file:
file.write(code)
ASTGenerator.define_expressions()
ASTGenerator.define_statements()