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
19 # spark seems to dispatch in the parser based on a token's
21 def __init__(self
, type, lineno
):
32 def __init__(self
, value
, lineno
):
41 def __init__(self
, value
, lineno
):
46 class ASDLSyntaxError(Exception):
48 def __init__(self
, lineno
, token
=None, msg
=None):
55 return "Error at '%s', line %d" % (self
.token
, self
.lineno
)
57 return "%s, line %d" % (self
.msg
, self
.lineno
)
59 class ASDLScanner(spark
.GenericScanner
, object):
61 def tokenize(self
, input):
64 super(ASDLScanner
, self
).tokenize(input)
69 # XXX doesn't distinguish upper vs. lower, which is
70 # significant for ASDL.
71 self
.rv
.append(Id(s
, self
.lineno
))
73 def t_string(self
, s
):
75 self
.rv
.append(String(s
, self
.lineno
))
77 def t_xxx(self
, s
): # not sure what this production means
79 self
.rv
.append(Token(s
, self
.lineno
))
81 def t_punctuation(self
, s
):
82 r
"[\{\}\*\=\|\(\)\,\?\:]"
83 self
.rv
.append(Token(s
, self
.lineno
))
85 def t_comment(self
, s
):
89 def t_newline(self
, s
):
93 def t_whitespace(self
, s
):
97 def t_default(self
, s
):
99 raise ValueError, "unmatched input: %s" % `s`
101 class ASDLParser(spark
.GenericParser
, object):
103 super(ASDLParser
, self
).__init
__("module")
105 def typestring(self
, tok
):
108 def error(self
, tok
):
109 raise ASDLSyntaxError(tok
.lineno
, tok
)
111 def p_module_0(self
, (module
, name
, version
, _0
, _1
)):
112 " module ::= Id Id version { } "
113 if module
.value
!= "module":
114 raise ASDLSyntaxError(module
.lineno
,
115 msg
="expected 'module', found %s" % module
)
116 return Module(name
, None, version
)
118 def p_module(self
, (module
, name
, version
, _0
, definitions
, _1
)):
119 " module ::= Id Id version { definitions } "
120 if module
.value
!= "module":
121 raise ASDLSyntaxError(module
.lineno
,
122 msg
="expected 'module', found %s" % module
)
123 return Module(name
, definitions
, version
)
125 def p_version(self
, (version
, V
)):
126 "version ::= Id String"
127 if version
.value
!= "version":
128 raise ASDLSyntaxError(version
.lineno
,
129 msg
="expected 'version', found %" % version
)
132 def p_definition_0(self
, (definition
,)):
133 " definitions ::= definition "
136 def p_definition_1(self
, (definitions
, definition
)):
137 " definitions ::= definition definitions "
138 return definitions
+ definition
140 def p_definition(self
, (id, _
, type)):
141 " definition ::= Id = type "
142 return [Type(id, type)]
144 def p_type_0(self
, (product
,)):
148 def p_type_1(self
, (sum,)):
152 def p_type_2(self
, (sum, id, _0
, attributes
, _1
)):
153 " type ::= sum Id ( fields ) "
154 if id.value
!= "attributes":
155 raise ASDLSyntaxError(id.lineno
,
156 msg
="expected attributes, found %s" % id)
159 return Sum(sum, attributes
)
161 def p_product(self
, (_0
, fields
, _1
)):
162 " product ::= ( fields ) "
163 # XXX can't I just construct things in the right order?
165 return Product(fields
)
167 def p_sum_0(self
, (constructor
,)):
168 " sum ::= constructor "
171 def p_sum_1(self
, (constructor
, _
, sum)):
172 " sum ::= constructor | sum "
173 return [constructor
] + sum
175 def p_sum_2(self
, (constructor
, _
, sum)):
176 " sum ::= constructor | sum "
177 return [constructor
] + sum
179 def p_constructor_0(self
, (id,)):
180 " constructor ::= Id "
181 return Constructor(id)
183 def p_constructor_1(self
, (id, _0
, fields
, _1
)):
184 " constructor ::= Id ( fields ) "
185 # XXX can't I just construct things in the right order?
187 return Constructor(id, fields
)
189 def p_fields_0(self
, (field
,)):
193 def p_fields_1(self
, (field
, _
, fields
)):
194 " fields ::= field , fields "
195 return fields
+ [field
]
197 def p_field_0(self
, (type,)):
201 def p_field_1(self
, (type, name
)):
203 return Field(type, name
)
205 def p_field_2(self
, (type, _
, name
)):
206 " field ::= Id * Id "
207 return Field(type, name
, seq
=True)
209 def p_field_3(self
, (type, _
, name
)):
210 " field ::= Id ? Id "
211 return Field(type, name
, opt
=True)
213 def p_field_4(self
, (type, _
)):
215 return Field(type, seq
=True)
217 def p_field_5(self
, (type, _
)):
219 return Field(type, opt
=True)
221 builtin_types
= ("identifier", "string", "int", "bool", "object")
223 # below is a collection of classes to capture the AST of an AST :-)
224 # not sure if any of the methods are useful yet, but I'm adding them
225 # piecemeal as they seem helpful
228 pass # a marker class
231 def __init__(self
, name
, dfns
, version
):
234 self
.version
= version
235 self
.types
= {} # maps type name to value (from dfns)
237 self
.types
[type.name
.value
] = type.value
240 return "Module(%s, %s)" % (self
.name
, self
.dfns
)
243 def __init__(self
, name
, value
):
248 return "Type(%s, %s)" % (self
.name
, self
.value
)
250 class Constructor(AST
):
251 def __init__(self
, name
, fields
=None):
253 self
.fields
= fields
or []
256 return "Constructor(%s, %s)" % (self
.name
, self
.fields
)
259 def __init__(self
, type, name
=None, seq
=False, opt
=False):
272 if self
.name
is None:
273 return "Field(%s%s)" % (self
.type, extra
)
275 return "Field(%s, %s%s)" % (self
.type, self
.name
, extra
)
278 def __init__(self
, types
, attributes
=None):
280 self
.attributes
= attributes
or []
283 if self
.attributes
is None:
284 return "Sum(%s)" % self
.types
286 return "Sum(%s, %s)" % (self
.types
, self
.attributes
)
289 def __init__(self
, fields
):
293 return "Product(%s)" % self
.fields
295 class VisitorBase(object):
297 def __init__(self
, skip
=False):
301 def visit(self
, object, *args
):
302 meth
= self
._dispatch
(object)
307 except Exception, err
:
308 print "Error visiting", repr(object)
310 traceback
.print_exc()
312 if hasattr(self
, 'file'):
316 def _dispatch(self
, object):
317 assert isinstance(object, AST
), repr(object)
318 klass
= object.__class
__
319 meth
= self
.cache
.get(klass
)
321 methname
= "visit" + klass
.__name
__
323 meth
= getattr(self
, methname
, None)
325 meth
= getattr(self
, methname
)
326 self
.cache
[klass
] = meth
329 class Check(VisitorBase
):
332 super(Check
, self
).__init
__(skip
=True)
337 def visitModule(self
, mod
):
341 def visitType(self
, type):
342 self
.visit(type.value
, str(type.name
))
344 def visitSum(self
, sum, name
):
348 def visitConstructor(self
, cons
, name
):
350 conflict
= self
.cons
.get(key
)
352 self
.cons
[key
] = name
354 print "Redefinition of constructor %s" % key
355 print "Defined in %s and %s" % (conflict
, name
)
357 for f
in cons
.fields
:
360 def visitField(self
, field
, name
):
361 key
= str(field
.type)
362 l
= self
.types
.setdefault(key
, [])
365 def visitProduct(self
, prod
, name
):
366 for f
in prod
.fields
:
374 if t
not in mod
.types
and not t
in builtin_types
:
376 uses
= ", ".join(v
.types
[t
])
377 print "Undefined type %s, used in %s" % (t
, uses
)
382 scanner
= ASDLScanner()
383 parser
= ASDLParser()
385 buf
= open(file).read()
386 tokens
= scanner
.tokenize(buf
)
388 return parser
.parse(tokens
)
389 except ASDLSyntaxError
, err
:
391 lines
= buf
.split("\n")
392 print lines
[err
.lineno
- 1] # lines starts at 0, files at 1
394 if __name__
== "__main__":
398 if len(sys
.argv
) > 1:
402 files
= glob
.glob(testdir
+ "/*.asdl")
407 print "module", mod
.name
408 print len(mod
.dfns
), "definitions"