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
21 # spark seems to dispatch in the parser based on a token's
23 def __init__(self
, type, lineno
):
34 def __init__(self
, value
, lineno
):
43 def __init__(self
, value
, lineno
):
48 class ASDLSyntaxError
:
50 def __init__(self
, lineno
, token
=None, msg
=None):
57 return "Error at '%s', line %d" % (self
.token
, self
.lineno
)
59 return "%s, line %d" % (self
.msg
, self
.lineno
)
61 class ASDLScanner(spark
.GenericScanner
, object):
63 def tokenize(self
, input):
66 super(ASDLScanner
, self
).tokenize(input)
71 # XXX doesn't distinguish upper vs. lower, which is
72 # significant for ASDL.
73 self
.rv
.append(Id(s
, self
.lineno
))
75 def t_string(self
, s
):
77 self
.rv
.append(String(s
, self
.lineno
))
79 def t_xxx(self
, s
): # not sure what this production means
81 self
.rv
.append(Token(s
, self
.lineno
))
83 def t_punctuation(self
, s
):
84 r
"[\{\}\*\=\|\(\)\,\?\:]"
85 self
.rv
.append(Token(s
, self
.lineno
))
87 def t_comment(self
, s
):
91 def t_newline(self
, s
):
95 def t_whitespace(self
, s
):
99 def t_default(self
, s
):
101 raise ValueError, "unmatched input: %s" % `s`
103 class ASDLParser(spark
.GenericParser
, object):
105 super(ASDLParser
, self
).__init
__("module")
107 def typestring(self
, tok
):
110 def error(self
, tok
):
111 raise ASDLSyntaxError(tok
.lineno
, tok
)
113 def p_module_0(self
, (module
, name
, version
, _0
, _1
)):
114 " module ::= Id Id version { } "
115 if module
.value
!= "module":
116 raise ASDLSyntaxError(module
.lineno
,
117 msg
="expected 'module', found %s" % module
)
118 return Module(name
, None, version
)
120 def p_module(self
, (module
, name
, version
, _0
, definitions
, _1
)):
121 " module ::= Id Id version { definitions } "
122 if module
.value
!= "module":
123 raise ASDLSyntaxError(module
.lineno
,
124 msg
="expected 'module', found %s" % module
)
125 return Module(name
, definitions
, version
)
127 def p_version(self
, (version
, V
)):
128 "version ::= Id String"
129 if version
.value
!= "version":
130 raise ASDLSyntaxError(version
.lineno
,
131 msg
="expected 'version', found %" % version
)
134 def p_definition_0(self
, (definition
,)):
135 " definitions ::= definition "
138 def p_definition_1(self
, (definitions
, definition
)):
139 " definitions ::= definition definitions "
140 return definitions
+ definition
142 def p_definition(self
, (id, _
, type)):
143 " definition ::= Id = type "
144 return [Type(id, type)]
146 def p_type_0(self
, (product
,)):
150 def p_type_1(self
, (sum,)):
154 def p_type_2(self
, (sum, id, _0
, attributes
, _1
)):
155 " type ::= sum Id ( fields ) "
156 if id.value
!= "attributes":
157 raise ASDLSyntaxError(id.lineno
,
158 msg
="expected attributes, found %s" % id)
161 return Sum(sum, attributes
)
163 def p_product(self
, (_0
, fields
, _1
)):
164 " product ::= ( fields ) "
165 # XXX can't I just construct things in the right order?
167 return Product(fields
)
169 def p_sum_0(self
, (constructor
,)):
170 " sum ::= constructor "
173 def p_sum_1(self
, (constructor
, _
, sum)):
174 " sum ::= constructor | sum "
175 return [constructor
] + sum
177 def p_sum_2(self
, (constructor
, _
, sum)):
178 " sum ::= constructor | sum "
179 return [constructor
] + sum
181 def p_constructor_0(self
, (id,)):
182 " constructor ::= Id "
183 return Constructor(id)
185 def p_constructor_1(self
, (id, _0
, fields
, _1
)):
186 " constructor ::= Id ( fields ) "
187 # XXX can't I just construct things in the right order?
189 return Constructor(id, fields
)
191 def p_fields_0(self
, (field
,)):
195 def p_fields_1(self
, (field
, _
, fields
)):
196 " fields ::= field , fields "
197 return fields
+ [field
]
199 def p_field_0(self
, (type,)):
203 def p_field_1(self
, (type, name
)):
205 return Field(type, name
)
207 def p_field_2(self
, (type, _
, name
)):
208 " field ::= Id * Id "
209 return Field(type, name
, seq
=1)
211 def p_field_3(self
, (type, _
, name
)):
212 " field ::= Id ? Id "
213 return Field(type, name
, opt
=1)
215 def p_field_4(self
, (type, _
)):
217 return Field(type, seq
=1)
219 def p_field_5(self
, (type, _
)):
221 return Field(type, opt
=1)
223 builtin_types
= ("identifier", "string", "int", "bool", "object")
225 # below is a collection of classes to capture the AST of an AST :-)
226 # not sure if any of the methods are useful yet, but I'm adding them
227 # piecemeal as they seem helpful
230 pass # a marker class
233 def __init__(self
, name
, dfns
, version
):
236 self
.version
= version
237 self
.types
= {} # maps type name to value (from dfns)
239 self
.types
[type.name
.value
] = type.value
242 return "Module(%s, %s)" % (self
.name
, self
.dfns
)
245 def __init__(self
, name
, value
):
250 return "Type(%s, %s)" % (self
.name
, self
.value
)
252 class Constructor(AST
):
253 def __init__(self
, name
, fields
=None):
255 self
.fields
= fields
or []
258 return "Constructor(%s, %s)" % (self
.name
, self
.fields
)
261 def __init__(self
, type, name
=None, seq
=0, opt
=0):
274 if self
.name
is None:
275 return "Field(%s%s)" % (self
.type, extra
)
277 return "Field(%s, %s%s)" % (self
.type, self
.name
, extra
)
280 def __init__(self
, types
, attributes
=None):
282 self
.attributes
= attributes
or []
285 if self
.attributes
is None:
286 return "Sum(%s)" % self
.types
288 return "Sum(%s, %s)" % (self
.types
, self
.attributes
)
291 def __init__(self
, fields
):
295 return "Product(%s)" % self
.fields
297 class VisitorBase(object):
299 def __init__(self
, skip
=0):
303 def visit(self
, object, *args
):
304 meth
= self
._dispatch
(object)
309 except Exception, err
:
310 print "Error visiting", repr(object)
312 traceback
.print_exc()
314 if hasattr(self
, 'file'):
318 def _dispatch(self
, object):
319 assert isinstance(object, AST
), repr(object)
320 klass
= object.__class
__
321 meth
= self
.cache
.get(klass
)
323 methname
= "visit" + klass
.__name
__
325 meth
= getattr(self
, methname
, None)
327 meth
= getattr(self
, methname
)
328 self
.cache
[klass
] = meth
331 class Check(VisitorBase
):
334 super(Check
, self
).__init
__(skip
=1)
339 def visitModule(self
, mod
):
343 def visitType(self
, type):
344 self
.visit(type.value
, str(type.name
))
346 def visitSum(self
, sum, name
):
350 def visitConstructor(self
, cons
, name
):
352 conflict
= self
.cons
.get(key
)
354 self
.cons
[key
] = name
356 print "Redefinition of constructor %s" % key
357 print "Defined in %s and %s" % (conflict
, name
)
359 for f
in cons
.fields
:
362 def visitField(self
, field
, name
):
363 key
= str(field
.type)
364 l
= self
.types
.setdefault(key
, [])
367 def visitProduct(self
, prod
, name
):
368 for f
in prod
.fields
:
376 if not mod
.types
.has_key(t
) and not t
in builtin_types
:
378 uses
= ", ".join(v
.types
[t
])
379 print "Undefined type %s, used in %s" % (t
, uses
)
384 scanner
= ASDLScanner()
385 parser
= ASDLParser()
387 buf
= open(file).read()
388 tokens
= scanner
.tokenize(buf
)
390 return parser
.parse(tokens
)
391 except ASDLSyntaxError
, err
:
393 lines
= buf
.split("\n")
394 print lines
[err
.lineno
- 1] # lines starts at 0, files at 1
396 if __name__
== "__main__":
400 if len(sys
.argv
) > 1:
404 files
= glob
.glob(testdir
+ "/*.asdl")
409 print "module", mod
.name
410 print len(mod
.dfns
), "definitions"