Signed-off-by: erick-alcachofa <erick@artichoke.dev>
Implement support for object literals using a unified syntax for both
struct and slice initialization. Since the parser lacks the semantic
context to distinguish between a struct or a slice at this stage, both
are represented by the new `ObjectLiteral` AST node.
initialization within curly braces following a type expression:
* **Named Initializers**: Uses the `.field = value` syntax (e.g.,
`Point { .x = 10, .y = 20 }`).
* **Positional Initializers**: Uses a comma-separated list of
expressions (e.g., `[]i32 { 1, 2, 3 }`).
* Renamed `StructLiteral` and `SliceLiteral` nodes to `ObjectLiteral`.
* Refactored initialization helper nodes (e.g.,
`StructLiteralNamedFieldInit` is now `ObjectLiteralNamedFieldInit`).
* Unified the representation in `Expressions.hpp` and `Literals.hpp` to
use a single `ObjectLiteral` struct containing a `type` and an
optional `initializer`.
* Integrated the opening brace `{` (`opLSquirly`) as a high-precedence
postfix operator (binding power 19).
* Implemented parsing logic in `Expressions.cpp` to handle the
transition from a type expression to an object initializer.
* Updated `toDot` and `toString` visitors to handle the unified
`ObjectLiteral` nodes and their respective initializer variants.
* Improved robustness in `Declarations.cpp` by ensuring list parsing
correctly handles closing braces in specific edge cases.
755 lines
22 KiB
C++
755 lines
22 KiB
C++
//============================================================================//
|
|
// //
|
|
// artichoke programming language //
|
|
// //
|
|
// Copyright (C) 2025 Erick Saul Guzman Ramos, whoami.artichoke.dev //
|
|
// //
|
|
// //
|
|
// This program is free software: you can redistribute it and/or modify //
|
|
// it under the terms of the GNU Affero General Public License as published //
|
|
// by the Free Software Foundation, either version 3 of the License, or //
|
|
// (at your option) any later version. //
|
|
// //
|
|
// This program is distributed in the hope that it will be useful, //
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
|
// GNU Affero General Public License for more details. //
|
|
// //
|
|
// You should have received a copy of the GNU Affero General Public License //
|
|
// along with this program. If not, see <https://www.gnu.org/licenses/>. //
|
|
// //
|
|
//============================================================================//
|
|
|
|
#include <artichoke/Parser/Parser.hpp>
|
|
#include <artichoke/Parser/Pratt.hpp>
|
|
|
|
namespace arti::lang {
|
|
|
|
Expected<ast::ExpressionNode>
|
|
Parser::parseExpression(std::uint16_t minBindingPower) {
|
|
auto peekToken = tokenizer.peek();
|
|
if (! peekToken) {
|
|
return Unexpected<>{ std::move(peekToken).error() };
|
|
}
|
|
|
|
bool keepParsing = true;
|
|
ast::Optional<ast::ExpressionNode> lhs = std::nullopt;
|
|
|
|
if (peekToken->value == TokenV::opLParen) {
|
|
std::ignore = tokenizer.consume();
|
|
|
|
if (auto lhsExpr = parseExpression(); ! lhsExpr) {
|
|
return Unexpected<>{ std::move(lhsExpr).error() };
|
|
}
|
|
else {
|
|
if (auto rParen = consume(TokenV::opRParen, "')'"); ! rParen) {
|
|
return Unexpected<>{ std::move(rParen).error() };
|
|
}
|
|
|
|
lhs = std::move(lhsExpr).value();
|
|
}
|
|
}
|
|
else if (pratt::isPrefixOperator(peekToken->value)) {
|
|
if (auto newLhs = parsePrefixExpression(); ! newLhs) {
|
|
return Unexpected<>{ std::move(newLhs).error() };
|
|
}
|
|
else {
|
|
lhs = std::move(newLhs).value();
|
|
}
|
|
}
|
|
else {
|
|
if (auto expr = parsePrimaryExpression(); ! expr) {
|
|
return Unexpected<>{ std::move(expr).error() };
|
|
}
|
|
else if (not expr.value().has_value()) {
|
|
return langException<ExceptCode::ecUnexpectedToken>(
|
|
peekToken->line,
|
|
peekToken->column,
|
|
toString(*peekToken),
|
|
"primary expression, i.e. "
|
|
"any of ( null, boolean, number, character, string, identifier )"
|
|
);
|
|
}
|
|
else {
|
|
lhs = std::move(expr).value().value();
|
|
}
|
|
}
|
|
|
|
while (keepParsing) {
|
|
peekToken = tokenizer.peek();
|
|
if (! peekToken) {
|
|
return Unexpected<>{ std::move(peekToken).error() };
|
|
}
|
|
|
|
if (pratt::isPostfixOperator(peekToken->value)) {
|
|
auto op = pratt::getPostfixOperator(peekToken->value);
|
|
auto bindingPower = pratt::postfixBindingPower(op);
|
|
|
|
if (bindingPower < minBindingPower) {
|
|
keepParsing = false;
|
|
}
|
|
else {
|
|
if (auto newLhs = parsePostfixExpression(std::move(lhs).value());
|
|
! newLhs) {
|
|
return Unexpected<>{ std::move(newLhs).error() };
|
|
}
|
|
else {
|
|
lhs = std::move(newLhs).value();
|
|
}
|
|
}
|
|
}
|
|
else if (pratt::isInfixOperator(peekToken->value)) {
|
|
auto op = pratt::getInfixOperator(peekToken->value);
|
|
auto [lbp, rbp] = pratt::infixBindingPower(op);
|
|
|
|
if (lbp < minBindingPower) {
|
|
keepParsing = false;
|
|
}
|
|
else {
|
|
if (auto newLhs = parseInfixExpression(std::move(lhs).value());
|
|
! newLhs) {
|
|
return Unexpected<>{ std::move(newLhs).error() };
|
|
}
|
|
else {
|
|
lhs = std::move(newLhs).value();
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
keepParsing = false;
|
|
}
|
|
}
|
|
|
|
if (not lhs.has_value()) {
|
|
return langException<ExceptCode::ecUnexpectedToken>(
|
|
peekToken->line,
|
|
peekToken->column,
|
|
toString(*peekToken),
|
|
"primary expression, i.e. "
|
|
"any of ( null, boolean, number, character, string, identifier )"
|
|
);
|
|
}
|
|
else {
|
|
return std::move(lhs).value();
|
|
}
|
|
}
|
|
|
|
Expected<ast::Optional<ast::ExpressionNode>>
|
|
Parser::parsePrimaryExpression() {
|
|
auto peekToken = tokenizer.peek();
|
|
|
|
if (! peekToken) {
|
|
return Unexpected<>{ std::move(peekToken).error() };
|
|
}
|
|
|
|
if (peekToken->value == TokenV::tkInteger) {
|
|
if (auto expr = parseIntegerLiteral(); ! expr) {
|
|
return Unexpected<>{ std::move(expr).error() };
|
|
}
|
|
else {
|
|
return std::move(expr).value();
|
|
}
|
|
}
|
|
else if (peekToken->value == TokenV::tkDecimal) {
|
|
if (auto expr = parseFloatLiteral(); ! expr) {
|
|
return Unexpected<>{ std::move(expr).error() };
|
|
}
|
|
else {
|
|
return std::move(expr).value();
|
|
}
|
|
}
|
|
else if (peekToken->value == TokenV::tkCharacter) {
|
|
if (auto expr = parseCharLiteral(); ! expr) {
|
|
return Unexpected<>{ std::move(expr).error() };
|
|
}
|
|
else {
|
|
return std::move(expr).value();
|
|
}
|
|
}
|
|
else if (peekToken->value == TokenV::tkString) {
|
|
if (auto expr = parseStringLiteral(); ! expr) {
|
|
return Unexpected<>{ std::move(expr).error() };
|
|
}
|
|
else {
|
|
return std::move(expr).value();
|
|
}
|
|
}
|
|
else if (peekToken->value == TokenV::kwTrue ||
|
|
peekToken->value == TokenV::kwFalse) {
|
|
if (auto expr = parseBooleanLiteral(); ! expr) {
|
|
return Unexpected<>{ std::move(expr).error() };
|
|
}
|
|
else {
|
|
return std::move(expr).value();
|
|
}
|
|
}
|
|
else if (peekToken->value == TokenV::kwNull) {
|
|
if (auto expr = parseNullLiteral(); ! expr) {
|
|
return Unexpected<>{ std::move(expr).error() };
|
|
}
|
|
else {
|
|
return std::move(expr).value();
|
|
}
|
|
}
|
|
else if (peekToken->value == TokenV::tkIdentifier) {
|
|
if (auto expr = parseIdentifierExpression(); ! expr) {
|
|
return Unexpected<>{ std::move(expr).error() };
|
|
}
|
|
else {
|
|
return std::move(expr).value();
|
|
}
|
|
}
|
|
else if (peekToken->value == TokenV::kwThis) {
|
|
auto node = ast::MakeNode<ast::IdentifierExprNode>();
|
|
|
|
if (auto ltrl = consume(TokenV::kwThis, "'this' keyword"); ! ltrl) {
|
|
return Unexpected<>{ std::move(ltrl).error() };
|
|
}
|
|
else {
|
|
node->location = {
|
|
.line = ltrl->line,
|
|
.column = ltrl->column
|
|
};
|
|
node->identifierName = ltrl->strValue;
|
|
}
|
|
|
|
return node;
|
|
}
|
|
else if (peekToken->value == TokenV::kwUnderscore) {
|
|
auto node = ast::MakeNode<ast::IdentifierExprNode>();
|
|
|
|
if (auto ltrl = consume(TokenV::kwUnderscore, "'_' keyword"); ! ltrl) {
|
|
return Unexpected<>{ std::move(ltrl).error() };
|
|
}
|
|
else {
|
|
node->location = {
|
|
.line = ltrl->line,
|
|
.column = ltrl->column
|
|
};
|
|
node->identifierName = ltrl->strValue;
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
Expected<ast::ExpressionNode>
|
|
Parser::parsePrefixExpression() {
|
|
auto peekToken = tokenizer.peek();
|
|
if (! peekToken) {
|
|
return Unexpected<>{ std::move(peekToken).error() };
|
|
}
|
|
|
|
auto op = pratt::getPrefixOperator(peekToken->value);
|
|
auto bindingPower = pratt::prefixBindingPower(op);
|
|
|
|
std::ignore = tokenizer.consume();
|
|
|
|
auto rhs = parseExpression(bindingPower);
|
|
|
|
auto node = ast::MakeNode<ast::PrefixExprNode>();
|
|
|
|
node->location = {
|
|
.line = peekToken->line,
|
|
.column = peekToken->column
|
|
};
|
|
|
|
node->op = op;
|
|
node->right = std::move(rhs).value();
|
|
|
|
return node;
|
|
}
|
|
|
|
Expected<ast::ExpressionNode>
|
|
Parser::parseInfixExpression(ast::ExpressionNode lhs) {
|
|
auto peekToken = tokenizer.peek();
|
|
if (! peekToken) {
|
|
return Unexpected<>{ std::move(peekToken).error() };
|
|
}
|
|
|
|
std::ignore = tokenizer.consume();
|
|
|
|
auto op = pratt::getInfixOperator(peekToken->value);
|
|
auto [lbp, rbp] = pratt::infixBindingPower(op);
|
|
|
|
if (op == ast::InfixOperator::ModuleAccess) {
|
|
if (auto isGeneric = match(TokenV::opLt); ! isGeneric) {
|
|
return Unexpected<>{ std::move(isGeneric).error() };
|
|
}
|
|
else if (isGeneric.value()) {
|
|
auto node = ast::MakeNode<ast::GenericExprNode>();
|
|
|
|
node->location = {
|
|
.line = peekToken->line,
|
|
.column = peekToken->column
|
|
};
|
|
|
|
if (auto args = parseGenericArgumentsList(); ! args) {
|
|
return Unexpected<>{ std::move(args).error() };
|
|
}
|
|
else {
|
|
node->typeNode = std::move(lhs);
|
|
node->genericArgs = std::move(args).value();
|
|
}
|
|
|
|
return node;
|
|
}
|
|
}
|
|
|
|
auto rhs = parseExpression(rbp);
|
|
|
|
if (! rhs) {
|
|
return Unexpected<>{ std::move(rhs).error() };
|
|
}
|
|
|
|
/* TODO: MemberAccess and PointerMemberAccess do not use their respective
|
|
* nodes types yet */
|
|
if (op == ast::InfixOperator::Assignment) {
|
|
auto node = ast::MakeNode<ast::AssignExprNode>();
|
|
|
|
node->location = {
|
|
.line = peekToken->line,
|
|
.column = peekToken->column
|
|
};
|
|
|
|
node->left = std::move(lhs);
|
|
node->right = std::move(rhs).value();
|
|
|
|
return node;
|
|
}
|
|
else if (op == ast::InfixOperator::ModuleAccess) {
|
|
auto node = ast::MakeNode<ast::ModuleAccessExprNode>();
|
|
|
|
node->location = {
|
|
.line = peekToken->line,
|
|
.column = peekToken->column
|
|
};
|
|
|
|
node->left = std::move(lhs);
|
|
node->right = std::move(rhs).value();
|
|
|
|
return node;
|
|
}
|
|
else if (op == ast::InfixOperator::MemberAccess) {
|
|
auto node = ast::MakeNode<ast::MemberAccessExprNode>();
|
|
|
|
node->location = {
|
|
.line = peekToken->line,
|
|
.column = peekToken->column
|
|
};
|
|
|
|
node->object = std::move(lhs);
|
|
node->member = std::move(rhs).value();
|
|
|
|
return node;
|
|
}
|
|
else if (op == ast::InfixOperator::PointerMemberAccess) {
|
|
auto node = ast::MakeNode<ast::PointerMemberAccessExprNode>();
|
|
|
|
node->location = {
|
|
.line = peekToken->line,
|
|
.column = peekToken->column
|
|
};
|
|
|
|
node->object = std::move(lhs);
|
|
node->member = std::move(rhs).value();
|
|
|
|
return node;
|
|
}
|
|
else if (pratt::isCompoundAssignOperator(op)) {
|
|
auto node = ast::MakeNode<ast::CompoundAssignExprNode>();
|
|
|
|
node->location = {
|
|
.line = peekToken->line,
|
|
.column = peekToken->column
|
|
};
|
|
|
|
node->op = pratt::getCompoundOperatorType(op);
|
|
node->left = std::move(lhs);
|
|
node->right = std::move(rhs).value();
|
|
|
|
return node;
|
|
}
|
|
else {
|
|
auto node = ast::MakeNode<ast::InfixExprNode>();
|
|
|
|
node->location = {
|
|
.line = peekToken->line,
|
|
.column = peekToken->column
|
|
};
|
|
|
|
node->op = op;
|
|
node->left = std::move(lhs);
|
|
node->right = std::move(rhs).value();
|
|
|
|
return node;
|
|
}
|
|
}
|
|
|
|
Expected<ast::ExpressionNode>
|
|
Parser::parsePostfixExpression(ast::ExpressionNode lhs) {
|
|
auto peekToken = tokenizer.peek();
|
|
if (! peekToken) {
|
|
return Unexpected<>{ std::move(peekToken).error() };
|
|
}
|
|
|
|
std::ignore = tokenizer.consume();
|
|
|
|
auto op = pratt::getPostfixOperator(peekToken->value);
|
|
auto bindingPower = pratt::postfixBindingPower(op);
|
|
|
|
std::optional<ast::ExpressionNode> node = std::nullopt;
|
|
|
|
if (op == ast::PostfixOperator::FunctionCall) {
|
|
auto newNode = ast::MakeNode<ast::FunctionCallExprNode>();
|
|
|
|
newNode->location = {
|
|
.line = peekToken->line,
|
|
.column = peekToken->column
|
|
};
|
|
|
|
bool stillParams = true;
|
|
|
|
if (auto close = match(TokenV::opRParen); ! close) {
|
|
return Unexpected<>{ std::move(close).error() };
|
|
}
|
|
else if (close.value()) {
|
|
stillParams = false;
|
|
}
|
|
|
|
while (stillParams) {
|
|
auto arg = parseExpression();
|
|
|
|
if (! arg) {
|
|
return Unexpected<>{ std::move(arg).error() };
|
|
}
|
|
|
|
newNode->arguments.emplace_back(std::move(arg).value());
|
|
|
|
if (auto comma = matchAndConsume(TokenV::opComma); ! comma) {
|
|
return Unexpected{ std::move(comma).error() };
|
|
}
|
|
else if (! comma.value()) {
|
|
if (auto ntok = tokenizer.peek(); ! ntok) {
|
|
return Unexpected{ std::move(ntok).error() };
|
|
}
|
|
else {
|
|
if (ntok->value != TokenV::opRParen) {
|
|
return langException<ExceptCode::ecUnexpectedToken>(
|
|
ntok->line,
|
|
ntok->column,
|
|
toString(*ntok),
|
|
"',' or ')'"
|
|
);
|
|
}
|
|
else {
|
|
stillParams = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (auto close = consume(TokenV::opRParen, "')'"); ! close) {
|
|
return Unexpected<>{ std::move(close).error() };
|
|
}
|
|
|
|
newNode->callee = std::move(lhs);
|
|
|
|
node = std::move(newNode);
|
|
}
|
|
else if (op == ast::PostfixOperator::ObjectLiteral) {
|
|
auto newNode = ast::MakeNode<ast::ObjectLtrlNode>();
|
|
|
|
newNode->location = {
|
|
.line = peekToken->line,
|
|
.column = peekToken->column
|
|
};
|
|
|
|
bool stillParams = true;
|
|
|
|
if (auto close = match(TokenV::opRSquirly); ! close) {
|
|
return Unexpected<>{ std::move(close).error() };
|
|
}
|
|
else if (close.value()) {
|
|
stillParams = false;
|
|
}
|
|
|
|
if (auto isNamed = match(TokenV::opDot); ! isNamed) {
|
|
return Unexpected<>{ std::move(isNamed).error() };
|
|
}
|
|
else if (isNamed.value()) {
|
|
auto initializerNode =
|
|
ast::MakeNode<ast::ObjectLtrlNamedInitializerNode>();
|
|
|
|
initializerNode->location = newNode->location;
|
|
|
|
while (stillParams) {
|
|
auto currLocation = ast::SourceLocation{};
|
|
|
|
if (auto dot = consume(TokenV::opDot, "'.'"); ! dot) {
|
|
return Unexpected<>{ std::move(dot).error() };
|
|
}
|
|
else {
|
|
currLocation.line = dot->line;
|
|
currLocation.column = dot->column;
|
|
}
|
|
|
|
if (auto ident = consume(TokenV::tkIdentifier, "identifier");
|
|
! ident) {
|
|
return Unexpected<>{ std::move(ident).error() };
|
|
}
|
|
else {
|
|
if (auto eq = consume(TokenV::opAssign, "'='"); ! eq) {
|
|
return Unexpected<>{ std::move(eq).error() };
|
|
}
|
|
|
|
auto value = parseExpression();
|
|
|
|
if (! value) {
|
|
return Unexpected<>{ std::move(value).error() };
|
|
}
|
|
|
|
auto init = ast::MakeNode<ast::ObjectLtrlNamedFieldInitNode>();
|
|
|
|
init->location = currLocation;
|
|
init->fieldName = ident->strValue;
|
|
init->fieldValue = std::move(value).value();
|
|
|
|
initializerNode->fields.push_back(std::move(init));
|
|
|
|
if (auto comma = matchAndConsume(TokenV::opComma); ! comma) {
|
|
return Unexpected{ std::move(comma).error() };
|
|
}
|
|
else if (! comma.value()) {
|
|
if (auto ntok = tokenizer.peek(); ! ntok) {
|
|
return Unexpected{ std::move(ntok).error() };
|
|
}
|
|
else {
|
|
if (ntok->value != TokenV::opRSquirly) {
|
|
return langException<ExceptCode::ecUnexpectedToken>(
|
|
ntok->line,
|
|
ntok->column,
|
|
toString(*ntok),
|
|
"',' or '}'"
|
|
);
|
|
}
|
|
else {
|
|
stillParams = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (auto close = match(TokenV::opRSquirly); ! close) {
|
|
return Unexpected<>{ std::move(close).error() };
|
|
}
|
|
else if (close.value()) {
|
|
stillParams = false;
|
|
}
|
|
}
|
|
|
|
newNode->initializer = std::move(initializerNode);
|
|
}
|
|
else {
|
|
auto initializerNode =
|
|
ast::MakeNode<ast::ObjectLtrlPositionalInitializerNode>();
|
|
|
|
initializerNode->location = newNode->location;
|
|
|
|
while (stillParams) {
|
|
auto arg = parseExpression();
|
|
|
|
if (! arg) {
|
|
return Unexpected<>{ std::move(arg).error() };
|
|
}
|
|
|
|
initializerNode->fields.push_back(std::move(arg).value());
|
|
|
|
if (auto comma = matchAndConsume(TokenV::opComma); ! comma) {
|
|
return Unexpected{ std::move(comma).error() };
|
|
}
|
|
else if (! comma.value()) {
|
|
if (auto ntok = tokenizer.peek(); ! ntok) {
|
|
return Unexpected{ std::move(ntok).error() };
|
|
}
|
|
else {
|
|
if (ntok->value != TokenV::opRSquirly) {
|
|
return langException<ExceptCode::ecUnexpectedToken>(
|
|
ntok->line,
|
|
ntok->column,
|
|
toString(*ntok),
|
|
"',' or '}'"
|
|
);
|
|
}
|
|
else {
|
|
stillParams = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (auto close = match(TokenV::opRSquirly); ! close) {
|
|
return Unexpected<>{ std::move(close).error() };
|
|
}
|
|
else if (close.value()) {
|
|
stillParams = false;
|
|
}
|
|
}
|
|
|
|
newNode->initializer = std::move(initializerNode);
|
|
}
|
|
|
|
if (auto close = consume(TokenV::opRSquirly, "'}'"); ! close) {
|
|
return Unexpected<>{ std::move(close).error() };
|
|
}
|
|
|
|
newNode->type = std::move(lhs);
|
|
|
|
node = std::move(newNode);
|
|
}
|
|
else if (op == ast::PostfixOperator::SliceAccess) {
|
|
auto idx = parseExpression();
|
|
|
|
if (! idx) {
|
|
return Unexpected<>{ std::move(idx).error() };
|
|
}
|
|
|
|
if (auto range = matchAndConsume(TokenV::opColon); ! range) {
|
|
return Unexpected<>{ std::move(range).error() };
|
|
}
|
|
else if (range.value()) {
|
|
auto newNode = ast::MakeNode<ast::SliceRangeExprNode>();
|
|
|
|
newNode->location = {
|
|
.line = peekToken->line,
|
|
.column = peekToken->column
|
|
};
|
|
|
|
newNode->start = std::move(idx).value();
|
|
|
|
auto endIdx = parseExpression();
|
|
|
|
if (! endIdx) {
|
|
return Unexpected<>{ std::move(endIdx).error() };
|
|
}
|
|
|
|
newNode->end = std::move(endIdx).value();
|
|
|
|
if (auto close = consume(TokenV::opRBracket, "']'"); ! close) {
|
|
return Unexpected<>{ std::move(close).error() };
|
|
}
|
|
|
|
newNode->slice = std::move(lhs);
|
|
|
|
node = std::move(newNode);
|
|
}
|
|
else {
|
|
auto newNode = ast::MakeNode<ast::SliceAccessExprNode>();
|
|
|
|
newNode->location = {
|
|
.line = peekToken->line,
|
|
.column = peekToken->column
|
|
};
|
|
|
|
newNode->index = std::move(idx).value();
|
|
|
|
if (auto close = consume(TokenV::opRBracket, "']'"); ! close) {
|
|
return Unexpected<>{ std::move(close).error() };
|
|
}
|
|
|
|
newNode->slice = std::move(lhs);
|
|
|
|
node = std::move(newNode);
|
|
}
|
|
}
|
|
else if (op == ast::PostfixOperator::SliceSize) {
|
|
auto newNode = ast::MakeNode<ast::SliceLengthExprNode>();
|
|
|
|
newNode->location = {
|
|
.line = peekToken->line,
|
|
.column = peekToken->column
|
|
};
|
|
|
|
newNode->object = std::move(lhs);
|
|
|
|
node = std::move(newNode);
|
|
}
|
|
else if (op == ast::PostfixOperator::PtrToSlice) {
|
|
auto newNode = ast::MakeNode<ast::SliceCreationExprNode>();
|
|
|
|
newNode->location = {
|
|
.line = peekToken->line,
|
|
.column = peekToken->column
|
|
};
|
|
|
|
auto len = parseExpression();
|
|
|
|
if (! len) {
|
|
return Unexpected<>{ std::move(len).error() };
|
|
}
|
|
|
|
newNode->length = std::move(len).value();
|
|
|
|
if (auto close = consume(TokenV::opRBracket, "']'"); ! close) {
|
|
return Unexpected<>{ std::move(close).error() };
|
|
}
|
|
|
|
newNode->object = std::move(lhs);
|
|
|
|
node = std::move(newNode);
|
|
}
|
|
else if (op == ast::PostfixOperator::SliceToPtr) {
|
|
auto newNode = ast::MakeNode<ast::SlicePtrExprNode>();
|
|
|
|
newNode->location = {
|
|
.line = peekToken->line,
|
|
.column = peekToken->column
|
|
};
|
|
|
|
newNode->object = std::move(lhs);
|
|
|
|
node = std::move(newNode);
|
|
}
|
|
else if (op == ast::PostfixOperator::Reflect) {
|
|
auto newNode = ast::MakeNode<ast::ReflectionExprNode>();
|
|
|
|
newNode->location = {
|
|
.line = peekToken->line,
|
|
.column = peekToken->column
|
|
};
|
|
|
|
if (auto ident = match(TokenV::tkIdentifier); ! ident) {
|
|
return Unexpected<>{ std::move(ident).error() };
|
|
}
|
|
else if (ident.value()) {
|
|
if (auto ident = consume(TokenV::tkIdentifier, "identifier");
|
|
! ident) {
|
|
return Unexpected<>{ std::move(ident).error() };
|
|
}
|
|
else {
|
|
newNode->attribute = ident->strValue;
|
|
}
|
|
}
|
|
|
|
newNode->object = std::move(lhs);
|
|
|
|
node = std::move(newNode);
|
|
}
|
|
|
|
if (not node.has_value()) {
|
|
return langException<ExceptCode::ecUnexpectedToken>(
|
|
peekToken->line,
|
|
peekToken->column,
|
|
toString(*peekToken),
|
|
"postfix operator, i.e. "
|
|
"any of ( /*TODO*/ )"
|
|
);
|
|
}
|
|
|
|
return std::move(node).value();
|
|
}
|
|
|
|
|
|
} // namespace arti::lang
|