OILS / asdl / ast.py View on Github | oilshell.org

238 lines, 136 significant
1"""AST for ASDL.
2
3(Not self-hosted!)
4"""
5from __future__ import print_function
6
7import cStringIO
8
9from typing import List
10
11# The following classes are the AST for the ASDL schema, i.e. the "meta-AST".
12# See the EBNF at the top of the file to understand the logical connection
13# between the various node types.
14
15
16class AST(object):
17
18 def Print(self, f, indent):
19 raise NotImplementedError()
20
21 def __str__(self):
22 f = cStringIO.StringIO()
23 self.Print(f, 0)
24 return f.getvalue()
25
26
27class Use(AST):
28
29 def __init__(self, module_parts, type_names):
30 self.module_parts = module_parts
31 self.type_names = type_names
32
33 def Print(self, f, indent):
34 ind = indent * ' '
35 f.write('%sUse %s {\n' % (ind, ' '.join(self.module_parts)))
36 f.write(' %s%s\n' % (ind, ', '.join(t for t in self.type_names)))
37 f.write('%s}\n' % ind)
38
39
40class Module(AST):
41
42 def __init__(self, name, uses, dfns):
43 self.name = name
44 self.uses = uses
45 self.dfns = dfns
46
47 def Print(self, f, indent):
48 ind = indent * ' '
49 f.write('%sModule %s {\n' % (ind, self.name))
50
51 for u in self.uses:
52 u.Print(f, indent + 1)
53 f.write('\n')
54
55 for d in self.dfns:
56 d.Print(f, indent + 1)
57 f.write('\n')
58 f.write('%s}\n' % ind)
59
60
61class TypeDecl(AST):
62 """A binding of name to a Sum or Product type."""
63
64 def __init__(self, name, value):
65 self.name = name # type: str
66 self.value = value # type: AST
67
68 def Print(self, f, indent):
69 ind = indent * ' '
70 f.write('%sType %s {\n' % (ind, self.name))
71 self.value.Print(f, indent + 1)
72 f.write('%s}\n' % ind)
73
74
75class NamedType(AST):
76 """Int, string are resolved to a Primitive type 'Point' can be resolved to
77 CompoundSum instance, etc."""
78
79 def __init__(self, name):
80 # type: (str) -> None
81 self.name = name
82
83 # Mutated by _ResolveModule / _ResolveType
84 self.resolved = None
85
86 def Print(self, f, indent):
87 """Printed on one line."""
88 f.write('NamedType %s' % (self.name)) # printed after field
89 f.write(' (%r)' % self.resolved)
90
91 def IsOptional(self):
92 return False
93
94 def IsList(self):
95 return False
96
97 def IsDict(self):
98 return False
99
100
101class ParameterizedType(AST):
102 """A parameterized type expression, e.g. the type of a field.
103
104 e.g. Dict[string, int] Dict[int, array[string]]
105
106 self.children is empty if it's a leaf.
107
108 Note:
109
110 string* <=> array[string]
111 mytype? <=> maybe[mytype]
112 """
113
114 def __init__(self, type_name, children):
115 self.type_name = type_name # type: str
116 self.children = children # type: List[AST]
117
118 def Print(self, f, indent):
119 """Printed on one line."""
120 f.write('%s' % (self.type_name)) # printed after field
121 if self.children:
122 f.write(' [ ')
123 for i, child in enumerate(self.children):
124 if i != 0:
125 f.write(', ')
126 child.Print(f, indent + 1)
127 f.write(' ]')
128
129 def IsOptional(self):
130 return self.type_name == 'Optional'
131
132 def IsList(self):
133 if self.type_name == 'List':
134 return True
135 if self.type_name == 'Optional':
136 return self.children[0].IsList()
137 return False
138
139 def IsDict(self):
140 if self.type_name == 'Dict':
141 return True
142 if self.type_name == 'Optional':
143 return self.children[0].IsDict()
144 return False
145
146
147class Field(AST):
148
149 def __init__(self, typ, name):
150 # type: (AST, str) -> None
151 self.typ = typ # type expression
152 self.name = name # variable name
153
154 def Print(self, f, indent):
155 ind = indent * ' '
156 f.write('%sField %r ' % (ind, self.name))
157 self.typ.Print(f, indent)
158 f.write('\n')
159
160
161class _CompoundAST(AST):
162 """Either a Product or Constructor.
163
164 encode.py and format.py need a reflection API.
165 """
166
167 def __init__(self, fields):
168 self.fields = fields or []
169
170
171class Constructor(_CompoundAST):
172
173 def __init__(self, name, shared_type=None, fields=None):
174 _CompoundAST.__init__(self, fields)
175 self.name = name
176 self.shared_type = shared_type # for DoubleQuoted %DoubleQuoted
177
178 def Print(self, f, indent):
179 ind = indent * ' '
180 f.write('%sConstructor %s' % (ind, self.name))
181 if self.shared_type:
182 f.write(' %%%s' % self.shared_type)
183
184 if self.fields:
185 f.write(' {\n')
186 for field in self.fields:
187 field.Print(f, indent + 1)
188 f.write('%s}' % ind)
189
190 f.write('\n')
191
192
193class Sum(AST):
194
195 def __init__(self, types, generate=None):
196 self.types = types # type: List[Constructor]
197 self.generate = generate or []
198
199 def Print(self, f, indent):
200 ind = indent * ' '
201 f.write('%sSum {\n' % ind)
202 for t in self.types:
203 t.Print(f, indent + 1)
204 if self.generate:
205 f.write('%s generate %s\n' % (ind, self.generate))
206 f.write('%s}\n' % ind)
207
208
209class SimpleSum(Sum):
210 pass
211
212
213class Product(_CompoundAST):
214
215 def __init__(self, fields):
216 _CompoundAST.__init__(self, fields)
217
218 def Print(self, f, indent):
219 ind = indent * ' '
220 f.write('%sProduct {\n' % ind)
221 for field in self.fields:
222 field.Print(f, indent + 1)
223 f.write('%s}\n' % ind)
224
225
226#
227# Helpers
228#
229
230
231def TypeNameHeuristic(t):
232 # type: (str) -> str
233 """For 'use'.
234
235 We don't parse the imported file, so we have a heuristic based on
236 the name! e.g. re_t or BraceGroup
237 """
238 return '%s_t' % t if t[0].islower() else t