implements PR-11.5

This commit is contained in:
bQUARKz 2026-03-10 09:59:07 +00:00
parent eb5cecc573
commit 852ff351ed
Signed by: bquarkz
SSH Key Fingerprint: SHA256:Z7dgqoglWwoK6j6u4QC87OveEq74WOhFN+gitsxtkf8

View File

@ -4,16 +4,11 @@ import p.studio.compiler.pbs.ast.PbsAst;
import p.studio.compiler.pbs.lexer.PbsToken;
import p.studio.compiler.pbs.lexer.PbsTokenKind;
import p.studio.compiler.source.Span;
import p.studio.utilities.structures.ReadOnlyList;
import java.util.ArrayList;
/**
* Dedicated expression parser for PBS.
* Dedicated expression parser for PBS precedence and orchestration.
*/
final class PbsExprParser {
private static final int MAX_TUPLE_LITERAL_ARITY = 6;
@FunctionalInterface
interface BlockParserDelegate {
PbsAst.Block parse(String message);
@ -33,9 +28,6 @@ final class PbsExprParser {
this.primaryParser = new PbsExprPrimaryParser(context, this::parseExpression, controlFlowParser::parseErrorPath);
}
/**
* Entry point for expression parsing.
*/
PbsAst.Expression parseExpression() {
return controlFlowParser.parseHandle();
}
@ -44,32 +36,6 @@ final class PbsExprParser {
return parseApply();
}
private PbsAst.Expression parseHandle() {
return controlFlowParser.parseHandle();
}
/**
* Parses right-associative else extraction: {@code a else (b else c)}.
*/
private PbsAst.Expression parseElse() {
return controlFlowParser.parseElse();
}
private PbsAst.Expression parseIfExpression() {
return controlFlowParser.parseIfExpression();
}
private PbsAst.IfExpr parseIfExpressionFromToken(final PbsToken ifToken) {
return controlFlowParser.parseIfExpressionFromToken(ifToken);
}
private PbsAst.Expression parseSwitchExpression() {
return controlFlowParser.parseSwitchExpression();
}
/**
* Parses right-associative apply expressions.
*/
private PbsAst.Expression parseApply() {
final var left = parseOr();
if (!cursor.match(PbsTokenKind.APPLY)) {
@ -85,7 +51,10 @@ final class PbsExprParser {
while (cursor.match(PbsTokenKind.OR_OR, PbsTokenKind.OR)) {
final var operator = cursor.previous();
final var right = parseAnd();
expression = new PbsAst.BinaryExpr(operator.lexeme(), expression, right,
expression = new PbsAst.BinaryExpr(
operator.lexeme(),
expression,
right,
span(expression.span().getStart(), right.span().getEnd()));
}
return expression;
@ -96,7 +65,10 @@ final class PbsExprParser {
while (cursor.match(PbsTokenKind.AND_AND, PbsTokenKind.AND)) {
final var operator = cursor.previous();
final var right = parseEquality();
expression = new PbsAst.BinaryExpr(operator.lexeme(), expression, right,
expression = new PbsAst.BinaryExpr(
operator.lexeme(),
expression,
right,
span(expression.span().getStart(), right.span().getEnd()));
}
return expression;
@ -107,10 +79,13 @@ final class PbsExprParser {
if (cursor.match(PbsTokenKind.EQUAL_EQUAL, PbsTokenKind.BANG_EQUAL)) {
final var operator = cursor.previous();
final var right = parseComparison();
expression = new PbsAst.BinaryExpr(operator.lexeme(), expression, right,
expression = new PbsAst.BinaryExpr(
operator.lexeme(),
expression,
right,
span(expression.span().getStart(), right.span().getEnd()));
if (cursor.check(PbsTokenKind.EQUAL_EQUAL) || cursor.check(PbsTokenKind.BANG_EQUAL)) {
report(cursor.peek(), ParseErrors.E_PARSE_NON_ASSOC, "Chained equality is not allowed");
context.report(cursor.peek(), ParseErrors.E_PARSE_NON_ASSOC, "Chained equality is not allowed");
while (cursor.match(PbsTokenKind.EQUAL_EQUAL, PbsTokenKind.BANG_EQUAL)) {
parseComparison();
}
@ -124,11 +99,16 @@ final class PbsExprParser {
if (cursor.match(PbsTokenKind.LESS, PbsTokenKind.LESS_EQUAL, PbsTokenKind.GREATER, PbsTokenKind.GREATER_EQUAL)) {
final var operator = cursor.previous();
final var right = parseAs();
expression = new PbsAst.BinaryExpr(operator.lexeme(), expression, right,
expression = new PbsAst.BinaryExpr(
operator.lexeme(),
expression,
right,
span(expression.span().getStart(), right.span().getEnd()));
if (cursor.check(PbsTokenKind.LESS) || cursor.check(PbsTokenKind.LESS_EQUAL)
|| cursor.check(PbsTokenKind.GREATER) || cursor.check(PbsTokenKind.GREATER_EQUAL)) {
report(cursor.peek(), ParseErrors.E_PARSE_NON_ASSOC, "Chained comparison is not allowed");
if (cursor.check(PbsTokenKind.LESS)
|| cursor.check(PbsTokenKind.LESS_EQUAL)
|| cursor.check(PbsTokenKind.GREATER)
|| cursor.check(PbsTokenKind.GREATER_EQUAL)) {
context.report(cursor.peek(), ParseErrors.E_PARSE_NON_ASSOC, "Chained comparison is not allowed");
while (cursor.match(PbsTokenKind.LESS, PbsTokenKind.LESS_EQUAL, PbsTokenKind.GREATER, PbsTokenKind.GREATER_EQUAL)) {
parseAs();
}
@ -140,8 +120,11 @@ final class PbsExprParser {
private PbsAst.Expression parseAs() {
var expression = parseTerm();
if (cursor.match(PbsTokenKind.AS)) {
final var contract = consume(PbsTokenKind.IDENTIFIER, "Expected contract identifier after 'as'");
expression = new PbsAst.AsExpr(expression, contract.lexeme(), span(expression.span().getStart(), contract.end()));
final var contract = context.consume(PbsTokenKind.IDENTIFIER, "Expected contract identifier after 'as'");
expression = new PbsAst.AsExpr(
expression,
contract.lexeme(),
span(expression.span().getStart(), contract.end()));
}
return expression;
}
@ -151,7 +134,10 @@ final class PbsExprParser {
while (cursor.match(PbsTokenKind.PLUS, PbsTokenKind.MINUS)) {
final var operator = cursor.previous();
final var right = parseFactor();
expression = new PbsAst.BinaryExpr(operator.lexeme(), expression, right,
expression = new PbsAst.BinaryExpr(
operator.lexeme(),
expression,
right,
span(expression.span().getStart(), right.span().getEnd()));
}
return expression;
@ -162,7 +148,10 @@ final class PbsExprParser {
while (cursor.match(PbsTokenKind.STAR, PbsTokenKind.SLASH, PbsTokenKind.PERCENT)) {
final var operator = cursor.previous();
final var right = parseUnary();
expression = new PbsAst.BinaryExpr(operator.lexeme(), expression, right,
expression = new PbsAst.BinaryExpr(
operator.lexeme(),
expression,
right,
span(expression.span().getStart(), right.span().getEnd()));
}
return expression;
@ -177,100 +166,7 @@ final class PbsExprParser {
return primaryParser.parsePostfix();
}
private PbsAst.Expression parsePostfix() {
return primaryParser.parsePostfix();
}
private PbsAst.Expression parsePrimary() {
return primaryParser.parsePrimary();
}
private PbsAst.Expression parseParenthesizedPrimary(final PbsToken open) {
return primaryParser.parseParenthesizedPrimary(open);
}
private PbsAst.SwitchPattern parseSwitchPattern() {
return controlFlowParser.parseSwitchPattern();
}
private PbsAst.Expression parseLiteralPatternExpression() {
return controlFlowParser.parseLiteralPatternExpression();
}
private PbsAst.HandlePattern parseHandlePattern() {
return controlFlowParser.parseHandlePattern();
}
private PbsAst.ErrorPath parseErrorPath() {
return controlFlowParser.parseErrorPath();
}
private PbsAst.Block parseSurfaceBlock(final String message) {
return context.parseSurfaceBlock(message);
}
private PbsToken consume(final PbsTokenKind kind, final String message) {
return context.consume(kind, message);
}
private PbsToken consumeMemberName(final String message) {
return context.consumeMemberName(message);
}
private Span span(final long start, final long end) {
return context.span(start, end);
}
private void report(final PbsToken token, final ParseErrors parseErrors, final String message) {
context.report(token, parseErrors, message);
}
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();
}
}