mirror of
https://github.com/DrBeef/Raze.git
synced 2025-02-20 18:52:43 +00:00
- added 'foreach' loop to ZScript.
Syntax: foreach(variable : array) { } the variable's type is automatically deducted.
This commit is contained in:
parent
dc9451d584
commit
6b3e57fd2c
9 changed files with 174 additions and 25 deletions
|
@ -174,7 +174,7 @@ std2:
|
|||
|
||||
/* Other keywords from UnrealScript */
|
||||
'abstract' { RET(TK_Abstract); }
|
||||
'foreach' { RET(TK_ForEach); }
|
||||
'foreach' { RET(ParseVersion >= MakeVersion(4, 10, 0)? TK_ForEach : TK_Identifier); }
|
||||
'true' { RET(TK_True); }
|
||||
'false' { RET(TK_False); }
|
||||
'none' { RET(TK_None); }
|
||||
|
|
|
@ -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)
|
||||
|
@ -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;
|
||||
|
|
|
@ -272,6 +272,7 @@ enum EFxType
|
|||
EFX_WhileLoop,
|
||||
EFX_DoWhileLoop,
|
||||
EFX_ForLoop,
|
||||
EFX_ForEachLoop,
|
||||
EFX_JumpStatement,
|
||||
EFX_ReturnStatement,
|
||||
EFX_ClassTypeCast,
|
||||
|
@ -349,6 +350,7 @@ public:
|
|||
bool IsArray() const { return ValueType->isArray() || (ValueType->isPointer() && ValueType->toPointer()->PointedType->isArray()); }
|
||||
bool isStaticArray() const { return (ValueType->isPointer() && ValueType->toPointer()->PointedType->isStaticArray()); } // can only exist in pointer form.
|
||||
bool IsDynamicArray() const { return (ValueType->isDynArray()); }
|
||||
bool IsStruct() const { return ValueType->isStruct(); }
|
||||
bool IsNativeStruct() const { return (ValueType->isStruct() && static_cast<PStruct*>(ValueType)->isNative); }
|
||||
|
||||
virtual ExpEmit Emit(VMFunctionBuilder *build);
|
||||
|
@ -1541,8 +1543,9 @@ public:
|
|||
bool AddressRequested;
|
||||
bool AddressWritable;
|
||||
bool arrayispointer = false;
|
||||
bool noboundscheck;
|
||||
|
||||
FxArrayElement(FxExpression*, FxExpression*);
|
||||
FxArrayElement(FxExpression*, FxExpression*, bool = false);
|
||||
~FxArrayElement();
|
||||
FxExpression *Resolve(FCompileContext&);
|
||||
bool RequestAddress(FCompileContext &ctx, bool *writable);
|
||||
|
@ -2000,7 +2003,26 @@ public:
|
|||
FxForLoop(FxExpression *init, FxExpression *condition, FxExpression *iteration, FxExpression *code, const FScriptPosition &pos);
|
||||
~FxForLoop();
|
||||
FxExpression *DoResolve(FCompileContext&);
|
||||
ExpEmit Emit(VMFunctionBuilder *build);
|
||||
ExpEmit Emit(VMFunctionBuilder* build);
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FxForLoop
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
class FxForEachLoop : public FxLoopStatement
|
||||
{
|
||||
FName loopVarName;
|
||||
FxExpression* Array;
|
||||
FxExpression* Array2;
|
||||
FxExpression* Code;
|
||||
|
||||
public:
|
||||
FxForEachLoop(FName vn, FxExpression* arrayvar, FxExpression* arrayvar2, FxExpression* code, const FScriptPosition& pos);
|
||||
~FxForEachLoop();
|
||||
FxExpression* DoResolve(FCompileContext&);
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
|
|
|
@ -1829,6 +1829,7 @@ statement(X) ::= compound_statement(A). { X = A; /*X-overwrites-A*/ }
|
|||
statement(X) ::= expression_statement(A) SEMICOLON. { X = A; /*X-overwrites-A*/ }
|
||||
statement(X) ::= selection_statement(X).
|
||||
statement(X) ::= iteration_statement(X).
|
||||
statement(X) ::= array_iteration_statement(X).
|
||||
statement(X) ::= jump_statement(X).
|
||||
statement(X) ::= assign_statement(A) SEMICOLON. { X = A; /*X-overwrites-A*/ }
|
||||
statement(X) ::= local_var(A) SEMICOLON. { X = A; /*X-overwrites-A*/ }
|
||||
|
@ -1986,6 +1987,17 @@ iteration_statement(X) ::= FOR(T) LPAREN for_init(IN) SEMICOLON opt_expr(EX) SEM
|
|||
X = wrap;
|
||||
}
|
||||
|
||||
%type array_iteration_statement{ZCC_Statement *}
|
||||
|
||||
array_iteration_statement(X) ::= FOREACH(T) LPAREN variable_name(IN) COLON expr(EX) RPAREN statement(ST).
|
||||
{
|
||||
NEW_AST_NODE(ArrayIterationStmt, iter, T);
|
||||
iter->ItName = IN;
|
||||
iter->ItArray = EX;
|
||||
iter->LoopStatement = ST;
|
||||
X = iter;
|
||||
}
|
||||
|
||||
while_or_until(X) ::= WHILE(T).
|
||||
{
|
||||
X.Int = ZCC_WHILE;
|
||||
|
|
|
@ -3179,6 +3179,17 @@ FxExpression *ZCCCompiler::ConvertNode(ZCC_TreeNode *ast, bool substitute)
|
|||
return new FxIfStatement(ConvertNode(iff->Condition), truePath, falsePath, *ast);
|
||||
}
|
||||
|
||||
case AST_ArrayIterationStmt:
|
||||
{
|
||||
auto iter = static_cast<ZCC_ArrayIterationStmt*>(ast);
|
||||
auto var = iter->ItName->Name;
|
||||
FxExpression* const itArray = ConvertNode(iter->ItArray);
|
||||
FxExpression* const itArray2 = ConvertNode(iter->ItArray); // the handler needs two copies of this - here's the easiest place to create them.
|
||||
FxExpression* const body = ConvertImplicitScopeNode(ast, iter->LoopStatement);
|
||||
return new FxForEachLoop(iter->ItName->Name, itArray, itArray2, body, *ast);
|
||||
|
||||
}
|
||||
|
||||
case AST_IterationStmt:
|
||||
{
|
||||
auto iter = static_cast<ZCC_IterationStmt *>(ast);
|
||||
|
|
|
@ -240,6 +240,7 @@ static void InitTokenMap()
|
|||
TOKENDEF (TK_Return, ZCC_RETURN);
|
||||
TOKENDEF (TK_Do, ZCC_DO);
|
||||
TOKENDEF (TK_For, ZCC_FOR);
|
||||
TOKENDEF (TK_ForEach, ZCC_FOREACH);
|
||||
TOKENDEF (TK_While, ZCC_WHILE);
|
||||
TOKENDEF (TK_Until, ZCC_UNTIL);
|
||||
TOKENDEF (TK_If, ZCC_IF);
|
||||
|
@ -1123,6 +1124,18 @@ ZCC_TreeNode *TreeNodeDeepCopy_Internal(ZCC_AST *ast, ZCC_TreeNode *orig, bool c
|
|||
break;
|
||||
}
|
||||
|
||||
case AST_ArrayIterationStmt:
|
||||
{
|
||||
TreeNodeDeepCopy_Start(ArrayIterationStmt);
|
||||
|
||||
// ZCC_IterationStmt
|
||||
copy->ItName = static_cast<ZCC_VarName*>(TreeNodeDeepCopy_Internal(ast, origCasted->ItName, true, copiedNodesList));
|
||||
copy->LoopStatement = static_cast<ZCC_Statement*>(TreeNodeDeepCopy_Internal(ast, origCasted->LoopStatement, true, copiedNodesList));
|
||||
copy->ItArray = static_cast<ZCC_Expression*>(TreeNodeDeepCopy_Internal(ast, origCasted->ItArray, true, copiedNodesList));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case AST_IfStmt:
|
||||
{
|
||||
TreeNodeDeepCopy_Start(IfStmt);
|
||||
|
|
|
@ -138,6 +138,7 @@ enum EZCCTreeNodeType
|
|||
AST_FlagDef,
|
||||
AST_MixinDef,
|
||||
AST_MixinStmt,
|
||||
AST_ArrayIterationStmt,
|
||||
|
||||
NUM_AST_NODE_TYPES
|
||||
};
|
||||
|
@ -492,6 +493,13 @@ struct ZCC_IterationStmt : ZCC_Statement
|
|||
enum { Start, End } CheckAt;
|
||||
};
|
||||
|
||||
struct ZCC_ArrayIterationStmt : ZCC_Statement
|
||||
{
|
||||
ZCC_VarName* ItName;
|
||||
ZCC_Expression* ItArray;
|
||||
ZCC_Statement* LoopStatement;
|
||||
};
|
||||
|
||||
struct ZCC_IfStmt : ZCC_Statement
|
||||
{
|
||||
ZCC_Expression *Condition;
|
||||
|
|
|
@ -49,7 +49,7 @@ const char *GetVersionString();
|
|||
#define RC_PRODUCTVERSION2 VERSIONSTR
|
||||
// These are for content versioning.
|
||||
#define VER_MAJOR 4
|
||||
#define VER_MINOR 9
|
||||
#define VER_MINOR 10
|
||||
#define VER_REVISION 0
|
||||
|
||||
#define ENG_MAJOR 1
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
version "4.9"
|
||||
version "4.10"
|
||||
#include "zscript/engine/base.zs"
|
||||
#include "zscript/engine/dynarrays.zs"
|
||||
#include "zscript/engine/inputevents.zs"
|
||||
|
|
Loading…
Reference in a new issue