Move TypeSignature into field of Function (#11364)

Move type-signature lines into `Function` field. Also implements #11293.

Stacked on #11346.
This commit is contained in:
Kaz Wesley 2024-10-22 12:50:14 -07:00 committed by GitHub
parent d278ad636c
commit 4e4a1e1df2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 770 additions and 336 deletions

View File

@ -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

View File

@ -449,6 +449,8 @@ type StructuralField<T extends TreeRefs = RawRefs> =
| TextElement<T>
| ArgumentDefinition<T>
| VectorElement<T>
| TypeSignature<T>
| SignatureLine<T>
/** Type whose fields are all suitable for storage as `Ast` fields. */
interface FieldObject<T extends TreeRefs> {
@ -566,6 +568,10 @@ function mapRefs<T extends TreeRefs, U extends TreeRefs>(
field: VectorElement<T>,
f: MapRef<T, U>,
): VectorElement<U>
function mapRefs<T extends TreeRefs, U extends TreeRefs>(
field: SignatureLine<T>,
f: MapRef<T, U>,
): SignatureLine<U>
function mapRefs<T extends TreeRefs, U extends TreeRefs>(
field: FieldData<T>,
f: MapRef<T, U>,
@ -2029,7 +2035,19 @@ interface ArgumentType<T extends TreeRefs = RawRefs> {
type: T['ast']
}
interface TypeSignature<T extends TreeRefs = RawRefs> {
name: T['ast']
operator: T['token']
type: T['ast']
}
interface SignatureLine<T extends TreeRefs = RawRefs> {
signature: TypeSignature<T>
newlines: T['token'][]
}
export interface FunctionFields {
signatureLine: SignatureLine | undefined
private_: NodeChild<SyncTokenId> | undefined
name: NodeChild<AstId>
argumentDefinitions: ArgumentDefinition[]
@ -2068,6 +2086,7 @@ export class Function extends Ast {
/** TODO: Add docs */
static concrete(
module: MutableModule,
signatureLine: SignatureLine<OwnedRefs> | undefined,
private_: NodeChild<Token> | undefined,
name: NodeChild<Owned>,
argumentDefinitions: ArgumentDefinition<OwnedRefs>[],
@ -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<RawNodeChild> {
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) {

View File

@ -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);

View File

@ -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<Expression> translateInline(Tree.BodyBlock ast) {
List<Expression> expressions = nil();
java.util.List<IdentifiedLocation> locations = new ArrayList<>();
var expressions = new ArrayList<Expression>();
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<Expression>) 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<Definition> translateMethodBinding(Tree.Function fn, List<Definition> 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<IR> translateTypeMethodBinding(Tree.Function fun, List<IR> 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<CallArgument>();
var args = new ArrayList<CallArgument>();
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<Expression> 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>();
Expression last = null;
var expressions = new ArrayList<Expression>();
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<Expression> 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<Tree> unrollOprRhs(Tree list, String operator) throws SyntaxException {
var segments = new java.util.ArrayList<Tree>();
var segments = new ArrayList<Tree>();
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<Tree> unrollApp(Tree list) {
var elems = new java.util.ArrayList<Tree>();
var elems = new ArrayList<Tree>();
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);
}

View File

@ -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))

View File

@ -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<lexpr::Value>,
body: lexpr::Value,
ret: lexpr::Value,
signature: lexpr::Value,
private: lexpr::Value,
name: lexpr::Value,
args: Vec<lexpr::Value>,
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<Arg>) -> Self {
@ -1871,8 +1889,8 @@ impl Function {
impl From<Function> 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)]
}
}

View File

@ -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());

View File

@ -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<Item = item::Line<'s>>,
lines: &mut Vec<item::Line<'s>>,
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<Item = item::Line<'s>>,
lines: &mut Vec<item::Line<'s>>,
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<item::Line<'s>>,
mut parse_line: impl FnMut(
&mut Vec<Line<'s, StatementPrefix<'s>>>,
item::Line<'s>,
) -> Line<'s, StatementOrPrefix<'s>>,
) -> Vec<block::Line<'s>> {
compound_lines_maybe_with_tail_expression(
lines,
|prefixes, line, _| parse_line(prefixes, line),
None,
)
}
fn compound_lines_with_tail_expression<'s>(
lines: &mut Vec<item::Line<'s>>,
parse_line: impl FnMut(
&mut Vec<Line<'s, StatementPrefix<'s>>>,
item::Line<'s>,
bool,
) -> Line<'s, StatementOrPrefix<'s>>,
) -> Vec<block::Line<'s>> {
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<item::Line<'s>>,
mut parse_line: impl FnMut(
&mut Vec<Line<'s, StatementPrefix<'s>>>,
item::Line<'s>,
bool,
) -> Line<'s, StatementOrPrefix<'s>>,
tail_index: Option<usize>,
) -> Vec<block::Line<'s>> {
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<T>,
}
impl<'s, T> Line<'s, T> {
fn map_content<U>(self, f: impl FnOnce(T) -> U) -> Line<'s, U> {
let Line { newline, content } = self;
Line { newline, content: content.map(f) }
}
}
impl<'s, T> From<token::Newline<'s>> for Line<'s, T> {
fn from(newline: token::Newline<'s>) -> Self {
Self { newline, content: None }
}
}
#[derive(Debug, Default)]
struct StatementParser<'s> {
args_buffer: Vec<ArgumentDefinition<'s>>,
@ -69,33 +156,49 @@ struct StatementParser<'s> {
impl<'s> StatementParser<'s> {
fn parse_statement(
&mut self,
items: &mut Vec<Item<'s>>,
start: usize,
prefixes: &mut Vec<Line<'s, StatementPrefix<'s>>>,
line: item::Line<'s>,
precedence: &mut Precedence<'s>,
) -> Option<Tree<'s>> {
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<Item<'s>>,
start: usize,
prefixes: &mut Vec<Line<'s, StatementPrefix<'s>>>,
line: item::Line<'s>,
precedence: &mut Precedence<'s>,
) -> Option<Tree<'s>> {
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<Item = impl AsRef<Item<'s>
.count()
}
enum StatementPrefix<'s> {
TypeSignature(TypeSignature<'s>),
}
impl<'s> From<StatementPrefix<'s>> 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<StatementOrPrefix<'s>> for Tree<'s> {
fn from(value: StatementOrPrefix<'s>) -> Self {
match value {
StatementOrPrefix::Statement(tree) => tree,
StatementOrPrefix::Prefix(prefix) => prefix.into(),
}
}
}
impl<'s> From<Tree<'s>> for StatementOrPrefix<'s> {
fn from(value: Tree<'s>) -> Self {
StatementOrPrefix::Statement(value)
}
}
fn parse_statement<'s>(
items: &mut Vec<Item<'s>>,
statement_start: usize,
prefixes: &mut Vec<Line<'s, StatementPrefix<'s>>>,
mut line: item::Line<'s>,
precedence: &mut Precedence<'s>,
args_buffer: &mut Vec<ArgumentDefinition<'s>>,
statement_context: StatementContext,
) -> Option<Tree<'s>> {
) -> 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<Tree<'s>>,
fn apply_private_keywords<'s, U: From<Tree<'s>> + Into<Tree<'s>>>(
mut statement: Option<U>,
keywords: impl Iterator<Item = Item<'s>>,
visibility_context: VisibilityContext,
) -> Option<Tree<'s>> {
) -> Option<U> {
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<Item<'s>>,
private_keywords_start: usize,
prefixes: &mut Vec<Line<'s, StatementPrefix<'s>>>,
mut line: item::Line<'s>,
start: usize,
operator: usize,
precedence: &mut Precedence<'s>,
args_buffer: &mut Vec<ArgumentDefinition<'s>>,
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),
),
},
}
}

View File

@ -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<Item<'s>>,
start: usize,
qn_len: usize,
precedence: &mut Precedence<'s>,
args_buffer: &mut Vec<ArgumentDefinition<'s>>,
) -> (Tree<'s>, Vec<ArgumentDefinition<'s>>, Option<ReturnSpecification<'s>>) {
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<ReturnSpecification<'s>>,
args: Vec<ArgumentDefinition<'s>>,
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<ArgumentDefinition<'s>>,
) -> 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<Line<'s, StatementPrefix<'s>>>,
operator: token::AssignmentOperator<'s>,
expression: Option<Tree<'s>>,
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<Tree>, b: &Option<Tree>) -> bool {
match (a, b) {
(Some(a), Some(b)) => qn_equivalent(a, b),
(None, None) => true,
_ => false,
}
}
/// Parse a sequence of argument definitions.

View File

@ -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<Item<'s>>,
prefixes: &mut Vec<Line<'s, StatementPrefix<'s>>>,
mut line: item::Line<'s>,
precedence: &mut Precedence<'s>,
args_buffer: &mut Vec<ArgumentDefinition<'s>>,
) -> Option<Tree<'s>> {
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>(

View File

@ -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<TypeSignatureLine<'s>>,
/// 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<token::PrivateKeyword<'s>>,
@ -268,15 +270,10 @@ macro_rules! with_ast_definition { ($f:ident ($($args:tt)*)) => { $f! { $($args)
pub body: Option<Tree<'s>>,
pub close: Option<token::CloseSymbol<'s>>,
},
/// 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<token::Newline<'s>>,
}
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)]

View File

@ -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<Item = item::Line<'s>>,
lines: &mut Vec<item::Line<'s>>,
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<Item = item::Line<'s>>,
lines: &mut Vec<item::Line<'s>>,
precedence: &mut operator::Precedence<'s>,
) -> Tree<'s> {
BodyBlockParser::default().parse_body_block(lines, precedence)

View File

@ -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);