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

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