| 1 | """AST for ASDL.
 | 
| 2 | 
 | 
| 3 | (Not self-hosted!)
 | 
| 4 | """
 | 
| 5 | from __future__ import print_function
 | 
| 6 | 
 | 
| 7 | import cStringIO
 | 
| 8 | 
 | 
| 9 | from typing import List
 | 
| 10 | 
 | 
| 11 | # The following classes are the AST for the ASDL schema, i.e. the "meta-AST".
 | 
| 12 | # See the EBNF at the top of the file to understand the logical connection
 | 
| 13 | # between the various node types.
 | 
| 14 | 
 | 
| 15 | 
 | 
| 16 | class AST(object):
 | 
| 17 | 
 | 
| 18 |     def Print(self, f, indent):
 | 
| 19 |         raise NotImplementedError()
 | 
| 20 | 
 | 
| 21 |     def __str__(self):
 | 
| 22 |         f = cStringIO.StringIO()
 | 
| 23 |         self.Print(f, 0)
 | 
| 24 |         return f.getvalue()
 | 
| 25 | 
 | 
| 26 | 
 | 
| 27 | class Use(AST):
 | 
| 28 | 
 | 
| 29 |     def __init__(self, module_parts, type_names):
 | 
| 30 |         self.module_parts = module_parts
 | 
| 31 |         self.type_names = type_names
 | 
| 32 | 
 | 
| 33 |     def Print(self, f, indent):
 | 
| 34 |         ind = indent * '  '
 | 
| 35 |         f.write('%sUse %s {\n' % (ind, ' '.join(self.module_parts)))
 | 
| 36 |         f.write('  %s%s\n' % (ind, ', '.join(t for t in self.type_names)))
 | 
| 37 |         f.write('%s}\n' % ind)
 | 
| 38 | 
 | 
| 39 | 
 | 
| 40 | class Module(AST):
 | 
| 41 | 
 | 
| 42 |     def __init__(self, name, uses, dfns):
 | 
| 43 |         self.name = name
 | 
| 44 |         self.uses = uses
 | 
| 45 |         self.dfns = dfns
 | 
| 46 | 
 | 
| 47 |     def Print(self, f, indent):
 | 
| 48 |         ind = indent * '  '
 | 
| 49 |         f.write('%sModule %s {\n' % (ind, self.name))
 | 
| 50 | 
 | 
| 51 |         for u in self.uses:
 | 
| 52 |             u.Print(f, indent + 1)
 | 
| 53 |             f.write('\n')
 | 
| 54 | 
 | 
| 55 |         for d in self.dfns:
 | 
| 56 |             d.Print(f, indent + 1)
 | 
| 57 |             f.write('\n')
 | 
| 58 |         f.write('%s}\n' % ind)
 | 
| 59 | 
 | 
| 60 | 
 | 
| 61 | class TypeDecl(AST):
 | 
| 62 |     """A binding of name to a Sum or Product type."""
 | 
| 63 | 
 | 
| 64 |     def __init__(self, name, value):
 | 
| 65 |         self.name = name  # type: str
 | 
| 66 |         self.value = value  # type: AST
 | 
| 67 | 
 | 
| 68 |     def Print(self, f, indent):
 | 
| 69 |         ind = indent * '  '
 | 
| 70 |         f.write('%sType %s {\n' % (ind, self.name))
 | 
| 71 |         self.value.Print(f, indent + 1)
 | 
| 72 |         f.write('%s}\n' % ind)
 | 
| 73 | 
 | 
| 74 | 
 | 
| 75 | class NamedType(AST):
 | 
| 76 |     """Int, string are resolved to a Primitive type 'Point' can be resolved to
 | 
| 77 |     CompoundSum instance, etc."""
 | 
| 78 | 
 | 
| 79 |     def __init__(self, name):
 | 
| 80 |         # type: (str) -> None
 | 
| 81 |         self.name = name
 | 
| 82 | 
 | 
| 83 |         # Mutated by _ResolveModule / _ResolveType
 | 
| 84 |         self.resolved = None
 | 
| 85 | 
 | 
| 86 |     def Print(self, f, indent):
 | 
| 87 |         """Printed on one line."""
 | 
| 88 |         f.write('NamedType %s' % (self.name))  # printed after field
 | 
| 89 |         f.write(' (%r)' % self.resolved)
 | 
| 90 | 
 | 
| 91 |     def IsOptional(self):
 | 
| 92 |         return False
 | 
| 93 | 
 | 
| 94 |     def IsList(self):
 | 
| 95 |         return False
 | 
| 96 | 
 | 
| 97 |     def IsDict(self):
 | 
| 98 |         return False
 | 
| 99 | 
 | 
| 100 | 
 | 
| 101 | class ParameterizedType(AST):
 | 
| 102 |     """A parameterized type expression, e.g. the type of a field.
 | 
| 103 | 
 | 
| 104 |     e.g. Dict[string, int]   Dict[int, array[string]]
 | 
| 105 | 
 | 
| 106 |     self.children is empty if it's a leaf.
 | 
| 107 | 
 | 
| 108 |     Note:
 | 
| 109 | 
 | 
| 110 |     string*  <=>  array[string]
 | 
| 111 |     mytype?  <=>  maybe[mytype]
 | 
| 112 |     """
 | 
| 113 | 
 | 
| 114 |     def __init__(self, type_name, children):
 | 
| 115 |         self.type_name = type_name  # type: str
 | 
| 116 |         self.children = children  # type: List[AST]
 | 
| 117 | 
 | 
| 118 |     def Print(self, f, indent):
 | 
| 119 |         """Printed on one line."""
 | 
| 120 |         f.write('%s' % (self.type_name))  # printed after field
 | 
| 121 |         if self.children:
 | 
| 122 |             f.write(' [ ')
 | 
| 123 |             for i, child in enumerate(self.children):
 | 
| 124 |                 if i != 0:
 | 
| 125 |                     f.write(', ')
 | 
| 126 |                 child.Print(f, indent + 1)
 | 
| 127 |             f.write(' ]')
 | 
| 128 | 
 | 
| 129 |     def IsOptional(self):
 | 
| 130 |         return self.type_name == 'Optional'
 | 
| 131 | 
 | 
| 132 |     def IsList(self):
 | 
| 133 |         if self.type_name == 'List':
 | 
| 134 |             return True
 | 
| 135 |         if self.type_name == 'Optional':
 | 
| 136 |             return self.children[0].IsList()
 | 
| 137 |         return False
 | 
| 138 | 
 | 
| 139 |     def IsDict(self):
 | 
| 140 |         if self.type_name == 'Dict':
 | 
| 141 |             return True
 | 
| 142 |         if self.type_name == 'Optional':
 | 
| 143 |             return self.children[0].IsDict()
 | 
| 144 |         return False
 | 
| 145 | 
 | 
| 146 | 
 | 
| 147 | class Field(AST):
 | 
| 148 | 
 | 
| 149 |     def __init__(self, typ, name):
 | 
| 150 |         # type: (AST, str) -> None
 | 
| 151 |         self.typ = typ  # type expression
 | 
| 152 |         self.name = name  # variable name
 | 
| 153 | 
 | 
| 154 |     def Print(self, f, indent):
 | 
| 155 |         ind = indent * '  '
 | 
| 156 |         f.write('%sField %r ' % (ind, self.name))
 | 
| 157 |         self.typ.Print(f, indent)
 | 
| 158 |         f.write('\n')
 | 
| 159 | 
 | 
| 160 | 
 | 
| 161 | class _CompoundAST(AST):
 | 
| 162 |     """Either a Product or Constructor.
 | 
| 163 | 
 | 
| 164 |     encode.py and format.py need a reflection API.
 | 
| 165 |     """
 | 
| 166 | 
 | 
| 167 |     def __init__(self, fields):
 | 
| 168 |         self.fields = fields or []
 | 
| 169 | 
 | 
| 170 | 
 | 
| 171 | class Constructor(_CompoundAST):
 | 
| 172 | 
 | 
| 173 |     def __init__(self, name, shared_type=None, fields=None):
 | 
| 174 |         _CompoundAST.__init__(self, fields)
 | 
| 175 |         self.name = name
 | 
| 176 |         self.shared_type = shared_type  # for DoubleQuoted %DoubleQuoted
 | 
| 177 | 
 | 
| 178 |     def Print(self, f, indent):
 | 
| 179 |         ind = indent * '  '
 | 
| 180 |         f.write('%sConstructor %s' % (ind, self.name))
 | 
| 181 |         if self.shared_type:
 | 
| 182 |             f.write(' %%%s' % self.shared_type)
 | 
| 183 | 
 | 
| 184 |         if self.fields:
 | 
| 185 |             f.write(' {\n')
 | 
| 186 |             for field in self.fields:
 | 
| 187 |                 field.Print(f, indent + 1)
 | 
| 188 |             f.write('%s}' % ind)
 | 
| 189 | 
 | 
| 190 |         f.write('\n')
 | 
| 191 | 
 | 
| 192 | 
 | 
| 193 | class Sum(AST):
 | 
| 194 | 
 | 
| 195 |     def __init__(self, types, generate=None):
 | 
| 196 |         self.types = types  # type: List[Constructor]
 | 
| 197 |         self.generate = generate or []
 | 
| 198 | 
 | 
| 199 |     def Print(self, f, indent):
 | 
| 200 |         ind = indent * '  '
 | 
| 201 |         f.write('%sSum {\n' % ind)
 | 
| 202 |         for t in self.types:
 | 
| 203 |             t.Print(f, indent + 1)
 | 
| 204 |         if self.generate:
 | 
| 205 |             f.write('%s  generate %s\n' % (ind, self.generate))
 | 
| 206 |         f.write('%s}\n' % ind)
 | 
| 207 | 
 | 
| 208 | 
 | 
| 209 | class SimpleSum(Sum):
 | 
| 210 |     pass
 | 
| 211 | 
 | 
| 212 | 
 | 
| 213 | class Product(_CompoundAST):
 | 
| 214 | 
 | 
| 215 |     def __init__(self, fields):
 | 
| 216 |         _CompoundAST.__init__(self, fields)
 | 
| 217 | 
 | 
| 218 |     def Print(self, f, indent):
 | 
| 219 |         ind = indent * '  '
 | 
| 220 |         f.write('%sProduct {\n' % ind)
 | 
| 221 |         for field in self.fields:
 | 
| 222 |             field.Print(f, indent + 1)
 | 
| 223 |         f.write('%s}\n' % ind)
 | 
| 224 | 
 | 
| 225 | 
 | 
| 226 | #
 | 
| 227 | # Helpers
 | 
| 228 | #
 | 
| 229 | 
 | 
| 230 | 
 | 
| 231 | def TypeNameHeuristic(t):
 | 
| 232 |     # type: (str) -> str
 | 
| 233 |     """For 'use'.
 | 
| 234 | 
 | 
| 235 |     We don't parse the imported file, so we have a heuristic based on
 | 
| 236 |     the name!  e.g. re_t or BraceGroup
 | 
| 237 |     """
 | 
| 238 |     return '%s_t' % t if t[0].islower() else t
 |