blob: 8fbd9ec9fe1a4f464a1da9b95e5f95794634ea8e [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" . +"
Collin Winter4902e692007-08-30 18:18:27 +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
Guido van Rossum1bc535d2007-05-15 18:46:22 +0000118 def p_module_0(self, info):
Martin v. Löwiseae93b72006-02-28 00:12:47 +0000119 " module ::= Id Id version { } "
Guido van Rossum1bc535d2007-05-15 18:46:22 +0000120 module, name, version, _0, _1 = info
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000121 if module.value != "module":
122 raise ASDLSyntaxError(module.lineno,
123 msg="expected 'module', found %s" % module)
Martin v. Löwiseae93b72006-02-28 00:12:47 +0000124 return Module(name, None, version)
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000125
Guido van Rossum1bc535d2007-05-15 18:46:22 +0000126 def p_module(self, info):
Martin v. Löwiseae93b72006-02-28 00:12:47 +0000127 " module ::= Id Id version { definitions } "
Guido van Rossum1bc535d2007-05-15 18:46:22 +0000128 module, name, version, _0, definitions, _1 = info
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000129 if module.value != "module":
130 raise ASDLSyntaxError(module.lineno,
131 msg="expected 'module', found %s" % module)
Martin v. Löwiseae93b72006-02-28 00:12:47 +0000132 return Module(name, definitions, version)
Tim Peters710ab3b2006-02-28 18:30:36 +0000133
Guido van Rossum1bc535d2007-05-15 18:46:22 +0000134 def p_version(self, info):
Martin v. Löwiseae93b72006-02-28 00:12:47 +0000135 "version ::= Id String"
Guido van Rossum1bc535d2007-05-15 18:46:22 +0000136 version, V = info
Martin v. Löwiseae93b72006-02-28 00:12:47 +0000137 if version.value != "version":
138 raise ASDLSyntaxError(version.lineno,
Guido van Rossum805365e2007-05-07 22:24:25 +0000139 msg="expected 'version', found %" % version)
Martin v. Löwiseae93b72006-02-28 00:12:47 +0000140 return V
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000141
Guido van Rossum1bc535d2007-05-15 18:46:22 +0000142 def p_definition_0(self, definition):
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000143 " definitions ::= definition "
Guido van Rossum1bc535d2007-05-15 18:46:22 +0000144 return definition[0]
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000145
Guido van Rossum1bc535d2007-05-15 18:46:22 +0000146 def p_definition_1(self, definitions):
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000147 " definitions ::= definition definitions "
Guido van Rossum1bc535d2007-05-15 18:46:22 +0000148 return definitions[0] + definitions[1]
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000149
Guido van Rossum1bc535d2007-05-15 18:46:22 +0000150 def p_definition(self, info):
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000151 " definition ::= Id = type "
Guido van Rossum1bc535d2007-05-15 18:46:22 +0000152 id, _, type = info
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000153 return [Type(id, type)]
154
Guido van Rossum1bc535d2007-05-15 18:46:22 +0000155 def p_type_0(self, product):
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000156 " type ::= product "
Guido van Rossum1bc535d2007-05-15 18:46:22 +0000157 return product[0]
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000158
Guido van Rossum1bc535d2007-05-15 18:46:22 +0000159 def p_type_1(self, sum):
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000160 " type ::= sum "
Guido van Rossum1bc535d2007-05-15 18:46:22 +0000161 return Sum(sum[0])
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000162
Guido van Rossum1bc535d2007-05-15 18:46:22 +0000163 def p_type_2(self, info):
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000164 " type ::= sum Id ( fields ) "
Guido van Rossum1bc535d2007-05-15 18:46:22 +0000165 sum, id, _0, attributes, _1 = info
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000166 if id.value != "attributes":
167 raise ASDLSyntaxError(id.lineno,
168 msg="expected attributes, found %s" % id)
Martin v. Löwis49c5da12006-03-01 22:49:05 +0000169 if attributes:
170 attributes.reverse()
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000171 return Sum(sum, attributes)
172
Guido van Rossum1bc535d2007-05-15 18:46:22 +0000173 def p_product(self, info):
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000174 " product ::= ( fields ) "
Guido van Rossum1bc535d2007-05-15 18:46:22 +0000175 _0, fields, _1 = info
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000176 # XXX can't I just construct things in the right order?
Tim Peters536cf992005-12-25 23:18:31 +0000177 fields.reverse()
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000178 return Product(fields)
179
Guido van Rossum1bc535d2007-05-15 18:46:22 +0000180 def p_sum_0(self, constructor):
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000181 " sum ::= constructor """
Guido van Rossum1bc535d2007-05-15 18:46:22 +0000182 return [constructor[0]]
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000183
Guido van Rossum1bc535d2007-05-15 18:46:22 +0000184 def p_sum_1(self, ):
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000185 " sum ::= constructor | sum "
Guido van Rossum1bc535d2007-05-15 18:46:22 +0000186 constructor, _, sum = info
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000187 return [constructor] + sum
188
Guido van Rossum1bc535d2007-05-15 18:46:22 +0000189 def p_sum_2(self, info):
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000190 " sum ::= constructor | sum "
Guido van Rossum1bc535d2007-05-15 18:46:22 +0000191 constructor, _, sum = info
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000192 return [constructor] + sum
193
Guido van Rossum1bc535d2007-05-15 18:46:22 +0000194 def p_constructor_0(self, id):
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000195 " constructor ::= Id "
Guido van Rossum1bc535d2007-05-15 18:46:22 +0000196 return Constructor(id[0])
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000197
Guido van Rossum1bc535d2007-05-15 18:46:22 +0000198 def p_constructor_1(self, info):
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000199 " constructor ::= Id ( fields ) "
Guido van Rossum1bc535d2007-05-15 18:46:22 +0000200 id, _0, fields, _1 = info
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000201 # XXX can't I just construct things in the right order?
Tim Peters536cf992005-12-25 23:18:31 +0000202 fields.reverse()
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000203 return Constructor(id, fields)
204
Guido van Rossum1bc535d2007-05-15 18:46:22 +0000205 def p_fields_0(self, field):
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000206 " fields ::= field "
Guido van Rossum1bc535d2007-05-15 18:46:22 +0000207 return [field[0]]
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000208
Guido van Rossum1bc535d2007-05-15 18:46:22 +0000209 def p_fields_1(self, info):
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000210 " fields ::= field , fields "
Guido van Rossum1bc535d2007-05-15 18:46:22 +0000211 field, _, fields = info
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000212 return fields + [field]
213
Guido van Rossum1bc535d2007-05-15 18:46:22 +0000214 def p_field_0(self, type_):
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000215 " field ::= Id "
Guido van Rossum1bc535d2007-05-15 18:46:22 +0000216 return Field(type_[0])
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000217
Guido van Rossum1bc535d2007-05-15 18:46:22 +0000218 def p_field_1(self, info):
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000219 " field ::= Id Id "
Guido van Rossum1bc535d2007-05-15 18:46:22 +0000220 type, name = info
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000221 return Field(type, name)
222
Guido van Rossum1bc535d2007-05-15 18:46:22 +0000223 def p_field_2(self, info):
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000224 " field ::= Id * Id "
Guido van Rossum1bc535d2007-05-15 18:46:22 +0000225 type, _, name = info
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000226 return Field(type, name, seq=1)
227
Guido van Rossum1bc535d2007-05-15 18:46:22 +0000228 def p_field_3(self, info):
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000229 " field ::= Id ? Id "
Guido van Rossum1bc535d2007-05-15 18:46:22 +0000230 type, _, name = info
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000231 return Field(type, name, opt=1)
232
Guido van Rossum1bc535d2007-05-15 18:46:22 +0000233 def p_field_4(self, type_):
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000234 " field ::= Id * "
Guido van Rossum1bc535d2007-05-15 18:46:22 +0000235 return Field(type_[0], seq=1)
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000236
Guido van Rossum1bc535d2007-05-15 18:46:22 +0000237 def p_field_5(self, type_):
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000238 " field ::= Id ? "
Guido van Rossum1bc535d2007-05-15 18:46:22 +0000239 return Field(type[0], opt=1)
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000240
241builtin_types = ("identifier", "string", "int", "bool", "object")
242
243# below is a collection of classes to capture the AST of an AST :-)
244# not sure if any of the methods are useful yet, but I'm adding them
245# piecemeal as they seem helpful
246
247class AST:
248 pass # a marker class
249
250class Module(AST):
Martin v. Löwiseae93b72006-02-28 00:12:47 +0000251 def __init__(self, name, dfns, version):
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000252 self.name = name
253 self.dfns = dfns
Martin v. Löwiseae93b72006-02-28 00:12:47 +0000254 self.version = version
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000255 self.types = {} # maps type name to value (from dfns)
256 for type in dfns:
257 self.types[type.name.value] = type.value
258
259 def __repr__(self):
260 return "Module(%s, %s)" % (self.name, self.dfns)
261
262class Type(AST):
263 def __init__(self, name, value):
264 self.name = name
265 self.value = value
266
267 def __repr__(self):
268 return "Type(%s, %s)" % (self.name, self.value)
269
270class Constructor(AST):
271 def __init__(self, name, fields=None):
272 self.name = name
273 self.fields = fields or []
274
275 def __repr__(self):
276 return "Constructor(%s, %s)" % (self.name, self.fields)
277
278class Field(AST):
279 def __init__(self, type, name=None, seq=0, opt=0):
280 self.type = type
281 self.name = name
282 self.seq = seq
283 self.opt = opt
284
285 def __repr__(self):
286 if self.seq:
287 extra = ", seq=1"
288 elif self.opt:
289 extra = ", opt=1"
290 else:
291 extra = ""
292 if self.name is None:
293 return "Field(%s%s)" % (self.type, extra)
294 else:
295 return "Field(%s, %s%s)" % (self.type, self.name, extra)
296
297class Sum(AST):
298 def __init__(self, types, attributes=None):
299 self.types = types
300 self.attributes = attributes or []
301
302 def __repr__(self):
303 if self.attributes is None:
304 return "Sum(%s)" % self.types
305 else:
306 return "Sum(%s, %s)" % (self.types, self.attributes)
307
308class Product(AST):
309 def __init__(self, fields):
310 self.fields = fields
311
312 def __repr__(self):
313 return "Product(%s)" % self.fields
314
315class VisitorBase(object):
316
317 def __init__(self, skip=0):
318 self.cache = {}
319 self.skip = skip
320
321 def visit(self, object, *args):
322 meth = self._dispatch(object)
323 if meth is None:
324 return
325 try:
326 meth(object, *args)
Guido van Rossum805365e2007-05-07 22:24:25 +0000327 except Exception:
328 output("Error visiting", repr(object))
329 output(sys.exc_info()[1])
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000330 traceback.print_exc()
331 # XXX hack
332 if hasattr(self, 'file'):
333 self.file.flush()
334 os._exit(1)
335
336 def _dispatch(self, object):
337 assert isinstance(object, AST), repr(object)
338 klass = object.__class__
339 meth = self.cache.get(klass)
340 if meth is None:
341 methname = "visit" + klass.__name__
342 if self.skip:
343 meth = getattr(self, methname, None)
344 else:
345 meth = getattr(self, methname)
346 self.cache[klass] = meth
347 return meth
348
349class Check(VisitorBase):
350
351 def __init__(self):
352 super(Check, self).__init__(skip=1)
353 self.cons = {}
354 self.errors = 0
355 self.types = {}
356
357 def visitModule(self, mod):
358 for dfn in mod.dfns:
359 self.visit(dfn)
360
361 def visitType(self, type):
362 self.visit(type.value, str(type.name))
363
364 def visitSum(self, sum, name):
365 for t in sum.types:
366 self.visit(t, name)
367
368 def visitConstructor(self, cons, name):
369 key = str(cons.name)
370 conflict = self.cons.get(key)
371 if conflict is None:
372 self.cons[key] = name
373 else:
Guido van Rossum805365e2007-05-07 22:24:25 +0000374 output("Redefinition of constructor %s" % key)
375 output("Defined in %s and %s" % (conflict, name))
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000376 self.errors += 1
377 for f in cons.fields:
378 self.visit(f, key)
379
380 def visitField(self, field, name):
381 key = str(field.type)
382 l = self.types.setdefault(key, [])
383 l.append(name)
384
385 def visitProduct(self, prod, name):
386 for f in prod.fields:
387 self.visit(f, name)
388
389def check(mod):
390 v = Check()
391 v.visit(mod)
392
393 for t in v.types:
Fred Drake34e4f522006-12-29 04:42:48 +0000394 if t not in mod.types and not t in builtin_types:
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000395 v.errors += 1
396 uses = ", ".join(v.types[t])
Guido van Rossum805365e2007-05-07 22:24:25 +0000397 output("Undefined type %s, used in %s" % (t, uses))
Tim Peters536cf992005-12-25 23:18:31 +0000398
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000399 return not v.errors
400
401def parse(file):
402 scanner = ASDLScanner()
403 parser = ASDLParser()
404
405 buf = open(file).read()
406 tokens = scanner.tokenize(buf)
407 try:
408 return parser.parse(tokens)
Guido van Rossum805365e2007-05-07 22:24:25 +0000409 except ASDLSyntaxError:
410 output(sys.exc_info()[1])
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000411 lines = buf.split("\n")
Guido van Rossum805365e2007-05-07 22:24:25 +0000412 output(lines[err.lineno - 1]) # lines starts at 0, files at 1
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000413
414if __name__ == "__main__":
415 import glob
416 import sys
417
418 if len(sys.argv) > 1:
419 files = sys.argv[1:]
420 else:
421 testdir = "tests"
422 files = glob.glob(testdir + "/*.asdl")
Tim Peters536cf992005-12-25 23:18:31 +0000423
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000424 for file in files:
Guido van Rossum805365e2007-05-07 22:24:25 +0000425 output(file)
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000426 mod = parse(file)
Guido van Rossum805365e2007-05-07 22:24:25 +0000427 output("module", mod.name)
428 output(len(mod.dfns), "definitions")
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000429 if not check(mod):
Guido van Rossum805365e2007-05-07 22:24:25 +0000430 output("Check failed")
Jeremy Hylton3e0055f2005-10-20 19:59:25 +0000431 else:
432 for dfn in mod.dfns:
Guido van Rossum805365e2007-05-07 22:24:25 +0000433 output(dfn.type)