mirror of
https://github.com/enso-org/enso.git
synced 2024-12-23 01:51:30 +03:00
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:
parent
d278ad636c
commit
4e4a1e1df2
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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))
|
||||
|
@ -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)]
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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());
|
||||
|
@ -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),
|
||||
),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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>(
|
||||
|
@ -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)]
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user