implements PR-10.2
This commit is contained in:
parent
e56fe2b9b7
commit
6b85dc33e4
@ -0,0 +1,165 @@
|
||||
package p.studio.compiler.pbs.parser;
|
||||
|
||||
import p.studio.compiler.pbs.ast.PbsAst;
|
||||
import p.studio.compiler.pbs.lexer.PbsToken;
|
||||
import p.studio.compiler.pbs.lexer.PbsTokenKind;
|
||||
import p.studio.utilities.structures.ReadOnlyList;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
final class PbsAttributeParser {
|
||||
private final PbsParserContext context;
|
||||
private final PbsTokenCursor cursor;
|
||||
private final Predicate<PbsTokenKind> topLevelRestartPredicate;
|
||||
|
||||
PbsAttributeParser(
|
||||
final PbsParserContext context,
|
||||
final Predicate<PbsTokenKind> topLevelRestartPredicate) {
|
||||
this.context = context;
|
||||
this.cursor = context.cursor();
|
||||
this.topLevelRestartPredicate = topLevelRestartPredicate;
|
||||
}
|
||||
|
||||
ReadOnlyList<PbsAst.Attribute> parseAttributeList() {
|
||||
final var attributes = new ArrayList<PbsAst.Attribute>();
|
||||
while (cursor.check(PbsTokenKind.LEFT_BRACKET)) {
|
||||
attributes.add(parseAttribute());
|
||||
}
|
||||
return ReadOnlyList.wrap(attributes);
|
||||
}
|
||||
|
||||
private PbsAst.Attribute parseAttribute() {
|
||||
final var leftBracket = consume(PbsTokenKind.LEFT_BRACKET, "Expected '[' to start attribute");
|
||||
final var name = consume(PbsTokenKind.IDENTIFIER, "Expected attribute identifier");
|
||||
final var arguments = new ArrayList<PbsAst.AttributeArgument>();
|
||||
if (cursor.match(PbsTokenKind.LEFT_PAREN)) {
|
||||
parseAttributeArguments(arguments);
|
||||
}
|
||||
if ("Slot".equals(name.lexeme())) {
|
||||
report(name, ParseErrors.E_PARSE_INVALID_DECL_SHAPE,
|
||||
"Attribute 'Slot' is not part of PBS syntax; builtin slots are inferred by the compiler");
|
||||
}
|
||||
|
||||
long end = Math.max(leftBracket.end(), name.end());
|
||||
if (cursor.match(PbsTokenKind.RIGHT_BRACKET)) {
|
||||
end = cursor.previous().end();
|
||||
} else {
|
||||
report(cursor.peek(), ParseErrors.E_PARSE_EXPECTED_TOKEN, "Expected ']' to close attribute");
|
||||
end = recoverUntilAttributeCloseOrTopLevel(end);
|
||||
}
|
||||
return new PbsAst.Attribute(
|
||||
name.lexeme(),
|
||||
ReadOnlyList.wrap(arguments),
|
||||
context.span(leftBracket.start(), end));
|
||||
}
|
||||
|
||||
private void parseAttributeArguments(final ArrayList<PbsAst.AttributeArgument> arguments) {
|
||||
if (!cursor.check(PbsTokenKind.RIGHT_PAREN)) {
|
||||
do {
|
||||
arguments.add(parseAttributeArgument());
|
||||
} while (cursor.match(PbsTokenKind.COMMA) && !cursor.check(PbsTokenKind.RIGHT_PAREN));
|
||||
}
|
||||
|
||||
if (cursor.match(PbsTokenKind.RIGHT_PAREN)) {
|
||||
return;
|
||||
}
|
||||
report(cursor.peek(), ParseErrors.E_PARSE_EXPECTED_TOKEN, "Expected ')' after attribute arguments");
|
||||
recoverUntilAttributeArgumentClose();
|
||||
}
|
||||
|
||||
private PbsAst.AttributeArgument parseAttributeArgument() {
|
||||
final var argumentStart = cursor.peek();
|
||||
final var name = consume(PbsTokenKind.IDENTIFIER, "Expected attribute argument name");
|
||||
consume(PbsTokenKind.EQUAL, "Expected '=' in attribute argument");
|
||||
final var value = parseAttributeValue();
|
||||
return new PbsAst.AttributeArgument(
|
||||
name.lexeme(),
|
||||
value,
|
||||
context.span(argumentStart.start(), value.span().getEnd()));
|
||||
}
|
||||
|
||||
private PbsAst.AttributeValue parseAttributeValue() {
|
||||
if (cursor.match(PbsTokenKind.STRING_LITERAL)) {
|
||||
final var token = cursor.previous();
|
||||
return new PbsAst.AttributeStringValue(token.lexeme(), context.span(token.start(), token.end()));
|
||||
}
|
||||
if (cursor.match(PbsTokenKind.INT_LITERAL)) {
|
||||
final var token = cursor.previous();
|
||||
final var parsedLong = parseLongOrNull(token.lexeme());
|
||||
if (parsedLong != null) {
|
||||
return new PbsAst.AttributeIntValue(parsedLong, context.span(token.start(), token.end()));
|
||||
}
|
||||
report(token, ParseErrors.E_PARSE_EXPECTED_TOKEN, "Invalid integer literal in attribute argument");
|
||||
return new PbsAst.AttributeErrorValue(token.lexeme(), context.span(token.start(), token.end()));
|
||||
}
|
||||
if (cursor.match(PbsTokenKind.TRUE)) {
|
||||
final var token = cursor.previous();
|
||||
return new PbsAst.AttributeBoolValue(true, context.span(token.start(), token.end()));
|
||||
}
|
||||
if (cursor.match(PbsTokenKind.FALSE)) {
|
||||
final var token = cursor.previous();
|
||||
return new PbsAst.AttributeBoolValue(false, context.span(token.start(), token.end()));
|
||||
}
|
||||
|
||||
final var token = cursor.peek();
|
||||
report(token, ParseErrors.E_PARSE_EXPECTED_TOKEN, "Expected attribute value (string, int, or bool)");
|
||||
if (!cursor.isAtEnd()
|
||||
&& !cursor.check(PbsTokenKind.COMMA)
|
||||
&& !cursor.check(PbsTokenKind.RIGHT_PAREN)
|
||||
&& !cursor.check(PbsTokenKind.RIGHT_BRACKET)) {
|
||||
cursor.advance();
|
||||
}
|
||||
return new PbsAst.AttributeErrorValue("<error>", context.span(token.start(), token.end()));
|
||||
}
|
||||
|
||||
private long recoverUntilAttributeCloseOrTopLevel(final long fallbackEnd) {
|
||||
long end = fallbackEnd;
|
||||
while (!cursor.isAtEnd()) {
|
||||
if (cursor.match(PbsTokenKind.RIGHT_BRACKET)) {
|
||||
return cursor.previous().end();
|
||||
}
|
||||
if (topLevelRestartPredicate.test(cursor.peek().kind())) {
|
||||
return end;
|
||||
}
|
||||
end = cursor.advance().end();
|
||||
}
|
||||
return end;
|
||||
}
|
||||
|
||||
private void recoverUntilAttributeArgumentClose() {
|
||||
while (!cursor.isAtEnd()) {
|
||||
if (cursor.match(PbsTokenKind.RIGHT_PAREN)) {
|
||||
return;
|
||||
}
|
||||
if (cursor.check(PbsTokenKind.RIGHT_BRACKET) || topLevelRestartPredicate.test(cursor.peek().kind())) {
|
||||
return;
|
||||
}
|
||||
cursor.advance();
|
||||
}
|
||||
}
|
||||
|
||||
private PbsToken consume(final PbsTokenKind kind, final String message) {
|
||||
if (cursor.check(kind)) {
|
||||
return cursor.advance();
|
||||
}
|
||||
final var token = cursor.peek();
|
||||
report(token, ParseErrors.E_PARSE_EXPECTED_TOKEN, message + ", found " + token.kind());
|
||||
if (!cursor.isAtEnd()) {
|
||||
return cursor.advance();
|
||||
}
|
||||
return token;
|
||||
}
|
||||
|
||||
private void report(final PbsToken token, final ParseErrors parseErrors, final String message) {
|
||||
context.report(token, parseErrors, message);
|
||||
}
|
||||
|
||||
private Long parseLongOrNull(final String text) {
|
||||
try {
|
||||
return Long.parseLong(text);
|
||||
} catch (NumberFormatException ignored) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -20,7 +20,6 @@ import java.util.List;
|
||||
* navigation is delegated to {@link PbsTokenCursor}.
|
||||
*/
|
||||
public final class PbsParser {
|
||||
private static final int MAX_NAMED_TUPLE_ARITY = 6;
|
||||
private static final boolean REQUIRE_SEMICOLON = true;
|
||||
private static final boolean ALLOW_TAIL_EXPRESSION = true;
|
||||
|
||||
@ -32,6 +31,8 @@ public final class PbsParser {
|
||||
private final PbsParserContext context;
|
||||
private final PbsTokenCursor cursor;
|
||||
private final PbsExprParser exprParser;
|
||||
private final PbsAttributeParser attributeParser;
|
||||
private final PbsTypeParser typeParser;
|
||||
|
||||
private PbsParser(
|
||||
final ReadOnlyList<PbsToken> tokens,
|
||||
@ -41,6 +42,8 @@ public final class PbsParser {
|
||||
this.context = new PbsParserContext(new PbsTokenCursor(tokens), fileId, diagnostics, parseMode);
|
||||
this.cursor = context.cursor();
|
||||
this.exprParser = new PbsExprParser(context, this::parseExpressionSurfaceBlock);
|
||||
this.attributeParser = new PbsAttributeParser(context, this::isTopLevelRestartToken);
|
||||
this.typeParser = new PbsTypeParser(context);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -454,121 +457,7 @@ public final class PbsParser {
|
||||
}
|
||||
|
||||
private ReadOnlyList<PbsAst.Attribute> parseAttributeList() {
|
||||
final var attributes = new ArrayList<PbsAst.Attribute>();
|
||||
while (cursor.check(PbsTokenKind.LEFT_BRACKET)) {
|
||||
attributes.add(parseAttribute());
|
||||
}
|
||||
return ReadOnlyList.wrap(attributes);
|
||||
}
|
||||
|
||||
private PbsAst.Attribute parseAttribute() {
|
||||
final var leftBracket = consume(PbsTokenKind.LEFT_BRACKET, "Expected '[' to start attribute");
|
||||
final var name = consume(PbsTokenKind.IDENTIFIER, "Expected attribute identifier");
|
||||
final var arguments = new ArrayList<PbsAst.AttributeArgument>();
|
||||
if (cursor.match(PbsTokenKind.LEFT_PAREN)) {
|
||||
parseAttributeArguments(arguments);
|
||||
}
|
||||
if ("Slot".equals(name.lexeme())) {
|
||||
report(name, ParseErrors.E_PARSE_INVALID_DECL_SHAPE,
|
||||
"Attribute 'Slot' is not part of PBS syntax; builtin slots are inferred by the compiler");
|
||||
}
|
||||
|
||||
long end = Math.max(leftBracket.end(), name.end());
|
||||
if (cursor.match(PbsTokenKind.RIGHT_BRACKET)) {
|
||||
end = cursor.previous().end();
|
||||
} else {
|
||||
report(cursor.peek(), ParseErrors.E_PARSE_EXPECTED_TOKEN, "Expected ']' to close attribute");
|
||||
end = recoverUntilAttributeCloseOrTopLevel(end);
|
||||
}
|
||||
return new PbsAst.Attribute(
|
||||
name.lexeme(),
|
||||
ReadOnlyList.wrap(arguments),
|
||||
span(leftBracket.start(), end));
|
||||
}
|
||||
|
||||
private void parseAttributeArguments(final ArrayList<PbsAst.AttributeArgument> arguments) {
|
||||
if (!cursor.check(PbsTokenKind.RIGHT_PAREN)) {
|
||||
do {
|
||||
arguments.add(parseAttributeArgument());
|
||||
} while (cursor.match(PbsTokenKind.COMMA) && !cursor.check(PbsTokenKind.RIGHT_PAREN));
|
||||
}
|
||||
|
||||
if (cursor.match(PbsTokenKind.RIGHT_PAREN)) {
|
||||
return;
|
||||
}
|
||||
report(cursor.peek(), ParseErrors.E_PARSE_EXPECTED_TOKEN, "Expected ')' after attribute arguments");
|
||||
recoverUntilAttributeArgumentClose();
|
||||
}
|
||||
|
||||
private PbsAst.AttributeArgument parseAttributeArgument() {
|
||||
final var argumentStart = cursor.peek();
|
||||
final var name = consume(PbsTokenKind.IDENTIFIER, "Expected attribute argument name");
|
||||
consume(PbsTokenKind.EQUAL, "Expected '=' in attribute argument");
|
||||
final var value = parseAttributeValue();
|
||||
return new PbsAst.AttributeArgument(
|
||||
name.lexeme(),
|
||||
value,
|
||||
span(argumentStart.start(), value.span().getEnd()));
|
||||
}
|
||||
|
||||
private PbsAst.AttributeValue parseAttributeValue() {
|
||||
if (cursor.match(PbsTokenKind.STRING_LITERAL)) {
|
||||
final var token = cursor.previous();
|
||||
return new PbsAst.AttributeStringValue(token.lexeme(), span(token.start(), token.end()));
|
||||
}
|
||||
if (cursor.match(PbsTokenKind.INT_LITERAL)) {
|
||||
final var token = cursor.previous();
|
||||
final var parsedLong = parseLongOrNull(token.lexeme());
|
||||
if (parsedLong != null) {
|
||||
return new PbsAst.AttributeIntValue(parsedLong, span(token.start(), token.end()));
|
||||
}
|
||||
report(token, ParseErrors.E_PARSE_EXPECTED_TOKEN, "Invalid integer literal in attribute argument");
|
||||
return new PbsAst.AttributeErrorValue(token.lexeme(), span(token.start(), token.end()));
|
||||
}
|
||||
if (cursor.match(PbsTokenKind.TRUE)) {
|
||||
final var token = cursor.previous();
|
||||
return new PbsAst.AttributeBoolValue(true, span(token.start(), token.end()));
|
||||
}
|
||||
if (cursor.match(PbsTokenKind.FALSE)) {
|
||||
final var token = cursor.previous();
|
||||
return new PbsAst.AttributeBoolValue(false, span(token.start(), token.end()));
|
||||
}
|
||||
|
||||
final var token = cursor.peek();
|
||||
report(token, ParseErrors.E_PARSE_EXPECTED_TOKEN, "Expected attribute value (string, int, or bool)");
|
||||
if (!cursor.isAtEnd()
|
||||
&& !cursor.check(PbsTokenKind.COMMA)
|
||||
&& !cursor.check(PbsTokenKind.RIGHT_PAREN)
|
||||
&& !cursor.check(PbsTokenKind.RIGHT_BRACKET)) {
|
||||
cursor.advance();
|
||||
}
|
||||
return new PbsAst.AttributeErrorValue("<error>", span(token.start(), token.end()));
|
||||
}
|
||||
|
||||
private long recoverUntilAttributeCloseOrTopLevel(final long fallbackEnd) {
|
||||
long end = fallbackEnd;
|
||||
while (!cursor.isAtEnd()) {
|
||||
if (cursor.match(PbsTokenKind.RIGHT_BRACKET)) {
|
||||
return cursor.previous().end();
|
||||
}
|
||||
if (isTopLevelRestartToken(cursor.peek().kind())) {
|
||||
return end;
|
||||
}
|
||||
end = cursor.advance().end();
|
||||
}
|
||||
return end;
|
||||
}
|
||||
|
||||
private void recoverUntilAttributeArgumentClose() {
|
||||
while (!cursor.isAtEnd()) {
|
||||
if (cursor.match(PbsTokenKind.RIGHT_PAREN)) {
|
||||
return;
|
||||
}
|
||||
if (cursor.check(PbsTokenKind.RIGHT_BRACKET) || isTopLevelRestartToken(cursor.peek().kind())) {
|
||||
return;
|
||||
}
|
||||
cursor.advance();
|
||||
}
|
||||
return attributeParser.parseAttributeList();
|
||||
}
|
||||
|
||||
private StructBodyParse parseStructBodyAndConsumeRightBrace(final PbsToken leftBrace) {
|
||||
@ -737,9 +626,9 @@ public final class PbsParser {
|
||||
return new PbsAst.FunctionDecl(
|
||||
name.lexeme(),
|
||||
ReadOnlyList.wrap(parameters),
|
||||
returnSpec.kind,
|
||||
returnSpec.returnType,
|
||||
returnSpec.resultErrorType,
|
||||
returnSpec.kind(),
|
||||
returnSpec.returnType(),
|
||||
returnSpec.resultErrorType(),
|
||||
body,
|
||||
span(fnToken.start(), body.span().getEnd()));
|
||||
}
|
||||
@ -754,7 +643,7 @@ public final class PbsParser {
|
||||
|
||||
final var returnSpec = parseCallableReturnSpec(rightParen);
|
||||
|
||||
long end = returnSpec.returnType.span().getEnd();
|
||||
long end = returnSpec.returnType().span().getEnd();
|
||||
if (REQUIRE_SEMICOLON) {
|
||||
end = consume(PbsTokenKind.SEMICOLON, "Expected ';' after function signature").end();
|
||||
}
|
||||
@ -762,9 +651,9 @@ public final class PbsParser {
|
||||
return new PbsAst.FunctionSignature(
|
||||
name.lexeme(),
|
||||
ReadOnlyList.wrap(parameters),
|
||||
returnSpec.kind,
|
||||
returnSpec.returnType,
|
||||
returnSpec.resultErrorType,
|
||||
returnSpec.kind(),
|
||||
returnSpec.returnType(),
|
||||
returnSpec.resultErrorType(),
|
||||
attributes,
|
||||
span(fnToken.start(), end));
|
||||
}
|
||||
@ -796,9 +685,9 @@ public final class PbsParser {
|
||||
return new PbsAst.CallbackDecl(
|
||||
name.lexeme(),
|
||||
ReadOnlyList.wrap(parameters),
|
||||
returnSpec.kind,
|
||||
returnSpec.returnType,
|
||||
returnSpec.resultErrorType,
|
||||
returnSpec.kind(),
|
||||
returnSpec.returnType(),
|
||||
returnSpec.resultErrorType(),
|
||||
span(declareToken.start(), semicolon.end()));
|
||||
}
|
||||
|
||||
@ -865,148 +754,15 @@ public final class PbsParser {
|
||||
}
|
||||
|
||||
private ArrayList<PbsAst.Parameter> parseParametersUntilRightParen() {
|
||||
final var parameters = new ArrayList<PbsAst.Parameter>();
|
||||
if (cursor.check(PbsTokenKind.RIGHT_PAREN)) {
|
||||
return parameters;
|
||||
}
|
||||
|
||||
do {
|
||||
final var parameterStart = cursor.peek();
|
||||
final var parameterName = consume(PbsTokenKind.IDENTIFIER, "Expected parameter name");
|
||||
consume(PbsTokenKind.COLON, "Expected ':' after parameter name");
|
||||
final var typeRef = parseTypeRef();
|
||||
parameters.add(new PbsAst.Parameter(
|
||||
parameterName.lexeme(),
|
||||
typeRef,
|
||||
span(parameterStart.start(), typeRef.span().getEnd())));
|
||||
} while (cursor.match(PbsTokenKind.COMMA));
|
||||
|
||||
return parameters;
|
||||
return typeParser.parseParametersUntilRightParen();
|
||||
}
|
||||
|
||||
private ParsedReturnSpec parseCallableReturnSpec(final PbsToken anchorToken) {
|
||||
if (cursor.match(PbsTokenKind.ARROW)) {
|
||||
return parseReturnSpecFromMarker();
|
||||
}
|
||||
|
||||
if (cursor.match(PbsTokenKind.COLON)) {
|
||||
report(cursor.previous(), ParseErrors.E_PARSE_INVALID_RETURN_ANNOTATION,
|
||||
"Return annotations must use '->' syntax");
|
||||
return parseReturnSpecFromMarker();
|
||||
}
|
||||
|
||||
final var inferredSpan = span(anchorToken.end(), anchorToken.end());
|
||||
return new ParsedReturnSpec(
|
||||
PbsAst.ReturnKind.INFERRED_UNIT,
|
||||
PbsAst.TypeRef.unit(inferredSpan),
|
||||
null);
|
||||
private PbsTypeParser.ParsedReturnSpec parseCallableReturnSpec(final PbsToken anchorToken) {
|
||||
return typeParser.parseCallableReturnSpec(anchorToken);
|
||||
}
|
||||
|
||||
private ParsedReturnSpec parseReturnSpecFromMarker() {
|
||||
if (cursor.match(PbsTokenKind.RESULT)) {
|
||||
final var resultToken = cursor.previous();
|
||||
consume(PbsTokenKind.LESS, "Expected '<' after 'result'");
|
||||
final var errorType = parseTypeRef();
|
||||
consume(PbsTokenKind.GREATER, "Expected '>' after result error type");
|
||||
|
||||
final PbsAst.TypeRef payloadType;
|
||||
if (isTypeStart(cursor.peek().kind())) {
|
||||
payloadType = parseTypeRef();
|
||||
} else {
|
||||
payloadType = PbsAst.TypeRef.unit(span(resultToken.end(), resultToken.end()));
|
||||
}
|
||||
|
||||
return new ParsedReturnSpec(
|
||||
PbsAst.ReturnKind.RESULT,
|
||||
payloadType,
|
||||
errorType);
|
||||
}
|
||||
|
||||
final var typeRef = parseTypeRef();
|
||||
if (typeRef.kind() == PbsAst.TypeRefKind.UNIT) {
|
||||
return new ParsedReturnSpec(PbsAst.ReturnKind.EXPLICIT_UNIT, typeRef, null);
|
||||
}
|
||||
return new ParsedReturnSpec(PbsAst.ReturnKind.PLAIN, typeRef, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses type references including optional, unit, grouped, and named-tuple forms.
|
||||
*/
|
||||
private PbsAst.TypeRef parseTypeRef() {
|
||||
if (cursor.match(PbsTokenKind.OPTIONAL)) {
|
||||
final var optionalToken = cursor.previous();
|
||||
final var inner = parseTypeRef();
|
||||
if (inner.kind() == PbsAst.TypeRefKind.UNIT) {
|
||||
report(optionalToken, ParseErrors.E_PARSE_INVALID_TYPE_SURFACE,
|
||||
"'optional void' is not a valid type surface");
|
||||
}
|
||||
return PbsAst.TypeRef.optional(inner, span(optionalToken.start(), inner.span().getEnd()));
|
||||
}
|
||||
|
||||
if (cursor.match(PbsTokenKind.VOID)) {
|
||||
final var token = cursor.previous();
|
||||
return PbsAst.TypeRef.unit(span(token.start(), token.end()));
|
||||
}
|
||||
|
||||
if (cursor.match(PbsTokenKind.SELF)) {
|
||||
final var token = cursor.previous();
|
||||
return PbsAst.TypeRef.self(span(token.start(), token.end()));
|
||||
}
|
||||
|
||||
if (cursor.match(PbsTokenKind.IDENTIFIER)) {
|
||||
final var token = cursor.previous();
|
||||
return PbsAst.TypeRef.simple(token.lexeme(), span(token.start(), token.end()));
|
||||
}
|
||||
|
||||
if (cursor.match(PbsTokenKind.LEFT_PAREN)) {
|
||||
final var open = cursor.previous();
|
||||
if (cursor.match(PbsTokenKind.RIGHT_PAREN)) {
|
||||
return PbsAst.TypeRef.unit(span(open.start(), cursor.previous().end()));
|
||||
}
|
||||
|
||||
if (cursor.check(PbsTokenKind.IDENTIFIER) && cursor.checkNext(PbsTokenKind.COLON)) {
|
||||
final var fields = parseNamedTupleTypeFields();
|
||||
final var close = consume(PbsTokenKind.RIGHT_PAREN, "Expected ')' after named tuple type");
|
||||
return PbsAst.TypeRef.namedTuple(ReadOnlyList.wrap(fields), span(open.start(), close.end()));
|
||||
}
|
||||
|
||||
final var inner = parseTypeRef();
|
||||
final var close = consume(PbsTokenKind.RIGHT_PAREN, "Expected ')' after grouped type");
|
||||
return PbsAst.TypeRef.group(inner, span(open.start(), close.end()));
|
||||
}
|
||||
|
||||
final var token = cursor.peek();
|
||||
report(token, ParseErrors.E_PARSE_EXPECTED_TOKEN, "Expected type surface");
|
||||
if (!cursor.isAtEnd()) {
|
||||
cursor.advance();
|
||||
}
|
||||
return PbsAst.TypeRef.error(span(token.start(), token.end()));
|
||||
}
|
||||
|
||||
private ArrayList<PbsAst.NamedTypeField> parseNamedTupleTypeFields() {
|
||||
final var fields = new ArrayList<PbsAst.NamedTypeField>();
|
||||
do {
|
||||
final var label = consume(PbsTokenKind.IDENTIFIER, "Expected tuple field label");
|
||||
consume(PbsTokenKind.COLON, "Expected ':' after tuple field label");
|
||||
final var typeRef = parseTypeRef();
|
||||
fields.add(new PbsAst.NamedTypeField(
|
||||
label.lexeme(),
|
||||
typeRef,
|
||||
span(label.start(), typeRef.span().getEnd())));
|
||||
if (fields.size() == MAX_NAMED_TUPLE_ARITY + 1) {
|
||||
report(label, ParseErrors.E_PARSE_INVALID_TYPE_SURFACE,
|
||||
"Named tuple type arity must be between 1 and 6 fields");
|
||||
}
|
||||
} while (cursor.match(PbsTokenKind.COMMA) && !cursor.check(PbsTokenKind.RIGHT_PAREN));
|
||||
return fields;
|
||||
}
|
||||
|
||||
private boolean isTypeStart(final PbsTokenKind kind) {
|
||||
return kind == PbsTokenKind.IDENTIFIER
|
||||
|| kind == PbsTokenKind.SELF
|
||||
|| kind == PbsTokenKind.OPTIONAL
|
||||
|| kind == PbsTokenKind.VOID
|
||||
|| kind == PbsTokenKind.LEFT_PAREN;
|
||||
return typeParser.parseTypeRef();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1416,12 +1172,6 @@ public final class PbsParser {
|
||||
context.report(token, parseErrors, message);
|
||||
}
|
||||
|
||||
private record ParsedReturnSpec(
|
||||
PbsAst.ReturnKind kind,
|
||||
PbsAst.TypeRef returnType,
|
||||
PbsAst.TypeRef resultErrorType) {
|
||||
}
|
||||
|
||||
private record StructBodyParse(
|
||||
ReadOnlyList<PbsAst.FunctionDecl> methods,
|
||||
ReadOnlyList<PbsAst.CtorDecl> ctors,
|
||||
|
||||
@ -0,0 +1,181 @@
|
||||
package p.studio.compiler.pbs.parser;
|
||||
|
||||
import p.studio.compiler.pbs.ast.PbsAst;
|
||||
import p.studio.compiler.pbs.lexer.PbsToken;
|
||||
import p.studio.compiler.pbs.lexer.PbsTokenKind;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
final class PbsTypeParser {
|
||||
private static final int MAX_NAMED_TUPLE_ARITY = 6;
|
||||
|
||||
private final PbsParserContext context;
|
||||
private final PbsTokenCursor cursor;
|
||||
|
||||
PbsTypeParser(final PbsParserContext context) {
|
||||
this.context = context;
|
||||
this.cursor = context.cursor();
|
||||
}
|
||||
|
||||
ArrayList<PbsAst.Parameter> parseParametersUntilRightParen() {
|
||||
final var parameters = new ArrayList<PbsAst.Parameter>();
|
||||
if (cursor.check(PbsTokenKind.RIGHT_PAREN)) {
|
||||
return parameters;
|
||||
}
|
||||
|
||||
do {
|
||||
final var parameterStart = cursor.peek();
|
||||
final var parameterName = consume(PbsTokenKind.IDENTIFIER, "Expected parameter name");
|
||||
consume(PbsTokenKind.COLON, "Expected ':' after parameter name");
|
||||
final var typeRef = parseTypeRef();
|
||||
parameters.add(new PbsAst.Parameter(
|
||||
parameterName.lexeme(),
|
||||
typeRef,
|
||||
context.span(parameterStart.start(), typeRef.span().getEnd())));
|
||||
} while (cursor.match(PbsTokenKind.COMMA));
|
||||
|
||||
return parameters;
|
||||
}
|
||||
|
||||
ParsedReturnSpec parseCallableReturnSpec(final PbsToken anchorToken) {
|
||||
if (cursor.match(PbsTokenKind.ARROW)) {
|
||||
return parseReturnSpecFromMarker();
|
||||
}
|
||||
|
||||
if (cursor.match(PbsTokenKind.COLON)) {
|
||||
context.report(cursor.previous(), ParseErrors.E_PARSE_INVALID_RETURN_ANNOTATION,
|
||||
"Return annotations must use '->' syntax");
|
||||
return parseReturnSpecFromMarker();
|
||||
}
|
||||
|
||||
final var inferredSpan = context.span(anchorToken.end(), anchorToken.end());
|
||||
return new ParsedReturnSpec(
|
||||
PbsAst.ReturnKind.INFERRED_UNIT,
|
||||
PbsAst.TypeRef.unit(inferredSpan),
|
||||
null);
|
||||
}
|
||||
|
||||
PbsAst.TypeRef parseTypeRef() {
|
||||
if (cursor.match(PbsTokenKind.OPTIONAL)) {
|
||||
final var optionalToken = cursor.previous();
|
||||
final var inner = parseTypeRef();
|
||||
if (inner.kind() == PbsAst.TypeRefKind.UNIT) {
|
||||
context.report(optionalToken, ParseErrors.E_PARSE_INVALID_TYPE_SURFACE,
|
||||
"'optional void' is not a valid type surface");
|
||||
}
|
||||
return PbsAst.TypeRef.optional(inner, context.span(optionalToken.start(), inner.span().getEnd()));
|
||||
}
|
||||
|
||||
if (cursor.match(PbsTokenKind.VOID)) {
|
||||
final var token = cursor.previous();
|
||||
return PbsAst.TypeRef.unit(context.span(token.start(), token.end()));
|
||||
}
|
||||
|
||||
if (cursor.match(PbsTokenKind.SELF)) {
|
||||
final var token = cursor.previous();
|
||||
return PbsAst.TypeRef.self(context.span(token.start(), token.end()));
|
||||
}
|
||||
|
||||
if (cursor.match(PbsTokenKind.IDENTIFIER)) {
|
||||
final var token = cursor.previous();
|
||||
return PbsAst.TypeRef.simple(token.lexeme(), context.span(token.start(), token.end()));
|
||||
}
|
||||
|
||||
if (cursor.match(PbsTokenKind.LEFT_PAREN)) {
|
||||
final var open = cursor.previous();
|
||||
if (cursor.match(PbsTokenKind.RIGHT_PAREN)) {
|
||||
return PbsAst.TypeRef.unit(context.span(open.start(), cursor.previous().end()));
|
||||
}
|
||||
|
||||
if (cursor.check(PbsTokenKind.IDENTIFIER) && cursor.checkNext(PbsTokenKind.COLON)) {
|
||||
final var fields = parseNamedTupleTypeFields();
|
||||
final var close = consume(PbsTokenKind.RIGHT_PAREN, "Expected ')' after named tuple type");
|
||||
return PbsAst.TypeRef.namedTuple(
|
||||
p.studio.utilities.structures.ReadOnlyList.wrap(fields),
|
||||
context.span(open.start(), close.end()));
|
||||
}
|
||||
|
||||
final var inner = parseTypeRef();
|
||||
final var close = consume(PbsTokenKind.RIGHT_PAREN, "Expected ')' after grouped type");
|
||||
return PbsAst.TypeRef.group(inner, context.span(open.start(), close.end()));
|
||||
}
|
||||
|
||||
final var token = cursor.peek();
|
||||
context.report(token, ParseErrors.E_PARSE_EXPECTED_TOKEN, "Expected type surface");
|
||||
if (!cursor.isAtEnd()) {
|
||||
cursor.advance();
|
||||
}
|
||||
return PbsAst.TypeRef.error(context.span(token.start(), token.end()));
|
||||
}
|
||||
|
||||
private ParsedReturnSpec parseReturnSpecFromMarker() {
|
||||
if (cursor.match(PbsTokenKind.RESULT)) {
|
||||
final var resultToken = cursor.previous();
|
||||
consume(PbsTokenKind.LESS, "Expected '<' after 'result'");
|
||||
final var errorType = parseTypeRef();
|
||||
consume(PbsTokenKind.GREATER, "Expected '>' after result error type");
|
||||
|
||||
final PbsAst.TypeRef payloadType;
|
||||
if (isTypeStart(cursor.peek().kind())) {
|
||||
payloadType = parseTypeRef();
|
||||
} else {
|
||||
payloadType = PbsAst.TypeRef.unit(context.span(resultToken.end(), resultToken.end()));
|
||||
}
|
||||
|
||||
return new ParsedReturnSpec(
|
||||
PbsAst.ReturnKind.RESULT,
|
||||
payloadType,
|
||||
errorType);
|
||||
}
|
||||
|
||||
final var typeRef = parseTypeRef();
|
||||
if (typeRef.kind() == PbsAst.TypeRefKind.UNIT) {
|
||||
return new ParsedReturnSpec(PbsAst.ReturnKind.EXPLICIT_UNIT, typeRef, null);
|
||||
}
|
||||
return new ParsedReturnSpec(PbsAst.ReturnKind.PLAIN, typeRef, null);
|
||||
}
|
||||
|
||||
private ArrayList<PbsAst.NamedTypeField> parseNamedTupleTypeFields() {
|
||||
final var fields = new ArrayList<PbsAst.NamedTypeField>();
|
||||
do {
|
||||
final var label = consume(PbsTokenKind.IDENTIFIER, "Expected tuple field label");
|
||||
consume(PbsTokenKind.COLON, "Expected ':' after tuple field label");
|
||||
final var typeRef = parseTypeRef();
|
||||
fields.add(new PbsAst.NamedTypeField(
|
||||
label.lexeme(),
|
||||
typeRef,
|
||||
context.span(label.start(), typeRef.span().getEnd())));
|
||||
if (fields.size() == MAX_NAMED_TUPLE_ARITY + 1) {
|
||||
context.report(label, ParseErrors.E_PARSE_INVALID_TYPE_SURFACE,
|
||||
"Named tuple type arity must be between 1 and 6 fields");
|
||||
}
|
||||
} while (cursor.match(PbsTokenKind.COMMA) && !cursor.check(PbsTokenKind.RIGHT_PAREN));
|
||||
return fields;
|
||||
}
|
||||
|
||||
private boolean isTypeStart(final PbsTokenKind kind) {
|
||||
return kind == PbsTokenKind.IDENTIFIER
|
||||
|| kind == PbsTokenKind.SELF
|
||||
|| kind == PbsTokenKind.OPTIONAL
|
||||
|| kind == PbsTokenKind.VOID
|
||||
|| kind == PbsTokenKind.LEFT_PAREN;
|
||||
}
|
||||
|
||||
private PbsToken consume(final PbsTokenKind kind, final String message) {
|
||||
if (cursor.check(kind)) {
|
||||
return cursor.advance();
|
||||
}
|
||||
final var token = cursor.peek();
|
||||
context.report(token, ParseErrors.E_PARSE_EXPECTED_TOKEN, message + ", found " + token.kind());
|
||||
if (!cursor.isAtEnd()) {
|
||||
return cursor.advance();
|
||||
}
|
||||
return token;
|
||||
}
|
||||
|
||||
record ParsedReturnSpec(
|
||||
PbsAst.ReturnKind kind,
|
||||
PbsAst.TypeRef returnType,
|
||||
PbsAst.TypeRef resultErrorType) {
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user