OILS / asdl / ast.py View on Github | oils.pub

355 lines, 172 significant
1"""AST for ASDL.
2
3This is not self-hosted! If it were, it would look something like this.
4
5TODO: move it toward this schema
6
7type_expr =
8 NamedType(str name, asdl_type resolved)
9| ParameterizedType(str type_name, List[type_expr] children)
10
11Field = (type_expr typ, str name)
12
13Constructor = (List[Field] fields, str name, str shared_type)
14
15# TODO: use variant instead of constructor
16variant =
17 Simple(str name)
18 Compound(str name, List[fields])
19 SharedVariant(str tag_name, str shared_variant)
20
21asdl_type =
22 Sum(List[Constructor] variants, List[str] generate)
23 | SimpleSum() # This is just List[name]
24 | Product(List[Field] fields)
25
26Use = (List[str] module_parts, str type_names)
27Extern = (List[str] names)
28
29Module = (str name, List[Use] uses, List[Extern] externs, List[binding] defs)
30
31# Note: binding and entry can be combined
32
33binding =
34 TypeDecl(str name, asdl_type value)
35| SubTypeDecl(str name, type_expr base_class)
36
37entry =
38 Use %Use
39| Extern %Extern
40| Binding(binding b)
41"""
42from __future__ import print_function
43
44import cStringIO
45
46from typing import List, Optional
47
48# The following classes are the AST for the ASDL schema, i.e. the "meta-AST".
49# See the EBNF at the top of the file to understand the logical connection
50# between the various node types.
51
52
53class _Printable(object):
54
55 def Print(self, f, indent):
56 raise NotImplementedError()
57
58 def __str__(self):
59 f = cStringIO.StringIO()
60 self.Print(f, 0)
61 return f.getvalue()
62
63
64class Use(_Printable):
65
66 def __init__(self, module_parts, type_names):
67 # type: (List[str], List[str]) -> None
68 self.module_parts = module_parts
69 self.type_names = type_names
70
71 def Print(self, f, indent):
72 ind = indent * ' '
73 f.write('%sUse %s {\n' % (ind, ' '.join(self.module_parts)))
74 f.write(' %s%s\n' % (ind, ', '.join(t for t in self.type_names)))
75 f.write('%s}\n' % ind)
76
77
78class Extern(_Printable):
79
80 def __init__(self, names):
81 # type: (List[str]) -> None
82 self.names = names
83
84 def Print(self, f, indent):
85 ind = indent * ' '
86 f.write('%sExtern [ %s ]\n' % (ind, ' '.join(self.names)))
87
88
89class Module(_Printable):
90
91 def __init__(self, name, uses, externs, dfns):
92 # type: (str, List[Use], List[Extern], List[binding_t]) -> None
93 self.name = name
94 self.uses = uses
95 self.externs = externs
96 self.dfns = dfns
97
98 def Print(self, f, indent):
99 ind = indent * ' '
100 f.write('%sModule %s {\n' % (ind, self.name))
101
102 for u in self.uses:
103 u.Print(f, indent + 1)
104 f.write('\n')
105
106 for e in self.externs:
107 e.Print(f, indent + 1)
108 f.write('\n')
109
110 for d in self.dfns:
111 d.Print(f, indent + 1)
112 f.write('\n')
113 f.write('%s}\n' % ind)
114
115
116class binding_t(_Printable):
117 pass
118
119
120class TypeDecl(binding_t):
121 """A binding of name to a Sum or Product type."""
122
123 def __init__(self, name, value):
124 # type: (str, asdl_type_t) -> None
125 self.name = name
126 self.value = value
127
128 def Print(self, f, indent):
129 ind = indent * ' '
130 f.write('%sType %s {\n' % (ind, self.name))
131 self.value.Print(f, indent + 1)
132 f.write('%s}\n' % ind)
133
134
135class SubTypeDecl(binding_t):
136 """A declaration of a subtype.
137
138 CompoundWord < List[word_part]
139 """
140
141 def __init__(self, name, base_class):
142 # type: (str, type_expr_t) -> None
143 self.name = name
144 self.base_class = base_class
145
146 def Print(self, f, indent):
147 ind = indent * ' '
148 f.write('%sType %s {\n' % (ind, self.name))
149 self.base_class.Print(f, indent + 1)
150 f.write('%s}\n' % ind)
151
152
153class type_expr_t(object):
154 pass
155
156
157class NamedType(type_expr_t):
158 """Int, string are resolved to a Primitive type 'Point' can be resolved to
159 CompoundSum instance, etc."""
160
161 def __init__(self, name):
162 # type: (str) -> None
163 self.name = name
164
165 # Mutated by _ResolveModule / _ResolveType
166 self.resolved = None
167
168 def Print(self, f, indent):
169 """Printed on one line."""
170 f.write('NamedType %s' % (self.name)) # printed after field
171 f.write(' (%r)' % self.resolved)
172
173
174class ParameterizedType(type_expr_t):
175 """A parameterized type expression, e.g. the type of a field.
176
177 e.g. Dict[string, int] Dict[int, array[string]]
178
179 self.children is empty if it's a leaf.
180
181 Note:
182
183 string* <=> array[string]
184 mytype? <=> maybe[mytype]
185 """
186
187 def __init__(self, type_name, children):
188 # type: (str, List[type_expr_t]) -> None
189 self.type_name = type_name
190 self.children = children
191
192 def Print(self, f, indent):
193 """Printed on one line."""
194 f.write('%s' % (self.type_name)) # printed after field
195 if self.children:
196 f.write(' [ ')
197 for i, child in enumerate(self.children):
198 if i != 0:
199 f.write(', ')
200 child.Print(f, indent + 1)
201 f.write(' ]')
202
203
204class Field(_Printable):
205
206 def __init__(self, typ, name):
207 # type: (type_expr_t, str) -> None
208 self.typ = typ # type expression
209 self.name = name # variable name
210
211 def Print(self, f, indent):
212 ind = indent * ' '
213 f.write('%sField %r ' % (ind, self.name))
214 self.typ.Print(f, indent)
215 f.write('\n')
216
217
218class asdl_type_t(_Printable):
219 pass
220
221
222class _CompoundAST(asdl_type_t):
223 """Either a Product or Constructor.
224
225 encode.py and format.py need a reflection API.
226 """
227
228 def __init__(self, fields):
229 # type: (List[Field]) -> None
230 self.fields = fields or []
231
232
233class Constructor(asdl_type_t):
234
235 def __init__(self, name, shared_type, fields):
236 # type: (str, Optional[type_expr_t], List[Field]) -> None
237 self.name = name
238 self.shared_type = shared_type # for DoubleQuoted %DoubleQuoted
239 self.fields = fields or []
240
241 def Print(self, f, indent):
242 ind = indent * ' '
243 f.write('%sConstructor %s' % (ind, self.name))
244 if self.shared_type:
245 f.write(' %%%s' % self.shared_type)
246
247 if self.fields:
248 f.write(' {\n')
249 for field in self.fields:
250 field.Print(f, indent + 1)
251 f.write('%s}' % ind)
252
253 f.write('\n')
254
255
256class Sum(_Printable):
257
258 def __init__(self, types, generate=None):
259 # type: (List[Constructor], Optional[List[str]]) -> None
260 self.types = types
261 self.generate = generate or [] # type: List[str]
262
263 def Print(self, f, indent):
264 ind = indent * ' '
265 f.write('%sSum {\n' % ind)
266 for t in self.types:
267 t.Print(f, indent + 1)
268 if self.generate:
269 f.write('%s generate %s\n' % (ind, self.generate))
270 f.write('%s}\n' % ind)
271
272
273class SimpleSum(Sum):
274 pass
275
276
277class Product(_CompoundAST):
278
279 def __init__(self, fields):
280 # type: (List[Field]) -> None
281 self.fields = fields
282
283 def Print(self, f, indent):
284 ind = indent * ' '
285 f.write('%sProduct {\n' % ind)
286 for field in self.fields:
287 field.Print(f, indent + 1)
288 f.write('%s}\n' % ind)
289
290
291#
292# Helpers
293#
294
295
296def TypeNameHeuristic(t):
297 # type: (str) -> str
298 """For 'use'.
299
300 We don't parse the imported file, so we have a heuristic based on
301 the name! e.g. re_t or BraceGroup
302 """
303 return '%s_t' % t if t[0].islower() else t
304
305
306def MakeSimpleVariant(name):
307 # type: (str) -> Constructor
308 """
309 Used by frontend/{consts,options}_gen.py
310 """
311 return Constructor(name, None, None)
312
313
314def IsOptional(t):
315 # type: (type_expr_t) -> bool
316 if isinstance(t, NamedType):
317 return False
318
319 elif isinstance(t, ParameterizedType):
320 return t.type_name == 'Optional'
321
322 else:
323 raise AssertionError()
324
325
326def IsList(t):
327 # type: (type_expr_t) -> bool
328 if isinstance(t, NamedType):
329 return False
330
331 elif isinstance(t, ParameterizedType):
332 if t.type_name == 'List':
333 return True
334 if t.type_name == 'Optional':
335 return IsList(t.children[0])
336 return False
337
338 else:
339 raise AssertionError()
340
341
342def IsDict(t):
343 # type: (type_expr_t) -> bool
344 if isinstance(t, NamedType):
345 return False
346
347 elif isinstance(t, ParameterizedType):
348 if t.type_name == 'Dict':
349 return True
350 if t.type_name == 'Optional':
351 return IsDict(t.children[0])
352 return False
353
354 else:
355 raise AssertionError()