diff --git a/app/ydoc-shared/src/ast/parse.ts b/app/ydoc-shared/src/ast/parse.ts index 1cb3911ce45..2f45da4e05c 100644 --- a/app/ydoc-shared/src/ast/parse.ts +++ b/app/ydoc-shared/src/ast/parse.ts @@ -167,6 +167,10 @@ class Abstractor { } case RawAst.Tree.Type.Function: { const name = this.abstractTree(tree.name) + const signatureLine = tree.signatureLine && { + signature: this.abstractTypeSignature(tree.signatureLine.signature), + newlines: Array.from(tree.signatureLine.newlines, this.abstractToken.bind(this)), + } const private_ = tree.private && this.abstractToken(tree.private) const argumentDefinitions = Array.from(tree.args, arg => ({ open: arg.open && this.abstractToken(arg.open), @@ -186,7 +190,15 @@ class Abstractor { })) const equals = this.abstractToken(tree.equals) const body = tree.body !== undefined ? this.abstractTree(tree.body) : undefined - node = Function.concrete(this.module, private_, name, argumentDefinitions, equals, body) + node = Function.concrete( + this.module, + signatureLine, + private_, + name, + argumentDefinitions, + equals, + body, + ) break } case RawAst.Tree.Type.Ident: { @@ -408,6 +420,14 @@ class Abstractor { throw new Error('Unreachable: Splice in non-interpolated text field') } } + + private abstractTypeSignature(signature: RawAst.TypeSignature) { + return { + name: this.abstractTree(signature.name), + operator: this.abstractToken(signature.operator), + type: this.abstractTree(signature.typeNode), + } + } } declare const nodeKeyBrand: unique symbol diff --git a/app/ydoc-shared/src/ast/tree.ts b/app/ydoc-shared/src/ast/tree.ts index 3f286c5ce5b..d0f59f407a8 100644 --- a/app/ydoc-shared/src/ast/tree.ts +++ b/app/ydoc-shared/src/ast/tree.ts @@ -449,6 +449,8 @@ type StructuralField = | TextElement | ArgumentDefinition | VectorElement + | TypeSignature + | SignatureLine /** Type whose fields are all suitable for storage as `Ast` fields. */ interface FieldObject { @@ -566,6 +568,10 @@ function mapRefs( field: VectorElement, f: MapRef, ): VectorElement +function mapRefs( + field: SignatureLine, + f: MapRef, +): SignatureLine function mapRefs( field: FieldData, f: MapRef, @@ -2029,7 +2035,19 @@ interface ArgumentType { type: T['ast'] } +interface TypeSignature { + name: T['ast'] + operator: T['token'] + type: T['ast'] +} + +interface SignatureLine { + signature: TypeSignature + newlines: T['token'][] +} + export interface FunctionFields { + signatureLine: SignatureLine | undefined private_: NodeChild | undefined name: NodeChild argumentDefinitions: ArgumentDefinition[] @@ -2068,6 +2086,7 @@ export class Function extends Ast { /** TODO: Add docs */ static concrete( module: MutableModule, + signatureLine: SignatureLine | undefined, private_: NodeChild | undefined, name: NodeChild, argumentDefinitions: ArgumentDefinition[], @@ -2077,6 +2096,7 @@ export class Function extends Ast { const base = module.baseObject('Function') const id_ = base.get('id') const fields = composeFieldData(base, { + signatureLine: signatureLine && mapRefs(signatureLine, ownedToRaw(module, id_)), private_, name: concreteChild(module, name, id_), argumentDefinitions: argumentDefinitions.map(def => mapRefs(def, ownedToRaw(module, id_))), @@ -2099,6 +2119,7 @@ export class Function extends Ast { return MutableFunction.concrete( module, undefined, + undefined, unspaced(Ident.newAllowingOperators(module, name)), argumentDefinitions, spaced(makeEquals()), @@ -2140,7 +2161,15 @@ export class Function extends Ast { /** TODO: Add docs */ *concreteChildren(_verbatim?: boolean): IterableIterator { - const { private_, name, argumentDefinitions, equals, body } = getAll(this.fields) + const { signatureLine, private_, name, argumentDefinitions, equals, body } = getAll(this.fields) + if (signatureLine) { + const { signature, newlines } = signatureLine + const { name, operator, type } = signature + yield name + yield operator + yield type + yield* newlines + } if (private_) yield private_ yield name for (const def of argumentDefinitions) { diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/compiler/test/ErrorCompilerTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/compiler/test/ErrorCompilerTest.java index b5b5cc70368..6d16e1003ee 100644 --- a/engine/runtime-integration-tests/src/test/java/org/enso/compiler/test/ErrorCompilerTest.java +++ b/engine/runtime-integration-tests/src/test/java/org/enso/compiler/test/ErrorCompilerTest.java @@ -322,6 +322,7 @@ public class ErrorCompilerTest extends CompilerTests { parse( """ fan_out_to_columns : Table -> Text | Integer -> (Any -> Vector Any) -> | Nothing -> Problem_Behavior -> Table | Nothing + fan_out_to_columns table text_or_integer any_to_vector_any wat problem_behavior = Nothing """); assertSingleSyntaxError( ir, Syntax.UnexpectedExpression$.MODULE$, "Unexpected expression", 48, 119); diff --git a/engine/runtime-parser/src/main/java/org/enso/compiler/core/TreeToIr.java b/engine/runtime-parser/src/main/java/org/enso/compiler/core/TreeToIr.java index be25ef96fdd..f47deb8b132 100644 --- a/engine/runtime-parser/src/main/java/org/enso/compiler/core/TreeToIr.java +++ b/engine/runtime-parser/src/main/java/org/enso/compiler/core/TreeToIr.java @@ -1,6 +1,7 @@ package org.enso.compiler.core; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.Map; import java.util.UUID; @@ -41,7 +42,8 @@ import org.enso.syntax2.Parser; import org.enso.syntax2.TextElement; import org.enso.syntax2.Token; import org.enso.syntax2.Tree; -import org.enso.syntax2.Tree.Invalid; +import org.enso.syntax2.TypeSignature; +import org.enso.syntax2.TypeSignatureLine; import scala.Option; import scala.collection.immutable.LinearSeq; @@ -90,62 +92,60 @@ final class TreeToIr { * {@link Option#empty()}. */ Option translateInline(Tree.BodyBlock ast) { - List expressions = nil(); - java.util.List locations = new ArrayList<>(); + var expressions = new ArrayList(); for (Line statement : ast.getStatements()) { Tree exprTree = statement.getExpression(); - Expression expr = switch (exprTree) { - case null -> null; - case Tree.Export x -> null; - case Tree.Import x -> null; - case Tree.Invalid x -> null; - case Tree.TypeSignature sig -> { - Expression methodReference; - try { - methodReference = translateMethodReference(sig.getVariable(), true); - } catch (SyntaxException ex) { - methodReference = ex.toError(); - } - var signature = translateType(sig.getType()); - yield new Type.Ascription( - methodReference, - signature, - Option.empty(), - getIdentifiedLocation(sig), - meta()); - } - case Tree.TypeAnnotated anno -> translateTypeAnnotated(anno); - default -> translateExpression(exprTree); - }; - if (expr != null) { - expressions = join(expr, expressions); - if (expr.location().isDefined()) { - locations.add(expr.location().get()); + switch (exprTree) { + case null -> {} + case Tree.Export x -> {} + case Tree.Import x -> {} + case Tree.Invalid x -> {} + case Tree.TypeSignatureDeclaration sigDeclaration -> { + Expression sigIr; + try { + sigIr = (Expression)translateMethodTypeSignature(sigDeclaration.getSignature()); + } catch (SyntaxException ex) { + sigIr = ex.toError(); + } + expressions.add(sigIr); } + case Tree.TypeAnnotated anno -> expressions.add(translateTypeAnnotated(anno)); + default -> translateBlockStatement(exprTree, expressions); } } return switch (expressions.size()) { case 0 -> Option.empty(); - case 1 -> Option.apply(expressions.head()); + case 1 -> Option.apply(expressions.get(0)); default -> { - IdentifiedLocation combinedLocation; - if (locations.isEmpty()) { - combinedLocation = null; - } else { + IdentifiedLocation firstLocation = null; + for (var expr : expressions) { + if (expr.location().isDefined()) { + firstLocation = expr.location().get(); + break; + } + } + IdentifiedLocation lastLocation = null; + for (var i = expressions.size() - 1; i >= 0; i--) { + if (expressions.get(i).location().isDefined()) { + lastLocation = expressions.get(i).location().get(); + break; + } + } + IdentifiedLocation combinedLocation = null; + if (firstLocation != null && lastLocation != null) { combinedLocation = new IdentifiedLocation( - new Location( - locations.get(1).start(), - locations.get(locations.size() - 1).end() - ), + new Location(firstLocation.start(), lastLocation.end()), null ); } - var returnValue = expressions.head(); - @SuppressWarnings("unchecked") - var statements = ((List) expressions.tail()).reverse(); + Expression returnValue = null; + if (!expressions.isEmpty()) { + returnValue = expressions.get(expressions.size() - 1); + expressions.remove(expressions.size() - 1); + } yield Option.apply(new Expression.Block( - statements, + CollectionConverters.asScala(expressions.iterator()).toList(), returnValue, combinedLocation, false, @@ -242,10 +242,7 @@ final class TreeToIr { yield join(type, appendTo); } - case Tree.Function fn -> { - var binding = translateMethodBinding(fn); - yield join(binding, appendTo); - } + case Tree.Function fn -> translateMethodBinding(fn, appendTo); case Tree.ForeignFunction fn when fn.getBody() instanceof Tree.TextLiteral body -> { var name = fn.getName(); @@ -290,11 +287,8 @@ final class TreeToIr { yield translateModuleSymbol(doc.getExpression(), join(comment, appendTo)); } - case Tree.TypeSignature sig -> { - var methodReference = translateMethodReference(sig.getVariable(), true); - var signature = translateType(sig.getType()); - var ascription = new Type.Ascription(methodReference, signature, Option.empty(), - getIdentifiedLocation(sig), meta()); + case Tree.TypeSignatureDeclaration sig -> { + var ascription = translateMethodTypeSignature(sig.getSignature()); yield join(ascription, appendTo); } @@ -363,20 +357,9 @@ final class TreeToIr { yield join(ir, appendTo); } - case Tree.TypeSignature sig -> { - var isMethod = false; - if (sig.getVariable() instanceof Tree.Ident ident) { - isMethod = ident.getToken().isOperatorLexically(); - } - var typeName = translateExpression(sig.getVariable(), isMethod); - var ir = translateTypeSignature(sig, sig.getType(), typeName); - yield join(ir, appendTo); - } + case Tree.TypeSignatureDeclaration sig -> join(translateTypeSignature(sig.getSignature()), appendTo); - case Tree.Function fun -> { - var ir = translateFunction(fun); - yield join(ir, appendTo); - } + case Tree.Function fun -> translateTypeMethodBinding(fun, appendTo); case Tree.ForeignFunction fn when fn.getBody() instanceof Tree.TextLiteral body -> { var name = buildName(fn.getName()); @@ -472,8 +455,11 @@ final class TreeToIr { } } - private Definition translateMethodBinding(Tree.Function fn) + private List translateMethodBinding(Tree.Function fn, List appendTo) throws SyntaxException { + if (fn.getSignatureLine() instanceof TypeSignatureLine sigLine) { + appendTo = join(translateMethodTypeSignature(sigLine.getSignature()), appendTo); + } var methodRef = translateMethodReference(fn.getName(), false); var args = translateArgumentsDefinition(fn.getArgs()); var isPrivate = fn.getPrivate() != null; @@ -492,14 +478,35 @@ final class TreeToIr { String functionName = fn.getName().codeRepr(); var ascribedBody = addTypeAscription(functionName, body, returnSignature, loc); - return new Method.Binding( - methodRef, - args, - isPrivate, - ascribedBody, - loc, - meta() - ); + return join(new Method.Binding( + methodRef, + args, + isPrivate, + ascribedBody, + loc, + meta() + ), appendTo); + } + + private List translateTypeMethodBinding(Tree.Function fun, List appendTo) { + if (fun.getSignatureLine() instanceof TypeSignatureLine sigLine) { + appendTo = join(translateTypeSignature(sigLine.getSignature()), appendTo); + } + return join(translateFunction(fun), appendTo); + } + + private Definition translateTypeSignature(TypeSignature sig) { + var name = sig.getName(); + var isMethod = name instanceof Tree.Ident ident && ident.getToken().isOperatorLexically(); + var fnName = translateExpression(name, isMethod); + var fnType = translateType(sig.getType()); + return new Type.Ascription(fnName, fnType, Option.empty(), getIdentifiedLocation(sig), meta()); + } + + private Definition translateMethodTypeSignature(TypeSignature sig) throws SyntaxException { + var methodReference = translateMethodReference(sig.getName(), true); + var signature = translateType(sig.getType()); + return new Type.Ascription(methodReference, signature, Option.empty(), getIdentifiedLocation(sig), meta()); } private Expression translateFunction(Tree.Function fun) { @@ -568,11 +575,6 @@ final class TreeToIr { return new Type.Ascription(body, type, Option.apply(comment), loc, meta()); } - private Type.Ascription translateTypeSignature(Tree sig, Tree type, Expression typeName) { - var fn = translateType(type); - return new Type.Ascription(typeName, fn, Option.empty(), getIdentifiedLocation(sig), meta()); - } - /** * Translates a method reference from [[AST]] into [[IR]]. @@ -607,7 +609,7 @@ final class TreeToIr { } private Expression translateCall(Tree ast, boolean isMethod) throws SyntaxException { - var args = new java.util.ArrayList(); + var args = new ArrayList(); var hasDefaultsSuspended = false; var tree = ast; for (; ; ) { @@ -899,14 +901,7 @@ final class TreeToIr { yield new Application.Prefix(fn, args.reverse(), false, getIdentifiedLocation(tree), meta()); } case Tree.BodyBlock body -> translateBodyBlock(body, false); - case Tree.Assignment assign -> { - var name = buildNameOrQualifiedName(assign.getPattern()); - var expr = translateExpression(assign.getExpr(), false); - if (expr == null) { - expr = translateSyntaxError(assign, Syntax.UnexpectedExpression$.MODULE$); - } - yield new Expression.Binding(name, expr, getIdentifiedLocation(tree), meta()); - } + case Tree.Assignment assign -> translateAssignment(assign); case Tree.ArgumentBlockApplication body -> { List expressions = nil(); Expression last = null; @@ -1013,18 +1008,6 @@ final class TreeToIr { case null -> translateSyntaxError(tree, new Syntax.UnsupportedSyntax("Strange unary -")); }; - case Tree.TypeSignature sig -> { - var methodName = buildName(sig.getVariable()); - var methodReference = new CallArgument.Specified( - Option.empty(), - methodName, - methodName.identifiedLocation(), - meta() - ); - var opName = buildName(null, sig.getOperator(), true); - var signature = translateTypeCallArgument(sig.getType()); - yield new Operator.Binary(methodReference, opName, signature, getIdentifiedLocation(sig), meta()); - } case Tree.TemplateFunction templ -> translateExpression(templ.getAst(), false); case Tree.Wildcard wild -> new Name.Blank(getIdentifiedLocation(wild), meta()); case Tree.AnnotatedBuiltin anno -> { @@ -1058,37 +1041,39 @@ final class TreeToIr { }; } + private Expression translateTypeSignatureToOprApp(TypeSignature sig) { + Name.Literal methodName; + try { + methodName = buildName(sig.getName()); + } catch (SyntaxException ex) { + return ex.toError(); + } + var methodReference = new CallArgument.Specified( + Option.empty(), + methodName, + methodName.identifiedLocation(), + meta() + ); + var opName = buildName(null, sig.getOperator(), true); + var signature = translateTypeCallArgument(sig.getType()); + return new Operator.Binary(methodReference, opName, signature, getIdentifiedLocation(sig), meta()); + } + private Expression.Block translateBodyBlock(Tree.BodyBlock body, boolean suspended) { - var expressions = new java.util.ArrayList(); - Expression last = null; + var expressions = new ArrayList(); for (var line : body.getStatements()) { Tree expr = line.getExpression(); - if (expr == null) { - continue; + if (expr != null) { + translateBlockStatement(expr, expressions); } - if (last != null) { - expressions.add(last); - } - while (expr instanceof Tree.Documented doc) { - expr = doc.getExpression(); - Expression commentIr; - try { - commentIr = translateComment(doc, doc.getDocumentation()); - } catch (SyntaxException ex) { - commentIr = ex.toError(); - } - expressions.add(commentIr); - } - last = translateExpression(expr, false); } var locationWithANewLine = getIdentifiedLocation(body, 0, 0, null); - if (last == null) { - if (expressions.isEmpty()) { - last = new Empty(locationWithANewLine, meta()); - } else { - last = expressions.get(expressions.size() - 1); - expressions.remove(expressions.size() - 1); - } + Expression last; + if (expressions.isEmpty()) { + last = new Empty(locationWithANewLine, meta()); + } else { + last = expressions.get(expressions.size() - 1); + expressions.remove(expressions.size() - 1); } var list = CollectionConverters.asScala(expressions.iterator()).toList(); if (last != null @@ -1102,6 +1087,54 @@ final class TreeToIr { return new Expression.Block(list, last, locationWithANewLine, suspended, meta()); } + /** Translate a statement in the body of function. */ + private void translateBlockStatement(Tree tree, Collection appendTo) { + switch (tree) { + case null -> {} + case Tree.Assignment assign -> { + appendTo.add(translateAssignment(assign)); + } + case Tree.Function fun -> { + if (fun.getSignatureLine() instanceof TypeSignatureLine sigLine) { + appendTo.add(translateTypeSignatureToOprApp(sigLine.getSignature())); + } + appendTo.add(translateFunction(fun)); + } + case Tree.TypeSignatureDeclaration sig -> { + appendTo.add(translateTypeSignatureToOprApp(sig.getSignature())); + } + case Tree.Documented doc -> { + Expression ir; + try { + ir = translateComment(doc, doc.getDocumentation()); + } catch (SyntaxException ex) { + ir = ex.toError(); + } + appendTo.add(ir); + translateBlockStatement(doc.getExpression(), appendTo); + } + default -> { + var expressionStatement = translateExpression(tree); + if (expressionStatement != null) { + appendTo.add(expressionStatement); + } + } + } + } + + private Expression translateAssignment(Tree.Assignment assign) { + try { + var name = buildNameOrQualifiedName(assign.getPattern()); + var expr = translateExpression(assign.getExpr(), false); + if (expr == null) { + expr = translateSyntaxError(assign, Syntax.UnexpectedExpression$.MODULE$); + } + return new Expression.Binding(name, expr, getIdentifiedLocation(assign), meta()); + } catch (SyntaxException ex) { + return ex.toError(); + } + } + private void attachTranslatedWarnings(IR ir, Tree tree) { for (var warning : tree.getWarnings()) { var message = Parser.getWarningMessage(warning); @@ -1143,7 +1176,6 @@ final class TreeToIr { case Tree.Import ignored -> null; case Tree.Export ignored -> null; case Tree.TypeDef ignored -> null; - case Tree.TypeSignature ignored -> null; case Tree.ArgumentBlockApplication app -> app.getLhs(); case Tree.OperatorBlockApplication app -> app.getLhs(); case Tree.OprApp app -> app.getLhs(); @@ -1570,7 +1602,7 @@ final class TreeToIr { } private java.util.List unrollOprRhs(Tree list, String operator) throws SyntaxException { - var segments = new java.util.ArrayList(); + var segments = new ArrayList(); while (list instanceof Tree.OprApp) { var app = (Tree.OprApp) list; if (app.getOpr().getRight() == null || !operator.equals(app.getOpr().getRight().codeRepr())) { @@ -1593,7 +1625,7 @@ final class TreeToIr { } private java.util.List unrollApp(Tree list) { - var elems = new java.util.ArrayList(); + var elems = new ArrayList(); while (list instanceof Tree.App app) { elems.add(app.getArg()); list = app.getFunc(); @@ -1694,7 +1726,7 @@ final class TreeToIr { meta() ); } catch (SyntaxException err) { - if (err.where instanceof Invalid invalid) { + if (err.where instanceof Tree.Invalid invalid) { return err.toError(invalidImportReason(invalid.getError())); } else { return err.toError(invalidImportReason(null)); @@ -1758,7 +1790,7 @@ final class TreeToIr { meta() ); } catch (SyntaxException err) { - if (err.where instanceof Invalid invalid) { + if (err.where instanceof Tree.Invalid invalid) { return err.toError(invalidExportReason(invalid.getError())); } else { return err.toError(invalidExportReason(null)); @@ -1905,6 +1937,10 @@ final class TreeToIr { return new IdentifiedLocation(begin_, end_, uuid); } + private IdentifiedLocation getIdentifiedLocation(TypeSignature sig) { + return expandToContain(getIdentifiedLocation(sig.getName()), getIdentifiedLocation(sig.getType())); + } + private IdentifiedLocation getIdentifiedLocation(Token ast) { return getIdentifiedLocation(ast, false); } diff --git a/lib/rust/parser/debug/src/lib.rs b/lib/rust/parser/debug/src/lib.rs index 48e79c6d7e8..a00d750b5ad 100644 --- a/lib/rust/parser/debug/src/lib.rs +++ b/lib/rust/parser/debug/src/lib.rs @@ -97,9 +97,11 @@ where T: serde::Serialize + Reflect { }; let line = rust_to_meta[&tree::block::Line::reflect().id]; let operator_line = rust_to_meta[&tree::block::OperatorLine::reflect().id]; + let type_signature_line = rust_to_meta[&tree::TypeSignatureLine::reflect().id]; let invalid = rust_to_meta[&tree::Invalid::reflect().id]; to_s_expr.mapper(line, into_car); to_s_expr.mapper(operator_line, into_car); + to_s_expr.mapper(type_signature_line, into_car); to_s_expr.mapper(invalid, strip_invalid); to_s_expr.mapper(text_escape_token, simplify_escape); tuplify(to_s_expr.value(ast_ty, &value)) diff --git a/lib/rust/parser/debug/tests/parse.rs b/lib/rust/parser/debug/tests/parse.rs index 18bc820c2b9..03a2043c66e 100644 --- a/lib/rust/parser/debug/tests/parse.rs +++ b/lib/rust/parser/debug/tests/parse.rs @@ -271,9 +271,9 @@ fn type_methods() { (TypeDef Problem_Builder #() #( (Documented (#((Section " Returns a vector containing all reported problems, aggregated.")) #(())) - (TypeSignature (Ident build_problemset) ":" (Ident Vector))) - ,(Function::new("build_problemset", block![(Ident self)]) - .with_arg("self"))))); + ,(Function::new("build_problemset", block![(Ident self)]) + .with_sig(sexp![(Ident Vector)]) + .with_arg("self")))))); test!("[foo., bar.]", (Array (OprSectionBoundary 1 (OprApp (Ident foo) (Ok ".") ())) #(("," (OprSectionBoundary 1 (OprApp (Ident bar) (Ok ".") ())))))); @@ -288,12 +288,15 @@ fn type_operator_methods() { " Foo.+ self b = b", ].join("\n"), (TypeDef Foo #() - #((TypeSignature (Ident #"+") ":" - (OprApp (Ident Foo) (Ok "->") (OprApp (Ident Foo) (Ok "->") (Ident Foo)))) - ,(Function::new("+", sexp![(Ident b)]).with_arg("self").with_arg("b")) - (TypeSignature (OprApp (Ident Foo) (Ok ".") (Ident #"+")) ":" (Ident Foo)) + #(,(Function::new("+", sexp![(Ident b)]) + .with_sig(sexp![ + (OprApp (Ident Foo) (Ok "->") (OprApp (Ident Foo) (Ok "->") (Ident Foo)))]) + .with_arg("self") + .with_arg("b")) ,(Function::named(sexp![(OprApp (Ident Foo) (Ok ".") (Ident #"+"))], sexp![(Ident b)]) - .with_arg("self").with_arg("b"))))); + .with_sig(sexp![(Ident Foo)]) + .with_arg("self") + .with_arg("b"))))); test!("Any.==", (OprApp (Ident Any) (Ok ".") (Ident #"=="))); expect_invalid_node("x.-y"); expect_invalid_node("x.-1"); @@ -871,7 +874,7 @@ fn method_app_in_minus_unary() { #[test] fn autoscope_operator() { - test_block!("x : ..True", (TypeSignature (Ident x) ":" (AutoscopedIdentifier ".." True))); + test!("x : ..True", (TypeSignatureDeclaration ((Ident x) ":" (AutoscopedIdentifier ".." True)))); test_block!("x = ..True", (Assignment (Ident x) (AutoscopedIdentifier ".." True))); test_block!("x = f ..True", (Assignment (Ident x) (App (Ident f) (AutoscopedIdentifier ".." True)))); @@ -997,13 +1000,21 @@ fn metadata_parsing() { #[test] fn type_signatures() { - test!("val : Bool", (TypeSignature (Ident val) ":" (Ident Bool))); - test!("val : List Int", (TypeSignature (Ident val) ":" (App (Ident List) (Ident Int)))); + test!("val : Bool", (TypeSignatureDeclaration ((Ident val) ":" (Ident Bool)))); + test_block!("val : Bool\nval", (TypeSignatureDeclaration ((Ident val) ":" (Ident Bool))) (Ident val)); + test_block!("val : Bool", (TypeAnnotated (Ident val) ":" (Ident Bool))); + test!("val : Bool\nval = True", + ,(Function::new("val", sexp![(Ident True)]) + .with_sig(sexp![(Ident Bool)]))); + test!("val : Bool\ndifferent_name = True", + (TypeSignatureDeclaration ((Ident val) ":" (Ident Bool))) + ,(Function::new("different_name", sexp![(Ident True)]))); + test!("val : List Int", (TypeSignatureDeclaration ((Ident val) ":" (App (Ident List) (Ident Int))))); test!("foo : [Integer | Text] -> (Integer | Text)", - (TypeSignature (Ident foo) ":" + (TypeSignatureDeclaration ((Ident foo) ":" (OprApp (Array (OprApp (Ident Integer) (Ok "|") (Ident Text)) #()) (Ok "->") - (Group (OprApp (Ident Integer) (Ok "|") (Ident Text)))))); + (Group (OprApp (Ident Integer) (Ok "|") (Ident Text))))))); test!("f a (b : Int) : Double", (TypeAnnotated (App (App (Ident f) (Ident a)) (Group (TypeAnnotated (Ident b) ":" (Ident Int)))) @@ -1028,8 +1039,8 @@ fn type_annotations() { ":" (App (Ident My_Type) (TemplateFunction 1 (Wildcard 0)))))); test!("x : List Int -> Int", - (TypeSignature (Ident x) ":" - (OprApp (App (Ident List) (Ident Int)) (Ok "->") (Ident Int)))); + (TypeSignatureDeclaration ((Ident x) ":" + (OprApp (App (Ident List) (Ident Int)) (Ok "->") (Ident Int))))); test!("p:Plus + m:Plus", (OprApp (TypeAnnotated (Ident p) ":" (Ident Plus)) (Ok "+") (TypeAnnotated (Ident m) ":" (Ident Plus)))); @@ -1476,9 +1487,9 @@ fn attributes() { (Annotated on_problems (OprApp (Ident P) (Ok ".") (Ident g)) #(()) - (TypeSignature (OprApp (Ident Table) (Ok ".") (Ident select_columns)) - ":" - (OprApp (Ident Text) (Ok "->") (Ident Table))))); + (TypeSignatureDeclaration ((OprApp (Ident Table) (Ok ".") (Ident select_columns)) + ":" + (OprApp (Ident Text) (Ok "->") (Ident Table)))))); test!("@a z\n@b\nx", (Annotated a (Ident z) #(()) (Annotated b () #(()) (Ident x)))); test!("@a\n@b\nx", (Annotated a () #(()) (Annotated b () #(()) (Ident x)))); } @@ -1838,11 +1849,12 @@ fn expect_valid(code: &str) { /// Builder for function definitions. struct Function { - private: lexpr::Value, - name: lexpr::Value, - args: Vec, - body: lexpr::Value, - ret: lexpr::Value, + signature: lexpr::Value, + private: lexpr::Value, + name: lexpr::Value, + args: Vec, + body: lexpr::Value, + ret: lexpr::Value, } impl Function { @@ -1852,7 +1864,13 @@ impl Function { } fn named(name: lexpr::Value, body: lexpr::Value) -> Self { - Self { private: sexp![()], name, args: vec![], body, ret: sexp![()] } + Self { signature: sexp![()], private: sexp![()], name, args: vec![], body, ret: sexp![()] } + } + + #[rustfmt::skip] + fn with_sig(self, signature: lexpr::Value) -> Self { + let name = self.name.clone(); + Self { signature: sexp![(,name ":" ,signature)], ..self } } fn with_arg(mut self, arg: impl Into) -> Self { @@ -1871,8 +1889,8 @@ impl Function { impl From for lexpr::Value { #[rustfmt::skip] - fn from(Function { private, name, args, ret, body }: Function) -> Self { - sexp![(Function ,private ,name ,args ,ret ,body)] + fn from(Function { signature, private, name, args, ret, body }: Function) -> Self { + sexp![(Function ,signature ,private ,name ,args ,ret ,body)] } } diff --git a/lib/rust/parser/src/macros/resolver.rs b/lib/rust/parser/src/macros/resolver.rs index 253e75e9191..a0722c1698e 100644 --- a/lib/rust/parser/src/macros/resolver.rs +++ b/lib/rust/parser/src/macros/resolver.rs @@ -176,10 +176,11 @@ impl<'s> Finish for ResolverState<'s> { fn finish(&mut self) -> Self::Result { self.finish_current_line(); - let lines = self.lines.drain(..); let tree = match self.root_context { - RootContext::Module => syntax::tree::block::parse_module(lines, &mut self.precedence), - RootContext::Block => syntax::tree::block::parse_block(lines, &mut self.precedence), + RootContext::Module => + syntax::tree::block::parse_module(&mut self.lines, &mut self.precedence), + RootContext::Block => + syntax::tree::block::parse_block(&mut self.lines, &mut self.precedence), }; debug_assert!(self.blocks.is_empty()); debug_assert!(self.lines.is_empty()); diff --git a/lib/rust/parser/src/syntax/statement.rs b/lib/rust/parser/src/syntax/statement.rs index 52c362ec11c..689a6c0ebfd 100644 --- a/lib/rust/parser/src/syntax/statement.rs +++ b/lib/rust/parser/src/syntax/statement.rs @@ -12,14 +12,15 @@ use crate::prelude::*; use crate::syntax::item; use crate::syntax::maybe_with_error; use crate::syntax::operator::Precedence; -use crate::syntax::statement::function_def::parse_function_decl; use crate::syntax::statement::function_def::try_parse_foreign_function; +use crate::syntax::statement::function_def::FunctionBuilder; use crate::syntax::statement::type_def::try_parse_type_def; use crate::syntax::token; use crate::syntax::tree; use crate::syntax::tree::block; use crate::syntax::tree::ArgumentDefinition; use crate::syntax::tree::SyntaxError; +use crate::syntax::tree::TypeSignature; use crate::syntax::treebuilding::Spacing; use crate::syntax::Item; use crate::syntax::Token; @@ -37,12 +38,15 @@ impl<'s> BodyBlockParser<'s> { /// Parse the statements in a block. pub fn parse_body_block( &mut self, - lines: impl IntoIterator>, + lines: &mut Vec>, precedence: &mut Precedence<'s>, ) -> Tree<'s> { - let lines = lines.into_iter().map(|item::Line { newline, mut items }| block::Line { - newline, - expression: self.statement_parser.parse_statement(&mut items, 0, precedence), + let lines = compound_lines_with_tail_expression(lines, |prefixes, line, is_tail| { + if is_tail { + self.statement_parser.parse_tail_expression(line, precedence) + } else { + self.statement_parser.parse_statement(prefixes, line, precedence) + } }); Tree::body_block(block::compound_lines(lines).collect()) } @@ -50,17 +54,100 @@ impl<'s> BodyBlockParser<'s> { /// Parse the declarations and statements at the top level of a module. pub fn parse_module( &mut self, - lines: impl IntoIterator>, + lines: &mut Vec>, precedence: &mut Precedence<'s>, ) -> Tree<'s> { - let lines = lines.into_iter().map(|item::Line { newline, mut items }| block::Line { - newline, - expression: self.statement_parser.parse_module_statement(&mut items, 0, precedence), + let lines = compound_lines(lines, |prefixes, line| { + self.statement_parser.parse_module_statement(prefixes, line, precedence) }); Tree::body_block(block::compound_lines(lines).collect()) } } +fn compound_lines<'s>( + lines: &mut Vec>, + mut parse_line: impl FnMut( + &mut Vec>>, + item::Line<'s>, + ) -> Line<'s, StatementOrPrefix<'s>>, +) -> Vec> { + compound_lines_maybe_with_tail_expression( + lines, + |prefixes, line, _| parse_line(prefixes, line), + None, + ) +} + +fn compound_lines_with_tail_expression<'s>( + lines: &mut Vec>, + parse_line: impl FnMut( + &mut Vec>>, + item::Line<'s>, + bool, + ) -> Line<'s, StatementOrPrefix<'s>>, +) -> Vec> { + compound_lines_maybe_with_tail_expression( + lines, + parse_line, + lines.iter().enumerate().rfind(|(_, prefix)| !prefix.items.is_empty()).map(|(i, _)| i), + ) +} + +fn compound_lines_maybe_with_tail_expression<'s>( + lines: &mut Vec>, + mut parse_line: impl FnMut( + &mut Vec>>, + item::Line<'s>, + bool, + ) -> Line<'s, StatementOrPrefix<'s>>, + tail_index: Option, +) -> Vec> { + let mut block_lines = Vec::new(); + let mut line_prefixes = Vec::new(); + for (i, line) in lines.drain(..).enumerate() { + let is_tail = tail_index == Some(i); + match parse_line(&mut line_prefixes, line, is_tail) { + Line { newline, content: Some(StatementOrPrefix::Statement(statement)) } => { + for Line { newline, content } in line_prefixes.drain(..) { + block_lines.push(block::Line { newline, expression: content.map(Tree::from) }) + } + block_lines.push(block::Line { newline, expression: Some(statement) }) + } + Line { newline, content: Some(StatementOrPrefix::Prefix(prefix)) } => + line_prefixes.push(Line { newline, content: Some(prefix) }), + Line { newline, content: None } => + if line_prefixes.is_empty() { + block_lines.push(newline.into()); + } else { + line_prefixes.push(newline.into()); + }, + } + } + for Line { newline, content } in line_prefixes { + block_lines.push(block::Line { newline, expression: content.map(Tree::from) }) + } + block_lines +} + +#[derive(Debug)] +struct Line<'s, T> { + newline: token::Newline<'s>, + content: Option, +} + +impl<'s, T> Line<'s, T> { + fn map_content(self, f: impl FnOnce(T) -> U) -> Line<'s, U> { + let Line { newline, content } = self; + Line { newline, content: content.map(f) } + } +} + +impl<'s, T> From> for Line<'s, T> { + fn from(newline: token::Newline<'s>) -> Self { + Self { newline, content: None } + } +} + #[derive(Debug, Default)] struct StatementParser<'s> { args_buffer: Vec>, @@ -69,33 +156,49 @@ struct StatementParser<'s> { impl<'s> StatementParser<'s> { fn parse_statement( &mut self, - items: &mut Vec>, - start: usize, + prefixes: &mut Vec>>, + line: item::Line<'s>, precedence: &mut Precedence<'s>, - ) -> Option> { - parse_statement(items, start, precedence, &mut self.args_buffer, StatementContext { + ) -> Line<'s, StatementOrPrefix<'s>> { + parse_statement(prefixes, line, precedence, &mut self.args_buffer, StatementContext { evaluation_context: EvaluationContext::Eager, visibility_context: VisibilityContext::Private, + tail_expression: false, + }) + } + + fn parse_tail_expression( + &mut self, + line: item::Line<'s>, + precedence: &mut Precedence<'s>, + ) -> Line<'s, StatementOrPrefix<'s>> { + parse_statement(&mut vec![], line, precedence, &mut self.args_buffer, StatementContext { + evaluation_context: EvaluationContext::Eager, + visibility_context: VisibilityContext::Private, + tail_expression: true, }) } fn parse_module_statement( &mut self, - items: &mut Vec>, - start: usize, + prefixes: &mut Vec>>, + line: item::Line<'s>, precedence: &mut Precedence<'s>, - ) -> Option> { - parse_statement(items, start, precedence, &mut self.args_buffer, StatementContext { + ) -> Line<'s, StatementOrPrefix<'s>> { + parse_statement(prefixes, line, precedence, &mut self.args_buffer, StatementContext { evaluation_context: EvaluationContext::Lazy, visibility_context: VisibilityContext::Public, + tail_expression: false, }) - .map(|statement| { - let error = match &statement.variant { - tree::Variant::Assignment(_) => - SyntaxError::StmtUnexpectedAssignmentInModuleBody.into(), - _ => None, - }; - maybe_with_error(statement, error) + .map_content(|statement_or_prefix| { + statement_or_prefix.map_statement(|statement| { + let error = match &statement.variant { + tree::Variant::Assignment(_) => + SyntaxError::StmtUnexpectedAssignmentInModuleBody.into(), + _ => None, + }; + maybe_with_error(statement, error) + }) }) } } @@ -112,45 +215,92 @@ fn scan_private_keywords<'s>(items: impl IntoIterator .count() } +enum StatementPrefix<'s> { + TypeSignature(TypeSignature<'s>), +} + +impl<'s> From> for Tree<'s> { + fn from(value: StatementPrefix<'s>) -> Self { + match value { + StatementPrefix::TypeSignature(signature) => + Tree::type_signature_declaration(signature), + } + } +} + +enum StatementOrPrefix<'s> { + Statement(Tree<'s>), + Prefix(StatementPrefix<'s>), +} + +impl<'s> StatementOrPrefix<'s> { + fn map_statement(self, f: impl FnOnce(Tree<'s>) -> Tree<'s>) -> Self { + match self { + StatementOrPrefix::Statement(statement) => StatementOrPrefix::Statement(f(statement)), + prefix => prefix, + } + } +} + +impl<'s> From> for Tree<'s> { + fn from(value: StatementOrPrefix<'s>) -> Self { + match value { + StatementOrPrefix::Statement(tree) => tree, + StatementOrPrefix::Prefix(prefix) => prefix.into(), + } + } +} + +impl<'s> From> for StatementOrPrefix<'s> { + fn from(value: Tree<'s>) -> Self { + StatementOrPrefix::Statement(value) + } +} + fn parse_statement<'s>( - items: &mut Vec>, - statement_start: usize, + prefixes: &mut Vec>>, + mut line: item::Line<'s>, precedence: &mut Precedence<'s>, args_buffer: &mut Vec>, statement_context: StatementContext, -) -> Option> { +) -> Line<'s, StatementOrPrefix<'s>> { + let newline = line.newline; use token::Variant; - let private_keywords = scan_private_keywords(&items[statement_start..]); - let start = statement_start + private_keywords; + let private_keywords = scan_private_keywords(&line.items); + let start = private_keywords; + let items = &mut line.items; if let Some(type_def) = try_parse_type_def(items, start, precedence, args_buffer) { debug_assert_eq!(items.len(), start); - return apply_private_keywords( - Some(type_def), - items.drain(statement_start..), - statement_context.visibility_context, - ); + return Line { + newline, + content: apply_private_keywords( + Some(type_def), + items.drain(..), + statement_context.visibility_context, + ) + .map(StatementOrPrefix::Statement), + }; } let top_level_operator = match find_top_level_operator(&items[start..]) { Ok(top_level_operator) => top_level_operator.map(|(i, t)| (i + start, t)), Err(e) => - return precedence - .resolve_non_section_offset(statement_start, items) - .unwrap() - .with_error(e) - .into(), + return Line { + newline, + content: Some(precedence.resolve_non_section(items).unwrap().with_error(e).into()), + }, }; - let statement = match top_level_operator { + match top_level_operator { Some((i, Token { variant: Variant::AssignmentOperator(_), .. })) => parse_assignment_like_statement( - items, - statement_start, + prefixes, + item::Line { newline, items: mem::take(items) }, start, i, precedence, args_buffer, statement_context, ) - .into(), + .map_content(StatementOrPrefix::Statement), Some((i, Token { variant: Variant::TypeAnnotationOperator(_), .. })) => { let type_ = precedence.resolve_non_section_offset(i + 1, items); let operator: token::TypeAnnotationOperator = @@ -159,53 +309,77 @@ fn parse_statement<'s>( let type_ = type_.unwrap_or_else(|| { empty_tree(operator.code.position_after()).with_error(SyntaxError::ExpectedType) }); - if lhs.as_ref().is_some_and(is_qualified_name) { - Tree::type_signature(lhs.unwrap(), operator, type_).into() - } else { - let lhs = lhs.unwrap_or_else(|| { - empty_tree(operator.left_offset.code.position_before()) - .with_error(SyntaxError::ExpectedExpression) - }); - Tree::type_annotated(lhs, operator, type_).into() + debug_assert!(items.len() <= start); + let statement = Some( + if lhs.as_ref().is_some_and(is_qualified_name) && !statement_context.tail_expression + { + StatementOrPrefix::Prefix(StatementPrefix::TypeSignature(TypeSignature { + name: lhs.unwrap(), + operator, + type_, + })) + } else { + let lhs = lhs.unwrap_or_else(|| { + empty_tree(operator.left_offset.code.position_before()) + .with_error(SyntaxError::ExpectedExpression) + }); + Tree::type_annotated(lhs, operator, type_).into() + }, + ); + Line { + newline, + content: apply_private_keywords( + statement, + items.drain(..), + statement_context.visibility_context, + ), } } Some(_) => unreachable!(), - None => precedence.resolve_offset(start, items), - }; - debug_assert!(items.len() <= start); - apply_private_keywords( - statement, - items.drain(statement_start..), - statement_context.visibility_context, - ) + None => { + let statement = precedence.resolve_offset(start, items); + debug_assert!(items.len() <= start); + Line { + newline, + content: apply_private_keywords( + statement, + items.drain(..), + statement_context.visibility_context, + ) + .map(StatementOrPrefix::Statement), + } + } + } } /// Apply any private keywords that were not already consumed by a statement parser that recognizes /// them specifically (such as in a function definition). -fn apply_private_keywords<'s>( - mut statement: Option>, +fn apply_private_keywords<'s, U: From> + Into>>( + mut statement: Option, keywords: impl Iterator>, visibility_context: VisibilityContext, -) -> Option> { +) -> Option { for item in keywords { let private = Tree::private(item.into_token().unwrap().try_into().unwrap()); - statement = match statement.take() { - Some(statement) => Tree::app( - private.with_error(match visibility_context { - VisibilityContext::Public => SyntaxError::StmtUnexpectedPrivateSubject, - VisibilityContext::Private => SyntaxError::StmtUnexpectedPrivateContext, + statement = Some( + match statement.take() { + Some(statement) => Tree::app( + private.with_error(match visibility_context { + VisibilityContext::Public => SyntaxError::StmtUnexpectedPrivateSubject, + VisibilityContext::Private => SyntaxError::StmtUnexpectedPrivateContext, + }), + statement.into(), + ), + None => maybe_with_error(private, match visibility_context { + // This is the only non-error case in this function: A `private` keyword was + // found not modifying any other statement, and in a context where a `private` + // declaration is allowed; in this case, we emit a `Private` declaration. + VisibilityContext::Public => None, + VisibilityContext::Private => Some(SyntaxError::StmtUnexpectedPrivateContext), }), - statement, - ), - None => maybe_with_error(private, match visibility_context { - // This is the only non-error case in this function: A `private` keyword was found - // not modifying any other statement, and in a context where a `private` declaration - // is allowed; in this case, we emit a `Private` declaration without error. - VisibilityContext::Public => None, - VisibilityContext::Private => Some(SyntaxError::StmtUnexpectedPrivateContext), - }), - } - .into(); + } + .into(), + ); } statement } @@ -214,6 +388,7 @@ fn apply_private_keywords<'s>( struct StatementContext { evaluation_context: EvaluationContext, visibility_context: VisibilityContext, + tail_expression: bool, } #[derive(Debug, Copy, Clone, PartialEq, Eq)] @@ -233,19 +408,25 @@ enum VisibilityContext { } fn parse_assignment_like_statement<'s>( - items: &mut Vec>, - private_keywords_start: usize, + prefixes: &mut Vec>>, + mut line: item::Line<'s>, start: usize, operator: usize, precedence: &mut Precedence<'s>, args_buffer: &mut Vec>, - StatementContext { evaluation_context, visibility_context }: StatementContext, -) -> Tree<'s> { + StatementContext { evaluation_context, visibility_context, .. }: StatementContext, +) -> Line<'s, Tree<'s>> { + let items = &mut line.items; + let newline = line.newline; if operator == start { - return precedence + let error = precedence .resolve_non_section_offset(start, items) .unwrap() .with_error(SyntaxError::StmtInvalidAssignmentOrMethod); + return Line { + newline, + content: apply_private_keywords(Some(error), items.drain(..), visibility_context), + }; } let mut expression = precedence.resolve_offset(operator + 1, items); @@ -269,7 +450,10 @@ fn parse_assignment_like_statement<'s>( precedence, args_buffer, ) { - return function; + return Line { + newline, + content: apply_private_keywords(Some(function), items.drain(..), visibility_context), + }; } let operator = operator.unwrap(); @@ -288,23 +472,33 @@ fn parse_assignment_like_statement<'s>( (expression, Some(qn_len)) => Type::Function { expression, qn_len }, (None, None) => Type::InvalidNoExpressionNoQn, } { - Type::Assignment { expression } => - parse_assignment(start, items, operator, expression, precedence), - Type::Function { expression, qn_len } => { - let (qn, args, return_) = - parse_function_decl(items, start, qn_len, precedence, args_buffer); - let private = (visibility_context != VisibilityContext::Private - && private_keywords_start < start) - .then(|| items.pop().unwrap().into_token().unwrap().try_into().unwrap()); - - Tree::function(private, qn, args, return_, operator, expression) - } - Type::InvalidNoExpressionNoQn => Tree::opr_app( - precedence.resolve_non_section_offset(start, items), - Ok(operator.with_variant(token::variant::Operator())), - None, + Type::Assignment { expression } => Line { + newline, + content: apply_private_keywords( + Some(parse_assignment(start, items, operator, expression, precedence)), + items.drain(..), + visibility_context, + ), + }, + Type::Function { expression, qn_len } => FunctionBuilder::new( + item::Line { newline, items: mem::take(items) }, + start, + qn_len, + precedence, + args_buffer, ) - .with_error(SyntaxError::StmtInvalidAssignmentOrMethod), + .build(prefixes, operator, expression, visibility_context), + Type::InvalidNoExpressionNoQn => Line { + newline, + content: Some( + Tree::opr_app( + precedence.resolve_non_section(items), + Ok(operator.with_variant(token::variant::Operator())), + None, + ) + .with_error(SyntaxError::StmtInvalidAssignmentOrMethod), + ), + }, } } diff --git a/lib/rust/parser/src/syntax/statement/function_def.rs b/lib/rust/parser/src/syntax/statement/function_def.rs index c8deedce067..e6fd82ce286 100644 --- a/lib/rust/parser/src/syntax/statement/function_def.rs +++ b/lib/rust/parser/src/syntax/statement/function_def.rs @@ -4,8 +4,12 @@ use crate::empty_tree; use crate::syntax::item; use crate::syntax::maybe_with_error; use crate::syntax::operator::Precedence; +use crate::syntax::statement::apply_private_keywords; use crate::syntax::statement::find_top_level_operator; use crate::syntax::statement::parse_pattern; +use crate::syntax::statement::Line; +use crate::syntax::statement::StatementPrefix; +use crate::syntax::statement::VisibilityContext; use crate::syntax::token; use crate::syntax::tree; use crate::syntax::tree::ArgumentDefault; @@ -14,6 +18,7 @@ use crate::syntax::tree::ArgumentDefinitionLine; use crate::syntax::tree::ArgumentType; use crate::syntax::tree::ReturnSpecification; use crate::syntax::tree::SyntaxError; +use crate::syntax::tree::TypeSignatureLine; use crate::syntax::treebuilding::Spacing; use crate::syntax::Item; use crate::syntax::Token; @@ -21,34 +26,114 @@ use crate::syntax::Tree; -pub fn parse_function_decl<'s>( - items: &mut Vec>, - start: usize, - qn_len: usize, - precedence: &mut Precedence<'s>, - args_buffer: &mut Vec>, -) -> (Tree<'s>, Vec>, Option>) { - let mut arg_starts = vec![]; - let mut arrow = None; - for (i, item) in items.iter().enumerate().skip(start + qn_len) { - if let Item::Token(Token { variant: token::Variant::ArrowOperator(_), .. }) = item { - arrow = Some(i); - break; +pub struct FunctionBuilder<'s> { + name: Tree<'s>, + return_: Option>, + args: Vec>, + line: item::Line<'s>, + start: usize, +} + +impl<'s> FunctionBuilder<'s> { + pub fn new( + mut line: item::Line<'s>, + start: usize, + qn_len: usize, + precedence: &mut Precedence<'s>, + args_buffer: &mut Vec>, + ) -> Self { + let mut arg_starts = vec![]; + let mut arrow = None; + let items = &mut line.items; + for (i, item) in items.iter().enumerate().skip(start + qn_len) { + if let Item::Token(Token { variant: token::Variant::ArrowOperator(_), .. }) = item { + arrow = Some(i); + break; + } + if i == start + qn_len || matches!(Spacing::of_item(item), Spacing::Spaced) { + arg_starts.push(i); + } } - if i == start + qn_len || matches!(Spacing::of_item(item), Spacing::Spaced) { - arg_starts.push(i); + let return_ = arrow.map(|arrow| parse_return_spec(items, arrow, precedence)); + + args_buffer.extend( + arg_starts.drain(..).rev().map(|arg_start| parse_arg_def(items, arg_start, precedence)), + ); + let args = args_buffer.drain(..).rev().collect(); + + let name = precedence.resolve_non_section_offset(start, items).unwrap(); + + Self { name, return_, args, line, start } + } + + pub fn build( + mut self, + prefixes: &mut Vec>>, + operator: token::AssignmentOperator<'s>, + expression: Option>, + visibility_context: VisibilityContext, + ) -> Line<'s, Tree<'s>> { + let items = &mut self.line.items; + let private_keywords_start = 0; + + let private = (visibility_context != VisibilityContext::Private + && self.start > private_keywords_start) + .then(|| items.pop().unwrap().into_token().unwrap().try_into().unwrap()); + + let mut first_newline = self.line.newline; + let mut signature_line = None; + if let Some(Line { content: Some(StatementPrefix::TypeSignature(signature)), .. }) = + prefixes.last() + { + if qn_equivalent(&self.name, &signature.name) { + let Some(Line { + newline: outer_newline, + content: Some(StatementPrefix::TypeSignature(signature)), + }) = prefixes.pop() + else { + unreachable!() + }; + let newline = mem::replace(&mut first_newline, outer_newline); + signature_line = + Some(TypeSignatureLine { signature, newlines: NonEmptyVec::singleton(newline) }) + } + } + + Line { + newline: first_newline, + content: apply_private_keywords( + Some(Tree::function( + signature_line, + private, + self.name, + self.args, + self.return_, + operator, + expression, + )), + items.drain(..), + visibility_context, + ), } } - let return_ = arrow.map(|arrow| parse_return_spec(items, arrow, precedence)); +} - args_buffer.extend( - arg_starts.drain(..).rev().map(|arg_start| parse_arg_def(items, arg_start, precedence)), - ); - let args = args_buffer.drain(..).rev().collect(); +fn qn_equivalent(a: &Tree, b: &Tree) -> bool { + use tree::Variant::*; + match (&a.variant, &b.variant) { + (Ident(a), Ident(b)) => a.token.code.repr == b.token.code.repr, + (OprApp(a), OprApp(b)) => + opt_qn_equivalent(&a.lhs, &b.lhs) && opt_qn_equivalent(&a.rhs, &b.rhs), + _ => false, + } +} - let qn = precedence.resolve_non_section_offset(start, items).unwrap(); - - (qn, args, return_) +fn opt_qn_equivalent(a: &Option, b: &Option) -> bool { + match (a, b) { + (Some(a), Some(b)) => qn_equivalent(a, b), + (None, None) => true, + _ => false, + } } /// Parse a sequence of argument definitions. diff --git a/lib/rust/parser/src/syntax/statement/type_def.rs b/lib/rust/parser/src/syntax/statement/type_def.rs index 599cde60b18..ee0983db064 100644 --- a/lib/rust/parser/src/syntax/statement/type_def.rs +++ b/lib/rust/parser/src/syntax/statement/type_def.rs @@ -3,12 +3,16 @@ use crate::prelude::*; use crate::syntax::item; use crate::syntax::maybe_with_error; use crate::syntax::operator::Precedence; +use crate::syntax::statement::compound_lines; use crate::syntax::statement::function_def::parse_constructor_definition; use crate::syntax::statement::function_def::parse_type_args; use crate::syntax::statement::parse_statement; use crate::syntax::statement::scan_private_keywords; use crate::syntax::statement::EvaluationContext; +use crate::syntax::statement::Line; use crate::syntax::statement::StatementContext; +use crate::syntax::statement::StatementOrPrefix; +use crate::syntax::statement::StatementPrefix; use crate::syntax::statement::VisibilityContext; use crate::syntax::token; use crate::syntax::tree; @@ -44,20 +48,17 @@ pub fn try_parse_type_def<'s>( } let body = if let Some(Item::Block(lines)) = items.last_mut() { - let block = mem::take(lines).into_vec(); + let mut block = mem::take(lines).into_vec(); items.pop(); - let lines = block.into_iter().map(|item::Line { newline, mut items }| block::Line { - newline, - expression: { - if let Some(Item::Token(token)) = items.first_mut() { - if matches!(token.variant, token::Variant::Operator(_)) { - let opr_ident = - token::variant::Ident { is_operator_lexically: true, ..default() }; - token.variant = token::Variant::Ident(opr_ident); - } + let lines = compound_lines(&mut block, |prefixes, mut line| { + if let Some(Item::Token(token)) = line.items.first_mut() { + if matches!(token.variant, token::Variant::Operator(_)) { + let opr_ident = + token::variant::Ident { is_operator_lexically: true, ..default() }; + token.variant = token::Variant::Ident(opr_ident); } - parse_type_body_statement(items, precedence, args_buffer) - }, + } + parse_type_body_statement(prefixes, line, precedence, args_buffer) }); block::compound_lines(lines).collect() } else { @@ -77,46 +78,61 @@ pub fn try_parse_type_def<'s>( } fn parse_type_body_statement<'s>( - mut items: Vec>, + prefixes: &mut Vec>>, + mut line: item::Line<'s>, precedence: &mut Precedence<'s>, args_buffer: &mut Vec>, -) -> Option> { - let private_keywords = scan_private_keywords(&items); - let statement = match items.get(private_keywords) { +) -> Line<'s, StatementOrPrefix<'s>> { + let private_keywords = scan_private_keywords(&line.items); + match line.items.get(private_keywords) { Some(Item::Token(Token { variant: token::Variant::Ident(ident), .. })) if ident.is_type - && !items + && !line + .items .get(private_keywords + 1) .is_some_and(|item| Spacing::of_item(item) == Spacing::Unspaced) => - Some(parse_constructor_definition( + { + let item::Line { newline, mut items } = line; + let def = parse_constructor_definition( &mut items, 0, private_keywords, precedence, args_buffer, - )), - None => None, - _ => { - let tree = parse_statement(&mut items, 0, precedence, args_buffer, StatementContext { - evaluation_context: EvaluationContext::Lazy, - visibility_context: VisibilityContext::Public, - }) - .unwrap(); - let error = match &tree.variant { - tree::Variant::Function(_) - | tree::Variant::ForeignFunction(_) - | tree::Variant::Assignment(_) - | tree::Variant::Documented(_) - | tree::Variant::Annotated(_) - | tree::Variant::AnnotatedBuiltin(_) => None, - tree::Variant::TypeSignature(_) => None, - tree::Variant::TypeDef(_) => None, - _ => Some(SyntaxError::UnexpectedExpressionInTypeBody), - }; - maybe_with_error(tree, error).into() + ); + Line { + newline, + content: apply_excess_private_keywords(Some(def), items.drain(..)) + .map(StatementOrPrefix::from), + } } - }; - apply_excess_private_keywords(statement, items.drain(..)) + None => Line { + newline: line.newline, + content: apply_excess_private_keywords(None, line.items.drain(..)) + .map(StatementOrPrefix::from), + }, + _ => parse_statement(prefixes, line, precedence, args_buffer, StatementContext { + evaluation_context: EvaluationContext::Lazy, + visibility_context: VisibilityContext::Public, + tail_expression: false, + }) + .map_content(|statement_or_prefix| { + statement_or_prefix.map_statement(|tree| { + let error = match &tree.variant { + tree::Variant::Function(_) + | tree::Variant::ForeignFunction(_) + | tree::Variant::Assignment(_) + | tree::Variant::Documented(_) + | tree::Variant::Annotated(_) + | tree::Variant::AnnotatedBuiltin(_) => None, + tree::Variant::TypeSignatureDeclaration(_) => None, + tree::Variant::TypeDef(_) => None, + _ => Some(SyntaxError::UnexpectedExpressionInTypeBody), + }; + maybe_with_error(tree, error) + }) + }), + } } fn apply_excess_private_keywords<'s>( diff --git a/lib/rust/parser/src/syntax/tree.rs b/lib/rust/parser/src/syntax/tree.rs index 43b77e012fa..938ea206ad5 100644 --- a/lib/rust/parser/src/syntax/tree.rs +++ b/lib/rust/parser/src/syntax/tree.rs @@ -215,6 +215,8 @@ macro_rules! with_ast_definition { ($f:ident ($($args:tt)*)) => { $f! { $($args) }, /// A function definition, like `add x y = x + y`. Function { + /// A type signature for the function, on its own line. + pub signature_line: Option>, /// The `private` keyword, if present. This is allowed at top level and in type /// definitions, must be `None` if the context is a function body. pub private: Option>, @@ -268,15 +270,10 @@ macro_rules! with_ast_definition { ($f:ident ($($args:tt)*)) => { $f! { $($args) pub body: Option>, pub close: Option>, }, - /// Statement declaring the type of a variable. - TypeSignature { - /// (Qualified) name of the item whose type is being declared. - pub variable: Tree<'s>, - /// The `:` token. - pub operator: token::TypeAnnotationOperator<'s>, - /// The variable's type. - #[reflect(rename = "type")] - pub type_: Tree<'s>, + /// Declaration of the type of a function, that was not able to be attached to a subsequent + /// function definition. + TypeSignatureDeclaration { + pub signature: TypeSignature<'s>, }, /// An expression with explicit type information attached. TypeAnnotated { @@ -532,6 +529,41 @@ impl<'s> span::Builder<'s> for FractionalDigits<'s> { // === Functions === +/// A function type signature line. +#[cfg_attr(feature = "debug", derive(Visitor))] +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Reflect, Deserialize)] +pub struct TypeSignatureLine<'s> { + /// The type signature. + pub signature: TypeSignature<'s>, + /// The end of the type signature line. + pub newlines: NonEmptyVec>, +} + +impl<'s> span::Builder<'s> for TypeSignatureLine<'s> { + fn add_to_span(&mut self, span: Span<'s>) -> Span<'s> { + span.add(&mut self.signature).add(&mut self.newlines) + } +} + +/// Specification of the type of an item. +#[cfg_attr(feature = "debug", derive(Visitor))] +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Reflect, Deserialize)] +pub struct TypeSignature<'s> { + /// (Qualified) name of the item whose type is being declared. + pub name: Tree<'s>, + /// The `:` token. + pub operator: token::TypeAnnotationOperator<'s>, + /// The declared type. + #[reflect(rename = "type")] + pub type_: Tree<'s>, +} + +impl<'s> span::Builder<'s> for TypeSignature<'s> { + fn add_to_span(&mut self, span: Span<'s>) -> Span<'s> { + span.add(&mut self.name).add(&mut self.operator).add(&mut self.type_) + } +} + /// A function argument definition. #[cfg_attr(feature = "debug", derive(Visitor))] #[derive(Clone, Debug, Eq, PartialEq, Serialize, Reflect, Deserialize)] diff --git a/lib/rust/parser/src/syntax/tree/block.rs b/lib/rust/parser/src/syntax/tree/block.rs index dd8dd30213a..f66a44b2553 100644 --- a/lib/rust/parser/src/syntax/tree/block.rs +++ b/lib/rust/parser/src/syntax/tree/block.rs @@ -50,7 +50,7 @@ impl<'s> span::Builder<'s> for Line<'s> { /// Parse the top-level of a module. pub fn parse_module<'s>( - lines: impl IntoIterator>, + lines: &mut Vec>, precedence: &mut operator::Precedence<'s>, ) -> Tree<'s> { BodyBlockParser::default().parse_module(lines, precedence) @@ -58,7 +58,7 @@ pub fn parse_module<'s>( /// Parse a body block. pub fn parse_block<'s>( - lines: impl IntoIterator>, + lines: &mut Vec>, precedence: &mut operator::Precedence<'s>, ) -> Tree<'s> { BodyBlockParser::default().parse_body_block(lines, precedence) diff --git a/lib/rust/parser/src/syntax/treebuilding/block.rs b/lib/rust/parser/src/syntax/treebuilding/block.rs index ced40a2e5a2..f7e92046735 100644 --- a/lib/rust/parser/src/syntax/treebuilding/block.rs +++ b/lib/rust/parser/src/syntax/treebuilding/block.rs @@ -50,7 +50,7 @@ where Inner: ItemConsumer<'s> + Finish }; items.push(Item::Tree(match block_context { BlockContext::Body => - self.block_parser.parse_body_block(lines.into_vec(), &mut child), + self.block_parser.parse_body_block(&mut lines.into_vec(), &mut child), BlockContext::ArgumentOrOperator => { for item::Line { newline, items } in lines.into_vec() { self.block_builder.push(newline, items, &mut child);