- added 'foreach' loop to ZScript.

Syntax:

foreach(variable : array)
{
}

the variable's type is automatically deducted.
This commit is contained in:
Christoph Oelckers 2022-11-15 21:34:45 +01:00
commit 29b4418c3a
8 changed files with 173 additions and 24 deletions

View file

@ -7310,8 +7310,8 @@ FxClassMember::FxClassMember(FxExpression *x, PField* mem, const FScriptPosition
//
//==========================================================================
FxArrayElement::FxArrayElement(FxExpression *base, FxExpression *_index)
:FxExpression(EFX_ArrayElement, base->ScriptPosition)
FxArrayElement::FxArrayElement(FxExpression *base, FxExpression *_index, bool nob)
:FxExpression(EFX_ArrayElement, base->ScriptPosition), noboundscheck(nob)
{
Array=base;
index = _index;
@ -7594,18 +7594,21 @@ ExpEmit FxArrayElement::Emit(VMFunctionBuilder *build)
else
{
ExpEmit indexv(index->Emit(build));
if (SizeAddr != ~0u || nestedarray)
if (!noboundscheck) // this is 'foreach' which is known to be inside the bounds.
{
build->Emit(OP_BOUND_R, indexv.RegNum, bound.RegNum);
bound.Free(build);
}
else if (arraytype->ElementCount > 65535)
{
build->Emit(OP_BOUND_K, indexv.RegNum, build->GetConstantInt(arraytype->ElementCount));
}
else
{
build->Emit(OP_BOUND, indexv.RegNum, arraytype->ElementCount);
if (SizeAddr != ~0u || nestedarray)
{
build->Emit(OP_BOUND_R, indexv.RegNum, bound.RegNum);
bound.Free(build);
}
else if (arraytype->ElementCount > 65535)
{
build->Emit(OP_BOUND_K, indexv.RegNum, build->GetConstantInt(arraytype->ElementCount));
}
else
{
build->Emit(OP_BOUND, indexv.RegNum, arraytype->ElementCount);
}
}
if (!start.Konst)
@ -8338,7 +8341,7 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx)
else if (Self->IsVector())
{
// handle builtins: Vectors got 5.
if (MethodName == NAME_Length || MethodName == NAME_LengthSquared || MethodName == NAME_Unit || MethodName == NAME_Angle)
if (MethodName == NAME_Length || MethodName == NAME_LengthSquared || MethodName == NAME_Sum || MethodName == NAME_Unit || MethodName == NAME_Angle)
{
if (ArgList.Size() > 0)
{
@ -10655,6 +10658,77 @@ ExpEmit FxForLoop::Emit(VMFunctionBuilder *build)
return ExpEmit();
}
//==========================================================================
//
// FxForLoop
//
//==========================================================================
FxForEachLoop::FxForEachLoop(FName vn, FxExpression* arrayvar, FxExpression* arrayvar2, FxExpression* code, const FScriptPosition& pos)
: FxLoopStatement(EFX_ForEachLoop, pos), loopVarName(vn), Array(arrayvar), Array2(arrayvar2), Code(code)
{
ValueType = TypeVoid;
if (Array != nullptr) Array->NeedResult = false;
if (Array2 != nullptr) Array2->NeedResult = false;
if (Code != nullptr) Code->NeedResult = false;
}
FxForEachLoop::~FxForEachLoop()
{
SAFE_DELETE(Array);
SAFE_DELETE(Array2);
SAFE_DELETE(Code);
}
FxExpression* FxForEachLoop::DoResolve(FCompileContext& ctx)
{
CHECKRESOLVED();
SAFE_RESOLVE(Array, ctx);
SAFE_RESOLVE(Array2, ctx);
// Instead of writing a new code generator for this, convert this into
//
// int @size = array.Size();
// for(int @i = 0; @i < @size; @i++)
// {
// let var = array[i];
// body
// }
// and let the existing 'for' loop code sort out the rest.
FName sizevar = "@size";
FName itvar = "@i";
FArgumentList al;
auto block = new FxCompoundStatement(ScriptPosition);
auto arraysize = new FxMemberFunctionCall(Array, NAME_Size, al, ScriptPosition);
auto size = new FxLocalVariableDeclaration(TypeSInt32, sizevar, arraysize, 0, ScriptPosition);
auto it = new FxLocalVariableDeclaration(TypeSInt32, itvar, new FxConstant(0, ScriptPosition), 0, ScriptPosition);
block->Add(size);
block->Add(it);
auto cit = new FxLocalVariable(it, ScriptPosition);
auto csiz = new FxLocalVariable(size, ScriptPosition);
auto comp = new FxCompareRel('<', cit, csiz); // new FxIdentifier(itvar, ScriptPosition), new FxIdentifier(sizevar, ScriptPosition));
auto iit = new FxLocalVariable(it, ScriptPosition);
auto bump = new FxPreIncrDecr(iit, TK_Incr);
auto ait = new FxLocalVariable(it, ScriptPosition);
auto access = new FxArrayElement(Array2, ait, true); // Note: Array must be a separate copy because these nodes cannot share the same element.
auto assign = new FxLocalVariableDeclaration(TypeAuto, loopVarName, access, 0, ScriptPosition);
auto body = new FxCompoundStatement(ScriptPosition);
body->Add(assign);
body->Add(Code);
auto forloop = new FxForLoop(nullptr, comp, bump, body, ScriptPosition);
block->Add(forloop);
Array2 = Array = nullptr;
Code = nullptr;
delete this;
return block->Resolve(ctx);
}
//==========================================================================
//
// FxJumpStatement
@ -11221,14 +11295,23 @@ FxExpression *FxLocalVariableDeclaration::Resolve(FCompileContext &ctx)
delete this;
return nullptr;
}
SAFE_RESOLVE_OPT(Init, ctx);
if (Init->ValueType->RegType == REGT_NIL)
{
ScriptPosition.Message(MSG_ERROR, "Cannot initialize non-scalar variable %s here", Name.GetChars());
delete this;
return nullptr;
}
SAFE_RESOLVE(Init, ctx);
ValueType = Init->ValueType;
if (ValueType->RegType == REGT_NIL)
{
if (Init->IsStruct())
{
ValueType = NewPointer(ValueType);
Init = new FxTypeCast(Init, ValueType, false);
SAFE_RESOLVE(Init, ctx);
}
else
{
ScriptPosition.Message(MSG_ERROR, "Cannot initialize non-scalar variable %s here", Name.GetChars());
delete this;
return nullptr;
}
}
// check for undersized ints and floats. These are not allowed as local variables.
if (IsInteger() && ValueType->Align < sizeof(int)) ValueType = TypeSInt32;
else if (IsFloat() && ValueType->Align < sizeof(double)) ValueType = TypeFloat64;