mirror of
https://github.com/enso-org/enso.git
synced 2024-12-23 17:34:10 +03:00
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:
parent
6b8d8e9270
commit
e15583fe65
@ -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);
|
||||
|
@ -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))));
|
||||
|
@ -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_;
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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> },
|
||||
|
Loading…
Reference in New Issue
Block a user