1 """An implementation of the Zephyr Abstract Syntax Definition Language.
3 See http://asdl.sourceforge.net/ and
4 http://www.cs.princeton.edu/~danwang/Papers/dsl97/dsl97-abstract.html.
6 Only supports top level module decl, not view. I'm guessing that view
7 is intended to support the browser and I'm not interested in the
10 Changes for Python: Add support for module versions
20 sys
.stdout
.write(string
+ "\n")
24 # spark seems to dispatch in the parser based on a token's
26 def __init__(self
, type, lineno
):
37 def __init__(self
, value
, lineno
):
46 def __init__(self
, value
, lineno
):
51 class ASDLSyntaxError(Exception):
53 def __init__(self
, lineno
, token
=None, msg
=None):
60 return "Error at '%s', line %d" % (self
.token
, self
.lineno
)
62 return "%s, line %d" % (self
.msg
, self
.lineno
)
64 class ASDLScanner(spark
.GenericScanner
, object):
66 def tokenize(self
, input):
69 super(ASDLScanner
, self
).tokenize(input)
74 # XXX doesn't distinguish upper vs. lower, which is
75 # significant for ASDL.
76 self
.rv
.append(Id(s
, self
.lineno
))
78 def t_string(self
, s
):
80 self
.rv
.append(String(s
, self
.lineno
))
82 def t_xxx(self
, s
): # not sure what this production means
84 self
.rv
.append(Token(s
, self
.lineno
))
86 def t_punctuation(self
, s
):
87 r
"[\{\}\*\=\|\(\)\,\?\:]"
88 self
.rv
.append(Token(s
, self
.lineno
))
90 def t_comment(self
, s
):
94 def t_newline(self
, s
):
98 def t_whitespace(self
, s
):
102 def t_default(self
, s
):
104 raise ValueError("unmatched input: %r" % s
)
106 class ASDLParser(spark
.GenericParser
, object):
108 super(ASDLParser
, self
).__init
__("module")
110 def typestring(self
, tok
):
113 def error(self
, tok
):
114 raise ASDLSyntaxError(tok
.lineno
, tok
)
116 def p_module_0(self
, info
):
117 " module ::= Id Id version { } "
118 module
, name
, version
, _0
, _1
= info
119 if module
.value
!= "module":
120 raise ASDLSyntaxError(module
.lineno
,
121 msg
="expected 'module', found %s" % module
)
122 return Module(name
, None, version
)
124 def p_module(self
, info
):
125 " module ::= Id Id version { definitions } "
126 module
, name
, version
, _0
, definitions
, _1
= info
127 if module
.value
!= "module":
128 raise ASDLSyntaxError(module
.lineno
,
129 msg
="expected 'module', found %s" % module
)
130 return Module(name
, definitions
, version
)
132 def p_version(self
, info
):
133 "version ::= Id String"
135 if version
.value
!= "version":
136 raise ASDLSyntaxError(version
.lineno
,
137 msg
="expected 'version', found %" % version
)
140 def p_definition_0(self
, definition
):
141 " definitions ::= definition "
144 def p_definition_1(self
, definitions
):
145 " definitions ::= definition definitions "
146 return definitions
[0] + definitions
[1]
148 def p_definition(self
, info
):
149 " definition ::= Id = type "
151 return [Type(id, type)]
153 def p_type_0(self
, product
):
157 def p_type_1(self
, sum):
161 def p_type_2(self
, info
):
162 " type ::= sum Id ( fields ) "
163 sum, id, _0
, attributes
, _1
= info
164 if id.value
!= "attributes":
165 raise ASDLSyntaxError(id.lineno
,
166 msg
="expected attributes, found %s" % id)
169 return Sum(sum, attributes
)
171 def p_product(self
, info
):
172 " product ::= ( fields ) "
173 _0
, fields
, _1
= info
174 # XXX can't I just construct things in the right order?
176 return Product(fields
)
178 def p_sum_0(self
, constructor
):
179 " sum ::= constructor "
180 return [constructor
[0]]
182 def p_sum_1(self
, info
):
183 " sum ::= constructor | sum "
184 constructor
, _
, sum = info
185 return [constructor
] + sum
187 def p_sum_2(self
, info
):
188 " sum ::= constructor | sum "
189 constructor
, _
, sum = info
190 return [constructor
] + sum
192 def p_constructor_0(self
, id):
193 " constructor ::= Id "
194 return Constructor(id[0])
196 def p_constructor_1(self
, info
):
197 " constructor ::= Id ( fields ) "
198 id, _0
, fields
, _1
= info
199 # XXX can't I just construct things in the right order?
201 return Constructor(id, fields
)
203 def p_fields_0(self
, field
):
207 def p_fields_1(self
, info
):
208 " fields ::= field , fields "
209 field
, _
, fields
= info
210 return fields
+ [field
]
212 def p_field_0(self
, type_
):
214 return Field(type_
[0])
216 def p_field_1(self
, info
):
219 return Field(type, name
)
221 def p_field_2(self
, info
):
222 " field ::= Id * Id "
224 return Field(type, name
, seq
=True)
226 def p_field_3(self
, info
):
227 " field ::= Id ? Id "
229 return Field(type, name
, opt
=True)
231 def p_field_4(self
, type_
):
233 return Field(type_
[0], seq
=True)
235 def p_field_5(self
, type_
):
237 return Field(type[0], opt
=True)
239 builtin_types
= ("identifier", "string", "int", "bool", "object")
241 # below is a collection of classes to capture the AST of an AST :-)
242 # not sure if any of the methods are useful yet, but I'm adding them
243 # piecemeal as they seem helpful
246 pass # a marker class
249 def __init__(self
, name
, dfns
, version
):
252 self
.version
= version
253 self
.types
= {} # maps type name to value (from dfns)
255 self
.types
[type.name
.value
] = type.value
258 return "Module(%s, %s)" % (self
.name
, self
.dfns
)
261 def __init__(self
, name
, value
):
266 return "Type(%s, %s)" % (self
.name
, self
.value
)
268 class Constructor(AST
):
269 def __init__(self
, name
, fields
=None):
271 self
.fields
= fields
or []
274 return "Constructor(%s, %s)" % (self
.name
, self
.fields
)
277 def __init__(self
, type, name
=None, seq
=False, opt
=False):
290 if self
.name
is None:
291 return "Field(%s%s)" % (self
.type, extra
)
293 return "Field(%s, %s%s)" % (self
.type, self
.name
, extra
)
296 def __init__(self
, types
, attributes
=None):
298 self
.attributes
= attributes
or []
301 if self
.attributes
is None:
302 return "Sum(%s)" % self
.types
304 return "Sum(%s, %s)" % (self
.types
, self
.attributes
)
307 def __init__(self
, fields
):
311 return "Product(%s)" % self
.fields
313 class VisitorBase(object):
315 def __init__(self
, skip
=False):
319 def visit(self
, object, *args
):
320 meth
= self
._dispatch
(object)
326 output("Error visiting", repr(object))
327 output(sys
.exc_info()[1])
328 traceback
.print_exc()
330 if hasattr(self
, 'file'):
334 def _dispatch(self
, object):
335 assert isinstance(object, AST
), repr(object)
336 klass
= object.__class
__
337 meth
= self
.cache
.get(klass
)
339 methname
= "visit" + klass
.__name
__
341 meth
= getattr(self
, methname
, None)
343 meth
= getattr(self
, methname
)
344 self
.cache
[klass
] = meth
347 class Check(VisitorBase
):
350 super(Check
, self
).__init
__(skip
=True)
355 def visitModule(self
, mod
):
359 def visitType(self
, type):
360 self
.visit(type.value
, str(type.name
))
362 def visitSum(self
, sum, name
):
366 def visitConstructor(self
, cons
, name
):
368 conflict
= self
.cons
.get(key
)
370 self
.cons
[key
] = name
372 output("Redefinition of constructor %s" % key
)
373 output("Defined in %s and %s" % (conflict
, name
))
375 for f
in cons
.fields
:
378 def visitField(self
, field
, name
):
379 key
= str(field
.type)
380 l
= self
.types
.setdefault(key
, [])
383 def visitProduct(self
, prod
, name
):
384 for f
in prod
.fields
:
392 if t
not in mod
.types
and not t
in builtin_types
:
394 uses
= ", ".join(v
.types
[t
])
395 output("Undefined type %s, used in %s" % (t
, uses
))
400 scanner
= ASDLScanner()
401 parser
= ASDLParser()
403 buf
= open(file).read()
404 tokens
= scanner
.tokenize(buf
)
406 return parser
.parse(tokens
)
407 except ASDLSyntaxError
:
408 err
= sys
.exc_info()[1]
410 lines
= buf
.split("\n")
411 output(lines
[err
.lineno
- 1]) # lines starts at 0, files at 1
413 if __name__
== "__main__":
417 if len(sys
.argv
) > 1:
421 files
= glob
.glob(testdir
+ "/*.asdl")
428 output("module", mod
.name
)
429 output(len(mod
.dfns
), "definitions")
431 output("Check failed")