DIRECTOR: LINGO: Reimplement loops

This commit is contained in:
djsrv 2021-06-16 22:16:08 -04:00 committed by D.J. Servilla
parent 03e6a56c5c
commit d9a1057cd6
5 changed files with 852 additions and 503 deletions

View File

@ -35,6 +35,10 @@ struct PropertyNode;
struct InstanceNode;
struct IfStmtNode;
struct IfElseStmtNode;
struct RepeatWhileNode;
struct RepeatWithToNode;
struct NextRepeatNode;
struct ExitRepeatNode;
struct IntNode;
struct FloatNode;
struct SymbolNode;
@ -68,6 +72,10 @@ enum NodeType {
kInstanceNode,
kIfStmtNode,
kIfElseStmtNode,
kRepeatWhileNode,
kRepeatWithToNode,
kNextRepeatNode,
kExitRepeatNode,
kIntNode,
kFloatNode,
kSymbolNode,
@ -95,6 +103,10 @@ public:
virtual void visitInstanceNode(InstanceNode *node) = 0;
virtual void visitIfStmtNode(IfStmtNode *node) = 0;
virtual void visitIfElseStmtNode(IfElseStmtNode *node) = 0;
virtual void visitRepeatWhileNode(RepeatWhileNode *node) = 0;
virtual void visitRepeatWithToNode(RepeatWithToNode *node) = 0;
virtual void visitNextRepeatNode(NextRepeatNode *node) = 0;
virtual void visitExitRepeatNode(ExitRepeatNode *node) = 0;
virtual void visitIntNode(IntNode *node) = 0;
virtual void visitFloatNode(FloatNode *node) = 0;
virtual void visitSymbolNode(SymbolNode *node) = 0;
@ -112,8 +124,9 @@ struct Node {
NodeType type;
bool isExpression;
bool isStatement;
bool isLoop;
Node(NodeType t) : type(t), isExpression(false), isStatement(false) {}
Node(NodeType t) : type(t), isExpression(false), isStatement(false), isLoop(false) {}
virtual ~Node() {}
virtual void accept(NodeVisitor *visitor) = 0;
};
@ -136,6 +149,18 @@ struct StmtNode : Node {
virtual ~StmtNode() {}
};
/* LoopNode */
struct LoopNode : StmtNode {
Common::Array<uint> nextRepeats;
Common::Array<uint> exitRepeats;
LoopNode(NodeType t) : StmtNode(t) {
isLoop = true;
}
virtual ~LoopNode() {}
};
/* ScriptNode */
struct ScriptNode : Node {
@ -281,6 +306,65 @@ struct IfElseStmtNode : StmtNode {
}
};
/* RepeatWhileNode */
struct RepeatWhileNode : LoopNode {
Node *cond;
NodeList *stmts;
RepeatWhileNode(Node *condIn, NodeList *stmtsIn)
: LoopNode(kRepeatWhileNode), cond(condIn), stmts(stmtsIn) {}
virtual ~RepeatWhileNode() {
delete cond;
deleteList(stmts);
}
virtual void accept(NodeVisitor *visitor) {
visitor->visitRepeatWhileNode(this);
}
};
/* RepeatWithToNode */
struct RepeatWithToNode : LoopNode {
Common::String *var;
Node *start;
bool down;
Node *end;
NodeList *stmts;
RepeatWithToNode(Common::String *varIn, Node *startIn, bool downIn, Node *endIn, NodeList *stmtsIn)
: LoopNode(kRepeatWithToNode), var(varIn), start(startIn), down(downIn), end(endIn), stmts(stmtsIn) {}
virtual ~RepeatWithToNode() {
delete var;
delete start;
delete end;
deleteList(stmts);
}
virtual void accept(NodeVisitor *visitor) {
visitor->visitRepeatWithToNode(this);
}
};
/* NextRepeatNode */
struct NextRepeatNode : StmtNode {
NextRepeatNode() : StmtNode(kNextRepeatNode) {}
virtual ~NextRepeatNode() {}
virtual void accept(NodeVisitor *visitor) {
visitor->visitNextRepeatNode(this);
}
};
/* ExitRepeatNode */
struct ExitRepeatNode : StmtNode {
ExitRepeatNode() : StmtNode(kExitRepeatNode) {}
virtual ~ExitRepeatNode() {}
virtual void accept(NodeVisitor *visitor) {
visitor->visitExitRepeatNode(this);
}
};
/* IntNode */
struct IntNode : ExprNode {

View File

@ -66,6 +66,7 @@ LingoCompiler::LingoCompiler() {
_lines[0] = _lines[1] = _lines[2] = nullptr;
_inFactory = false;
_currentLoop = nullptr;
_hadError = false;
}
@ -251,6 +252,15 @@ int LingoCompiler::codeFunc(Common::String *s, int numpar) {
return ret;
}
VarType LingoCompiler::globalCheck() {
// If in a definition, assume variables are local unless
// they were declared global with `global varname`
if (_indef) {
return kVarLocal;
}
return kVarGlobal;
}
void LingoCompiler::registerMethodVar(const Common::String &name, VarType type) {
if (!_methodVars->contains(name)) {
(*_methodVars)[name] = type;
@ -274,6 +284,24 @@ void LingoCompiler::registerFactory(Common::String &name) {
}
}
void LingoCompiler::updateLoopJumps(uint nextTargetPos, uint exitTargetPos) {
if (!_currentLoop)
return;
for (uint i = 0; i < _currentLoop->nextRepeats.size(); i++) {
uint nextRepeatPos = _currentLoop->nextRepeats[i];
inst jmpOffset = 0;
WRITE_UINT32(&jmpOffset, nextTargetPos - nextRepeatPos);
(*_currentAssembly)[nextRepeatPos + 1] = jmpOffset;
}
for (uint i = 0; i < _currentLoop->exitRepeats.size(); i++) {
uint exitRepeatPos = _currentLoop->exitRepeats[i];
inst jmpOffset = 0;
WRITE_UINT32(&jmpOffset, exitTargetPos - exitRepeatPos);
(*_currentAssembly)[exitRepeatPos + 1] = jmpOffset;
}
}
void LingoCompiler::parseMenu(const char *code) {
warning("STUB: parseMenu");
}
@ -433,6 +461,112 @@ void LingoCompiler::visitIfElseStmtNode(IfElseStmtNode *node) {
(*_currentAssembly)[jmpPos + 1] = jmpOffset;
}
/* RepeatWhileNode */
void LingoCompiler::visitRepeatWhileNode(RepeatWhileNode *node) {
LoopNode *prevLoop = _currentLoop;
_currentLoop = node;
uint startPos = _currentAssembly->size();
compile(node->cond);
uint jzPos = _currentAssembly->size();
code2(LC::c_jumpifz, 0);
compileList(node->stmts);
uint jmpPos = _currentAssembly->size();
code2(LC::c_jump, 0);
uint endPos = _currentAssembly->size();
inst jzOffset = 0;
WRITE_UINT32(&jzOffset, endPos - jzPos);
(*_currentAssembly)[jzPos + 1] = jzOffset;
inst jmpOffset = 0;
WRITE_UINT32(&jmpOffset, startPos - jmpPos);
(*_currentAssembly)[jmpPos + 1] = jmpOffset;
updateLoopJumps(jmpPos, endPos);
_currentLoop = prevLoop;
}
/* RepeatWithToNode */
void LingoCompiler::visitRepeatWithToNode(RepeatWithToNode *node) {
LoopNode *prevLoop = _currentLoop;
_currentLoop = node;
registerMethodVar(*node->var, globalCheck());
compile(node->start);
code1(LC::c_varpush);
codeString(node->var->c_str());
code1(LC::c_assign);
uint startPos = _currentAssembly->size();
code1(LC::c_eval);
codeString(node->var->c_str());
compile(node->end);
if (node->down) {
code1(LC::c_ge);
} else {
code1(LC::c_le);
}
uint jzPos = _currentAssembly->size();
code2(LC::c_jumpifz, 0);
compileList(node->stmts);
uint incrementPos = _currentAssembly->size();
code1(LC::c_eval);
codeString(node->var->c_str());
code1(LC::c_intpush);
codeInt(1);
if (node->down) {
code1(LC::c_sub);
} else {
code1(LC::c_add);
}
code1(LC::c_varpush);
codeString(node->var->c_str());
code1(LC::c_assign);
uint jmpPos = _currentAssembly->size();
code2(LC::c_jump, 0);
uint endPos = _currentAssembly->size();
inst jzOffset = 0;
WRITE_UINT32(&jzOffset, endPos - jzPos);
(*_currentAssembly)[jzPos + 1] = jzOffset;
inst jmpOffset = 0;
WRITE_UINT32(&jmpOffset, startPos - jmpPos);
(*_currentAssembly)[jmpPos + 1] = jmpOffset;
updateLoopJumps(incrementPos, endPos);
_currentLoop = prevLoop;
}
/* NextRepeatNode */
void LingoCompiler::visitNextRepeatNode(NextRepeatNode *node) {
if (_currentLoop) {
_currentLoop->nextRepeats.push_back(_currentAssembly->size());
code2(LC::c_jump, 0);
} else {
warning("# LINGO: next repeat not inside repeat loop");
}
}
/* ExitRepeatNode */
void LingoCompiler::visitExitRepeatNode(ExitRepeatNode *node) {
if (_currentLoop) {
_currentLoop->exitRepeats.push_back(_currentAssembly->size());
code2(LC::c_jump, 0);
} else {
warning("# LINGO: exit repeat not inside repeat loop");
}
}
/* IntNode */
void LingoCompiler::visitIntNode(IntNode *node) {

View File

@ -48,7 +48,9 @@ public:
int codeInt(int val);
int codeString(const char *s);
void registerFactory(Common::String &s);
VarType globalCheck();
void registerMethodVar(const Common::String &name, VarType type);
void updateLoopJumps(uint nextTargetPos, uint exitTargetPos);
LingoArchive *_assemblyArchive;
ScriptContext *_assemblyContext;
@ -60,6 +62,7 @@ public:
uint _bytenumber;
const char *_lines[3];
bool _inFactory;
LoopNode *_currentLoop;
Common::HashMap<Common::String, VarType, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> *_methodVars;
Common::HashMap<Common::String, VarType, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> *_methodVarsStash;
@ -79,6 +82,10 @@ public:
virtual void visitInstanceNode(InstanceNode *node);
virtual void visitIfStmtNode(IfStmtNode *node);
virtual void visitIfElseStmtNode(IfElseStmtNode *node);
virtual void visitRepeatWhileNode(RepeatWhileNode *node);
virtual void visitRepeatWithToNode(RepeatWithToNode *node);
virtual void visitNextRepeatNode(NextRepeatNode *node);
virtual void visitExitRepeatNode(ExitRepeatNode *node);
virtual void visitIntNode(IntNode *node);
virtual void visitFloatNode(FloatNode *node);
virtual void visitSymbolNode(SymbolNode *node);

File diff suppressed because it is too large Load Diff

View File

@ -164,7 +164,7 @@ static void checkEnd(Common::String *token, Common::String *expect, bool require
%type<idlist> idlist nonemptyidlist
// STATEMENT
%type<node> stmt stmtoneliner proc definevars ifstmt ifelsestmt
%type<node> stmt stmtoneliner proc definevars ifstmt ifelsestmt loop
%type<nodelist> stmtlist nonemptystmtlist
%type<node> stmtlistline
@ -354,6 +354,7 @@ nonemptyidlist: ID[item] {
stmt: stmtoneliner
| ifstmt
| ifelsestmt
| loop
;
stmtoneliner: proc
@ -362,6 +363,8 @@ stmtoneliner: proc
proc: ID '(' exprlist[args] ')' '\n' { $$ = new CmdNode($ID, $args); }
| ID exprlist[args] '\n' { $$ = new CmdNode($ID, $args); }
| tNEXT tREPEAT { $$ = new NextRepeatNode(); }
| tEXIT tREPEAT { $$ = new ExitRepeatNode(); }
;
definevars: tGLOBAL idlist '\n' { $$ = new GlobalNode($idlist); }
@ -395,6 +398,14 @@ ifelsestmt: tIF expr tTHEN stmt[stmt1] tELSE stmt[stmt2] {
$$ = new IfElseStmtNode($expr, $stmtlist1, $stmtlist2); }
;
loop: tREPEAT tWHILE expr '\n' stmtlist tENDREPEAT '\n' {
$$ = new RepeatWhileNode($expr, $stmtlist); }
| tREPEAT tWITH ID tEQ expr[start] tTO expr[end] '\n' stmtlist tENDREPEAT '\n' {
$$ = new RepeatWithToNode($ID, $start, false, $end, $stmtlist); }
| tREPEAT tWITH ID tEQ expr[start] tDOWN tTO expr[end] '\n' stmtlist tENDREPEAT '\n' {
$$ = new RepeatWithToNode($ID, $start, true, $end, $stmtlist); }
;
stmtlist: /* empty */ { $$ = new NodeList; }
| nonemptystmtlist
;