blob: b1afd0fab1ac928e0fb9fffb48accfae2a82e5aa [file] [log] [blame]
Jeremy Hylton3e0055f2005-10-20 19:59:25 +00001"""An implementation of the Zephyr Abstract Syntax Definition Language.
2
3See http://asdl.sourceforge.net/ and
4http://www.cs.princeton.edu/~danwang/Papers/dsl97/dsl97-abstract.html.
5
6Only supports top level module decl, not view. I'm guessing that view
7is intended to support the browser and I'm not interested in the
8browser.
Martin v. Löwiseae93b72006-02-28 00:12:47 +00009
10Changes for Python: Add support for module versions
Jeremy Hylton3e0055f2005-10-20 19:59:25 +000011"""
12
13#__metaclass__ = type
14
15import os
Guido van Rossum805365e2007-05-07 22:24:25 +000016import sys
Jeremy Hylton3e0055f2005-10-20 19:59:25 +000017import traceback
18
19import spark
20
Guido van Rossum805365e2007-05-07 22:24:25 +000021def output(string):
22 sys.stdout.write(string + "\n")
23
24
Jeremy Hylton3e0055f2005-10-20 19:59:25 +000025class Token:
26 # spark seems to dispatch in the parser based on a token's
27 # type attribute
28 def __init__(self, type, lineno):
29 self.type = type
30 self.lineno = lineno
31
32 def __str__(self):
33 return self.type
34
35 def __repr__(self):
36 return str(self)
37
38class Id(Token):
39 def __init__(self, value, lineno):
40 self.type = 'Id'
41 self.value = value
42 self.lineno = lineno
43
44 def __str__(self):
45 return self.value
Tim Peters710ab3b2006-02-28 18:30:36 +000046
Martin v. Löwiseae93b72006-02-28 00:12:47 +000047class String(Token):
48 def __init__(self, value, lineno):
49 self.type = 'String'
50 self.value = value
51 self.lineno = lineno
Jeremy Hylton3e0055f2005-10-20 19:59:25 +000052
Guido van Rossum805365e2007-05-07 22:24:25 +000053class ASDLSyntaxError(Exception):
Jeremy Hylton3e0055f2005-10-20 19:59:25 +000054
55 def __init__(self, lineno, token=None, msg=None):
56 self.lineno = lineno
57 self.token = token
58 self.msg = msg
59
60 def __str__(self):
61 if self.msg is None:
62 return "Error at '%s', line %d" % (self.token, self.lineno)
63 else:
64 return "%s, line %d" % (self.msg, self.lineno)
65
66class ASDLScanner(spark.GenericScanner, object):
67
68 def tokenize(self, input):
69 self.rv = []
70 self.lineno = 1
71 super(ASDLScanner, self).tokenize(input)
72 return self.rv
73
74 def t_id(self, s):
75 r"[\w\.]+"
76 # XXX doesn't distinguish upper vs. lower, which is
77 # significant for ASDL.
78 self.rv.append(Id(s, self.lineno))
Tim Peters710ab3b2006-02-28 18:30:36 +000079
Martin v. Löwiseae93b72006-02-28 00:12:47 +000080 def t_string(self, s):
81 r'"[^"]*"'
82 self.rv.append(String(s, self.lineno))
Jeremy Hylton3e0055f2005-10-20 19:59:25 +000083
84 def t_xxx(self, s): # not sure what this production means
85 r"<="
86 self.rv.append(Token(s, self.lineno))
87
88 def t_punctuation(self, s):
89 r"[\{\}\*\=\|\(\)\,\?\:]"
90 self.rv.append(Token(s, self.lineno))
91
92 def t_comment(self, s):
93 r"\-\-[^\n]*"
94 pass
95
96 def t_newline(self, s):
97 r"\n"
98 self.lineno += 1
99
100 def t_whitespace(self, s):
101 r"[ \t]+"
102 pass
103
104 def t_default(self, s):
105 r" . +"
Brett Cannonf0365512006-08-25 04:06:31 +0000106 raise ValueError, "unmatched input: %r" % s
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000107
108class ASDLParser(spark.GenericParser, object):
109 def __init__(self):
110 super(ASDLParser, self).__init__("module")
111
112 def typestring(self, tok):
113 return tok.type
114
115 def error(self, tok):
116 raise ASDLSyntaxError(tok.lineno, tok)
117
Martin v. Löwiseae93b72006-02-28 00:12:47 +0000118 def p_module_0(self, (module, name, version, _0, _1)):
119 " module ::= Id Id version { } "
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000120 if module.value != "module":
121 raise ASDLSyntaxError(module.lineno,
122 msg="expected 'module', found %s" % module)
Martin v. Löwiseae93b72006-02-28 00:12:47 +0000123 return Module(name, None, version)
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000124
Martin v. Löwiseae93b72006-02-28 00:12:47 +0000125 def p_module(self, (module, name, version, _0, definitions, _1)):
126 " module ::= Id Id version { definitions } "
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000127 if module.value != "module":
128 raise ASDLSyntaxError(module.lineno,
129 msg="expected 'module', found %s" % module)
Martin v. Löwiseae93b72006-02-28 00:12:47 +0000130 return Module(name, definitions, version)
Tim Peters710ab3b2006-02-28 18:30:36 +0000131
Martin v. Löwiseae93b72006-02-28 00:12:47 +0000132 def p_version(self, (version, V)):
133 "version ::= Id String"
134 if version.value != "version":
135 raise ASDLSyntaxError(version.lineno,
Guido van Rossum805365e2007-05-07 22:24:25 +0000136 msg="expected 'version', found %" % version)
Martin v. Löwiseae93b72006-02-28 00:12:47 +0000137 return V
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000138
139 def p_definition_0(self, (definition,)):
140 " definitions ::= definition "
141 return definition
142
143 def p_definition_1(self, (definitions, definition)):
144 " definitions ::= definition definitions "
145 return definitions + definition
146
147 def p_definition(self, (id, _, type)):
148 " definition ::= Id = type "
149 return [Type(id, type)]
150
151 def p_type_0(self, (product,)):
152 " type ::= product "
153 return product
154
155 def p_type_1(self, (sum,)):
156 " type ::= sum "
157 return Sum(sum)
158
159 def p_type_2(self, (sum, id, _0, attributes, _1)):
160 " type ::= sum Id ( fields ) "
161 if id.value != "attributes":
162 raise ASDLSyntaxError(id.lineno,
163 msg="expected attributes, found %s" % id)
Martin v. Löwis49c5da12006-03-01 22:49:05 +0000164 if attributes:
165 attributes.reverse()
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000166 return Sum(sum, attributes)
167
168 def p_product(self, (_0, fields, _1)):
169 " product ::= ( fields ) "
170 # XXX can't I just construct things in the right order?
Tim Peters536cf992005-12-25 23:18:31 +0000171 fields.reverse()
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000172 return Product(fields)
173
174 def p_sum_0(self, (constructor,)):
175 " sum ::= constructor """
176 return [constructor]
177
178 def p_sum_1(self, (constructor, _, sum)):
179 " sum ::= constructor | sum "
180 return [constructor] + sum
181
182 def p_sum_2(self, (constructor, _, sum)):
183 " sum ::= constructor | sum "
184 return [constructor] + sum
185
186 def p_constructor_0(self, (id,)):
187 " constructor ::= Id "
188 return Constructor(id)
189
190 def p_constructor_1(self, (id, _0, fields, _1)):
191 " constructor ::= Id ( fields ) "
192 # XXX can't I just construct things in the right order?
Tim Peters536cf992005-12-25 23:18:31 +0000193 fields.reverse()
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000194 return Constructor(id, fields)
195
196 def p_fields_0(self, (field,)):
197 " fields ::= field "
198 return [field]
199
200 def p_fields_1(self, (field, _, fields)):
201 " fields ::= field , fields "
202 return fields + [field]
203
204 def p_field_0(self, (type,)):
205 " field ::= Id "
206 return Field(type)
207
208 def p_field_1(self, (type, name)):
209 " field ::= Id Id "
210 return Field(type, name)
211
212 def p_field_2(self, (type, _, name)):
213 " field ::= Id * Id "
214 return Field(type, name, seq=1)
215
216 def p_field_3(self, (type, _, name)):
217 " field ::= Id ? Id "
218 return Field(type, name, opt=1)
219
220 def p_field_4(self, (type, _)):
221 " field ::= Id * "
222 return Field(type, seq=1)
223
224 def p_field_5(self, (type, _)):
225 " field ::= Id ? "
226 return Field(type, opt=1)
227
228builtin_types = ("identifier", "string", "int", "bool", "object")
229
230# below is a collection of classes to capture the AST of an AST :-)
231# not sure if any of the methods are useful yet, but I'm adding them
232# piecemeal as they seem helpful
233
234class AST:
235 pass # a marker class
236
237class Module(AST):
Martin v. Löwiseae93b72006-02-28 00:12:47 +0000238 def __init__(self, name, dfns, version):
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000239 self.name = name
240 self.dfns = dfns
Martin v. Löwiseae93b72006-02-28 00:12:47 +0000241 self.version = version
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000242 self.types = {} # maps type name to value (from dfns)
243 for type in dfns:
244 self.types[type.name.value] = type.value
245
246 def __repr__(self):
247 return "Module(%s, %s)" % (self.name, self.dfns)
248
249class Type(AST):
250 def __init__(self, name, value):
251 self.name = name
252 self.value = value
253
254 def __repr__(self):
255 return "Type(%s, %s)" % (self.name, self.value)
256
257class Constructor(AST):
258 def __init__(self, name, fields=None):
259 self.name = name
260 self.fields = fields or []
261
262 def __repr__(self):
263 return "Constructor(%s, %s)" % (self.name, self.fields)
264
265class Field(AST):
266 def __init__(self, type, name=None, seq=0, opt=0):
267 self.type = type
268 self.name = name
269 self.seq = seq
270 self.opt = opt
271
272 def __repr__(self):
273 if self.seq:
274 extra = ", seq=1"
275 elif self.opt:
276 extra = ", opt=1"
277 else:
278 extra = ""
279 if self.name is None:
280 return "Field(%s%s)" % (self.type, extra)
281 else:
282 return "Field(%s, %s%s)" % (self.type, self.name, extra)
283
284class Sum(AST):
285 def __init__(self, types, attributes=None):
286 self.types = types
287 self.attributes = attributes or []
288
289 def __repr__(self):
290 if self.attributes is None:
291 return "Sum(%s)" % self.types
292 else:
293 return "Sum(%s, %s)" % (self.types, self.attributes)
294
295class Product(AST):
296 def __init__(self, fields):
297 self.fields = fields
298
299 def __repr__(self):
300 return "Product(%s)" % self.fields
301
302class VisitorBase(object):
303
304 def __init__(self, skip=0):
305 self.cache = {}
306 self.skip = skip
307
308 def visit(self, object, *args):
309 meth = self._dispatch(object)
310 if meth is None:
311 return
312 try:
313 meth(object, *args)
Guido van Rossum805365e2007-05-07 22:24:25 +0000314 except Exception:
315 output("Error visiting", repr(object))
316 output(sys.exc_info()[1])
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000317 traceback.print_exc()
318 # XXX hack
319 if hasattr(self, 'file'):
320 self.file.flush()
321 os._exit(1)
322
323 def _dispatch(self, object):
324 assert isinstance(object, AST), repr(object)
325 klass = object.__class__
326 meth = self.cache.get(klass)
327 if meth is None:
328 methname = "visit" + klass.__name__
329 if self.skip:
330 meth = getattr(self, methname, None)
331 else:
332 meth = getattr(self, methname)
333 self.cache[klass] = meth
334 return meth
335
336class Check(VisitorBase):
337
338 def __init__(self):
339 super(Check, self).__init__(skip=1)
340 self.cons = {}
341 self.errors = 0
342 self.types = {}
343
344 def visitModule(self, mod):
345 for dfn in mod.dfns:
346 self.visit(dfn)
347
348 def visitType(self, type):
349 self.visit(type.value, str(type.name))
350
351 def visitSum(self, sum, name):
352 for t in sum.types:
353 self.visit(t, name)
354
355 def visitConstructor(self, cons, name):
356 key = str(cons.name)
357 conflict = self.cons.get(key)
358 if conflict is None:
359 self.cons[key] = name
360 else:
Guido van Rossum805365e2007-05-07 22:24:25 +0000361 output("Redefinition of constructor %s" % key)
362 output("Defined in %s and %s" % (conflict, name))
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000363 self.errors += 1
364 for f in cons.fields:
365 self.visit(f, key)
366
367 def visitField(self, field, name):
368 key = str(field.type)
369 l = self.types.setdefault(key, [])
370 l.append(name)
371
372 def visitProduct(self, prod, name):
373 for f in prod.fields:
374 self.visit(f, name)
375
376def check(mod):
377 v = Check()
378 v.visit(mod)
379
380 for t in v.types:
Fred Drake34e4f522006-12-29 04:42:48 +0000381 if t not in mod.types and not t in builtin_types:
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000382 v.errors += 1
383 uses = ", ".join(v.types[t])
Guido van Rossum805365e2007-05-07 22:24:25 +0000384 output("Undefined type %s, used in %s" % (t, uses))
Tim Peters536cf992005-12-25 23:18:31 +0000385
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000386 return not v.errors
387
388def parse(file):
389 scanner = ASDLScanner()
390 parser = ASDLParser()
391
392 buf = open(file).read()
393 tokens = scanner.tokenize(buf)
394 try:
395 return parser.parse(tokens)
Guido van Rossum805365e2007-05-07 22:24:25 +0000396 except ASDLSyntaxError:
397 output(sys.exc_info()[1])
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000398 lines = buf.split("\n")
Guido van Rossum805365e2007-05-07 22:24:25 +0000399 output(lines[err.lineno - 1]) # lines starts at 0, files at 1
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000400
401if __name__ == "__main__":
402 import glob
403 import sys
404
405 if len(sys.argv) > 1:
406 files = sys.argv[1:]
407 else:
408 testdir = "tests"
409 files = glob.glob(testdir + "/*.asdl")
Tim Peters536cf992005-12-25 23:18:31 +0000410
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000411 for file in files:
Guido van Rossum805365e2007-05-07 22:24:25 +0000412 output(file)
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000413 mod = parse(file)
Guido van Rossum805365e2007-05-07 22:24:25 +0000414 output("module", mod.name)
415 output(len(mod.dfns), "definitions")
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000416 if not check(mod):
Guido van Rossum805365e2007-05-07 22:24:25 +0000417 output("Check failed")
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000418 else:
419 for dfn in mod.dfns:
Guido van Rossum805365e2007-05-07 22:24:25 +0000420 output(dfn.type)