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}.
|
* navigation is delegated to {@link PbsTokenCursor}.
|
||||||
*/
|
*/
|
||||||
public final class PbsParser {
|
public final class PbsParser {
|
||||||
private static final int MAX_NAMED_TUPLE_ARITY = 6;
|
|
||||||
private static final boolean REQUIRE_SEMICOLON = true;
|
private static final boolean REQUIRE_SEMICOLON = true;
|
||||||
private static final boolean ALLOW_TAIL_EXPRESSION = true;
|
private static final boolean ALLOW_TAIL_EXPRESSION = true;
|
||||||
|
|
||||||
@ -32,6 +31,8 @@ public final class PbsParser {
|
|||||||
private final PbsParserContext context;
|
private final PbsParserContext context;
|
||||||
private final PbsTokenCursor cursor;
|
private final PbsTokenCursor cursor;
|
||||||
private final PbsExprParser exprParser;
|
private final PbsExprParser exprParser;
|
||||||
|
private final PbsAttributeParser attributeParser;
|
||||||
|
private final PbsTypeParser typeParser;
|
||||||
|
|
||||||
private PbsParser(
|
private PbsParser(
|
||||||
final ReadOnlyList<PbsToken> tokens,
|
final ReadOnlyList<PbsToken> tokens,
|
||||||
@ -41,6 +42,8 @@ public final class PbsParser {
|
|||||||
this.context = new PbsParserContext(new PbsTokenCursor(tokens), fileId, diagnostics, parseMode);
|
this.context = new PbsParserContext(new PbsTokenCursor(tokens), fileId, diagnostics, parseMode);
|
||||||
this.cursor = context.cursor();
|
this.cursor = context.cursor();
|
||||||
this.exprParser = new PbsExprParser(context, this::parseExpressionSurfaceBlock);
|
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() {
|
private ReadOnlyList<PbsAst.Attribute> parseAttributeList() {
|
||||||
final var attributes = new ArrayList<PbsAst.Attribute>();
|
return attributeParser.parseAttributeList();
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private StructBodyParse parseStructBodyAndConsumeRightBrace(final PbsToken leftBrace) {
|
private StructBodyParse parseStructBodyAndConsumeRightBrace(final PbsToken leftBrace) {
|
||||||
@ -737,9 +626,9 @@ public final class PbsParser {
|
|||||||
return new PbsAst.FunctionDecl(
|
return new PbsAst.FunctionDecl(
|
||||||
name.lexeme(),
|
name.lexeme(),
|
||||||
ReadOnlyList.wrap(parameters),
|
ReadOnlyList.wrap(parameters),
|
||||||
returnSpec.kind,
|
returnSpec.kind(),
|
||||||
returnSpec.returnType,
|
returnSpec.returnType(),
|
||||||
returnSpec.resultErrorType,
|
returnSpec.resultErrorType(),
|
||||||
body,
|
body,
|
||||||
span(fnToken.start(), body.span().getEnd()));
|
span(fnToken.start(), body.span().getEnd()));
|
||||||
}
|
}
|
||||||
@ -754,7 +643,7 @@ public final class PbsParser {
|
|||||||
|
|
||||||
final var returnSpec = parseCallableReturnSpec(rightParen);
|
final var returnSpec = parseCallableReturnSpec(rightParen);
|
||||||
|
|
||||||
long end = returnSpec.returnType.span().getEnd();
|
long end = returnSpec.returnType().span().getEnd();
|
||||||
if (REQUIRE_SEMICOLON) {
|
if (REQUIRE_SEMICOLON) {
|
||||||
end = consume(PbsTokenKind.SEMICOLON, "Expected ';' after function signature").end();
|
end = consume(PbsTokenKind.SEMICOLON, "Expected ';' after function signature").end();
|
||||||
}
|
}
|
||||||
@ -762,9 +651,9 @@ public final class PbsParser {
|
|||||||
return new PbsAst.FunctionSignature(
|
return new PbsAst.FunctionSignature(
|
||||||
name.lexeme(),
|
name.lexeme(),
|
||||||
ReadOnlyList.wrap(parameters),
|
ReadOnlyList.wrap(parameters),
|
||||||
returnSpec.kind,
|
returnSpec.kind(),
|
||||||
returnSpec.returnType,
|
returnSpec.returnType(),
|
||||||
returnSpec.resultErrorType,
|
returnSpec.resultErrorType(),
|
||||||
attributes,
|
attributes,
|
||||||
span(fnToken.start(), end));
|
span(fnToken.start(), end));
|
||||||
}
|
}
|
||||||
@ -796,9 +685,9 @@ public final class PbsParser {
|
|||||||
return new PbsAst.CallbackDecl(
|
return new PbsAst.CallbackDecl(
|
||||||
name.lexeme(),
|
name.lexeme(),
|
||||||
ReadOnlyList.wrap(parameters),
|
ReadOnlyList.wrap(parameters),
|
||||||
returnSpec.kind,
|
returnSpec.kind(),
|
||||||
returnSpec.returnType,
|
returnSpec.returnType(),
|
||||||
returnSpec.resultErrorType,
|
returnSpec.resultErrorType(),
|
||||||
span(declareToken.start(), semicolon.end()));
|
span(declareToken.start(), semicolon.end()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -865,148 +754,15 @@ public final class PbsParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private ArrayList<PbsAst.Parameter> parseParametersUntilRightParen() {
|
private ArrayList<PbsAst.Parameter> parseParametersUntilRightParen() {
|
||||||
final var parameters = new ArrayList<PbsAst.Parameter>();
|
return typeParser.parseParametersUntilRightParen();
|
||||||
if (cursor.check(PbsTokenKind.RIGHT_PAREN)) {
|
|
||||||
return parameters;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
do {
|
private PbsTypeParser.ParsedReturnSpec parseCallableReturnSpec(final PbsToken anchorToken) {
|
||||||
final var parameterStart = cursor.peek();
|
return typeParser.parseCallableReturnSpec(anchorToken);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 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() {
|
private PbsAst.TypeRef parseTypeRef() {
|
||||||
if (cursor.match(PbsTokenKind.OPTIONAL)) {
|
return typeParser.parseTypeRef();
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1416,12 +1172,6 @@ public final class PbsParser {
|
|||||||
context.report(token, parseErrors, message);
|
context.report(token, parseErrors, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
private record ParsedReturnSpec(
|
|
||||||
PbsAst.ReturnKind kind,
|
|
||||||
PbsAst.TypeRef returnType,
|
|
||||||
PbsAst.TypeRef resultErrorType) {
|
|
||||||
}
|
|
||||||
|
|
||||||
private record StructBodyParse(
|
private record StructBodyParse(
|
||||||
ReadOnlyList<PbsAst.FunctionDecl> methods,
|
ReadOnlyList<PbsAst.FunctionDecl> methods,
|
||||||
ReadOnlyList<PbsAst.CtorDecl> ctors,
|
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