| 1 | """front_end.py: Lexer and parser for the ASDL schema language."""
|
| 2 | from __future__ import print_function
|
| 3 |
|
| 4 | from asdl import ast
|
| 5 | from asdl.ast import (Module, TypeDecl, SubTypeDecl, Field)
|
| 6 | from asdl import parse
|
| 7 | from asdl.parse import ASDLSyntaxError
|
| 8 | from asdl.util import log
|
| 9 |
|
| 10 | from typing import List, Dict, Tuple, IO, cast, TYPE_CHECKING
|
| 11 |
|
| 12 | if TYPE_CHECKING:
|
| 13 | TypeLookup = Dict[str, ast.asdl_type_t]
|
| 14 |
|
| 15 | _ = log
|
| 16 |
|
| 17 | _PRIMITIVE_TYPES = [
|
| 18 | 'string',
|
| 19 | 'int',
|
| 20 | 'uint16', # used for Token length - should we generalize this?
|
| 21 | 'BigInt',
|
| 22 | 'float',
|
| 23 | 'bool',
|
| 24 |
|
| 25 | # 'any' is used for value.{BuiltinProc,BuiltinFunc}, to cast from class
|
| 26 | # type
|
| 27 | 'any',
|
| 28 | ]
|
| 29 |
|
| 30 |
|
| 31 | def _ResolveType(typ, type_lookup):
|
| 32 | # type: (ast.type_expr_t, TypeLookup) -> None
|
| 33 | """Recursively attach a 'resolved' field to AST nodes."""
|
| 34 | if isinstance(typ, ast.NamedType):
|
| 35 | if typ.name not in _PRIMITIVE_TYPES:
|
| 36 | ast_node = type_lookup.get(typ.name)
|
| 37 | if ast_node is None:
|
| 38 | raise ASDLSyntaxError("Couldn't find type %r" % typ.name)
|
| 39 | typ.resolved = ast_node
|
| 40 |
|
| 41 | elif isinstance(typ, ast.ParameterizedType):
|
| 42 | for child in typ.children:
|
| 43 | _ResolveType(child, type_lookup)
|
| 44 |
|
| 45 | if typ.type_name == 'Optional':
|
| 46 | child = typ.children[0]
|
| 47 | if isinstance(child, ast.NamedType):
|
| 48 | if child.name in _PRIMITIVE_TYPES and child.name != 'string':
|
| 49 | raise ASDLSyntaxError(
|
| 50 | 'Optional primitive type {} not allowed'.format(
|
| 51 | child.name))
|
| 52 |
|
| 53 | if child.resolved and isinstance(child.resolved,
|
| 54 | ast.SimpleSum):
|
| 55 | raise ASDLSyntaxError(
|
| 56 | 'Optional simple sum type {} not allowed'.format(
|
| 57 | child.name))
|
| 58 |
|
| 59 | else:
|
| 60 | raise AssertionError()
|
| 61 |
|
| 62 |
|
| 63 | def _ResolveFields(field_ast_nodes, type_lookup):
|
| 64 | # type: (List[Field], TypeLookup) -> None
|
| 65 | """
|
| 66 | Args:
|
| 67 | type_lookup: Populated by name resolution
|
| 68 | """
|
| 69 | for field in field_ast_nodes:
|
| 70 | _ResolveType(field.typ, type_lookup)
|
| 71 |
|
| 72 |
|
| 73 | def _ResolveModule(module, type_lookup):
|
| 74 | # type: (Module, TypeLookup) -> None
|
| 75 | """Name resolution for NamedType."""
|
| 76 |
|
| 77 | # Note: we don't actually load the type, and instead leave that to MyPy /
|
| 78 | # C++. A consequence of this is TypeNameHeuristic().
|
| 79 | for u in module.uses:
|
| 80 | for type_name in u.type_names:
|
| 81 | type_lookup[type_name] = u
|
| 82 |
|
| 83 | # NOTE: We need two passes because types can be mutually recursive, e.g.
|
| 84 | # asdl/arith.asdl.
|
| 85 |
|
| 86 | # First pass: collect declared types and make entries for them.
|
| 87 | for ex in module.externs:
|
| 88 | last = ex.names[-1] # e.g. _Callable
|
| 89 | if last in type_lookup:
|
| 90 | raise ASDLSyntaxError('Type %r was already defined' % last)
|
| 91 | type_lookup[last] = ex
|
| 92 |
|
| 93 | for d in module.dfns:
|
| 94 |
|
| 95 | if isinstance(d, SubTypeDecl):
|
| 96 | if d.name in type_lookup:
|
| 97 | raise ASDLSyntaxError('Type %r was already defined' % d.name)
|
| 98 |
|
| 99 | # e.g. CompoundWord < List[str]
|
| 100 | type_lookup[d.name] = ast.DummyType() # this value isn't used?
|
| 101 |
|
| 102 | elif isinstance(d, TypeDecl):
|
| 103 | if d.name in type_lookup:
|
| 104 | raise ASDLSyntaxError('Type %r was already defined' % d.name)
|
| 105 |
|
| 106 | # e.g. Token = (str a)
|
| 107 | type_lookup[d.name] = d.value
|
| 108 |
|
| 109 | else:
|
| 110 | raise AssertionError()
|
| 111 |
|
| 112 | # Second pass: add NamedType.resolved field
|
| 113 | for d in module.dfns:
|
| 114 | if isinstance(d, SubTypeDecl): # no fields
|
| 115 | _ResolveType(d.base_class, type_lookup)
|
| 116 | continue
|
| 117 |
|
| 118 | d = cast(ast.TypeDecl, d)
|
| 119 |
|
| 120 | ast_node = d.value
|
| 121 | if isinstance(ast_node, ast.Product):
|
| 122 | #log('fields %s', ast_node.fields)
|
| 123 | _ResolveFields(ast_node.fields, type_lookup)
|
| 124 | continue
|
| 125 |
|
| 126 | if isinstance(ast_node, ast.Sum):
|
| 127 | for cons in ast_node.types:
|
| 128 | _ResolveFields(cons.fields, type_lookup)
|
| 129 | continue
|
| 130 |
|
| 131 | raise AssertionError(ast_node)
|
| 132 |
|
| 133 |
|
| 134 | def LoadSchema(f, verbose=False):
|
| 135 | # type: (IO[bytes], bool) -> Tuple[ast.Module, TypeLookup]
|
| 136 | """Returns an AST for the schema."""
|
| 137 | p = parse.ASDLParser()
|
| 138 | schema_ast = p.parse(f)
|
| 139 | if verbose:
|
| 140 | import sys
|
| 141 | schema_ast.Print(sys.stdout, 0)
|
| 142 |
|
| 143 | # Make sure all the names are valid
|
| 144 | type_lookup = {} # type: TypeLookup
|
| 145 | _ResolveModule(schema_ast, type_lookup)
|
| 146 | return schema_ast, type_lookup
|