mirror of
https://github.com/RustPython/RustPython.git
synced 2026-06-02 19:39:49 +09:00
Implement asdl in the ast and parser
This commit is contained in:
26
Cargo.lock
generated
26
Cargo.lock
generated
@@ -292,15 +292,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "console"
|
||||
version = "0.12.0"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0b1aacfaffdbff75be81c15a399b4bedf78aaefe840e8af1d299ac2ade885d2"
|
||||
checksum = "7cc80946b3480f421c2f17ed1cb841753a371c7c5104f51d507e13f532c856aa"
|
||||
dependencies = [
|
||||
"encode_unicode",
|
||||
"lazy_static 1.4.0",
|
||||
"libc",
|
||||
"terminal_size",
|
||||
"termios",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
@@ -919,9 +918,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "insta"
|
||||
version = "1.3.0"
|
||||
version = "1.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "863bf97e7130bf788f29a99bc4073735af6b8ecc3da6a39c23b3a688d2d3109a"
|
||||
checksum = "bca6f2bcc5e2ce13f3652ecd05a643b986d035add3f0c38fbabd78f723b5f7e9"
|
||||
dependencies = [
|
||||
"console",
|
||||
"difference",
|
||||
@@ -929,6 +928,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_yaml",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1923,6 +1923,7 @@ name = "rustpython-parser"
|
||||
version = "0.1.2"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"insta",
|
||||
"lalrpop",
|
||||
"lalrpop-util",
|
||||
"log",
|
||||
@@ -2329,15 +2330,6 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termios"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "411c5bf740737c7918b8b1fe232dca4dc9f8e754b8ad5e20966814001ed0ac6b"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.11.0"
|
||||
@@ -2642,6 +2634,12 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9fde2f6a4bea1d6e007c4ad38c6839fa71cbb63b6dbf5b595aa38dc9b1093c11"
|
||||
|
||||
[[package]]
|
||||
name = "vcpkg"
|
||||
version = "0.2.10"
|
||||
|
||||
@@ -26,7 +26,7 @@ module Python
|
||||
| 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)
|
||||
| AnnAssign(expr target, expr annotation, expr? value, bool simple)
|
||||
|
||||
-- use 'orelse' because else is a keyword in target languages
|
||||
| For(expr target, expr iter, stmt* body, stmt* orelse, string? type_comment)
|
||||
@@ -41,7 +41,7 @@ module Python
|
||||
| Assert(expr test, expr? msg)
|
||||
|
||||
| Import(alias* names)
|
||||
| ImportFrom(identifier? module, alias* names, int? level)
|
||||
| ImportFrom(identifier? module, alias* names, int level)
|
||||
|
||||
| Global(identifier* names)
|
||||
| Nonlocal(identifier* names)
|
||||
@@ -58,7 +58,7 @@ module Python
|
||||
| UnaryOp(unaryop op, expr operand)
|
||||
| Lambda(arguments args, expr body)
|
||||
| IfExp(expr test, expr body, expr orelse)
|
||||
| Dict(expr* keys, expr* values)
|
||||
| Dict(expr?* keys, expr* values)
|
||||
| Set(expr* elts)
|
||||
| ListComp(expr elt, comprehension* generators)
|
||||
| SetComp(expr elt, comprehension* generators)
|
||||
@@ -72,7 +72,7 @@ module Python
|
||||
-- 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)
|
||||
| FormattedValue(expr value, conversion_flag? conversion, expr? format_spec)
|
||||
| JoinedStr(expr* values)
|
||||
| Constant(constant value, string? kind)
|
||||
|
||||
@@ -101,13 +101,13 @@ module Python
|
||||
|
||||
cmpop = Eq | NotEq | Lt | LtE | Gt | GtE | Is | IsNot | In | NotIn
|
||||
|
||||
comprehension = (expr target, expr iter, expr* ifs, int is_async)
|
||||
comprehension = (expr target, expr iter, expr* ifs, bool 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)
|
||||
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)
|
||||
|
||||
@@ -33,7 +33,7 @@ __all__ = [
|
||||
# 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'}
|
||||
builtin_types = {'identifier', 'string', 'int', 'constant', 'bool', 'conversion_flag'}
|
||||
|
||||
class AST:
|
||||
def __repr__(self):
|
||||
@@ -340,12 +340,12 @@ class ASDLParser:
|
||||
|
||||
def _parse_optional_field_quantifier(self):
|
||||
is_seq, is_opt = False, False
|
||||
if self.cur_token.kind == TokenKind.Question:
|
||||
is_opt = True
|
||||
self._advance()
|
||||
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):
|
||||
|
||||
427
ast/asdl_rs.py
427
ast/asdl_rs.py
@@ -1,5 +1,5 @@
|
||||
#! /usr/bin/env python
|
||||
"""Generate C code from an ASDL description."""
|
||||
"""Generate Rust code from an ASDL description."""
|
||||
|
||||
import os
|
||||
import sys
|
||||
@@ -11,63 +11,27 @@ from pathlib import Path
|
||||
import asdl
|
||||
|
||||
TABSIZE = 4
|
||||
MAX_COL = 80
|
||||
AUTOGEN_MESSAGE = "// File automatically generated by {}.\n\n"
|
||||
|
||||
def get_c_type(name):
|
||||
builtin_type_mapping = {
|
||||
'identifier': 'Ident',
|
||||
'string': 'String',
|
||||
'int': 'usize',
|
||||
'constant': 'Constant',
|
||||
'bool': 'bool',
|
||||
'conversion_flag': 'ConversionFlag',
|
||||
}
|
||||
assert builtin_type_mapping.keys() == asdl.builtin_types
|
||||
|
||||
def get_rust_type(name):
|
||||
"""Return a string for the C name of the type.
|
||||
|
||||
This function special cases the default types provided by asdl.
|
||||
"""
|
||||
if name in asdl.builtin_types:
|
||||
return name
|
||||
return builtin_type_mapping[name]
|
||||
else:
|
||||
return "%s_ty" % name
|
||||
|
||||
def reflow_lines(s, depth):
|
||||
"""Reflow the line s indented depth tabs.
|
||||
|
||||
Return a sequence of lines where no line extends beyond MAX_COL
|
||||
when properly indented. The first line is properly indented based
|
||||
exclusively on depth * TABSIZE. All following lines -- these are
|
||||
the reflowed lines generated by this function -- start at the same
|
||||
column as the first character beyond the opening { in the first
|
||||
line.
|
||||
"""
|
||||
size = MAX_COL - depth * TABSIZE
|
||||
if len(s) < size:
|
||||
return [s]
|
||||
|
||||
lines = []
|
||||
cur = s
|
||||
padding = ""
|
||||
while len(cur) > size:
|
||||
i = cur.rfind(' ', 0, size)
|
||||
# XXX this should be fixed for real
|
||||
if i == -1 and 'GeneratorExp' in cur:
|
||||
i = size + 3
|
||||
assert i != -1, "Impossible line %d to reflow: %r" % (size, s)
|
||||
lines.append(padding + cur[:i])
|
||||
if len(lines) == 1:
|
||||
# find new size based on brace
|
||||
j = cur.find('{', 0, i)
|
||||
if j >= 0:
|
||||
j += 2 # account for the brace and the space after it
|
||||
size -= j
|
||||
padding = " " * j
|
||||
else:
|
||||
j = cur.find('(', 0, i)
|
||||
if j >= 0:
|
||||
j += 1 # account for the paren (no space after it)
|
||||
size -= j
|
||||
padding = " " * j
|
||||
cur = cur[i+1:]
|
||||
else:
|
||||
lines.append(padding + cur)
|
||||
return lines
|
||||
|
||||
def reflow_c_string(s, depth):
|
||||
return '"%s"' % s.replace('\n', '\\n"\n%s"' % (' ' * depth * TABSIZE))
|
||||
return "".join(part.capitalize() for part in name.split("_"))
|
||||
|
||||
def is_simple(sum):
|
||||
"""Return True if a sum is a simple.
|
||||
@@ -102,32 +66,93 @@ class EmitVisitor(asdl.VisitorBase):
|
||||
def __init__(self, file):
|
||||
self.file = file
|
||||
self.identifiers = set()
|
||||
self.singletons = set()
|
||||
self.types = set()
|
||||
super(EmitVisitor, self).__init__()
|
||||
|
||||
def emit_identifier(self, name):
|
||||
self.identifiers.add(str(name))
|
||||
name = str(name)
|
||||
if name in self.identifiers:
|
||||
return
|
||||
self.emit("_Py_IDENTIFIER(%s);" % name, 0)
|
||||
self.identifiers.add(name)
|
||||
|
||||
def emit_singleton(self, name):
|
||||
self.singletons.add(str(name))
|
||||
def emit(self, line, depth):
|
||||
if line:
|
||||
line = (" " * TABSIZE * depth) + line
|
||||
self.file.write(line + "\n")
|
||||
|
||||
def emit_type(self, name):
|
||||
self.types.add(str(name))
|
||||
class TypeInfo:
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.has_userdata = None
|
||||
self.children = set()
|
||||
self.boxed = False
|
||||
|
||||
def emit(self, s, depth, reflow=True):
|
||||
# XXX reflow long lines?
|
||||
if reflow:
|
||||
lines = reflow_lines(s, depth)
|
||||
def __repr__(self):
|
||||
return f"<TypeInfo: {self.name}>"
|
||||
|
||||
def determine_userdata(self, typeinfo, 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
|
||||
childinfo = typeinfo[child]
|
||||
child_has_userdata = childinfo.determine_userdata(typeinfo, stack)
|
||||
if self.has_userdata is None and child_has_userdata is True:
|
||||
self.has_userdata = True
|
||||
|
||||
stack.remove(self.name)
|
||||
return self.has_userdata
|
||||
|
||||
class FindUserdataTypesVisitor(asdl.VisitorBase):
|
||||
def __init__(self, typeinfo):
|
||||
self.typeinfo = typeinfo
|
||||
super().__init__()
|
||||
|
||||
def visitModule(self, mod):
|
||||
for dfn in mod.dfns:
|
||||
self.visit(dfn)
|
||||
stack = set()
|
||||
for info in self.typeinfo.values():
|
||||
info.determine_userdata(self.typeinfo, stack)
|
||||
|
||||
def visitType(self, type):
|
||||
self.typeinfo[type.name] = TypeInfo(type.name)
|
||||
self.visit(type.value, type.name)
|
||||
|
||||
def visitSum(self, sum, name):
|
||||
info = self.typeinfo[name]
|
||||
if is_simple(sum):
|
||||
info.has_userdata = False
|
||||
else:
|
||||
lines = [s]
|
||||
for line in lines:
|
||||
if line:
|
||||
line = (" " * TABSIZE * depth) + line
|
||||
self.file.write(line + "\n")
|
||||
if len(sum.types) > 1:
|
||||
info.boxed = True
|
||||
if sum.attributes:
|
||||
# attributes means Located, which has the `custom: U` field
|
||||
info.has_userdata = True
|
||||
for variant in sum.types:
|
||||
self.add_children(name, variant.fields)
|
||||
|
||||
def visitProduct(self, product, name):
|
||||
info = self.typeinfo[name]
|
||||
if product.attributes:
|
||||
# attributes means Located, which has the `custom: U` field
|
||||
info.has_userdata = True
|
||||
if len(product.fields) > 2:
|
||||
info.boxed = True
|
||||
self.add_children(name, product.fields)
|
||||
|
||||
def add_children(self, name, fields):
|
||||
self.typeinfo[name].children.update((field.type, field.seq) for field in fields)
|
||||
|
||||
class StructVisitor(EmitVisitor):
|
||||
"""Visitor to generate typedefs for AST."""
|
||||
|
||||
def __init__(self, file, typeinfo):
|
||||
self.typeinfo = typeinfo
|
||||
super().__init__(file)
|
||||
|
||||
class TypeDefVisitor(EmitVisitor):
|
||||
def visitModule(self, mod):
|
||||
for dfn in mod.dfns:
|
||||
self.visit(dfn)
|
||||
@@ -141,128 +166,82 @@ class TypeDefVisitor(EmitVisitor):
|
||||
else:
|
||||
self.sum_with_constructors(sum, name, depth)
|
||||
|
||||
def emit_attrs(self, depth):
|
||||
self.emit("#[derive(Debug, PartialEq)]", depth)
|
||||
|
||||
def simple_sum(self, sum, name, depth):
|
||||
enum = []
|
||||
for i in range(len(sum.types)):
|
||||
type = sum.types[i]
|
||||
enum.append("%s=%d" % (type.name, i + 1))
|
||||
enums = ", ".join(enum)
|
||||
ctype = get_c_type(name)
|
||||
s = "typedef enum _%s { %s } %s;" % (name, enums, ctype)
|
||||
self.emit(s, depth)
|
||||
rustname = get_rust_type(name)
|
||||
self.emit_attrs(depth)
|
||||
self.emit(f"pub enum {rustname} {{", depth)
|
||||
for variant in sum.types:
|
||||
self.emit(f"{variant.name},", depth + 1)
|
||||
self.emit("}", depth)
|
||||
self.emit("", depth)
|
||||
|
||||
def sum_with_constructors(self, sum, name, depth):
|
||||
ctype = get_c_type(name)
|
||||
s = "typedef struct _%(name)s *%(ctype)s;" % locals()
|
||||
self.emit(s, depth)
|
||||
self.emit("", depth)
|
||||
|
||||
def visitProduct(self, product, name, depth):
|
||||
ctype = get_c_type(name)
|
||||
s = "typedef struct _%(name)s *%(ctype)s;" % locals()
|
||||
self.emit(s, depth)
|
||||
self.emit("", depth)
|
||||
|
||||
class SequenceDefVisitor(EmitVisitor):
|
||||
def visitModule(self, mod):
|
||||
for dfn in mod.dfns:
|
||||
self.visit(dfn)
|
||||
|
||||
def visitType(self, type, depth=0):
|
||||
self.visit(type.value, type.name, depth)
|
||||
|
||||
def visitSum(self, sum, name, depth):
|
||||
if is_simple(sum):
|
||||
return
|
||||
self.emit_sequence_constructor(name, depth)
|
||||
|
||||
def emit_sequence_constructor(self, name,depth):
|
||||
ctype = get_c_type(name)
|
||||
self.emit("""\
|
||||
typedef struct {
|
||||
_ASDL_SEQ_HEAD
|
||||
%(ctype)s typed_elements[1];
|
||||
} asdl_%(name)s_seq;""" % locals(), reflow=False, depth=depth)
|
||||
self.emit("", depth)
|
||||
self.emit("asdl_%(name)s_seq *_Py_asdl_%(name)s_seq_new(Py_ssize_t size, PyArena *arena);" % locals(), depth)
|
||||
self.emit("", depth)
|
||||
|
||||
def visitProduct(self, product, name, depth):
|
||||
self.emit_sequence_constructor(name, depth)
|
||||
|
||||
class StructVisitor(EmitVisitor):
|
||||
"""Visitor to generate typedefs for AST."""
|
||||
|
||||
def visitModule(self, mod):
|
||||
for dfn in mod.dfns:
|
||||
self.visit(dfn)
|
||||
|
||||
def visitType(self, type, depth=0):
|
||||
self.visit(type.value, type.name, depth)
|
||||
|
||||
def visitSum(self, sum, name, depth):
|
||||
if not is_simple(sum):
|
||||
self.sum_with_constructors(sum, name, depth)
|
||||
|
||||
def sum_with_constructors(self, sum, name, depth):
|
||||
def emit(s, depth=depth):
|
||||
self.emit(s % sys._getframe(1).f_locals, depth)
|
||||
enum = []
|
||||
for i in range(len(sum.types)):
|
||||
type = sum.types[i]
|
||||
enum.append("%s_kind=%d" % (type.name, i + 1))
|
||||
|
||||
emit("enum _%(name)s_kind {" + ", ".join(enum) + "};")
|
||||
|
||||
emit("struct _%(name)s {")
|
||||
emit("enum _%(name)s_kind kind;", depth + 1)
|
||||
emit("union {", depth + 1)
|
||||
typeinfo = self.typeinfo[name]
|
||||
generics, generics_applied = self.get_generics(name)
|
||||
enumname = rustname = get_rust_type(name)
|
||||
# all the attributes right now are for location, so if it has attrs we
|
||||
# can just wrap it in Located<>
|
||||
if sum.attributes:
|
||||
enumname = rustname + "Kind"
|
||||
self.emit_attrs(depth)
|
||||
self.emit(f"pub enum {enumname}{generics} {{", depth)
|
||||
for t in sum.types:
|
||||
self.visit(t, depth + 2)
|
||||
emit("} v;", depth + 1)
|
||||
for field in sum.attributes:
|
||||
# rudimentary attribute handling
|
||||
type = str(field.type)
|
||||
assert type in asdl.builtin_types, type
|
||||
emit("%s %s;" % (type, field.name), depth + 1);
|
||||
emit("};")
|
||||
emit("")
|
||||
|
||||
def visitConstructor(self, cons, depth):
|
||||
if cons.fields:
|
||||
self.emit("struct {", depth)
|
||||
for f in cons.fields:
|
||||
self.visit(f, depth + 1)
|
||||
self.emit("} %s;" % cons.name, depth)
|
||||
self.emit("", depth)
|
||||
|
||||
def visitField(self, field, depth):
|
||||
# XXX need to lookup field.type, because it might be something
|
||||
# like a builtin...
|
||||
ctype = get_c_type(field.type)
|
||||
name = field.name
|
||||
if field.seq:
|
||||
if field.type == 'cmpop':
|
||||
self.emit("asdl_int_seq *%(name)s;" % locals(), depth)
|
||||
else:
|
||||
_type = field.type
|
||||
self.emit("asdl_%(_type)s_seq *%(name)s;" % locals(), depth)
|
||||
else:
|
||||
self.emit("%(ctype)s %(name)s;" % locals(), depth)
|
||||
|
||||
def visitProduct(self, product, name, depth):
|
||||
self.emit("struct _%(name)s {" % locals(), depth)
|
||||
for f in product.fields:
|
||||
self.visit(f, depth + 1)
|
||||
for field in product.attributes:
|
||||
# rudimentary attribute handling
|
||||
type = str(field.type)
|
||||
assert type in asdl.builtin_types, type
|
||||
self.emit("%s %s;" % (type, field.name), depth + 1);
|
||||
self.emit("};", depth)
|
||||
self.visit(t, typeinfo, depth + 1)
|
||||
self.emit("}", depth)
|
||||
if sum.attributes:
|
||||
self.emit(f"pub type {rustname}<U = ()> = Located<{enumname}{generics_applied}, U>;", 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)
|
||||
self.emit("},", depth)
|
||||
else:
|
||||
self.emit(f"{cons.name},", depth)
|
||||
|
||||
def visitField(self, field, parent, vis, depth):
|
||||
typ = get_rust_type(field.type)
|
||||
fieldtype = self.typeinfo.get(field.type)
|
||||
if fieldtype and fieldtype.has_userdata:
|
||||
typ = f"{typ}<U>"
|
||||
# don't box if we're doing Vec<T>, but do box if we're doing Vec<Option<Box<T>>>
|
||||
if fieldtype and fieldtype.boxed and (not field.seq or field.opt):
|
||||
typ = f"Box<{typ}>"
|
||||
if field.opt:
|
||||
typ = f"Option<{typ}>"
|
||||
if field.seq:
|
||||
typ = f"Vec<{typ}>"
|
||||
name = field.name
|
||||
if name == 'type':
|
||||
name = 'type_'
|
||||
self.emit(f"{vis}{name}: {typ},", depth)
|
||||
|
||||
def visitProduct(self, product, name, depth):
|
||||
typeinfo = self.typeinfo[name]
|
||||
generics, generics_applied = self.get_generics(name)
|
||||
dataname = rustname = get_rust_type(name)
|
||||
if product.attributes:
|
||||
dataname = rustname + "Data"
|
||||
self.emit_attrs(depth)
|
||||
self.emit(f"pub struct {dataname}{generics} {{", depth)
|
||||
for f in product.fields:
|
||||
self.visit(f, typeinfo, "pub ", depth + 1)
|
||||
self.emit("}", depth)
|
||||
if product.attributes:
|
||||
# attributes should just be location info
|
||||
self.emit(f"pub type {rustname}<U = ()> = Located<{dataname}{generics_applied}, U>;", depth);
|
||||
self.emit("", depth)
|
||||
|
||||
def get_generics(self, typ):
|
||||
if self.typeinfo[typ].has_userdata:
|
||||
return "<U = ()>", "<U>"
|
||||
else:
|
||||
return "", ""
|
||||
|
||||
class PrototypeVisitor(EmitVisitor):
|
||||
"""Generate function prototypes for the .h file"""
|
||||
@@ -689,6 +668,8 @@ class PyTypesVisitor(PickleVisitor):
|
||||
|
||||
def visitModule(self, mod):
|
||||
self.emit("""
|
||||
_Py_IDENTIFIER(_fields);
|
||||
_Py_IDENTIFIER(_attributes);
|
||||
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
@@ -1181,16 +1162,16 @@ class StaticVisitor(PickleVisitor):
|
||||
class ObjVisitor(PickleVisitor):
|
||||
|
||||
def func_begin(self, name):
|
||||
ctype = get_c_type(name)
|
||||
ctype = get_rust_type(name)
|
||||
self.emit("PyObject*", 0)
|
||||
self.emit("ast2obj_%s(struct ast_state *state, void* _o)" % (name), 0)
|
||||
self.emit("ast2obj_%s(void* _o)" % (name), 0)
|
||||
self.emit("{", 0)
|
||||
self.emit("%s o = (%s)_o;" % (ctype, ctype), 1)
|
||||
self.emit("PyObject *result = NULL, *value = NULL;", 1)
|
||||
self.emit("PyTypeObject *tp;", 1)
|
||||
self.emit('if (!o) {', 1)
|
||||
self.emit("Py_RETURN_NONE;", 2)
|
||||
self.emit("}", 1)
|
||||
self.emit('', 0)
|
||||
|
||||
def func_end(self):
|
||||
self.emit("return result;", 1)
|
||||
@@ -1299,6 +1280,9 @@ PyObject* PyAST_mod2obj(mod_ty t)
|
||||
return NULL;
|
||||
}
|
||||
return ast2obj_mod(state, t);
|
||||
if (!init_types())
|
||||
return NULL;
|
||||
return ast2obj_mod(t);
|
||||
}
|
||||
|
||||
/* mode is 0 for "exec", 1 for "eval" and 2 for "single" input */
|
||||
@@ -1487,34 +1471,30 @@ get_ast_state(void)
|
||||
f.write(' return 1;\n')
|
||||
f.write('};\n\n')
|
||||
|
||||
def write_header(mod, f):
|
||||
f.write('#ifndef Py_PYTHON_AST_H\n')
|
||||
f.write('#define Py_PYTHON_AST_H\n')
|
||||
f.write('#ifdef __cplusplus\n')
|
||||
f.write('extern "C" {\n')
|
||||
f.write('#endif\n')
|
||||
def write_ast_def(mod, f):
|
||||
f.write('pub use crate::location::Location;\n')
|
||||
f.write('pub use crate::constant::*;\n')
|
||||
f.write('\n')
|
||||
f.write('#ifndef Py_LIMITED_API\n')
|
||||
f.write('#include "asdl.h"\n')
|
||||
f.write('type Ident = String;\n')
|
||||
f.write('\n')
|
||||
f.write('#undef Yield /* undefine macro conflicting with <winbase.h> */\n')
|
||||
f.write('\n')
|
||||
c = ChainOfVisitors(TypeDefVisitor(f),
|
||||
SequenceDefVisitor(f),
|
||||
StructVisitor(f))
|
||||
c.visit(mod)
|
||||
f.write("// Note: these macros affect function definitions, not only call sites.\n")
|
||||
PrototypeVisitor(f).visit(mod)
|
||||
f.write("\n")
|
||||
f.write("PyObject* PyAST_mod2obj(mod_ty t);\n")
|
||||
f.write("mod_ty PyAST_obj2mod(PyObject* ast, PyArena* arena, int mode);\n")
|
||||
f.write("int PyAST_Check(PyObject* obj);\n")
|
||||
f.write("#endif /* !Py_LIMITED_API */\n")
|
||||
f.write('\n')
|
||||
f.write('#ifdef __cplusplus\n')
|
||||
StructVisitor(f, {}).emit_attrs(0)
|
||||
f.write('pub struct Located<T, U = ()> {\n')
|
||||
f.write(' pub location: Location,\n')
|
||||
f.write(' pub custom: U,\n')
|
||||
f.write(' pub node: T,\n')
|
||||
f.write('}\n')
|
||||
f.write('#endif\n')
|
||||
f.write('#endif /* !Py_PYTHON_AST_H */\n')
|
||||
f.write('\n')
|
||||
f.write('impl<T> Located<T> {\n')
|
||||
f.write(' pub fn new(location: Location, node: T) -> Self {\n')
|
||||
f.write(' Self { location, custom: (), node }\n')
|
||||
f.write(' }\n')
|
||||
f.write('}\n')
|
||||
f.write('\n')
|
||||
|
||||
typeinfo = {}
|
||||
FindUserdataTypesVisitor(typeinfo).visit(mod)
|
||||
|
||||
StructVisitor(f, typeinfo).visit(mod)
|
||||
|
||||
|
||||
def write_internal_h_header(mod, f):
|
||||
@@ -1563,7 +1543,7 @@ def write_source(mod, f, internal_h_file):
|
||||
)
|
||||
v.visit(mod)
|
||||
|
||||
def main(input_filename, c_filename, h_filename, internal_h_filename, dump_module=False):
|
||||
def main(input_filename, ast_mod_filename, ast_def_filename, dump_module=False):
|
||||
auto_gen_msg = AUTOGEN_MESSAGE.format("/".join(Path(__file__).parts[-2:]))
|
||||
mod = asdl.parse(input_filename)
|
||||
if dump_module:
|
||||
@@ -1572,28 +1552,21 @@ def main(input_filename, c_filename, h_filename, internal_h_filename, dump_modul
|
||||
if not asdl.check(mod):
|
||||
sys.exit(1)
|
||||
|
||||
with c_filename.open("w") as c_file, \
|
||||
h_filename.open("w") as h_file, \
|
||||
internal_h_filename.open("w") as internal_h_file:
|
||||
c_file.write(auto_gen_msg)
|
||||
h_file.write(auto_gen_msg)
|
||||
internal_h_file.write(auto_gen_msg)
|
||||
with ast_def_filename.open("w") as def_file, \
|
||||
ast_mod_filename.open("w") as mod_file:
|
||||
def_file.write(auto_gen_msg)
|
||||
mod_file.write(auto_gen_msg)
|
||||
|
||||
write_internal_h_header(mod, internal_h_file)
|
||||
write_source(mod, c_file, internal_h_file)
|
||||
write_header(mod, h_file)
|
||||
write_internal_h_footer(mod, internal_h_file)
|
||||
write_ast_def(mod, def_file)
|
||||
|
||||
print(f"{c_filename}, {h_filename}, {internal_h_filename} regenerated.")
|
||||
print(f"{ast_def_filename}, {ast_mod_filename} regenerated.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = ArgumentParser()
|
||||
parser.add_argument("input_file", type=Path)
|
||||
parser.add_argument("-C", "--c-file", type=Path, required=True)
|
||||
parser.add_argument("-H", "--h-file", type=Path, required=True)
|
||||
parser.add_argument("-I", "--internal-h-file", type=Path, required=True)
|
||||
parser.add_argument("-M", "--mod-file", type=Path, required=True)
|
||||
parser.add_argument("-D", "--def-file", type=Path, required=True)
|
||||
parser.add_argument("-d", "--dump-module", action="store_true")
|
||||
|
||||
args = parser.parse_args()
|
||||
main(args.input_file, args.c_file, args.h_file,
|
||||
args.internal_h_file, args.dump_module)
|
||||
main(args.input_file, args.mod_file, args.def_file, args.dump_module)
|
||||
|
||||
556
ast/src/ast.rs
556
ast/src/ast.rs
@@ -1,556 +0,0 @@
|
||||
//! Implement abstract syntax tree (AST) nodes for the python language.
|
||||
//!
|
||||
//! Roughly equivalent to [the python AST](https://docs.python.org/3/library/ast.html)
|
||||
//! Many AST nodes have a location attribute, to determine the sourcecode
|
||||
//! location of the node.
|
||||
|
||||
pub use crate::location::Location;
|
||||
use num_bigint::BigInt;
|
||||
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Top<U = ()> {
|
||||
Program(Program<U>),
|
||||
Statement(Vec<Statement<U>>),
|
||||
Expression(Expression<U>),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
/// A full python program, it's a sequence of statements.
|
||||
pub struct Program<U = ()> {
|
||||
pub statements: Suite<U>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct ImportSymbol {
|
||||
pub symbol: String,
|
||||
pub alias: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct Located<T, U = ()> {
|
||||
pub location: Location,
|
||||
pub node: T,
|
||||
pub custom: U,
|
||||
}
|
||||
|
||||
pub type Statement<U = ()> = Located<StatementType<U>, U>;
|
||||
pub type Suite<U = ()> = Vec<Statement<U>>;
|
||||
|
||||
/// Abstract syntax tree nodes for python statements.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum StatementType<U = ()> {
|
||||
/// A [`break`](https://docs.python.org/3/reference/simple_stmts.html#the-break-statement) statement.
|
||||
Break,
|
||||
|
||||
/// A [`continue`](https://docs.python.org/3/reference/simple_stmts.html#the-continue-statement) statement.
|
||||
Continue,
|
||||
|
||||
/// A [`return`](https://docs.python.org/3/reference/simple_stmts.html#the-return-statement) statement.
|
||||
/// This is used to return from a function.
|
||||
Return { value: Option<Expression<U>> },
|
||||
|
||||
/// An [`import`](https://docs.python.org/3/reference/simple_stmts.html#the-import-statement) statement.
|
||||
Import { names: Vec<ImportSymbol> },
|
||||
|
||||
/// An [`import` `from`](https://docs.python.org/3/reference/simple_stmts.html#the-import-statement) statement.
|
||||
ImportFrom {
|
||||
level: usize,
|
||||
module: Option<String>,
|
||||
names: Vec<ImportSymbol>,
|
||||
},
|
||||
|
||||
/// A [`pass`](https://docs.python.org/3/reference/simple_stmts.html#pass) statement.
|
||||
Pass,
|
||||
|
||||
/// An [`assert`](https://docs.python.org/3/reference/simple_stmts.html#the-assert-statement) statement.
|
||||
Assert {
|
||||
test: Expression<U>,
|
||||
msg: Option<Expression<U>>,
|
||||
},
|
||||
|
||||
/// A `del` statement, to delete some variables.
|
||||
Delete { targets: Vec<Expression<U>> },
|
||||
|
||||
/// Variable assignment. Note that we can assign to multiple targets.
|
||||
Assign {
|
||||
targets: Vec<Expression<U>>,
|
||||
value: Expression<U>,
|
||||
},
|
||||
|
||||
/// Augmented assignment.
|
||||
AugAssign {
|
||||
target: Box<Expression<U>>,
|
||||
op: Operator,
|
||||
value: Box<Expression<U>>,
|
||||
},
|
||||
|
||||
/// A type annotated assignment.
|
||||
AnnAssign {
|
||||
target: Box<Expression<U>>,
|
||||
annotation: Box<Expression<U>>,
|
||||
value: Option<Expression<U>>,
|
||||
},
|
||||
|
||||
/// An expression used as a statement.
|
||||
Expression { expression: Expression<U> },
|
||||
|
||||
/// The [`global`](https://docs.python.org/3/reference/simple_stmts.html#the-global-statement) statement,
|
||||
/// to declare names as global variables.
|
||||
Global { names: Vec<String> },
|
||||
|
||||
/// A [`nonlocal`](https://docs.python.org/3/reference/simple_stmts.html#the-nonlocal-statement) statement,
|
||||
/// to declare names a non-local variables.
|
||||
Nonlocal { names: Vec<String> },
|
||||
|
||||
/// An [`if`](https://docs.python.org/3/reference/compound_stmts.html#the-if-statement) statement.
|
||||
If {
|
||||
test: Expression<U>,
|
||||
body: Suite<U>,
|
||||
orelse: Option<Suite<U>>,
|
||||
},
|
||||
|
||||
/// A [`while`](https://docs.python.org/3/reference/compound_stmts.html#the-while-statement) statement.
|
||||
While {
|
||||
test: Expression<U>,
|
||||
body: Suite<U>,
|
||||
orelse: Option<Suite<U>>,
|
||||
},
|
||||
|
||||
/// The [`with`](https://docs.python.org/3/reference/compound_stmts.html#the-with-statement) statement.
|
||||
With {
|
||||
is_async: bool,
|
||||
items: Vec<WithItem<U>>,
|
||||
body: Suite<U>,
|
||||
},
|
||||
|
||||
/// A [`for`](https://docs.python.org/3/reference/compound_stmts.html#the-for-statement) statement.
|
||||
/// Contains the body of the loop, and the `else` clause.
|
||||
For {
|
||||
is_async: bool,
|
||||
target: Box<Expression<U>>,
|
||||
iter: Box<Expression<U>>,
|
||||
body: Suite<U>,
|
||||
orelse: Option<Suite<U>>,
|
||||
},
|
||||
|
||||
/// A `raise` statement.
|
||||
Raise {
|
||||
exception: Option<Expression<U>>,
|
||||
cause: Option<Expression<U>>,
|
||||
},
|
||||
|
||||
/// A [`try`](https://docs.python.org/3/reference/compound_stmts.html#the-try-statement) statement.
|
||||
Try {
|
||||
body: Suite<U>,
|
||||
handlers: Vec<ExceptHandler<U>>,
|
||||
orelse: Option<Suite<U>>,
|
||||
finalbody: Option<Suite<U>>,
|
||||
},
|
||||
|
||||
/// A [class definition](https://docs.python.org/3/reference/compound_stmts.html#class-definitions).
|
||||
ClassDef {
|
||||
name: String,
|
||||
body: Suite<U>,
|
||||
bases: Vec<Expression<U>>,
|
||||
keywords: Vec<Keyword<U>>,
|
||||
decorator_list: Vec<Expression<U>>,
|
||||
},
|
||||
|
||||
/// A [function definition](https://docs.python.org/3/reference/compound_stmts.html#function-definitions).
|
||||
/// Contains the name of the function, it's body
|
||||
/// some decorators and formal parameters to the function.
|
||||
FunctionDef {
|
||||
is_async: bool,
|
||||
name: String,
|
||||
args: Box<Parameters<U>>,
|
||||
body: Suite<U>,
|
||||
decorator_list: Vec<Expression<U>>,
|
||||
returns: Option<Expression<U>>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct WithItem<U = ()> {
|
||||
pub context_expr: Expression<U>,
|
||||
pub optional_vars: Option<Expression<U>>,
|
||||
}
|
||||
|
||||
/// An expression at a given location in the sourcecode.
|
||||
pub type Expression<U = ()> = Located<ExpressionType<U>, U>;
|
||||
|
||||
/// A certain type of expression.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum ExpressionType<U = ()> {
|
||||
BoolOp {
|
||||
op: BooleanOperator,
|
||||
values: Vec<Expression<U>>,
|
||||
},
|
||||
|
||||
/// A binary operation on two operands.
|
||||
Binop {
|
||||
a: Box<Expression<U>>,
|
||||
op: Operator,
|
||||
b: Box<Expression<U>>,
|
||||
},
|
||||
|
||||
/// Subscript operation.
|
||||
Subscript {
|
||||
a: Box<Expression<U>>,
|
||||
b: Box<Expression<U>>,
|
||||
},
|
||||
|
||||
/// An unary operation.
|
||||
Unop {
|
||||
op: UnaryOperator,
|
||||
a: Box<Expression<U>>,
|
||||
},
|
||||
|
||||
/// An await expression.
|
||||
Await {
|
||||
value: Box<Expression<U>>,
|
||||
},
|
||||
|
||||
/// A yield expression.
|
||||
Yield {
|
||||
value: Option<Box<Expression<U>>>,
|
||||
},
|
||||
|
||||
// A yield from expression.
|
||||
YieldFrom {
|
||||
value: Box<Expression<U>>,
|
||||
},
|
||||
|
||||
/// A chained comparison. Note that in python you can use
|
||||
/// `1 < a < 10` for example.
|
||||
Compare {
|
||||
vals: Vec<Expression<U>>,
|
||||
ops: Vec<Comparison>,
|
||||
},
|
||||
|
||||
/// Attribute access in the form of `value.name`.
|
||||
Attribute {
|
||||
value: Box<Expression<U>>,
|
||||
name: String,
|
||||
},
|
||||
|
||||
/// A call expression.
|
||||
Call {
|
||||
function: Box<Expression<U>>,
|
||||
args: Vec<Expression<U>>,
|
||||
keywords: Vec<Keyword<U>>,
|
||||
},
|
||||
|
||||
/// A numeric literal.
|
||||
Number {
|
||||
value: Number,
|
||||
},
|
||||
|
||||
/// A `list` literal value.
|
||||
List {
|
||||
elements: Vec<Expression<U>>,
|
||||
},
|
||||
|
||||
/// A `tuple` literal value.
|
||||
Tuple {
|
||||
elements: Vec<Expression<U>>,
|
||||
},
|
||||
|
||||
/// A `dict` literal value.
|
||||
/// For example: `{2: 'two', 3: 'three'}`
|
||||
Dict {
|
||||
elements: Vec<(Option<Expression<U>>, Expression<U>)>,
|
||||
},
|
||||
|
||||
/// A `set` literal.
|
||||
Set {
|
||||
elements: Vec<Expression<U>>,
|
||||
},
|
||||
|
||||
Comprehension {
|
||||
kind: Box<ComprehensionKind<U>>,
|
||||
generators: Vec<Comprehension<U>>,
|
||||
},
|
||||
|
||||
/// A starred expression.
|
||||
Starred {
|
||||
value: Box<Expression<U>>,
|
||||
},
|
||||
|
||||
/// A slice expression.
|
||||
Slice {
|
||||
elements: Vec<Expression<U>>,
|
||||
},
|
||||
|
||||
/// A string literal.
|
||||
String {
|
||||
value: StringGroup<U>,
|
||||
},
|
||||
|
||||
/// A bytes literal.
|
||||
Bytes {
|
||||
value: Vec<u8>,
|
||||
},
|
||||
|
||||
/// An identifier, designating a certain variable or type.
|
||||
Identifier {
|
||||
name: String,
|
||||
},
|
||||
|
||||
/// A `lambda` function expression.
|
||||
Lambda {
|
||||
args: Box<Parameters<U>>,
|
||||
body: Box<Expression<U>>,
|
||||
},
|
||||
|
||||
/// An if-expression.
|
||||
IfExpression {
|
||||
test: Box<Expression<U>>,
|
||||
body: Box<Expression<U>>,
|
||||
orelse: Box<Expression<U>>,
|
||||
},
|
||||
|
||||
// A named expression
|
||||
NamedExpression {
|
||||
left: Box<Expression<U>>,
|
||||
right: Box<Expression<U>>,
|
||||
},
|
||||
|
||||
/// The literal 'True'.
|
||||
True,
|
||||
|
||||
/// The literal 'False'.
|
||||
False,
|
||||
|
||||
// The literal `None`.
|
||||
None,
|
||||
|
||||
/// The ellipsis literal `...`.
|
||||
Ellipsis,
|
||||
}
|
||||
|
||||
impl<U> Expression<U> {
|
||||
/// Returns a short name for the node suitable for use in error messages.
|
||||
pub fn name(&self) -> &'static str {
|
||||
use self::ExpressionType::*;
|
||||
use self::StringGroup::*;
|
||||
|
||||
match &self.node {
|
||||
BoolOp { .. } | Binop { .. } | Unop { .. } => "operator",
|
||||
Subscript { .. } => "subscript",
|
||||
Await { .. } => "await expression",
|
||||
Yield { .. } | YieldFrom { .. } => "yield expression",
|
||||
Compare { .. } => "comparison",
|
||||
Attribute { .. } => "attribute",
|
||||
Call { .. } => "function call",
|
||||
Number { .. }
|
||||
| String {
|
||||
value: Constant { .. },
|
||||
}
|
||||
| Bytes { .. } => "literal",
|
||||
List { .. } => "list",
|
||||
Tuple { .. } => "tuple",
|
||||
Dict { .. } => "dict display",
|
||||
Set { .. } => "set display",
|
||||
Comprehension { kind, .. } => match **kind {
|
||||
ComprehensionKind::List { .. } => "list comprehension",
|
||||
ComprehensionKind::Dict { .. } => "dict comprehension",
|
||||
ComprehensionKind::Set { .. } => "set comprehension",
|
||||
ComprehensionKind::GeneratorExpression { .. } => "generator expression",
|
||||
},
|
||||
Starred { .. } => "starred",
|
||||
Slice { .. } => "slice",
|
||||
String {
|
||||
value: Joined { .. },
|
||||
}
|
||||
| String {
|
||||
value: FormattedValue { .. },
|
||||
} => "f-string expression",
|
||||
Identifier { .. } => "named expression",
|
||||
Lambda { .. } => "lambda",
|
||||
IfExpression { .. } => "conditional expression",
|
||||
True | False | None => "keyword",
|
||||
Ellipsis => "ellipsis",
|
||||
NamedExpression { .. } => "named expression",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Formal parameters to a function.
|
||||
///
|
||||
/// In cpython this is called arguments, but we choose parameters to
|
||||
/// distinguish between function parameters and actual call arguments.
|
||||
#[derive(Debug, PartialEq, Default)]
|
||||
pub struct Parameters<U = ()> {
|
||||
pub posonlyargs_count: usize,
|
||||
pub args: Vec<Parameter<U>>,
|
||||
pub kwonlyargs: Vec<Parameter<U>>,
|
||||
pub vararg: Varargs<U>, // Optionally we handle optionally named '*args' or '*'
|
||||
pub kwarg: Varargs<U>,
|
||||
pub defaults: Vec<Expression<U>>,
|
||||
pub kw_defaults: Vec<Option<Expression<U>>>,
|
||||
}
|
||||
|
||||
/// A single formal parameter to a function.
|
||||
#[derive(Debug, PartialEq, Default)]
|
||||
pub struct Parameter<U = ()> {
|
||||
pub location: Location,
|
||||
pub arg: String,
|
||||
pub annotation: Option<Box<Expression<U>>>,
|
||||
}
|
||||
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum ComprehensionKind<U = ()> {
|
||||
GeneratorExpression {
|
||||
element: Expression<U>,
|
||||
},
|
||||
List {
|
||||
element: Expression<U>,
|
||||
},
|
||||
Set {
|
||||
element: Expression<U>,
|
||||
},
|
||||
Dict {
|
||||
key: Expression<U>,
|
||||
value: Expression<U>,
|
||||
},
|
||||
}
|
||||
|
||||
/// A list/set/dict/generator compression.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct Comprehension<U = ()> {
|
||||
pub location: Location,
|
||||
pub target: Expression<U>,
|
||||
pub iter: Expression<U>,
|
||||
pub ifs: Vec<Expression<U>>,
|
||||
pub is_async: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct ArgumentList<U = ()> {
|
||||
pub args: Vec<Expression<U>>,
|
||||
pub keywords: Vec<Keyword<U>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct Keyword<U = ()> {
|
||||
pub name: Option<String>,
|
||||
pub value: Expression<U>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct ExceptHandler<U = ()> {
|
||||
pub location: Location,
|
||||
pub typ: Option<Expression<U>>,
|
||||
pub name: Option<String>,
|
||||
pub body: Suite<U>,
|
||||
}
|
||||
|
||||
/// An operator for a binary operation (an operation with two operands).
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Operator {
|
||||
Add,
|
||||
Sub,
|
||||
Mult,
|
||||
MatMult,
|
||||
Div,
|
||||
Mod,
|
||||
Pow,
|
||||
LShift,
|
||||
RShift,
|
||||
BitOr,
|
||||
BitXor,
|
||||
BitAnd,
|
||||
FloorDiv,
|
||||
}
|
||||
|
||||
/// A boolean operation.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum BooleanOperator {
|
||||
And,
|
||||
Or,
|
||||
}
|
||||
|
||||
/// An unary operator. This is an operation with only a single operand.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum UnaryOperator {
|
||||
Pos,
|
||||
Neg,
|
||||
Not,
|
||||
Inv,
|
||||
}
|
||||
|
||||
/// A comparison operation.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Comparison {
|
||||
Equal,
|
||||
NotEqual,
|
||||
Less,
|
||||
LessOrEqual,
|
||||
Greater,
|
||||
GreaterOrEqual,
|
||||
In,
|
||||
NotIn,
|
||||
Is,
|
||||
IsNot,
|
||||
}
|
||||
|
||||
/// A numeric literal.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Number {
|
||||
Integer { value: BigInt },
|
||||
Float { value: f64 },
|
||||
Complex { real: f64, imag: f64 },
|
||||
}
|
||||
|
||||
/// Transforms a value prior to formatting it.
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum ConversionFlag {
|
||||
/// Converts by calling `str(<value>)`.
|
||||
Str,
|
||||
/// Converts by calling `ascii(<value>)`.
|
||||
Ascii,
|
||||
/// Converts by calling `repr(<value>)`.
|
||||
Repr,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum StringGroup<U = ()> {
|
||||
Constant {
|
||||
value: String,
|
||||
},
|
||||
FormattedValue {
|
||||
value: Box<Expression<U>>,
|
||||
conversion: Option<ConversionFlag>,
|
||||
spec: Option<Box<StringGroup<U>>>,
|
||||
},
|
||||
Joined {
|
||||
values: Vec<StringGroup<U>>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Varargs<U = ()> {
|
||||
None,
|
||||
Unnamed,
|
||||
Named(Parameter<U>),
|
||||
}
|
||||
|
||||
impl<U> Default for Varargs<U> {
|
||||
fn default() -> Varargs<U> {
|
||||
Varargs::None
|
||||
}
|
||||
}
|
||||
|
||||
impl<U> From<Option<Option<Parameter<U>>>> for Varargs<U> {
|
||||
fn from(opt: Option<Option<Parameter<U>>>) -> Varargs<U> {
|
||||
match opt {
|
||||
Some(inner_opt) => match inner_opt {
|
||||
Some(param) => Varargs::Named(param),
|
||||
None => Varargs::Unnamed,
|
||||
},
|
||||
None => Varargs::None,
|
||||
}
|
||||
}
|
||||
}
|
||||
389
ast/src/ast_gen.rs
Normal file
389
ast/src/ast_gen.rs
Normal file
@@ -0,0 +1,389 @@
|
||||
// File automatically generated by ast/asdl_rs.py.
|
||||
|
||||
pub use crate::constant::*;
|
||||
pub use crate::location::Location;
|
||||
|
||||
type Ident = String;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct Located<T, U = ()> {
|
||||
pub location: Location,
|
||||
pub custom: U,
|
||||
pub node: T,
|
||||
}
|
||||
|
||||
impl<T> Located<T> {
|
||||
pub fn new(location: Location, node: T) -> Self {
|
||||
Self {
|
||||
location,
|
||||
custom: (),
|
||||
node,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Mod<U = ()> {
|
||||
Module {
|
||||
body: Vec<Stmt<U>>,
|
||||
type_ignores: Vec<TypeIgnore>,
|
||||
},
|
||||
Interactive {
|
||||
body: Vec<Stmt<U>>,
|
||||
},
|
||||
Expression {
|
||||
body: Box<Expr<U>>,
|
||||
},
|
||||
FunctionType {
|
||||
argtypes: Vec<Expr<U>>,
|
||||
returns: Box<Expr<U>>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum StmtKind<U = ()> {
|
||||
FunctionDef {
|
||||
name: Ident,
|
||||
args: Box<Arguments<U>>,
|
||||
body: Vec<Stmt<U>>,
|
||||
decorator_list: Vec<Expr<U>>,
|
||||
returns: Option<Box<Expr<U>>>,
|
||||
type_comment: Option<String>,
|
||||
},
|
||||
AsyncFunctionDef {
|
||||
name: Ident,
|
||||
args: Box<Arguments<U>>,
|
||||
body: Vec<Stmt<U>>,
|
||||
decorator_list: Vec<Expr<U>>,
|
||||
returns: Option<Box<Expr<U>>>,
|
||||
type_comment: Option<String>,
|
||||
},
|
||||
ClassDef {
|
||||
name: Ident,
|
||||
bases: Vec<Expr<U>>,
|
||||
keywords: Vec<Keyword<U>>,
|
||||
body: Vec<Stmt<U>>,
|
||||
decorator_list: Vec<Expr<U>>,
|
||||
},
|
||||
Return {
|
||||
value: Option<Box<Expr<U>>>,
|
||||
},
|
||||
Delete {
|
||||
targets: Vec<Expr<U>>,
|
||||
},
|
||||
Assign {
|
||||
targets: Vec<Expr<U>>,
|
||||
value: Box<Expr<U>>,
|
||||
type_comment: Option<String>,
|
||||
},
|
||||
AugAssign {
|
||||
target: Box<Expr<U>>,
|
||||
op: Operator,
|
||||
value: Box<Expr<U>>,
|
||||
},
|
||||
AnnAssign {
|
||||
target: Box<Expr<U>>,
|
||||
annotation: Box<Expr<U>>,
|
||||
value: Option<Box<Expr<U>>>,
|
||||
simple: bool,
|
||||
},
|
||||
For {
|
||||
target: Box<Expr<U>>,
|
||||
iter: Box<Expr<U>>,
|
||||
body: Vec<Stmt<U>>,
|
||||
orelse: Vec<Stmt<U>>,
|
||||
type_comment: Option<String>,
|
||||
},
|
||||
AsyncFor {
|
||||
target: Box<Expr<U>>,
|
||||
iter: Box<Expr<U>>,
|
||||
body: Vec<Stmt<U>>,
|
||||
orelse: Vec<Stmt<U>>,
|
||||
type_comment: Option<String>,
|
||||
},
|
||||
While {
|
||||
test: Box<Expr<U>>,
|
||||
body: Vec<Stmt<U>>,
|
||||
orelse: Vec<Stmt<U>>,
|
||||
},
|
||||
If {
|
||||
test: Box<Expr<U>>,
|
||||
body: Vec<Stmt<U>>,
|
||||
orelse: Vec<Stmt<U>>,
|
||||
},
|
||||
With {
|
||||
items: Vec<Withitem<U>>,
|
||||
body: Vec<Stmt<U>>,
|
||||
type_comment: Option<String>,
|
||||
},
|
||||
AsyncWith {
|
||||
items: Vec<Withitem<U>>,
|
||||
body: Vec<Stmt<U>>,
|
||||
type_comment: Option<String>,
|
||||
},
|
||||
Raise {
|
||||
exc: Option<Box<Expr<U>>>,
|
||||
cause: Option<Box<Expr<U>>>,
|
||||
},
|
||||
Try {
|
||||
body: Vec<Stmt<U>>,
|
||||
handlers: Vec<Excepthandler<U>>,
|
||||
orelse: Vec<Stmt<U>>,
|
||||
finalbody: Vec<Stmt<U>>,
|
||||
},
|
||||
Assert {
|
||||
test: Box<Expr<U>>,
|
||||
msg: Option<Box<Expr<U>>>,
|
||||
},
|
||||
Import {
|
||||
names: Vec<Alias>,
|
||||
},
|
||||
ImportFrom {
|
||||
module: Option<Ident>,
|
||||
names: Vec<Alias>,
|
||||
level: usize,
|
||||
},
|
||||
Global {
|
||||
names: Vec<Ident>,
|
||||
},
|
||||
Nonlocal {
|
||||
names: Vec<Ident>,
|
||||
},
|
||||
Expr {
|
||||
value: Box<Expr<U>>,
|
||||
},
|
||||
Pass,
|
||||
Break,
|
||||
Continue,
|
||||
}
|
||||
pub type Stmt<U = ()> = Located<StmtKind<U>, U>;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum ExprKind<U = ()> {
|
||||
BoolOp {
|
||||
op: Boolop,
|
||||
values: Vec<Expr<U>>,
|
||||
},
|
||||
NamedExpr {
|
||||
target: Box<Expr<U>>,
|
||||
value: Box<Expr<U>>,
|
||||
},
|
||||
BinOp {
|
||||
left: Box<Expr<U>>,
|
||||
op: Operator,
|
||||
right: Box<Expr<U>>,
|
||||
},
|
||||
UnaryOp {
|
||||
op: Unaryop,
|
||||
operand: Box<Expr<U>>,
|
||||
},
|
||||
Lambda {
|
||||
args: Box<Arguments<U>>,
|
||||
body: Box<Expr<U>>,
|
||||
},
|
||||
IfExp {
|
||||
test: Box<Expr<U>>,
|
||||
body: Box<Expr<U>>,
|
||||
orelse: Box<Expr<U>>,
|
||||
},
|
||||
Dict {
|
||||
keys: Vec<Option<Box<Expr<U>>>>,
|
||||
values: Vec<Expr<U>>,
|
||||
},
|
||||
Set {
|
||||
elts: Vec<Expr<U>>,
|
||||
},
|
||||
ListComp {
|
||||
elt: Box<Expr<U>>,
|
||||
generators: Vec<Comprehension<U>>,
|
||||
},
|
||||
SetComp {
|
||||
elt: Box<Expr<U>>,
|
||||
generators: Vec<Comprehension<U>>,
|
||||
},
|
||||
DictComp {
|
||||
key: Box<Expr<U>>,
|
||||
value: Box<Expr<U>>,
|
||||
generators: Vec<Comprehension<U>>,
|
||||
},
|
||||
GeneratorExp {
|
||||
elt: Box<Expr<U>>,
|
||||
generators: Vec<Comprehension<U>>,
|
||||
},
|
||||
Await {
|
||||
value: Box<Expr<U>>,
|
||||
},
|
||||
Yield {
|
||||
value: Option<Box<Expr<U>>>,
|
||||
},
|
||||
YieldFrom {
|
||||
value: Box<Expr<U>>,
|
||||
},
|
||||
Compare {
|
||||
left: Box<Expr<U>>,
|
||||
ops: Vec<Cmpop>,
|
||||
comparators: Vec<Expr<U>>,
|
||||
},
|
||||
Call {
|
||||
func: Box<Expr<U>>,
|
||||
args: Vec<Expr<U>>,
|
||||
keywords: Vec<Keyword<U>>,
|
||||
},
|
||||
FormattedValue {
|
||||
value: Box<Expr<U>>,
|
||||
conversion: Option<ConversionFlag>,
|
||||
format_spec: Option<Box<Expr<U>>>,
|
||||
},
|
||||
JoinedStr {
|
||||
values: Vec<Expr<U>>,
|
||||
},
|
||||
Constant {
|
||||
value: Constant,
|
||||
kind: Option<String>,
|
||||
},
|
||||
Attribute {
|
||||
value: Box<Expr<U>>,
|
||||
attr: Ident,
|
||||
ctx: ExprContext,
|
||||
},
|
||||
Subscript {
|
||||
value: Box<Expr<U>>,
|
||||
slice: Box<Expr<U>>,
|
||||
ctx: ExprContext,
|
||||
},
|
||||
Starred {
|
||||
value: Box<Expr<U>>,
|
||||
ctx: ExprContext,
|
||||
},
|
||||
Name {
|
||||
id: Ident,
|
||||
ctx: ExprContext,
|
||||
},
|
||||
List {
|
||||
elts: Vec<Expr<U>>,
|
||||
ctx: ExprContext,
|
||||
},
|
||||
Tuple {
|
||||
elts: Vec<Expr<U>>,
|
||||
ctx: ExprContext,
|
||||
},
|
||||
Slice {
|
||||
lower: Option<Box<Expr<U>>>,
|
||||
upper: Option<Box<Expr<U>>>,
|
||||
step: Option<Box<Expr<U>>>,
|
||||
},
|
||||
}
|
||||
pub type Expr<U = ()> = Located<ExprKind<U>, U>;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum ExprContext {
|
||||
Load,
|
||||
Store,
|
||||
Del,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Boolop {
|
||||
And,
|
||||
Or,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Operator {
|
||||
Add,
|
||||
Sub,
|
||||
Mult,
|
||||
MatMult,
|
||||
Div,
|
||||
Mod,
|
||||
Pow,
|
||||
LShift,
|
||||
RShift,
|
||||
BitOr,
|
||||
BitXor,
|
||||
BitAnd,
|
||||
FloorDiv,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Unaryop {
|
||||
Invert,
|
||||
Not,
|
||||
UAdd,
|
||||
USub,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Cmpop {
|
||||
Eq,
|
||||
NotEq,
|
||||
Lt,
|
||||
LtE,
|
||||
Gt,
|
||||
GtE,
|
||||
Is,
|
||||
IsNot,
|
||||
In,
|
||||
NotIn,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct Comprehension<U = ()> {
|
||||
pub target: Box<Expr<U>>,
|
||||
pub iter: Box<Expr<U>>,
|
||||
pub ifs: Vec<Expr<U>>,
|
||||
pub is_async: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum ExcepthandlerKind<U = ()> {
|
||||
ExceptHandler {
|
||||
type_: Option<Box<Expr<U>>>,
|
||||
name: Option<Ident>,
|
||||
body: Vec<Stmt<U>>,
|
||||
},
|
||||
}
|
||||
pub type Excepthandler<U = ()> = Located<ExcepthandlerKind<U>, U>;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct Arguments<U = ()> {
|
||||
pub posonlyargs: Vec<Arg<U>>,
|
||||
pub args: Vec<Arg<U>>,
|
||||
pub vararg: Option<Box<Arg<U>>>,
|
||||
pub kwonlyargs: Vec<Arg<U>>,
|
||||
pub kw_defaults: Vec<Option<Box<Expr<U>>>>,
|
||||
pub kwarg: Option<Box<Arg<U>>>,
|
||||
pub defaults: Vec<Expr<U>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct ArgData<U = ()> {
|
||||
pub arg: Ident,
|
||||
pub annotation: Option<Box<Expr<U>>>,
|
||||
pub type_comment: Option<String>,
|
||||
}
|
||||
pub type Arg<U = ()> = Located<ArgData<U>, U>;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct KeywordData<U = ()> {
|
||||
pub arg: Option<Ident>,
|
||||
pub value: Box<Expr<U>>,
|
||||
}
|
||||
pub type Keyword<U = ()> = Located<KeywordData<U>, U>;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct Alias {
|
||||
pub name: Ident,
|
||||
pub asname: Option<Ident>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct Withitem<U = ()> {
|
||||
pub context_expr: Box<Expr<U>>,
|
||||
pub optional_vars: Option<Box<Expr<U>>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum TypeIgnore {
|
||||
TypeIgnore { lineno: usize, tag: String },
|
||||
}
|
||||
41
ast/src/constant.rs
Normal file
41
ast/src/constant.rs
Normal file
@@ -0,0 +1,41 @@
|
||||
use num_bigint::BigInt;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Constant {
|
||||
None,
|
||||
Bool(bool),
|
||||
Str(String),
|
||||
Bytes(Vec<u8>),
|
||||
Int(BigInt),
|
||||
Tuple(Vec<Constant>),
|
||||
Float(f64),
|
||||
Complex { real: f64, imag: f64 },
|
||||
Ellipsis,
|
||||
}
|
||||
|
||||
impl From<String> for Constant {
|
||||
fn from(s: String) -> Constant {
|
||||
Self::Str(s)
|
||||
}
|
||||
}
|
||||
impl From<Vec<u8>> for Constant {
|
||||
fn from(b: Vec<u8>) -> Constant {
|
||||
Self::Bytes(b)
|
||||
}
|
||||
}
|
||||
impl From<bool> for Constant {
|
||||
fn from(b: bool) -> Constant {
|
||||
Self::Bool(b)
|
||||
}
|
||||
}
|
||||
|
||||
/// Transforms a value prior to formatting it.
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum ConversionFlag {
|
||||
/// Converts by calling `str(<value>)`.
|
||||
Str,
|
||||
/// Converts by calling `ascii(<value>)`.
|
||||
Ascii,
|
||||
/// Converts by calling `repr(<value>)`.
|
||||
Repr,
|
||||
}
|
||||
53
ast/src/impls.rs
Normal file
53
ast/src/impls.rs
Normal file
@@ -0,0 +1,53 @@
|
||||
use crate::{Constant, ExprKind};
|
||||
|
||||
impl<U> ExprKind<U> {
|
||||
/// Returns a short name for the node suitable for use in error messages.
|
||||
pub fn name(&self) -> &'static str {
|
||||
match self {
|
||||
ExprKind::BoolOp { .. } | ExprKind::BinOp { .. } | ExprKind::UnaryOp { .. } => {
|
||||
"operator"
|
||||
}
|
||||
ExprKind::Subscript { .. } => "subscript",
|
||||
ExprKind::Await { .. } => "await expression",
|
||||
ExprKind::Yield { .. } | ExprKind::YieldFrom { .. } => "yield expression",
|
||||
ExprKind::Compare { .. } => "comparison",
|
||||
ExprKind::Attribute { .. } => "attribute",
|
||||
ExprKind::Call { .. } => "function call",
|
||||
ExprKind::Constant { value, .. } => match value {
|
||||
Constant::Str(_)
|
||||
| Constant::Int(_)
|
||||
| Constant::Float(_)
|
||||
| Constant::Complex { .. }
|
||||
| Constant::Bytes(_) => "literal",
|
||||
Constant::Tuple(_) => "tuple",
|
||||
Constant::Bool(_) | Constant::None => "keyword",
|
||||
Constant::Ellipsis => "ellipsis",
|
||||
},
|
||||
ExprKind::List { .. } => "list",
|
||||
ExprKind::Tuple { .. } => "tuple",
|
||||
ExprKind::Dict { .. } => "dict display",
|
||||
ExprKind::Set { .. } => "set display",
|
||||
ExprKind::ListComp { .. } => "list comprehension",
|
||||
ExprKind::DictComp { .. } => "dict comprehension",
|
||||
ExprKind::SetComp { .. } => "set comprehension",
|
||||
ExprKind::GeneratorExp { .. } => "generator expression",
|
||||
ExprKind::Starred { .. } => "starred",
|
||||
ExprKind::Slice { .. } => "slice",
|
||||
ExprKind::JoinedStr { values } => {
|
||||
if values
|
||||
.iter()
|
||||
.any(|e| matches!(e.node, ExprKind::JoinedStr { .. }))
|
||||
{
|
||||
"f-string expression"
|
||||
} else {
|
||||
"literal"
|
||||
}
|
||||
}
|
||||
ExprKind::FormattedValue { .. } => "f-string expression",
|
||||
ExprKind::Name { .. } => "name",
|
||||
ExprKind::Lambda { .. } => "lambda",
|
||||
ExprKind::IfExp { .. } => "conditional expression",
|
||||
ExprKind::NamedExpr { .. } => "named expression",
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,9 @@
|
||||
mod ast;
|
||||
mod ast_gen;
|
||||
mod constant;
|
||||
mod impls;
|
||||
mod location;
|
||||
|
||||
pub use ast::*;
|
||||
pub use ast_gen::*;
|
||||
pub use location::Location;
|
||||
|
||||
pub type Suite<U = ()> = Vec<Stmt<U>>;
|
||||
|
||||
@@ -19,4 +19,4 @@ ahash = "0.6"
|
||||
|
||||
[dev-dependencies]
|
||||
rustpython-parser = { path = "../parser" }
|
||||
insta = "1.1"
|
||||
insta = "1.5"
|
||||
|
||||
@@ -22,3 +22,6 @@ unic-ucd-ident = "0.9"
|
||||
unicode_names2 = "0.4"
|
||||
phf = { version = "0.8", features = ["macros"] }
|
||||
ahash = "0.6"
|
||||
|
||||
[dev-dependencies]
|
||||
insta = "1.5"
|
||||
|
||||
@@ -2,25 +2,31 @@ use std::iter;
|
||||
use std::mem;
|
||||
use std::str;
|
||||
|
||||
use crate::ast::{ConversionFlag, Expression, Location, StringGroup};
|
||||
use crate::ast::{Constant, ConversionFlag, Expr, ExprKind, Location};
|
||||
use crate::error::{FStringError, FStringErrorType, ParseError};
|
||||
use crate::parser::parse_expression;
|
||||
|
||||
use self::FStringErrorType::*;
|
||||
use self::StringGroup::*;
|
||||
|
||||
struct FStringParser<'a> {
|
||||
chars: iter::Peekable<str::Chars<'a>>,
|
||||
str_location: Location,
|
||||
}
|
||||
|
||||
impl<'a> FStringParser<'a> {
|
||||
fn new(source: &'a str) -> Self {
|
||||
fn new(source: &'a str, str_location: Location) -> Self {
|
||||
Self {
|
||||
chars: source.chars().peekable(),
|
||||
str_location,
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_formatted_value(&mut self) -> Result<StringGroup, FStringErrorType> {
|
||||
#[inline]
|
||||
fn expr(&self, node: ExprKind) -> Expr {
|
||||
Expr::new(self.str_location, node)
|
||||
}
|
||||
|
||||
fn parse_formatted_value(&mut self) -> Result<Vec<Expr>, FStringErrorType> {
|
||||
let mut expression = String::new();
|
||||
let mut spec = None;
|
||||
let mut delims = Vec::new();
|
||||
@@ -114,20 +120,23 @@ impl<'a> FStringParser<'a> {
|
||||
if in_nested {
|
||||
return Err(UnclosedLbrace);
|
||||
}
|
||||
if nested {
|
||||
spec = Some(Box::new(FormattedValue {
|
||||
value: Box::new(
|
||||
parse_fstring_expr(&spec_expression)
|
||||
.map_err(|e| InvalidExpression(Box::new(e.error)))?,
|
||||
),
|
||||
conversion: None,
|
||||
spec: None,
|
||||
}))
|
||||
spec = Some(if nested {
|
||||
Box::new(
|
||||
self.expr(ExprKind::FormattedValue {
|
||||
value: Box::new(
|
||||
parse_fstring_expr(&spec_expression)
|
||||
.map_err(|e| InvalidExpression(Box::new(e.error)))?,
|
||||
),
|
||||
conversion: None,
|
||||
format_spec: None,
|
||||
}),
|
||||
)
|
||||
} else {
|
||||
spec = Some(Box::new(Constant {
|
||||
value: spec_expression.to_owned(),
|
||||
Box::new(self.expr(ExprKind::Constant {
|
||||
value: spec_expression.to_owned().into(),
|
||||
kind: None,
|
||||
}))
|
||||
}
|
||||
})
|
||||
}
|
||||
'(' | '{' | '[' => {
|
||||
expression.push(ch);
|
||||
@@ -155,35 +164,36 @@ impl<'a> FStringParser<'a> {
|
||||
if expression.is_empty() {
|
||||
return Err(EmptyExpression);
|
||||
}
|
||||
if pred_expression_text.is_empty() {
|
||||
return Ok(FormattedValue {
|
||||
let ret = if pred_expression_text.is_empty() {
|
||||
vec![self.expr(ExprKind::FormattedValue {
|
||||
value: Box::new(
|
||||
parse_fstring_expr(&expression)
|
||||
.map_err(|e| InvalidExpression(Box::new(e.error)))?,
|
||||
),
|
||||
conversion,
|
||||
spec,
|
||||
});
|
||||
format_spec: spec,
|
||||
})]
|
||||
} else {
|
||||
return Ok(Joined {
|
||||
values: vec![
|
||||
Constant {
|
||||
value: pred_expression_text + "=",
|
||||
},
|
||||
Constant {
|
||||
value: trailing_seq,
|
||||
},
|
||||
FormattedValue {
|
||||
value: Box::new(
|
||||
parse_fstring_expr(&expression)
|
||||
.map_err(|e| InvalidExpression(Box::new(e.error)))?,
|
||||
),
|
||||
conversion,
|
||||
spec,
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
vec![
|
||||
self.expr(ExprKind::Constant {
|
||||
value: Constant::Str(pred_expression_text + "="),
|
||||
kind: None,
|
||||
}),
|
||||
self.expr(ExprKind::Constant {
|
||||
value: trailing_seq.into(),
|
||||
kind: None,
|
||||
}),
|
||||
self.expr(ExprKind::FormattedValue {
|
||||
value: Box::new(
|
||||
parse_fstring_expr(&expression)
|
||||
.map_err(|e| InvalidExpression(Box::new(e.error)))?,
|
||||
),
|
||||
conversion,
|
||||
format_spec: spec,
|
||||
}),
|
||||
]
|
||||
};
|
||||
return Ok(ret);
|
||||
}
|
||||
'"' | '\'' => {
|
||||
expression.push(ch);
|
||||
@@ -205,7 +215,7 @@ impl<'a> FStringParser<'a> {
|
||||
Err(UnclosedLbrace)
|
||||
}
|
||||
|
||||
fn parse(mut self) -> Result<StringGroup, FStringErrorType> {
|
||||
fn parse(mut self) -> Result<Expr, FStringErrorType> {
|
||||
let mut content = String::new();
|
||||
let mut values = vec![];
|
||||
|
||||
@@ -217,12 +227,13 @@ impl<'a> FStringParser<'a> {
|
||||
content.push('{');
|
||||
} else {
|
||||
if !content.is_empty() {
|
||||
values.push(Constant {
|
||||
value: mem::replace(&mut content, String::new()),
|
||||
});
|
||||
values.push(self.expr(ExprKind::Constant {
|
||||
value: mem::take(&mut content).into(),
|
||||
kind: None,
|
||||
}));
|
||||
}
|
||||
|
||||
values.push(self.parse_formatted_value()?);
|
||||
values.extend(self.parse_formatted_value()?);
|
||||
}
|
||||
}
|
||||
'}' => {
|
||||
@@ -240,151 +251,96 @@ impl<'a> FStringParser<'a> {
|
||||
}
|
||||
|
||||
if !content.is_empty() {
|
||||
values.push(Constant { value: content })
|
||||
values.push(self.expr(ExprKind::Constant {
|
||||
value: content.into(),
|
||||
kind: None,
|
||||
}))
|
||||
}
|
||||
|
||||
Ok(match values.len() {
|
||||
0 => Constant {
|
||||
value: String::new(),
|
||||
},
|
||||
let s = match values.len() {
|
||||
0 => self.expr(ExprKind::Constant {
|
||||
value: String::new().into(),
|
||||
kind: None,
|
||||
}),
|
||||
1 => values.into_iter().next().unwrap(),
|
||||
_ => Joined { values },
|
||||
})
|
||||
_ => self.expr(ExprKind::JoinedStr { values }),
|
||||
};
|
||||
Ok(s)
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_fstring_expr(source: &str) -> Result<Expression, ParseError> {
|
||||
fn parse_fstring_expr(source: &str) -> Result<Expr, ParseError> {
|
||||
let fstring_body = format!("({})", source);
|
||||
let mut expression = parse_expression(&fstring_body)?;
|
||||
expression.location.go_left();
|
||||
Ok(expression)
|
||||
}
|
||||
|
||||
/// Parse an f-string into a string group.
|
||||
fn parse_fstring(source: &str) -> Result<StringGroup, FStringErrorType> {
|
||||
FStringParser::new(source).parse()
|
||||
parse_expression(&fstring_body)
|
||||
}
|
||||
|
||||
/// Parse an fstring from a string, located at a certain position in the sourcecode.
|
||||
/// In case of errors, we will get the location and the error returned.
|
||||
pub fn parse_located_fstring(
|
||||
source: &str,
|
||||
location: Location,
|
||||
) -> Result<StringGroup, FStringError> {
|
||||
parse_fstring(source).map_err(|error| FStringError { error, location })
|
||||
pub fn parse_located_fstring(source: &str, location: Location) -> Result<Expr, FStringError> {
|
||||
FStringParser::new(source, location)
|
||||
.parse()
|
||||
.map_err(|error| FStringError { error, location })
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::ast;
|
||||
|
||||
use super::*;
|
||||
|
||||
fn mk_ident(name: &str, row: usize, col: usize) -> ast::Expression {
|
||||
ast::Expression {
|
||||
location: ast::Location::new(row, col),
|
||||
custom: (),
|
||||
node: ast::ExpressionType::Identifier {
|
||||
name: name.to_owned(),
|
||||
},
|
||||
}
|
||||
fn parse_fstring(source: &str) -> Result<Expr, FStringErrorType> {
|
||||
FStringParser::new(source, Location::default()).parse()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_fstring() {
|
||||
let source = String::from("{a}{ b }{{foo}}");
|
||||
let source = "{a}{ b }{{foo}}";
|
||||
let parse_ast = parse_fstring(&source).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
parse_ast,
|
||||
Joined {
|
||||
values: vec![
|
||||
FormattedValue {
|
||||
value: Box::new(mk_ident("a", 1, 1)),
|
||||
conversion: None,
|
||||
spec: None,
|
||||
},
|
||||
FormattedValue {
|
||||
value: Box::new(mk_ident("b", 1, 2)),
|
||||
conversion: None,
|
||||
spec: None,
|
||||
},
|
||||
Constant {
|
||||
value: "{foo}".to_owned()
|
||||
}
|
||||
]
|
||||
}
|
||||
);
|
||||
insta::assert_debug_snapshot!(parse_ast);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_fstring_nested_spec() {
|
||||
let source = String::from("{foo:{spec}}");
|
||||
let source = "{foo:{spec}}";
|
||||
let parse_ast = parse_fstring(&source).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
parse_ast,
|
||||
FormattedValue {
|
||||
value: Box::new(mk_ident("foo", 1, 1)),
|
||||
conversion: None,
|
||||
spec: Some(Box::new(FormattedValue {
|
||||
value: Box::new(mk_ident("spec", 1, 1)),
|
||||
conversion: None,
|
||||
spec: None,
|
||||
})),
|
||||
}
|
||||
);
|
||||
insta::assert_debug_snapshot!(parse_ast);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_fstring_not_nested_spec() {
|
||||
let source = String::from("{foo:spec}");
|
||||
let source = "{foo:spec}";
|
||||
let parse_ast = parse_fstring(&source).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
parse_ast,
|
||||
FormattedValue {
|
||||
value: Box::new(mk_ident("foo", 1, 1)),
|
||||
conversion: None,
|
||||
spec: Some(Box::new(Constant {
|
||||
value: "spec".to_owned(),
|
||||
})),
|
||||
}
|
||||
);
|
||||
insta::assert_debug_snapshot!(parse_ast);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_empty_fstring() {
|
||||
assert_eq!(
|
||||
parse_fstring(""),
|
||||
Ok(Constant {
|
||||
value: String::new(),
|
||||
}),
|
||||
);
|
||||
insta::assert_debug_snapshot!(parse_fstring("").unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fstring_parse_selfdocumenting_base() {
|
||||
let src = String::from("{user=}");
|
||||
let parse_ast = parse_fstring(&src);
|
||||
let src = "{user=}";
|
||||
let parse_ast = parse_fstring(&src).unwrap();
|
||||
|
||||
assert!(parse_ast.is_ok());
|
||||
insta::assert_debug_snapshot!(parse_ast);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fstring_parse_selfdocumenting_base_more() {
|
||||
let src = String::from("mix {user=} with text and {second=}");
|
||||
let parse_ast = parse_fstring(&src);
|
||||
let src = "mix {user=} with text and {second=}";
|
||||
let parse_ast = parse_fstring(&src).unwrap();
|
||||
|
||||
assert!(parse_ast.is_ok());
|
||||
insta::assert_debug_snapshot!(parse_ast);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fstring_parse_selfdocumenting_format() {
|
||||
let src = String::from("{user=:>10}");
|
||||
let parse_ast = parse_fstring(&src);
|
||||
let src = "{user=:>10}";
|
||||
let parse_ast = parse_fstring(&src).unwrap();
|
||||
|
||||
assert!(parse_ast.is_ok());
|
||||
insta::assert_debug_snapshot!(parse_ast);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -414,36 +370,36 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_parse_fstring_not_equals() {
|
||||
let source = String::from("{1 != 2}");
|
||||
let parse_ast = parse_fstring(&source);
|
||||
assert!(parse_ast.is_ok());
|
||||
let source = "{1 != 2}";
|
||||
let parse_ast = parse_fstring(&source).unwrap();
|
||||
insta::assert_debug_snapshot!(parse_ast);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_fstring_equals() {
|
||||
let source = String::from("{42 == 42}");
|
||||
let parse_ast = parse_fstring(&source);
|
||||
assert!(parse_ast.is_ok());
|
||||
let source = "{42 == 42}";
|
||||
let parse_ast = parse_fstring(&source).unwrap();
|
||||
insta::assert_debug_snapshot!(parse_ast);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_fstring_selfdoc_prec_space() {
|
||||
let source = String::from("{x =}");
|
||||
let parse_ast = parse_fstring(&source);
|
||||
assert!(parse_ast.is_ok());
|
||||
let source = "{x =}";
|
||||
let parse_ast = parse_fstring(&source).unwrap();
|
||||
insta::assert_debug_snapshot!(parse_ast);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_fstring_selfdoc_trailing_space() {
|
||||
let source = String::from("{x= }");
|
||||
let parse_ast = parse_fstring(&source);
|
||||
assert!(parse_ast.is_ok());
|
||||
let source = "{x= }";
|
||||
let parse_ast = parse_fstring(&source).unwrap();
|
||||
insta::assert_debug_snapshot!(parse_ast);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_fstring_yield_expr() {
|
||||
let source = String::from("{yield}");
|
||||
let parse_ast = parse_fstring(&source);
|
||||
assert!(parse_ast.is_ok());
|
||||
let source = "{yield}";
|
||||
let parse_ast = parse_fstring(&source).unwrap();
|
||||
insta::assert_debug_snapshot!(parse_ast);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,16 +4,22 @@ use std::collections::HashSet;
|
||||
use crate::ast;
|
||||
use crate::error::{LexicalError, LexicalErrorType};
|
||||
|
||||
type ParameterDefs = (Vec<ast::Parameter>, Vec<ast::Expression>);
|
||||
type ParameterDef = (ast::Parameter, Option<ast::Expression>);
|
||||
pub struct ArgumentList {
|
||||
pub args: Vec<ast::Expr>,
|
||||
pub keywords: Vec<ast::Keyword>,
|
||||
}
|
||||
|
||||
type ParameterDefs = (Vec<ast::Arg>, Vec<ast::Arg>, Vec<ast::Expr>);
|
||||
type ParameterDef = (ast::Arg, Option<ast::Expr>);
|
||||
|
||||
pub fn parse_params(
|
||||
params: (Vec<ParameterDef>, Vec<ParameterDef>),
|
||||
) -> Result<ParameterDefs, LexicalError> {
|
||||
let mut names = vec![];
|
||||
let mut posonly = Vec::with_capacity(params.0.len());
|
||||
let mut names = Vec::with_capacity(params.1.len());
|
||||
let mut defaults = vec![];
|
||||
|
||||
let mut try_default = |name: &ast::Parameter, default| {
|
||||
let mut try_default = |name: &ast::Arg, default| {
|
||||
if let Some(default) = default {
|
||||
defaults.push(default);
|
||||
} else if !defaults.is_empty() {
|
||||
@@ -29,7 +35,7 @@ pub fn parse_params(
|
||||
|
||||
for (name, default) in params.0 {
|
||||
try_default(&name, default)?;
|
||||
names.push(name);
|
||||
posonly.push(name);
|
||||
}
|
||||
|
||||
for (name, default) in params.1 {
|
||||
@@ -37,31 +43,37 @@ pub fn parse_params(
|
||||
names.push(name);
|
||||
}
|
||||
|
||||
Ok((names, defaults))
|
||||
Ok((posonly, names, defaults))
|
||||
}
|
||||
|
||||
type FunctionArgument = (Option<Option<String>>, ast::Expression);
|
||||
type FunctionArgument = (Option<(ast::Location, Option<String>)>, ast::Expr);
|
||||
|
||||
pub fn parse_args(func_args: Vec<FunctionArgument>) -> Result<ast::ArgumentList, LexicalError> {
|
||||
pub fn parse_args(func_args: Vec<FunctionArgument>) -> Result<ArgumentList, LexicalError> {
|
||||
let mut args = vec![];
|
||||
let mut keywords = vec![];
|
||||
|
||||
let mut keyword_names = HashSet::with_capacity_and_hasher(func_args.len(), RandomState::new());
|
||||
for (name, value) in func_args {
|
||||
match name {
|
||||
Some(n) => {
|
||||
if let Some(keyword_name) = n.clone() {
|
||||
if keyword_names.contains(&keyword_name) {
|
||||
Some((location, name)) => {
|
||||
if let Some(keyword_name) = &name {
|
||||
if keyword_names.contains(keyword_name) {
|
||||
return Err(LexicalError {
|
||||
error: LexicalErrorType::DuplicateKeywordArgumentError,
|
||||
location: value.location,
|
||||
location,
|
||||
});
|
||||
}
|
||||
|
||||
keyword_names.insert(keyword_name.clone());
|
||||
}
|
||||
|
||||
keywords.push(ast::Keyword { name: n, value });
|
||||
keywords.push(ast::Keyword::new(
|
||||
location,
|
||||
ast::KeywordData {
|
||||
arg: name,
|
||||
value: Box::new(value),
|
||||
},
|
||||
));
|
||||
}
|
||||
None => {
|
||||
// Allow starred args after keyword arguments.
|
||||
@@ -76,9 +88,9 @@ pub fn parse_args(func_args: Vec<FunctionArgument>) -> Result<ast::ArgumentList,
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(ast::ArgumentList { args, keywords })
|
||||
Ok(ArgumentList { args, keywords })
|
||||
}
|
||||
|
||||
fn is_starred(exp: &ast::Expression) -> bool {
|
||||
matches!(exp.node, ast::ExpressionType::Starred { .. })
|
||||
fn is_starred(exp: &ast::Expr) -> bool {
|
||||
matches!(exp.node, ast::ExprKind::Starred { .. })
|
||||
}
|
||||
|
||||
@@ -111,9 +111,17 @@ pub static KEYWORDS: phf::Map<&'static str, Tok> = phf::phf_map! {
|
||||
pub type Spanned = (Location, Tok, Location);
|
||||
pub type LexResult = Result<Spanned, LexicalError>;
|
||||
|
||||
#[inline]
|
||||
pub fn make_tokenizer(source: &str) -> impl Iterator<Item = LexResult> + '_ {
|
||||
make_tokenizer_located(source, Location::new(0, 0))
|
||||
}
|
||||
|
||||
pub fn make_tokenizer_located(
|
||||
source: &str,
|
||||
start_location: Location,
|
||||
) -> impl Iterator<Item = LexResult> + '_ {
|
||||
let nlh = NewlineHandler::new(source.chars());
|
||||
Lexer::new(nlh)
|
||||
Lexer::new(nlh, start_location)
|
||||
}
|
||||
|
||||
// The newline handler is an iterator which collapses different newline
|
||||
@@ -177,7 +185,7 @@ impl<T> Lexer<T>
|
||||
where
|
||||
T: Iterator<Item = char>,
|
||||
{
|
||||
pub fn new(input: T) -> Self {
|
||||
pub fn new(input: T, start: Location) -> Self {
|
||||
let mut lxr = Lexer {
|
||||
chars: input,
|
||||
at_begin_of_line: true,
|
||||
@@ -185,7 +193,7 @@ where
|
||||
indentation_stack: vec![Default::default()],
|
||||
pending: Vec::new(),
|
||||
chr0: None,
|
||||
location: Location::new(0, 0),
|
||||
location: start,
|
||||
chr1: None,
|
||||
chr2: None,
|
||||
};
|
||||
|
||||
@@ -1,15 +1,28 @@
|
||||
use crate::token::Tok;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum Mode {
|
||||
Program,
|
||||
Statement,
|
||||
Module,
|
||||
Interactive,
|
||||
Expression,
|
||||
}
|
||||
|
||||
impl Mode {
|
||||
pub(crate) fn to_marker(self) -> Tok {
|
||||
match self {
|
||||
Self::Module => Tok::StartModule,
|
||||
Self::Interactive => Tok::StartInteractive,
|
||||
Self::Expression => Tok::StartExpression,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::str::FromStr for Mode {
|
||||
type Err = ModeParseError;
|
||||
fn from_str(s: &str) -> Result<Self, ModeParseError> {
|
||||
match s {
|
||||
"exec" | "single" => Ok(Mode::Program),
|
||||
"eval" => Ok(Mode::Statement),
|
||||
"exec" | "single" => Ok(Mode::Module),
|
||||
"eval" => Ok(Mode::Expression),
|
||||
_ => Err(ModeParseError { _priv: () }),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ use crate::error::ParseError;
|
||||
use crate::lexer;
|
||||
pub use crate::mode::Mode;
|
||||
use crate::python;
|
||||
use crate::token;
|
||||
|
||||
/*
|
||||
* Parse python code.
|
||||
@@ -20,33 +19,12 @@ use crate::token;
|
||||
* https://github.com/antlr/grammars-v4/tree/master/python3
|
||||
*/
|
||||
|
||||
macro_rules! do_lalr_parsing {
|
||||
($input: expr, $pat: ident, $tok: ident) => {{
|
||||
let lxr = lexer::make_tokenizer($input);
|
||||
let marker_token = (Default::default(), token::Tok::$tok, Default::default());
|
||||
let tokenizer = iter::once(Ok(marker_token)).chain(lxr);
|
||||
|
||||
match python::TopParser::new().parse(tokenizer) {
|
||||
Err(err) => Err(ParseError::from(err)),
|
||||
Ok(top) => {
|
||||
if let ast::Top::$pat(x) = top {
|
||||
Ok(x)
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
/// Parse a full python program, containing usually multiple lines.
|
||||
pub fn parse_program(source: &str) -> Result<ast::Program, ParseError> {
|
||||
do_lalr_parsing!(source, Program, StartProgram)
|
||||
}
|
||||
|
||||
/// Parse a single statement.
|
||||
pub fn parse_statement(source: &str) -> Result<Vec<ast::Statement>, ParseError> {
|
||||
do_lalr_parsing!(source, Statement, StartStatement)
|
||||
pub fn parse_program(source: &str) -> Result<ast::Suite, ParseError> {
|
||||
parse(source, Mode::Module).map(|top| match top {
|
||||
ast::Mod::Module { body, .. } => body,
|
||||
_ => unreachable!(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Parses a python expression
|
||||
@@ -54,478 +32,136 @@ pub fn parse_statement(source: &str) -> Result<Vec<ast::Statement>, ParseError>
|
||||
/// # Example
|
||||
/// ```
|
||||
/// extern crate num_bigint;
|
||||
/// use num_bigint::BigInt;
|
||||
/// use rustpython_parser::{parser, ast};
|
||||
/// let expr = parser::parse_expression("1 + 2").unwrap();
|
||||
///
|
||||
/// assert_eq!(ast::Expression {
|
||||
/// assert_eq!(
|
||||
/// expr,
|
||||
/// ast::Expr {
|
||||
/// location: ast::Location::new(1, 3),
|
||||
/// custom: (),
|
||||
/// node: ast::ExpressionType::Binop {
|
||||
/// a: Box::new(ast::Expression {
|
||||
/// node: ast::ExprKind::BinOp {
|
||||
/// left: Box::new(ast::Expr {
|
||||
/// location: ast::Location::new(1, 1),
|
||||
/// custom: (),
|
||||
/// node: ast::ExpressionType::Number {
|
||||
/// value: ast::Number::Integer { value: BigInt::from(1) }
|
||||
/// node: ast::ExprKind::Constant {
|
||||
/// value: ast::Constant::Int(1.into()),
|
||||
/// kind: None,
|
||||
/// }
|
||||
/// }),
|
||||
/// op: ast::Operator::Add,
|
||||
/// b: Box::new(ast::Expression {
|
||||
/// right: Box::new(ast::Expr {
|
||||
/// location: ast::Location::new(1, 5),
|
||||
/// custom: (),
|
||||
/// node: ast::ExpressionType::Number {
|
||||
/// value: ast::Number::Integer { value: BigInt::from(2) }
|
||||
/// node: ast::ExprKind::Constant {
|
||||
/// value: ast::Constant::Int(2.into()),
|
||||
/// kind: None,
|
||||
/// }
|
||||
/// })
|
||||
/// }
|
||||
/// },
|
||||
/// expr);
|
||||
/// );
|
||||
///
|
||||
/// ```
|
||||
pub fn parse_expression(source: &str) -> Result<ast::Expression, ParseError> {
|
||||
do_lalr_parsing!(source, Expression, StartExpression)
|
||||
pub fn parse_expression(source: &str) -> Result<ast::Expr, ParseError> {
|
||||
parse(source, Mode::Expression).map(|top| match top {
|
||||
ast::Mod::Expression { body } => *body,
|
||||
_ => unreachable!(),
|
||||
})
|
||||
}
|
||||
|
||||
// Parse a given source code
|
||||
pub fn parse(source: &str, mode: Mode) -> Result<ast::Top, ParseError> {
|
||||
Ok(match mode {
|
||||
Mode::Program => {
|
||||
let ast = parse_program(source)?;
|
||||
ast::Top::Program(ast)
|
||||
}
|
||||
Mode::Statement => {
|
||||
let statement = parse_statement(source)?;
|
||||
ast::Top::Statement(statement)
|
||||
}
|
||||
})
|
||||
pub fn parse(source: &str, mode: Mode) -> Result<ast::Mod, ParseError> {
|
||||
let lxr = lexer::make_tokenizer(source);
|
||||
let marker_token = (Default::default(), mode.to_marker(), Default::default());
|
||||
let tokenizer = iter::once(Ok(marker_token)).chain(lxr);
|
||||
|
||||
python::TopParser::new()
|
||||
.parse(tokenizer)
|
||||
.map_err(ParseError::from)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::ast;
|
||||
use super::parse_expression;
|
||||
use super::parse_program;
|
||||
use super::parse_statement;
|
||||
use num_bigint::BigInt;
|
||||
|
||||
fn mk_ident(name: &str, row: usize, col: usize) -> ast::Expression {
|
||||
ast::Expression {
|
||||
location: ast::Location::new(row, col),
|
||||
custom: (),
|
||||
node: ast::ExpressionType::Identifier {
|
||||
name: name.to_owned(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn make_int(value: i32, row: usize, col: usize) -> ast::Expression {
|
||||
ast::Expression {
|
||||
location: ast::Location::new(row, col),
|
||||
custom: (),
|
||||
node: ast::ExpressionType::Number {
|
||||
value: ast::Number::Integer {
|
||||
value: BigInt::from(value),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn make_string(value: &str, row: usize, col: usize) -> ast::Expression {
|
||||
ast::Expression {
|
||||
location: ast::Location::new(row, col),
|
||||
custom: (),
|
||||
node: ast::ExpressionType::String {
|
||||
value: ast::StringGroup::Constant {
|
||||
value: String::from(value),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn as_statement(expr: ast::Expression) -> ast::Statement {
|
||||
ast::Statement {
|
||||
location: expr.location,
|
||||
custom: (),
|
||||
node: ast::StatementType::Expression { expression: expr },
|
||||
}
|
||||
}
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_parse_empty() {
|
||||
let parse_ast = parse_program("");
|
||||
assert_eq!(parse_ast, Ok(ast::Program { statements: vec![] }))
|
||||
let parse_ast = parse_program("").unwrap();
|
||||
insta::assert_debug_snapshot!(parse_ast);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_print_hello() {
|
||||
let source = String::from("print('Hello world')");
|
||||
let parse_ast = parse_program(&source).unwrap();
|
||||
assert_eq!(
|
||||
parse_ast,
|
||||
ast::Program {
|
||||
statements: vec![ast::Statement {
|
||||
location: ast::Location::new(1, 1),
|
||||
custom: (),
|
||||
node: ast::StatementType::Expression {
|
||||
expression: ast::Expression {
|
||||
custom: (),
|
||||
location: ast::Location::new(1, 6),
|
||||
node: ast::ExpressionType::Call {
|
||||
function: Box::new(mk_ident("print", 1, 1)),
|
||||
args: vec![make_string("Hello world", 1, 8)],
|
||||
keywords: vec![],
|
||||
}
|
||||
},
|
||||
},
|
||||
},],
|
||||
}
|
||||
);
|
||||
insta::assert_debug_snapshot!(parse_ast);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_print_2() {
|
||||
let source = String::from("print('Hello world', 2)");
|
||||
let parse_ast = parse_program(&source).unwrap();
|
||||
assert_eq!(
|
||||
parse_ast,
|
||||
ast::Program {
|
||||
statements: vec![ast::Statement {
|
||||
location: ast::Location::new(1, 1),
|
||||
custom: (),
|
||||
node: ast::StatementType::Expression {
|
||||
expression: ast::Expression {
|
||||
location: ast::Location::new(1, 6),
|
||||
custom: (),
|
||||
node: ast::ExpressionType::Call {
|
||||
function: Box::new(mk_ident("print", 1, 1)),
|
||||
args: vec![make_string("Hello world", 1, 8), make_int(2, 1, 22),],
|
||||
keywords: vec![],
|
||||
},
|
||||
},
|
||||
},
|
||||
},],
|
||||
}
|
||||
);
|
||||
insta::assert_debug_snapshot!(parse_ast);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_kwargs() {
|
||||
let source = String::from("my_func('positional', keyword=2)");
|
||||
let parse_ast = parse_program(&source).unwrap();
|
||||
assert_eq!(
|
||||
parse_ast,
|
||||
ast::Program {
|
||||
statements: vec![ast::Statement {
|
||||
location: ast::Location::new(1, 1),
|
||||
custom: (),
|
||||
node: ast::StatementType::Expression {
|
||||
expression: ast::Expression {
|
||||
location: ast::Location::new(1, 8),
|
||||
custom: (),
|
||||
node: ast::ExpressionType::Call {
|
||||
function: Box::new(mk_ident("my_func", 1, 1)),
|
||||
args: vec![make_string("positional", 1, 10)],
|
||||
keywords: vec![ast::Keyword {
|
||||
name: Some("keyword".to_owned()),
|
||||
value: make_int(2, 1, 31),
|
||||
}],
|
||||
}
|
||||
},
|
||||
},
|
||||
},],
|
||||
}
|
||||
);
|
||||
insta::assert_debug_snapshot!(parse_ast);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_if_elif_else() {
|
||||
let source = String::from("if 1: 10\nelif 2: 20\nelse: 30");
|
||||
let parse_ast = parse_statement(&source).unwrap();
|
||||
assert_eq!(
|
||||
parse_ast,
|
||||
vec![ast::Statement {
|
||||
location: ast::Location::new(1, 1),
|
||||
custom: (),
|
||||
node: ast::StatementType::If {
|
||||
test: make_int(1, 1, 4),
|
||||
body: vec![as_statement(make_int(10, 1, 7))],
|
||||
orelse: Some(vec![ast::Statement {
|
||||
location: ast::Location::new(2, 1),
|
||||
custom: (),
|
||||
node: ast::StatementType::If {
|
||||
test: make_int(2, 2, 6),
|
||||
body: vec![as_statement(make_int(20, 2, 9))],
|
||||
orelse: Some(vec![as_statement(make_int(30, 3, 7))]),
|
||||
}
|
||||
},]),
|
||||
}
|
||||
}]
|
||||
);
|
||||
let parse_ast = parse_program(&source).unwrap();
|
||||
insta::assert_debug_snapshot!(parse_ast);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_lambda() {
|
||||
let source = String::from("lambda x, y: x * y"); // lambda(x, y): x * y");
|
||||
let parse_ast = parse_statement(&source);
|
||||
assert_eq!(
|
||||
parse_ast,
|
||||
Ok(vec![as_statement(ast::Expression {
|
||||
location: ast::Location::new(1, 1),
|
||||
custom: (),
|
||||
node: ast::ExpressionType::Lambda {
|
||||
args: Box::new(ast::Parameters {
|
||||
posonlyargs_count: 0,
|
||||
args: vec![
|
||||
ast::Parameter {
|
||||
location: ast::Location::new(1, 8),
|
||||
arg: String::from("x"),
|
||||
annotation: None,
|
||||
},
|
||||
ast::Parameter {
|
||||
location: ast::Location::new(1, 11),
|
||||
arg: String::from("y"),
|
||||
annotation: None,
|
||||
}
|
||||
],
|
||||
kwonlyargs: vec![],
|
||||
vararg: ast::Varargs::None,
|
||||
kwarg: ast::Varargs::None,
|
||||
defaults: vec![],
|
||||
kw_defaults: vec![],
|
||||
}),
|
||||
body: Box::new(ast::Expression {
|
||||
location: ast::Location::new(1, 16),
|
||||
custom: (),
|
||||
node: ast::ExpressionType::Binop {
|
||||
a: Box::new(mk_ident("x", 1, 14)),
|
||||
op: ast::Operator::Mult,
|
||||
b: Box::new(mk_ident("y", 1, 18))
|
||||
}
|
||||
})
|
||||
}
|
||||
})])
|
||||
)
|
||||
let source = "lambda x, y: x * y"; // lambda(x, y): x * y";
|
||||
let parse_ast = parse_program(&source).unwrap();
|
||||
insta::assert_debug_snapshot!(parse_ast);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_tuples() {
|
||||
let source = String::from("a, b = 4, 5");
|
||||
let source = "a, b = 4, 5";
|
||||
|
||||
assert_eq!(
|
||||
parse_statement(&source),
|
||||
Ok(vec![ast::Statement {
|
||||
location: ast::Location::new(1, 1),
|
||||
custom: (),
|
||||
node: ast::StatementType::Assign {
|
||||
targets: vec![ast::Expression {
|
||||
custom: (),
|
||||
location: ast::Location::new(1, 1),
|
||||
node: ast::ExpressionType::Tuple {
|
||||
elements: vec![mk_ident("a", 1, 1), mk_ident("b", 1, 4),]
|
||||
}
|
||||
}],
|
||||
value: ast::Expression {
|
||||
location: ast::Location::new(1, 8),
|
||||
custom: (),
|
||||
node: ast::ExpressionType::Tuple {
|
||||
elements: vec![make_int(4, 1, 8), make_int(5, 1, 11),]
|
||||
}
|
||||
}
|
||||
}
|
||||
}])
|
||||
)
|
||||
insta::assert_debug_snapshot!(parse_program(&source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_class() {
|
||||
let source = String::from(
|
||||
"class Foo(A, B):\n def __init__(self):\n pass\n def method_with_default(self, arg='default'):\n pass",
|
||||
);
|
||||
assert_eq!(
|
||||
parse_statement(&source),
|
||||
Ok(vec![ast::Statement {
|
||||
location: ast::Location::new(1, 1),
|
||||
custom: (),
|
||||
node: ast::StatementType::ClassDef {
|
||||
name: String::from("Foo"),
|
||||
bases: vec![mk_ident("A", 1, 11), mk_ident("B", 1, 14)],
|
||||
keywords: vec![],
|
||||
body: vec![
|
||||
ast::Statement {
|
||||
location: ast::Location::new(2, 2),
|
||||
custom: (),
|
||||
node: ast::StatementType::FunctionDef {
|
||||
is_async: false,
|
||||
name: String::from("__init__"),
|
||||
args: Box::new(ast::Parameters {
|
||||
posonlyargs_count: 0,
|
||||
args: vec![ast::Parameter {
|
||||
location: ast::Location::new(2, 15),
|
||||
arg: String::from("self"),
|
||||
annotation: None,
|
||||
}],
|
||||
kwonlyargs: vec![],
|
||||
vararg: ast::Varargs::None,
|
||||
kwarg: ast::Varargs::None,
|
||||
defaults: vec![],
|
||||
kw_defaults: vec![],
|
||||
}),
|
||||
body: vec![ast::Statement {
|
||||
location: ast::Location::new(3, 3),
|
||||
custom: (),
|
||||
node: ast::StatementType::Pass,
|
||||
}],
|
||||
decorator_list: vec![],
|
||||
returns: None,
|
||||
}
|
||||
},
|
||||
ast::Statement {
|
||||
location: ast::Location::new(4, 2),
|
||||
custom: (),
|
||||
node: ast::StatementType::FunctionDef {
|
||||
is_async: false,
|
||||
name: String::from("method_with_default"),
|
||||
args: Box::new(ast::Parameters {
|
||||
posonlyargs_count: 0,
|
||||
args: vec![
|
||||
ast::Parameter {
|
||||
location: ast::Location::new(4, 26),
|
||||
arg: String::from("self"),
|
||||
annotation: None,
|
||||
},
|
||||
ast::Parameter {
|
||||
location: ast::Location::new(4, 32),
|
||||
arg: String::from("arg"),
|
||||
annotation: None,
|
||||
}
|
||||
],
|
||||
kwonlyargs: vec![],
|
||||
vararg: ast::Varargs::None,
|
||||
kwarg: ast::Varargs::None,
|
||||
defaults: vec![make_string("default", 4, 37)],
|
||||
kw_defaults: vec![],
|
||||
}),
|
||||
body: vec![ast::Statement {
|
||||
location: ast::Location::new(5, 3),
|
||||
custom: (),
|
||||
node: ast::StatementType::Pass,
|
||||
}],
|
||||
decorator_list: vec![],
|
||||
returns: None,
|
||||
}
|
||||
}
|
||||
],
|
||||
decorator_list: vec![],
|
||||
}
|
||||
}])
|
||||
)
|
||||
let source = "\
|
||||
class Foo(A, B):
|
||||
def __init__(self):
|
||||
pass
|
||||
def method_with_default(self, arg='default'):
|
||||
pass";
|
||||
insta::assert_debug_snapshot!(parse_program(&source).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_dict_comprehension() {
|
||||
let source = String::from("{x1: x2 for y in z}");
|
||||
let parse_ast = parse_expression(&source).unwrap();
|
||||
assert_eq!(
|
||||
parse_ast,
|
||||
ast::Expression {
|
||||
location: ast::Location::new(1, 1),
|
||||
custom: (),
|
||||
node: ast::ExpressionType::Comprehension {
|
||||
kind: Box::new(ast::ComprehensionKind::Dict {
|
||||
key: mk_ident("x1", 1, 2),
|
||||
value: mk_ident("x2", 1, 6),
|
||||
}),
|
||||
generators: vec![ast::Comprehension {
|
||||
location: ast::Location::new(1, 9),
|
||||
target: mk_ident("y", 1, 13),
|
||||
iter: mk_ident("z", 1, 18),
|
||||
ifs: vec![],
|
||||
is_async: false,
|
||||
}],
|
||||
}
|
||||
}
|
||||
);
|
||||
insta::assert_debug_snapshot!(parse_ast);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_list_comprehension() {
|
||||
let source = String::from("[x for y in z]");
|
||||
let parse_ast = parse_expression(&source).unwrap();
|
||||
assert_eq!(
|
||||
parse_ast,
|
||||
ast::Expression {
|
||||
custom: (),
|
||||
location: ast::Location::new(1, 1),
|
||||
node: ast::ExpressionType::Comprehension {
|
||||
kind: Box::new(ast::ComprehensionKind::List {
|
||||
element: mk_ident("x", 1, 2),
|
||||
}),
|
||||
generators: vec![ast::Comprehension {
|
||||
location: ast::Location::new(1, 4),
|
||||
target: mk_ident("y", 1, 8),
|
||||
iter: mk_ident("z", 1, 13),
|
||||
ifs: vec![],
|
||||
is_async: false,
|
||||
}],
|
||||
}
|
||||
}
|
||||
);
|
||||
insta::assert_debug_snapshot!(parse_ast);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_double_list_comprehension() {
|
||||
let source = String::from("[x for y, y2 in z for a in b if a < 5 if a > 10]");
|
||||
let parse_ast = parse_expression(&source).unwrap();
|
||||
assert_eq!(
|
||||
parse_ast,
|
||||
ast::Expression {
|
||||
custom: (),
|
||||
location: ast::Location::new(1, 1),
|
||||
node: ast::ExpressionType::Comprehension {
|
||||
kind: Box::new(ast::ComprehensionKind::List {
|
||||
element: mk_ident("x", 1, 2)
|
||||
}),
|
||||
generators: vec![
|
||||
ast::Comprehension {
|
||||
location: ast::Location::new(1, 4),
|
||||
target: ast::Expression {
|
||||
custom: (),
|
||||
location: ast::Location::new(1, 8),
|
||||
node: ast::ExpressionType::Tuple {
|
||||
elements: vec![mk_ident("y", 1, 8), mk_ident("y2", 1, 11),],
|
||||
}
|
||||
},
|
||||
iter: mk_ident("z", 1, 17),
|
||||
ifs: vec![],
|
||||
is_async: false,
|
||||
},
|
||||
ast::Comprehension {
|
||||
location: ast::Location::new(1, 19),
|
||||
target: mk_ident("a", 1, 23),
|
||||
iter: mk_ident("b", 1, 28),
|
||||
ifs: vec![
|
||||
ast::Expression {
|
||||
custom: (),
|
||||
location: ast::Location::new(1, 35),
|
||||
node: ast::ExpressionType::Compare {
|
||||
vals: vec![mk_ident("a", 1, 33), make_int(5, 1, 37),],
|
||||
ops: vec![ast::Comparison::Less],
|
||||
}
|
||||
},
|
||||
ast::Expression {
|
||||
custom: (),
|
||||
location: ast::Location::new(1, 44),
|
||||
node: ast::ExpressionType::Compare {
|
||||
vals: vec![mk_ident("a", 1, 42), make_int(10, 1, 46),],
|
||||
ops: vec![ast::Comparison::Greater],
|
||||
},
|
||||
},
|
||||
],
|
||||
is_async: false,
|
||||
}
|
||||
],
|
||||
}
|
||||
}
|
||||
);
|
||||
insta::assert_debug_snapshot!(parse_ast);
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,63 @@
|
||||
---
|
||||
source: parser/src/fstring.rs
|
||||
expression: parse_ast
|
||||
---
|
||||
Located {
|
||||
location: Location {
|
||||
row: 0,
|
||||
column: 0,
|
||||
},
|
||||
custom: (),
|
||||
node: JoinedStr {
|
||||
values: [
|
||||
Located {
|
||||
location: Location {
|
||||
row: 0,
|
||||
column: 0,
|
||||
},
|
||||
custom: (),
|
||||
node: Constant {
|
||||
value: Str(
|
||||
"user=",
|
||||
),
|
||||
kind: None,
|
||||
},
|
||||
},
|
||||
Located {
|
||||
location: Location {
|
||||
row: 0,
|
||||
column: 0,
|
||||
},
|
||||
custom: (),
|
||||
node: Constant {
|
||||
value: Str(
|
||||
"",
|
||||
),
|
||||
kind: None,
|
||||
},
|
||||
},
|
||||
Located {
|
||||
location: Location {
|
||||
row: 0,
|
||||
column: 0,
|
||||
},
|
||||
custom: (),
|
||||
node: FormattedValue {
|
||||
value: Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 2,
|
||||
},
|
||||
custom: (),
|
||||
node: Name {
|
||||
id: "user",
|
||||
ctx: Load,
|
||||
},
|
||||
},
|
||||
conversion: None,
|
||||
format_spec: None,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
---
|
||||
source: parser/src/fstring.rs
|
||||
expression: parse_ast
|
||||
---
|
||||
Located {
|
||||
location: Location {
|
||||
row: 0,
|
||||
column: 0,
|
||||
},
|
||||
custom: (),
|
||||
node: JoinedStr {
|
||||
values: [
|
||||
Located {
|
||||
location: Location {
|
||||
row: 0,
|
||||
column: 0,
|
||||
},
|
||||
custom: (),
|
||||
node: Constant {
|
||||
value: Str(
|
||||
"mix ",
|
||||
),
|
||||
kind: None,
|
||||
},
|
||||
},
|
||||
Located {
|
||||
location: Location {
|
||||
row: 0,
|
||||
column: 0,
|
||||
},
|
||||
custom: (),
|
||||
node: Constant {
|
||||
value: Str(
|
||||
"user=",
|
||||
),
|
||||
kind: None,
|
||||
},
|
||||
},
|
||||
Located {
|
||||
location: Location {
|
||||
row: 0,
|
||||
column: 0,
|
||||
},
|
||||
custom: (),
|
||||
node: Constant {
|
||||
value: Str(
|
||||
"",
|
||||
),
|
||||
kind: None,
|
||||
},
|
||||
},
|
||||
Located {
|
||||
location: Location {
|
||||
row: 0,
|
||||
column: 0,
|
||||
},
|
||||
custom: (),
|
||||
node: FormattedValue {
|
||||
value: Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 2,
|
||||
},
|
||||
custom: (),
|
||||
node: Name {
|
||||
id: "user",
|
||||
ctx: Load,
|
||||
},
|
||||
},
|
||||
conversion: None,
|
||||
format_spec: None,
|
||||
},
|
||||
},
|
||||
Located {
|
||||
location: Location {
|
||||
row: 0,
|
||||
column: 0,
|
||||
},
|
||||
custom: (),
|
||||
node: Constant {
|
||||
value: Str(
|
||||
" with text and ",
|
||||
),
|
||||
kind: None,
|
||||
},
|
||||
},
|
||||
Located {
|
||||
location: Location {
|
||||
row: 0,
|
||||
column: 0,
|
||||
},
|
||||
custom: (),
|
||||
node: Constant {
|
||||
value: Str(
|
||||
"second=",
|
||||
),
|
||||
kind: None,
|
||||
},
|
||||
},
|
||||
Located {
|
||||
location: Location {
|
||||
row: 0,
|
||||
column: 0,
|
||||
},
|
||||
custom: (),
|
||||
node: Constant {
|
||||
value: Str(
|
||||
"",
|
||||
),
|
||||
kind: None,
|
||||
},
|
||||
},
|
||||
Located {
|
||||
location: Location {
|
||||
row: 0,
|
||||
column: 0,
|
||||
},
|
||||
custom: (),
|
||||
node: FormattedValue {
|
||||
value: Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 2,
|
||||
},
|
||||
custom: (),
|
||||
node: Name {
|
||||
id: "second",
|
||||
ctx: Load,
|
||||
},
|
||||
},
|
||||
conversion: None,
|
||||
format_spec: None,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
---
|
||||
source: parser/src/fstring.rs
|
||||
expression: parse_ast
|
||||
---
|
||||
Located {
|
||||
location: Location {
|
||||
row: 0,
|
||||
column: 0,
|
||||
},
|
||||
custom: (),
|
||||
node: JoinedStr {
|
||||
values: [
|
||||
Located {
|
||||
location: Location {
|
||||
row: 0,
|
||||
column: 0,
|
||||
},
|
||||
custom: (),
|
||||
node: Constant {
|
||||
value: Str(
|
||||
"user=",
|
||||
),
|
||||
kind: None,
|
||||
},
|
||||
},
|
||||
Located {
|
||||
location: Location {
|
||||
row: 0,
|
||||
column: 0,
|
||||
},
|
||||
custom: (),
|
||||
node: Constant {
|
||||
value: Str(
|
||||
"",
|
||||
),
|
||||
kind: None,
|
||||
},
|
||||
},
|
||||
Located {
|
||||
location: Location {
|
||||
row: 0,
|
||||
column: 0,
|
||||
},
|
||||
custom: (),
|
||||
node: FormattedValue {
|
||||
value: Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 2,
|
||||
},
|
||||
custom: (),
|
||||
node: Name {
|
||||
id: "user",
|
||||
ctx: Load,
|
||||
},
|
||||
},
|
||||
conversion: None,
|
||||
format_spec: Some(
|
||||
Located {
|
||||
location: Location {
|
||||
row: 0,
|
||||
column: 0,
|
||||
},
|
||||
custom: (),
|
||||
node: Constant {
|
||||
value: Str(
|
||||
">10",
|
||||
),
|
||||
kind: None,
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
---
|
||||
source: parser/src/fstring.rs
|
||||
expression: "parse_fstring(\"\").unwrap()"
|
||||
---
|
||||
Located {
|
||||
location: Location {
|
||||
row: 0,
|
||||
column: 0,
|
||||
},
|
||||
custom: (),
|
||||
node: Constant {
|
||||
value: Str(
|
||||
"",
|
||||
),
|
||||
kind: None,
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
---
|
||||
source: parser/src/fstring.rs
|
||||
expression: parse_ast
|
||||
---
|
||||
Located {
|
||||
location: Location {
|
||||
row: 0,
|
||||
column: 0,
|
||||
},
|
||||
custom: (),
|
||||
node: JoinedStr {
|
||||
values: [
|
||||
Located {
|
||||
location: Location {
|
||||
row: 0,
|
||||
column: 0,
|
||||
},
|
||||
custom: (),
|
||||
node: FormattedValue {
|
||||
value: Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 2,
|
||||
},
|
||||
custom: (),
|
||||
node: Name {
|
||||
id: "a",
|
||||
ctx: Load,
|
||||
},
|
||||
},
|
||||
conversion: None,
|
||||
format_spec: None,
|
||||
},
|
||||
},
|
||||
Located {
|
||||
location: Location {
|
||||
row: 0,
|
||||
column: 0,
|
||||
},
|
||||
custom: (),
|
||||
node: FormattedValue {
|
||||
value: Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 3,
|
||||
},
|
||||
custom: (),
|
||||
node: Name {
|
||||
id: "b",
|
||||
ctx: Load,
|
||||
},
|
||||
},
|
||||
conversion: None,
|
||||
format_spec: None,
|
||||
},
|
||||
},
|
||||
Located {
|
||||
location: Location {
|
||||
row: 0,
|
||||
column: 0,
|
||||
},
|
||||
custom: (),
|
||||
node: Constant {
|
||||
value: Str(
|
||||
"{foo}",
|
||||
),
|
||||
kind: None,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
---
|
||||
source: parser/src/fstring.rs
|
||||
expression: parse_ast
|
||||
---
|
||||
Located {
|
||||
location: Location {
|
||||
row: 0,
|
||||
column: 0,
|
||||
},
|
||||
custom: (),
|
||||
node: FormattedValue {
|
||||
value: Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 5,
|
||||
},
|
||||
custom: (),
|
||||
node: Compare {
|
||||
left: Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 2,
|
||||
},
|
||||
custom: (),
|
||||
node: Constant {
|
||||
value: Int(
|
||||
BigInt {
|
||||
sign: Plus,
|
||||
data: BigUint {
|
||||
data: [
|
||||
42,
|
||||
],
|
||||
},
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
},
|
||||
},
|
||||
ops: [
|
||||
Eq,
|
||||
],
|
||||
comparators: [
|
||||
Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 8,
|
||||
},
|
||||
custom: (),
|
||||
node: Constant {
|
||||
value: Int(
|
||||
BigInt {
|
||||
sign: Plus,
|
||||
data: BigUint {
|
||||
data: [
|
||||
42,
|
||||
],
|
||||
},
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
conversion: None,
|
||||
format_spec: None,
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
---
|
||||
source: parser/src/fstring.rs
|
||||
expression: parse_ast
|
||||
---
|
||||
Located {
|
||||
location: Location {
|
||||
row: 0,
|
||||
column: 0,
|
||||
},
|
||||
custom: (),
|
||||
node: FormattedValue {
|
||||
value: Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 2,
|
||||
},
|
||||
custom: (),
|
||||
node: Name {
|
||||
id: "foo",
|
||||
ctx: Load,
|
||||
},
|
||||
},
|
||||
conversion: None,
|
||||
format_spec: Some(
|
||||
Located {
|
||||
location: Location {
|
||||
row: 0,
|
||||
column: 0,
|
||||
},
|
||||
custom: (),
|
||||
node: FormattedValue {
|
||||
value: Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 2,
|
||||
},
|
||||
custom: (),
|
||||
node: Name {
|
||||
id: "spec",
|
||||
ctx: Load,
|
||||
},
|
||||
},
|
||||
conversion: None,
|
||||
format_spec: None,
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
---
|
||||
source: parser/src/fstring.rs
|
||||
expression: parse_ast
|
||||
---
|
||||
Located {
|
||||
location: Location {
|
||||
row: 0,
|
||||
column: 0,
|
||||
},
|
||||
custom: (),
|
||||
node: FormattedValue {
|
||||
value: Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 4,
|
||||
},
|
||||
custom: (),
|
||||
node: Compare {
|
||||
left: Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 2,
|
||||
},
|
||||
custom: (),
|
||||
node: Constant {
|
||||
value: Int(
|
||||
BigInt {
|
||||
sign: Plus,
|
||||
data: BigUint {
|
||||
data: [
|
||||
1,
|
||||
],
|
||||
},
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
},
|
||||
},
|
||||
ops: [
|
||||
NotEq,
|
||||
],
|
||||
comparators: [
|
||||
Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 7,
|
||||
},
|
||||
custom: (),
|
||||
node: Constant {
|
||||
value: Int(
|
||||
BigInt {
|
||||
sign: Plus,
|
||||
data: BigUint {
|
||||
data: [
|
||||
2,
|
||||
],
|
||||
},
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
conversion: None,
|
||||
format_spec: None,
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
---
|
||||
source: parser/src/fstring.rs
|
||||
expression: parse_ast
|
||||
---
|
||||
Located {
|
||||
location: Location {
|
||||
row: 0,
|
||||
column: 0,
|
||||
},
|
||||
custom: (),
|
||||
node: FormattedValue {
|
||||
value: Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 2,
|
||||
},
|
||||
custom: (),
|
||||
node: Name {
|
||||
id: "foo",
|
||||
ctx: Load,
|
||||
},
|
||||
},
|
||||
conversion: None,
|
||||
format_spec: Some(
|
||||
Located {
|
||||
location: Location {
|
||||
row: 0,
|
||||
column: 0,
|
||||
},
|
||||
custom: (),
|
||||
node: Constant {
|
||||
value: Str(
|
||||
"spec",
|
||||
),
|
||||
kind: None,
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
---
|
||||
source: parser/src/fstring.rs
|
||||
expression: parse_ast
|
||||
---
|
||||
Located {
|
||||
location: Location {
|
||||
row: 0,
|
||||
column: 0,
|
||||
},
|
||||
custom: (),
|
||||
node: JoinedStr {
|
||||
values: [
|
||||
Located {
|
||||
location: Location {
|
||||
row: 0,
|
||||
column: 0,
|
||||
},
|
||||
custom: (),
|
||||
node: Constant {
|
||||
value: Str(
|
||||
"x =",
|
||||
),
|
||||
kind: None,
|
||||
},
|
||||
},
|
||||
Located {
|
||||
location: Location {
|
||||
row: 0,
|
||||
column: 0,
|
||||
},
|
||||
custom: (),
|
||||
node: Constant {
|
||||
value: Str(
|
||||
"",
|
||||
),
|
||||
kind: None,
|
||||
},
|
||||
},
|
||||
Located {
|
||||
location: Location {
|
||||
row: 0,
|
||||
column: 0,
|
||||
},
|
||||
custom: (),
|
||||
node: FormattedValue {
|
||||
value: Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 2,
|
||||
},
|
||||
custom: (),
|
||||
node: Name {
|
||||
id: "x",
|
||||
ctx: Load,
|
||||
},
|
||||
},
|
||||
conversion: None,
|
||||
format_spec: None,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
---
|
||||
source: parser/src/fstring.rs
|
||||
expression: parse_ast
|
||||
---
|
||||
Located {
|
||||
location: Location {
|
||||
row: 0,
|
||||
column: 0,
|
||||
},
|
||||
custom: (),
|
||||
node: JoinedStr {
|
||||
values: [
|
||||
Located {
|
||||
location: Location {
|
||||
row: 0,
|
||||
column: 0,
|
||||
},
|
||||
custom: (),
|
||||
node: Constant {
|
||||
value: Str(
|
||||
"x=",
|
||||
),
|
||||
kind: None,
|
||||
},
|
||||
},
|
||||
Located {
|
||||
location: Location {
|
||||
row: 0,
|
||||
column: 0,
|
||||
},
|
||||
custom: (),
|
||||
node: Constant {
|
||||
value: Str(
|
||||
" ",
|
||||
),
|
||||
kind: None,
|
||||
},
|
||||
},
|
||||
Located {
|
||||
location: Location {
|
||||
row: 0,
|
||||
column: 0,
|
||||
},
|
||||
custom: (),
|
||||
node: FormattedValue {
|
||||
value: Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 2,
|
||||
},
|
||||
custom: (),
|
||||
node: Name {
|
||||
id: "x",
|
||||
ctx: Load,
|
||||
},
|
||||
},
|
||||
conversion: None,
|
||||
format_spec: None,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
---
|
||||
source: parser/src/fstring.rs
|
||||
expression: parse_ast
|
||||
---
|
||||
Located {
|
||||
location: Location {
|
||||
row: 0,
|
||||
column: 0,
|
||||
},
|
||||
custom: (),
|
||||
node: FormattedValue {
|
||||
value: Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 2,
|
||||
},
|
||||
custom: (),
|
||||
node: Yield {
|
||||
value: None,
|
||||
},
|
||||
},
|
||||
conversion: None,
|
||||
format_spec: None,
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
---
|
||||
source: parser/src/parser.rs
|
||||
expression: parse_program(&source).unwrap()
|
||||
---
|
||||
[
|
||||
Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 1,
|
||||
},
|
||||
custom: (),
|
||||
node: ClassDef {
|
||||
name: "Foo",
|
||||
bases: [
|
||||
Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 11,
|
||||
},
|
||||
custom: (),
|
||||
node: Name {
|
||||
id: "A",
|
||||
ctx: Load,
|
||||
},
|
||||
},
|
||||
Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 14,
|
||||
},
|
||||
custom: (),
|
||||
node: Name {
|
||||
id: "B",
|
||||
ctx: Load,
|
||||
},
|
||||
},
|
||||
],
|
||||
keywords: [],
|
||||
body: [
|
||||
Located {
|
||||
location: Location {
|
||||
row: 2,
|
||||
column: 2,
|
||||
},
|
||||
custom: (),
|
||||
node: FunctionDef {
|
||||
name: "__init__",
|
||||
args: Arguments {
|
||||
posonlyargs: [],
|
||||
args: [
|
||||
Located {
|
||||
location: Location {
|
||||
row: 2,
|
||||
column: 15,
|
||||
},
|
||||
custom: (),
|
||||
node: ArgData {
|
||||
arg: "self",
|
||||
annotation: None,
|
||||
type_comment: None,
|
||||
},
|
||||
},
|
||||
],
|
||||
vararg: None,
|
||||
kwonlyargs: [],
|
||||
kw_defaults: [],
|
||||
kwarg: None,
|
||||
defaults: [],
|
||||
},
|
||||
body: [
|
||||
Located {
|
||||
location: Location {
|
||||
row: 3,
|
||||
column: 3,
|
||||
},
|
||||
custom: (),
|
||||
node: Pass,
|
||||
},
|
||||
],
|
||||
decorator_list: [],
|
||||
returns: None,
|
||||
type_comment: None,
|
||||
},
|
||||
},
|
||||
Located {
|
||||
location: Location {
|
||||
row: 4,
|
||||
column: 2,
|
||||
},
|
||||
custom: (),
|
||||
node: FunctionDef {
|
||||
name: "method_with_default",
|
||||
args: Arguments {
|
||||
posonlyargs: [],
|
||||
args: [
|
||||
Located {
|
||||
location: Location {
|
||||
row: 4,
|
||||
column: 26,
|
||||
},
|
||||
custom: (),
|
||||
node: ArgData {
|
||||
arg: "self",
|
||||
annotation: None,
|
||||
type_comment: None,
|
||||
},
|
||||
},
|
||||
Located {
|
||||
location: Location {
|
||||
row: 4,
|
||||
column: 32,
|
||||
},
|
||||
custom: (),
|
||||
node: ArgData {
|
||||
arg: "arg",
|
||||
annotation: None,
|
||||
type_comment: None,
|
||||
},
|
||||
},
|
||||
],
|
||||
vararg: None,
|
||||
kwonlyargs: [],
|
||||
kw_defaults: [],
|
||||
kwarg: None,
|
||||
defaults: [
|
||||
Located {
|
||||
location: Location {
|
||||
row: 4,
|
||||
column: 37,
|
||||
},
|
||||
custom: (),
|
||||
node: Constant {
|
||||
value: Str(
|
||||
"default",
|
||||
),
|
||||
kind: None,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
body: [
|
||||
Located {
|
||||
location: Location {
|
||||
row: 5,
|
||||
column: 3,
|
||||
},
|
||||
custom: (),
|
||||
node: Pass,
|
||||
},
|
||||
],
|
||||
decorator_list: [],
|
||||
returns: None,
|
||||
type_comment: None,
|
||||
},
|
||||
},
|
||||
],
|
||||
decorator_list: [],
|
||||
},
|
||||
},
|
||||
]
|
||||
@@ -0,0 +1,63 @@
|
||||
---
|
||||
source: parser/src/parser.rs
|
||||
expression: parse_ast
|
||||
---
|
||||
Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 1,
|
||||
},
|
||||
custom: (),
|
||||
node: DictComp {
|
||||
key: Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 2,
|
||||
},
|
||||
custom: (),
|
||||
node: Name {
|
||||
id: "x1",
|
||||
ctx: Load,
|
||||
},
|
||||
},
|
||||
value: Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 6,
|
||||
},
|
||||
custom: (),
|
||||
node: Name {
|
||||
id: "x2",
|
||||
ctx: Load,
|
||||
},
|
||||
},
|
||||
generators: [
|
||||
Comprehension {
|
||||
target: Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 13,
|
||||
},
|
||||
custom: (),
|
||||
node: Name {
|
||||
id: "y",
|
||||
ctx: Load,
|
||||
},
|
||||
},
|
||||
iter: Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 18,
|
||||
},
|
||||
custom: (),
|
||||
node: Name {
|
||||
id: "z",
|
||||
ctx: Load,
|
||||
},
|
||||
},
|
||||
ifs: [],
|
||||
is_async: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
---
|
||||
source: parser/src/parser.rs
|
||||
expression: parse_ast
|
||||
---
|
||||
Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 1,
|
||||
},
|
||||
custom: (),
|
||||
node: ListComp {
|
||||
elt: Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 2,
|
||||
},
|
||||
custom: (),
|
||||
node: Name {
|
||||
id: "x",
|
||||
ctx: Load,
|
||||
},
|
||||
},
|
||||
generators: [
|
||||
Comprehension {
|
||||
target: Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 8,
|
||||
},
|
||||
custom: (),
|
||||
node: Tuple {
|
||||
elts: [
|
||||
Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 8,
|
||||
},
|
||||
custom: (),
|
||||
node: Name {
|
||||
id: "y",
|
||||
ctx: Load,
|
||||
},
|
||||
},
|
||||
Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 11,
|
||||
},
|
||||
custom: (),
|
||||
node: Name {
|
||||
id: "y2",
|
||||
ctx: Load,
|
||||
},
|
||||
},
|
||||
],
|
||||
ctx: Load,
|
||||
},
|
||||
},
|
||||
iter: Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 17,
|
||||
},
|
||||
custom: (),
|
||||
node: Name {
|
||||
id: "z",
|
||||
ctx: Load,
|
||||
},
|
||||
},
|
||||
ifs: [],
|
||||
is_async: false,
|
||||
},
|
||||
Comprehension {
|
||||
target: Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 23,
|
||||
},
|
||||
custom: (),
|
||||
node: Name {
|
||||
id: "a",
|
||||
ctx: Load,
|
||||
},
|
||||
},
|
||||
iter: Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 28,
|
||||
},
|
||||
custom: (),
|
||||
node: Name {
|
||||
id: "b",
|
||||
ctx: Load,
|
||||
},
|
||||
},
|
||||
ifs: [
|
||||
Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 35,
|
||||
},
|
||||
custom: (),
|
||||
node: Compare {
|
||||
left: Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 33,
|
||||
},
|
||||
custom: (),
|
||||
node: Name {
|
||||
id: "a",
|
||||
ctx: Load,
|
||||
},
|
||||
},
|
||||
ops: [
|
||||
Lt,
|
||||
],
|
||||
comparators: [
|
||||
Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 37,
|
||||
},
|
||||
custom: (),
|
||||
node: Constant {
|
||||
value: Int(
|
||||
BigInt {
|
||||
sign: Plus,
|
||||
data: BigUint {
|
||||
data: [
|
||||
5,
|
||||
],
|
||||
},
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 44,
|
||||
},
|
||||
custom: (),
|
||||
node: Compare {
|
||||
left: Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 42,
|
||||
},
|
||||
custom: (),
|
||||
node: Name {
|
||||
id: "a",
|
||||
ctx: Load,
|
||||
},
|
||||
},
|
||||
ops: [
|
||||
Gt,
|
||||
],
|
||||
comparators: [
|
||||
Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 46,
|
||||
},
|
||||
custom: (),
|
||||
node: Constant {
|
||||
value: Int(
|
||||
BigInt {
|
||||
sign: Plus,
|
||||
data: BigUint {
|
||||
data: [
|
||||
10,
|
||||
],
|
||||
},
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
is_async: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
source: parser/src/parser.rs
|
||||
expression: parse_ast
|
||||
---
|
||||
[]
|
||||
@@ -0,0 +1,159 @@
|
||||
---
|
||||
source: parser/src/parser.rs
|
||||
expression: parse_ast
|
||||
---
|
||||
[
|
||||
Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 1,
|
||||
},
|
||||
custom: (),
|
||||
node: If {
|
||||
test: Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 4,
|
||||
},
|
||||
custom: (),
|
||||
node: Constant {
|
||||
value: Int(
|
||||
BigInt {
|
||||
sign: Plus,
|
||||
data: BigUint {
|
||||
data: [
|
||||
1,
|
||||
],
|
||||
},
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
},
|
||||
},
|
||||
body: [
|
||||
Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 7,
|
||||
},
|
||||
custom: (),
|
||||
node: Expr {
|
||||
value: Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 7,
|
||||
},
|
||||
custom: (),
|
||||
node: Constant {
|
||||
value: Int(
|
||||
BigInt {
|
||||
sign: Plus,
|
||||
data: BigUint {
|
||||
data: [
|
||||
10,
|
||||
],
|
||||
},
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
orelse: [
|
||||
Located {
|
||||
location: Location {
|
||||
row: 2,
|
||||
column: 1,
|
||||
},
|
||||
custom: (),
|
||||
node: If {
|
||||
test: Located {
|
||||
location: Location {
|
||||
row: 2,
|
||||
column: 6,
|
||||
},
|
||||
custom: (),
|
||||
node: Constant {
|
||||
value: Int(
|
||||
BigInt {
|
||||
sign: Plus,
|
||||
data: BigUint {
|
||||
data: [
|
||||
2,
|
||||
],
|
||||
},
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
},
|
||||
},
|
||||
body: [
|
||||
Located {
|
||||
location: Location {
|
||||
row: 2,
|
||||
column: 9,
|
||||
},
|
||||
custom: (),
|
||||
node: Expr {
|
||||
value: Located {
|
||||
location: Location {
|
||||
row: 2,
|
||||
column: 9,
|
||||
},
|
||||
custom: (),
|
||||
node: Constant {
|
||||
value: Int(
|
||||
BigInt {
|
||||
sign: Plus,
|
||||
data: BigUint {
|
||||
data: [
|
||||
20,
|
||||
],
|
||||
},
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
orelse: [
|
||||
Located {
|
||||
location: Location {
|
||||
row: 3,
|
||||
column: 7,
|
||||
},
|
||||
custom: (),
|
||||
node: Expr {
|
||||
value: Located {
|
||||
location: Location {
|
||||
row: 3,
|
||||
column: 7,
|
||||
},
|
||||
custom: (),
|
||||
node: Constant {
|
||||
value: Int(
|
||||
BigInt {
|
||||
sign: Plus,
|
||||
data: BigUint {
|
||||
data: [
|
||||
30,
|
||||
],
|
||||
},
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
@@ -0,0 +1,84 @@
|
||||
---
|
||||
source: parser/src/parser.rs
|
||||
expression: parse_ast
|
||||
---
|
||||
[
|
||||
Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 1,
|
||||
},
|
||||
custom: (),
|
||||
node: Expr {
|
||||
value: Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 8,
|
||||
},
|
||||
custom: (),
|
||||
node: Call {
|
||||
func: Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 1,
|
||||
},
|
||||
custom: (),
|
||||
node: Name {
|
||||
id: "my_func",
|
||||
ctx: Load,
|
||||
},
|
||||
},
|
||||
args: [
|
||||
Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 10,
|
||||
},
|
||||
custom: (),
|
||||
node: Constant {
|
||||
value: Str(
|
||||
"positional",
|
||||
),
|
||||
kind: None,
|
||||
},
|
||||
},
|
||||
],
|
||||
keywords: [
|
||||
Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 23,
|
||||
},
|
||||
custom: (),
|
||||
node: KeywordData {
|
||||
arg: Some(
|
||||
"keyword",
|
||||
),
|
||||
value: Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 31,
|
||||
},
|
||||
custom: (),
|
||||
node: Constant {
|
||||
value: Int(
|
||||
BigInt {
|
||||
sign: Plus,
|
||||
data: BigUint {
|
||||
data: [
|
||||
2,
|
||||
],
|
||||
},
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
@@ -0,0 +1,90 @@
|
||||
---
|
||||
source: parser/src/parser.rs
|
||||
expression: parse_ast
|
||||
---
|
||||
[
|
||||
Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 1,
|
||||
},
|
||||
custom: (),
|
||||
node: Expr {
|
||||
value: Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 1,
|
||||
},
|
||||
custom: (),
|
||||
node: Lambda {
|
||||
args: Arguments {
|
||||
posonlyargs: [],
|
||||
args: [
|
||||
Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 8,
|
||||
},
|
||||
custom: (),
|
||||
node: ArgData {
|
||||
arg: "x",
|
||||
annotation: None,
|
||||
type_comment: None,
|
||||
},
|
||||
},
|
||||
Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 11,
|
||||
},
|
||||
custom: (),
|
||||
node: ArgData {
|
||||
arg: "y",
|
||||
annotation: None,
|
||||
type_comment: None,
|
||||
},
|
||||
},
|
||||
],
|
||||
vararg: None,
|
||||
kwonlyargs: [],
|
||||
kw_defaults: [],
|
||||
kwarg: None,
|
||||
defaults: [],
|
||||
},
|
||||
body: Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 16,
|
||||
},
|
||||
custom: (),
|
||||
node: BinOp {
|
||||
left: Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 14,
|
||||
},
|
||||
custom: (),
|
||||
node: Name {
|
||||
id: "x",
|
||||
ctx: Load,
|
||||
},
|
||||
},
|
||||
op: Mult,
|
||||
right: Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 18,
|
||||
},
|
||||
custom: (),
|
||||
node: Name {
|
||||
id: "y",
|
||||
ctx: Load,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
@@ -0,0 +1,52 @@
|
||||
---
|
||||
source: parser/src/parser.rs
|
||||
expression: parse_ast
|
||||
---
|
||||
Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 1,
|
||||
},
|
||||
custom: (),
|
||||
node: ListComp {
|
||||
elt: Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 2,
|
||||
},
|
||||
custom: (),
|
||||
node: Name {
|
||||
id: "x",
|
||||
ctx: Load,
|
||||
},
|
||||
},
|
||||
generators: [
|
||||
Comprehension {
|
||||
target: Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 8,
|
||||
},
|
||||
custom: (),
|
||||
node: Name {
|
||||
id: "y",
|
||||
ctx: Load,
|
||||
},
|
||||
},
|
||||
iter: Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 13,
|
||||
},
|
||||
custom: (),
|
||||
node: Name {
|
||||
id: "z",
|
||||
ctx: Load,
|
||||
},
|
||||
},
|
||||
ifs: [],
|
||||
is_async: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
---
|
||||
source: parser/src/parser.rs
|
||||
expression: parse_ast
|
||||
---
|
||||
[
|
||||
Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 1,
|
||||
},
|
||||
custom: (),
|
||||
node: Expr {
|
||||
value: Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 6,
|
||||
},
|
||||
custom: (),
|
||||
node: Call {
|
||||
func: Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 1,
|
||||
},
|
||||
custom: (),
|
||||
node: Name {
|
||||
id: "print",
|
||||
ctx: Load,
|
||||
},
|
||||
},
|
||||
args: [
|
||||
Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 8,
|
||||
},
|
||||
custom: (),
|
||||
node: Constant {
|
||||
value: Str(
|
||||
"Hello world",
|
||||
),
|
||||
kind: None,
|
||||
},
|
||||
},
|
||||
Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 22,
|
||||
},
|
||||
custom: (),
|
||||
node: Constant {
|
||||
value: Int(
|
||||
BigInt {
|
||||
sign: Plus,
|
||||
data: BigUint {
|
||||
data: [
|
||||
2,
|
||||
],
|
||||
},
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
},
|
||||
},
|
||||
],
|
||||
keywords: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
@@ -0,0 +1,51 @@
|
||||
---
|
||||
source: parser/src/parser.rs
|
||||
expression: parse_ast
|
||||
---
|
||||
[
|
||||
Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 1,
|
||||
},
|
||||
custom: (),
|
||||
node: Expr {
|
||||
value: Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 6,
|
||||
},
|
||||
custom: (),
|
||||
node: Call {
|
||||
func: Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 1,
|
||||
},
|
||||
custom: (),
|
||||
node: Name {
|
||||
id: "print",
|
||||
ctx: Load,
|
||||
},
|
||||
},
|
||||
args: [
|
||||
Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 8,
|
||||
},
|
||||
custom: (),
|
||||
node: Constant {
|
||||
value: Str(
|
||||
"Hello world",
|
||||
),
|
||||
kind: None,
|
||||
},
|
||||
},
|
||||
],
|
||||
keywords: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
@@ -0,0 +1,104 @@
|
||||
---
|
||||
source: parser/src/parser.rs
|
||||
expression: parse_program(&source).unwrap()
|
||||
---
|
||||
[
|
||||
Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 1,
|
||||
},
|
||||
custom: (),
|
||||
node: Assign {
|
||||
targets: [
|
||||
Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 1,
|
||||
},
|
||||
custom: (),
|
||||
node: Tuple {
|
||||
elts: [
|
||||
Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 1,
|
||||
},
|
||||
custom: (),
|
||||
node: Name {
|
||||
id: "a",
|
||||
ctx: Load,
|
||||
},
|
||||
},
|
||||
Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 4,
|
||||
},
|
||||
custom: (),
|
||||
node: Name {
|
||||
id: "b",
|
||||
ctx: Load,
|
||||
},
|
||||
},
|
||||
],
|
||||
ctx: Load,
|
||||
},
|
||||
},
|
||||
],
|
||||
value: Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 8,
|
||||
},
|
||||
custom: (),
|
||||
node: Tuple {
|
||||
elts: [
|
||||
Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 8,
|
||||
},
|
||||
custom: (),
|
||||
node: Constant {
|
||||
value: Int(
|
||||
BigInt {
|
||||
sign: Plus,
|
||||
data: BigUint {
|
||||
data: [
|
||||
4,
|
||||
],
|
||||
},
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
},
|
||||
},
|
||||
Located {
|
||||
location: Location {
|
||||
row: 1,
|
||||
column: 11,
|
||||
},
|
||||
custom: (),
|
||||
node: Constant {
|
||||
value: Int(
|
||||
BigInt {
|
||||
sign: Plus,
|
||||
data: BigUint {
|
||||
data: [
|
||||
5,
|
||||
],
|
||||
},
|
||||
},
|
||||
),
|
||||
kind: None,
|
||||
},
|
||||
},
|
||||
],
|
||||
ctx: Load,
|
||||
},
|
||||
},
|
||||
type_comment: None,
|
||||
},
|
||||
},
|
||||
]
|
||||
@@ -15,8 +15,8 @@ pub enum Tok {
|
||||
Newline,
|
||||
Indent,
|
||||
Dedent,
|
||||
StartProgram,
|
||||
StartStatement,
|
||||
StartModule,
|
||||
StartInteractive,
|
||||
StartExpression,
|
||||
EndOfFile,
|
||||
Lpar,
|
||||
@@ -136,8 +136,8 @@ impl fmt::Display for Tok {
|
||||
Newline => f.write_str("Newline"),
|
||||
Indent => f.write_str("Indent"),
|
||||
Dedent => f.write_str("Dedent"),
|
||||
StartProgram => f.write_str("StartProgram"),
|
||||
StartStatement => f.write_str("StartStatement"),
|
||||
StartModule => f.write_str("StartProgram"),
|
||||
StartInteractive => f.write_str("StartInteractive"),
|
||||
StartExpression => f.write_str("StartExpression"),
|
||||
EndOfFile => f.write_str("EOF"),
|
||||
Lpar => f.write_str("'('"),
|
||||
|
||||
Reference in New Issue
Block a user