From 27fd5552cc4bdaccb50b0c92f6fafd2a45757cb9 Mon Sep 17 00:00:00 2001 From: Brian Fiete Date: Sun, 24 Oct 2021 08:12:18 -0700 Subject: [PATCH] Enhanced ranges --- BeefLibs/corlib/src/Array.bf | 11 +++ BeefLibs/corlib/src/Collections/List.bf | 11 +++ BeefLibs/corlib/src/Range.bf | 125 ++++++++++++++++++++++++ BeefLibs/corlib/src/Span.bf | 47 +++++++++ IDEHelper/Compiler/BfAst.cpp | 5 +- IDEHelper/Compiler/BfAst.h | 1 + IDEHelper/Compiler/BfCompiler.cpp | 4 + IDEHelper/Compiler/BfCompiler.h | 2 + IDEHelper/Compiler/BfExprEvaluator.cpp | 106 ++++++++++++++++++-- IDEHelper/Compiler/BfReducer.cpp | 43 +++++--- IDEHelper/Compiler/BfReducer.h | 3 +- IDEHelper/Tests/src/Loops.bf | 29 ++++++ 12 files changed, 365 insertions(+), 22 deletions(-) diff --git a/BeefLibs/corlib/src/Array.bf b/BeefLibs/corlib/src/Array.bf index 122c35d9..89c44b7e 100644 --- a/BeefLibs/corlib/src/Array.bf +++ b/BeefLibs/corlib/src/Array.bf @@ -260,6 +260,17 @@ namespace System } } + public Span this[IndexRange range] + { +#if !DEBUG + [Inline] +#endif + get + { + return Span(&mFirstElement, mLength)[range]; + } + } + [Inline] public T* CArray() { diff --git a/BeefLibs/corlib/src/Collections/List.bf b/BeefLibs/corlib/src/Collections/List.bf index e2445326..9bbb7e7f 100644 --- a/BeefLibs/corlib/src/Collections/List.bf +++ b/BeefLibs/corlib/src/Collections/List.bf @@ -211,6 +211,17 @@ namespace System.Collections } } + public Span this[IndexRange range] + { +#if !DEBUG + [Inline] +#endif + get + { + return Span(mItems, mSize)[range]; + } + } + public ref T Front { get diff --git a/BeefLibs/corlib/src/Range.bf b/BeefLibs/corlib/src/Range.bf index 4fe7bdda..1023a33a 100644 --- a/BeefLibs/corlib/src/Range.bf +++ b/BeefLibs/corlib/src/Range.bf @@ -8,6 +8,24 @@ namespace System } + enum Index + { + case FromFront(int offset); + case FromEnd(int offset); + + public override void ToString(String strBuffer) + { + switch (this) + { + case .FromFront(let offset): + offset.ToString(strBuffer); + case .FromEnd(let offset): + strBuffer.Append('^'); + offset.ToString(strBuffer); + } + } + } + struct Range : RangeExpression, IEnumerable { protected int mStart; @@ -110,6 +128,11 @@ namespace System mEnd = 0; } + public static operator IndexRange(Range list) + { + return .(.FromFront(list.mStart), .FromFront(list.mEnd), false); + } + [Inline] public Enumerator GetEnumerator() { @@ -192,6 +215,103 @@ namespace System } } + struct IndexRange : RangeExpression + { + public Index mStart; + public Index mEnd; + public bool mIsClosed; + + public this() + { + this = default; + } + + [Inline] + public this(Index start, Index end, bool isClosed=true) + { + mStart = start; + mEnd = end; + mIsClosed = isClosed; + } + + [Inline] + public this(int start, Index end, bool isClosed=true) + { + mStart = .FromFront(start); + mEnd = end; + mIsClosed = isClosed; + } + + [Inline] + public this(Index start, int end, bool isClosed=true) + { + mStart = start; + mEnd = .FromFront(end); + mIsClosed = isClosed; + } + + [Inline] + public this(int start, int end, bool isClosed=true) + { + mStart = .FromFront(start); + mEnd = .FromFront(end); + mIsClosed = isClosed; + } + + public Index Start + { + [Inline] + get + { + return mStart; + } + + [Inline] + set mut + { + mStart = value; + } + } + + public Index End + { + [Inline] + get + { + return mEnd; + } + + set mut + { + mEnd = value; + } + } + + public bool IsClosed + { + [Inline] + get + { + return mIsClosed; + } + + set mut + { + mIsClosed = value; + } + } + + public override void ToString(String strBuffer) + { + mStart.ToString(strBuffer); + if (mIsClosed) + strBuffer.Append("..."); + else + strBuffer.Append("..<"); + mEnd.ToString(strBuffer); + } + } + struct ClosedRange : RangeExpression, IEnumerable { protected int mStart; @@ -294,6 +414,11 @@ namespace System mEnd = 0; } + public static operator IndexRange(ClosedRange list) + { + return .(.FromFront(list.mStart), .FromFront(list.mEnd), true); + } + [Inline] public Enumerator GetEnumerator() { diff --git a/BeefLibs/corlib/src/Span.bf b/BeefLibs/corlib/src/Span.bf index c632f6d2..f4c971df 100644 --- a/BeefLibs/corlib/src/Span.bf +++ b/BeefLibs/corlib/src/Span.bf @@ -134,6 +134,53 @@ namespace System } } + public Span this[IndexRange range] + { +#if !DEBUG + [Inline] +#endif + get + { + T* start; + switch (range.mStart) + { + case .FromFront(let offset): + Debug.Assert((uint)offset <= (uint)mLength); + start = mPtr + offset; + case .FromEnd(let offset): + Debug.Assert((uint)offset <= (uint)mLength); + start = mPtr + mLength - 1 - offset; + } + T* end; + if (range.mIsClosed) + { + switch (range.mEnd) + { + case .FromFront(let offset): + Debug.Assert((uint)offset < (uint)mLength); + end = mPtr + offset + 1; + case .FromEnd(let offset): + Debug.Assert((uint)offset < (uint)mLength); + end = mPtr + mLength - offset; + } + } + else + { + switch (range.mEnd) + { + case .FromFront(let offset): + Debug.Assert((uint)offset <= (uint)mLength); + end = mPtr + offset; + case .FromEnd(let offset): + Debug.Assert((uint)offset <= (uint)mLength); + end = mPtr + mLength - 1 - offset; + } + } + + return .(start, end - start); + } + } + public Span Slice(int index) { Debug.Assert((uint)index <= (uint)mLength); diff --git a/IDEHelper/Compiler/BfAst.cpp b/IDEHelper/Compiler/BfAst.cpp index 0edcd2ec..c55e9d5d 100644 --- a/IDEHelper/Compiler/BfAst.cpp +++ b/IDEHelper/Compiler/BfAst.cpp @@ -1753,9 +1753,10 @@ const char* Beefy::BfGetOpName(BfUnaryOp unaryOp) case BfUnaryOp_Mut: return "mut"; case BfUnaryOp_Params: return "params"; case BfUnaryOp_Cascade: return ".."; + case BfUnaryOp_FromEnd: return "^"; case BfUnaryOp_PartialRangeUpTo: return "..<"; case BfUnaryOp_PartialRangeThrough: return "..."; - case BfUnaryOp_PartialRangeFrom: return "..."; + case BfUnaryOp_PartialRangeFrom: return "..."; default: return "???"; } } @@ -1855,6 +1856,8 @@ BfUnaryOp Beefy::BfTokenToUnaryOp(BfToken token) return BfUnaryOp_Params; case BfToken_DotDot: return BfUnaryOp_Cascade; + case BfToken_Carat: + return BfUnaryOp_FromEnd; case BfToken_DotDotDot: return BfUnaryOp_PartialRangeThrough; case BfToken_DotDotLess: diff --git a/IDEHelper/Compiler/BfAst.h b/IDEHelper/Compiler/BfAst.h index 5758ee14..df9e8ebb 100644 --- a/IDEHelper/Compiler/BfAst.h +++ b/IDEHelper/Compiler/BfAst.h @@ -1866,6 +1866,7 @@ enum BfUnaryOp BfUnaryOp_Mut, BfUnaryOp_Params, BfUnaryOp_Cascade, + BfUnaryOp_FromEnd, BfUnaryOp_PartialRangeUpTo, BfUnaryOp_PartialRangeThrough, BfUnaryOp_PartialRangeFrom, diff --git a/IDEHelper/Compiler/BfCompiler.cpp b/IDEHelper/Compiler/BfCompiler.cpp index 2163f422..215da562 100644 --- a/IDEHelper/Compiler/BfCompiler.cpp +++ b/IDEHelper/Compiler/BfCompiler.cpp @@ -384,6 +384,8 @@ BfCompiler::BfCompiler(BfSystem* bfSystem, bool isResolveOnly) mSpanTypeDef = NULL; mRangeTypeDef = NULL; mClosedRangeTypeDef = NULL; + mIndexTypeDef = NULL; + mIndexRangeTypeDef = NULL; mAttributeTypeDef = NULL; mAttributeUsageAttributeTypeDef = NULL; mClassVDataTypeDef = NULL; @@ -6723,6 +6725,8 @@ bool BfCompiler::DoCompile(const StringImpl& outputDirectory) mSpanTypeDef = _GetRequiredType("System.Span", 1); mRangeTypeDef = _GetRequiredType("System.Range"); mClosedRangeTypeDef = _GetRequiredType("System.ClosedRange"); + mIndexTypeDef = _GetRequiredType("System.Index"); + mIndexRangeTypeDef = _GetRequiredType("System.IndexRange"); mAttributeTypeDef = _GetRequiredType("System.Attribute"); mAttributeUsageAttributeTypeDef = _GetRequiredType("System.AttributeUsageAttribute"); mClassVDataTypeDef = _GetRequiredType("System.ClassVData"); diff --git a/IDEHelper/Compiler/BfCompiler.h b/IDEHelper/Compiler/BfCompiler.h index 3f8d16f6..d859da0f 100644 --- a/IDEHelper/Compiler/BfCompiler.h +++ b/IDEHelper/Compiler/BfCompiler.h @@ -349,6 +349,8 @@ public: BfTypeDef* mSpanTypeDef; BfTypeDef* mRangeTypeDef; BfTypeDef* mClosedRangeTypeDef; + BfTypeDef* mIndexTypeDef; + BfTypeDef* mIndexRangeTypeDef; BfTypeDef* mClassVDataTypeDef; diff --git a/IDEHelper/Compiler/BfExprEvaluator.cpp b/IDEHelper/Compiler/BfExprEvaluator.cpp index 5b2c4aea..05fe60c7 100644 --- a/IDEHelper/Compiler/BfExprEvaluator.cpp +++ b/IDEHelper/Compiler/BfExprEvaluator.cpp @@ -15220,11 +15220,6 @@ void BfExprEvaluator::InjectMixin(BfAstNode* targetSrc, BfTypedValue target, boo } auto autoComplete = GetAutoComplete(); - if ((autoComplete != NULL) && (autoComplete->mIsCapturingMethodMatchInfo) && (autoComplete->mMethodMatchInfo == NULL)) - { - NOP; - } - if ((autoComplete != NULL) && (autoComplete->mIsCapturingMethodMatchInfo) && (autoComplete->mMethodMatchInfo != NULL) && (autoComplete->mMethodMatchInfo->mInstanceList.size() != 0)) autoComplete->mIsCapturingMethodMatchInfo = false; @@ -19797,6 +19792,13 @@ void BfExprEvaluator::Visit(BfUnaryOperatorExpression* unaryOpExpr) void BfExprEvaluator::PerformUnaryOperation(BfExpression* unaryOpExpr, BfUnaryOp unaryOp, BfTokenNode* opToken, BfUnaryOpFlags opFlags) { + if ((unaryOpExpr == NULL) && (unaryOp == BfUnaryOp_PartialRangeThrough)) + { + PerformBinaryOperation(NULL, NULL, BfBinaryOp_ClosedRange, opToken, BfBinOpFlag_None); + return; + } + + /// { // If this is a cast, we don't want the value to be coerced before the unary operator is applied. // WAIT: Why not? @@ -20456,8 +20458,32 @@ void BfExprEvaluator::PerformUnaryOperation_OnResult(BfExpression* unaryOpExpr, mModule->Fail("Illegal use of argument cascade expression", opToken); } break; + case BfUnaryOp_FromEnd: + { + CheckResultForReading(mResult); + auto value = mModule->Cast(unaryOpExpr, mResult, mModule->GetPrimitiveType(BfTypeCode_IntPtr)); + value = mModule->LoadValue(value); + if (value) + { + auto indexType = mModule->ResolveTypeDef(mModule->mCompiler->mIndexTypeDef); + auto alloca = mModule->CreateAlloca(indexType); + mModule->mBfIRBuilder->CreateStore(value.mValue, mModule->mBfIRBuilder->CreateInBoundsGEP(mModule->mBfIRBuilder->CreateInBoundsGEP(alloca, 0, 1), 0, 1)); + mModule->mBfIRBuilder->CreateStore(mModule->mBfIRBuilder->CreateConst(BfTypeCode_Int8, 1), mModule->mBfIRBuilder->CreateInBoundsGEP(alloca, 0, 2)); + mResult = BfTypedValue(alloca, indexType, BfTypedValueKind_Addr); + } + } + break; + case BfUnaryOp_PartialRangeUpTo: + PerformBinaryOperation(NULL, unaryOpExpr, BfBinaryOp_Range, opToken, BfBinOpFlag_None); + break; + case BfUnaryOp_PartialRangeThrough: + PerformBinaryOperation(NULL, unaryOpExpr, BfBinaryOp_ClosedRange, opToken, BfBinOpFlag_None); + break; + case BfUnaryOp_PartialRangeFrom: + PerformBinaryOperation(unaryOpExpr, NULL, BfBinaryOp_ClosedRange, opToken, BfBinOpFlag_None); + break; default: - mModule->Fail("INTERNAL ERROR: Unhandled unary operator", unaryOpExpr); + mModule->Fail(StrFormat("Illegal use of '%s' unary operator", BfGetOpName(unaryOp)), unaryOpExpr); break; } @@ -20792,12 +20818,74 @@ void BfExprEvaluator::PerformBinaryOperation(BfExpression* leftExpression, BfExp { auto intType = mModule->GetPrimitiveType(BfTypeCode_IntPtr); - auto allocType = mModule->ResolveTypeDef((binaryOp == BfBinaryOp_Range) ? mModule->mCompiler->mRangeTypeDef : mModule->mCompiler->mClosedRangeTypeDef)->ToTypeInstance(); + bool isIndexExpr = false; + BfTypeDef* typeDef = NULL; + if (auto unaryOpExpr = BfNodeDynCast(leftExpression)) + if (unaryOpExpr->mOp == BfUnaryOp_FromEnd) + isIndexExpr = true; + if (rightExpression == NULL) + isIndexExpr = true; + if (auto unaryOpExpr = BfNodeDynCast(rightExpression)) + if (unaryOpExpr->mOp == BfUnaryOp_FromEnd) + isIndexExpr = true; + + if (isIndexExpr) + typeDef = mModule->mCompiler->mIndexRangeTypeDef; + else + typeDef = (binaryOp == BfBinaryOp_Range) ? mModule->mCompiler->mRangeTypeDef : mModule->mCompiler->mClosedRangeTypeDef; + + auto allocType = mModule->ResolveTypeDef(typeDef)->ToTypeInstance(); auto alloca = mModule->CreateAlloca(allocType); + BfTypedValueExpression leftTypedValueExpr; + BfTypedValueExpression rightTypedValueExpr; + BfTypedValueExpression isClosedTypedValueExpr; + SizedArray argExprs; - argExprs.Add(leftExpression); - argExprs.Add(rightExpression); + if (leftExpression != NULL) + { + argExprs.Add(leftExpression); + } + else + { + leftTypedValueExpr.mRefNode = opToken; + leftTypedValueExpr.mTypedValue = BfTypedValue(mModule->mBfIRBuilder->CreateConst(BfTypeCode_IntPtr, 0), mModule->GetPrimitiveType(BfTypeCode_IntPtr)); + argExprs.Add(&leftTypedValueExpr); + } + + if (rightExpression != NULL) + { + argExprs.Add(rightExpression); + } + else + { + // Add as a `^0` + auto indexType = mModule->ResolveTypeDef(mModule->mCompiler->mIndexTypeDef)->ToTypeInstance(); + rightTypedValueExpr.mRefNode = opToken; + + auto valueTypeEmpty = mModule->mBfIRBuilder->CreateConstAgg(mModule->mBfIRBuilder->MapType(indexType->mBaseType), {}); + + SizedArray tupleMembers; + tupleMembers.Add(valueTypeEmpty); + tupleMembers.Add(mModule->mBfIRBuilder->CreateConst(BfTypeCode_IntPtr, 0)); + auto tupleValue = mModule->mBfIRBuilder->CreateConstAgg(mModule->mBfIRBuilder->MapType(indexType->mFieldInstances[0].mResolvedType), tupleMembers); + + SizedArray indexMembers; + indexMembers.Add(valueTypeEmpty); + indexMembers.Add(tupleValue); + indexMembers.Add(mModule->mBfIRBuilder->CreateConst(BfTypeCode_Int8, 1)); + auto indexValue = mModule->mBfIRBuilder->CreateConstAgg(mModule->mBfIRBuilder->MapType(indexType), indexMembers); + + rightTypedValueExpr.mTypedValue = BfTypedValue(indexValue, indexType); + argExprs.Add(&rightTypedValueExpr); + } + + if (isIndexExpr) + { + isClosedTypedValueExpr.mRefNode = opToken; + isClosedTypedValueExpr.mTypedValue = BfTypedValue(mModule->mBfIRBuilder->CreateConst(BfTypeCode_Boolean, (binaryOp == BfBinaryOp_ClosedRange) ? 1 : 0), mModule->GetPrimitiveType(BfTypeCode_Boolean)); + argExprs.Add(&isClosedTypedValueExpr); + } BfSizedArray args = argExprs; diff --git a/IDEHelper/Compiler/BfReducer.cpp b/IDEHelper/Compiler/BfReducer.cpp index eee7a224..0f33ce0c 100644 --- a/IDEHelper/Compiler/BfReducer.cpp +++ b/IDEHelper/Compiler/BfReducer.cpp @@ -2277,14 +2277,15 @@ BfExpression* BfReducer::CreateExpression(BfAstNode* node, CreateExprFlags creat CreateExprFlags innerFlags = (CreateExprFlags)(rhsCreateExprFlags | CreateExprFlags_EarlyExit); if (unaryOp == BfUnaryOp_Cascade) - { innerFlags = (CreateExprFlags)(innerFlags | (createExprFlags & CreateExprFlags_AllowVariableDecl)); - } + + if (unaryOp == BfUnaryOp_PartialRangeThrough) // This allows for just a naked '...' + innerFlags = (CreateExprFlags)(innerFlags | CreateExprFlags_AllowEmpty); // Don't attempt binary or unary operations- they will always be lower precedence unaryOpExpr->mExpression = CreateExpressionAfter(unaryOpExpr, innerFlags); if (unaryOpExpr->mExpression == NULL) - return NULL; + return unaryOpExpr; MoveNode(unaryOpExpr->mExpression, unaryOpExpr); } @@ -2356,7 +2357,8 @@ BfExpression* BfReducer::CreateExpression(BfAstNode* node, CreateExprFlags creat if (exprLeft == NULL) { - Fail("Expected expression", node); + if ((createExprFlags & CreateExprFlags_AllowEmpty) == 0) + Fail("Expected expression", node); return NULL; } @@ -2377,7 +2379,7 @@ BfExpression* BfReducer::CreateExpression(BfAstNode* node, CreateExprFlags creat if (token == BfToken_DblPlus) postUnaryOp = BfUnaryOp_PostIncrement; if (token == BfToken_DblMinus) - postUnaryOp = BfUnaryOp_PostDecrement; + postUnaryOp = BfUnaryOp_PostDecrement; if (token == BfToken_DotDotDot) { @@ -2700,17 +2702,36 @@ BfExpression* BfReducer::CreateExpression(BfAstNode* node, CreateExprFlags creat { if ((createExprFlags & CreateExprFlags_EarlyExit) != 0) return exprLeft; - auto binOpExpression = mAlloc->Alloc(); - ReplaceNode(exprLeft, binOpExpression); - binOpExpression->mLeft = exprLeft; - binOpExpression->mOp = binOp; - MEMBER_SET(binOpExpression, mOpToken, tokenNode); + mVisitorPos.MoveNext(); // We only need to check binary operator precedence at the "top level" binary operator rhsCreateExprFlags = (CreateExprFlags)(rhsCreateExprFlags | CreateExprFlags_NoCheckBinOpPrecedence); - auto exprRight = CreateExpressionAfter(binOpExpression, rhsCreateExprFlags); + if (tokenNode->mToken == BfToken_DotDotDot) + rhsCreateExprFlags = (CreateExprFlags)(rhsCreateExprFlags | CreateExprFlags_AllowEmpty); + + BfExpression* exprRight = CreateExpressionAfter(tokenNode, rhsCreateExprFlags); + + if (exprRight == NULL) + { + if (tokenNode->mToken == BfToken_DotDotDot) + { + auto unaryOpExpression = mAlloc->Alloc(); + ReplaceNode(exprLeft, unaryOpExpression); + unaryOpExpression->mExpression = exprLeft; + unaryOpExpression->mOp = BfUnaryOp_PartialRangeFrom; + MEMBER_SET(unaryOpExpression, mOpToken, tokenNode); + return unaryOpExpression; + } + } + + auto binOpExpression = mAlloc->Alloc(); + ReplaceNode(exprLeft, binOpExpression); + binOpExpression->mLeft = exprLeft; + binOpExpression->mOp = binOp; + MEMBER_SET(binOpExpression, mOpToken, tokenNode); + if (exprRight == NULL) return binOpExpression; MEMBER_SET(binOpExpression, mRight, exprRight); diff --git a/IDEHelper/Compiler/BfReducer.h b/IDEHelper/Compiler/BfReducer.h index d233fedb..6a3c2cbc 100644 --- a/IDEHelper/Compiler/BfReducer.h +++ b/IDEHelper/Compiler/BfReducer.h @@ -26,7 +26,8 @@ public: CreateExprFlags_ExitOnParenExpr = 0x80, CreateExprFlags_NoCheckBinOpPrecedence = 0x100, CreateExprFlags_BreakOnCascade = 0x200, - CreateExprFlags_EarlyExit = 0x400 // Don't attempt binary or ternary operations + CreateExprFlags_EarlyExit = 0x400, // Don't attempt binary or ternary operations + CreateExprFlags_AllowEmpty = 0x800 }; enum CreateStmtFlags diff --git a/IDEHelper/Tests/src/Loops.bf b/IDEHelper/Tests/src/Loops.bf index c9a80c83..0462f519 100644 --- a/IDEHelper/Tests/src/Loops.bf +++ b/IDEHelper/Tests/src/Loops.bf @@ -133,6 +133,35 @@ namespace Tests Test.Assert((1..<3).Contains(1..<3)); Test.Assert(!(1..<3).Contains(1..<4)); + List iList = scope .() { 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 }; + total = 0; + for (int i in iList[...]) + total += i; + Test.Assert(total == 10+20+30+40+50+60+70+80+90+100); + + total = 0; + for (int i in iList[1...]) + total += i; + Test.Assert(total == 20+30+40+50+60+70+80+90+100); + + total = 0; + for (int i in iList[...^1]) + total += i; + Test.Assert(total == 10+20+30+40+50+60+70+80+90); + + total = 0; + for (int i in iList[..<^1]) + total += i; + Test.Assert(total == 10+20+30+40+50+60+70+80); + + total = 0; + for (int i in iList[...^1][1...^1]) + total += i; + Test.Assert(total == 20+30+40+50+60+70+80); + + var str = scope String(); + (2...^3).ToString(str); + Test.Assert(str == "2...^3"); } public static void TestEnumerator1(EnumeratorTest e)