diff --git a/BeefLibs/corlib/src/Range.bf b/BeefLibs/corlib/src/Range.bf new file mode 100644 index 00000000..ce3301e2 --- /dev/null +++ b/BeefLibs/corlib/src/Range.bf @@ -0,0 +1,271 @@ +using System.Collections; +using System.Diagnostics; + +namespace System +{ + interface RangeExpression + { + + } + + struct Range : RangeExpression, IEnumerable + { + protected int mStart; + protected int mEnd; + + public this() + { + mStart = 0; + mEnd = 0; + } + + [Inline] + public this(int start, int end) + { + Debug.Assert(end >= start); + mStart = start; + mEnd = end; + } + + public int Length + { + [Inline] + get + { + return mEnd - mStart; + } + + [Inline] + set mut + { + mEnd = mStart + value; + } + } + + public int Start + { + [Inline] + get + { + return mStart; + } + + [Inline] + set mut + { + mStart = value; + } + } + + public int End + { + [Inline] + get + { + return mEnd; + } + + set mut + { + mEnd = value; + } + } + + public bool IsEmpty + { + [Inline] + get + { + return mEnd == mStart; + } + } + + public bool Contains(int idx) + { + return (idx >= mStart) && (idx < mStart); + } + + public void Clear() mut + { + mStart = 0; + mEnd = 0; + } + + [Inline] + public Enumerator GetEnumerator() + { + return Enumerator(this); + } + + public override void ToString(String strBuffer) + { + strBuffer.AppendF($"{mStart}..<{mEnd}"); + } + + public struct Enumerator : IEnumerator + { + private int mEnd; + private int mIndex; + + [Inline] + public this(Range range) + { + mIndex = range.mStart - 1; + mEnd = range.mEnd; + } + + public void Dispose() + { + } + + public ref int Index + { + get mut + { + return ref mIndex; + } + } + + public int End => mEnd; + + [Inline] + public Result GetNext() mut + { + if (mIndex + 1 >= mEnd) + return .Err; + return ++mIndex; + } + + } + } + + struct ClosedRange : RangeExpression, IEnumerable + { + protected int mStart; + protected int mEnd; + + public this() + { + mStart = 0; + mEnd = 0; + } + + [Inline] + public this(int start, int end) + { + Debug.Assert(end >= start); + mStart = start; + mEnd = end; + } + + public int Length + { + [Inline] + get + { + return mEnd - mStart; + } + + [Inline] + set mut + { + mEnd = mStart + value; + } + } + + public int Start + { + [Inline] + get + { + return mStart; + } + + [Inline] + set mut + { + mStart = value; + } + } + + public int End + { + [Inline] + get + { + return mEnd; + } + + set mut + { + mEnd = value; + } + } + + public bool IsEmpty + { + [Inline] + get + { + return mEnd == mStart; + } + } + + public bool Contains(int idx) + { + return (idx >= mStart) && (idx < mStart); + } + + public void Clear() mut + { + mStart = 0; + mEnd = 0; + } + + [Inline] + public Enumerator GetEnumerator() + { + return Enumerator(this); + } + + public override void ToString(String strBuffer) + { + strBuffer.AppendF($"{mStart}...{mEnd}"); + } + + public struct Enumerator : IEnumerator + { + private int mEnd; + private int mIndex; + + [Inline] + public this(ClosedRange range) + { + mIndex = range.mStart - 1; + mEnd = range.mEnd; + } + + public void Dispose() + { + } + + public ref int Index + { + get mut + { + return ref mIndex; + } + } + + public int End => mEnd; + + [Inline] + public Result GetNext() mut + { + if (mIndex >= mEnd) + return .Err; + return ++mIndex; + } + } + } +} diff --git a/IDE/mintest/minlib/src/System/Range.bf b/IDE/mintest/minlib/src/System/Range.bf new file mode 100644 index 00000000..1337136d --- /dev/null +++ b/IDE/mintest/minlib/src/System/Range.bf @@ -0,0 +1,43 @@ +using System.Collections; +using System.Diagnostics; + +namespace System +{ + struct Range + { + protected int mStart; + protected int mEnd; + + public this() + { + mStart = 0; + mEnd = 0; + } + + public this(int start, int end) + { + Debug.Assert(end >= start); + mStart = start; + mEnd = end; + } + } + + struct ClosedRange + { + protected int mStart; + protected int mEnd; + + public this() + { + mStart = 0; + mEnd = 0; + } + + public this(int start, int end) + { + Debug.Assert(end >= start); + mStart = start; + mEnd = end; + } + } +} diff --git a/IDEHelper/Compiler/BfAst.cpp b/IDEHelper/Compiler/BfAst.cpp index dfb61177..177af086 100644 --- a/IDEHelper/Compiler/BfAst.cpp +++ b/IDEHelper/Compiler/BfAst.cpp @@ -1545,6 +1545,8 @@ const char* Beefy::BfTokenToString(BfToken token) return ".."; case BfToken_DotDotDot: return "..."; + case BfToken_DotDotLess: + return "..<"; case BfToken_QuestionDot: return "?."; case BfToken_QuestionLBracket: @@ -1639,22 +1641,24 @@ int Beefy::BfGetBinaryOpPrecendence(BfBinaryOp binOp) case BfBinaryOp_OverflowMultiply: case BfBinaryOp_Divide: case BfBinaryOp_Modulus: - return 13; + return 14; case BfBinaryOp_Add: case BfBinaryOp_Subtract: case BfBinaryOp_OverflowAdd: case BfBinaryOp_OverflowSubtract: - return 12; + return 13; case BfBinaryOp_LeftShift: case BfBinaryOp_RightShift: - return 11; + return 12; case BfBinaryOp_BitwiseAnd: - return 10; + return 11; case BfBinaryOp_ExclusiveOr: - return 9; + return 10; case BfBinaryOp_BitwiseOr: + return 9; + case BfBinaryOp_Range: + case BfBinaryOp_ClosedRange: return 8; - // "Range" inserted here if we were copying swift case BfBinaryOp_Is: case BfBinaryOp_As: return 7; @@ -1715,6 +1719,8 @@ const char* Beefy::BfGetOpName(BfBinaryOp binOp) case BfBinaryOp_NullCoalesce: return "??"; case BfBinaryOp_Is: return "is"; case BfBinaryOp_As: return "as"; + case BfBinaryOp_Range: return "..<"; + case BfBinaryOp_ClosedRange: return "..."; default: return "???"; } } @@ -1740,6 +1746,9 @@ const char* Beefy::BfGetOpName(BfUnaryOp unaryOp) case BfUnaryOp_Mut: return "mut"; case BfUnaryOp_Params: return "params"; case BfUnaryOp_Cascade: return ".."; + case BfUnaryOp_PartialRangeUpTo: return "..<"; + case BfUnaryOp_PartialRangeThrough: return "..."; + case BfUnaryOp_PartialRangeFrom: return "..."; default: return "???"; } } @@ -1798,6 +1807,10 @@ BfBinaryOp Beefy::BfTokenToBinaryOp(BfToken token) return BfBinaryOp_ConditionalOr; case BfToken_DblQuestion: return BfBinaryOp_NullCoalesce; + case BfToken_DotDotLess: + return BfBinaryOp_Range; + case BfToken_DotDotDot: + return BfBinaryOp_ClosedRange; default: return BfBinaryOp_None; } @@ -1835,6 +1848,10 @@ BfUnaryOp Beefy::BfTokenToUnaryOp(BfToken token) return BfUnaryOp_Params; case BfToken_DotDot: return BfUnaryOp_Cascade; + case BfToken_DotDotDot: + return BfUnaryOp_PartialRangeThrough; + case BfToken_DotDotLess: + return BfUnaryOp_PartialRangeUpTo; default: return BfUnaryOp_None; } diff --git a/IDEHelper/Compiler/BfAst.h b/IDEHelper/Compiler/BfAst.h index b28e3d24..9b0e24c5 100644 --- a/IDEHelper/Compiler/BfAst.h +++ b/IDEHelper/Compiler/BfAst.h @@ -232,6 +232,7 @@ enum BfToken : uint8 BfToken_Dot, BfToken_DotDot, BfToken_DotDotDot, + BfToken_DotDotLess, BfToken_QuestionDot, BfToken_QuestionLBracket, BfToken_AutocompleteDot, @@ -1821,7 +1822,9 @@ enum BfBinaryOp BfBinaryOp_ConditionalOr, BfBinaryOp_NullCoalesce, BfBinaryOp_Is, - BfBinaryOp_As + BfBinaryOp_As, + BfBinaryOp_Range, + BfBinaryOp_ClosedRange, }; enum BfAssignmentOp @@ -1859,7 +1862,10 @@ enum BfUnaryOp BfUnaryOp_Out, BfUnaryOp_Mut, BfUnaryOp_Params, - BfUnaryOp_Cascade + BfUnaryOp_Cascade, + BfUnaryOp_PartialRangeUpTo, + BfUnaryOp_PartialRangeThrough, + BfUnaryOp_PartialRangeFrom, }; class BfTokenNode : public BfAstNode diff --git a/IDEHelper/Compiler/BfCompiler.cpp b/IDEHelper/Compiler/BfCompiler.cpp index 7f37b5b2..1e3a7fe0 100644 --- a/IDEHelper/Compiler/BfCompiler.cpp +++ b/IDEHelper/Compiler/BfCompiler.cpp @@ -382,6 +382,8 @@ BfCompiler::BfCompiler(BfSystem* bfSystem, bool isResolveOnly) mArray3TypeDef = NULL; mArray4TypeDef = NULL; mSpanTypeDef = NULL; + mRangeTypeDef = NULL; + mClosedRangeTypeDef = NULL; mAttributeTypeDef = NULL; mAttributeUsageAttributeTypeDef = NULL; mClassVDataTypeDef = NULL; @@ -6722,7 +6724,9 @@ bool BfCompiler::DoCompile(const StringImpl& outputDirectory) mArray2TypeDef = _GetRequiredType("System.Array2", 1); mArray3TypeDef = _GetRequiredType("System.Array3", 1); mArray4TypeDef = _GetRequiredType("System.Array4", 1); - mSpanTypeDef = _GetRequiredType("System.Span", 1); + mSpanTypeDef = _GetRequiredType("System.Span", 1); + mRangeTypeDef = _GetRequiredType("System.Range"); + mClosedRangeTypeDef = _GetRequiredType("System.ClosedRange"); 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 a892f0dc..3f8d16f6 100644 --- a/IDEHelper/Compiler/BfCompiler.h +++ b/IDEHelper/Compiler/BfCompiler.h @@ -347,6 +347,8 @@ public: BfTypeDef* mArray3TypeDef; BfTypeDef* mArray4TypeDef; BfTypeDef* mSpanTypeDef; + BfTypeDef* mRangeTypeDef; + BfTypeDef* mClosedRangeTypeDef; BfTypeDef* mClassVDataTypeDef; diff --git a/IDEHelper/Compiler/BfExprEvaluator.cpp b/IDEHelper/Compiler/BfExprEvaluator.cpp index 9780d8d2..144120ad 100644 --- a/IDEHelper/Compiler/BfExprEvaluator.cpp +++ b/IDEHelper/Compiler/BfExprEvaluator.cpp @@ -20433,7 +20433,7 @@ void BfExprEvaluator::PerformBinaryOperation(BfExpression* leftExpression, BfExp } if (leftValue.mType->IsRef()) leftValue.mType = leftValue.mType->GetUnderlyingType(); - + if ((binaryOp == BfBinaryOp_ConditionalAnd) || (binaryOp == BfBinaryOp_ConditionalOr)) { if (mModule->mCurMethodState->mDeferredLocalAssignData != NULL) @@ -20670,7 +20670,7 @@ bool BfExprEvaluator::PerformBinaryOperation_NullCoalesce(BfTokenNode* opToken, mModule->AddBasicBlock(endBB); if (assignTo != NULL) - { + { mResult = *assignTo; } else @@ -20689,12 +20689,35 @@ bool BfExprEvaluator::PerformBinaryOperation_NullCoalesce(BfTokenNode* opToken, void BfExprEvaluator::PerformBinaryOperation(BfExpression* leftExpression, BfExpression* rightExpression, BfBinaryOp binaryOp, BfTokenNode* opToken, BfBinOpFlags flags) { + if ((binaryOp == BfBinaryOp_Range) || (binaryOp == BfBinaryOp_ClosedRange)) + { + auto intType = mModule->GetPrimitiveType(BfTypeCode_IntPtr); + + auto allocType = mModule->ResolveTypeDef((binaryOp == BfBinaryOp_Range) ? mModule->mCompiler->mRangeTypeDef : mModule->mCompiler->mClosedRangeTypeDef)->ToTypeInstance(); + auto alloca = mModule->CreateAlloca(allocType); + + SizedArray argExprs; + argExprs.Add(leftExpression); + argExprs.Add(rightExpression); + + BfSizedArray args = argExprs; + + BfResolvedArgs argValues; + argValues.Init(&args); + ResolveArgValues(argValues, BfResolveArgsFlag_DeferParamEval); + + mResult = BfTypedValue(alloca, allocType, true); + MatchConstructor(opToken, NULL, mResult, allocType, argValues, true, false); + + return; + } + BfTypedValue leftValue; if (leftExpression != NULL) { leftValue = mModule->CreateValueFromExpression(leftExpression, mExpectingType, (BfEvalExprFlags)((mBfEvalExprFlags & BfEvalExprFlags_InheritFlags) | BfEvalExprFlags_NoCast | BfEvalExprFlags_AllowIntUnknown)); } - return PerformBinaryOperation(leftExpression, rightExpression, binaryOp, opToken, flags, leftValue); + PerformBinaryOperation(leftExpression, rightExpression, binaryOp, opToken, flags, leftValue); } bool BfExprEvaluator::CheckConstCompare(BfBinaryOp binaryOp, BfAstNode* opToken, const BfTypedValue& leftValue, const BfTypedValue& rightValue) diff --git a/IDEHelper/Compiler/BfParser.cpp b/IDEHelper/Compiler/BfParser.cpp index a4deab7c..8db3a7e4 100644 --- a/IDEHelper/Compiler/BfParser.cpp +++ b/IDEHelper/Compiler/BfParser.cpp @@ -2316,6 +2316,13 @@ void BfParser::NextToken(int endIdx, bool outerIsInterpolate) mToken = BfToken_DotDotDot; mSyntaxToken = BfSyntaxToken_Token; } + else if (mSrc[mSrcIdx + 1] == '<') + { + mSrcIdx += 2; + mTokenEnd = mSrcIdx; + mToken = BfToken_DotDotLess; + mSyntaxToken = BfSyntaxToken_Token; + } else { mSrcIdx++; @@ -2526,8 +2533,15 @@ void BfParser::NextToken(int endIdx, bool outerIsInterpolate) bool endNumber = false; + bool hasDot = c == '.'; + if ((hasDot) && (mSrc[mSrcIdx] == '.')) + { + // Skip float parsing if we have a double-dot `1..` case + hasDot = false; + } + // The 'prevIsDot' helps tuple lookups like "tuple.0.0", interpreting those as two integers rather than a float - if (((c == '.') && (!prevIsDot)) || (hasExp)) + if (((hasDot) && (!prevIsDot)) || (hasExp)) { // Switch to floating point mode //double dVal = val; diff --git a/IDEHelper/Compiler/BfPrinter.cpp b/IDEHelper/Compiler/BfPrinter.cpp index aa568373..f63b7877 100644 --- a/IDEHelper/Compiler/BfPrinter.cpp +++ b/IDEHelper/Compiler/BfPrinter.cpp @@ -2287,7 +2287,7 @@ void BfPrinter::Visit(BfUnaryOperatorExpression* unaryOpExpr) { Visit(unaryOpExpr->ToBase()); - bool postOp = (unaryOpExpr->mOp == BfUnaryOp_PostIncrement) || (unaryOpExpr->mOp == BfUnaryOp_PostDecrement); + bool postOp = (unaryOpExpr->mOp == BfUnaryOp_PostIncrement) || (unaryOpExpr->mOp == BfUnaryOp_PostDecrement) || (unaryOpExpr->mOp == BfUnaryOp_PartialRangeFrom); if (!postOp) VisitChild(unaryOpExpr->mOpToken); if ((unaryOpExpr->mOp == BfUnaryOp_Ref) || (unaryOpExpr->mOp == BfUnaryOp_Mut) || (unaryOpExpr->mOp == BfUnaryOp_Out) || (unaryOpExpr->mOp == BfUnaryOp_Params) || (unaryOpExpr->mOp == BfUnaryOp_Cascade)) diff --git a/IDEHelper/Compiler/BfReducer.cpp b/IDEHelper/Compiler/BfReducer.cpp index e19789ad..83015c12 100644 --- a/IDEHelper/Compiler/BfReducer.cpp +++ b/IDEHelper/Compiler/BfReducer.cpp @@ -2353,12 +2353,22 @@ BfExpression* BfReducer::CreateExpression(BfAstNode* node, CreateExprFlags creat ((token == BfToken_RChevron) || (token == BfToken_RDblChevron))) return exprLeft; - if ((token == BfToken_DblPlus) || (token == BfToken_DblMinus)) - { - // Post-increment, post-decrement + BfUnaryOp postUnaryOp = BfUnaryOp_None; + if (token == BfToken_DblPlus) + postUnaryOp = BfUnaryOp_PostIncrement; + if (token == BfToken_DblMinus) + postUnaryOp = BfUnaryOp_PostDecrement; + + if (token == BfToken_DotDotDot) + { + //TODO: Detect if this is a BfUnaryOp_PartialRangeFrom + } + + if (postUnaryOp != BfUnaryOp_None) + { auto unaryOpExpr = mAlloc->Alloc(); ReplaceNode(exprLeft, unaryOpExpr); - unaryOpExpr->mOp = (token == BfToken_DblPlus) ? BfUnaryOp_PostIncrement : BfUnaryOp_PostDecrement; + unaryOpExpr->mOp = postUnaryOp; MEMBER_SET(unaryOpExpr, mOpToken, tokenNode); MEMBER_SET(unaryOpExpr, mExpression, exprLeft); exprLeft = unaryOpExpr; diff --git a/IDEHelper/Tests/src/Loops.bf b/IDEHelper/Tests/src/Loops.bf index 82adf5ba..864ca3cc 100644 --- a/IDEHelper/Tests/src/Loops.bf +++ b/IDEHelper/Tests/src/Loops.bf @@ -72,6 +72,16 @@ namespace Tests } Test.Assert(iterations == 19); Test.Assert(sGetValCount == 20); + + int total = 0; + for (int i in 1..<10) + total += i; + Test.Assert(total == 1+2+3+4+5+6+7+8+9); + + total = 0; + for (int i in 1...10) + total += i; + Test.Assert(total == 1+2+3+4+5+6+7+8+9+10); } public static void TestEnumerator1(EnumeratorTest e)