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

313 lines, 158 significant
1"""AST for ASDL.
2
3This is not self-hosted! If it were, it would look something like this.
4
5(TODO: we should probably improve it! And add static types)
6
7type_ref =
8 NamedType(str name, type resolved)
9| ParameterizedType(str type_name, List[type_ref] children)
10
11Field = (type_ref 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
21type =
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, type value)
35| SubTypeDecl(str name, type_ref 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
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 AST(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(AST):
65
66 def __init__(self, module_parts, type_names):
67 self.module_parts = module_parts
68 self.type_names = type_names
69
70 def Print(self, f, indent):
71 ind = indent * ' '
72 f.write('%sUse %s {\n' % (ind, ' '.join(self.module_parts)))
73 f.write(' %s%s\n' % (ind, ', '.join(t for t in self.type_names)))
74 f.write('%s}\n' % ind)
75
76
77class Extern(AST):
78
79 def __init__(self, names):
80 self.names = names
81
82 def Print(self, f, indent):
83 ind = indent * ' '
84 f.write('%sExtern [ %s ]\n' % (ind, ' '.join(self.names)))
85
86
87class Module(AST):
88
89 def __init__(self, name, uses, externs, dfns):
90 self.name = name
91 self.uses = uses
92 self.externs = externs
93 self.dfns = dfns
94
95 def Print(self, f, indent):
96 ind = indent * ' '
97 f.write('%sModule %s {\n' % (ind, self.name))
98
99 for u in self.uses:
100 u.Print(f, indent + 1)
101 f.write('\n')
102
103 for e in self.externs:
104 e.Print(f, indent + 1)
105 f.write('\n')
106
107 for d in self.dfns:
108 d.Print(f, indent + 1)
109 f.write('\n')
110 f.write('%s}\n' % ind)
111
112
113class TypeDecl(AST):
114 """A binding of name to a Sum or Product type."""
115
116 def __init__(self, name, value):
117 self.name = name # type: str
118 self.value = value # type: AST
119
120 def Print(self, f, indent):
121 ind = indent * ' '
122 f.write('%sType %s {\n' % (ind, self.name))
123 self.value.Print(f, indent + 1)
124 f.write('%s}\n' % ind)
125
126
127class SubTypeDecl(AST):
128 """A declaration of a subtype.
129
130 CompoundWord < List[word_part]
131 """
132
133 def __init__(self, name, base_class):
134 self.name = name # type: str
135 self.base_class = base_class # type: AST
136
137 def Print(self, f, indent):
138 ind = indent * ' '
139 f.write('%sType %s {\n' % (ind, self.name))
140 self.base_class.Print(f, indent + 1)
141 f.write('%s}\n' % ind)
142
143
144class type_ref_t(object):
145
146 def __init__(self):
147 pass
148
149 def IsOptional(self):
150 return False
151
152 def IsList(self):
153 return False
154
155 def IsDict(self):
156 return False
157
158
159class NamedType(type_ref_t):
160 """Int, string are resolved to a Primitive type 'Point' can be resolved to
161 CompoundSum instance, etc."""
162
163 def __init__(self, name):
164 # type: (str) -> None
165 self.name = name
166
167 # Mutated by _ResolveModule / _ResolveType
168 self.resolved = None
169
170 def Print(self, f, indent):
171 """Printed on one line."""
172 f.write('NamedType %s' % (self.name)) # printed after field
173 f.write(' (%r)' % self.resolved)
174
175
176class ParameterizedType(type_ref_t):
177 """A parameterized type expression, e.g. the type of a field.
178
179 e.g. Dict[string, int] Dict[int, array[string]]
180
181 self.children is empty if it's a leaf.
182
183 Note:
184
185 string* <=> array[string]
186 mytype? <=> maybe[mytype]
187 """
188
189 def __init__(self, type_name, children):
190 self.type_name = type_name # type: str
191 self.children = children # type: List[type_ref_t]
192
193 def Print(self, f, indent):
194 """Printed on one line."""
195 f.write('%s' % (self.type_name)) # printed after field
196 if self.children:
197 f.write(' [ ')
198 for i, child in enumerate(self.children):
199 if i != 0:
200 f.write(', ')
201 child.Print(f, indent + 1)
202 f.write(' ]')
203
204 def IsOptional(self):
205 return self.type_name == 'Optional'
206
207 def IsList(self):
208 if self.type_name == 'List':
209 return True
210 if self.type_name == 'Optional':
211 return self.children[0].IsList()
212 return False
213
214 def IsDict(self):
215 if self.type_name == 'Dict':
216 return True
217 if self.type_name == 'Optional':
218 return self.children[0].IsDict()
219 return False
220
221
222class Field(AST):
223
224 def __init__(self, typ, name):
225 # type: (AST, str) -> None
226 self.typ = typ # type expression
227 self.name = name # variable name
228
229 def Print(self, f, indent):
230 ind = indent * ' '
231 f.write('%sField %r ' % (ind, self.name))
232 self.typ.Print(f, indent)
233 f.write('\n')
234
235
236class _CompoundAST(AST):
237 """Either a Product or Constructor.
238
239 encode.py and format.py need a reflection API.
240 """
241
242 def __init__(self, fields):
243 self.fields = fields or []
244
245
246class Constructor(_CompoundAST):
247
248 def __init__(self, name, shared_type=None, fields=None):
249 _CompoundAST.__init__(self, fields)
250 self.name = name
251 self.shared_type = shared_type # for DoubleQuoted %DoubleQuoted
252
253 def Print(self, f, indent):
254 ind = indent * ' '
255 f.write('%sConstructor %s' % (ind, self.name))
256 if self.shared_type:
257 f.write(' %%%s' % self.shared_type)
258
259 if self.fields:
260 f.write(' {\n')
261 for field in self.fields:
262 field.Print(f, indent + 1)
263 f.write('%s}' % ind)
264
265 f.write('\n')
266
267
268class Sum(AST):
269
270 def __init__(self, types, generate=None):
271 self.types = types # type: List[Constructor]
272 self.generate = generate or []
273
274 def Print(self, f, indent):
275 ind = indent * ' '
276 f.write('%sSum {\n' % ind)
277 for t in self.types:
278 t.Print(f, indent + 1)
279 if self.generate:
280 f.write('%s generate %s\n' % (ind, self.generate))
281 f.write('%s}\n' % ind)
282
283
284class SimpleSum(Sum):
285 pass
286
287
288class Product(_CompoundAST):
289
290 def __init__(self, fields):
291 _CompoundAST.__init__(self, fields)
292
293 def Print(self, f, indent):
294 ind = indent * ' '
295 f.write('%sProduct {\n' % ind)
296 for field in self.fields:
297 field.Print(f, indent + 1)
298 f.write('%s}\n' % ind)
299
300
301#
302# Helpers
303#
304
305
306def TypeNameHeuristic(t):
307 # type: (str) -> str
308 """For 'use'.
309
310 We don't parse the imported file, so we have a heuristic based on
311 the name! e.g. re_t or BraceGroup
312 """
313 return '%s_t' % t if t[0].islower() else t