mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-07-16 09:04:38 +00:00
fix foreach/foreach$ loops.
This commit is contained in:
parent
3e04196a53
commit
4e678292e1
4 changed files with 176 additions and 24 deletions
|
|
@ -413,11 +413,18 @@ U32 IterStmtNode::compileStmt(CodeStream& codeStream, U32 ip)
|
||||||
|
|
||||||
codeStream.pushFixScope(true);
|
codeStream.pushFixScope(true);
|
||||||
|
|
||||||
|
bool isGlobal = varName[0] == '$';
|
||||||
|
TypeReq varType = isStringIter ? TypeReqString : TypeReqUInt;
|
||||||
|
|
||||||
const U32 startIp = ip;
|
const U32 startIp = ip;
|
||||||
containerExpr->compile(codeStream, startIp, TypeReqString);
|
containerExpr->compile(codeStream, startIp, TypeReqString); // todo: figure out better way to codegen this so we don't rely on STR
|
||||||
|
|
||||||
codeStream.emit(isStringIter ? OP_ITER_BEGIN_STR : OP_ITER_BEGIN);
|
codeStream.emit(isStringIter ? OP_ITER_BEGIN_STR : OP_ITER_BEGIN);
|
||||||
|
codeStream.emit(isGlobal);
|
||||||
|
if (isGlobal)
|
||||||
codeStream.emitSTE(varName);
|
codeStream.emitSTE(varName);
|
||||||
|
else
|
||||||
|
codeStream.emit(gFuncVars->assign(varName, varType));
|
||||||
const U32 finalFix = codeStream.emit(0);
|
const U32 finalFix = codeStream.emit(0);
|
||||||
const U32 continueIp = codeStream.emit(OP_ITER);
|
const U32 continueIp = codeStream.emit(OP_ITER);
|
||||||
codeStream.emitFix(CodeStream::FIXTYPE_BREAK);
|
codeStream.emitFix(CodeStream::FIXTYPE_BREAK);
|
||||||
|
|
|
||||||
|
|
@ -643,7 +643,8 @@ ConsoleValue CodeBlock::compileExec(StringTableEntry fileName, const char *inStr
|
||||||
codeStream.emit(OP_RETURN);
|
codeStream.emit(OP_RETURN);
|
||||||
codeStream.emitCodeStream(&codeSize, &code, &lineBreakPairs);
|
codeStream.emitCodeStream(&codeSize, &code, &lineBreakPairs);
|
||||||
|
|
||||||
//dumpInstructions(0, false);
|
if (Con::getBoolVariable("dump"))
|
||||||
|
dumpInstructions(0, false);
|
||||||
|
|
||||||
consoleAllocReset();
|
consoleAllocReset();
|
||||||
|
|
||||||
|
|
@ -1392,23 +1393,50 @@ void CodeBlock::dumpInstructions(U32 startIp, bool upToReturn)
|
||||||
|
|
||||||
case OP_ITER_BEGIN:
|
case OP_ITER_BEGIN:
|
||||||
{
|
{
|
||||||
StringTableEntry varName = CodeToSTE(code, ip);
|
bool isGlobal = code[ip];
|
||||||
|
if (isGlobal)
|
||||||
|
{
|
||||||
|
StringTableEntry varName = CodeToSTE(code, ip + 1);
|
||||||
|
U32 failIp = code[ip + 3];
|
||||||
|
|
||||||
|
Con::printf("%i: OP_ITER_BEGIN varName=%s failIp=%i isGlobal=%s", ip - 1, varName, failIp, "true");
|
||||||
|
|
||||||
|
ip += 4;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
S32 reg = code[ip + 1];
|
||||||
U32 failIp = code[ip + 2];
|
U32 failIp = code[ip + 2];
|
||||||
|
|
||||||
Con::printf("%i: OP_ITER_BEGIN varName=%s failIp=%i", ip - 1, varName, failIp);
|
Con::printf("%i: OP_ITER_BEGIN varRegister=%d failIp=%i isGlobal=%s", ip - 1, reg, failIp, "false");
|
||||||
|
|
||||||
ip += 3;
|
ip += 3;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case OP_ITER_BEGIN_STR:
|
case OP_ITER_BEGIN_STR:
|
||||||
{
|
{
|
||||||
StringTableEntry varName = CodeToSTE(code, ip);
|
bool isGlobal = code[ip];
|
||||||
|
if (isGlobal)
|
||||||
|
{
|
||||||
|
StringTableEntry varName = CodeToSTE(code, ip + 1);
|
||||||
|
U32 failIp = code[ip + 3];
|
||||||
|
|
||||||
|
Con::printf("%i: OP_ITER_BEGIN_STR varName=%s failIp=%i isGlobal=%s", ip - 1, varName, failIp, "true");
|
||||||
|
|
||||||
|
ip += 4;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
S32 reg = code[ip + 1];
|
||||||
U32 failIp = code[ip + 2];
|
U32 failIp = code[ip + 2];
|
||||||
|
|
||||||
Con::printf("%i: OP_ITER_BEGIN varName=%s failIp=%i", ip - 1, varName, failIp);
|
Con::printf("%i: OP_ITER_BEGIN_STR varRegister=%d failIp=%i isGlobal=%s", ip - 1, reg, failIp, "false");
|
||||||
|
|
||||||
ip += 3;
|
ip += 3;
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -66,9 +66,19 @@ struct IterStackRecord
|
||||||
/// If true, this is a foreach$ loop; if not, it's a foreach loop.
|
/// If true, this is a foreach$ loop; if not, it's a foreach loop.
|
||||||
bool mIsStringIter;
|
bool mIsStringIter;
|
||||||
|
|
||||||
/// The iterator variable.
|
/// True if the variable referenced is a global
|
||||||
|
bool mIsGlobalVariable;
|
||||||
|
|
||||||
|
union
|
||||||
|
{
|
||||||
|
|
||||||
|
/// The iterator variable if we are a global variable
|
||||||
Dictionary::Entry* mVariable;
|
Dictionary::Entry* mVariable;
|
||||||
|
|
||||||
|
/// The register variable if we are a local variable
|
||||||
|
S32 mRegister;
|
||||||
|
} mVar;
|
||||||
|
|
||||||
/// Information for an object iterator loop.
|
/// Information for an object iterator loop.
|
||||||
struct ObjectPos
|
struct ObjectPos
|
||||||
{
|
{
|
||||||
|
|
@ -1745,6 +1755,7 @@ ConsoleValue CodeBlock::exec(U32 ip, const char* functionName, Namespace* thisNa
|
||||||
{
|
{
|
||||||
if (nsEntry->mFunctionOffset)
|
if (nsEntry->mFunctionOffset)
|
||||||
{
|
{
|
||||||
|
// TODO: not make this strings only for returns.
|
||||||
ConsoleValue returnFromFn = nsEntry->mCode->exec(nsEntry->mFunctionOffset, fnName, nsEntry->mNamespace, callArgc, callArgv, false, nsEntry->mPackage);
|
ConsoleValue returnFromFn = nsEntry->mCode->exec(nsEntry->mFunctionOffset, fnName, nsEntry->mNamespace, callArgc, callArgv, false, nsEntry->mPackage);
|
||||||
STR.setStringValue(returnFromFn.getString());
|
STR.setStringValue(returnFromFn.getString());
|
||||||
}
|
}
|
||||||
|
|
@ -1954,12 +1965,22 @@ ConsoleValue CodeBlock::exec(U32 ip, const char* functionName, Namespace* thisNa
|
||||||
|
|
||||||
case OP_ITER_BEGIN:
|
case OP_ITER_BEGIN:
|
||||||
{
|
{
|
||||||
StringTableEntry varName = CodeToSTE(code, ip);
|
bool isGlobal = code[ip];
|
||||||
U32 failIp = code[ip + 2];
|
|
||||||
|
U32 failIp = code[ip + isGlobal ? 3 : 2];
|
||||||
|
|
||||||
IterStackRecord& iter = iterStack[_ITER];
|
IterStackRecord& iter = iterStack[_ITER];
|
||||||
|
iter.mIsGlobalVariable = isGlobal;
|
||||||
|
|
||||||
iter.mVariable = gEvalState.getCurrentFrame().add(varName);
|
if (isGlobal)
|
||||||
|
{
|
||||||
|
StringTableEntry varName = CodeToSTE(code, ip + 1);
|
||||||
|
iter.mVar.mVariable = gEvalState.globalVars.add(varName);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
iter.mVar.mRegister = code[ip + 1];
|
||||||
|
}
|
||||||
|
|
||||||
if (iter.mIsStringIter)
|
if (iter.mIsStringIter)
|
||||||
{
|
{
|
||||||
|
|
@ -1990,7 +2011,7 @@ ConsoleValue CodeBlock::exec(U32 ip, const char* functionName, Namespace* thisNa
|
||||||
|
|
||||||
STR.push();
|
STR.push();
|
||||||
|
|
||||||
ip += 3;
|
ip += isGlobal ? 4 : 3;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2026,11 +2047,21 @@ ConsoleValue CodeBlock::exec(U32 ip, const char* functionName, Namespace* thisNa
|
||||||
{
|
{
|
||||||
char savedChar = str[endIndex];
|
char savedChar = str[endIndex];
|
||||||
const_cast<char*>(str)[endIndex] = '\0'; // We are on the string stack so this is okay.
|
const_cast<char*>(str)[endIndex] = '\0'; // We are on the string stack so this is okay.
|
||||||
iter.mVariable->setStringValue(&str[startIndex]);
|
|
||||||
|
if (iter.mIsGlobalVariable)
|
||||||
|
iter.mVar.mVariable->setStringValue(&str[startIndex]);
|
||||||
|
else
|
||||||
|
gEvalState.setLocalStringVariable(iter.mVar.mRegister, &str[startIndex], endIndex - startIndex);
|
||||||
|
|
||||||
const_cast<char*>(str)[endIndex] = savedChar;
|
const_cast<char*>(str)[endIndex] = savedChar;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
iter.mVariable->setStringValue("");
|
{
|
||||||
|
if (iter.mIsGlobalVariable)
|
||||||
|
iter.mVar.mVariable->setStringValue("");
|
||||||
|
else
|
||||||
|
gEvalState.setLocalStringVariable(iter.mVar.mRegister, "", 0);
|
||||||
|
}
|
||||||
|
|
||||||
// Skip separator.
|
// Skip separator.
|
||||||
if (str[endIndex] != '\0')
|
if (str[endIndex] != '\0')
|
||||||
|
|
@ -2049,7 +2080,13 @@ ConsoleValue CodeBlock::exec(U32 ip, const char* functionName, Namespace* thisNa
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
iter.mVariable->setIntValue(set->at(index)->getId());
|
SimObjectId id = set->at(index)->getId();
|
||||||
|
|
||||||
|
if (iter.mIsGlobalVariable)
|
||||||
|
iter.mVar.mVariable->setIntValue(id);
|
||||||
|
else
|
||||||
|
gEvalState.setLocalIntVariable(iter.mVar.mRegister, id);
|
||||||
|
|
||||||
iter.mData.mObj.mIndex = index + 1;
|
iter.mData.mObj.mIndex = index + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -241,9 +241,11 @@ TEST(Script, Basic_Loop_Statements)
|
||||||
ASSERT_STREQ(forValue.getString(), "aaa");
|
ASSERT_STREQ(forValue.getString(), "aaa");
|
||||||
|
|
||||||
ConsoleValue forIfValue = RunScript(R"(
|
ConsoleValue forIfValue = RunScript(R"(
|
||||||
function t() {
|
function t()
|
||||||
|
{
|
||||||
%str = "";
|
%str = "";
|
||||||
for (%i = 0; %i < 5; %i++) {
|
for (%i = 0; %i < 5; %i++)
|
||||||
|
{
|
||||||
|
|
||||||
%loopValue = %i;
|
%loopValue = %i;
|
||||||
|
|
||||||
|
|
@ -261,6 +263,82 @@ TEST(Script, Basic_Loop_Statements)
|
||||||
ASSERT_STREQ(forIfValue.getString(), "0, 1, 2, 3, 4");
|
ASSERT_STREQ(forIfValue.getString(), "0, 1, 2, 3, 4");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(Script, ForEachLoop)
|
||||||
|
{
|
||||||
|
ConsoleValue forEach1 = RunScript(R"(
|
||||||
|
$theSimSet = new SimSet();
|
||||||
|
$theSimSet.add(new SimObject());
|
||||||
|
$theSimSet.add(new SimObject());
|
||||||
|
|
||||||
|
$counter = 0;
|
||||||
|
foreach ($obj in $theSimSet)
|
||||||
|
{
|
||||||
|
$counter++;
|
||||||
|
}
|
||||||
|
|
||||||
|
$theSimSet.delete();
|
||||||
|
|
||||||
|
return $counter;
|
||||||
|
)");
|
||||||
|
|
||||||
|
ASSERT_EQ(forEach1.getInt(), 2);
|
||||||
|
|
||||||
|
ConsoleValue forEach2 = RunScript(R"(
|
||||||
|
$counter = 0;
|
||||||
|
foreach$ ($word in "a b c d")
|
||||||
|
{
|
||||||
|
$counter++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $counter;
|
||||||
|
)");
|
||||||
|
|
||||||
|
ASSERT_EQ(forEach2.getInt(), 4);
|
||||||
|
|
||||||
|
ConsoleValue forEach3 = RunScript(R"(
|
||||||
|
function SimObject::addOne(%this)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function test()
|
||||||
|
{
|
||||||
|
%set = new SimSet();
|
||||||
|
%set.add(new SimObject());
|
||||||
|
%set.add(new SimObject());
|
||||||
|
|
||||||
|
%count = 0;
|
||||||
|
foreach (%obj in %set)
|
||||||
|
%count += %obj.addOne();
|
||||||
|
|
||||||
|
%set.delete();
|
||||||
|
|
||||||
|
return %count;
|
||||||
|
}
|
||||||
|
|
||||||
|
return test();
|
||||||
|
)");
|
||||||
|
|
||||||
|
ASSERT_EQ(forEach3.getInt(), 2);
|
||||||
|
|
||||||
|
ConsoleValue forEach4 = RunScript(R"(
|
||||||
|
function test()
|
||||||
|
{
|
||||||
|
%string = "a b c d e";
|
||||||
|
|
||||||
|
%count = 0;
|
||||||
|
foreach$ (%word in %string)
|
||||||
|
%count++;
|
||||||
|
|
||||||
|
return %count;
|
||||||
|
}
|
||||||
|
|
||||||
|
return test();
|
||||||
|
)");
|
||||||
|
|
||||||
|
ASSERT_EQ(forEach4.getInt(), 5);
|
||||||
|
}
|
||||||
|
|
||||||
TEST(Script, TorqueScript_Array_Testing)
|
TEST(Script, TorqueScript_Array_Testing)
|
||||||
{
|
{
|
||||||
ConsoleValue value = RunScript(R"(
|
ConsoleValue value = RunScript(R"(
|
||||||
|
|
@ -281,7 +359,8 @@ TEST(Script, TorqueScript_Array_Testing)
|
||||||
TEST(Script, Basic_SimObject)
|
TEST(Script, Basic_SimObject)
|
||||||
{
|
{
|
||||||
ConsoleValue object = RunScript(R"(
|
ConsoleValue object = RunScript(R"(
|
||||||
return new SimObject(FudgeCollector) {
|
return new SimObject(FudgeCollector)
|
||||||
|
{
|
||||||
fudge = "Chocolate";
|
fudge = "Chocolate";
|
||||||
};
|
};
|
||||||
)");
|
)");
|
||||||
|
|
@ -329,7 +408,8 @@ TEST(Script, Basic_Package)
|
||||||
{
|
{
|
||||||
ConsoleValue value = RunScript(R"(
|
ConsoleValue value = RunScript(R"(
|
||||||
function a() { return 3; }
|
function a() { return 3; }
|
||||||
package overrides {
|
package overrides
|
||||||
|
{
|
||||||
function a() { return 5; }
|
function a() { return 5; }
|
||||||
};
|
};
|
||||||
return a();
|
return a();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue