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: { case RawAst.Tree.Type.Function: {
const name = this.abstractTree(tree.name) 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 private_ = tree.private && this.abstractToken(tree.private)
const argumentDefinitions = Array.from(tree.args, arg => ({ const argumentDefinitions = Array.from(tree.args, arg => ({
open: arg.open && this.abstractToken(arg.open), open: arg.open && this.abstractToken(arg.open),
@ -186,7 +190,15 @@ class Abstractor {
})) }))
const equals = this.abstractToken(tree.equals) const equals = this.abstractToken(tree.equals)
const body = tree.body !== undefined ? this.abstractTree(tree.body) : undefined 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 break
} }
case RawAst.Tree.Type.Ident: { case RawAst.Tree.Type.Ident: {
@ -408,6 +420,14 @@ class Abstractor {
throw new Error('Unreachable: Splice in non-interpolated text field') 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 declare const nodeKeyBrand: unique symbol

View File

@ -449,6 +449,8 @@ type StructuralField<T extends TreeRefs = RawRefs> =
| TextElement<T> | TextElement<T>
| ArgumentDefinition<T> | ArgumentDefinition<T>
| VectorElement<T> | VectorElement<T>
| TypeSignature<T>
| SignatureLine<T>
/** Type whose fields are all suitable for storage as `Ast` fields. */ /** Type whose fields are all suitable for storage as `Ast` fields. */
interface FieldObject<T extends TreeRefs> { interface FieldObject<T extends TreeRefs> {
@ -566,6 +568,10 @@ function mapRefs<T extends TreeRefs, U extends TreeRefs>(
field: VectorElement<T>, field: VectorElement<T>,
f: MapRef<T, U>, f: MapRef<T, U>,
): VectorElement<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>( function mapRefs<T extends TreeRefs, U extends TreeRefs>(
field: FieldData<T>, field: FieldData<T>,
f: MapRef<T, U>, f: MapRef<T, U>,
@ -2029,7 +2035,19 @@ interface ArgumentType<T extends TreeRefs = RawRefs> {
type: T['ast'] 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 { export interface FunctionFields {
signatureLine: SignatureLine | undefined
private_: NodeChild<SyncTokenId> | undefined private_: NodeChild<SyncTokenId> | undefined
name: NodeChild<AstId> name: NodeChild<AstId>
argumentDefinitions: ArgumentDefinition[] argumentDefinitions: ArgumentDefinition[]
@ -2068,6 +2086,7 @@ export class Function extends Ast {
/** TODO: Add docs */ /** TODO: Add docs */
static concrete( static concrete(
module: MutableModule, module: MutableModule,
signatureLine: SignatureLine<OwnedRefs> | undefined,
private_: NodeChild<Token> | undefined, private_: NodeChild<Token> | undefined,
name: NodeChild<Owned>, name: NodeChild<Owned>,
argumentDefinitions: ArgumentDefinition<OwnedRefs>[], argumentDefinitions: ArgumentDefinition<OwnedRefs>[],
@ -2077,6 +2096,7 @@ export class Function extends Ast {
const base = module.baseObject('Function') const base = module.baseObject('Function')
const id_ = base.get('id') const id_ = base.get('id')
const fields = composeFieldData(base, { const fields = composeFieldData(base, {
signatureLine: signatureLine && mapRefs(signatureLine, ownedToRaw(module, id_)),
private_, private_,
name: concreteChild(module, name, id_), name: concreteChild(module, name, id_),
argumentDefinitions: argumentDefinitions.map(def => mapRefs(def, ownedToRaw(module, id_))), argumentDefinitions: argumentDefinitions.map(def => mapRefs(def, ownedToRaw(module, id_))),
@ -2099,6 +2119,7 @@ export class Function extends Ast {
return MutableFunction.concrete( return MutableFunction.concrete(
module, module,
undefined, undefined,
undefined,
unspaced(Ident.newAllowingOperators(module, name)), unspaced(Ident.newAllowingOperators(module, name)),
argumentDefinitions, argumentDefinitions,
spaced(makeEquals()), spaced(makeEquals()),
@ -2140,7 +2161,15 @@ export class Function extends Ast {
/** TODO: Add docs */ /** TODO: Add docs */
*concreteChildren(_verbatim?: boolean): IterableIterator<RawNodeChild> { *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_ if (private_) yield private_
yield name yield name
for (const def of argumentDefinitions) { for (const def of argumentDefinitions) {

View File

@ -322,6 +322,7 @@ public class ErrorCompilerTest extends CompilerTests {
parse( parse(
""" """
fan_out_to_columns : Table -> Text | Integer -> (Any -> Vector Any) -> | Nothing -> Problem_Behavior -> Table | Nothing 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( assertSingleSyntaxError(
ir, Syntax.UnexpectedExpression$.MODULE$, "Unexpected expression", 48, 119); ir, Syntax.UnexpectedExpression$.MODULE$, "Unexpected expression", 48, 119);

View File

@ -1,6 +1,7 @@
package org.enso.compiler.core; package org.enso.compiler.core;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
@ -41,7 +42,8 @@ import org.enso.syntax2.Parser;
import org.enso.syntax2.TextElement; import org.enso.syntax2.TextElement;
import org.enso.syntax2.Token; import org.enso.syntax2.Token;
import org.enso.syntax2.Tree; 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.Option;
import scala.collection.immutable.LinearSeq; import scala.collection.immutable.LinearSeq;
@ -90,62 +92,60 @@ final class TreeToIr {
* {@link Option#empty()}. * {@link Option#empty()}.
*/ */
Option<Expression> translateInline(Tree.BodyBlock ast) { Option<Expression> translateInline(Tree.BodyBlock ast) {
List<Expression> expressions = nil(); var expressions = new ArrayList<Expression>();
java.util.List<IdentifiedLocation> locations = new ArrayList<>();
for (Line statement : ast.getStatements()) { for (Line statement : ast.getStatements()) {
Tree exprTree = statement.getExpression(); Tree exprTree = statement.getExpression();
Expression expr = switch (exprTree) { switch (exprTree) {
case null -> null; case null -> {}
case Tree.Export x -> null; case Tree.Export x -> {}
case Tree.Import x -> null; case Tree.Import x -> {}
case Tree.Invalid x -> null; case Tree.Invalid x -> {}
case Tree.TypeSignature sig -> { case Tree.TypeSignatureDeclaration sigDeclaration -> {
Expression methodReference; Expression sigIr;
try { try {
methodReference = translateMethodReference(sig.getVariable(), true); sigIr = (Expression)translateMethodTypeSignature(sigDeclaration.getSignature());
} catch (SyntaxException ex) { } catch (SyntaxException ex) {
methodReference = ex.toError(); sigIr = ex.toError();
} }
var signature = translateType(sig.getType()); expressions.add(sigIr);
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());
} }
case Tree.TypeAnnotated anno -> expressions.add(translateTypeAnnotated(anno));
default -> translateBlockStatement(exprTree, expressions);
} }
} }
return switch (expressions.size()) { return switch (expressions.size()) {
case 0 -> Option.empty(); case 0 -> Option.empty();
case 1 -> Option.apply(expressions.head()); case 1 -> Option.apply(expressions.get(0));
default -> { default -> {
IdentifiedLocation combinedLocation; IdentifiedLocation firstLocation = null;
if (locations.isEmpty()) { for (var expr : expressions) {
combinedLocation = null; if (expr.location().isDefined()) {
} else { 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 = combinedLocation =
new IdentifiedLocation( new IdentifiedLocation(
new Location( new Location(firstLocation.start(), lastLocation.end()),
locations.get(1).start(),
locations.get(locations.size() - 1).end()
),
null null
); );
} }
var returnValue = expressions.head(); Expression returnValue = null;
@SuppressWarnings("unchecked") if (!expressions.isEmpty()) {
var statements = ((List<Expression>) expressions.tail()).reverse(); returnValue = expressions.get(expressions.size() - 1);
expressions.remove(expressions.size() - 1);
}
yield Option.apply(new Expression.Block( yield Option.apply(new Expression.Block(
statements, CollectionConverters.asScala(expressions.iterator()).toList(),
returnValue, returnValue,
combinedLocation, combinedLocation,
false, false,
@ -242,10 +242,7 @@ final class TreeToIr {
yield join(type, appendTo); yield join(type, appendTo);
} }
case Tree.Function fn -> { case Tree.Function fn -> translateMethodBinding(fn, appendTo);
var binding = translateMethodBinding(fn);
yield join(binding, appendTo);
}
case Tree.ForeignFunction fn when fn.getBody() instanceof Tree.TextLiteral body -> { case Tree.ForeignFunction fn when fn.getBody() instanceof Tree.TextLiteral body -> {
var name = fn.getName(); var name = fn.getName();
@ -290,11 +287,8 @@ final class TreeToIr {
yield translateModuleSymbol(doc.getExpression(), join(comment, appendTo)); yield translateModuleSymbol(doc.getExpression(), join(comment, appendTo));
} }
case Tree.TypeSignature sig -> { case Tree.TypeSignatureDeclaration sig -> {
var methodReference = translateMethodReference(sig.getVariable(), true); var ascription = translateMethodTypeSignature(sig.getSignature());
var signature = translateType(sig.getType());
var ascription = new Type.Ascription(methodReference, signature, Option.empty(),
getIdentifiedLocation(sig), meta());
yield join(ascription, appendTo); yield join(ascription, appendTo);
} }
@ -363,20 +357,9 @@ final class TreeToIr {
yield join(ir, appendTo); yield join(ir, appendTo);
} }
case Tree.TypeSignature sig -> { case Tree.TypeSignatureDeclaration sig -> join(translateTypeSignature(sig.getSignature()), appendTo);
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.Function fun -> { case Tree.Function fun -> translateTypeMethodBinding(fun, appendTo);
var ir = translateFunction(fun);
yield join(ir, appendTo);
}
case Tree.ForeignFunction fn when fn.getBody() instanceof Tree.TextLiteral body -> { case Tree.ForeignFunction fn when fn.getBody() instanceof Tree.TextLiteral body -> {
var name = buildName(fn.getName()); 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 { throws SyntaxException {
if (fn.getSignatureLine() instanceof TypeSignatureLine sigLine) {
appendTo = join(translateMethodTypeSignature(sigLine.getSignature()), appendTo);
}
var methodRef = translateMethodReference(fn.getName(), false); var methodRef = translateMethodReference(fn.getName(), false);
var args = translateArgumentsDefinition(fn.getArgs()); var args = translateArgumentsDefinition(fn.getArgs());
var isPrivate = fn.getPrivate() != null; var isPrivate = fn.getPrivate() != null;
@ -492,14 +478,35 @@ final class TreeToIr {
String functionName = fn.getName().codeRepr(); String functionName = fn.getName().codeRepr();
var ascribedBody = addTypeAscription(functionName, body, returnSignature, loc); var ascribedBody = addTypeAscription(functionName, body, returnSignature, loc);
return new Method.Binding( return join(new Method.Binding(
methodRef, methodRef,
args, args,
isPrivate, isPrivate,
ascribedBody, ascribedBody,
loc, loc,
meta() 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) { private Expression translateFunction(Tree.Function fun) {
@ -568,11 +575,6 @@ final class TreeToIr {
return new Type.Ascription(body, type, Option.apply(comment), loc, meta()); 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]]. * Translates a method reference from [[AST]] into [[IR]].
@ -607,7 +609,7 @@ final class TreeToIr {
} }
private Expression translateCall(Tree ast, boolean isMethod) throws SyntaxException { 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 hasDefaultsSuspended = false;
var tree = ast; var tree = ast;
for (; ; ) { for (; ; ) {
@ -899,14 +901,7 @@ final class TreeToIr {
yield new Application.Prefix(fn, args.reverse(), false, getIdentifiedLocation(tree), meta()); yield new Application.Prefix(fn, args.reverse(), false, getIdentifiedLocation(tree), meta());
} }
case Tree.BodyBlock body -> translateBodyBlock(body, false); case Tree.BodyBlock body -> translateBodyBlock(body, false);
case Tree.Assignment assign -> { case Tree.Assignment assign -> translateAssignment(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.ArgumentBlockApplication body -> { case Tree.ArgumentBlockApplication body -> {
List<Expression> expressions = nil(); List<Expression> expressions = nil();
Expression last = null; Expression last = null;
@ -1013,18 +1008,6 @@ final class TreeToIr {
case null -> case null ->
translateSyntaxError(tree, new Syntax.UnsupportedSyntax("Strange unary -")); 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.TemplateFunction templ -> translateExpression(templ.getAst(), false);
case Tree.Wildcard wild -> new Name.Blank(getIdentifiedLocation(wild), meta()); case Tree.Wildcard wild -> new Name.Blank(getIdentifiedLocation(wild), meta());
case Tree.AnnotatedBuiltin anno -> { case Tree.AnnotatedBuiltin anno -> {
@ -1058,38 +1041,40 @@ 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) { private Expression.Block translateBodyBlock(Tree.BodyBlock body, boolean suspended) {
var expressions = new java.util.ArrayList<Expression>(); var expressions = new ArrayList<Expression>();
Expression last = null;
for (var line : body.getStatements()) { for (var line : body.getStatements()) {
Tree expr = line.getExpression(); Tree expr = line.getExpression();
if (expr == null) { if (expr != null) {
continue; 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); var locationWithANewLine = getIdentifiedLocation(body, 0, 0, null);
if (last == null) { Expression last;
if (expressions.isEmpty()) { if (expressions.isEmpty()) {
last = new Empty(locationWithANewLine, meta()); last = new Empty(locationWithANewLine, meta());
} else { } else {
last = expressions.get(expressions.size() - 1); last = expressions.get(expressions.size() - 1);
expressions.remove(expressions.size() - 1); expressions.remove(expressions.size() - 1);
} }
}
var list = CollectionConverters.asScala(expressions.iterator()).toList(); var list = CollectionConverters.asScala(expressions.iterator()).toList();
if (last != null if (last != null
&& last.location().isDefined() && last.location().isDefined()
@ -1102,6 +1087,54 @@ final class TreeToIr {
return new Expression.Block(list, last, locationWithANewLine, suspended, meta()); 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) { private void attachTranslatedWarnings(IR ir, Tree tree) {
for (var warning : tree.getWarnings()) { for (var warning : tree.getWarnings()) {
var message = Parser.getWarningMessage(warning); var message = Parser.getWarningMessage(warning);
@ -1143,7 +1176,6 @@ final class TreeToIr {
case Tree.Import ignored -> null; case Tree.Import ignored -> null;
case Tree.Export ignored -> null; case Tree.Export ignored -> null;
case Tree.TypeDef ignored -> null; case Tree.TypeDef ignored -> null;
case Tree.TypeSignature ignored -> null;
case Tree.ArgumentBlockApplication app -> app.getLhs(); case Tree.ArgumentBlockApplication app -> app.getLhs();
case Tree.OperatorBlockApplication app -> app.getLhs(); case Tree.OperatorBlockApplication app -> app.getLhs();
case Tree.OprApp 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 { 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) { while (list instanceof Tree.OprApp) {
var app = (Tree.OprApp) list; var app = (Tree.OprApp) list;
if (app.getOpr().getRight() == null || !operator.equals(app.getOpr().getRight().codeRepr())) { 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) { 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) { while (list instanceof Tree.App app) {
elems.add(app.getArg()); elems.add(app.getArg());
list = app.getFunc(); list = app.getFunc();
@ -1694,7 +1726,7 @@ final class TreeToIr {
meta() meta()
); );
} catch (SyntaxException err) { } catch (SyntaxException err) {
if (err.where instanceof Invalid invalid) { if (err.where instanceof Tree.Invalid invalid) {
return err.toError(invalidImportReason(invalid.getError())); return err.toError(invalidImportReason(invalid.getError()));
} else { } else {
return err.toError(invalidImportReason(null)); return err.toError(invalidImportReason(null));
@ -1758,7 +1790,7 @@ final class TreeToIr {
meta() meta()
); );
} catch (SyntaxException err) { } catch (SyntaxException err) {
if (err.where instanceof Invalid invalid) { if (err.where instanceof Tree.Invalid invalid) {
return err.toError(invalidExportReason(invalid.getError())); return err.toError(invalidExportReason(invalid.getError()));
} else { } else {
return err.toError(invalidExportReason(null)); return err.toError(invalidExportReason(null));
@ -1905,6 +1937,10 @@ final class TreeToIr {
return new IdentifiedLocation(begin_, end_, uuid); return new IdentifiedLocation(begin_, end_, uuid);
} }
private IdentifiedLocation getIdentifiedLocation(TypeSignature sig) {
return expandToContain(getIdentifiedLocation(sig.getName()), getIdentifiedLocation(sig.getType()));
}
private IdentifiedLocation getIdentifiedLocation(Token ast) { private IdentifiedLocation getIdentifiedLocation(Token ast) {
return getIdentifiedLocation(ast, false); 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 line = rust_to_meta[&tree::block::Line::reflect().id];
let operator_line = rust_to_meta[&tree::block::OperatorLine::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]; let invalid = rust_to_meta[&tree::Invalid::reflect().id];
to_s_expr.mapper(line, into_car); to_s_expr.mapper(line, into_car);
to_s_expr.mapper(operator_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(invalid, strip_invalid);
to_s_expr.mapper(text_escape_token, simplify_escape); to_s_expr.mapper(text_escape_token, simplify_escape);
tuplify(to_s_expr.value(ast_ty, &value)) tuplify(to_s_expr.value(ast_ty, &value))

View File

@ -271,9 +271,9 @@ fn type_methods() {
(TypeDef Problem_Builder #() #( (TypeDef Problem_Builder #() #(
(Documented (Documented
(#((Section " Returns a vector containing all reported problems, aggregated.")) #(())) (#((Section " Returns a vector containing all reported problems, aggregated.")) #(()))
(TypeSignature (Ident build_problemset) ":" (Ident Vector)))
,(Function::new("build_problemset", block![(Ident self)]) ,(Function::new("build_problemset", block![(Ident self)])
.with_arg("self"))))); .with_sig(sexp![(Ident Vector)])
.with_arg("self"))))));
test!("[foo., bar.]", test!("[foo., bar.]",
(Array (OprSectionBoundary 1 (OprApp (Ident foo) (Ok ".") ())) (Array (OprSectionBoundary 1 (OprApp (Ident foo) (Ok ".") ()))
#(("," (OprSectionBoundary 1 (OprApp (Ident bar) (Ok ".") ())))))); #(("," (OprSectionBoundary 1 (OprApp (Ident bar) (Ok ".") ()))))));
@ -288,12 +288,15 @@ fn type_operator_methods() {
" Foo.+ self b = b", " Foo.+ self b = b",
].join("\n"), ].join("\n"),
(TypeDef Foo #() (TypeDef Foo #()
#((TypeSignature (Ident #"+") ":" #(,(Function::new("+", sexp![(Ident b)])
(OprApp (Ident Foo) (Ok "->") (OprApp (Ident Foo) (Ok "->") (Ident Foo)))) .with_sig(sexp![
,(Function::new("+", sexp![(Ident b)]).with_arg("self").with_arg("b")) (OprApp (Ident Foo) (Ok "->") (OprApp (Ident Foo) (Ok "->") (Ident Foo)))])
(TypeSignature (OprApp (Ident Foo) (Ok ".") (Ident #"+")) ":" (Ident Foo)) .with_arg("self")
.with_arg("b"))
,(Function::named(sexp![(OprApp (Ident Foo) (Ok ".") (Ident #"+"))], sexp![(Ident 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 #"=="))); test!("Any.==", (OprApp (Ident Any) (Ok ".") (Ident #"==")));
expect_invalid_node("x.-y"); expect_invalid_node("x.-y");
expect_invalid_node("x.-1"); expect_invalid_node("x.-1");
@ -871,7 +874,7 @@ fn method_app_in_minus_unary() {
#[test] #[test]
fn autoscope_operator() { 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 = ..True", (Assignment (Ident x) (AutoscopedIdentifier ".." True)));
test_block!("x = f ..True", test_block!("x = f ..True",
(Assignment (Ident x) (App (Ident f) (AutoscopedIdentifier ".." True)))); (Assignment (Ident x) (App (Ident f) (AutoscopedIdentifier ".." True))));
@ -997,13 +1000,21 @@ fn metadata_parsing() {
#[test] #[test]
fn type_signatures() { fn type_signatures() {
test!("val : Bool", (TypeSignature (Ident val) ":" (Ident Bool))); test!("val : Bool", (TypeSignatureDeclaration ((Ident val) ":" (Ident Bool))));
test!("val : List Int", (TypeSignature (Ident val) ":" (App (Ident List) (Ident Int)))); 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)", test!("foo : [Integer | Text] -> (Integer | Text)",
(TypeSignature (Ident foo) ":" (TypeSignatureDeclaration ((Ident foo) ":"
(OprApp (Array (OprApp (Ident Integer) (Ok "|") (Ident Text)) #()) (OprApp (Array (OprApp (Ident Integer) (Ok "|") (Ident Text)) #())
(Ok "->") (Ok "->")
(Group (OprApp (Ident Integer) (Ok "|") (Ident Text)))))); (Group (OprApp (Ident Integer) (Ok "|") (Ident Text)))))));
test!("f a (b : Int) : Double", test!("f a (b : Int) : Double",
(TypeAnnotated (TypeAnnotated
(App (App (Ident f) (Ident a)) (Group (TypeAnnotated (Ident b) ":" (Ident Int)))) (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)))))); (App (Ident My_Type) (TemplateFunction 1 (Wildcard 0))))));
test!("x : List Int -> Int", test!("x : List Int -> Int",
(TypeSignature (Ident x) ":" (TypeSignatureDeclaration ((Ident x) ":"
(OprApp (App (Ident List) (Ident Int)) (Ok "->") (Ident Int)))); (OprApp (App (Ident List) (Ident Int)) (Ok "->") (Ident Int)))));
test!("p:Plus + m:Plus", test!("p:Plus + m:Plus",
(OprApp (TypeAnnotated (Ident p) ":" (Ident Plus)) (OprApp (TypeAnnotated (Ident p) ":" (Ident Plus))
(Ok "+") (TypeAnnotated (Ident m) ":" (Ident Plus)))); (Ok "+") (TypeAnnotated (Ident m) ":" (Ident Plus))));
@ -1476,9 +1487,9 @@ fn attributes() {
(Annotated on_problems (Annotated on_problems
(OprApp (Ident P) (Ok ".") (Ident g)) (OprApp (Ident P) (Ok ".") (Ident g))
#(()) #(())
(TypeSignature (OprApp (Ident Table) (Ok ".") (Ident select_columns)) (TypeSignatureDeclaration ((OprApp (Ident Table) (Ok ".") (Ident select_columns))
":" ":"
(OprApp (Ident Text) (Ok "->") (Ident Table))))); (OprApp (Ident Text) (Ok "->") (Ident Table))))));
test!("@a z\n@b\nx", (Annotated a (Ident z) #(()) (Annotated b () #(()) (Ident x)))); 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)))); test!("@a\n@b\nx", (Annotated a () #(()) (Annotated b () #(()) (Ident x))));
} }
@ -1838,6 +1849,7 @@ fn expect_valid(code: &str) {
/// Builder for function definitions. /// Builder for function definitions.
struct Function { struct Function {
signature: lexpr::Value,
private: lexpr::Value, private: lexpr::Value,
name: lexpr::Value, name: lexpr::Value,
args: Vec<lexpr::Value>, args: Vec<lexpr::Value>,
@ -1852,7 +1864,13 @@ impl Function {
} }
fn named(name: lexpr::Value, body: lexpr::Value) -> Self { 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 { fn with_arg(mut self, arg: impl Into<Arg>) -> Self {
@ -1871,8 +1889,8 @@ impl Function {
impl From<Function> for lexpr::Value { impl From<Function> for lexpr::Value {
#[rustfmt::skip] #[rustfmt::skip]
fn from(Function { private, name, args, ret, body }: Function) -> Self { fn from(Function { signature, private, name, args, ret, body }: Function) -> Self {
sexp![(Function ,private ,name ,args ,ret ,body)] 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 { fn finish(&mut self) -> Self::Result {
self.finish_current_line(); self.finish_current_line();
let lines = self.lines.drain(..);
let tree = match self.root_context { let tree = match self.root_context {
RootContext::Module => syntax::tree::block::parse_module(lines, &mut self.precedence), RootContext::Module =>
RootContext::Block => syntax::tree::block::parse_block(lines, &mut self.precedence), 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.blocks.is_empty());
debug_assert!(self.lines.is_empty()); debug_assert!(self.lines.is_empty());

View File

@ -12,14 +12,15 @@ use crate::prelude::*;
use crate::syntax::item; use crate::syntax::item;
use crate::syntax::maybe_with_error; use crate::syntax::maybe_with_error;
use crate::syntax::operator::Precedence; 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::try_parse_foreign_function;
use crate::syntax::statement::function_def::FunctionBuilder;
use crate::syntax::statement::type_def::try_parse_type_def; use crate::syntax::statement::type_def::try_parse_type_def;
use crate::syntax::token; use crate::syntax::token;
use crate::syntax::tree; use crate::syntax::tree;
use crate::syntax::tree::block; use crate::syntax::tree::block;
use crate::syntax::tree::ArgumentDefinition; use crate::syntax::tree::ArgumentDefinition;
use crate::syntax::tree::SyntaxError; use crate::syntax::tree::SyntaxError;
use crate::syntax::tree::TypeSignature;
use crate::syntax::treebuilding::Spacing; use crate::syntax::treebuilding::Spacing;
use crate::syntax::Item; use crate::syntax::Item;
use crate::syntax::Token; use crate::syntax::Token;
@ -37,12 +38,15 @@ impl<'s> BodyBlockParser<'s> {
/// Parse the statements in a block. /// Parse the statements in a block.
pub fn parse_body_block( pub fn parse_body_block(
&mut self, &mut self,
lines: impl IntoIterator<Item = item::Line<'s>>, lines: &mut Vec<item::Line<'s>>,
precedence: &mut Precedence<'s>, precedence: &mut Precedence<'s>,
) -> Tree<'s> { ) -> Tree<'s> {
let lines = lines.into_iter().map(|item::Line { newline, mut items }| block::Line { let lines = compound_lines_with_tail_expression(lines, |prefixes, line, is_tail| {
newline, if is_tail {
expression: self.statement_parser.parse_statement(&mut items, 0, precedence), 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()) 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. /// Parse the declarations and statements at the top level of a module.
pub fn parse_module( pub fn parse_module(
&mut self, &mut self,
lines: impl IntoIterator<Item = item::Line<'s>>, lines: &mut Vec<item::Line<'s>>,
precedence: &mut Precedence<'s>, precedence: &mut Precedence<'s>,
) -> Tree<'s> { ) -> Tree<'s> {
let lines = lines.into_iter().map(|item::Line { newline, mut items }| block::Line { let lines = compound_lines(lines, |prefixes, line| {
newline, self.statement_parser.parse_module_statement(prefixes, line, precedence)
expression: self.statement_parser.parse_module_statement(&mut items, 0, precedence),
}); });
Tree::body_block(block::compound_lines(lines).collect()) 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)] #[derive(Debug, Default)]
struct StatementParser<'s> { struct StatementParser<'s> {
args_buffer: Vec<ArgumentDefinition<'s>>, args_buffer: Vec<ArgumentDefinition<'s>>,
@ -69,27 +156,42 @@ struct StatementParser<'s> {
impl<'s> StatementParser<'s> { impl<'s> StatementParser<'s> {
fn parse_statement( fn parse_statement(
&mut self, &mut self,
items: &mut Vec<Item<'s>>, prefixes: &mut Vec<Line<'s, StatementPrefix<'s>>>,
start: usize, line: item::Line<'s>,
precedence: &mut Precedence<'s>, precedence: &mut Precedence<'s>,
) -> Option<Tree<'s>> { ) -> Line<'s, StatementOrPrefix<'s>> {
parse_statement(items, start, precedence, &mut self.args_buffer, StatementContext { parse_statement(prefixes, line, precedence, &mut self.args_buffer, StatementContext {
evaluation_context: EvaluationContext::Eager, evaluation_context: EvaluationContext::Eager,
visibility_context: VisibilityContext::Private, 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( fn parse_module_statement(
&mut self, &mut self,
items: &mut Vec<Item<'s>>, prefixes: &mut Vec<Line<'s, StatementPrefix<'s>>>,
start: usize, line: item::Line<'s>,
precedence: &mut Precedence<'s>, precedence: &mut Precedence<'s>,
) -> Option<Tree<'s>> { ) -> Line<'s, StatementOrPrefix<'s>> {
parse_statement(items, start, precedence, &mut self.args_buffer, StatementContext { parse_statement(prefixes, line, precedence, &mut self.args_buffer, StatementContext {
evaluation_context: EvaluationContext::Lazy, evaluation_context: EvaluationContext::Lazy,
visibility_context: VisibilityContext::Public, visibility_context: VisibilityContext::Public,
tail_expression: false,
}) })
.map(|statement| { .map_content(|statement_or_prefix| {
statement_or_prefix.map_statement(|statement| {
let error = match &statement.variant { let error = match &statement.variant {
tree::Variant::Assignment(_) => tree::Variant::Assignment(_) =>
SyntaxError::StmtUnexpectedAssignmentInModuleBody.into(), SyntaxError::StmtUnexpectedAssignmentInModuleBody.into(),
@ -97,6 +199,7 @@ impl<'s> StatementParser<'s> {
}; };
maybe_with_error(statement, error) maybe_with_error(statement, error)
}) })
})
} }
} }
@ -112,45 +215,92 @@ fn scan_private_keywords<'s>(items: impl IntoIterator<Item = impl AsRef<Item<'s>
.count() .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>( fn parse_statement<'s>(
items: &mut Vec<Item<'s>>, prefixes: &mut Vec<Line<'s, StatementPrefix<'s>>>,
statement_start: usize, mut line: item::Line<'s>,
precedence: &mut Precedence<'s>, precedence: &mut Precedence<'s>,
args_buffer: &mut Vec<ArgumentDefinition<'s>>, args_buffer: &mut Vec<ArgumentDefinition<'s>>,
statement_context: StatementContext, statement_context: StatementContext,
) -> Option<Tree<'s>> { ) -> Line<'s, StatementOrPrefix<'s>> {
let newline = line.newline;
use token::Variant; use token::Variant;
let private_keywords = scan_private_keywords(&items[statement_start..]); let private_keywords = scan_private_keywords(&line.items);
let start = statement_start + private_keywords; let start = private_keywords;
let items = &mut line.items;
if let Some(type_def) = try_parse_type_def(items, start, precedence, args_buffer) { if let Some(type_def) = try_parse_type_def(items, start, precedence, args_buffer) {
debug_assert_eq!(items.len(), start); debug_assert_eq!(items.len(), start);
return apply_private_keywords( return Line {
newline,
content: apply_private_keywords(
Some(type_def), Some(type_def),
items.drain(statement_start..), items.drain(..),
statement_context.visibility_context, statement_context.visibility_context,
); )
.map(StatementOrPrefix::Statement),
};
} }
let top_level_operator = match find_top_level_operator(&items[start..]) { let top_level_operator = match find_top_level_operator(&items[start..]) {
Ok(top_level_operator) => top_level_operator.map(|(i, t)| (i + start, t)), Ok(top_level_operator) => top_level_operator.map(|(i, t)| (i + start, t)),
Err(e) => Err(e) =>
return precedence return Line {
.resolve_non_section_offset(statement_start, items) newline,
.unwrap() content: Some(precedence.resolve_non_section(items).unwrap().with_error(e).into()),
.with_error(e) },
.into(),
}; };
let statement = match top_level_operator { match top_level_operator {
Some((i, Token { variant: Variant::AssignmentOperator(_), .. })) => Some((i, Token { variant: Variant::AssignmentOperator(_), .. })) =>
parse_assignment_like_statement( parse_assignment_like_statement(
items, prefixes,
statement_start, item::Line { newline, items: mem::take(items) },
start, start,
i, i,
precedence, precedence,
args_buffer, args_buffer,
statement_context, statement_context,
) )
.into(), .map_content(StatementOrPrefix::Statement),
Some((i, Token { variant: Variant::TypeAnnotationOperator(_), .. })) => { Some((i, Token { variant: Variant::TypeAnnotationOperator(_), .. })) => {
let type_ = precedence.resolve_non_section_offset(i + 1, items); let type_ = precedence.resolve_non_section_offset(i + 1, items);
let operator: token::TypeAnnotationOperator = let operator: token::TypeAnnotationOperator =
@ -159,53 +309,77 @@ fn parse_statement<'s>(
let type_ = type_.unwrap_or_else(|| { let type_ = type_.unwrap_or_else(|| {
empty_tree(operator.code.position_after()).with_error(SyntaxError::ExpectedType) empty_tree(operator.code.position_after()).with_error(SyntaxError::ExpectedType)
}); });
if lhs.as_ref().is_some_and(is_qualified_name) { debug_assert!(items.len() <= start);
Tree::type_signature(lhs.unwrap(), operator, type_).into() 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 { } else {
let lhs = lhs.unwrap_or_else(|| { let lhs = lhs.unwrap_or_else(|| {
empty_tree(operator.left_offset.code.position_before()) empty_tree(operator.left_offset.code.position_before())
.with_error(SyntaxError::ExpectedExpression) .with_error(SyntaxError::ExpectedExpression)
}); });
Tree::type_annotated(lhs, operator, type_).into() Tree::type_annotated(lhs, operator, type_).into()
},
);
Line {
newline,
content: apply_private_keywords(
statement,
items.drain(..),
statement_context.visibility_context,
),
} }
} }
Some(_) => unreachable!(), Some(_) => unreachable!(),
None => precedence.resolve_offset(start, items), None => {
}; let statement = precedence.resolve_offset(start, items);
debug_assert!(items.len() <= start); debug_assert!(items.len() <= start);
apply_private_keywords( Line {
newline,
content: apply_private_keywords(
statement, statement,
items.drain(statement_start..), items.drain(..),
statement_context.visibility_context, statement_context.visibility_context,
) )
.map(StatementOrPrefix::Statement),
}
}
}
} }
/// Apply any private keywords that were not already consumed by a statement parser that recognizes /// Apply any private keywords that were not already consumed by a statement parser that recognizes
/// them specifically (such as in a function definition). /// them specifically (such as in a function definition).
fn apply_private_keywords<'s>( fn apply_private_keywords<'s, U: From<Tree<'s>> + Into<Tree<'s>>>(
mut statement: Option<Tree<'s>>, mut statement: Option<U>,
keywords: impl Iterator<Item = Item<'s>>, keywords: impl Iterator<Item = Item<'s>>,
visibility_context: VisibilityContext, visibility_context: VisibilityContext,
) -> Option<Tree<'s>> { ) -> Option<U> {
for item in keywords { for item in keywords {
let private = Tree::private(item.into_token().unwrap().try_into().unwrap()); let private = Tree::private(item.into_token().unwrap().try_into().unwrap());
statement = match statement.take() { statement = Some(
match statement.take() {
Some(statement) => Tree::app( Some(statement) => Tree::app(
private.with_error(match visibility_context { private.with_error(match visibility_context {
VisibilityContext::Public => SyntaxError::StmtUnexpectedPrivateSubject, VisibilityContext::Public => SyntaxError::StmtUnexpectedPrivateSubject,
VisibilityContext::Private => SyntaxError::StmtUnexpectedPrivateContext, VisibilityContext::Private => SyntaxError::StmtUnexpectedPrivateContext,
}), }),
statement, statement.into(),
), ),
None => maybe_with_error(private, match visibility_context { None => maybe_with_error(private, match visibility_context {
// This is the only non-error case in this function: A `private` keyword was found // This is the only non-error case in this function: A `private` keyword was
// not modifying any other statement, and in a context where a `private` declaration // found not modifying any other statement, and in a context where a `private`
// is allowed; in this case, we emit a `Private` declaration without error. // declaration is allowed; in this case, we emit a `Private` declaration.
VisibilityContext::Public => None, VisibilityContext::Public => None,
VisibilityContext::Private => Some(SyntaxError::StmtUnexpectedPrivateContext), VisibilityContext::Private => Some(SyntaxError::StmtUnexpectedPrivateContext),
}), }),
} }
.into(); .into(),
);
} }
statement statement
} }
@ -214,6 +388,7 @@ fn apply_private_keywords<'s>(
struct StatementContext { struct StatementContext {
evaluation_context: EvaluationContext, evaluation_context: EvaluationContext,
visibility_context: VisibilityContext, visibility_context: VisibilityContext,
tail_expression: bool,
} }
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
@ -233,19 +408,25 @@ enum VisibilityContext {
} }
fn parse_assignment_like_statement<'s>( fn parse_assignment_like_statement<'s>(
items: &mut Vec<Item<'s>>, prefixes: &mut Vec<Line<'s, StatementPrefix<'s>>>,
private_keywords_start: usize, mut line: item::Line<'s>,
start: usize, start: usize,
operator: usize, operator: usize,
precedence: &mut Precedence<'s>, precedence: &mut Precedence<'s>,
args_buffer: &mut Vec<ArgumentDefinition<'s>>, args_buffer: &mut Vec<ArgumentDefinition<'s>>,
StatementContext { evaluation_context, visibility_context }: StatementContext, StatementContext { evaluation_context, visibility_context, .. }: StatementContext,
) -> Tree<'s> { ) -> Line<'s, Tree<'s>> {
let items = &mut line.items;
let newline = line.newline;
if operator == start { if operator == start {
return precedence let error = precedence
.resolve_non_section_offset(start, items) .resolve_non_section_offset(start, items)
.unwrap() .unwrap()
.with_error(SyntaxError::StmtInvalidAssignmentOrMethod); .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); let mut expression = precedence.resolve_offset(operator + 1, items);
@ -269,7 +450,10 @@ fn parse_assignment_like_statement<'s>(
precedence, precedence,
args_buffer, args_buffer,
) { ) {
return function; return Line {
newline,
content: apply_private_keywords(Some(function), items.drain(..), visibility_context),
};
} }
let operator = operator.unwrap(); let operator = operator.unwrap();
@ -288,23 +472,33 @@ fn parse_assignment_like_statement<'s>(
(expression, Some(qn_len)) => Type::Function { expression, qn_len }, (expression, Some(qn_len)) => Type::Function { expression, qn_len },
(None, None) => Type::InvalidNoExpressionNoQn, (None, None) => Type::InvalidNoExpressionNoQn,
} { } {
Type::Assignment { expression } => Type::Assignment { expression } => Line {
parse_assignment(start, items, operator, expression, precedence), newline,
Type::Function { expression, qn_len } => { content: apply_private_keywords(
let (qn, args, return_) = Some(parse_assignment(start, items, operator, expression, precedence)),
parse_function_decl(items, start, qn_len, precedence, args_buffer); items.drain(..),
let private = (visibility_context != VisibilityContext::Private visibility_context,
&& private_keywords_start < start) ),
.then(|| items.pop().unwrap().into_token().unwrap().try_into().unwrap()); },
Type::Function { expression, qn_len } => FunctionBuilder::new(
Tree::function(private, qn, args, return_, operator, expression) item::Line { newline, items: mem::take(items) },
} start,
Type::InvalidNoExpressionNoQn => Tree::opr_app( qn_len,
precedence.resolve_non_section_offset(start, items), precedence,
args_buffer,
)
.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())), Ok(operator.with_variant(token::variant::Operator())),
None, None,
) )
.with_error(SyntaxError::StmtInvalidAssignmentOrMethod), .with_error(SyntaxError::StmtInvalidAssignmentOrMethod),
),
},
} }
} }

View File

@ -4,8 +4,12 @@ use crate::empty_tree;
use crate::syntax::item; use crate::syntax::item;
use crate::syntax::maybe_with_error; use crate::syntax::maybe_with_error;
use crate::syntax::operator::Precedence; use crate::syntax::operator::Precedence;
use crate::syntax::statement::apply_private_keywords;
use crate::syntax::statement::find_top_level_operator; use crate::syntax::statement::find_top_level_operator;
use crate::syntax::statement::parse_pattern; 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::token;
use crate::syntax::tree; use crate::syntax::tree;
use crate::syntax::tree::ArgumentDefault; use crate::syntax::tree::ArgumentDefault;
@ -14,6 +18,7 @@ use crate::syntax::tree::ArgumentDefinitionLine;
use crate::syntax::tree::ArgumentType; use crate::syntax::tree::ArgumentType;
use crate::syntax::tree::ReturnSpecification; use crate::syntax::tree::ReturnSpecification;
use crate::syntax::tree::SyntaxError; use crate::syntax::tree::SyntaxError;
use crate::syntax::tree::TypeSignatureLine;
use crate::syntax::treebuilding::Spacing; use crate::syntax::treebuilding::Spacing;
use crate::syntax::Item; use crate::syntax::Item;
use crate::syntax::Token; use crate::syntax::Token;
@ -21,15 +26,25 @@ use crate::syntax::Tree;
pub fn parse_function_decl<'s>( pub struct FunctionBuilder<'s> {
items: &mut Vec<Item<'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, start: usize,
qn_len: usize, qn_len: usize,
precedence: &mut Precedence<'s>, precedence: &mut Precedence<'s>,
args_buffer: &mut Vec<ArgumentDefinition<'s>>, args_buffer: &mut Vec<ArgumentDefinition<'s>>,
) -> (Tree<'s>, Vec<ArgumentDefinition<'s>>, Option<ReturnSpecification<'s>>) { ) -> Self {
let mut arg_starts = vec![]; let mut arg_starts = vec![];
let mut arrow = None; let mut arrow = None;
let items = &mut line.items;
for (i, item) in items.iter().enumerate().skip(start + qn_len) { for (i, item) in items.iter().enumerate().skip(start + qn_len) {
if let Item::Token(Token { variant: token::Variant::ArrowOperator(_), .. }) = item { if let Item::Token(Token { variant: token::Variant::ArrowOperator(_), .. }) = item {
arrow = Some(i); arrow = Some(i);
@ -46,9 +61,79 @@ pub fn parse_function_decl<'s>(
); );
let args = args_buffer.drain(..).rev().collect(); let args = args_buffer.drain(..).rev().collect();
let qn = precedence.resolve_non_section_offset(start, items).unwrap(); let name = precedence.resolve_non_section_offset(start, items).unwrap();
(qn, args, return_) 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,
),
}
}
}
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,
}
}
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. /// Parse a sequence of argument definitions.

View File

@ -3,12 +3,16 @@ use crate::prelude::*;
use crate::syntax::item; use crate::syntax::item;
use crate::syntax::maybe_with_error; use crate::syntax::maybe_with_error;
use crate::syntax::operator::Precedence; 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_constructor_definition;
use crate::syntax::statement::function_def::parse_type_args; use crate::syntax::statement::function_def::parse_type_args;
use crate::syntax::statement::parse_statement; use crate::syntax::statement::parse_statement;
use crate::syntax::statement::scan_private_keywords; use crate::syntax::statement::scan_private_keywords;
use crate::syntax::statement::EvaluationContext; use crate::syntax::statement::EvaluationContext;
use crate::syntax::statement::Line;
use crate::syntax::statement::StatementContext; use crate::syntax::statement::StatementContext;
use crate::syntax::statement::StatementOrPrefix;
use crate::syntax::statement::StatementPrefix;
use crate::syntax::statement::VisibilityContext; use crate::syntax::statement::VisibilityContext;
use crate::syntax::token; use crate::syntax::token;
use crate::syntax::tree; 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 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(); items.pop();
let lines = block.into_iter().map(|item::Line { newline, mut items }| block::Line { let lines = compound_lines(&mut block, |prefixes, mut line| {
newline, if let Some(Item::Token(token)) = line.items.first_mut() {
expression: {
if let Some(Item::Token(token)) = items.first_mut() {
if matches!(token.variant, token::Variant::Operator(_)) { if matches!(token.variant, token::Variant::Operator(_)) {
let opr_ident = let opr_ident =
token::variant::Ident { is_operator_lexically: true, ..default() }; token::variant::Ident { is_operator_lexically: true, ..default() };
token.variant = token::Variant::Ident(opr_ident); 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() block::compound_lines(lines).collect()
} else { } else {
@ -77,31 +78,46 @@ pub fn try_parse_type_def<'s>(
} }
fn parse_type_body_statement<'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>, precedence: &mut Precedence<'s>,
args_buffer: &mut Vec<ArgumentDefinition<'s>>, args_buffer: &mut Vec<ArgumentDefinition<'s>>,
) -> Option<Tree<'s>> { ) -> Line<'s, StatementOrPrefix<'s>> {
let private_keywords = scan_private_keywords(&items); let private_keywords = scan_private_keywords(&line.items);
let statement = match items.get(private_keywords) { match line.items.get(private_keywords) {
Some(Item::Token(Token { variant: token::Variant::Ident(ident), .. })) Some(Item::Token(Token { variant: token::Variant::Ident(ident), .. }))
if ident.is_type if ident.is_type
&& !items && !line
.items
.get(private_keywords + 1) .get(private_keywords + 1)
.is_some_and(|item| Spacing::of_item(item) == Spacing::Unspaced) => .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, &mut items,
0, 0,
private_keywords, private_keywords,
precedence, precedence,
args_buffer, args_buffer,
)), );
None => None, Line {
_ => { newline,
let tree = parse_statement(&mut items, 0, precedence, args_buffer, StatementContext { content: apply_excess_private_keywords(Some(def), items.drain(..))
.map(StatementOrPrefix::from),
}
}
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, evaluation_context: EvaluationContext::Lazy,
visibility_context: VisibilityContext::Public, visibility_context: VisibilityContext::Public,
tail_expression: false,
}) })
.unwrap(); .map_content(|statement_or_prefix| {
statement_or_prefix.map_statement(|tree| {
let error = match &tree.variant { let error = match &tree.variant {
tree::Variant::Function(_) tree::Variant::Function(_)
| tree::Variant::ForeignFunction(_) | tree::Variant::ForeignFunction(_)
@ -109,14 +125,14 @@ fn parse_type_body_statement<'s>(
| tree::Variant::Documented(_) | tree::Variant::Documented(_)
| tree::Variant::Annotated(_) | tree::Variant::Annotated(_)
| tree::Variant::AnnotatedBuiltin(_) => None, | tree::Variant::AnnotatedBuiltin(_) => None,
tree::Variant::TypeSignature(_) => None, tree::Variant::TypeSignatureDeclaration(_) => None,
tree::Variant::TypeDef(_) => None, tree::Variant::TypeDef(_) => None,
_ => Some(SyntaxError::UnexpectedExpressionInTypeBody), _ => Some(SyntaxError::UnexpectedExpressionInTypeBody),
}; };
maybe_with_error(tree, error).into() maybe_with_error(tree, error)
})
}),
} }
};
apply_excess_private_keywords(statement, items.drain(..))
} }
fn apply_excess_private_keywords<'s>( 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`. /// A function definition, like `add x y = x + y`.
Function { 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 /// 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. /// definitions, must be `None` if the context is a function body.
pub private: Option<token::PrivateKeyword<'s>>, 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 body: Option<Tree<'s>>,
pub close: Option<token::CloseSymbol<'s>>, pub close: Option<token::CloseSymbol<'s>>,
}, },
/// Statement declaring the type of a variable. /// Declaration of the type of a function, that was not able to be attached to a subsequent
TypeSignature { /// function definition.
/// (Qualified) name of the item whose type is being declared. TypeSignatureDeclaration {
pub variable: Tree<'s>, pub signature: TypeSignature<'s>,
/// The `:` token.
pub operator: token::TypeAnnotationOperator<'s>,
/// The variable's type.
#[reflect(rename = "type")]
pub type_: Tree<'s>,
}, },
/// An expression with explicit type information attached. /// An expression with explicit type information attached.
TypeAnnotated { TypeAnnotated {
@ -532,6 +529,41 @@ impl<'s> span::Builder<'s> for FractionalDigits<'s> {
// === Functions === // === 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. /// A function argument definition.
#[cfg_attr(feature = "debug", derive(Visitor))] #[cfg_attr(feature = "debug", derive(Visitor))]
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Reflect, Deserialize)] #[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. /// Parse the top-level of a module.
pub fn parse_module<'s>( pub fn parse_module<'s>(
lines: impl IntoIterator<Item = item::Line<'s>>, lines: &mut Vec<item::Line<'s>>,
precedence: &mut operator::Precedence<'s>, precedence: &mut operator::Precedence<'s>,
) -> Tree<'s> { ) -> Tree<'s> {
BodyBlockParser::default().parse_module(lines, precedence) BodyBlockParser::default().parse_module(lines, precedence)
@ -58,7 +58,7 @@ pub fn parse_module<'s>(
/// Parse a body block. /// Parse a body block.
pub fn parse_block<'s>( pub fn parse_block<'s>(
lines: impl IntoIterator<Item = item::Line<'s>>, lines: &mut Vec<item::Line<'s>>,
precedence: &mut operator::Precedence<'s>, precedence: &mut operator::Precedence<'s>,
) -> Tree<'s> { ) -> Tree<'s> {
BodyBlockParser::default().parse_body_block(lines, precedence) 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 { items.push(Item::Tree(match block_context {
BlockContext::Body => 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 => { BlockContext::ArgumentOrOperator => {
for item::Line { newline, items } in lines.into_vec() { for item::Line { newline, items } in lines.into_vec() {
self.block_builder.push(newline, items, &mut child); self.block_builder.push(newline, items, &mut child);