implements PR-10.1

This commit is contained in:
bQUARKz 2026-03-10 09:30:36 +00:00
parent ba69a64832
commit e56fe2b9b7
Signed by: bquarkz
SSH Key Fingerprint: SHA256:Z7dgqoglWwoK6j6u4QC87OveEq74WOhFN+gitsxtkf8
3 changed files with 87 additions and 38 deletions

View File

@ -4,8 +4,6 @@ import p.studio.compiler.pbs.ast.PbsAst;
import p.studio.compiler.pbs.lexer.PbsToken; import p.studio.compiler.pbs.lexer.PbsToken;
import p.studio.compiler.pbs.lexer.PbsTokenKind; import p.studio.compiler.pbs.lexer.PbsTokenKind;
import p.studio.compiler.source.Span; import p.studio.compiler.source.Span;
import p.studio.compiler.source.diagnostics.DiagnosticSink;
import p.studio.compiler.source.identifiers.FileId;
import p.studio.utilities.structures.ReadOnlyList; import p.studio.utilities.structures.ReadOnlyList;
import java.util.ArrayList; import java.util.ArrayList;
@ -21,19 +19,15 @@ final class PbsExprParser {
PbsAst.Block parse(String message); PbsAst.Block parse(String message);
} }
private final PbsParserContext context;
private final PbsTokenCursor cursor; private final PbsTokenCursor cursor;
private final FileId fileId;
private final DiagnosticSink diagnostics;
private final BlockParserDelegate blockParserDelegate; private final BlockParserDelegate blockParserDelegate;
PbsExprParser( PbsExprParser(
final PbsTokenCursor cursor, final PbsParserContext context,
final FileId fileId,
final DiagnosticSink diagnostics,
final BlockParserDelegate blockParserDelegate) { final BlockParserDelegate blockParserDelegate) {
this.cursor = cursor; this.context = context;
this.fileId = fileId; this.cursor = context.cursor();
this.diagnostics = diagnostics;
this.blockParserDelegate = blockParserDelegate; this.blockParserDelegate = blockParserDelegate;
} }
@ -644,11 +638,11 @@ final class PbsExprParser {
} }
private Span span(final long start, final long end) { private Span span(final long start, final long end) {
return new Span(fileId, start, end); return context.span(start, end);
} }
private void report(final PbsToken token, final ParseErrors parseErrors, final String message) { private void report(final PbsToken token, final ParseErrors parseErrors, final String message) {
p.studio.compiler.source.diagnostics.Diagnostics.error(diagnostics, parseErrors.name(), message, new Span(fileId, token.start(), token.end())); context.report(token, parseErrors, message);
} }
private long parseLongOrDefault(final String text) { private long parseLongOrDefault(final String text) {

View File

@ -4,7 +4,6 @@ import p.studio.compiler.pbs.ast.PbsAst;
import p.studio.compiler.pbs.lexer.PbsToken; import p.studio.compiler.pbs.lexer.PbsToken;
import p.studio.compiler.pbs.lexer.PbsTokenKind; import p.studio.compiler.pbs.lexer.PbsTokenKind;
import p.studio.compiler.source.Span; import p.studio.compiler.source.Span;
import p.studio.compiler.source.diagnostics.DiagnosticSink;
import p.studio.compiler.source.diagnostics.RelatedSpan; import p.studio.compiler.source.diagnostics.RelatedSpan;
import p.studio.compiler.source.identifiers.FileId; import p.studio.compiler.source.identifiers.FileId;
import p.studio.utilities.structures.ReadOnlyList; import p.studio.utilities.structures.ReadOnlyList;
@ -30,23 +29,18 @@ public final class PbsParser {
INTERFACE_MODULE INTERFACE_MODULE
} }
private final PbsParserContext context;
private final PbsTokenCursor cursor; private final PbsTokenCursor cursor;
private final PbsExprParser exprParser; private final PbsExprParser exprParser;
private final FileId fileId;
private final DiagnosticSink diagnostics;
private final ParseMode parseMode;
private int loopDepth;
private PbsParser( private PbsParser(
final ReadOnlyList<PbsToken> tokens, final ReadOnlyList<PbsToken> tokens,
final FileId fileId, final FileId fileId,
final DiagnosticSink diagnostics, final p.studio.compiler.source.diagnostics.DiagnosticSink diagnostics,
final ParseMode parseMode) { final ParseMode parseMode) {
this.cursor = new PbsTokenCursor(tokens); this.context = new PbsParserContext(new PbsTokenCursor(tokens), fileId, diagnostics, parseMode);
this.exprParser = new PbsExprParser(cursor, fileId, diagnostics, this::parseExpressionSurfaceBlock); this.cursor = context.cursor();
this.fileId = fileId; this.exprParser = new PbsExprParser(context, this::parseExpressionSurfaceBlock);
this.diagnostics = diagnostics;
this.parseMode = parseMode;
} }
/** /**
@ -55,14 +49,14 @@ public final class PbsParser {
public static PbsAst.File parse( public static PbsAst.File parse(
final ReadOnlyList<PbsToken> tokens, final ReadOnlyList<PbsToken> tokens,
final FileId fileId, final FileId fileId,
final DiagnosticSink diagnostics) { final p.studio.compiler.source.diagnostics.DiagnosticSink diagnostics) {
return parse(tokens, fileId, diagnostics, ParseMode.ORDINARY); return parse(tokens, fileId, diagnostics, ParseMode.ORDINARY);
} }
public static PbsAst.File parse( public static PbsAst.File parse(
final ReadOnlyList<PbsToken> tokens, final ReadOnlyList<PbsToken> tokens,
final FileId fileId, final FileId fileId,
final DiagnosticSink diagnostics, final p.studio.compiler.source.diagnostics.DiagnosticSink diagnostics,
final ParseMode parseMode) { final ParseMode parseMode) {
return new PbsParser(tokens, fileId, diagnostics, parseMode).parseFile(); return new PbsParser(tokens, fileId, diagnostics, parseMode).parseFile();
} }
@ -374,7 +368,7 @@ public final class PbsParser {
final ReadOnlyList<PbsAst.Attribute> attributes, final ReadOnlyList<PbsAst.Attribute> attributes,
final String message) { final String message) {
for (final var attribute : attributes) { for (final var attribute : attributes) {
p.studio.compiler.source.diagnostics.Diagnostics.error(diagnostics, p.studio.compiler.source.diagnostics.Diagnostics.error(context.diagnostics(),
ParseErrors.E_PARSE_ATTRIBUTES_NOT_ALLOWED.name(), ParseErrors.E_PARSE_ATTRIBUTES_NOT_ALLOWED.name(),
message, message,
attribute.span()); attribute.span());
@ -382,11 +376,11 @@ public final class PbsParser {
} }
private boolean isNotInterfaceMode() { private boolean isNotInterfaceMode() {
return parseMode != ParseMode.INTERFACE_MODULE; return context.parseMode() != ParseMode.INTERFACE_MODULE;
} }
private boolean isOrdinaryMode() { private boolean isOrdinaryMode() {
return parseMode == ParseMode.ORDINARY; return context.parseMode() == ParseMode.ORDINARY;
} }
private PbsAst.StructDecl parseStructDeclaration(final PbsToken declareToken) { private PbsAst.StructDecl parseStructDeclaration(final PbsToken declareToken) {
@ -690,7 +684,7 @@ public final class PbsParser {
} else { } else {
final var firstIdSpan = explicitCaseIds.putIfAbsent(explicitValue, intTokenSpan); final var firstIdSpan = explicitCaseIds.putIfAbsent(explicitValue, intTokenSpan);
if (firstIdSpan != null) { if (firstIdSpan != null) {
p.studio.compiler.source.diagnostics.Diagnostics.error(diagnostics, p.studio.compiler.source.diagnostics.Diagnostics.error(context.diagnostics(),
ParseErrors.E_PARSE_DUPLICATE_ENUM_CASE_ID.name(), ParseErrors.E_PARSE_DUPLICATE_ENUM_CASE_ID.name(),
"Duplicate explicit enum identifier '%s'".formatted(explicitValue), "Duplicate explicit enum identifier '%s'".formatted(explicitValue),
intTokenSpan, intTokenSpan,
@ -703,7 +697,7 @@ public final class PbsParser {
final var firstLabelSpan = caseLabels.putIfAbsent(caseName.lexeme(), caseNameSpan); final var firstLabelSpan = caseLabels.putIfAbsent(caseName.lexeme(), caseNameSpan);
if (firstLabelSpan != null) { if (firstLabelSpan != null) {
p.studio.compiler.source.diagnostics.Diagnostics.error(diagnostics, p.studio.compiler.source.diagnostics.Diagnostics.error(context.diagnostics(),
ParseErrors.E_PARSE_DUPLICATE_ENUM_CASE_LABEL.name(), ParseErrors.E_PARSE_DUPLICATE_ENUM_CASE_LABEL.name(),
"Duplicate enum case label '%s'".formatted(caseName.lexeme()), "Duplicate enum case label '%s'".formatted(caseName.lexeme()),
caseNameSpan, caseNameSpan,
@ -1226,9 +1220,9 @@ public final class PbsParser {
stepExpression = exprParser.parseExpression(); stepExpression = exprParser.parseExpression();
} }
loopDepth++; context.enterLoop();
final var body = parseBlock(); final var body = parseBlock();
loopDepth--; context.exitLoop();
return new PbsAst.ForStatement( return new PbsAst.ForStatement(
iterator.lexeme(), iterator.lexeme(),
@ -1254,15 +1248,15 @@ public final class PbsParser {
private PbsAst.Statement parseWhileStatement(final PbsToken whileToken) { private PbsAst.Statement parseWhileStatement(final PbsToken whileToken) {
final var condition = exprParser.parseExpression(); final var condition = exprParser.parseExpression();
loopDepth++; context.enterLoop();
final var body = parseBlock(); final var body = parseBlock();
loopDepth--; context.exitLoop();
return new PbsAst.WhileStatement(condition, body, span(whileToken.start(), body.span().getEnd())); return new PbsAst.WhileStatement(condition, body, span(whileToken.start(), body.span().getEnd()));
} }
private PbsAst.Statement parseBreakStatement(final PbsToken breakToken) { private PbsAst.Statement parseBreakStatement(final PbsToken breakToken) {
final var semicolon = consume(PbsTokenKind.SEMICOLON, "Expected ';' after 'break'"); final var semicolon = consume(PbsTokenKind.SEMICOLON, "Expected ';' after 'break'");
if (loopDepth <= 0) { if (!context.isInsideLoop()) {
report(breakToken, ParseErrors.E_PARSE_LOOP_CONTROL_OUTSIDE_LOOP, report(breakToken, ParseErrors.E_PARSE_LOOP_CONTROL_OUTSIDE_LOOP,
"'break' is only valid inside loop contexts"); "'break' is only valid inside loop contexts");
} }
@ -1271,7 +1265,7 @@ public final class PbsParser {
private PbsAst.Statement parseContinueStatement(final PbsToken continueToken) { private PbsAst.Statement parseContinueStatement(final PbsToken continueToken) {
final var semicolon = consume(PbsTokenKind.SEMICOLON, "Expected ';' after 'continue'"); final var semicolon = consume(PbsTokenKind.SEMICOLON, "Expected ';' after 'continue'");
if (loopDepth <= 0) { if (!context.isInsideLoop()) {
report(continueToken, ParseErrors.E_PARSE_LOOP_CONTROL_OUTSIDE_LOOP, report(continueToken, ParseErrors.E_PARSE_LOOP_CONTROL_OUTSIDE_LOOP,
"'continue' is only valid inside loop contexts"); "'continue' is only valid inside loop contexts");
} }
@ -1412,14 +1406,14 @@ public final class PbsParser {
* Builds a source span for the current file. * Builds a source span for the current file.
*/ */
private Span span(final long start, final long end) { private Span span(final long start, final long end) {
return new Span(fileId, start, end); return context.span(start, end);
} }
/** /**
* Reports a parser diagnostic at the given token span. * Reports a parser diagnostic at the given token span.
*/ */
private void report(final PbsToken token, final ParseErrors parseErrors, final String message) { private void report(final PbsToken token, final ParseErrors parseErrors, final String message) {
p.studio.compiler.source.diagnostics.Diagnostics.error(diagnostics, parseErrors.name(), message, new Span(fileId, token.start(), token.end())); context.report(token, parseErrors, message);
} }
private record ParsedReturnSpec( private record ParsedReturnSpec(

View File

@ -0,0 +1,61 @@
package p.studio.compiler.pbs.parser;
import p.studio.compiler.pbs.lexer.PbsToken;
import p.studio.compiler.source.Span;
import p.studio.compiler.source.diagnostics.DiagnosticSink;
import p.studio.compiler.source.identifiers.FileId;
final class PbsParserContext {
private final PbsTokenCursor cursor;
private final FileId fileId;
private final DiagnosticSink diagnostics;
private final PbsParser.ParseMode parseMode;
private int loopDepth;
PbsParserContext(
final PbsTokenCursor cursor,
final FileId fileId,
final DiagnosticSink diagnostics,
final PbsParser.ParseMode parseMode) {
this.cursor = cursor;
this.fileId = fileId;
this.diagnostics = diagnostics;
this.parseMode = parseMode;
}
PbsTokenCursor cursor() {
return cursor;
}
DiagnosticSink diagnostics() {
return diagnostics;
}
PbsParser.ParseMode parseMode() {
return parseMode;
}
void enterLoop() {
loopDepth++;
}
void exitLoop() {
loopDepth--;
}
boolean isInsideLoop() {
return loopDepth > 0;
}
Span span(final long start, final long end) {
return new Span(fileId, start, end);
}
void report(final PbsToken token, final ParseErrors parseErrors, final String message) {
p.studio.compiler.source.diagnostics.Diagnostics.error(
diagnostics,
parseErrors.name(),
message,
span(token.start(), token.end()));
}
}