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

312 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 def __init__(self):
146 pass
147
148 def IsOptional(self):
149 return False
150
151 def IsList(self):
152 return False
153
154 def IsDict(self):
155 return False
156
157
158class NamedType(type_ref_t):
159 """Int, string are resolved to a Primitive type 'Point' can be resolved to
160 CompoundSum instance, etc."""
161
162 def __init__(self, name):
163 # type: (str) -> None
164 self.name = name
165
166 # Mutated by _ResolveModule / _ResolveType
167 self.resolved = None
168
169 def Print(self, f, indent):
170 """Printed on one line."""
171 f.write('NamedType %s' % (self.name)) # printed after field
172 f.write(' (%r)' % self.resolved)
173
174
175class ParameterizedType(type_ref_t):
176 """A parameterized type expression, e.g. the type of a field.
177
178 e.g. Dict[string, int] Dict[int, array[string]]
179
180 self.children is empty if it's a leaf.
181
182 Note:
183
184 string* <=> array[string]
185 mytype? <=> maybe[mytype]
186 """
187
188 def __init__(self, type_name, children):
189 self.type_name = type_name # type: str
190 self.children = children # type: List[type_ref_t]
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 def IsOptional(self):
204 return self.type_name == 'Optional'
205
206 def IsList(self):
207 if self.type_name == 'List':
208 return True
209 if self.type_name == 'Optional':
210 return self.children[0].IsList()
211 return False
212
213 def IsDict(self):
214 if self.type_name == 'Dict':
215 return True
216 if self.type_name == 'Optional':
217 return self.children[0].IsDict()
218 return False
219
220
221class Field(AST):
222
223 def __init__(self, typ, name):
224 # type: (AST, str) -> None
225 self.typ = typ # type expression
226 self.name = name # variable name
227
228 def Print(self, f, indent):
229 ind = indent * ' '
230 f.write('%sField %r ' % (ind, self.name))
231 self.typ.Print(f, indent)
232 f.write('\n')
233
234
235class _CompoundAST(AST):
236 """Either a Product or Constructor.
237
238 encode.py and format.py need a reflection API.
239 """
240
241 def __init__(self, fields):
242 self.fields = fields or []
243
244
245class Constructor(_CompoundAST):
246
247 def __init__(self, name, shared_type=None, fields=None):
248 _CompoundAST.__init__(self, fields)
249 self.name = name
250 self.shared_type = shared_type # for DoubleQuoted %DoubleQuoted
251
252 def Print(self, f, indent):
253 ind = indent * ' '
254 f.write('%sConstructor %s' % (ind, self.name))
255 if self.shared_type:
256 f.write(' %%%s' % self.shared_type)
257
258 if self.fields:
259 f.write(' {\n')
260 for field in self.fields:
261 field.Print(f, indent + 1)
262 f.write('%s}' % ind)
263
264 f.write('\n')
265
266
267class Sum(AST):
268
269 def __init__(self, types, generate=None):
270 self.types = types # type: List[Constructor]
271 self.generate = generate or []
272
273 def Print(self, f, indent):
274 ind = indent * ' '
275 f.write('%sSum {\n' % ind)
276 for t in self.types:
277 t.Print(f, indent + 1)
278 if self.generate:
279 f.write('%s generate %s\n' % (ind, self.generate))
280 f.write('%s}\n' % ind)
281
282
283class SimpleSum(Sum):
284 pass
285
286
287class Product(_CompoundAST):
288
289 def __init__(self, fields):
290 _CompoundAST.__init__(self, fields)
291
292 def Print(self, f, indent):
293 ind = indent * ' '
294 f.write('%sProduct {\n' % ind)
295 for field in self.fields:
296 field.Print(f, indent + 1)
297 f.write('%s}\n' % ind)
298
299
300#
301# Helpers
302#
303
304
305def TypeNameHeuristic(t):
306 # type: (str) -> str
307 """For 'use'.
308
309 We don't parse the imported file, so we have a heuristic based on
310 the name! e.g. re_t or BraceGroup
311 """
312 return '%s_t' % t if t[0].islower() else t