1
0
Fork 0
mirror of https://github.com/beefytech/Beef.git synced 2025-06-09 03:52:19 +02:00

Enhanced ranges

This commit is contained in:
Brian Fiete 2021-10-24 08:12:18 -07:00
parent eec2cb5e6c
commit 27fd5552cc
12 changed files with 365 additions and 22 deletions

View file

@ -260,6 +260,17 @@ namespace System
} }
} }
public Span<T> this[IndexRange range]
{
#if !DEBUG
[Inline]
#endif
get
{
return Span<T>(&mFirstElement, mLength)[range];
}
}
[Inline] [Inline]
public T* CArray() public T* CArray()
{ {

View file

@ -211,6 +211,17 @@ namespace System.Collections
} }
} }
public Span<T> this[IndexRange range]
{
#if !DEBUG
[Inline]
#endif
get
{
return Span<T>(mItems, mSize)[range];
}
}
public ref T Front public ref T Front
{ {
get get

View file

@ -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<int> struct Range : RangeExpression, IEnumerable<int>
{ {
protected int mStart; protected int mStart;
@ -110,6 +128,11 @@ namespace System
mEnd = 0; mEnd = 0;
} }
public static operator IndexRange(Range list)
{
return .(.FromFront(list.mStart), .FromFront(list.mEnd), false);
}
[Inline] [Inline]
public Enumerator GetEnumerator() 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<int> struct ClosedRange : RangeExpression, IEnumerable<int>
{ {
protected int mStart; protected int mStart;
@ -294,6 +414,11 @@ namespace System
mEnd = 0; mEnd = 0;
} }
public static operator IndexRange(ClosedRange list)
{
return .(.FromFront(list.mStart), .FromFront(list.mEnd), true);
}
[Inline] [Inline]
public Enumerator GetEnumerator() public Enumerator GetEnumerator()
{ {

View file

@ -134,6 +134,53 @@ namespace System
} }
} }
public Span<T> 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<T> Slice(int index) public Span<T> Slice(int index)
{ {
Debug.Assert((uint)index <= (uint)mLength); Debug.Assert((uint)index <= (uint)mLength);

View file

@ -1753,9 +1753,10 @@ const char* Beefy::BfGetOpName(BfUnaryOp unaryOp)
case BfUnaryOp_Mut: return "mut"; case BfUnaryOp_Mut: return "mut";
case BfUnaryOp_Params: return "params"; case BfUnaryOp_Params: return "params";
case BfUnaryOp_Cascade: return ".."; case BfUnaryOp_Cascade: return "..";
case BfUnaryOp_FromEnd: return "^";
case BfUnaryOp_PartialRangeUpTo: return "..<"; case BfUnaryOp_PartialRangeUpTo: return "..<";
case BfUnaryOp_PartialRangeThrough: return "..."; case BfUnaryOp_PartialRangeThrough: return "...";
case BfUnaryOp_PartialRangeFrom: return "..."; case BfUnaryOp_PartialRangeFrom: return "...";
default: return "???"; default: return "???";
} }
} }
@ -1855,6 +1856,8 @@ BfUnaryOp Beefy::BfTokenToUnaryOp(BfToken token)
return BfUnaryOp_Params; return BfUnaryOp_Params;
case BfToken_DotDot: case BfToken_DotDot:
return BfUnaryOp_Cascade; return BfUnaryOp_Cascade;
case BfToken_Carat:
return BfUnaryOp_FromEnd;
case BfToken_DotDotDot: case BfToken_DotDotDot:
return BfUnaryOp_PartialRangeThrough; return BfUnaryOp_PartialRangeThrough;
case BfToken_DotDotLess: case BfToken_DotDotLess:

View file

@ -1866,6 +1866,7 @@ enum BfUnaryOp
BfUnaryOp_Mut, BfUnaryOp_Mut,
BfUnaryOp_Params, BfUnaryOp_Params,
BfUnaryOp_Cascade, BfUnaryOp_Cascade,
BfUnaryOp_FromEnd,
BfUnaryOp_PartialRangeUpTo, BfUnaryOp_PartialRangeUpTo,
BfUnaryOp_PartialRangeThrough, BfUnaryOp_PartialRangeThrough,
BfUnaryOp_PartialRangeFrom, BfUnaryOp_PartialRangeFrom,

View file

@ -384,6 +384,8 @@ BfCompiler::BfCompiler(BfSystem* bfSystem, bool isResolveOnly)
mSpanTypeDef = NULL; mSpanTypeDef = NULL;
mRangeTypeDef = NULL; mRangeTypeDef = NULL;
mClosedRangeTypeDef = NULL; mClosedRangeTypeDef = NULL;
mIndexTypeDef = NULL;
mIndexRangeTypeDef = NULL;
mAttributeTypeDef = NULL; mAttributeTypeDef = NULL;
mAttributeUsageAttributeTypeDef = NULL; mAttributeUsageAttributeTypeDef = NULL;
mClassVDataTypeDef = NULL; mClassVDataTypeDef = NULL;
@ -6723,6 +6725,8 @@ bool BfCompiler::DoCompile(const StringImpl& outputDirectory)
mSpanTypeDef = _GetRequiredType("System.Span", 1); mSpanTypeDef = _GetRequiredType("System.Span", 1);
mRangeTypeDef = _GetRequiredType("System.Range"); mRangeTypeDef = _GetRequiredType("System.Range");
mClosedRangeTypeDef = _GetRequiredType("System.ClosedRange"); mClosedRangeTypeDef = _GetRequiredType("System.ClosedRange");
mIndexTypeDef = _GetRequiredType("System.Index");
mIndexRangeTypeDef = _GetRequiredType("System.IndexRange");
mAttributeTypeDef = _GetRequiredType("System.Attribute"); mAttributeTypeDef = _GetRequiredType("System.Attribute");
mAttributeUsageAttributeTypeDef = _GetRequiredType("System.AttributeUsageAttribute"); mAttributeUsageAttributeTypeDef = _GetRequiredType("System.AttributeUsageAttribute");
mClassVDataTypeDef = _GetRequiredType("System.ClassVData"); mClassVDataTypeDef = _GetRequiredType("System.ClassVData");

View file

@ -349,6 +349,8 @@ public:
BfTypeDef* mSpanTypeDef; BfTypeDef* mSpanTypeDef;
BfTypeDef* mRangeTypeDef; BfTypeDef* mRangeTypeDef;
BfTypeDef* mClosedRangeTypeDef; BfTypeDef* mClosedRangeTypeDef;
BfTypeDef* mIndexTypeDef;
BfTypeDef* mIndexRangeTypeDef;
BfTypeDef* mClassVDataTypeDef; BfTypeDef* mClassVDataTypeDef;

View file

@ -15220,11 +15220,6 @@ void BfExprEvaluator::InjectMixin(BfAstNode* targetSrc, BfTypedValue target, boo
} }
auto autoComplete = GetAutoComplete(); 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)) if ((autoComplete != NULL) && (autoComplete->mIsCapturingMethodMatchInfo) && (autoComplete->mMethodMatchInfo != NULL) && (autoComplete->mMethodMatchInfo->mInstanceList.size() != 0))
autoComplete->mIsCapturingMethodMatchInfo = false; autoComplete->mIsCapturingMethodMatchInfo = false;
@ -19797,6 +19792,13 @@ void BfExprEvaluator::Visit(BfUnaryOperatorExpression* unaryOpExpr)
void BfExprEvaluator::PerformUnaryOperation(BfExpression* unaryOpExpr, BfUnaryOp unaryOp, BfTokenNode* opToken, BfUnaryOpFlags opFlags) 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. // If this is a cast, we don't want the value to be coerced before the unary operator is applied.
// WAIT: Why not? // WAIT: Why not?
@ -20456,8 +20458,32 @@ void BfExprEvaluator::PerformUnaryOperation_OnResult(BfExpression* unaryOpExpr,
mModule->Fail("Illegal use of argument cascade expression", opToken); mModule->Fail("Illegal use of argument cascade expression", opToken);
} }
break; 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: default:
mModule->Fail("INTERNAL ERROR: Unhandled unary operator", unaryOpExpr); mModule->Fail(StrFormat("Illegal use of '%s' unary operator", BfGetOpName(unaryOp)), unaryOpExpr);
break; break;
} }
@ -20792,12 +20818,74 @@ void BfExprEvaluator::PerformBinaryOperation(BfExpression* leftExpression, BfExp
{ {
auto intType = mModule->GetPrimitiveType(BfTypeCode_IntPtr); 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<BfUnaryOperatorExpression>(leftExpression))
if (unaryOpExpr->mOp == BfUnaryOp_FromEnd)
isIndexExpr = true;
if (rightExpression == NULL)
isIndexExpr = true;
if (auto unaryOpExpr = BfNodeDynCast<BfUnaryOperatorExpression>(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); auto alloca = mModule->CreateAlloca(allocType);
BfTypedValueExpression leftTypedValueExpr;
BfTypedValueExpression rightTypedValueExpr;
BfTypedValueExpression isClosedTypedValueExpr;
SizedArray<BfExpression*, 2> argExprs; SizedArray<BfExpression*, 2> argExprs;
argExprs.Add(leftExpression); if (leftExpression != NULL)
argExprs.Add(rightExpression); {
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<BfIRValue, 8> 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<BfIRValue, 8> 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<BfExpression*> args = argExprs; BfSizedArray<BfExpression*> args = argExprs;

View file

@ -2277,14 +2277,15 @@ BfExpression* BfReducer::CreateExpression(BfAstNode* node, CreateExprFlags creat
CreateExprFlags innerFlags = (CreateExprFlags)(rhsCreateExprFlags | CreateExprFlags_EarlyExit); CreateExprFlags innerFlags = (CreateExprFlags)(rhsCreateExprFlags | CreateExprFlags_EarlyExit);
if (unaryOp == BfUnaryOp_Cascade) if (unaryOp == BfUnaryOp_Cascade)
{
innerFlags = (CreateExprFlags)(innerFlags | (createExprFlags & CreateExprFlags_AllowVariableDecl)); 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 // Don't attempt binary or unary operations- they will always be lower precedence
unaryOpExpr->mExpression = CreateExpressionAfter(unaryOpExpr, innerFlags); unaryOpExpr->mExpression = CreateExpressionAfter(unaryOpExpr, innerFlags);
if (unaryOpExpr->mExpression == NULL) if (unaryOpExpr->mExpression == NULL)
return NULL; return unaryOpExpr;
MoveNode(unaryOpExpr->mExpression, unaryOpExpr); MoveNode(unaryOpExpr->mExpression, unaryOpExpr);
} }
@ -2356,7 +2357,8 @@ BfExpression* BfReducer::CreateExpression(BfAstNode* node, CreateExprFlags creat
if (exprLeft == NULL) if (exprLeft == NULL)
{ {
Fail("Expected expression", node); if ((createExprFlags & CreateExprFlags_AllowEmpty) == 0)
Fail("Expected expression", node);
return NULL; return NULL;
} }
@ -2377,7 +2379,7 @@ BfExpression* BfReducer::CreateExpression(BfAstNode* node, CreateExprFlags creat
if (token == BfToken_DblPlus) if (token == BfToken_DblPlus)
postUnaryOp = BfUnaryOp_PostIncrement; postUnaryOp = BfUnaryOp_PostIncrement;
if (token == BfToken_DblMinus) if (token == BfToken_DblMinus)
postUnaryOp = BfUnaryOp_PostDecrement; postUnaryOp = BfUnaryOp_PostDecrement;
if (token == BfToken_DotDotDot) if (token == BfToken_DotDotDot)
{ {
@ -2700,17 +2702,36 @@ BfExpression* BfReducer::CreateExpression(BfAstNode* node, CreateExprFlags creat
{ {
if ((createExprFlags & CreateExprFlags_EarlyExit) != 0) if ((createExprFlags & CreateExprFlags_EarlyExit) != 0)
return exprLeft; return exprLeft;
auto binOpExpression = mAlloc->Alloc<BfBinaryOperatorExpression>();
ReplaceNode(exprLeft, binOpExpression);
binOpExpression->mLeft = exprLeft;
binOpExpression->mOp = binOp;
MEMBER_SET(binOpExpression, mOpToken, tokenNode);
mVisitorPos.MoveNext(); mVisitorPos.MoveNext();
// We only need to check binary operator precedence at the "top level" binary operator // We only need to check binary operator precedence at the "top level" binary operator
rhsCreateExprFlags = (CreateExprFlags)(rhsCreateExprFlags | CreateExprFlags_NoCheckBinOpPrecedence); 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<BfUnaryOperatorExpression>();
ReplaceNode(exprLeft, unaryOpExpression);
unaryOpExpression->mExpression = exprLeft;
unaryOpExpression->mOp = BfUnaryOp_PartialRangeFrom;
MEMBER_SET(unaryOpExpression, mOpToken, tokenNode);
return unaryOpExpression;
}
}
auto binOpExpression = mAlloc->Alloc<BfBinaryOperatorExpression>();
ReplaceNode(exprLeft, binOpExpression);
binOpExpression->mLeft = exprLeft;
binOpExpression->mOp = binOp;
MEMBER_SET(binOpExpression, mOpToken, tokenNode);
if (exprRight == NULL) if (exprRight == NULL)
return binOpExpression; return binOpExpression;
MEMBER_SET(binOpExpression, mRight, exprRight); MEMBER_SET(binOpExpression, mRight, exprRight);

View file

@ -26,7 +26,8 @@ public:
CreateExprFlags_ExitOnParenExpr = 0x80, CreateExprFlags_ExitOnParenExpr = 0x80,
CreateExprFlags_NoCheckBinOpPrecedence = 0x100, CreateExprFlags_NoCheckBinOpPrecedence = 0x100,
CreateExprFlags_BreakOnCascade = 0x200, 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 enum CreateStmtFlags

View file

@ -133,6 +133,35 @@ namespace Tests
Test.Assert((1..<3).Contains(1..<3)); Test.Assert((1..<3).Contains(1..<3));
Test.Assert(!(1..<3).Contains(1..<4)); Test.Assert(!(1..<3).Contains(1..<4));
List<int> 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) public static void TestEnumerator1(EnumeratorTest e)