implements PR-11.3
This commit is contained in:
parent
abcc099334
commit
ea44d40410
@ -22,6 +22,7 @@ final class PbsExprParser {
|
||||
private final PbsExprParserContext context;
|
||||
private final PbsTokenCursor cursor;
|
||||
private final PbsExprControlFlowParser controlFlowParser;
|
||||
private final PbsExprPrimaryParser primaryParser;
|
||||
|
||||
PbsExprParser(
|
||||
final PbsParserContext parserContext,
|
||||
@ -29,6 +30,7 @@ final class PbsExprParser {
|
||||
this.context = new PbsExprParserContext(parserContext, blockParserDelegate);
|
||||
this.cursor = context.cursor();
|
||||
this.controlFlowParser = new PbsExprControlFlowParser(context, this::parseExpression, this::parsePrecedenceExpression);
|
||||
this.primaryParser = new PbsExprPrimaryParser(context, this::parseExpression, controlFlowParser::parseErrorPath);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -172,260 +174,19 @@ final class PbsExprParser {
|
||||
final var right = parseUnary();
|
||||
return new PbsAst.UnaryExpr(operator.lexeme(), right, span(operator.start(), right.span().getEnd()));
|
||||
}
|
||||
return parsePostfix();
|
||||
return primaryParser.parsePostfix();
|
||||
}
|
||||
|
||||
private PbsAst.Expression parsePostfix() {
|
||||
var expression = parsePrimary();
|
||||
|
||||
while (true) {
|
||||
if (cursor.match(PbsTokenKind.LEFT_PAREN)) {
|
||||
final var arguments = new ArrayList<PbsAst.Expression>();
|
||||
if (!cursor.check(PbsTokenKind.RIGHT_PAREN)) {
|
||||
do {
|
||||
arguments.add(parseExpression());
|
||||
} while (cursor.match(PbsTokenKind.COMMA));
|
||||
}
|
||||
final var close = consume(PbsTokenKind.RIGHT_PAREN, "Expected ')' after call arguments");
|
||||
expression = new PbsAst.CallExpr(expression, ReadOnlyList.wrap(arguments),
|
||||
span(expression.span().getStart(), close.end()));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cursor.match(PbsTokenKind.DOT)) {
|
||||
final var member = consumeMemberName("Expected member name after '.'");
|
||||
expression = new PbsAst.MemberExpr(
|
||||
expression,
|
||||
member.lexeme(),
|
||||
span(expression.span().getStart(), member.end()));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cursor.match(PbsTokenKind.BANG)) {
|
||||
final var bang = cursor.previous();
|
||||
expression = new PbsAst.PropagateExpr(expression, span(expression.span().getStart(), bang.end()));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cursor.match(PbsTokenKind.QUESTION)) {
|
||||
final var question = cursor.previous();
|
||||
report(question, ParseErrors.E_PARSE_INVALID_PROPAGATE_OPERATOR,
|
||||
"Use '!' as propagation operator; '?' is not valid PBS syntax");
|
||||
continue;
|
||||
}
|
||||
|
||||
return expression;
|
||||
}
|
||||
return primaryParser.parsePostfix();
|
||||
}
|
||||
|
||||
private PbsAst.Expression parsePrimary() {
|
||||
if (cursor.match(PbsTokenKind.TRUE)) {
|
||||
final var token = cursor.previous();
|
||||
return new PbsAst.BoolLiteralExpr(true, span(token.start(), token.end()));
|
||||
}
|
||||
if (cursor.match(PbsTokenKind.FALSE)) {
|
||||
final var token = cursor.previous();
|
||||
return new PbsAst.BoolLiteralExpr(false, span(token.start(), token.end()));
|
||||
}
|
||||
if (cursor.match(PbsTokenKind.INT_LITERAL)) {
|
||||
final var token = cursor.previous();
|
||||
return new PbsAst.IntLiteralExpr(parseLongOrDefault(token.lexeme()), span(token.start(), token.end()));
|
||||
}
|
||||
if (cursor.match(PbsTokenKind.FLOAT_LITERAL)) {
|
||||
final var token = cursor.previous();
|
||||
return new PbsAst.FloatLiteralExpr(parseDoubleOrDefault(token.lexeme()), span(token.start(), token.end()));
|
||||
}
|
||||
if (cursor.match(PbsTokenKind.BOUNDED_LITERAL)) {
|
||||
final var token = cursor.previous();
|
||||
final var raw = token.lexeme().substring(0, Math.max(token.lexeme().length() - 1, 0));
|
||||
return new PbsAst.BoundedLiteralExpr(parseIntOrDefault(raw), span(token.start(), token.end()));
|
||||
}
|
||||
if (cursor.match(PbsTokenKind.STRING_LITERAL)) {
|
||||
final var token = cursor.previous();
|
||||
return new PbsAst.StringLiteralExpr(unescapeString(token.lexeme()), span(token.start(), token.end()));
|
||||
}
|
||||
if (cursor.match(PbsTokenKind.THIS)) {
|
||||
final var token = cursor.previous();
|
||||
return new PbsAst.ThisExpr(span(token.start(), token.end()));
|
||||
}
|
||||
if (cursor.match(PbsTokenKind.NEW)) {
|
||||
return parseNewExpression(cursor.previous());
|
||||
}
|
||||
if (cursor.match(PbsTokenKind.BIND)) {
|
||||
return parseBindExpression(cursor.previous());
|
||||
}
|
||||
if (cursor.match(PbsTokenKind.SOME)) {
|
||||
return parseSomeExpression(cursor.previous());
|
||||
}
|
||||
if (cursor.match(PbsTokenKind.NONE)) {
|
||||
final var token = cursor.previous();
|
||||
return new PbsAst.NoneExpr(span(token.start(), token.end()));
|
||||
}
|
||||
if (cursor.match(PbsTokenKind.OK)) {
|
||||
return parseOkExpression(cursor.previous());
|
||||
}
|
||||
if (cursor.match(PbsTokenKind.ERR)) {
|
||||
return parseErrExpression(cursor.previous());
|
||||
}
|
||||
if (cursor.match(PbsTokenKind.IDENTIFIER)) {
|
||||
final var token = cursor.previous();
|
||||
return new PbsAst.IdentifierExpr(token.lexeme(), span(token.start(), token.end()));
|
||||
}
|
||||
if (cursor.match(PbsTokenKind.LEFT_PAREN)) {
|
||||
return parseParenthesizedPrimary(cursor.previous());
|
||||
}
|
||||
if (cursor.check(PbsTokenKind.LEFT_BRACE)) {
|
||||
final var block = parseSurfaceBlock("Expected block expression");
|
||||
return new PbsAst.BlockExpr(block, block.span());
|
||||
}
|
||||
|
||||
final var token = cursor.peek();
|
||||
report(token, ParseErrors.E_PARSE_UNEXPECTED_TOKEN, "Unexpected token in expression: " + token.kind());
|
||||
cursor.advance();
|
||||
return new PbsAst.IntLiteralExpr(0L, span(token.start(), token.end()));
|
||||
return primaryParser.parsePrimary();
|
||||
}
|
||||
|
||||
private PbsAst.Expression parseParenthesizedPrimary(final PbsToken open) {
|
||||
if (cursor.match(PbsTokenKind.RIGHT_PAREN)) {
|
||||
final var close = cursor.previous();
|
||||
return new PbsAst.UnitExpr(span(open.start(), close.end()));
|
||||
}
|
||||
|
||||
if (cursor.check(PbsTokenKind.IDENTIFIER) && cursor.checkNext(PbsTokenKind.COLON)) {
|
||||
final var items = parseTupleItems(true);
|
||||
final var close = consume(PbsTokenKind.RIGHT_PAREN, "Expected ')' after tuple literal");
|
||||
if (items.size() < 2) {
|
||||
report(close, ParseErrors.E_PARSE_INVALID_TUPLE_LITERAL,
|
||||
"Single-slot tuple literal is not allowed in PBS core syntax");
|
||||
}
|
||||
if (items.size() > MAX_TUPLE_LITERAL_ARITY) {
|
||||
report(close, ParseErrors.E_PARSE_INVALID_TUPLE_LITERAL,
|
||||
"Tuple literal arity must be between 2 and 6 items");
|
||||
}
|
||||
return new PbsAst.TupleExpr(ReadOnlyList.wrap(items), span(open.start(), close.end()));
|
||||
}
|
||||
|
||||
final var first = parseExpression();
|
||||
if (!cursor.match(PbsTokenKind.COMMA)) {
|
||||
final var close = consume(PbsTokenKind.RIGHT_PAREN, "Expected ')' after grouped expression");
|
||||
return new PbsAst.GroupExpr(first, span(open.start(), close.end()));
|
||||
}
|
||||
|
||||
final var items = new ArrayList<PbsAst.TupleItem>();
|
||||
items.add(new PbsAst.TupleItem(null, first, first.span()));
|
||||
var hasLabels = false;
|
||||
while (!cursor.check(PbsTokenKind.RIGHT_PAREN) && !cursor.isAtEnd()) {
|
||||
final var item = parseTupleItem();
|
||||
if (item.label() != null) {
|
||||
hasLabels = true;
|
||||
}
|
||||
items.add(item);
|
||||
if (!cursor.match(PbsTokenKind.COMMA)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (items.size() < 2) {
|
||||
report(cursor.peek(), ParseErrors.E_PARSE_INVALID_TUPLE_LITERAL,
|
||||
"Tuple literals require at least two items");
|
||||
}
|
||||
|
||||
final var close = consume(PbsTokenKind.RIGHT_PAREN, "Expected ')' after tuple literal");
|
||||
if (items.size() > MAX_TUPLE_LITERAL_ARITY) {
|
||||
report(close, ParseErrors.E_PARSE_INVALID_TUPLE_LITERAL,
|
||||
"Tuple literal arity must be between 2 and 6 items");
|
||||
}
|
||||
if (hasLabels) {
|
||||
for (final var item : items) {
|
||||
if (item.label() == null) {
|
||||
report(close, ParseErrors.E_PARSE_INVALID_TUPLE_LITERAL,
|
||||
"Mixed labeled/unlabeled tuple items are not allowed");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new PbsAst.TupleExpr(ReadOnlyList.wrap(items), span(open.start(), close.end()));
|
||||
}
|
||||
|
||||
private ArrayList<PbsAst.TupleItem> parseTupleItems(final boolean labeledOnly) {
|
||||
final var items = new ArrayList<PbsAst.TupleItem>();
|
||||
do {
|
||||
final var item = parseTupleItem();
|
||||
items.add(item);
|
||||
if (labeledOnly && item.label() == null) {
|
||||
report(cursor.previous(), ParseErrors.E_PARSE_INVALID_TUPLE_LITERAL,
|
||||
"Named tuple literal items must use labels");
|
||||
}
|
||||
} while (cursor.match(PbsTokenKind.COMMA) && !cursor.check(PbsTokenKind.RIGHT_PAREN));
|
||||
return items;
|
||||
}
|
||||
|
||||
private PbsAst.TupleItem parseTupleItem() {
|
||||
if (cursor.check(PbsTokenKind.IDENTIFIER) && cursor.checkNext(PbsTokenKind.COLON)) {
|
||||
final var label = cursor.advance();
|
||||
consume(PbsTokenKind.COLON, "Expected ':' after tuple label");
|
||||
final var value = parseExpression();
|
||||
return new PbsAst.TupleItem(label.lexeme(), value, span(label.start(), value.span().getEnd()));
|
||||
}
|
||||
|
||||
final var value = parseExpression();
|
||||
return new PbsAst.TupleItem(null, value, value.span());
|
||||
}
|
||||
|
||||
private PbsAst.Expression parseNewExpression(final PbsToken newToken) {
|
||||
final var typeName = consume(PbsTokenKind.IDENTIFIER, "Expected type name after 'new'");
|
||||
String ctorName = null;
|
||||
if (cursor.match(PbsTokenKind.DOT)) {
|
||||
ctorName = consume(PbsTokenKind.IDENTIFIER, "Expected constructor name after '.' in new target").lexeme();
|
||||
}
|
||||
|
||||
consume(PbsTokenKind.LEFT_PAREN, "Expected '(' after new target");
|
||||
final var arguments = new ArrayList<PbsAst.Expression>();
|
||||
if (!cursor.check(PbsTokenKind.RIGHT_PAREN)) {
|
||||
do {
|
||||
arguments.add(parseExpression());
|
||||
} while (cursor.match(PbsTokenKind.COMMA));
|
||||
}
|
||||
final var close = consume(PbsTokenKind.RIGHT_PAREN, "Expected ')' after new arguments");
|
||||
return new PbsAst.NewExpr(
|
||||
typeName.lexeme(),
|
||||
ctorName,
|
||||
ReadOnlyList.wrap(arguments),
|
||||
span(newToken.start(), close.end()));
|
||||
}
|
||||
|
||||
private PbsAst.Expression parseBindExpression(final PbsToken bindToken) {
|
||||
consume(PbsTokenKind.LEFT_PAREN, "Expected '(' after 'bind'");
|
||||
final var contextExpression = parseExpression();
|
||||
consume(PbsTokenKind.COMMA, "Expected ',' in bind(context, fn_name)");
|
||||
final var functionName = consume(PbsTokenKind.IDENTIFIER, "Expected function identifier in bind(context, fn_name)");
|
||||
final var close = consume(PbsTokenKind.RIGHT_PAREN, "Expected ')' after bind arguments");
|
||||
return new PbsAst.BindExpr(
|
||||
contextExpression,
|
||||
functionName.lexeme(),
|
||||
span(bindToken.start(), close.end()));
|
||||
}
|
||||
|
||||
private PbsAst.Expression parseSomeExpression(final PbsToken someToken) {
|
||||
consume(PbsTokenKind.LEFT_PAREN, "Expected '(' after 'some'");
|
||||
final var value = parseExpression();
|
||||
final var close = consume(PbsTokenKind.RIGHT_PAREN, "Expected ')' after some(payload)");
|
||||
return new PbsAst.SomeExpr(value, span(someToken.start(), close.end()));
|
||||
}
|
||||
|
||||
private PbsAst.Expression parseOkExpression(final PbsToken okToken) {
|
||||
consume(PbsTokenKind.LEFT_PAREN, "Expected '(' after 'ok'");
|
||||
final var value = parseExpression();
|
||||
final var close = consume(PbsTokenKind.RIGHT_PAREN, "Expected ')' after ok(payload)");
|
||||
return new PbsAst.OkExpr(value, span(okToken.start(), close.end()));
|
||||
}
|
||||
|
||||
private PbsAst.Expression parseErrExpression(final PbsToken errToken) {
|
||||
consume(PbsTokenKind.LEFT_PAREN, "Expected '(' after 'err'");
|
||||
final var errorPath = parseErrorPath();
|
||||
final var close = consume(PbsTokenKind.RIGHT_PAREN, "Expected ')' after err(Error.case)");
|
||||
return new PbsAst.ErrExpr(errorPath, span(errToken.start(), close.end()));
|
||||
return primaryParser.parseParenthesizedPrimary(open);
|
||||
}
|
||||
|
||||
private PbsAst.SwitchPattern parseSwitchPattern() {
|
||||
|
||||
@ -0,0 +1,339 @@
|
||||
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;
|
||||
|
||||
final class PbsExprPrimaryParser {
|
||||
private static final int MAX_TUPLE_LITERAL_ARITY = 6;
|
||||
|
||||
@FunctionalInterface
|
||||
interface ExpressionParserDelegate {
|
||||
PbsAst.Expression parse();
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
interface ErrorPathParserDelegate {
|
||||
PbsAst.ErrorPath parse();
|
||||
}
|
||||
|
||||
private final PbsExprParserContext context;
|
||||
private final PbsTokenCursor cursor;
|
||||
private final ExpressionParserDelegate expressionParserDelegate;
|
||||
private final ErrorPathParserDelegate errorPathParserDelegate;
|
||||
|
||||
PbsExprPrimaryParser(
|
||||
final PbsExprParserContext context,
|
||||
final ExpressionParserDelegate expressionParserDelegate,
|
||||
final ErrorPathParserDelegate errorPathParserDelegate) {
|
||||
this.context = context;
|
||||
this.cursor = context.cursor();
|
||||
this.expressionParserDelegate = expressionParserDelegate;
|
||||
this.errorPathParserDelegate = errorPathParserDelegate;
|
||||
}
|
||||
|
||||
PbsAst.Expression parsePostfix() {
|
||||
var expression = parsePrimary();
|
||||
|
||||
while (true) {
|
||||
if (cursor.match(PbsTokenKind.LEFT_PAREN)) {
|
||||
final var arguments = new ArrayList<PbsAst.Expression>();
|
||||
if (!cursor.check(PbsTokenKind.RIGHT_PAREN)) {
|
||||
do {
|
||||
arguments.add(expressionParserDelegate.parse());
|
||||
} while (cursor.match(PbsTokenKind.COMMA));
|
||||
}
|
||||
final var close = context.consume(PbsTokenKind.RIGHT_PAREN, "Expected ')' after call arguments");
|
||||
expression = new PbsAst.CallExpr(expression, ReadOnlyList.wrap(arguments),
|
||||
context.span(expression.span().getStart(), close.end()));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cursor.match(PbsTokenKind.DOT)) {
|
||||
final var member = context.consumeMemberName("Expected member name after '.'");
|
||||
expression = new PbsAst.MemberExpr(
|
||||
expression,
|
||||
member.lexeme(),
|
||||
context.span(expression.span().getStart(), member.end()));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cursor.match(PbsTokenKind.BANG)) {
|
||||
final var bang = cursor.previous();
|
||||
expression = new PbsAst.PropagateExpr(expression, context.span(expression.span().getStart(), bang.end()));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cursor.match(PbsTokenKind.QUESTION)) {
|
||||
final var question = cursor.previous();
|
||||
context.report(question, ParseErrors.E_PARSE_INVALID_PROPAGATE_OPERATOR,
|
||||
"Use '!' as propagation operator; '?' is not valid PBS syntax");
|
||||
continue;
|
||||
}
|
||||
|
||||
return expression;
|
||||
}
|
||||
}
|
||||
|
||||
PbsAst.Expression parsePrimary() {
|
||||
if (cursor.match(PbsTokenKind.TRUE)) {
|
||||
final var token = cursor.previous();
|
||||
return new PbsAst.BoolLiteralExpr(true, context.span(token.start(), token.end()));
|
||||
}
|
||||
if (cursor.match(PbsTokenKind.FALSE)) {
|
||||
final var token = cursor.previous();
|
||||
return new PbsAst.BoolLiteralExpr(false, context.span(token.start(), token.end()));
|
||||
}
|
||||
if (cursor.match(PbsTokenKind.INT_LITERAL)) {
|
||||
final var token = cursor.previous();
|
||||
return new PbsAst.IntLiteralExpr(parseLongOrDefault(token.lexeme()), context.span(token.start(), token.end()));
|
||||
}
|
||||
if (cursor.match(PbsTokenKind.FLOAT_LITERAL)) {
|
||||
final var token = cursor.previous();
|
||||
return new PbsAst.FloatLiteralExpr(parseDoubleOrDefault(token.lexeme()), context.span(token.start(), token.end()));
|
||||
}
|
||||
if (cursor.match(PbsTokenKind.BOUNDED_LITERAL)) {
|
||||
final var token = cursor.previous();
|
||||
final var raw = token.lexeme().substring(0, Math.max(token.lexeme().length() - 1, 0));
|
||||
return new PbsAst.BoundedLiteralExpr(parseIntOrDefault(raw), context.span(token.start(), token.end()));
|
||||
}
|
||||
if (cursor.match(PbsTokenKind.STRING_LITERAL)) {
|
||||
final var token = cursor.previous();
|
||||
return new PbsAst.StringLiteralExpr(unescapeString(token.lexeme()), context.span(token.start(), token.end()));
|
||||
}
|
||||
if (cursor.match(PbsTokenKind.THIS)) {
|
||||
final var token = cursor.previous();
|
||||
return new PbsAst.ThisExpr(context.span(token.start(), token.end()));
|
||||
}
|
||||
if (cursor.match(PbsTokenKind.NEW)) {
|
||||
return parseNewExpression(cursor.previous());
|
||||
}
|
||||
if (cursor.match(PbsTokenKind.BIND)) {
|
||||
return parseBindExpression(cursor.previous());
|
||||
}
|
||||
if (cursor.match(PbsTokenKind.SOME)) {
|
||||
return parseSomeExpression(cursor.previous());
|
||||
}
|
||||
if (cursor.match(PbsTokenKind.NONE)) {
|
||||
final var token = cursor.previous();
|
||||
return new PbsAst.NoneExpr(context.span(token.start(), token.end()));
|
||||
}
|
||||
if (cursor.match(PbsTokenKind.OK)) {
|
||||
return parseOkExpression(cursor.previous());
|
||||
}
|
||||
if (cursor.match(PbsTokenKind.ERR)) {
|
||||
return parseErrExpression(cursor.previous());
|
||||
}
|
||||
if (cursor.match(PbsTokenKind.IDENTIFIER)) {
|
||||
final var token = cursor.previous();
|
||||
return new PbsAst.IdentifierExpr(token.lexeme(), context.span(token.start(), token.end()));
|
||||
}
|
||||
if (cursor.match(PbsTokenKind.LEFT_PAREN)) {
|
||||
return parseParenthesizedPrimary(cursor.previous());
|
||||
}
|
||||
if (cursor.check(PbsTokenKind.LEFT_BRACE)) {
|
||||
final var block = context.parseSurfaceBlock("Expected block expression");
|
||||
return new PbsAst.BlockExpr(block, block.span());
|
||||
}
|
||||
|
||||
final var token = cursor.peek();
|
||||
context.report(token, ParseErrors.E_PARSE_UNEXPECTED_TOKEN, "Unexpected token in expression: " + token.kind());
|
||||
cursor.advance();
|
||||
return new PbsAst.IntLiteralExpr(0L, context.span(token.start(), token.end()));
|
||||
}
|
||||
|
||||
PbsAst.Expression parseParenthesizedPrimary(final PbsToken open) {
|
||||
if (cursor.match(PbsTokenKind.RIGHT_PAREN)) {
|
||||
final var close = cursor.previous();
|
||||
return new PbsAst.UnitExpr(context.span(open.start(), close.end()));
|
||||
}
|
||||
|
||||
if (cursor.check(PbsTokenKind.IDENTIFIER) && cursor.checkNext(PbsTokenKind.COLON)) {
|
||||
final var items = parseTupleItems(true);
|
||||
final var close = context.consume(PbsTokenKind.RIGHT_PAREN, "Expected ')' after tuple literal");
|
||||
if (items.size() < 2) {
|
||||
context.report(close, ParseErrors.E_PARSE_INVALID_TUPLE_LITERAL,
|
||||
"Single-slot tuple literal is not allowed in PBS core syntax");
|
||||
}
|
||||
if (items.size() > MAX_TUPLE_LITERAL_ARITY) {
|
||||
context.report(close, ParseErrors.E_PARSE_INVALID_TUPLE_LITERAL,
|
||||
"Tuple literal arity must be between 2 and 6 items");
|
||||
}
|
||||
return new PbsAst.TupleExpr(ReadOnlyList.wrap(items), context.span(open.start(), close.end()));
|
||||
}
|
||||
|
||||
final var first = expressionParserDelegate.parse();
|
||||
if (!cursor.match(PbsTokenKind.COMMA)) {
|
||||
final var close = context.consume(PbsTokenKind.RIGHT_PAREN, "Expected ')' after grouped expression");
|
||||
return new PbsAst.GroupExpr(first, context.span(open.start(), close.end()));
|
||||
}
|
||||
|
||||
final var items = new ArrayList<PbsAst.TupleItem>();
|
||||
items.add(new PbsAst.TupleItem(null, first, first.span()));
|
||||
var hasLabels = false;
|
||||
while (!cursor.check(PbsTokenKind.RIGHT_PAREN) && !cursor.isAtEnd()) {
|
||||
final var item = parseTupleItem();
|
||||
if (item.label() != null) {
|
||||
hasLabels = true;
|
||||
}
|
||||
items.add(item);
|
||||
if (!cursor.match(PbsTokenKind.COMMA)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (items.size() < 2) {
|
||||
context.report(cursor.peek(), ParseErrors.E_PARSE_INVALID_TUPLE_LITERAL,
|
||||
"Tuple literals require at least two items");
|
||||
}
|
||||
|
||||
final var close = context.consume(PbsTokenKind.RIGHT_PAREN, "Expected ')' after tuple literal");
|
||||
if (items.size() > MAX_TUPLE_LITERAL_ARITY) {
|
||||
context.report(close, ParseErrors.E_PARSE_INVALID_TUPLE_LITERAL,
|
||||
"Tuple literal arity must be between 2 and 6 items");
|
||||
}
|
||||
if (hasLabels) {
|
||||
for (final var item : items) {
|
||||
if (item.label() == null) {
|
||||
context.report(close, ParseErrors.E_PARSE_INVALID_TUPLE_LITERAL,
|
||||
"Mixed labeled/unlabeled tuple items are not allowed");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new PbsAst.TupleExpr(ReadOnlyList.wrap(items), context.span(open.start(), close.end()));
|
||||
}
|
||||
|
||||
private ArrayList<PbsAst.TupleItem> parseTupleItems(final boolean labeledOnly) {
|
||||
final var items = new ArrayList<PbsAst.TupleItem>();
|
||||
do {
|
||||
final var item = parseTupleItem();
|
||||
items.add(item);
|
||||
if (labeledOnly && item.label() == null) {
|
||||
context.report(cursor.previous(), ParseErrors.E_PARSE_INVALID_TUPLE_LITERAL,
|
||||
"Named tuple literal items must use labels");
|
||||
}
|
||||
} while (cursor.match(PbsTokenKind.COMMA) && !cursor.check(PbsTokenKind.RIGHT_PAREN));
|
||||
return items;
|
||||
}
|
||||
|
||||
private PbsAst.TupleItem parseTupleItem() {
|
||||
if (cursor.check(PbsTokenKind.IDENTIFIER) && cursor.checkNext(PbsTokenKind.COLON)) {
|
||||
final var label = cursor.advance();
|
||||
context.consume(PbsTokenKind.COLON, "Expected ':' after tuple label");
|
||||
final var value = expressionParserDelegate.parse();
|
||||
return new PbsAst.TupleItem(label.lexeme(), value, context.span(label.start(), value.span().getEnd()));
|
||||
}
|
||||
|
||||
final var value = expressionParserDelegate.parse();
|
||||
return new PbsAst.TupleItem(null, value, value.span());
|
||||
}
|
||||
|
||||
private PbsAst.Expression parseNewExpression(final PbsToken newToken) {
|
||||
final var typeName = context.consume(PbsTokenKind.IDENTIFIER, "Expected type name after 'new'");
|
||||
String ctorName = null;
|
||||
if (cursor.match(PbsTokenKind.DOT)) {
|
||||
ctorName = context.consume(PbsTokenKind.IDENTIFIER, "Expected constructor name after '.' in new target").lexeme();
|
||||
}
|
||||
|
||||
context.consume(PbsTokenKind.LEFT_PAREN, "Expected '(' after new target");
|
||||
final var arguments = new ArrayList<PbsAst.Expression>();
|
||||
if (!cursor.check(PbsTokenKind.RIGHT_PAREN)) {
|
||||
do {
|
||||
arguments.add(expressionParserDelegate.parse());
|
||||
} while (cursor.match(PbsTokenKind.COMMA));
|
||||
}
|
||||
final var close = context.consume(PbsTokenKind.RIGHT_PAREN, "Expected ')' after new arguments");
|
||||
return new PbsAst.NewExpr(
|
||||
typeName.lexeme(),
|
||||
ctorName,
|
||||
ReadOnlyList.wrap(arguments),
|
||||
context.span(newToken.start(), close.end()));
|
||||
}
|
||||
|
||||
private PbsAst.Expression parseBindExpression(final PbsToken bindToken) {
|
||||
context.consume(PbsTokenKind.LEFT_PAREN, "Expected '(' after 'bind'");
|
||||
final var contextExpression = expressionParserDelegate.parse();
|
||||
context.consume(PbsTokenKind.COMMA, "Expected ',' in bind(context, fn_name)");
|
||||
final var functionName = context.consume(PbsTokenKind.IDENTIFIER, "Expected function identifier in bind(context, fn_name)");
|
||||
final var close = context.consume(PbsTokenKind.RIGHT_PAREN, "Expected ')' after bind arguments");
|
||||
return new PbsAst.BindExpr(
|
||||
contextExpression,
|
||||
functionName.lexeme(),
|
||||
context.span(bindToken.start(), close.end()));
|
||||
}
|
||||
|
||||
private PbsAst.Expression parseSomeExpression(final PbsToken someToken) {
|
||||
context.consume(PbsTokenKind.LEFT_PAREN, "Expected '(' after 'some'");
|
||||
final var value = expressionParserDelegate.parse();
|
||||
final var close = context.consume(PbsTokenKind.RIGHT_PAREN, "Expected ')' after some(payload)");
|
||||
return new PbsAst.SomeExpr(value, context.span(someToken.start(), close.end()));
|
||||
}
|
||||
|
||||
private PbsAst.Expression parseOkExpression(final PbsToken okToken) {
|
||||
context.consume(PbsTokenKind.LEFT_PAREN, "Expected '(' after 'ok'");
|
||||
final var value = expressionParserDelegate.parse();
|
||||
final var close = context.consume(PbsTokenKind.RIGHT_PAREN, "Expected ')' after ok(payload)");
|
||||
return new PbsAst.OkExpr(value, context.span(okToken.start(), close.end()));
|
||||
}
|
||||
|
||||
private PbsAst.Expression parseErrExpression(final PbsToken errToken) {
|
||||
context.consume(PbsTokenKind.LEFT_PAREN, "Expected '(' after 'err'");
|
||||
final var errorPath = errorPathParserDelegate.parse();
|
||||
final var close = context.consume(PbsTokenKind.RIGHT_PAREN, "Expected ')' after err(Error.case)");
|
||||
return new PbsAst.ErrExpr(errorPath, context.span(errToken.start(), close.end()));
|
||||
}
|
||||
|
||||
private long parseLongOrDefault(final String text) {
|
||||
try {
|
||||
return Long.parseLong(text);
|
||||
} catch (NumberFormatException ignored) {
|
||||
return 0L;
|
||||
}
|
||||
}
|
||||
|
||||
private int parseIntOrDefault(final String text) {
|
||||
try {
|
||||
return Integer.parseInt(text);
|
||||
} catch (NumberFormatException ignored) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private double parseDoubleOrDefault(final String text) {
|
||||
try {
|
||||
return Double.parseDouble(text);
|
||||
} catch (NumberFormatException ignored) {
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
private String unescapeString(final String lexeme) {
|
||||
if (lexeme.length() < 2) {
|
||||
return "";
|
||||
}
|
||||
final var raw = lexeme.substring(1, lexeme.length() - 1);
|
||||
final var sb = new StringBuilder(raw.length());
|
||||
for (int i = 0; i < raw.length(); i++) {
|
||||
final char c = raw.charAt(i);
|
||||
if (c != '\\' || i + 1 >= raw.length()) {
|
||||
sb.append(c);
|
||||
continue;
|
||||
}
|
||||
final char next = raw.charAt(++i);
|
||||
switch (next) {
|
||||
case 'n' -> sb.append('\n');
|
||||
case 'r' -> sb.append('\r');
|
||||
case 't' -> sb.append('\t');
|
||||
case '"' -> sb.append('"');
|
||||
case '\\' -> sb.append('\\');
|
||||
default -> sb.append('\\').append(next);
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user