From 25486fbacef82eb5b73b72c1dfd715f0898dcadf Mon Sep 17 00:00:00 2001 From: erick-alcachofa Date: Sun, 28 Dec 2025 11:26:27 -0600 Subject: [PATCH] fix(parser): support optional start and end indices in slice ranges Signed-off-by: erick-alcachofa Update the SliceAccess postfix operator logic to handle the full variety of slice range syntaxes. This allows for open-ended slices by making the start and end expressions optional within the brackets. - Add logic to detect a leading colon for `[:end]` and `[:]` forms. - Support trailing colons for `[start:]` forms. - Differentiate between a single index access and a slice range based on the presence of the colon operator. - Update SliceRangeExprNode construction to handle optional boundaries. --- lib/src/Parser/Expressions.cpp | 72 ++++++++++++++++++++++++++-------- 1 file changed, 56 insertions(+), 16 deletions(-) diff --git a/lib/src/Parser/Expressions.cpp b/lib/src/Parser/Expressions.cpp index 83afed0..e696667 100644 --- a/lib/src/Parser/Expressions.cpp +++ b/lib/src/Parser/Expressions.cpp @@ -632,16 +632,52 @@ namespace arti::lang { node = std::move(newNode); } else if (op == ast::PostfixOperator::SliceAccess) { - auto idx = parseExpression(); + bool isSlice = false; + bool skipSliceEnd = false; + bool skipSliceStart = false; - if (! idx) { - return Unexpected<>{ std::move(idx).error() }; + if (auto skipLeft = matchAndConsume(TokenV::opColon); ! skipLeft) { + return Unexpected<>{ std::move(skipLeft).error() }; + } + else if (skipLeft.value()) { + isSlice = true; + skipSliceStart = true; + + if (auto close = matchAndConsume(TokenV::opRBracket); ! close) { + return Unexpected<>{ std::move(close).error() }; + } + else if (close.value()) { + skipSliceEnd = true; + } } - if (auto range = matchAndConsume(TokenV::opColon); ! range) { - return Unexpected<>{ std::move(range).error() }; + auto idxExpr = ast::Optional{}; + + if (! skipSliceStart) { + 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()) { + isSlice = true; + + if (auto close = matchAndConsume(TokenV::opRBracket); ! close) { + return Unexpected<>{ std::move(close).error() }; + } + else if (close.value()) { + skipSliceEnd = true; + } + } + + idxExpr = std::move(idx).value(); } - else if (range.value()) { + + if (isSlice) { auto newNode = ast::MakeNode(); newNode->location = { @@ -649,18 +685,22 @@ namespace arti::lang { .column = peekToken->column }; - newNode->start = std::move(idx).value(); - - auto endIdx = parseExpression(); - - if (! endIdx) { - return Unexpected<>{ std::move(endIdx).error() }; + if (! skipSliceStart) { + newNode->start = std::move(idxExpr).value(); } - newNode->end = std::move(endIdx).value(); + if (! skipSliceEnd) { + auto endIdx = parseExpression(); - if (auto close = consume(TokenV::opRBracket, "']'"); ! close) { - return Unexpected<>{ std::move(close).error() }; + 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); @@ -675,7 +715,7 @@ namespace arti::lang { .column = peekToken->column }; - newNode->index = std::move(idx).value(); + newNode->index = std::move(idxExpr).value(); if (auto close = consume(TokenV::opRBracket, "']'"); ! close) { return Unexpected<>{ std::move(close).error() };