| 
									
										
										
										
											2014-05-09 17:58:22 -07:00
										 |  |  | #------------------------------------------------------------------------------- | 
					
						
							|  |  |  | # Parser for ASDL [1] definition files. Reads in an ASDL description and parses | 
					
						
							|  |  |  | # it into an AST that describes it. | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # The EBNF we're parsing here: Figure 1 of the paper [1]. Extended to support | 
					
						
							|  |  |  | # modules and attributes after a product. Words starting with Capital letters | 
					
						
							|  |  |  | # are terminals. Literal tokens are in "double quotes". Others are | 
					
						
							|  |  |  | # non-terminals. Id is either TokenId or ConstructorId. | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # module        ::= "module" Id "{" [definitions] "}" | 
					
						
							|  |  |  | # definitions   ::= { TypeId "=" type } | 
					
						
							|  |  |  | # type          ::= product | sum | 
					
						
							|  |  |  | # product       ::= fields ["attributes" fields] | 
					
						
							|  |  |  | # fields        ::= "(" { field, "," } field ")" | 
					
						
							|  |  |  | # field         ::= TypeId ["?" | "*"] [Id] | 
					
						
							|  |  |  | # sum           ::= constructor { "|" constructor } ["attributes" fields] | 
					
						
							|  |  |  | # constructor   ::= ConstructorId [fields] | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # [1] "The Zephyr Abstract Syntax Description Language" by Wang, et. al. See | 
					
						
							|  |  |  | #     http://asdl.sourceforge.net/ | 
					
						
							|  |  |  | #------------------------------------------------------------------------------- | 
					
						
							|  |  |  | from collections import namedtuple | 
					
						
							|  |  |  | import re | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | __all__ = [ | 
					
						
							|  |  |  |     'builtin_types', 'parse', 'AST', 'Module', 'Type', 'Constructor', | 
					
						
							|  |  |  |     'Field', 'Sum', 'Product', 'VisitorBase', 'Check', 'check'] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # The following classes define nodes into which the ASDL description is parsed. | 
					
						
							|  |  |  | # Note: this is a "meta-AST". ASDL files (such as Python.asdl) describe the AST | 
					
						
							|  |  |  | # structure used by a programming language. But ASDL files themselves need to be | 
					
						
							|  |  |  | # parsed. This module parses ASDL files and uses a simple AST to represent them. | 
					
						
							|  |  |  | # See the EBNF at the top of the file to understand the logical connection | 
					
						
							|  |  |  | # between the various node types. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-11-09 15:56:33 -08:00
										 |  |  | builtin_types = {'identifier', 'string', 'bytes', 'int', 'object', 'singleton'} | 
					
						
							| 
									
										
										
										
											2014-05-09 17:58:22 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | class AST: | 
					
						
							| 
									
										
										
										
											2005-10-20 19:59:25 +00:00
										 |  |  |     def __repr__(self): | 
					
						
							| 
									
										
										
										
											2014-05-09 17:58:22 -07:00
										 |  |  |         raise NotImplementedError | 
					
						
							| 
									
										
										
										
											2005-10-20 19:59:25 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | class Module(AST): | 
					
						
							| 
									
										
										
										
											2011-03-12 18:28:16 -06:00
										 |  |  |     def __init__(self, name, dfns): | 
					
						
							| 
									
										
										
										
											2005-10-20 19:59:25 +00:00
										 |  |  |         self.name = name | 
					
						
							|  |  |  |         self.dfns = dfns | 
					
						
							| 
									
										
										
										
											2014-05-09 17:58:22 -07:00
										 |  |  |         self.types = {type.name: type.value for type in dfns} | 
					
						
							| 
									
										
										
										
											2005-10-20 19:59:25 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def __repr__(self): | 
					
						
							| 
									
										
										
										
											2014-05-09 17:58:22 -07:00
										 |  |  |         return 'Module({0.name}, {0.dfns})'.format(self) | 
					
						
							| 
									
										
										
										
											2005-10-20 19:59:25 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | class Type(AST): | 
					
						
							|  |  |  |     def __init__(self, name, value): | 
					
						
							|  |  |  |         self.name = name | 
					
						
							|  |  |  |         self.value = value | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __repr__(self): | 
					
						
							| 
									
										
										
										
											2014-05-09 17:58:22 -07:00
										 |  |  |         return 'Type({0.name}, {0.value})'.format(self) | 
					
						
							| 
									
										
										
										
											2005-10-20 19:59:25 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | class Constructor(AST): | 
					
						
							|  |  |  |     def __init__(self, name, fields=None): | 
					
						
							|  |  |  |         self.name = name | 
					
						
							|  |  |  |         self.fields = fields or [] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __repr__(self): | 
					
						
							| 
									
										
										
										
											2014-05-09 17:58:22 -07:00
										 |  |  |         return 'Constructor({0.name}, {0.fields})'.format(self) | 
					
						
							| 
									
										
										
										
											2005-10-20 19:59:25 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | class Field(AST): | 
					
						
							| 
									
										
											  
											
												Merged revisions 73196,73278-73280,73299,73308,73312-73313,73317-73318,73321,73324,73331,73335,73340,73363 via svnmerge from
svn+ssh://pythondev@svn.python.org/python/trunk
........
  r73196 | benjamin.peterson | 2009-06-03 20:40:29 -0500 (Wed, 03 Jun 2009) | 1 line
  use the offical api
........
  r73278 | benjamin.peterson | 2009-06-07 17:33:11 -0500 (Sun, 07 Jun 2009) | 1 line
  inherit from object
........
  r73279 | benjamin.peterson | 2009-06-07 17:35:00 -0500 (Sun, 07 Jun 2009) | 1 line
  always inherit from an appropiate base class
........
  r73280 | benjamin.peterson | 2009-06-07 17:54:35 -0500 (Sun, 07 Jun 2009) | 1 line
  use booleans for flags
........
  r73299 | georg.brandl | 2009-06-08 13:41:36 -0500 (Mon, 08 Jun 2009) | 1 line
  Typo fix.
........
  r73308 | benjamin.peterson | 2009-06-08 17:18:32 -0500 (Mon, 08 Jun 2009) | 1 line
  remove useless assertion
........
  r73312 | benjamin.peterson | 2009-06-08 18:44:13 -0500 (Mon, 08 Jun 2009) | 1 line
  remove error checks already done in set_context()
........
  r73313 | r.david.murray | 2009-06-08 19:44:22 -0500 (Mon, 08 Jun 2009) | 4 lines
  Issue 2947: document how return code handling translates from
  os.popen to subprocess.  Also fixes reference link in the
  os.spawn documentation.
........
  r73317 | benjamin.peterson | 2009-06-09 12:24:26 -0500 (Tue, 09 Jun 2009) | 1 line
  make ast.c depend on the grammar
........
  r73318 | benjamin.peterson | 2009-06-09 12:29:51 -0500 (Tue, 09 Jun 2009) | 1 line
  explain why keyword names are not just NAME
........
  r73321 | benjamin.peterson | 2009-06-09 16:13:43 -0500 (Tue, 09 Jun 2009) | 1 line
  update symbol.py from with statement changes
........
  r73324 | amaury.forgeotdarc | 2009-06-09 17:53:16 -0500 (Tue, 09 Jun 2009) | 2 lines
  Avoid invoking the parser/compiler just to test the presence of a function.
........
  r73331 | benjamin.peterson | 2009-06-10 08:45:31 -0500 (Wed, 10 Jun 2009) | 1 line
  fix spelling
........
  r73335 | raymond.hettinger | 2009-06-10 11:15:40 -0500 (Wed, 10 Jun 2009) | 1 line
  Fix signed/unsigned compiler warning.
........
  r73340 | amaury.forgeotdarc | 2009-06-10 15:30:19 -0500 (Wed, 10 Jun 2009) | 2 lines
  Fix a typo spotted by Nick Coghlan.
........
  r73363 | benjamin.peterson | 2009-06-11 12:51:17 -0500 (Thu, 11 Jun 2009) | 1 line
  use multi-with syntax
........
											
										 
											2009-06-11 22:54:11 +00:00
										 |  |  |     def __init__(self, type, name=None, seq=False, opt=False): | 
					
						
							| 
									
										
										
										
											2005-10-20 19:59:25 +00:00
										 |  |  |         self.type = type | 
					
						
							|  |  |  |         self.name = name | 
					
						
							|  |  |  |         self.seq = seq | 
					
						
							|  |  |  |         self.opt = opt | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __repr__(self): | 
					
						
							|  |  |  |         if self.seq: | 
					
						
							| 
									
										
											  
											
												Merged revisions 73196,73278-73280,73299,73308,73312-73313,73317-73318,73321,73324,73331,73335,73340,73363 via svnmerge from
svn+ssh://pythondev@svn.python.org/python/trunk
........
  r73196 | benjamin.peterson | 2009-06-03 20:40:29 -0500 (Wed, 03 Jun 2009) | 1 line
  use the offical api
........
  r73278 | benjamin.peterson | 2009-06-07 17:33:11 -0500 (Sun, 07 Jun 2009) | 1 line
  inherit from object
........
  r73279 | benjamin.peterson | 2009-06-07 17:35:00 -0500 (Sun, 07 Jun 2009) | 1 line
  always inherit from an appropiate base class
........
  r73280 | benjamin.peterson | 2009-06-07 17:54:35 -0500 (Sun, 07 Jun 2009) | 1 line
  use booleans for flags
........
  r73299 | georg.brandl | 2009-06-08 13:41:36 -0500 (Mon, 08 Jun 2009) | 1 line
  Typo fix.
........
  r73308 | benjamin.peterson | 2009-06-08 17:18:32 -0500 (Mon, 08 Jun 2009) | 1 line
  remove useless assertion
........
  r73312 | benjamin.peterson | 2009-06-08 18:44:13 -0500 (Mon, 08 Jun 2009) | 1 line
  remove error checks already done in set_context()
........
  r73313 | r.david.murray | 2009-06-08 19:44:22 -0500 (Mon, 08 Jun 2009) | 4 lines
  Issue 2947: document how return code handling translates from
  os.popen to subprocess.  Also fixes reference link in the
  os.spawn documentation.
........
  r73317 | benjamin.peterson | 2009-06-09 12:24:26 -0500 (Tue, 09 Jun 2009) | 1 line
  make ast.c depend on the grammar
........
  r73318 | benjamin.peterson | 2009-06-09 12:29:51 -0500 (Tue, 09 Jun 2009) | 1 line
  explain why keyword names are not just NAME
........
  r73321 | benjamin.peterson | 2009-06-09 16:13:43 -0500 (Tue, 09 Jun 2009) | 1 line
  update symbol.py from with statement changes
........
  r73324 | amaury.forgeotdarc | 2009-06-09 17:53:16 -0500 (Tue, 09 Jun 2009) | 2 lines
  Avoid invoking the parser/compiler just to test the presence of a function.
........
  r73331 | benjamin.peterson | 2009-06-10 08:45:31 -0500 (Wed, 10 Jun 2009) | 1 line
  fix spelling
........
  r73335 | raymond.hettinger | 2009-06-10 11:15:40 -0500 (Wed, 10 Jun 2009) | 1 line
  Fix signed/unsigned compiler warning.
........
  r73340 | amaury.forgeotdarc | 2009-06-10 15:30:19 -0500 (Wed, 10 Jun 2009) | 2 lines
  Fix a typo spotted by Nick Coghlan.
........
  r73363 | benjamin.peterson | 2009-06-11 12:51:17 -0500 (Thu, 11 Jun 2009) | 1 line
  use multi-with syntax
........
											
										 
											2009-06-11 22:54:11 +00:00
										 |  |  |             extra = ", seq=True" | 
					
						
							| 
									
										
										
										
											2005-10-20 19:59:25 +00:00
										 |  |  |         elif self.opt: | 
					
						
							| 
									
										
											  
											
												Merged revisions 73196,73278-73280,73299,73308,73312-73313,73317-73318,73321,73324,73331,73335,73340,73363 via svnmerge from
svn+ssh://pythondev@svn.python.org/python/trunk
........
  r73196 | benjamin.peterson | 2009-06-03 20:40:29 -0500 (Wed, 03 Jun 2009) | 1 line
  use the offical api
........
  r73278 | benjamin.peterson | 2009-06-07 17:33:11 -0500 (Sun, 07 Jun 2009) | 1 line
  inherit from object
........
  r73279 | benjamin.peterson | 2009-06-07 17:35:00 -0500 (Sun, 07 Jun 2009) | 1 line
  always inherit from an appropiate base class
........
  r73280 | benjamin.peterson | 2009-06-07 17:54:35 -0500 (Sun, 07 Jun 2009) | 1 line
  use booleans for flags
........
  r73299 | georg.brandl | 2009-06-08 13:41:36 -0500 (Mon, 08 Jun 2009) | 1 line
  Typo fix.
........
  r73308 | benjamin.peterson | 2009-06-08 17:18:32 -0500 (Mon, 08 Jun 2009) | 1 line
  remove useless assertion
........
  r73312 | benjamin.peterson | 2009-06-08 18:44:13 -0500 (Mon, 08 Jun 2009) | 1 line
  remove error checks already done in set_context()
........
  r73313 | r.david.murray | 2009-06-08 19:44:22 -0500 (Mon, 08 Jun 2009) | 4 lines
  Issue 2947: document how return code handling translates from
  os.popen to subprocess.  Also fixes reference link in the
  os.spawn documentation.
........
  r73317 | benjamin.peterson | 2009-06-09 12:24:26 -0500 (Tue, 09 Jun 2009) | 1 line
  make ast.c depend on the grammar
........
  r73318 | benjamin.peterson | 2009-06-09 12:29:51 -0500 (Tue, 09 Jun 2009) | 1 line
  explain why keyword names are not just NAME
........
  r73321 | benjamin.peterson | 2009-06-09 16:13:43 -0500 (Tue, 09 Jun 2009) | 1 line
  update symbol.py from with statement changes
........
  r73324 | amaury.forgeotdarc | 2009-06-09 17:53:16 -0500 (Tue, 09 Jun 2009) | 2 lines
  Avoid invoking the parser/compiler just to test the presence of a function.
........
  r73331 | benjamin.peterson | 2009-06-10 08:45:31 -0500 (Wed, 10 Jun 2009) | 1 line
  fix spelling
........
  r73335 | raymond.hettinger | 2009-06-10 11:15:40 -0500 (Wed, 10 Jun 2009) | 1 line
  Fix signed/unsigned compiler warning.
........
  r73340 | amaury.forgeotdarc | 2009-06-10 15:30:19 -0500 (Wed, 10 Jun 2009) | 2 lines
  Fix a typo spotted by Nick Coghlan.
........
  r73363 | benjamin.peterson | 2009-06-11 12:51:17 -0500 (Thu, 11 Jun 2009) | 1 line
  use multi-with syntax
........
											
										 
											2009-06-11 22:54:11 +00:00
										 |  |  |             extra = ", opt=True" | 
					
						
							| 
									
										
										
										
											2005-10-20 19:59:25 +00:00
										 |  |  |         else: | 
					
						
							|  |  |  |             extra = "" | 
					
						
							|  |  |  |         if self.name is None: | 
					
						
							| 
									
										
										
										
											2014-05-09 17:58:22 -07:00
										 |  |  |             return 'Field({0.type}{1})'.format(self, extra) | 
					
						
							| 
									
										
										
										
											2005-10-20 19:59:25 +00:00
										 |  |  |         else: | 
					
						
							| 
									
										
										
										
											2014-05-09 17:58:22 -07:00
										 |  |  |             return 'Field({0.type}, {0.name}{1})'.format(self, extra) | 
					
						
							| 
									
										
										
										
											2005-10-20 19:59:25 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | class Sum(AST): | 
					
						
							|  |  |  |     def __init__(self, types, attributes=None): | 
					
						
							|  |  |  |         self.types = types | 
					
						
							|  |  |  |         self.attributes = attributes or [] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __repr__(self): | 
					
						
							| 
									
										
										
										
											2014-05-09 17:58:22 -07:00
										 |  |  |         if self.attributes: | 
					
						
							|  |  |  |             return 'Sum({0.types}, {0.attributes})'.format(self) | 
					
						
							| 
									
										
										
										
											2005-10-20 19:59:25 +00:00
										 |  |  |         else: | 
					
						
							| 
									
										
										
										
											2014-05-09 17:58:22 -07:00
										 |  |  |             return 'Sum({0.types})'.format(self) | 
					
						
							| 
									
										
										
										
											2005-10-20 19:59:25 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | class Product(AST): | 
					
						
							| 
									
										
										
										
											2013-03-18 10:48:58 -07:00
										 |  |  |     def __init__(self, fields, attributes=None): | 
					
						
							| 
									
										
										
										
											2005-10-20 19:59:25 +00:00
										 |  |  |         self.fields = fields | 
					
						
							| 
									
										
										
										
											2013-03-18 10:48:58 -07:00
										 |  |  |         self.attributes = attributes or [] | 
					
						
							| 
									
										
										
										
											2005-10-20 19:59:25 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def __repr__(self): | 
					
						
							| 
									
										
										
										
											2014-05-09 17:58:22 -07:00
										 |  |  |         if self.attributes: | 
					
						
							|  |  |  |             return 'Product({0.fields}, {0.attributes})'.format(self) | 
					
						
							| 
									
										
										
										
											2013-03-18 10:48:58 -07:00
										 |  |  |         else: | 
					
						
							| 
									
										
										
										
											2014-05-09 17:58:22 -07:00
										 |  |  |             return 'Product({0.fields})'.format(self) | 
					
						
							| 
									
										
										
										
											2005-10-20 19:59:25 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-05-09 17:58:22 -07:00
										 |  |  | # A generic visitor for the meta-AST that describes ASDL. This can be used by | 
					
						
							|  |  |  | # emitters. Note that this visitor does not provide a generic visit method, so a | 
					
						
							|  |  |  | # subclass needs to define visit methods from visitModule to as deep as the | 
					
						
							|  |  |  | # interesting node. | 
					
						
							|  |  |  | # We also define a Check visitor that makes sure the parsed ASDL is well-formed. | 
					
						
							| 
									
										
										
										
											2005-10-20 19:59:25 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-08 16:22:48 -07:00
										 |  |  | class VisitorBase(object): | 
					
						
							| 
									
										
										
										
											2014-05-09 17:58:22 -07:00
										 |  |  |     """Generic tree visitor for ASTs.""" | 
					
						
							|  |  |  |     def __init__(self): | 
					
						
							| 
									
										
										
										
											2005-10-20 19:59:25 +00:00
										 |  |  |         self.cache = {} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-05-09 17:58:22 -07:00
										 |  |  |     def visit(self, obj, *args): | 
					
						
							|  |  |  |         klass = obj.__class__ | 
					
						
							| 
									
										
										
										
											2005-10-20 19:59:25 +00:00
										 |  |  |         meth = self.cache.get(klass) | 
					
						
							|  |  |  |         if meth is None: | 
					
						
							|  |  |  |             methname = "visit" + klass.__name__ | 
					
						
							| 
									
										
										
										
											2014-05-09 17:58:22 -07:00
										 |  |  |             meth = getattr(self, methname, None) | 
					
						
							| 
									
										
										
										
											2005-10-20 19:59:25 +00:00
										 |  |  |             self.cache[klass] = meth | 
					
						
							| 
									
										
										
										
											2014-05-09 17:58:22 -07:00
										 |  |  |         if meth: | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 meth(obj, *args) | 
					
						
							|  |  |  |             except Exception as e: | 
					
						
							|  |  |  |                 print("Error visiting %r: %s" % (obj, e)) | 
					
						
							|  |  |  |                 raise | 
					
						
							| 
									
										
										
										
											2005-10-20 19:59:25 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | class Check(VisitorBase): | 
					
						
							| 
									
										
										
										
											2014-05-09 17:58:22 -07:00
										 |  |  |     """A visitor that checks a parsed ASDL tree for correctness.
 | 
					
						
							| 
									
										
										
										
											2005-10-20 19:59:25 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-05-09 17:58:22 -07:00
										 |  |  |     Errors are printed and accumulated. | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2005-10-20 19:59:25 +00:00
										 |  |  |     def __init__(self): | 
					
						
							| 
									
										
										
										
											2014-07-08 16:22:48 -07:00
										 |  |  |         super(Check, self).__init__() | 
					
						
							| 
									
										
										
										
											2005-10-20 19:59:25 +00:00
										 |  |  |         self.cons = {} | 
					
						
							|  |  |  |         self.errors = 0 | 
					
						
							|  |  |  |         self.types = {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def visitModule(self, mod): | 
					
						
							|  |  |  |         for dfn in mod.dfns: | 
					
						
							|  |  |  |             self.visit(dfn) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def visitType(self, type): | 
					
						
							|  |  |  |         self.visit(type.value, str(type.name)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def visitSum(self, sum, name): | 
					
						
							|  |  |  |         for t in sum.types: | 
					
						
							|  |  |  |             self.visit(t, name) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def visitConstructor(self, cons, name): | 
					
						
							|  |  |  |         key = str(cons.name) | 
					
						
							|  |  |  |         conflict = self.cons.get(key) | 
					
						
							|  |  |  |         if conflict is None: | 
					
						
							|  |  |  |             self.cons[key] = name | 
					
						
							|  |  |  |         else: | 
					
						
							| 
									
										
										
										
											2014-05-09 17:58:22 -07:00
										 |  |  |             print('Redefinition of constructor {}'.format(key)) | 
					
						
							|  |  |  |             print('Defined in {} and {}'.format(conflict, name)) | 
					
						
							| 
									
										
										
										
											2005-10-20 19:59:25 +00:00
										 |  |  |             self.errors += 1 | 
					
						
							|  |  |  |         for f in cons.fields: | 
					
						
							|  |  |  |             self.visit(f, key) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def visitField(self, field, name): | 
					
						
							|  |  |  |         key = str(field.type) | 
					
						
							|  |  |  |         l = self.types.setdefault(key, []) | 
					
						
							|  |  |  |         l.append(name) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def visitProduct(self, prod, name): | 
					
						
							|  |  |  |         for f in prod.fields: | 
					
						
							|  |  |  |             self.visit(f, name) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def check(mod): | 
					
						
							| 
									
										
										
										
											2014-05-09 17:58:22 -07:00
										 |  |  |     """Check the parsed ASDL tree for correctness.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Return True if success. For failure, the errors are printed out and False | 
					
						
							|  |  |  |     is returned. | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2005-10-20 19:59:25 +00:00
										 |  |  |     v = Check() | 
					
						
							|  |  |  |     v.visit(mod) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for t in v.types: | 
					
						
							| 
									
										
										
										
											2006-12-29 04:42:48 +00:00
										 |  |  |         if t not in mod.types and not t in builtin_types: | 
					
						
							| 
									
										
										
										
											2005-10-20 19:59:25 +00:00
										 |  |  |             v.errors += 1 | 
					
						
							|  |  |  |             uses = ", ".join(v.types[t]) | 
					
						
							| 
									
										
										
										
											2014-05-09 17:58:22 -07:00
										 |  |  |             print('Undefined type {}, used in {}'.format(t, uses)) | 
					
						
							| 
									
										
										
										
											2005-10-20 19:59:25 +00:00
										 |  |  |     return not v.errors | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-05-09 17:58:22 -07:00
										 |  |  | # The ASDL parser itself comes next. The only interesting external interface | 
					
						
							|  |  |  | # here is the top-level parse function. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def parse(filename): | 
					
						
							|  |  |  |     """Parse ASDL from the given file and return a Module node describing it.""" | 
					
						
							|  |  |  |     with open(filename) as f: | 
					
						
							|  |  |  |         parser = ASDLParser() | 
					
						
							|  |  |  |         return parser.parse(f.read()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # Types for describing tokens in an ASDL specification. | 
					
						
							|  |  |  | class TokenKind: | 
					
						
							|  |  |  |     """TokenKind is provides a scope for enumerated token kinds.""" | 
					
						
							|  |  |  |     (ConstructorId, TypeId, Equals, Comma, Question, Pipe, Asterisk, | 
					
						
							|  |  |  |      LParen, RParen, LBrace, RBrace) = range(11) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     operator_table = { | 
					
						
							|  |  |  |         '=': Equals, ',': Comma,    '?': Question, '|': Pipe,    '(': LParen, | 
					
						
							|  |  |  |         ')': RParen, '*': Asterisk, '{': LBrace,   '}': RBrace} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Token = namedtuple('Token', 'kind value lineno') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class ASDLSyntaxError(Exception): | 
					
						
							|  |  |  |     def __init__(self, msg, lineno=None): | 
					
						
							|  |  |  |         self.msg = msg | 
					
						
							|  |  |  |         self.lineno = lineno or '<unknown>' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __str__(self): | 
					
						
							|  |  |  |         return 'Syntax error on line {0.lineno}: {0.msg}'.format(self) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def tokenize_asdl(buf): | 
					
						
							|  |  |  |     """Tokenize the given buffer. Yield Token objects.""" | 
					
						
							|  |  |  |     for lineno, line in enumerate(buf.splitlines(), 1): | 
					
						
							|  |  |  |         for m in re.finditer(r'\s*(\w+|--.*|.)', line.strip()): | 
					
						
							|  |  |  |             c = m.group(1) | 
					
						
							|  |  |  |             if c[0].isalpha(): | 
					
						
							|  |  |  |                 # Some kind of identifier | 
					
						
							|  |  |  |                 if c[0].isupper(): | 
					
						
							|  |  |  |                     yield Token(TokenKind.ConstructorId, c, lineno) | 
					
						
							|  |  |  |                 else: | 
					
						
							|  |  |  |                     yield Token(TokenKind.TypeId, c, lineno) | 
					
						
							|  |  |  |             elif c[:2] == '--': | 
					
						
							|  |  |  |                 # Comment | 
					
						
							|  |  |  |                 break | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 # Operators | 
					
						
							|  |  |  |                 try: | 
					
						
							|  |  |  |                     op_kind = TokenKind.operator_table[c] | 
					
						
							|  |  |  |                 except KeyError: | 
					
						
							|  |  |  |                     raise ASDLSyntaxError('Invalid operator %s' % c, lineno) | 
					
						
							|  |  |  |                 yield Token(op_kind, c, lineno) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class ASDLParser: | 
					
						
							|  |  |  |     """Parser for ASDL files.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Create, then call the parse method on a buffer containing ASDL. | 
					
						
							|  |  |  |     This is a simple recursive descent parser that uses tokenize_asdl for the | 
					
						
							|  |  |  |     lexing. | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     def __init__(self): | 
					
						
							|  |  |  |         self._tokenizer = None | 
					
						
							|  |  |  |         self.cur_token = None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def parse(self, buf): | 
					
						
							|  |  |  |         """Parse the ASDL in the buffer and return an AST with a Module root.
 | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         self._tokenizer = tokenize_asdl(buf) | 
					
						
							|  |  |  |         self._advance() | 
					
						
							|  |  |  |         return self._parse_module() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _parse_module(self): | 
					
						
							|  |  |  |         if self._at_keyword('module'): | 
					
						
							|  |  |  |             self._advance() | 
					
						
							| 
									
										
										
										
											2005-10-20 19:59:25 +00:00
										 |  |  |         else: | 
					
						
							| 
									
										
										
										
											2014-05-09 17:58:22 -07:00
										 |  |  |             raise ASDLSyntaxError( | 
					
						
							|  |  |  |                 'Expected "module" (found {})'.format(self.cur_token.value), | 
					
						
							|  |  |  |                 self.cur_token.lineno) | 
					
						
							|  |  |  |         name = self._match(self._id_kinds) | 
					
						
							|  |  |  |         self._match(TokenKind.LBrace) | 
					
						
							|  |  |  |         defs = self._parse_definitions() | 
					
						
							|  |  |  |         self._match(TokenKind.RBrace) | 
					
						
							|  |  |  |         return Module(name, defs) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _parse_definitions(self): | 
					
						
							|  |  |  |         defs = [] | 
					
						
							|  |  |  |         while self.cur_token.kind == TokenKind.TypeId: | 
					
						
							|  |  |  |             typename = self._advance() | 
					
						
							|  |  |  |             self._match(TokenKind.Equals) | 
					
						
							|  |  |  |             type = self._parse_type() | 
					
						
							|  |  |  |             defs.append(Type(typename, type)) | 
					
						
							|  |  |  |         return defs | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _parse_type(self): | 
					
						
							|  |  |  |         if self.cur_token.kind == TokenKind.LParen: | 
					
						
							|  |  |  |             # If we see a (, it's a product | 
					
						
							|  |  |  |             return self._parse_product() | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             # Otherwise it's a sum. Look for ConstructorId | 
					
						
							|  |  |  |             sumlist = [Constructor(self._match(TokenKind.ConstructorId), | 
					
						
							|  |  |  |                                    self._parse_optional_fields())] | 
					
						
							|  |  |  |             while self.cur_token.kind  == TokenKind.Pipe: | 
					
						
							|  |  |  |                 # More constructors | 
					
						
							|  |  |  |                 self._advance() | 
					
						
							|  |  |  |                 sumlist.append(Constructor( | 
					
						
							|  |  |  |                                 self._match(TokenKind.ConstructorId), | 
					
						
							|  |  |  |                                 self._parse_optional_fields())) | 
					
						
							|  |  |  |             return Sum(sumlist, self._parse_optional_attributes()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _parse_product(self): | 
					
						
							|  |  |  |         return Product(self._parse_fields(), self._parse_optional_attributes()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _parse_fields(self): | 
					
						
							|  |  |  |         fields = [] | 
					
						
							|  |  |  |         self._match(TokenKind.LParen) | 
					
						
							|  |  |  |         while self.cur_token.kind == TokenKind.TypeId: | 
					
						
							|  |  |  |             typename = self._advance() | 
					
						
							|  |  |  |             is_seq, is_opt = self._parse_optional_field_quantifier() | 
					
						
							|  |  |  |             id = (self._advance() if self.cur_token.kind in self._id_kinds | 
					
						
							|  |  |  |                                   else None) | 
					
						
							|  |  |  |             fields.append(Field(typename, id, seq=is_seq, opt=is_opt)) | 
					
						
							|  |  |  |             if self.cur_token.kind == TokenKind.RParen: | 
					
						
							|  |  |  |                 break | 
					
						
							|  |  |  |             elif self.cur_token.kind == TokenKind.Comma: | 
					
						
							|  |  |  |                 self._advance() | 
					
						
							|  |  |  |         self._match(TokenKind.RParen) | 
					
						
							|  |  |  |         return fields | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _parse_optional_fields(self): | 
					
						
							|  |  |  |         if self.cur_token.kind == TokenKind.LParen: | 
					
						
							|  |  |  |             return self._parse_fields() | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             return None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _parse_optional_attributes(self): | 
					
						
							|  |  |  |         if self._at_keyword('attributes'): | 
					
						
							|  |  |  |             self._advance() | 
					
						
							|  |  |  |             return self._parse_fields() | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             return None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _parse_optional_field_quantifier(self): | 
					
						
							|  |  |  |         is_seq, is_opt = False, False | 
					
						
							|  |  |  |         if self.cur_token.kind == TokenKind.Asterisk: | 
					
						
							|  |  |  |             is_seq = True | 
					
						
							|  |  |  |             self._advance() | 
					
						
							|  |  |  |         elif self.cur_token.kind == TokenKind.Question: | 
					
						
							|  |  |  |             is_opt = True | 
					
						
							|  |  |  |             self._advance() | 
					
						
							|  |  |  |         return is_seq, is_opt | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _advance(self): | 
					
						
							|  |  |  |         """ Return the value of the current token and read the next one into
 | 
					
						
							|  |  |  |             self.cur_token. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         cur_val = None if self.cur_token is None else self.cur_token.value | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             self.cur_token = next(self._tokenizer) | 
					
						
							|  |  |  |         except StopIteration: | 
					
						
							|  |  |  |             self.cur_token = None | 
					
						
							|  |  |  |         return cur_val | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     _id_kinds = (TokenKind.ConstructorId, TokenKind.TypeId) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _match(self, kind): | 
					
						
							|  |  |  |         """The 'match' primitive of RD parsers.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         * Verifies that the current token is of the given kind (kind can | 
					
						
							|  |  |  |           be a tuple, in which the kind must match one of its members). | 
					
						
							|  |  |  |         * Returns the value of the current token | 
					
						
							|  |  |  |         * Reads in the next token | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         if (isinstance(kind, tuple) and self.cur_token.kind in kind or | 
					
						
							|  |  |  |             self.cur_token.kind == kind | 
					
						
							|  |  |  |             ): | 
					
						
							|  |  |  |             value = self.cur_token.value | 
					
						
							|  |  |  |             self._advance() | 
					
						
							|  |  |  |             return value | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             raise ASDLSyntaxError( | 
					
						
							|  |  |  |                 'Unmatched {} (found {})'.format(kind, self.cur_token.kind), | 
					
						
							|  |  |  |                 self.cur_token.lineno) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _at_keyword(self, keyword): | 
					
						
							|  |  |  |         return (self.cur_token.kind == TokenKind.TypeId and | 
					
						
							|  |  |  |                 self.cur_token.value == keyword) |