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

270 lines, 155 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 Extern(AST):
41
42 def __init__(self, names):
43 self.names = names
44
45 def Print(self, f, indent):
46 ind = indent * ' '
47 f.write('%sExtern [ %s ]\n' % (ind, ' '.join(self.names)))
48
49
50class Module(AST):
51
52 def __init__(self, name, uses, externs, dfns):
53 self.name = name
54 self.uses = uses
55 self.externs = externs
56 self.dfns = dfns
57
58 def Print(self, f, indent):
59 ind = indent * ' '
60 f.write('%sModule %s {\n' % (ind, self.name))
61
62 for u in self.uses:
63 u.Print(f, indent + 1)
64 f.write('\n')
65
66 for e in self.externs:
67 e.Print(f, indent + 1)
68 f.write('\n')
69
70 for d in self.dfns:
71 d.Print(f, indent + 1)
72 f.write('\n')
73 f.write('%s}\n' % ind)
74
75
76class TypeDecl(AST):
77 """A binding of name to a Sum or Product type."""
78
79 def __init__(self, name, value):
80 self.name = name # type: str
81 self.value = value # type: AST
82
83 def Print(self, f, indent):
84 ind = indent * ' '
85 f.write('%sType %s {\n' % (ind, self.name))
86 self.value.Print(f, indent + 1)
87 f.write('%s}\n' % ind)
88
89
90class SubTypeDecl(AST):
91 """A declaration of a subtype.
92
93 CompoundWord < List[word_part]
94 """
95
96 def __init__(self, name, base_class):
97 self.name = name # type: str
98 self.base_class = base_class # type: AST
99
100 def Print(self, f, indent):
101 ind = indent * ' '
102 f.write('%sType %s {\n' % (ind, self.name))
103 self.base_class.Print(f, indent + 1)
104 f.write('%s}\n' % ind)
105
106
107class NamedType(AST):
108 """Int, string are resolved to a Primitive type 'Point' can be resolved to
109 CompoundSum instance, etc."""
110
111 def __init__(self, name):
112 # type: (str) -> None
113 self.name = name
114
115 # Mutated by _ResolveModule / _ResolveType
116 self.resolved = None
117
118 def Print(self, f, indent):
119 """Printed on one line."""
120 f.write('NamedType %s' % (self.name)) # printed after field
121 f.write(' (%r)' % self.resolved)
122
123 def IsOptional(self):
124 return False
125
126 def IsList(self):
127 return False
128
129 def IsDict(self):
130 return False
131
132
133class ParameterizedType(AST):
134 """A parameterized type expression, e.g. the type of a field.
135
136 e.g. Dict[string, int] Dict[int, array[string]]
137
138 self.children is empty if it's a leaf.
139
140 Note:
141
142 string* <=> array[string]
143 mytype? <=> maybe[mytype]
144 """
145
146 def __init__(self, type_name, children):
147 self.type_name = type_name # type: str
148 self.children = children # type: List[AST]
149
150 def Print(self, f, indent):
151 """Printed on one line."""
152 f.write('%s' % (self.type_name)) # printed after field
153 if self.children:
154 f.write(' [ ')
155 for i, child in enumerate(self.children):
156 if i != 0:
157 f.write(', ')
158 child.Print(f, indent + 1)
159 f.write(' ]')
160
161 def IsOptional(self):
162 return self.type_name == 'Optional'
163
164 def IsList(self):
165 if self.type_name == 'List':
166 return True
167 if self.type_name == 'Optional':
168 return self.children[0].IsList()
169 return False
170
171 def IsDict(self):
172 if self.type_name == 'Dict':
173 return True
174 if self.type_name == 'Optional':
175 return self.children[0].IsDict()
176 return False
177
178
179class Field(AST):
180
181 def __init__(self, typ, name):
182 # type: (AST, str) -> None
183 self.typ = typ # type expression
184 self.name = name # variable name
185
186 def Print(self, f, indent):
187 ind = indent * ' '
188 f.write('%sField %r ' % (ind, self.name))
189 self.typ.Print(f, indent)
190 f.write('\n')
191
192
193class _CompoundAST(AST):
194 """Either a Product or Constructor.
195
196 encode.py and format.py need a reflection API.
197 """
198
199 def __init__(self, fields):
200 self.fields = fields or []
201
202
203class Constructor(_CompoundAST):
204
205 def __init__(self, name, shared_type=None, fields=None):
206 _CompoundAST.__init__(self, fields)
207 self.name = name
208 self.shared_type = shared_type # for DoubleQuoted %DoubleQuoted
209
210 def Print(self, f, indent):
211 ind = indent * ' '
212 f.write('%sConstructor %s' % (ind, self.name))
213 if self.shared_type:
214 f.write(' %%%s' % self.shared_type)
215
216 if self.fields:
217 f.write(' {\n')
218 for field in self.fields:
219 field.Print(f, indent + 1)
220 f.write('%s}' % ind)
221
222 f.write('\n')
223
224
225class Sum(AST):
226
227 def __init__(self, types, generate=None):
228 self.types = types # type: List[Constructor]
229 self.generate = generate or []
230
231 def Print(self, f, indent):
232 ind = indent * ' '
233 f.write('%sSum {\n' % ind)
234 for t in self.types:
235 t.Print(f, indent + 1)
236 if self.generate:
237 f.write('%s generate %s\n' % (ind, self.generate))
238 f.write('%s}\n' % ind)
239
240
241class SimpleSum(Sum):
242 pass
243
244
245class Product(_CompoundAST):
246
247 def __init__(self, fields):
248 _CompoundAST.__init__(self, fields)
249
250 def Print(self, f, indent):
251 ind = indent * ' '
252 f.write('%sProduct {\n' % ind)
253 for field in self.fields:
254 field.Print(f, indent + 1)
255 f.write('%s}\n' % ind)
256
257
258#
259# Helpers
260#
261
262
263def TypeNameHeuristic(t):
264 # type: (str) -> str
265 """For 'use'.
266
267 We don't parse the imported file, so we have a heuristic based on
268 the name! e.g. re_t or BraceGroup
269 """
270 return '%s_t' % t if t[0].islower() else t