Remove asdl (#21)

This removes the ASDL code generation in favor of handwriting the AST. 

The motivations for moving away from the ASDL are:

* CPython compatibility is no longer a goal
* The ASDL grammar isn't as expressive as we would like
* The codegen scripts have a high complexity which makes extensions time
consuming
* We don't make heavy use of code generation (compared to e.g.
RustPython that generates Pyo3 bindings, a fold implementation etc).

We may want to revisit a grammar based code generation in the future,
e.g. by using [ungrammar](https://github.com/rust-analyzer/ungrammar)
This commit is contained in:
konsti
2023-07-05 14:25:26 +02:00
committed by GitHub
parent 0f2e295f3a
commit 2d1f69cbb9
12 changed files with 3641 additions and 5003 deletions

View File

@@ -73,4 +73,3 @@ jobs:
'core/**/*.rs'
'literal/**/*.rs'
'parser/**/*.rs'
'ast/asdl_rs.py'

View File

@@ -1,147 +0,0 @@
-- ASDL's 4 builtin types are:
-- identifier, int, string, constant, decorator
module Python
{
mod = Module(stmt* body, type_ignore* type_ignores)
| Interactive(stmt* body)
| Expression(expr body)
| FunctionType(expr* argtypes, expr returns)
stmt = FunctionDef(identifier name, arguments args,
stmt* body, decorator* decorator_list, expr? returns,
string? type_comment)
| AsyncFunctionDef(identifier name, arguments args,
stmt* body, decorator* decorator_list, expr? returns,
string? type_comment)
| ClassDef(identifier name,
expr* bases,
keyword* keywords,
stmt* body,
decorator* decorator_list)
| Return(expr? value)
| Delete(expr* targets)
| Assign(expr* targets, expr value, string? type_comment)
| AugAssign(expr target, operator op, expr value)
-- 'simple' indicates that we annotate simple name without parens
| AnnAssign(expr target, expr annotation, expr? value, int simple)
-- use 'orelse' because else is a keyword in target languages
| For(expr target, expr iter, stmt* body, stmt* orelse, string? type_comment)
| AsyncFor(expr target, expr iter, stmt* body, stmt* orelse, string? type_comment)
| While(expr test, stmt* body, stmt* orelse)
| If(expr test, stmt* body, stmt* orelse)
| With(withitem* items, stmt* body, string? type_comment)
| AsyncWith(withitem* items, stmt* body, string? type_comment)
| Match(expr subject, match_case* cases)
| Raise(expr? exc, expr? cause)
| Try(stmt* body, excepthandler* handlers, stmt* orelse, stmt* finalbody)
| TryStar(stmt* body, excepthandler* handlers, stmt* orelse, stmt* finalbody)
| Assert(expr test, expr? msg)
| Import(alias* names)
| ImportFrom(identifier? module, alias* names, int? level)
| Global(identifier* names)
| Nonlocal(identifier* names)
| Expr(expr value)
| Pass | Break | Continue
-- col_offset is the byte offset in the utf8 string the parser uses
attributes (int lineno, int col_offset, int? end_lineno, int? end_col_offset)
-- BoolOp() can use left & right?
expr = BoolOp(boolop op, expr* values)
| NamedExpr(expr target, expr value)
| BinOp(expr left, operator op, expr right)
| UnaryOp(unaryop op, expr operand)
| Lambda(arguments args, expr body)
| IfExp(expr test, expr body, expr orelse)
| Dict(expr* keys, expr* values)
| Set(expr* elts)
| ListComp(expr elt, comprehension* generators)
| SetComp(expr elt, comprehension* generators)
| DictComp(expr key, expr value, comprehension* generators)
| GeneratorExp(expr elt, comprehension* generators)
-- the grammar constrains where yield expressions can occur
| Await(expr value)
| Yield(expr? value)
| YieldFrom(expr value)
-- need sequences for compare to distinguish between
-- x < 4 < 3 and (x < 4) < 3
| Compare(expr left, cmpop* ops, expr* comparators)
| Call(expr func, expr* args, keyword* keywords)
| FormattedValue(expr value, int conversion, expr? format_spec)
| JoinedStr(expr* values)
| Constant(constant value, string? kind)
-- the following expression can appear in assignment context
| Attribute(expr value, identifier attr, expr_context ctx)
| Subscript(expr value, expr slice, expr_context ctx)
| Starred(expr value, expr_context ctx)
| Name(identifier id, expr_context ctx)
| List(expr* elts, expr_context ctx)
| Tuple(expr* elts, expr_context ctx)
-- can appear only in Subscript
| Slice(expr? lower, expr? upper, expr? step)
-- col_offset is the byte offset in the utf8 string the parser uses
attributes (int lineno, int col_offset, int? end_lineno, int? end_col_offset)
expr_context = Load | Store | Del
boolop = And | Or
operator = Add | Sub | Mult | MatMult | Div | Mod | Pow | LShift
| RShift | BitOr | BitXor | BitAnd | FloorDiv
unaryop = Invert | Not | UAdd | USub
cmpop = Eq | NotEq | Lt | LtE | Gt | GtE | Is | IsNot | In | NotIn
comprehension = (expr target, expr iter, expr* ifs, int is_async)
excepthandler = ExceptHandler(expr? type, identifier? name, stmt* body)
attributes (int lineno, int col_offset, int? end_lineno, int? end_col_offset)
arguments = (arg* posonlyargs, arg* args, arg? vararg, arg* kwonlyargs,
expr* kw_defaults, arg? kwarg, expr* defaults)
arg = (identifier arg, expr? annotation, string? type_comment)
attributes (int lineno, int col_offset, int? end_lineno, int? end_col_offset)
-- keyword arguments supplied to call (NULL identifier for **kwargs)
keyword = (identifier? arg, expr value)
attributes (int lineno, int col_offset, int? end_lineno, int? end_col_offset)
-- import name with optional 'as' alias.
alias = (identifier name, identifier? asname)
attributes (int lineno, int col_offset, int? end_lineno, int? end_col_offset)
withitem = (expr context_expr, expr? optional_vars)
match_case = (pattern pattern, expr? guard, stmt* body)
pattern = MatchValue(expr value)
| MatchSingleton(constant value)
| MatchSequence(pattern* patterns)
| MatchMapping(expr* keys, pattern* patterns, identifier? rest)
| MatchClass(expr cls, pattern* patterns, identifier* kwd_attrs, pattern* kwd_patterns)
| MatchStar(identifier? name)
-- The optional "rest" MatchMapping parameter handles capturing extra mapping keys
| MatchAs(pattern? pattern, identifier? name)
| MatchOr(pattern* patterns)
attributes (int lineno, int col_offset, int end_lineno, int end_col_offset)
type_ignore = TypeIgnore(int lineno, string tag)
decorator = (expr expression)
}

View File

@@ -1,385 +0,0 @@
#-------------------------------------------------------------------------------
# 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.
builtin_types = {'identifier', 'string', 'int', 'constant'}
class AST:
def __repr__(self):
raise NotImplementedError
class Module(AST):
def __init__(self, name, dfns):
self.name = name
self.dfns = dfns
self.types = {type.name: type.value for type in dfns}
def __repr__(self):
return 'Module({0.name}, {0.dfns})'.format(self)
class Type(AST):
def __init__(self, name, value):
self.name = name
self.value = value
def __repr__(self):
return 'Type({0.name}, {0.value})'.format(self)
class Constructor(AST):
def __init__(self, name, fields=None):
self.name = name
self.fields = fields or []
def __repr__(self):
return 'Constructor({0.name}, {0.fields})'.format(self)
class Field(AST):
def __init__(self, type, name=None, seq=False, opt=False):
self.type = type
self.name = name
self.seq = seq
self.opt = opt
def __str__(self):
if self.seq:
extra = "*"
elif self.opt:
extra = "?"
else:
extra = ""
return "{}{} {}".format(self.type, extra, self.name)
def __repr__(self):
if self.seq:
extra = ", seq=True"
elif self.opt:
extra = ", opt=True"
else:
extra = ""
if self.name is None:
return 'Field({0.type}{1})'.format(self, extra)
else:
return 'Field({0.type}, {0.name}{1})'.format(self, extra)
class Sum(AST):
def __init__(self, types, attributes=None):
self.types = types
self.attributes = attributes or []
def __repr__(self):
if self.attributes:
return 'Sum({0.types}, {0.attributes})'.format(self)
else:
return 'Sum({0.types})'.format(self)
class Product(AST):
def __init__(self, fields, attributes=None):
self.fields = fields
self.attributes = attributes or []
def __repr__(self):
if self.attributes:
return 'Product({0.fields}, {0.attributes})'.format(self)
else:
return 'Product({0.fields})'.format(self)
# 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.
class VisitorBase(object):
"""Generic tree visitor for ASTs."""
def __init__(self):
self.cache = {}
def visit(self, obj, *args):
klass = obj.__class__
meth = self.cache.get(klass)
if meth is None:
methname = "visit" + klass.__name__
meth = getattr(self, methname, None)
self.cache[klass] = meth
if meth:
try:
meth(obj, *args)
except Exception as e:
print("Error visiting %r: %s" % (obj, e))
raise
class Check(VisitorBase):
"""A visitor that checks a parsed ASDL tree for correctness.
Errors are printed and accumulated.
"""
def __init__(self):
super(Check, self).__init__()
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:
print('Redefinition of constructor {}'.format(key))
print('Defined in {} and {}'.format(conflict, name))
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, []) # noqa
l.append(name)
def visitProduct(self, prod, name):
for f in prod.fields:
self.visit(f, name)
def check(mod):
"""Check the parsed ASDL tree for correctness.
Return True if success. For failure, the errors are printed out and False
is returned.
"""
v = Check()
v.visit(mod)
for t in v.types:
if t not in mod.types and t not in builtin_types:
v.errors += 1
uses = ", ".join(v.types[t])
print('Undefined type {}, used in {}'.format(t, uses))
return not v.errors
# 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, encoding="utf-8") 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()
else:
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)

View File

@@ -1,817 +0,0 @@
# spell-checker:words dfn dfns
# ! /usr/bin/env python
"""Generate Rust code from an ASDL description."""
import re
import sys
import textwrap
from argparse import ArgumentParser
from pathlib import Path
from typing import Any, Dict, Optional
import asdl
TABSIZE = 4
AUTO_GEN_MESSAGE = "// File automatically generated by {}.\n\n"
BUILTIN_TYPE_NAMES = {
"identifier": "Identifier",
"string": "String",
"int": "Int",
"constant": "Constant",
}
assert BUILTIN_TYPE_NAMES.keys() == asdl.builtin_types
BUILTIN_INT_NAMES = {
"simple": "bool",
"is_async": "bool",
"conversion": "ConversionFlag",
}
RENAME_MAP = {
"cmpop": "cmp_op",
"unaryop": "unary_op",
"boolop": "bool_op",
"excepthandler": "except_handler",
"withitem": "with_item",
}
RUST_KEYWORDS = {
"if",
"while",
"for",
"return",
"match",
"try",
"await",
"yield",
"in",
"mod",
"type",
}
attributes = [
asdl.Field("int", "lineno"),
asdl.Field("int", "col_offset"),
asdl.Field("int", "end_lineno"),
asdl.Field("int", "end_col_offset"),
]
ORIGINAL_NODE_WARNING = "NOTE: This type is different from original Python AST."
arg_with_default = asdl.Type(
"arg_with_default",
asdl.Product(
[
asdl.Field("arg", "def"),
asdl.Field(
"expr", "default", opt=True
), # order is important for cost-free borrow!
],
),
)
arg_with_default.doc = f"""
An alternative type of AST `arg`. This is used for each function argument that might have a default value.
Used by `Arguments` original type.
{ORIGINAL_NODE_WARNING}
""".strip()
alt_arguments = asdl.Type(
"alt:arguments",
asdl.Product(
[
asdl.Field("arg_with_default", "posonlyargs", seq=True),
asdl.Field("arg_with_default", "args", seq=True),
asdl.Field("arg", "vararg", opt=True),
asdl.Field("arg_with_default", "kwonlyargs", seq=True),
asdl.Field("arg", "kwarg", opt=True),
]
),
)
alt_arguments.doc = f"""
An alternative type of AST `arguments`. This is parser-friendly and human-friendly definition of function arguments.
This form also has advantage to implement pre-order traverse.
`defaults` and `kw_defaults` fields are removed and the default values are placed under each `arg_with_default` typed argument.
`vararg` and `kwarg` are still typed as `arg` because they never can have a default value.
The matching Python style AST type is [PythonArguments]. While [PythonArguments] has ordered `kwonlyargs` fields by
default existence, [Arguments] has location-ordered kwonlyargs fields.
{ORIGINAL_NODE_WARNING}
""".strip()
# Must be used only for rust types, not python types
CUSTOM_TYPES = [
alt_arguments,
arg_with_default,
]
CUSTOM_REPLACEMENTS = {
"arguments": alt_arguments,
}
CUSTOM_ATTACHMENTS = [
arg_with_default,
]
def maybe_custom(type):
return CUSTOM_REPLACEMENTS.get(type.name, type)
def rust_field_name(name):
name = rust_type_name(name)
return re.sub(r"(?<!^)(?=[A-Z])", "_", name).lower()
def rust_type_name(name):
"""Return a string for the C name of the type.
This function special cases the default types provided by asdl.
"""
name = RENAME_MAP.get(name, name)
if name in asdl.builtin_types:
builtin = BUILTIN_TYPE_NAMES[name]
return builtin
elif name.islower():
return "".join(part.capitalize() for part in name.split("_"))
else:
return name
def is_simple(sum):
"""Return True if a sum is a simple.
A sum is simple if its types have no fields, e.g.
unaryop = Invert | Not | UAdd | USub
"""
for t in sum.types:
if t.fields:
return False
return True
def asdl_of(name, obj):
if isinstance(obj, asdl.Product) or isinstance(obj, asdl.Constructor):
fields = ", ".join(map(str, obj.fields))
if fields:
fields = "({})".format(fields)
return "{}{}".format(name, fields)
else:
if is_simple(obj):
types = " | ".join(type.name for type in obj.types)
else:
sep = "\n{}| ".format(" " * (len(name) + 1))
types = sep.join(asdl_of(type.name, type) for type in obj.types)
return "{} = {}".format(name, types)
class TypeInfo:
type: asdl.Type
enum_name: Optional[str]
has_user_data: Optional[bool]
has_attributes: bool
is_simple: bool
children: set
fields: Optional[Any]
boxed: bool
def __init__(self, type):
self.type = type
self.enum_name = None
self.has_user_data = None
self.has_attributes = False
self.is_simple = False
self.children = set()
self.fields = None
self.boxed = False
def __repr__(self):
return f"<TypeInfo: {self.name}>"
@property
def name(self):
return self.type.name
@property
def is_type(self):
return isinstance(self.type, asdl.Type)
@property
def is_product(self):
return self.is_type and isinstance(self.type.value, asdl.Product)
@property
def is_sum(self):
return self.is_type and isinstance(self.type.value, asdl.Sum)
@property
def has_expr(self):
return self.is_product and any(
f.type != "identifier" for f in self.type.value.fields
)
@property
def is_custom(self):
return self.type.name in [t.name for t in CUSTOM_TYPES]
@property
def is_custom_replaced(self):
return self.type.name in CUSTOM_REPLACEMENTS
@property
def custom(self):
if self.type.name in CUSTOM_REPLACEMENTS:
return CUSTOM_REPLACEMENTS[self.type.name]
return self.type
def no_cfg(self, typeinfo):
if self.is_product:
return self.has_attributes
elif self.enum_name:
return typeinfo[self.enum_name].has_attributes
else:
return self.has_attributes
@property
def rust_name(self):
return rust_type_name(self.name)
@property
def full_field_name(self):
name = self.name
if name.startswith("alt:"):
name = name[4:]
if self.enum_name is None:
return name
else:
return f"{self.enum_name}_{rust_field_name(name)}"
@property
def full_type_name(self):
name = self.name
if name.startswith("alt:"):
name = name[4:]
rust_name = rust_type_name(name)
if self.enum_name is not None:
rust_name = rust_type_name(self.enum_name) + rust_name
if self.is_custom_replaced:
rust_name = "Python" + rust_name
return rust_name
def determine_user_data(self, type_info, stack):
if self.name in stack:
return None
stack.add(self.name)
for child, child_seq in self.children:
if child in asdl.builtin_types:
continue
child_info = type_info[child]
child_has_user_data = child_info.determine_user_data(type_info, stack)
if self.has_user_data is None and child_has_user_data is True:
self.has_user_data = True
stack.remove(self.name)
return self.has_user_data
class TypeInfoMixin:
type_info: Dict[str, TypeInfo]
def customized_type_info(self, type_name):
info = self.type_info[type_name]
return self.type_info[info.custom.name]
def has_user_data(self, typ):
return self.type_info[typ].has_user_data
class EmitVisitor(asdl.VisitorBase, TypeInfoMixin):
"""Visit that emits lines"""
def __init__(self, file, type_info):
self.file = file
self.type_info = type_info
self.identifiers = set()
super(EmitVisitor, self).__init__()
def emit_identifier(self, name):
name = str(name)
if name in self.identifiers:
return
self.emit("_Py_IDENTIFIER(%s);" % name, 0)
self.identifiers.add(name)
def emit(self, line, depth):
if line:
line = (" " * TABSIZE * depth) + textwrap.dedent(line)
self.file.write(line + "\n")
class FindUserDataTypesVisitor(asdl.VisitorBase):
def __init__(self, type_info):
self.type_info = type_info
super().__init__()
def visitModule(self, mod):
for dfn in mod.dfns + CUSTOM_TYPES:
self.visit(dfn)
stack = set()
for info in self.type_info.values():
info.determine_user_data(self.type_info, stack)
def visitType(self, type):
key = type.name
info = self.type_info[key] = TypeInfo(type)
self.visit(type.value, info)
def visitSum(self, sum, info):
type = info.type
info.is_simple = is_simple(sum)
for cons in sum.types:
self.visit(cons, type, info.is_simple)
if info.is_simple:
info.has_user_data = False
return
for t in sum.types:
self.add_children(t.name, t.fields)
if len(sum.types) > 1:
info.boxed = True
if sum.attributes:
# attributes means located, which has the `range: R` field
info.has_user_data = True
info.has_attributes = True
for variant in sum.types:
self.add_children(type.name, variant.fields)
def visitConstructor(self, cons, type, simple):
info = self.type_info[cons.name] = TypeInfo(cons)
info.enum_name = type.name
info.is_simple = simple
def visitProduct(self, product, info):
type = info.type
if product.attributes:
# attributes means located, which has the `range: R` field
info.has_user_data = True
info.has_attributes = True
if len(product.fields) > 2:
info.boxed = True
self.add_children(type.name, product.fields)
def add_children(self, name, fields):
self.type_info[name].children.update(
(field.type, field.seq) for field in fields
)
def rust_field(field_name):
if field_name in RUST_KEYWORDS:
field_name += "_"
return field_name
class StructVisitor(EmitVisitor):
"""Visitor to generate type-defs for AST."""
def __init__(self, *args, **kw):
super().__init__(*args, **kw)
def emit_attrs(self, depth):
self.emit("#[derive(Clone, Debug, PartialEq)]", depth)
def emit_range(self, has_attributes, depth):
self.emit("pub range: TextRange,", depth + 1)
def visitModule(self, mod):
self.emit_attrs(0)
self.emit(
"""
#[derive(is_macro::Is)]
pub enum Ast {
""",
0,
)
for dfn in mod.dfns:
info = self.customized_type_info(dfn.name)
dfn = info.custom
rust_name = info.full_type_name
if dfn.name == "mod":
# This is exceptional rule to other enums.
# Unlike other enums, this is justified because `Mod` is only used as
# the top node of parsing result and never a child node of other nodes.
# Because it will be very rarely used in very particular applications,
# "ast_" prefix to everywhere seems less useful.
self.emit('#[is(name = "module")]', 1)
self.emit(f"{rust_name}({rust_name}),", 1)
self.emit(
"""
}
impl Node for Ast {
const NAME: &'static str = "AST";
const FIELD_NAMES: &'static [&'static str] = &[];
}
""",
0,
)
for dfn in mod.dfns:
info = self.customized_type_info(dfn.name)
rust_name = info.full_type_name
self.emit(
f"""
impl From<{rust_name}> for Ast {{
fn from(node: {rust_name}) -> Self {{
Ast::{rust_name}(node)
}}
}}
""",
0,
)
for dfn in mod.dfns + CUSTOM_TYPES:
self.visit(dfn)
def visitType(self, type, depth=0):
if hasattr(type, "doc"):
doc = "/// " + type.doc.replace("\n", "\n/// ") + "\n"
else:
doc = f"/// See also [{type.name}](https://docs.python.org/3/library/ast.html#ast.{type.name})"
self.emit(doc, depth)
self.visit(type.value, type, depth)
def visitSum(self, sum, type, depth):
if is_simple(sum):
self.simple_sum(sum, type, depth)
else:
self.sum_with_constructors(sum, type, depth)
self.emit(
f"""
impl Node for {rust_type_name(type.name)} {{
const NAME: &'static str = "{type.name}";
const FIELD_NAMES: &'static [&'static str] = &[];
}}
""",
depth,
)
def simple_sum(self, sum, type, depth):
rust_name = rust_type_name(type.name)
self.emit_attrs(depth)
self.emit("#[derive(is_macro::Is, Copy, Hash, Eq)]", depth)
self.emit(f"pub enum {rust_name} {{", depth)
for cons in sum.types:
self.emit(f"{cons.name},", depth + 1)
self.emit("}", depth)
self.emit(f"impl {rust_name} {{", depth)
needs_escape = any(rust_field_name(t.name) in RUST_KEYWORDS for t in sum.types)
if needs_escape:
prefix = rust_field_name(type.name) + "_"
else:
prefix = ""
for cons in sum.types:
self.emit(
f"""
#[inline]
pub const fn {prefix}{rust_field_name(cons.name)}(&self) -> Option<{rust_name}{cons.name}> {{
match self {{
{rust_name}::{cons.name} => Some({rust_name}{cons.name}),
_ => None,
}}
}}
""",
depth,
)
self.emit("}", depth)
self.emit("", depth)
for cons in sum.types:
self.emit(
f"""
pub struct {rust_name}{cons.name};
impl From<{rust_name}{cons.name}> for {rust_name} {{
fn from(_: {rust_name}{cons.name}) -> Self {{
{rust_name}::{cons.name}
}}
}}
impl From<{rust_name}{cons.name}> for Ast {{
fn from(_: {rust_name}{cons.name}) -> Self {{
{rust_name}::{cons.name}.into()
}}
}}
impl Node for {rust_name}{cons.name} {{
const NAME: &'static str = "{cons.name}";
const FIELD_NAMES: &'static [&'static str] = &[];
}}
impl std::cmp::PartialEq<{rust_name}> for {rust_name}{cons.name} {{
#[inline]
fn eq(&self, other: &{rust_name}) -> bool {{
matches!(other, {rust_name}::{cons.name})
}}
}}
""",
0,
)
def sum_with_constructors(self, sum, type, depth):
type_info = self.type_info[type.name]
rust_name = rust_type_name(type.name)
self.emit_attrs(depth)
self.emit("#[derive(is_macro::Is)]", depth)
self.emit(f"pub enum {rust_name} {{", depth)
needs_escape = any(rust_field_name(t.name) in RUST_KEYWORDS for t in sum.types)
for t in sum.types:
if needs_escape:
self.emit(
f'#[is(name = "{rust_field_name(t.name)}_{rust_name.lower()}")]',
depth + 1,
)
self.emit(f"{t.name}({rust_name}{t.name}),", depth + 1)
self.emit("}", depth)
self.emit("", depth)
for t in sum.types:
self.sum_subtype_struct(type_info, t, rust_name, depth)
def sum_subtype_struct(self, sum_type_info, t, rust_name, depth):
self.emit(
f"""/// See also [{t.name}](https://docs.python.org/3/library/ast.html#ast.{t.name})""",
depth,
)
self.emit_attrs(depth)
payload_name = f"{rust_name}{t.name}"
self.emit(f"pub struct {payload_name} {{", depth)
self.emit_range(sum_type_info.has_attributes, depth)
for f in t.fields:
self.visit(f, sum_type_info, "pub ", depth + 1, t.name)
assert sum_type_info.has_attributes == self.type_info[t.name].no_cfg(
self.type_info
)
self.emit("}", depth)
field_names = [f'"{f.name}"' for f in t.fields]
self.emit(
f"""
impl Node for {payload_name} {{
const NAME: &'static str = "{t.name}";
const FIELD_NAMES: &'static [&'static str] = &[{', '.join(field_names)}];
}}
impl From<{payload_name}> for {rust_name} {{
fn from(payload: {payload_name}) -> Self {{
{rust_name}::{t.name}(payload)
}}
}}
impl From<{payload_name}> for Ast {{
fn from(payload: {payload_name}) -> Self {{
{rust_name}::from(payload).into()
}}
}}
""",
depth,
)
self.emit("", depth)
def visitConstructor(self, cons, parent, depth):
if cons.fields:
self.emit(f"{cons.name} {{", depth)
for f in cons.fields:
self.visit(f, parent, "", depth + 1, cons.name)
self.emit("},", depth)
else:
self.emit(f"{cons.name},", depth)
def visitField(self, field, parent, vis, depth, constructor=None):
try:
field_type = self.customized_type_info(field.type)
typ = field_type.full_type_name
except KeyError:
field_type = None
typ = rust_type_name(field.type)
if field_type and not field_type.is_simple:
typ = f"{typ}"
# don't box if we're doing Vec<T>, but do box if we're doing Vec<Option<Box<T>>>
if (
field_type
and field_type.boxed
and (not (parent.is_product or field.seq) or field.opt)
):
typ = f"Box<{typ}>"
if field.opt or (
# When a dictionary literal contains dictionary unpacking (e.g., `{**d}`),
# the expression to be unpacked goes in `values` with a `None` at the corresponding
# position in `keys`. To handle this, the type of `keys` needs to be `Option<Vec<T>>`.
constructor == "Dict"
and field.name == "keys"
):
typ = f"Option<{typ}>"
if field.seq:
typ = f"Vec<{typ}>"
if typ == "Int":
typ = BUILTIN_INT_NAMES.get(field.name, typ)
name = rust_field(field.name)
# Use a String, rather than an Identifier, for the `id` field of `Expr::Name`.
# Names already include a range, so there's no need to duplicate the span.
if name == "id":
typ = "String"
self.emit(f"{vis}{name}: {typ},", depth)
def visitProduct(self, product, type, depth):
type_info = self.type_info[type.name]
product_name = type_info.full_type_name
self.emit_attrs(depth)
self.emit(f"pub struct {product_name} {{", depth)
self.emit_range(product.attributes, depth + 1)
for f in product.fields:
self.visit(f, type_info, "pub ", depth + 1)
assert bool(product.attributes) == type_info.no_cfg(self.type_info)
self.emit("}", depth)
field_names = [f'"{f.name}"' for f in product.fields]
self.emit(
f"""
impl Node for {product_name} {{
const NAME: &'static str = "{type.name}";
const FIELD_NAMES: &'static [&'static str] = &[
{', '.join(field_names)}
];
}}
""",
depth,
)
class RangedDefVisitor(EmitVisitor):
def visitModule(self, mod):
for dfn in mod.dfns + CUSTOM_TYPES:
self.visit(dfn)
def visitType(self, type, depth=0):
self.visit(type.value, type.name, depth)
def visitSum(self, sum, name, depth):
info = self.type_info[name]
self.emit_type_alias(info)
if info.is_simple:
for ty in sum.types:
variant_info = self.type_info[ty.name]
self.emit_type_alias(variant_info)
return
sum_match_arms = ""
for ty in sum.types:
variant_info = self.type_info[ty.name]
sum_match_arms += (
f" Self::{variant_info.rust_name}(node) => node.range(),"
)
self.emit_type_alias(variant_info)
self.emit_ranged_impl(variant_info)
self.emit(
f"""
impl Ranged for crate::{info.full_type_name} {{
fn range(&self) -> TextRange {{
match self {{
{sum_match_arms}
}}
}}
}}
""".lstrip(),
0,
)
def visitProduct(self, product, name, depth):
info = self.type_info[name]
self.emit_type_alias(info)
self.emit_ranged_impl(info)
def emit_type_alias(self, info):
return # disable
self.emit(
f"pub type {info.full_type_name} = crate::generic::{info.full_type_name};",
0,
)
self.emit("", 0)
def emit_ranged_impl(self, info):
self.file.write(
f"""
impl Ranged for crate::generic::{info.full_type_name} {{
fn range(&self) -> TextRange {{
self.range
}}
}}
""".strip()
)
def write_ast_def(mod, type_info, f):
f.write("use crate::text_size::TextRange;")
StructVisitor(f, type_info).visit(mod)
def write_ranged_def(mod, type_info, f):
RangedDefVisitor(f, type_info).visit(mod)
def write_parse_def(mod, type_info, f):
for info in type_info.values():
if info.enum_name not in ["expr", "stmt"]:
continue
type_name = rust_type_name(info.enum_name)
cons_name = rust_type_name(info.name)
f.write(
f"""
impl Parse for ast::{info.full_type_name} {{
fn lex_starts_at(
source: &str,
offset: TextSize,
) -> SoftKeywordTransformer<Lexer<std::str::Chars>> {{
ast::{type_name}::lex_starts_at(source, offset)
}}
fn parse_tokens(
lxr: impl IntoIterator<Item = LexResult>,
source_path: &str,
) -> Result<Self, ParseError> {{
let node = ast::{type_name}::parse_tokens(lxr, source_path)?;
match node {{
ast::{type_name}::{cons_name}(node) => Ok(node),
node => Err(ParseError {{
error: ParseErrorType::InvalidToken,
offset: node.range().start(),
source_path: source_path.to_owned(),
}}),
}}
}}
}}
"""
)
def main(
input_filename,
ast_dir,
parser_dir,
dump_module=False,
):
auto_gen_msg = AUTO_GEN_MESSAGE.format("/".join(Path(__file__).parts[-2:]))
mod = asdl.parse(input_filename)
if dump_module:
print("Parsed Module:")
print(mod)
if not asdl.check(mod):
sys.exit(1)
type_info = {}
FindUserDataTypesVisitor(type_info).visit(mod)
from functools import partial as p
for filename, write in [
("generic", p(write_ast_def, mod, type_info)),
("ranged", p(write_ranged_def, mod, type_info)),
]:
with (ast_dir / f"{filename}.rs").open("w") as f:
f.write(auto_gen_msg)
write(f)
for filename, write in [
("parse", p(write_parse_def, mod, type_info)),
]:
with (parser_dir / f"{filename}.rs").open("w") as f:
f.write(auto_gen_msg)
write(f)
print(f"{ast_dir} regenerated.")
if __name__ == "__main__":
parser = ArgumentParser()
parser.add_argument("input_file", type=Path)
parser.add_argument("-A", "--ast-dir", type=Path, required=True)
parser.add_argument("-P", "--parser-dir", type=Path, required=True)
parser.add_argument("-d", "--dump-module", action="store_true")
args = parser.parse_args()
main(
args.input_file,
args.ast_dir,
args.parser_dir,
args.dump_module,
)

View File

@@ -1,4 +1,4 @@
//! `builtin_types` in asdl.py and Attributed
//! `builtin_types` in Attributed
use rustpython_parser_core::text_size::TextRange;

File diff suppressed because it is too large Load Diff

View File

@@ -1,502 +0,0 @@
// File automatically generated by ast/asdl_rs.py.
impl Ranged for crate::generic::ModModule {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::ModInteractive {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::ModExpression {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::ModFunctionType {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::Mod {
fn range(&self) -> TextRange {
match self {
Self::Module(node) => node.range(),
Self::Interactive(node) => node.range(),
Self::Expression(node) => node.range(),
Self::FunctionType(node) => node.range(),
}
}
}
impl Ranged for crate::generic::StmtFunctionDef {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::StmtAsyncFunctionDef {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::StmtClassDef {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::StmtReturn {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::StmtDelete {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::StmtAssign {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::StmtAugAssign {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::StmtAnnAssign {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::StmtFor {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::StmtAsyncFor {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::StmtWhile {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::StmtIf {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::StmtWith {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::StmtAsyncWith {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::StmtMatch {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::StmtRaise {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::StmtTry {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::StmtTryStar {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::StmtAssert {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::StmtImport {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::StmtImportFrom {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::StmtGlobal {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::StmtNonlocal {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::StmtExpr {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::StmtPass {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::StmtBreak {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::StmtContinue {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::Stmt {
fn range(&self) -> TextRange {
match self {
Self::FunctionDef(node) => node.range(),
Self::AsyncFunctionDef(node) => node.range(),
Self::ClassDef(node) => node.range(),
Self::Return(node) => node.range(),
Self::Delete(node) => node.range(),
Self::Assign(node) => node.range(),
Self::AugAssign(node) => node.range(),
Self::AnnAssign(node) => node.range(),
Self::For(node) => node.range(),
Self::AsyncFor(node) => node.range(),
Self::While(node) => node.range(),
Self::If(node) => node.range(),
Self::With(node) => node.range(),
Self::AsyncWith(node) => node.range(),
Self::Match(node) => node.range(),
Self::Raise(node) => node.range(),
Self::Try(node) => node.range(),
Self::TryStar(node) => node.range(),
Self::Assert(node) => node.range(),
Self::Import(node) => node.range(),
Self::ImportFrom(node) => node.range(),
Self::Global(node) => node.range(),
Self::Nonlocal(node) => node.range(),
Self::Expr(node) => node.range(),
Self::Pass(node) => node.range(),
Self::Break(node) => node.range(),
Self::Continue(node) => node.range(),
}
}
}
impl Ranged for crate::generic::ExprBoolOp {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::ExprNamedExpr {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::ExprBinOp {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::ExprUnaryOp {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::ExprLambda {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::ExprIfExp {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::ExprDict {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::ExprSet {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::ExprListComp {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::ExprSetComp {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::ExprDictComp {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::ExprGeneratorExp {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::ExprAwait {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::ExprYield {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::ExprYieldFrom {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::ExprCompare {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::ExprCall {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::ExprFormattedValue {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::ExprJoinedStr {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::ExprConstant {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::ExprAttribute {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::ExprSubscript {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::ExprStarred {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::ExprName {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::ExprList {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::ExprTuple {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::ExprSlice {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::Expr {
fn range(&self) -> TextRange {
match self {
Self::BoolOp(node) => node.range(),
Self::NamedExpr(node) => node.range(),
Self::BinOp(node) => node.range(),
Self::UnaryOp(node) => node.range(),
Self::Lambda(node) => node.range(),
Self::IfExp(node) => node.range(),
Self::Dict(node) => node.range(),
Self::Set(node) => node.range(),
Self::ListComp(node) => node.range(),
Self::SetComp(node) => node.range(),
Self::DictComp(node) => node.range(),
Self::GeneratorExp(node) => node.range(),
Self::Await(node) => node.range(),
Self::Yield(node) => node.range(),
Self::YieldFrom(node) => node.range(),
Self::Compare(node) => node.range(),
Self::Call(node) => node.range(),
Self::FormattedValue(node) => node.range(),
Self::JoinedStr(node) => node.range(),
Self::Constant(node) => node.range(),
Self::Attribute(node) => node.range(),
Self::Subscript(node) => node.range(),
Self::Starred(node) => node.range(),
Self::Name(node) => node.range(),
Self::List(node) => node.range(),
Self::Tuple(node) => node.range(),
Self::Slice(node) => node.range(),
}
}
}
impl Ranged for crate::generic::Comprehension {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::ExceptHandlerExceptHandler {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::ExceptHandler {
fn range(&self) -> TextRange {
match self {
Self::ExceptHandler(node) => node.range(),
}
}
}
impl Ranged for crate::generic::PythonArguments {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::Arg {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::Keyword {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::Alias {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::WithItem {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::MatchCase {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::PatternMatchValue {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::PatternMatchSingleton {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::PatternMatchSequence {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::PatternMatchMapping {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::PatternMatchClass {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::PatternMatchStar {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::PatternMatchAs {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::PatternMatchOr {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::Pattern {
fn range(&self) -> TextRange {
match self {
Self::MatchValue(node) => node.range(),
Self::MatchSingleton(node) => node.range(),
Self::MatchSequence(node) => node.range(),
Self::MatchMapping(node) => node.range(),
Self::MatchClass(node) => node.range(),
Self::MatchStar(node) => node.range(),
Self::MatchAs(node) => node.range(),
Self::MatchOr(node) => node.range(),
}
}
}
impl Ranged for crate::generic::TypeIgnoreTypeIgnore {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::TypeIgnore {
fn range(&self) -> TextRange {
match self {
Self::TypeIgnore(node) => node.range(),
}
}
}
impl Ranged for crate::generic::Decorator {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::Arguments {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::ArgWithDefault {
fn range(&self) -> TextRange {
self.range
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,3 +1,5 @@
// This file was originally generated from asdl by a python script, but we now edit it manually
use crate::text_size::{TextRange, TextSize};
pub use crate::builtin::*;
@@ -29,4 +31,503 @@ where
}
}
include!("gen/ranged.rs");
impl Ranged for crate::generic::ModModule {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::ModInteractive {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::ModExpression {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::ModFunctionType {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::Mod {
fn range(&self) -> TextRange {
match self {
Self::Module(node) => node.range(),
Self::Interactive(node) => node.range(),
Self::Expression(node) => node.range(),
Self::FunctionType(node) => node.range(),
}
}
}
impl Ranged for crate::generic::StmtFunctionDef {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::StmtAsyncFunctionDef {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::StmtClassDef {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::StmtReturn {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::StmtDelete {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::StmtAssign {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::StmtAugAssign {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::StmtAnnAssign {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::StmtFor {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::StmtAsyncFor {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::StmtWhile {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::StmtIf {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::StmtWith {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::StmtAsyncWith {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::StmtMatch {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::StmtRaise {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::StmtTry {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::StmtTryStar {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::StmtAssert {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::StmtImport {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::StmtImportFrom {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::StmtGlobal {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::StmtNonlocal {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::StmtExpr {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::StmtPass {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::StmtBreak {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::StmtContinue {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::Stmt {
fn range(&self) -> TextRange {
match self {
Self::FunctionDef(node) => node.range(),
Self::AsyncFunctionDef(node) => node.range(),
Self::ClassDef(node) => node.range(),
Self::Return(node) => node.range(),
Self::Delete(node) => node.range(),
Self::Assign(node) => node.range(),
Self::AugAssign(node) => node.range(),
Self::AnnAssign(node) => node.range(),
Self::For(node) => node.range(),
Self::AsyncFor(node) => node.range(),
Self::While(node) => node.range(),
Self::If(node) => node.range(),
Self::With(node) => node.range(),
Self::AsyncWith(node) => node.range(),
Self::Match(node) => node.range(),
Self::Raise(node) => node.range(),
Self::Try(node) => node.range(),
Self::TryStar(node) => node.range(),
Self::Assert(node) => node.range(),
Self::Import(node) => node.range(),
Self::ImportFrom(node) => node.range(),
Self::Global(node) => node.range(),
Self::Nonlocal(node) => node.range(),
Self::Expr(node) => node.range(),
Self::Pass(node) => node.range(),
Self::Break(node) => node.range(),
Self::Continue(node) => node.range(),
}
}
}
impl Ranged for crate::generic::ExprBoolOp {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::ExprNamedExpr {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::ExprBinOp {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::ExprUnaryOp {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::ExprLambda {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::ExprIfExp {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::ExprDict {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::ExprSet {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::ExprListComp {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::ExprSetComp {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::ExprDictComp {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::ExprGeneratorExp {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::ExprAwait {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::ExprYield {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::ExprYieldFrom {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::ExprCompare {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::ExprCall {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::ExprFormattedValue {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::ExprJoinedStr {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::ExprConstant {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::ExprAttribute {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::ExprSubscript {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::ExprStarred {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::ExprName {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::ExprList {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::ExprTuple {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::ExprSlice {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::Expr {
fn range(&self) -> TextRange {
match self {
Self::BoolOp(node) => node.range(),
Self::NamedExpr(node) => node.range(),
Self::BinOp(node) => node.range(),
Self::UnaryOp(node) => node.range(),
Self::Lambda(node) => node.range(),
Self::IfExp(node) => node.range(),
Self::Dict(node) => node.range(),
Self::Set(node) => node.range(),
Self::ListComp(node) => node.range(),
Self::SetComp(node) => node.range(),
Self::DictComp(node) => node.range(),
Self::GeneratorExp(node) => node.range(),
Self::Await(node) => node.range(),
Self::Yield(node) => node.range(),
Self::YieldFrom(node) => node.range(),
Self::Compare(node) => node.range(),
Self::Call(node) => node.range(),
Self::FormattedValue(node) => node.range(),
Self::JoinedStr(node) => node.range(),
Self::Constant(node) => node.range(),
Self::Attribute(node) => node.range(),
Self::Subscript(node) => node.range(),
Self::Starred(node) => node.range(),
Self::Name(node) => node.range(),
Self::List(node) => node.range(),
Self::Tuple(node) => node.range(),
Self::Slice(node) => node.range(),
}
}
}
impl Ranged for crate::generic::Comprehension {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::ExceptHandlerExceptHandler {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::ExceptHandler {
fn range(&self) -> TextRange {
match self {
Self::ExceptHandler(node) => node.range(),
}
}
}
impl Ranged for crate::generic::PythonArguments {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::Arg {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::Keyword {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::Alias {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::WithItem {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::MatchCase {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::PatternMatchValue {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::PatternMatchSingleton {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::PatternMatchSequence {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::PatternMatchMapping {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::PatternMatchClass {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::PatternMatchStar {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::PatternMatchAs {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::PatternMatchOr {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::Pattern {
fn range(&self) -> TextRange {
match self {
Self::MatchValue(node) => node.range(),
Self::MatchSingleton(node) => node.range(),
Self::MatchSequence(node) => node.range(),
Self::MatchMapping(node) => node.range(),
Self::MatchClass(node) => node.range(),
Self::MatchStar(node) => node.range(),
Self::MatchAs(node) => node.range(),
Self::MatchOr(node) => node.range(),
}
}
}
impl Ranged for crate::generic::TypeIgnoreTypeIgnore {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::TypeIgnore {
fn range(&self) -> TextRange {
match self {
Self::TypeIgnore(node) => node.range(),
}
}
}
impl Ranged for crate::generic::Decorator {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::Arguments {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::generic::ArgWithDefault {
fn range(&self) -> TextRange {
self.range
}
}

View File

@@ -1,4 +1,4 @@
// File automatically generated by ast/asdl_rs.py.
// This file was originally generated from asdl by a python script, but we now edit it manually
impl Parse for ast::StmtFunctionDef {
fn lex_starts_at(

View File

@@ -1,3 +1,2 @@
#!/bin/bash
cspell "ast/**/*.rs" "literal/**/*.rs" "core/**/*.rs" "parser/**/*.rs"
cspell ast/asdl_rs.py

View File

@@ -1,8 +0,0 @@
#!/bin/bash
set -e
cd "$(dirname "$(dirname "$0")")"
# rm ast/src/gen/*.rs
python ast/asdl_rs.py --ast-dir ast/src/gen/ --parser-dir parser/src/gen/ ast/Python.asdl
rustfmt ast/src/gen/*.rs parser/src/gen/*.rs