Parser: Support annotations in type defs (#4036)

Support application of the new type of annotation to method bindings in type definitions.
This commit is contained in:
Kaz Wesley 2023-01-12 08:51:44 -08:00 committed by GitHub
parent 6b8d8e9270
commit e15583fe65
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 230 additions and 288 deletions

View File

@ -69,7 +69,6 @@ import org.enso.syntax2.Line;
import org.enso.syntax2.TextElement;
import org.enso.syntax2.Token;
import org.enso.syntax2.Tree;
import org.enso.syntax2.TypeDefStatement;
import scala.Option;
import scala.collection.immutable.LinearSeq;
@ -207,25 +206,10 @@ final class TreeToIr {
return switch (inputAst) {
case null -> appendTo;
case Tree.TypeDef def -> {
List<IR> irBody = nil();
var typeName = buildName(def.getName(), true);
List<IR> irBody = nil();
for (var line : def.getBody()) {
var definition = line.getStatement();
switch (definition) {
case null -> {}
case TypeDefStatement.Binding bind -> irBody = translateTypeBodyExpression(bind.getStatement(), irBody);
case TypeDefStatement.TypeConstructorDef cons -> {
if (cons.getDocumentation() != null) {
irBody = cons(translateComment(def, cons.getDocumentation()), irBody);
}
var constructorName = buildName(inputAst, cons.getConstructor());
List<IR.DefinitionArgument> args = translateArgumentsDefinition(cons.getArguments());
var cAt = getIdentifiedLocation(inputAst);
var ir = new IR$Module$Scope$Definition$Data(constructorName, args, cAt, meta(), diag());
irBody = cons(ir, irBody);
}
default -> {}
}
irBody = translateTypeBodyExpression(line.getExpression(), irBody);
}
List<IR.DefinitionArgument> args = translateArgumentsDefinition(def.getParams());
var type = new IR$Module$Scope$Definition$SugaredType(
@ -317,6 +301,13 @@ final class TreeToIr {
return CollectionConverters.asScala(args.stream().map(p -> translateArgumentDefinition(p)).iterator()).toList();
}
IR translateConstructorDefinition(Tree.ConstructorDefinition cons, Tree inputAst) {
var constructorName = buildName(inputAst, cons.getConstructor());
List<IR.DefinitionArgument> args = translateArgumentsDefinition(cons.getArguments());
var cAt = getIdentifiedLocation(inputAst);
return new IR$Module$Scope$Definition$Data(constructorName, args, cAt, meta(), diag());
}
/** Translates any expression that can be found in the body of a type
* declaration from [[AST]] into [[IR]].
*
@ -328,6 +319,7 @@ final class TreeToIr {
var inputAst = maybeManyParensed(exp);
return switch (inputAst) {
case null -> appendTo;
case Tree.ConstructorDefinition cons -> cons(translateConstructorDefinition(cons, inputAst), appendTo);
case Tree.TypeDef def -> {
var ir = translateSyntaxError(def, IR$Error$Syntax$UnexpectedDeclarationInType$.MODULE$);
yield cons(ir, appendTo);

View File

@ -169,29 +169,28 @@ fn type_constructors() {
#[rustfmt::skip]
let expected = block![
(TypeDef type Geo #()
#(((TypeConstructorDef
() Circle #() #(((() (Ident radius) () ())) ((() (Ident x) () ())))))
((TypeConstructorDef
() Rectangle #((() (Ident width) () ()) (() (Ident height) () ())) #()))
((TypeConstructorDef () Point #() #()))))];
#((ConstructorDefinition
Circle #() #(((() (Ident radius) () ())) ((() (Ident x) () ()))))
(ConstructorDefinition
Rectangle #((() (Ident width) () ()) (() (Ident height) () ())) #())
(ConstructorDefinition Point #() #())))];
test(&code.join("\n"), expected);
let code = "type Foo\n Bar (a : B = C.D)";
#[rustfmt::skip]
let expected = block![
(TypeDef type Foo #() #(((TypeConstructorDef
()
(TypeDef type Foo #() #((ConstructorDefinition
Bar
#((() (Ident a) (":" (Ident B)) ("=" (OprApp (Ident C) (Ok ".") (Ident D)))))
#()))))];
#())))];
test(code, expected);
let code = "type Foo\n ## Bar\n Baz";
let expected = block![(TypeDef type Foo #() #((
(TypeConstructorDef (#((Section " Bar")) #(())) Baz #() #()))))];
let expected = block![(TypeDef type Foo #() #(
(Documented (#((Section " Bar")) #(())) (ConstructorDefinition Baz #() #()))))];
test(code, expected);
let code = ["type A", " Foo (a : Integer, b : Integer)"];
#[rustfmt::skip]
let expected = block![(TypeDef type A #() #((
(TypeConstructorDef () Foo #((() (Invalid) () ())) #()))))];
let expected = block![(TypeDef type A #() #(
(ConstructorDefinition Foo #((() (Invalid) () ())) #())))];
test(&code.join("\n"), expected);
}
@ -201,9 +200,26 @@ fn type_methods() {
#[rustfmt::skip]
let expected = block![
(TypeDef type Geo #()
#(((Binding (Function (Ident number) #() "=" (BodyBlock #((Ident x))))))
((Binding (Function (Ident area) #((() (Ident self) () ())) "="
(OprApp (Ident x) (Ok "+") (Ident x)))))))];
#((Function (Ident number) #() "=" (BodyBlock #((Ident x))))
(Function (Ident area) #((() (Ident self) () ()))
"=" (OprApp (Ident x) (Ok "+") (Ident x)))))];
test(&code.join("\n"), expected);
let code = [
"type Problem_Builder",
" ## Returns a vector containing all reported problems, aggregated.",
" build_problemset : Vector",
" build_problemset self =",
" self",
];
#[rustfmt::skip]
let expected = block![
(TypeDef type Problem_Builder #() #(
(Documented
(#((Section " Returns a vector containing all reported problems, aggregated.")) #(()))
(TypeSignature (Ident build_problemset) ":" (Ident Vector)))
(Function (Ident build_problemset) #((() (Ident self) () ()))
"=" (BodyBlock #((Ident self))))))
];
test(&code.join("\n"), expected);
}
@ -220,13 +236,12 @@ fn type_operator_methods() {
#[rustfmt::skip]
let expected = block![
(TypeDef type Foo #()
#(((Binding (TypeSignature (Ident #"+") ":"
(OprApp (Ident Foo) (Ok "->") (OprApp (Ident Foo) (Ok "->") (Ident Foo))))))
((Binding
(Function (Ident #"+") #((() (Ident self) () ()) (() (Ident b) () ())) "=" (Ident b))))
((Binding (TypeSignature (OprApp (Ident Foo) (Ok ".") (Ident #"+")) ":" (Ident Foo))))
((Binding (Function (OprApp (Ident Foo) (Ok ".") (Ident #"+"))
#((() (Ident self) () ()) (() (Ident b) () ())) "=" (Ident b))))))];
#((TypeSignature (Ident #"+") ":"
(OprApp (Ident Foo) (Ok "->") (OprApp (Ident Foo) (Ok "->") (Ident Foo))))
(Function (Ident #"+") #((() (Ident self) () ()) (() (Ident b) () ())) "=" (Ident b))
(TypeSignature (OprApp (Ident Foo) (Ok ".") (Ident #"+")) ":" (Ident Foo))
(Function (OprApp (Ident Foo) (Ok ".") (Ident #"+"))
#((() (Ident self) () ()) (() (Ident b) () ())) "=" (Ident b))))];
test(&code.join("\n"), expected);
}
@ -247,16 +262,16 @@ fn type_def_full() {
#[rustfmt::skip]
let expected = block![
(TypeDef type Geo #()
#(((TypeConstructorDef () Circle #() #(
#((ConstructorDefinition Circle #() #(
((() (Ident radius) (":" (Ident float)) ()))
((() (Ident x) () ())))))
((TypeConstructorDef
() Rectangle #((() (Ident width) () ()) (() (Ident height) () ())) #()))
((TypeConstructorDef () Point #() #()))
(())
((Binding (Function (Ident number) #() "=" (BodyBlock #((Ident x))))))
((Binding (Function (Ident area) #((() (Ident self) () ())) "="
(OprApp (Ident x) (Ok "+") (Ident x)))))))];
((() (Ident x) () ()))))
(ConstructorDefinition
Rectangle #((() (Ident width) () ()) (() (Ident height) () ())) #())
(ConstructorDefinition Point #() #())
()
(Function (Ident number) #() "=" (BodyBlock #((Ident x))))
(Function (Ident area) #((() (Ident self) () ()))
"=" (OprApp (Ident x) (Ok "+") (Ident x)))))];
test(&code.join("\n"), expected);
}
@ -267,8 +282,8 @@ fn type_def_defaults() {
let expected = block![
(TypeDef type Result #((() (Ident error) () ())
(() (Ident ok) () ("=" (Ident Nothing))))
#(((TypeConstructorDef () Ok
#((() (Ident value) (":" (Ident ok)) ("=" (Ident Nothing)))) #()))))];
#((ConstructorDefinition Ok
#((() (Ident value) (":" (Ident ok)) ("=" (Ident Nothing)))) #())))];
test(&code.join("\n"), expected);
}
@ -283,8 +298,8 @@ fn type_def_nested() {
#[rustfmt::skip]
let expected = block![
(TypeDef type Foo #()
#(((Binding (TypeDef type Bar #() #())))
((Binding (TypeDef type Baz #() #())))))
#((TypeDef type Bar #() #())
(TypeDef type Baz #() #())))
];
test(&code.join("\n"), expected);
}
@ -1216,7 +1231,6 @@ fn at_operator() {
test!("foo @ bar", (OprApp (Ident foo) (Ok "@") (Ident bar)));
}
#[test]
fn attributes() {
test!("@on_problems P.g\nTable.select_columns : Text -> Table",
@ -1230,6 +1244,13 @@ fn attributes() {
test!("@a\n@b\nx", (Annotated "@" a () #(()) (Annotated "@" b () #(()) (Ident x))));
}
#[test]
fn attributes_in_types() {
test!("type A\n @a z\n @b\n x",
(TypeDef type A #() #(
(Annotated "@" a (Ident z) #(()) (Annotated "@" b () #(()) (Ident x))))));
}
#[test]
fn inline_builtin_annotations() {
test!("@Tail_Call go t", (AnnotatedBuiltin "@" Tail_Call #() (App (Ident go) (Ident t))));

View File

@ -210,6 +210,18 @@ impl Default for Parser {
fn expression_to_statement(mut tree: syntax::Tree<'_>) -> syntax::Tree<'_> {
use syntax::tree::*;
let mut left_offset = source::span::Offset::default();
if let Tree { variant: box Variant::Annotated(annotated), .. } = &mut tree {
annotated.expression = annotated.expression.take().map(expression_to_statement);
return tree;
}
if let Tree { variant: box Variant::AnnotatedBuiltin(annotated), .. } = &mut tree {
annotated.expression = annotated.expression.take().map(expression_to_statement);
return tree;
}
if let Tree { variant: box Variant::Documented(documented), .. } = &mut tree {
documented.expression = documented.expression.take().map(expression_to_statement);
return tree;
}
if let Tree { variant: box Variant::TypeAnnotated(annotated), span } = tree {
let colon = annotated.operator;
let type_ = annotated.type_;

View File

@ -291,143 +291,89 @@ fn type_def_body(matched_segments: NonEmptyVec<MatchedSegment>) -> syntax::Tree
.resolve_non_section(tokens)
.map(crate::collect_arguments_inclusive)
.unwrap_or_default();
let mut builder = TypeDefBodyBuilder::default();
for syntax::item::Line { newline, mut items } in block {
match items.first_mut() {
Some(syntax::Item::Token(syntax::Token { variant, .. }))
if matches!(variant, syntax::token::Variant::Operator(_)) =>
{
let opr_ident =
syntax::token::variant::Ident { is_operator_lexically: true, ..default() };
*variant = syntax::token::Variant::Ident(opr_ident);
}
_ => (),
for line in &mut block {
if let Some(syntax::Item::Token(syntax::Token { variant, .. })) = line.items.first_mut()
&& let syntax::token::Variant::Operator(operator) = variant
&& !operator.properties.is_annotation() {
let opr_ident =
syntax::token::variant::Ident { is_operator_lexically: true, ..default() };
*variant = syntax::token::Variant::Ident(opr_ident);
}
let expression = precedence.resolve(items);
builder.line(newline, expression);
}
let body = builder.finish();
let parse_line = |syntax::item::Line { newline, items }| block::Line {
newline,
expression: precedence.resolve(items),
};
let body = block::compound_lines(block.into_iter().map(parse_line))
.map(|line| line.map_expression(to_body_statement))
.collect();
Tree::type_def(header, name, params, body)
}
#[derive(Default)]
struct TypeDefBodyBuilder<'s> {
body: Vec<syntax::tree::TypeDefLine<'s>>,
documentation: Option<(syntax::token::Newline<'s>, syntax::tree::DocComment<'s>)>,
}
impl<'s> TypeDefBodyBuilder<'s> {
/// Apply the line to the state.
pub fn line(
&mut self,
mut newline: syntax::token::Newline<'s>,
expression: Option<syntax::Tree<'s>>,
) {
if self.documentation.is_none() &&
let Some(syntax::Tree { span, variant: box syntax::tree::Variant::Documented(
syntax::tree::Documented { mut documentation, expression: None }) }) = expression {
documentation.open.left_offset += span.left_offset;
self.documentation = (newline, documentation).into();
return;
}
if let Some((_, doc)) = &mut self.documentation && expression.is_none() {
doc.newlines.push(newline);
return;
}
let statement = expression.map(|expression| {
let mut statement = Self::to_body_statement(expression);
match &mut statement {
syntax::tree::TypeDefStatement::Constructor { constructor } => {
if let Some((nl, mut doc)) = self.documentation.take() {
let nl = mem::replace(&mut newline, nl);
doc.newlines.push(nl);
constructor.documentation = doc.into();
}
}
syntax::tree::TypeDefStatement::Binding { statement } => {
if let Some((nl, mut doc)) = self.documentation.take() {
let nl = mem::replace(&mut newline, nl);
doc.newlines.push(nl);
*statement = syntax::Tree::documented(doc, statement.clone().into());
}
}
}
statement
});
let line = syntax::tree::TypeDefLine { newline, statement };
self.body.push(line);
fn to_body_statement(mut line_expression: syntax::Tree<'_>) -> syntax::Tree<'_> {
use syntax::tree::*;
if let Tree { variant: box Variant::Documented(Documented { expression, .. }), .. } =
&mut line_expression
{
*expression = expression.take().map(to_body_statement);
return line_expression;
}
/// Return the type body statements.
pub fn finish(self) -> Vec<syntax::tree::TypeDefLine<'s>> {
let mut body = self.body;
if let Some((newline, doc)) = self.documentation {
let statement = syntax::Tree::documented(doc, default());
let statement = Some(syntax::tree::TypeDefStatement::Binding { statement });
body.push(syntax::tree::TypeDefLine { newline, statement });
}
body
if let Tree { variant: box Variant::Annotated(Annotated { expression, .. }), .. } =
&mut line_expression
{
*expression = expression.take().map(to_body_statement);
return line_expression;
}
fn to_body_statement(expression: syntax::Tree<'_>) -> syntax::tree::TypeDefStatement<'_> {
use syntax::tree::*;
let mut last_argument_default = default();
let mut left_offset = crate::source::Offset::default();
let documentation = default();
let lhs = match &expression {
Tree {
variant:
box Variant::OprApp(OprApp { lhs: Some(lhs), opr: Ok(opr), rhs: Some(rhs) }),
span,
} if opr.properties.is_assignment() => {
left_offset = span.left_offset.clone();
last_argument_default = Some((opr.clone(), rhs.clone()));
lhs
}
Tree {
variant:
box Variant::ArgumentBlockApplication(ArgumentBlockApplication {
lhs: Some(Tree { variant: box Variant::Ident(ident), span: span_ }),
arguments,
}),
span,
} => {
let mut constructor = ident.token.clone();
constructor.left_offset += &span.left_offset;
constructor.left_offset += &span_.left_offset;
let block = arguments
.iter()
.cloned()
.map(|block::Line { newline, expression }| ArgumentDefinitionLine {
newline,
argument: expression.map(crate::parse_argument_definition),
})
.collect();
let arguments = default();
let constructor =
TypeConstructorDef { documentation, constructor, arguments, block };
return TypeDefStatement::Constructor { constructor };
}
_ => &expression,
};
let (constructor, mut arguments) = crate::collect_arguments(lhs.clone());
if let Tree { variant: box Variant::Ident(Ident { token }), span } = constructor && token.is_type {
let mut constructor = token;
constructor.left_offset += left_offset;
constructor.left_offset += span.left_offset;
if let Some((equals, expression)) = last_argument_default
&& let Some(ArgumentDefinition { open: None, default, close: None, .. })
= arguments.last_mut() && default.is_none() {
*default = Some(ArgumentDefault { equals, expression });
}
let block = default();
let constructor =
TypeConstructorDef { documentation, constructor, arguments, block };
return TypeDefStatement::Constructor { constructor };
let mut last_argument_default = default();
let mut left_offset = crate::source::Offset::default();
let lhs = match &line_expression {
Tree {
variant: box Variant::OprApp(OprApp { lhs: Some(lhs), opr: Ok(opr), rhs: Some(rhs) }),
span,
} if opr.properties.is_assignment() => {
left_offset = span.left_offset.clone();
last_argument_default = Some((opr.clone(), rhs.clone()));
lhs
}
let statement = crate::expression_to_statement(expression);
TypeDefStatement::Binding { statement }
Tree {
variant:
box Variant::ArgumentBlockApplication(ArgumentBlockApplication {
lhs: Some(Tree { variant: box Variant::Ident(ident), span: span_ }),
arguments,
}),
span,
} => {
let mut constructor = ident.token.clone();
constructor.left_offset += &span.left_offset;
constructor.left_offset += &span_.left_offset;
let block = arguments
.iter()
.cloned()
.map(|block::Line { newline, expression }| ArgumentDefinitionLine {
newline,
argument: expression.map(crate::parse_argument_definition),
})
.collect();
let arguments = default();
return Tree::constructor_definition(constructor, arguments, block);
}
_ => &line_expression,
};
let (constructor, mut arguments) = crate::collect_arguments(lhs.clone());
if let Tree { variant: box Variant::Ident(Ident { token }), span } = constructor
&& token.is_type {
let mut constructor = token;
constructor.left_offset += left_offset;
constructor.left_offset += span.left_offset;
if let Some((equals, expression)) = last_argument_default
&& let Some(ArgumentDefinition { open: None, default, close: None, .. })
= arguments.last_mut() && default.is_none() {
*default = Some(ArgumentDefault { equals, expression });
}
let block = default();
return Tree::constructor_definition(constructor, arguments, block);
}
crate::expression_to_statement(line_expression)
}
/// Lambda expression.

View File

@ -207,7 +207,7 @@ macro_rules! with_ast_definition { ($f:ident ($($args:tt)*)) => { $f! { $($args)
pub keyword: token::Ident<'s>,
pub name: token::Ident<'s>,
pub params: Vec<ArgumentDefinition<'s>>,
pub body: Vec<TypeDefLine<'s>>,
pub body: Vec<block::Line<'s>>,
},
/// A variable assignment, like `foo = bar 23`.
Assignment {
@ -342,6 +342,15 @@ macro_rules! with_ast_definition { ($f:ident ($($args:tt)*)) => { $f! { $($args)
/// The item being documented.
pub expression: Option<Tree<'s>>,
},
/// Defines a type constructor.
ConstructorDefinition {
/// The identifier naming the type constructor.
pub constructor: token::Ident<'s>,
/// The arguments the type constructor accepts, specified inline.
pub arguments: Vec<ArgumentDefinition<'s>>,
/// The arguments the type constructor accepts, specified on their own lines.
pub block: Vec<ArgumentDefinitionLine<'s>>,
},
}
}};}
@ -412,77 +421,6 @@ impl<'s> span::Builder<'s> for Error {
}
// === Type Definitions ===
/// A line in a type definition's body block.
#[derive(Debug, Clone, Eq, PartialEq, Visitor, Serialize, Reflect, Deserialize)]
pub struct TypeDefLine<'s> {
/// Token ending the previous line.
pub newline: token::Newline<'s>,
/// Type definition body statement, if any.
pub statement: Option<TypeDefStatement<'s>>,
}
impl<'s> span::Builder<'s> for TypeDefLine<'s> {
fn add_to_span(&mut self, span: Span<'s>) -> Span<'s> {
span.add(&mut self.newline).add(&mut self.statement)
}
}
impl<'s> From<token::Newline<'s>> for TypeDefLine<'s> {
fn from(newline: token::Newline<'s>) -> Self {
Self { newline, statement: None }
}
}
/// A statement in a type-definition body.
#[derive(Debug, Clone, Eq, PartialEq, Visitor, Serialize, Reflect, Deserialize)]
pub enum TypeDefStatement<'s> {
/// A binding in a type-definition body.
Binding {
/// The binding statement.
statement: Tree<'s>,
},
/// A constructor definition within a type-definition body.
#[reflect(inline)]
Constructor {
/// The constructor.
constructor: TypeConstructorDef<'s>,
},
}
impl<'s> span::Builder<'s> for TypeDefStatement<'s> {
fn add_to_span(&mut self, span: Span<'s>) -> Span<'s> {
match self {
TypeDefStatement::Binding { statement } => span.add(statement),
TypeDefStatement::Constructor { constructor } => span.add(constructor),
}
}
}
/// A type constructor definition within a type definition.
#[derive(Clone, Debug, Eq, PartialEq, Visitor, Serialize, Reflect, Deserialize)]
pub struct TypeConstructorDef<'s> {
/// Documentation, if any.
pub documentation: Option<DocComment<'s>>,
/// The identifier naming the type constructor.
pub constructor: token::Ident<'s>,
/// The arguments the type constructor accepts, specified inline.
pub arguments: Vec<ArgumentDefinition<'s>>,
/// The arguments the type constructor accepts, specified on their own lines.
pub block: Vec<ArgumentDefinitionLine<'s>>,
}
impl<'s> span::Builder<'s> for TypeConstructorDef<'s> {
fn add_to_span(&mut self, span: Span<'s>) -> Span<'s> {
span.add(&mut self.documentation)
.add(&mut self.constructor)
.add(&mut self.arguments)
.add(&mut self.block)
}
}
// === Argument blocks ===
/// An argument specification on its own line.

View File

@ -54,52 +54,85 @@ impl<'s> span::Builder<'s> for Line<'s> {
/// documented statements.
pub fn body_from_lines<'s>(lines: impl IntoIterator<Item = Line<'s>>) -> Tree<'s> {
use crate::expression_to_statement;
let lines = lines.into_iter();
let mut statements = Vec::with_capacity(lines.size_hint().0);
let mut prefixes: Vec<Prefix> = Vec::new();
let mut newline = None;
for line in lines {
match prefixes.last_mut() {
Some(prefix) => prefix.newlines().push(line.newline),
None =>
if let Some(empty_line) = newline.replace(line.newline) {
statements.push(empty_line.into());
},
};
if let Some(expression) = line.expression {
match Prefix::try_from(expression_to_statement(expression)) {
Ok(prefix) => prefixes.push(prefix),
Err(mut statement) => {
for prefix in prefixes.drain(..).rev() {
statement = prefix.apply_to(statement);
}
let newline = newline.take().unwrap();
statements.push(Line { newline, expression: Some(statement) });
let lines = lines.into_iter().map(|l| l.map_expression(expression_to_statement));
let statements: Vec<_> = compound_lines(lines).collect();
Tree::body_block(statements)
}
// === Multi-line expression construction ===
/// Adapts a sequence of lines by combining sibling lines in case of multi-line statements, such as
/// annotated statements and documented statements.
pub fn compound_lines<'s, I: IntoIterator<Item = Line<'s>>>(
lines: I,
) -> CompoundLines<'s, I::IntoIter> {
CompoundLines { lines: lines.into_iter(), prefixes: default(), newline: default() }
}
/// [`Iterator`] that adapts a sequence of lines by merging multi-line statements.
#[derive(Debug)]
pub struct CompoundLines<'s, I> {
lines: I,
prefixes: Vec<Prefix<'s>>,
newline: Option<token::Newline<'s>>,
}
impl<'s, I> Iterator for CompoundLines<'s, I>
where I: Iterator<Item = Line<'s>>
{
type Item = Line<'s>;
fn next(&mut self) -> Option<Self::Item> {
for line in &mut self.lines {
match line.expression.map(Prefix::try_from) {
Some(Ok(prefix)) => {
match self.prefixes.last_mut() {
Some(prefix) => prefix.newlines().push(line.newline),
None => self.newline = Some(line.newline),
};
self.prefixes.push(prefix);
}
Some(Err(mut statement)) => {
return Some(match self.prefixes.last_mut() {
Some(prefix) => {
prefix.newlines().push(line.newline);
for prefix in self.prefixes.drain(..).rev() {
statement = prefix.apply_to(statement);
}
let newline = self.newline.take().unwrap();
Line { newline, expression: Some(statement) }
}
None => Line { newline: line.newline, expression: Some(statement) },
});
}
None => {
match self.prefixes.last_mut() {
Some(prefix) => prefix.newlines().push(line.newline),
None => return Some(line.newline.into()),
};
}
}
}
}
if let Some(prefix) = prefixes.pop() {
let mut statement = prefix.into();
for prefix in prefixes.drain(..).rev() {
statement = prefix.apply_to(statement);
if let Some(prefix) = self.prefixes.pop() {
let mut statement = prefix.into();
for prefix in self.prefixes.drain(..).rev() {
statement = prefix.apply_to(statement);
}
let newline = self.newline.take().unwrap();
return Some(Line { newline, expression: Some(statement) });
}
let newline = newline.take().unwrap();
statements.push(Line { newline, expression: Some(statement) });
}
if let Some(line) = newline {
match prefixes.last_mut() {
Some(prefix) => prefix.newlines().push(line),
None => statements.push(line.into()),
if let Some(line) = self.newline.take() {
return Some(line.into());
}
None
}
Tree::body_block(statements)
}
// === Prefix-list representation ===
/// Representation used to build multi-line statements.
#[derive(Debug)]
enum Prefix<'s> {
Annotation { node: Annotated<'s>, span: Span<'s> },
BuiltinAnnotation { node: AnnotatedBuiltin<'s>, span: Span<'s> },