Better error handling of expressions

This commit is contained in:
Rafał Grodziński
2025-07-03 22:15:40 +09:00
parent 8fdba80d00
commit d809f317d8
2 changed files with 39 additions and 40 deletions

View File

@@ -72,25 +72,26 @@ shared_ptr<Statement> Parser::nextStatement() {
shared_ptr<Statement> Parser::nextInBlockStatement() { shared_ptr<Statement> Parser::nextInBlockStatement() {
shared_ptr<Statement> statement; shared_ptr<Statement> statement;
int errorsCount = errors.size();
statement = matchStatementVariable(); statement = matchStatementVariable();
if (statement != nullptr) if (statement != nullptr || errors.size() > errorsCount)
return statement; return statement;
statement = matchStatementAssignment(); statement = matchStatementAssignment();
if (statement != nullptr) if (statement != nullptr || errors.size() > errorsCount)
return statement; return statement;
statement = matchStatementReturn(); statement = matchStatementReturn();
if (statement != nullptr) if (statement != nullptr || errors.size() > errorsCount)
return statement; return statement;
statement = matchStatementRepeat(); statement = matchStatementRepeat();
if (statement != nullptr) if (statement != nullptr || errors.size() > errorsCount)
return statement; return statement;
statement = matchStatementExpression(); statement = matchStatementExpression();
if (statement != nullptr) if (statement != nullptr || errors.size() > errorsCount)
return statement; return statement;
markError({}, {}); markError({}, {});
@@ -374,19 +375,21 @@ shared_ptr<Statement> Parser::matchStatementExpression() {
// //
shared_ptr<Expression> Parser::nextExpression() { shared_ptr<Expression> Parser::nextExpression() {
shared_ptr<Expression> expression; shared_ptr<Expression> expression;
int errorsCount = errors.size();
expression = matchEquality(); expression = matchEquality();
if (expression != nullptr) if (expression != nullptr || errors.size() > errorsCount)
return expression; return expression;
expression = matchExpressionIfElse(); expression = matchExpressionIfElse();
if (expression != nullptr) if (expression != nullptr || errors.size() > errorsCount)
return expression; return expression;
expression = matchExpressionVariable(); expression = matchExpressionVariable();
if (expression != nullptr) if (expression != nullptr || errors.size() > errorsCount)
return expression; return expression;
markError({}, {});
return nullptr; return nullptr;
} }
@@ -461,12 +464,12 @@ shared_ptr<Expression> Parser::matchExpressionGrouping() {
if (tryMatchingTokenKinds({TokenKind::LEFT_PAREN}, true, true)) { if (tryMatchingTokenKinds({TokenKind::LEFT_PAREN}, true, true)) {
shared_ptr<Expression> expression = matchTerm(); shared_ptr<Expression> expression = matchTerm();
// has grouped expression failed? // has grouped expression failed?
if (expression == nullptr || !expression->isValid()) { if (expression == nullptr) {
return expression ?: matchExpressionInvalid("Expected expression"); return nullptr;
} else if (tryMatchingTokenKinds({TokenKind::RIGHT_PAREN}, true, true)) { } else if (tryMatchingTokenKinds({TokenKind::RIGHT_PAREN}, true, true)) {
return make_shared<ExpressionGrouping>(expression); return make_shared<ExpressionGrouping>(expression);
} else { } else {
return matchExpressionInvalid("Unexpected token"); markError(TokenKind::RIGHT_PAREN, {});
} }
} }
@@ -510,8 +513,10 @@ shared_ptr<Expression> Parser::matchExpressionCall() {
} while (tryMatchingTokenKinds({TokenKind::COMMA}, true, true)); } while (tryMatchingTokenKinds({TokenKind::COMMA}, true, true));
tryMatchingTokenKinds({TokenKind::NEW_LINE}, true, true); // optional new line tryMatchingTokenKinds({TokenKind::NEW_LINE}, true, true); // optional new line
if (!tryMatchingTokenKinds({TokenKind::RIGHT_PAREN}, true, true)) if (!tryMatchingTokenKinds({TokenKind::RIGHT_PAREN}, true, true)) {
return matchExpressionInvalid("Expected \")\""); markError(TokenKind::RIGHT_PAREN, {});
return nullptr;
}
return make_shared<ExpressionCall>(identifierToken->getLexme(), argumentExpressions); return make_shared<ExpressionCall>(identifierToken->getLexme(), argumentExpressions);
} }
@@ -526,30 +531,29 @@ shared_ptr<Expression> Parser::matchExpressionIfElse() {
// condition expression // condition expression
condition = nextExpression(); condition = nextExpression();
if (condition == nullptr || !condition->isValid()) if (condition == nullptr)
return condition ?: matchExpressionInvalid("Expected condition expression"); return nullptr;
if (!tryMatchingTokenKinds({TokenKind::COLON}, true, true)) if (!tryMatchingTokenKinds({TokenKind::COLON}, true, true)) {
return matchExpressionInvalid("Expected \":\""); markError(TokenKind::COLON, {});
return nullptr;
}
// then // then
bool isMultiLine = false; bool isMultiLine = tryMatchingTokenKinds({TokenKind::NEW_LINE}, true, true);
if (tryMatchingTokenKinds({TokenKind::NEW_LINE}, true, true))
isMultiLine = true;
// then block // then block
if (isMultiLine) if (isMultiLine)
thenBlock = matchExpressionBlock({TokenKind::ELSE, TokenKind::SEMICOLON}); thenBlock = matchExpressionBlock({TokenKind::ELSE, TokenKind::SEMICOLON});
else else
thenBlock = matchExpressionBlock({TokenKind::ELSE, TokenKind::NEW_LINE}); thenBlock = matchExpressionBlock({TokenKind::ELSE, TokenKind::NEW_LINE});
if (thenBlock == nullptr || !thenBlock->isValid())
return thenBlock ?: matchExpressionInvalid("Expected then block"); if (thenBlock == nullptr)
return nullptr;
// else // else
if (tryMatchingTokenKinds({TokenKind::ELSE}, true, true)) { if (tryMatchingTokenKinds({TokenKind::ELSE}, true, true)) {
if (tryMatchingTokenKinds({TokenKind::NEW_LINE}, true, true)) isMultiLine = (tryMatchingTokenKinds({TokenKind::NEW_LINE}, true, true));
isMultiLine = true;
// else block // else block
if (isMultiLine) if (isMultiLine)
@@ -557,8 +561,8 @@ shared_ptr<Expression> Parser::matchExpressionIfElse() {
else else
elseBlock = matchExpressionBlock({TokenKind::NEW_LINE}); elseBlock = matchExpressionBlock({TokenKind::NEW_LINE});
if (elseBlock == nullptr || !elseBlock->isValid()) if (elseBlock == nullptr)
return elseBlock ?: matchExpressionInvalid("Expected else block"); return nullptr;
} }
tryMatchingTokenKinds({TokenKind::SEMICOLON}, false, true); tryMatchingTokenKinds({TokenKind::SEMICOLON}, false, true);
@@ -580,7 +584,7 @@ shared_ptr<Expression> Parser::matchExpressionBinary(shared_ptr<Expression> left
} }
if (right == nullptr) { if (right == nullptr) {
return matchExpressionInvalid("Expected right-side expression"); return nullptr;
} else if (!right->isValid()) { } else if (!right->isValid()) {
return right; return right;
} else { } else {
@@ -595,27 +599,23 @@ shared_ptr<Expression> Parser::matchExpressionBlock(vector<TokenKind> terminalTo
while (!tryMatchingTokenKinds(terminalTokenKinds, false, false)) { while (!tryMatchingTokenKinds(terminalTokenKinds, false, false)) {
shared_ptr<Statement> statement = nextInBlockStatement(); shared_ptr<Statement> statement = nextInBlockStatement();
if (statement == nullptr) {
markError({}, "Expected statement"); if (statement != nullptr)
return nullptr;
}
statements.push_back(statement); statements.push_back(statement);
if (tryMatchingTokenKinds(terminalTokenKinds, false, false)) if (tryMatchingTokenKinds(terminalTokenKinds, false, false))
break; break;
// except new line // except new line
if (!tryMatchingTokenKinds({TokenKind::NEW_LINE}, true, true)) if (statement != nullptr && !tryMatchingTokenKinds({TokenKind::NEW_LINE}, true, true)) {
return matchExpressionInvalid("Expected new line"); markError(TokenKind::NEW_LINE, {});
return nullptr;
}
} }
return make_shared<ExpressionBlock>(statements); return make_shared<ExpressionBlock>(statements);
} }
shared_ptr<ExpressionInvalid> Parser::matchExpressionInvalid(string message) {
return make_shared<ExpressionInvalid>(tokens.at(currentIndex));
}
bool Parser::tryMatchingTokenKinds(vector<TokenKind> kinds, bool shouldMatchAll, bool shouldAdvance) { bool Parser::tryMatchingTokenKinds(vector<TokenKind> kinds, bool shouldMatchAll, bool shouldAdvance) {
int requiredCount = shouldMatchAll ? kinds.size() : 1; int requiredCount = shouldMatchAll ? kinds.size() : 1;
if (currentIndex + requiredCount > tokens.size()) if (currentIndex + requiredCount > tokens.size())

View File

@@ -48,7 +48,6 @@ private:
shared_ptr<Expression> matchExpressionIfElse(); shared_ptr<Expression> matchExpressionIfElse();
shared_ptr<Expression> matchExpressionBinary(shared_ptr<Expression> left); shared_ptr<Expression> matchExpressionBinary(shared_ptr<Expression> left);
shared_ptr<Expression> matchExpressionBlock(vector<TokenKind> terminalTokenKinds); shared_ptr<Expression> matchExpressionBlock(vector<TokenKind> terminalTokenKinds);
shared_ptr<ExpressionInvalid> matchExpressionInvalid(string message);
bool tryMatchingTokenKinds(vector<TokenKind> kinds, bool shouldMatchAll, bool shouldAdvance); bool tryMatchingTokenKinds(vector<TokenKind> kinds, bool shouldMatchAll, bool shouldAdvance);
optional<ValueType> valueTypeForToken(shared_ptr<Token> token); optional<ValueType> valueTypeForToken(shared_ptr<Token> token);