implements PR-10.4
This commit is contained in:
parent
416c4e7a11
commit
49fdcb58ca
@ -0,0 +1,325 @@
|
||||
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 PbsBlockParser {
|
||||
private static final boolean ALLOW_TAIL_EXPRESSION = true;
|
||||
|
||||
private final PbsParserContext context;
|
||||
private final PbsTokenCursor cursor;
|
||||
private final PbsExprParser exprParser;
|
||||
private final PbsTypeParser typeParser;
|
||||
|
||||
PbsBlockParser(
|
||||
final PbsParserContext context,
|
||||
final PbsExprParser exprParser,
|
||||
final PbsTypeParser typeParser) {
|
||||
this.context = context;
|
||||
this.cursor = context.cursor();
|
||||
this.exprParser = exprParser;
|
||||
this.typeParser = typeParser;
|
||||
}
|
||||
|
||||
PbsAst.Block parseBlock() {
|
||||
return parseBlock("Expected '{' to start block");
|
||||
}
|
||||
|
||||
PbsAst.Block parseBlock(final String message) {
|
||||
final var leftBrace = consume(PbsTokenKind.LEFT_BRACE, message);
|
||||
final var statements = new ArrayList<PbsAst.Statement>();
|
||||
PbsAst.Expression tailExpression = null;
|
||||
while (!cursor.check(PbsTokenKind.RIGHT_BRACE) && !cursor.isAtEnd()) {
|
||||
if (startsStructuredStatement() || isAssignmentStatementStart()) {
|
||||
statements.add(parseStatement());
|
||||
continue;
|
||||
}
|
||||
|
||||
final var expression = exprParser.parseExpression();
|
||||
if (cursor.match(PbsTokenKind.SEMICOLON)) {
|
||||
statements.add(new PbsAst.ExpressionStatement(
|
||||
expression,
|
||||
context.span(expression.span().getStart(), cursor.previous().end())));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ALLOW_TAIL_EXPRESSION && cursor.check(PbsTokenKind.RIGHT_BRACE)) {
|
||||
tailExpression = expression;
|
||||
break;
|
||||
}
|
||||
|
||||
context.report(cursor.peek(), ParseErrors.E_PARSE_EXPECTED_TOKEN, "Expected ';' after expression");
|
||||
if (!cursor.isAtEnd() && !cursor.check(PbsTokenKind.RIGHT_BRACE)) {
|
||||
cursor.advance();
|
||||
}
|
||||
}
|
||||
final var rightBrace = consume(PbsTokenKind.RIGHT_BRACE, "Expected '}' to end block");
|
||||
return new PbsAst.Block(
|
||||
ReadOnlyList.wrap(statements),
|
||||
tailExpression,
|
||||
context.span(leftBrace.start(), rightBrace.end()));
|
||||
}
|
||||
|
||||
private boolean startsStructuredStatement() {
|
||||
return cursor.check(PbsTokenKind.LET)
|
||||
|| cursor.check(PbsTokenKind.IF)
|
||||
|| cursor.check(PbsTokenKind.FOR)
|
||||
|| cursor.check(PbsTokenKind.WHILE)
|
||||
|| cursor.check(PbsTokenKind.BREAK)
|
||||
|| cursor.check(PbsTokenKind.CONTINUE)
|
||||
|| cursor.check(PbsTokenKind.RETURN);
|
||||
}
|
||||
|
||||
private PbsAst.Statement parseStatement() {
|
||||
if (cursor.match(PbsTokenKind.LET)) {
|
||||
return parseLetStatement(cursor.previous());
|
||||
}
|
||||
if (cursor.match(PbsTokenKind.IF)) {
|
||||
return parseIfStatement(cursor.previous());
|
||||
}
|
||||
if (cursor.match(PbsTokenKind.FOR)) {
|
||||
return parseForStatement(cursor.previous());
|
||||
}
|
||||
if (cursor.match(PbsTokenKind.WHILE)) {
|
||||
return parseWhileStatement(cursor.previous());
|
||||
}
|
||||
if (cursor.match(PbsTokenKind.BREAK)) {
|
||||
return parseBreakStatement(cursor.previous());
|
||||
}
|
||||
if (cursor.match(PbsTokenKind.CONTINUE)) {
|
||||
return parseContinueStatement(cursor.previous());
|
||||
}
|
||||
if (cursor.match(PbsTokenKind.RETURN)) {
|
||||
return parseReturnStatement(cursor.previous());
|
||||
}
|
||||
if (isAssignmentStatementStart()) {
|
||||
return parseAssignStatement();
|
||||
}
|
||||
return parseExpressionStatement();
|
||||
}
|
||||
|
||||
private PbsAst.Statement parseLetStatement(final PbsToken letToken) {
|
||||
final boolean isConst = cursor.match(PbsTokenKind.CONST);
|
||||
final var name = consume(PbsTokenKind.IDENTIFIER, "Expected variable name");
|
||||
|
||||
PbsAst.TypeRef explicitType = null;
|
||||
if (cursor.match(PbsTokenKind.COLON)) {
|
||||
explicitType = typeParser.parseTypeRef();
|
||||
}
|
||||
|
||||
consume(PbsTokenKind.EQUAL, "Expected '=' in let statement");
|
||||
final var initializer = exprParser.parseExpression();
|
||||
final var semicolon = consume(PbsTokenKind.SEMICOLON, "Expected ';' after let statement");
|
||||
|
||||
return new PbsAst.LetStatement(
|
||||
isConst,
|
||||
name.lexeme(),
|
||||
explicitType,
|
||||
initializer,
|
||||
context.span(letToken.start(), semicolon.end()));
|
||||
}
|
||||
|
||||
private PbsAst.Statement parseAssignStatement() {
|
||||
final var lValue = parseLValue();
|
||||
final var operator = parseAssignOperator(consumeAssignOperator());
|
||||
final var value = exprParser.parseExpression();
|
||||
final var semicolon = consume(PbsTokenKind.SEMICOLON, "Expected ';' after assignment");
|
||||
return new PbsAst.AssignStatement(
|
||||
lValue,
|
||||
operator,
|
||||
value,
|
||||
context.span(lValue.span().getStart(), semicolon.end()));
|
||||
}
|
||||
|
||||
private PbsAst.LValue parseLValue() {
|
||||
final PbsToken root;
|
||||
if (cursor.match(PbsTokenKind.IDENTIFIER, PbsTokenKind.THIS)) {
|
||||
root = cursor.previous();
|
||||
} else {
|
||||
root = consume(PbsTokenKind.IDENTIFIER, "Expected assignment target identifier");
|
||||
}
|
||||
final var segments = new ArrayList<String>();
|
||||
var end = root.end();
|
||||
while (cursor.match(PbsTokenKind.DOT)) {
|
||||
final var segment = consume(PbsTokenKind.IDENTIFIER, "Expected member name after '.' in assignment target");
|
||||
segments.add(segment.lexeme());
|
||||
end = segment.end();
|
||||
}
|
||||
return new PbsAst.LValue(root.lexeme(), ReadOnlyList.wrap(segments), context.span(root.start(), end));
|
||||
}
|
||||
|
||||
private PbsToken consumeAssignOperator() {
|
||||
if (cursor.match(PbsTokenKind.EQUAL,
|
||||
PbsTokenKind.PLUS_EQUAL,
|
||||
PbsTokenKind.MINUS_EQUAL,
|
||||
PbsTokenKind.STAR_EQUAL,
|
||||
PbsTokenKind.SLASH_EQUAL,
|
||||
PbsTokenKind.PERCENT_EQUAL)) {
|
||||
return cursor.previous();
|
||||
}
|
||||
final var token = cursor.peek();
|
||||
context.report(token, ParseErrors.E_PARSE_INVALID_ASSIGN_TARGET,
|
||||
"Expected assignment operator after assignment target");
|
||||
if (!cursor.isAtEnd()) {
|
||||
return cursor.advance();
|
||||
}
|
||||
return token;
|
||||
}
|
||||
|
||||
private PbsAst.AssignOperator parseAssignOperator(final PbsToken token) {
|
||||
return switch (token.kind()) {
|
||||
case EQUAL -> PbsAst.AssignOperator.ASSIGN;
|
||||
case PLUS_EQUAL -> PbsAst.AssignOperator.ADD_ASSIGN;
|
||||
case MINUS_EQUAL -> PbsAst.AssignOperator.SUB_ASSIGN;
|
||||
case STAR_EQUAL -> PbsAst.AssignOperator.MUL_ASSIGN;
|
||||
case SLASH_EQUAL -> PbsAst.AssignOperator.DIV_ASSIGN;
|
||||
case PERCENT_EQUAL -> PbsAst.AssignOperator.MOD_ASSIGN;
|
||||
default -> PbsAst.AssignOperator.ASSIGN;
|
||||
};
|
||||
}
|
||||
|
||||
private PbsAst.IfStatement parseIfStatement(final PbsToken ifToken) {
|
||||
final var condition = exprParser.parseExpression();
|
||||
final var thenBlock = parseBlock();
|
||||
|
||||
PbsAst.IfStatement elseIf = null;
|
||||
PbsAst.Block elseBlock = null;
|
||||
if (cursor.match(PbsTokenKind.ELSE)) {
|
||||
if (cursor.match(PbsTokenKind.IF)) {
|
||||
elseIf = parseIfStatement(cursor.previous());
|
||||
} else {
|
||||
elseBlock = parseBlock();
|
||||
}
|
||||
}
|
||||
|
||||
final var end = elseIf != null
|
||||
? elseIf.span().getEnd()
|
||||
: (elseBlock != null ? elseBlock.span().getEnd() : thenBlock.span().getEnd());
|
||||
|
||||
return new PbsAst.IfStatement(
|
||||
condition,
|
||||
thenBlock,
|
||||
elseIf,
|
||||
elseBlock,
|
||||
context.span(ifToken.start(), end));
|
||||
}
|
||||
|
||||
private PbsAst.Statement parseForStatement(final PbsToken forToken) {
|
||||
final var iterator = consume(PbsTokenKind.IDENTIFIER, "Expected loop iterator name in 'for'");
|
||||
consumeForToken(PbsTokenKind.COLON, "Expected ':' after iterator name in 'for'");
|
||||
final var iteratorType = typeParser.parseTypeRef();
|
||||
consumeForToken(PbsTokenKind.FROM, "Expected 'from' in 'for' statement");
|
||||
final var fromExpression = exprParser.parseExpression();
|
||||
consumeForToken(PbsTokenKind.UNTIL, "Expected 'until' in 'for' statement");
|
||||
final var untilExpression = exprParser.parseExpression();
|
||||
|
||||
PbsAst.Expression stepExpression = null;
|
||||
if (cursor.match(PbsTokenKind.STEP)) {
|
||||
stepExpression = exprParser.parseExpression();
|
||||
}
|
||||
|
||||
context.enterLoop();
|
||||
final var body = parseBlock();
|
||||
context.exitLoop();
|
||||
|
||||
return new PbsAst.ForStatement(
|
||||
iterator.lexeme(),
|
||||
iteratorType,
|
||||
fromExpression,
|
||||
untilExpression,
|
||||
stepExpression,
|
||||
body,
|
||||
context.span(forToken.start(), body.span().getEnd()));
|
||||
}
|
||||
|
||||
private PbsToken consumeForToken(final PbsTokenKind kind, final String message) {
|
||||
if (cursor.check(kind)) {
|
||||
return cursor.advance();
|
||||
}
|
||||
final var token = cursor.peek();
|
||||
context.report(token, ParseErrors.E_PARSE_INVALID_FOR_FORM, message + ", found " + token.kind());
|
||||
if (!cursor.isAtEnd()) {
|
||||
return cursor.advance();
|
||||
}
|
||||
return token;
|
||||
}
|
||||
|
||||
private PbsAst.Statement parseWhileStatement(final PbsToken whileToken) {
|
||||
final var condition = exprParser.parseExpression();
|
||||
context.enterLoop();
|
||||
final var body = parseBlock();
|
||||
context.exitLoop();
|
||||
return new PbsAst.WhileStatement(condition, body, context.span(whileToken.start(), body.span().getEnd()));
|
||||
}
|
||||
|
||||
private PbsAst.Statement parseBreakStatement(final PbsToken breakToken) {
|
||||
final var semicolon = consume(PbsTokenKind.SEMICOLON, "Expected ';' after 'break'");
|
||||
if (!context.isInsideLoop()) {
|
||||
context.report(breakToken, ParseErrors.E_PARSE_LOOP_CONTROL_OUTSIDE_LOOP,
|
||||
"'break' is only valid inside loop contexts");
|
||||
}
|
||||
return new PbsAst.BreakStatement(context.span(breakToken.start(), semicolon.end()));
|
||||
}
|
||||
|
||||
private PbsAst.Statement parseContinueStatement(final PbsToken continueToken) {
|
||||
final var semicolon = consume(PbsTokenKind.SEMICOLON, "Expected ';' after 'continue'");
|
||||
if (!context.isInsideLoop()) {
|
||||
context.report(continueToken, ParseErrors.E_PARSE_LOOP_CONTROL_OUTSIDE_LOOP,
|
||||
"'continue' is only valid inside loop contexts");
|
||||
}
|
||||
return new PbsAst.ContinueStatement(context.span(continueToken.start(), semicolon.end()));
|
||||
}
|
||||
|
||||
private PbsAst.Statement parseReturnStatement(final PbsToken returnToken) {
|
||||
PbsAst.Expression value = null;
|
||||
if (!cursor.check(PbsTokenKind.SEMICOLON)) {
|
||||
value = exprParser.parseExpression();
|
||||
}
|
||||
final var semicolon = consume(PbsTokenKind.SEMICOLON, "Expected ';' after return");
|
||||
return new PbsAst.ReturnStatement(value, context.span(returnToken.start(), semicolon.end()));
|
||||
}
|
||||
|
||||
private PbsAst.Statement parseExpressionStatement() {
|
||||
final var expression = exprParser.parseExpression();
|
||||
final var semicolon = consume(PbsTokenKind.SEMICOLON, "Expected ';' after expression");
|
||||
return new PbsAst.ExpressionStatement(expression, context.span(expression.span().getStart(), semicolon.end()));
|
||||
}
|
||||
|
||||
private boolean isAssignmentStatementStart() {
|
||||
if (!cursor.check(PbsTokenKind.IDENTIFIER) && !cursor.check(PbsTokenKind.THIS)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int offset = 1;
|
||||
while (cursor.peek(offset).kind() == PbsTokenKind.DOT) {
|
||||
if (cursor.peek(offset + 1).kind() != PbsTokenKind.IDENTIFIER) {
|
||||
return false;
|
||||
}
|
||||
if (cursor.peek(offset + 2).kind() == PbsTokenKind.LEFT_PAREN) {
|
||||
return false;
|
||||
}
|
||||
offset += 2;
|
||||
}
|
||||
|
||||
return switch (cursor.peek(offset).kind()) {
|
||||
case EQUAL, PLUS_EQUAL, MINUS_EQUAL, STAR_EQUAL, SLASH_EQUAL, PERCENT_EQUAL -> true;
|
||||
default -> false;
|
||||
};
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -18,8 +18,6 @@ import java.util.ArrayList;
|
||||
*/
|
||||
public final class PbsParser {
|
||||
private static final boolean REQUIRE_SEMICOLON = true;
|
||||
private static final boolean ALLOW_TAIL_EXPRESSION = true;
|
||||
|
||||
public enum ParseMode {
|
||||
ORDINARY,
|
||||
INTERFACE_MODULE
|
||||
@ -32,6 +30,7 @@ public final class PbsParser {
|
||||
private final PbsTypeParser typeParser;
|
||||
private final PbsDeclarationParser declarationParser;
|
||||
private final PbsTopLevelParser topLevelParser;
|
||||
private final PbsBlockParser blockParser;
|
||||
|
||||
private PbsParser(
|
||||
final ReadOnlyList<PbsToken> tokens,
|
||||
@ -43,12 +42,13 @@ public final class PbsParser {
|
||||
this.exprParser = new PbsExprParser(context, this::parseExpressionSurfaceBlock);
|
||||
this.attributeParser = new PbsAttributeParser(context, this::isTopLevelRestartToken);
|
||||
this.typeParser = new PbsTypeParser(context);
|
||||
this.blockParser = new PbsBlockParser(context, exprParser, typeParser);
|
||||
this.declarationParser = new PbsDeclarationParser(
|
||||
context,
|
||||
exprParser,
|
||||
attributeParser,
|
||||
typeParser,
|
||||
this::parseBlock,
|
||||
blockParser::parseBlock,
|
||||
this::consumeDeclarationTerminator,
|
||||
this::synchronizeTopLevel);
|
||||
this.topLevelParser = new PbsTopLevelParser(context, attributeParser, declarationParser);
|
||||
@ -232,306 +232,91 @@ public final class PbsParser {
|
||||
* Parses a brace-delimited block.
|
||||
*/
|
||||
private PbsAst.Block parseBlock() {
|
||||
return parseBlock("Expected '{' to start block");
|
||||
return blockParser.parseBlock();
|
||||
}
|
||||
|
||||
private PbsAst.Block parseExpressionSurfaceBlock(final String message) {
|
||||
return parseBlock(message);
|
||||
return blockParser.parseBlock(message);
|
||||
}
|
||||
|
||||
private PbsAst.Block parseBlock(final String message) {
|
||||
final var leftBrace = consume(PbsTokenKind.LEFT_BRACE, message);
|
||||
final var statements = new ArrayList<PbsAst.Statement>();
|
||||
PbsAst.Expression tailExpression = null;
|
||||
while (!cursor.check(PbsTokenKind.RIGHT_BRACE) && !cursor.isAtEnd()) {
|
||||
if (startsStructuredStatement() || isAssignmentStatementStart()) {
|
||||
statements.add(parseStatement());
|
||||
continue;
|
||||
}
|
||||
|
||||
final var expression = exprParser.parseExpression();
|
||||
if (cursor.match(PbsTokenKind.SEMICOLON)) {
|
||||
statements.add(new PbsAst.ExpressionStatement(
|
||||
expression,
|
||||
span(expression.span().getStart(), cursor.previous().end())));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ALLOW_TAIL_EXPRESSION && cursor.check(PbsTokenKind.RIGHT_BRACE)) {
|
||||
tailExpression = expression;
|
||||
break;
|
||||
}
|
||||
|
||||
report(cursor.peek(), ParseErrors.E_PARSE_EXPECTED_TOKEN, "Expected ';' after expression");
|
||||
if (!cursor.isAtEnd() && !cursor.check(PbsTokenKind.RIGHT_BRACE)) {
|
||||
cursor.advance();
|
||||
}
|
||||
}
|
||||
final var rightBrace = consume(PbsTokenKind.RIGHT_BRACE, "Expected '}' to end block");
|
||||
return new PbsAst.Block(
|
||||
ReadOnlyList.wrap(statements),
|
||||
tailExpression,
|
||||
span(leftBrace.start(), rightBrace.end()));
|
||||
return blockParser.parseBlock(message);
|
||||
}
|
||||
|
||||
private boolean startsStructuredStatement() {
|
||||
return cursor.check(PbsTokenKind.LET)
|
||||
|| cursor.check(PbsTokenKind.IF)
|
||||
|| cursor.check(PbsTokenKind.FOR)
|
||||
|| cursor.check(PbsTokenKind.WHILE)
|
||||
|| cursor.check(PbsTokenKind.BREAK)
|
||||
|| cursor.check(PbsTokenKind.CONTINUE)
|
||||
|| cursor.check(PbsTokenKind.RETURN);
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses one statement inside a block.
|
||||
*/
|
||||
private PbsAst.Statement parseStatement() {
|
||||
if (cursor.match(PbsTokenKind.LET)) {
|
||||
return parseLetStatement(cursor.previous());
|
||||
}
|
||||
if (cursor.match(PbsTokenKind.IF)) {
|
||||
return parseIfStatement(cursor.previous());
|
||||
}
|
||||
if (cursor.match(PbsTokenKind.FOR)) {
|
||||
return parseForStatement(cursor.previous());
|
||||
}
|
||||
if (cursor.match(PbsTokenKind.WHILE)) {
|
||||
return parseWhileStatement(cursor.previous());
|
||||
}
|
||||
if (cursor.match(PbsTokenKind.BREAK)) {
|
||||
return parseBreakStatement(cursor.previous());
|
||||
}
|
||||
if (cursor.match(PbsTokenKind.CONTINUE)) {
|
||||
return parseContinueStatement(cursor.previous());
|
||||
}
|
||||
if (cursor.match(PbsTokenKind.RETURN)) {
|
||||
return parseReturnStatement(cursor.previous());
|
||||
}
|
||||
if (isAssignmentStatementStart()) {
|
||||
return parseAssignStatement();
|
||||
}
|
||||
return parseExpressionStatement();
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a local binding statement.
|
||||
*/
|
||||
private PbsAst.Statement parseLetStatement(final PbsToken letToken) {
|
||||
final boolean isConst = cursor.match(PbsTokenKind.CONST);
|
||||
final var name = consume(PbsTokenKind.IDENTIFIER, "Expected variable name");
|
||||
|
||||
PbsAst.TypeRef explicitType = null;
|
||||
if (cursor.match(PbsTokenKind.COLON)) {
|
||||
explicitType = parseTypeRef();
|
||||
}
|
||||
|
||||
consume(PbsTokenKind.EQUAL, "Expected '=' in let statement");
|
||||
final var initializer = exprParser.parseExpression();
|
||||
final var semicolon = consume(PbsTokenKind.SEMICOLON, "Expected ';' after let statement");
|
||||
|
||||
return new PbsAst.LetStatement(
|
||||
isConst,
|
||||
name.lexeme(),
|
||||
explicitType,
|
||||
initializer,
|
||||
span(letToken.start(), semicolon.end()));
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
private PbsAst.Statement parseAssignStatement() {
|
||||
final var lValue = parseLValue();
|
||||
final var operatorToken = cursor.peek();
|
||||
final var operator = parseAssignOperator(consumeAssignOperator());
|
||||
final var value = exprParser.parseExpression();
|
||||
final var semicolon = consume(PbsTokenKind.SEMICOLON, "Expected ';' after assignment");
|
||||
return new PbsAst.AssignStatement(
|
||||
lValue,
|
||||
operator,
|
||||
value,
|
||||
span(lValue.span().getStart(), semicolon.end()));
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
private PbsAst.LValue parseLValue() {
|
||||
final PbsToken root;
|
||||
if (cursor.match(PbsTokenKind.IDENTIFIER, PbsTokenKind.THIS)) {
|
||||
root = cursor.previous();
|
||||
} else {
|
||||
root = consume(PbsTokenKind.IDENTIFIER, "Expected assignment target identifier");
|
||||
}
|
||||
final var segments = new ArrayList<String>();
|
||||
var end = root.end();
|
||||
while (cursor.match(PbsTokenKind.DOT)) {
|
||||
final var segment = consume(PbsTokenKind.IDENTIFIER, "Expected member name after '.' in assignment target");
|
||||
segments.add(segment.lexeme());
|
||||
end = segment.end();
|
||||
}
|
||||
return new PbsAst.LValue(root.lexeme(), ReadOnlyList.wrap(segments), span(root.start(), end));
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
private PbsToken consumeAssignOperator() {
|
||||
if (cursor.match(PbsTokenKind.EQUAL,
|
||||
PbsTokenKind.PLUS_EQUAL,
|
||||
PbsTokenKind.MINUS_EQUAL,
|
||||
PbsTokenKind.STAR_EQUAL,
|
||||
PbsTokenKind.SLASH_EQUAL,
|
||||
PbsTokenKind.PERCENT_EQUAL)) {
|
||||
return cursor.previous();
|
||||
}
|
||||
final var token = cursor.peek();
|
||||
report(token, ParseErrors.E_PARSE_INVALID_ASSIGN_TARGET,
|
||||
"Expected assignment operator after assignment target");
|
||||
if (!cursor.isAtEnd()) {
|
||||
return cursor.advance();
|
||||
}
|
||||
return token;
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
private PbsAst.AssignOperator parseAssignOperator(final PbsToken token) {
|
||||
return switch (token.kind()) {
|
||||
case EQUAL -> PbsAst.AssignOperator.ASSIGN;
|
||||
case PLUS_EQUAL -> PbsAst.AssignOperator.ADD_ASSIGN;
|
||||
case MINUS_EQUAL -> PbsAst.AssignOperator.SUB_ASSIGN;
|
||||
case STAR_EQUAL -> PbsAst.AssignOperator.MUL_ASSIGN;
|
||||
case SLASH_EQUAL -> PbsAst.AssignOperator.DIV_ASSIGN;
|
||||
case PERCENT_EQUAL -> PbsAst.AssignOperator.MOD_ASSIGN;
|
||||
default -> PbsAst.AssignOperator.ASSIGN;
|
||||
};
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
private PbsAst.IfStatement parseIfStatement(final PbsToken ifToken) {
|
||||
final var condition = exprParser.parseExpression();
|
||||
final var thenBlock = parseBlock();
|
||||
|
||||
PbsAst.IfStatement elseIf = null;
|
||||
PbsAst.Block elseBlock = null;
|
||||
if (cursor.match(PbsTokenKind.ELSE)) {
|
||||
if (cursor.match(PbsTokenKind.IF)) {
|
||||
elseIf = parseIfStatement(cursor.previous());
|
||||
} else {
|
||||
elseBlock = parseBlock();
|
||||
}
|
||||
}
|
||||
|
||||
final var end = elseIf != null
|
||||
? elseIf.span().getEnd()
|
||||
: (elseBlock != null ? elseBlock.span().getEnd() : thenBlock.span().getEnd());
|
||||
|
||||
return new PbsAst.IfStatement(
|
||||
condition,
|
||||
thenBlock,
|
||||
elseIf,
|
||||
elseBlock,
|
||||
span(ifToken.start(), end));
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
private PbsAst.Statement parseForStatement(final PbsToken forToken) {
|
||||
final var iterator = consume(PbsTokenKind.IDENTIFIER, "Expected loop iterator name in 'for'");
|
||||
consumeForToken(PbsTokenKind.COLON, "Expected ':' after iterator name in 'for'");
|
||||
final var iteratorType = parseTypeRef();
|
||||
consumeForToken(PbsTokenKind.FROM, "Expected 'from' in 'for' statement");
|
||||
final var fromExpression = exprParser.parseExpression();
|
||||
consumeForToken(PbsTokenKind.UNTIL, "Expected 'until' in 'for' statement");
|
||||
final var untilExpression = exprParser.parseExpression();
|
||||
|
||||
PbsAst.Expression stepExpression = null;
|
||||
if (cursor.match(PbsTokenKind.STEP)) {
|
||||
stepExpression = exprParser.parseExpression();
|
||||
}
|
||||
|
||||
context.enterLoop();
|
||||
final var body = parseBlock();
|
||||
context.exitLoop();
|
||||
|
||||
return new PbsAst.ForStatement(
|
||||
iterator.lexeme(),
|
||||
iteratorType,
|
||||
fromExpression,
|
||||
untilExpression,
|
||||
stepExpression,
|
||||
body,
|
||||
span(forToken.start(), body.span().getEnd()));
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
private PbsToken consumeForToken(final PbsTokenKind kind, final String message) {
|
||||
if (cursor.check(kind)) {
|
||||
return cursor.advance();
|
||||
}
|
||||
final var token = cursor.peek();
|
||||
report(token, ParseErrors.E_PARSE_INVALID_FOR_FORM, message + ", found " + token.kind());
|
||||
if (!cursor.isAtEnd()) {
|
||||
return cursor.advance();
|
||||
}
|
||||
return token;
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
private PbsAst.Statement parseWhileStatement(final PbsToken whileToken) {
|
||||
final var condition = exprParser.parseExpression();
|
||||
context.enterLoop();
|
||||
final var body = parseBlock();
|
||||
context.exitLoop();
|
||||
return new PbsAst.WhileStatement(condition, body, span(whileToken.start(), body.span().getEnd()));
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
private PbsAst.Statement parseBreakStatement(final PbsToken breakToken) {
|
||||
final var semicolon = consume(PbsTokenKind.SEMICOLON, "Expected ';' after 'break'");
|
||||
if (!context.isInsideLoop()) {
|
||||
report(breakToken, ParseErrors.E_PARSE_LOOP_CONTROL_OUTSIDE_LOOP,
|
||||
"'break' is only valid inside loop contexts");
|
||||
}
|
||||
return new PbsAst.BreakStatement(span(breakToken.start(), semicolon.end()));
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
private PbsAst.Statement parseContinueStatement(final PbsToken continueToken) {
|
||||
final var semicolon = consume(PbsTokenKind.SEMICOLON, "Expected ';' after 'continue'");
|
||||
if (!context.isInsideLoop()) {
|
||||
report(continueToken, ParseErrors.E_PARSE_LOOP_CONTROL_OUTSIDE_LOOP,
|
||||
"'continue' is only valid inside loop contexts");
|
||||
}
|
||||
return new PbsAst.ContinueStatement(span(continueToken.start(), semicolon.end()));
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a return statement with an optional returned value.
|
||||
*/
|
||||
private PbsAst.Statement parseReturnStatement(final PbsToken returnToken) {
|
||||
PbsAst.Expression value = null;
|
||||
if (!cursor.check(PbsTokenKind.SEMICOLON)) {
|
||||
value = exprParser.parseExpression();
|
||||
}
|
||||
final var semicolon = consume(PbsTokenKind.SEMICOLON, "Expected ';' after return");
|
||||
return new PbsAst.ReturnStatement(value, span(returnToken.start(), semicolon.end()));
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an expression statement terminated by a semicolon.
|
||||
*/
|
||||
private PbsAst.Statement parseExpressionStatement() {
|
||||
final var expression = exprParser.parseExpression();
|
||||
final var semicolon = consume(PbsTokenKind.SEMICOLON, "Expected ';' after expression");
|
||||
return new PbsAst.ExpressionStatement(expression, span(expression.span().getStart(), semicolon.end()));
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
private boolean isAssignmentStatementStart() {
|
||||
if (!cursor.check(PbsTokenKind.IDENTIFIER) && !cursor.check(PbsTokenKind.THIS)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int offset = 1;
|
||||
while (cursor.peek(offset).kind() == PbsTokenKind.DOT) {
|
||||
if (cursor.peek(offset + 1).kind() != PbsTokenKind.IDENTIFIER) {
|
||||
return false;
|
||||
}
|
||||
if (cursor.peek(offset + 2).kind() == PbsTokenKind.LEFT_PAREN) {
|
||||
return false;
|
||||
}
|
||||
offset += 2;
|
||||
}
|
||||
|
||||
return switch (cursor.peek(offset).kind()) {
|
||||
case EQUAL, PLUS_EQUAL, MINUS_EQUAL, STAR_EQUAL, SLASH_EQUAL, PERCENT_EQUAL -> true;
|
||||
default -> false;
|
||||
};
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
private long consumeDeclarationTerminator() {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user