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

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