mirror of
https://github.com/openmoh/openmohaa.git
synced 2025-04-28 13:47:58 +03:00
1641 lines
42 KiB
C++
1641 lines
42 KiB
C++
/*
|
|
===========================================================================
|
|
Copyright (C) 2015 the OpenMoHAA team
|
|
|
|
This file is part of OpenMoHAA source code.
|
|
|
|
OpenMoHAA source code is free software; you can redistribute it
|
|
and/or modify it under the terms of the GNU General Public License as
|
|
published by the Free Software Foundation; either version 2 of the License,
|
|
or (at your option) any later version.
|
|
|
|
OpenMoHAA source code is distributed in the hope that it will be
|
|
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with OpenMoHAA source code; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
===========================================================================
|
|
*/
|
|
|
|
// compiler.cpp : Parse, then compile to op-codes.
|
|
|
|
#include "scriptcompiler.h"
|
|
#include "scriptvm.h"
|
|
#include "../fgame/level.h"
|
|
#include "../fgame/parm.h"
|
|
#include "../fgame/game.h"
|
|
#include "../fgame/scriptmaster.h"
|
|
#include "../fgame/scriptthread.h"
|
|
#include "scriptclass.h"
|
|
#include "scriptexception.h"
|
|
#include "../parser/parsetree.h"
|
|
#include "../parser/generated/yyParser.hpp"
|
|
#include "../parser/generated/yyLexer.h"
|
|
|
|
ScriptCompiler Compiler;
|
|
int ScriptCompiler::current_label;
|
|
|
|
ScriptCompiler::ScriptCompiler()
|
|
{
|
|
Reset();
|
|
}
|
|
|
|
void ScriptCompiler::Reset()
|
|
{
|
|
code_pos = NULL;
|
|
code_ptr = NULL;
|
|
prog_ptr = NULL;
|
|
prog_end_ptr = NULL;
|
|
|
|
for (int i = 0; i < BREAK_JUMP_LOCATION_COUNT; i++) {
|
|
apucBreakJumpLocations[i] = 0;
|
|
apucContinueJumpLocations[i] = 0;
|
|
prev_opcodes[i].opcode = 0;
|
|
prev_opcodes[i].VarStackOffset = 0;
|
|
}
|
|
|
|
iBreakJumpLocCount = 0;
|
|
iContinueJumpLocCount = 0;
|
|
|
|
m_iHasExternal = 0;
|
|
m_iInternalMaxVarStackOffset = 0;
|
|
m_iMaxCallStackOffset = 0;
|
|
m_iMaxExternalVarStackOffset = 0;
|
|
m_iVarStackOffset = 0;
|
|
|
|
bCanBreak = false;
|
|
bCanContinue = false;
|
|
|
|
prev_opcode_pos = 0;
|
|
}
|
|
|
|
unsigned char ScriptCompiler::PrevOpcode()
|
|
{
|
|
return prev_opcodes[prev_opcode_pos].opcode;
|
|
}
|
|
|
|
char ScriptCompiler::PrevVarStackOffset()
|
|
{
|
|
return prev_opcodes[prev_opcode_pos].VarStackOffset;
|
|
}
|
|
|
|
void ScriptCompiler::AbsorbPrevOpcode()
|
|
{
|
|
m_iVarStackOffset -= PrevVarStackOffset();
|
|
|
|
code_pos -= OpcodeLength(PrevOpcode());
|
|
|
|
if (!prev_opcode_pos) {
|
|
prev_opcode_pos = 100;
|
|
}
|
|
|
|
prev_opcode_pos--;
|
|
}
|
|
|
|
void ScriptCompiler::ClearPrevOpcode()
|
|
{
|
|
prev_opcodes[prev_opcode_pos].opcode = OP_PREVIOUS;
|
|
}
|
|
|
|
void ScriptCompiler::AddBreakJumpLocation(unsigned char *pos)
|
|
{
|
|
if (iBreakJumpLocCount < BREAK_JUMP_LOCATION_COUNT) {
|
|
apucBreakJumpLocations[iBreakJumpLocCount++] = pos;
|
|
} else {
|
|
iBreakJumpLocCount = 0;
|
|
CompileError(-1, "Increase BREAK_JUMP_LOCATION_COUNT and recompile.\n");
|
|
}
|
|
}
|
|
|
|
void ScriptCompiler::AddContinueJumpLocation(unsigned char *pos)
|
|
{
|
|
if (iContinueJumpLocCount < CONTINUE_JUMP_LOCATION_COUNT) {
|
|
apucContinueJumpLocations[iContinueJumpLocCount++] = pos;
|
|
} else {
|
|
iContinueJumpLocCount = 0;
|
|
CompileError(-1, "Increase CONTINUE_JUMP_LOCATION_COUNT and recompile.\n");
|
|
}
|
|
}
|
|
|
|
void ScriptCompiler::AddJumpLocation(unsigned char *pos)
|
|
{
|
|
unsigned int offset = code_pos - sizeof(unsigned int) - pos;
|
|
|
|
SetOpcodeValue(offset);
|
|
ClearPrevOpcode();
|
|
}
|
|
|
|
void ScriptCompiler::AddJumpBackLocation(unsigned char *pos)
|
|
{
|
|
int offset = (code_pos - pos);
|
|
|
|
EmitOpcodeValue(offset, sizeof(unsigned int));
|
|
ClearPrevOpcode();
|
|
}
|
|
|
|
void ScriptCompiler::AddJumpToLocation(unsigned char *pos)
|
|
{
|
|
int offset = (pos - code_pos - 1);
|
|
|
|
EmitOpcodeValue(offset, sizeof(unsigned int));
|
|
ClearPrevOpcode();
|
|
}
|
|
|
|
bool ScriptCompiler::BuiltinReadVariable(unsigned int sourcePos, int type, int eventnum)
|
|
{
|
|
ClassDef *c;
|
|
EventDef *def;
|
|
|
|
switch (type) {
|
|
case method_game:
|
|
c = Game::classinfostatic();
|
|
break;
|
|
|
|
case method_level:
|
|
c = Level::classinfostatic();
|
|
break;
|
|
|
|
case method_local:
|
|
c = ScriptThread::classinfostatic();
|
|
break;
|
|
|
|
case method_parm:
|
|
c = Parm::classinfostatic();
|
|
break;
|
|
|
|
case method_group:
|
|
c = ScriptClass::classinfostatic();
|
|
break;
|
|
|
|
default:
|
|
return true;
|
|
}
|
|
|
|
def = c->GetDef(eventnum);
|
|
|
|
if (def) {
|
|
if (def->type == EV_GETTER) {
|
|
return true;
|
|
} else {
|
|
CompileError(sourcePos, "Cannot get a write-only variable");
|
|
return false;
|
|
}
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool ScriptCompiler::BuiltinWriteVariable(unsigned int sourcePos, int type, int eventnum)
|
|
{
|
|
ClassDef *c;
|
|
EventDef *def;
|
|
|
|
switch (type) {
|
|
case method_game:
|
|
c = Game::classinfostatic();
|
|
break;
|
|
|
|
case method_level:
|
|
c = Level::classinfostatic();
|
|
break;
|
|
|
|
case method_local:
|
|
c = ScriptThread::classinfostatic();
|
|
break;
|
|
|
|
case method_parm:
|
|
c = Parm::classinfostatic();
|
|
break;
|
|
|
|
case method_group:
|
|
c = ScriptClass::classinfostatic();
|
|
break;
|
|
|
|
default:
|
|
return true;
|
|
}
|
|
|
|
def = c->GetDef(eventnum);
|
|
|
|
if (def) {
|
|
if (def->type == EV_SETTER) {
|
|
return true;
|
|
} else {
|
|
CompileError(sourcePos, "Cannot get a read-only variable");
|
|
return false;
|
|
}
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void ScriptCompiler::EmitAssignmentStatement(sval_t lhs, unsigned int sourcePos)
|
|
{
|
|
int eventnum;
|
|
sval_t listener_val;
|
|
char *name = lhs.node[2].stringValue;
|
|
str name2 = name;
|
|
name2.tolower();
|
|
|
|
if (lhs.node[0].type != sval_field) {
|
|
if (lhs.node[0].type == sval_array) {
|
|
EmitRef(lhs.node[1], sourcePos);
|
|
EmitValue(lhs.node[2]);
|
|
EmitOpcode(OP_LOAD_ARRAY_VAR, lhs.node[3].sourcePosValue);
|
|
|
|
return;
|
|
} else {
|
|
CompileError(sourcePos, "bad lvalue: %d (expecting field or array)", lhs.node[0].type);
|
|
}
|
|
}
|
|
|
|
unsigned int index = Director.AddString(name);
|
|
|
|
eventnum = Event::FindSetterEventNum(name2);
|
|
|
|
if (eventnum) {
|
|
index = Director.GetString(name2);
|
|
}
|
|
|
|
listener_val = lhs.node[1];
|
|
|
|
if (listener_val.node[0].type != sval_store_method
|
|
|| (eventnum && BuiltinWriteVariable(sourcePos, listener_val.node[1].byteValue, eventnum))) {
|
|
EmitValue(listener_val);
|
|
EmitOpcode(OP_LOAD_FIELD_VAR, sourcePos);
|
|
} else {
|
|
EmitOpcode(OP_LOAD_GAME_VAR + listener_val.node[1].byteValue, sourcePos);
|
|
}
|
|
|
|
EmitOpcodeValue(index, sizeof(unsigned int));
|
|
}
|
|
|
|
void ScriptCompiler::EmitBoolJumpFalse(unsigned int sourcePos)
|
|
{
|
|
if (PrevOpcode() == OP_UN_CAST_BOOLEAN) {
|
|
AbsorbPrevOpcode();
|
|
EmitOpcode(OP_VAR_JUMP_FALSE4, sourcePos);
|
|
} else {
|
|
EmitOpcode(OP_BOOL_JUMP_FALSE4, sourcePos);
|
|
}
|
|
}
|
|
|
|
void ScriptCompiler::EmitBoolJumpTrue(unsigned int sourcePos)
|
|
{
|
|
if (PrevOpcode() == OP_UN_CAST_BOOLEAN) {
|
|
AbsorbPrevOpcode();
|
|
EmitOpcode(OP_VAR_JUMP_TRUE4, sourcePos);
|
|
} else {
|
|
EmitOpcode(OP_BOOL_JUMP_TRUE4, sourcePos);
|
|
}
|
|
}
|
|
|
|
void ScriptCompiler::EmitBoolNot(unsigned int sourcePos)
|
|
{
|
|
int prev = PrevOpcode();
|
|
|
|
if (prev == OP_BOOL_STORE_TRUE) {
|
|
AbsorbPrevOpcode();
|
|
return EmitOpcode(OP_BOOL_STORE_FALSE, sourcePos);
|
|
} else if (prev > OP_BOOL_STORE_TRUE && prev == OP_BOOL_UN_NOT) {
|
|
AbsorbPrevOpcode();
|
|
return EmitNil(sourcePos);
|
|
} else if (prev == OP_BOOL_STORE_FALSE) {
|
|
AbsorbPrevOpcode();
|
|
return EmitOpcode(OP_BOOL_STORE_TRUE, sourcePos);
|
|
}
|
|
|
|
return EmitOpcode(OP_BOOL_UN_NOT, sourcePos);
|
|
}
|
|
|
|
void ScriptCompiler::EmitBoolToVar(unsigned int sourcePos)
|
|
{
|
|
if (PrevOpcode() == OP_UN_CAST_BOOLEAN) {
|
|
AbsorbPrevOpcode();
|
|
EmitOpcode(OP_UN_CAST_BOOLEAN, sourcePos);
|
|
} else {
|
|
if (showopcodes->integer) {
|
|
glbs.DPrintf("\t\t%08d: %s (%d)\n", code_pos - code_ptr, OpcodeName(OP_BOOL_TO_VAR), m_iVarStackOffset);
|
|
}
|
|
|
|
int pos = (prev_opcode_pos + 1) % 100;
|
|
|
|
prev_opcode_pos = pos;
|
|
prev_opcodes[pos].opcode = OP_BOOL_TO_VAR;
|
|
prev_opcodes[pos].VarStackOffset = 0;
|
|
prev_opcodes[(pos + 1) % 100].opcode = OP_PREVIOUS;
|
|
}
|
|
}
|
|
|
|
void ScriptCompiler::EmitBreak(unsigned int sourcePos)
|
|
{
|
|
if (bCanBreak) {
|
|
EmitOpcode(OP_JUMP4, sourcePos);
|
|
unsigned char *pos = code_pos;
|
|
code_pos += 4;
|
|
ClearPrevOpcode();
|
|
|
|
AddBreakJumpLocation(pos);
|
|
} else {
|
|
CompileError(sourcePos, "illegal break\n");
|
|
}
|
|
}
|
|
|
|
void ScriptCompiler::EmitCatch(sval_t val, unsigned char *try_begin_code_pos, unsigned int sourcePos)
|
|
{
|
|
unsigned char *old_code_pos;
|
|
StateScript *m_oldStateScript;
|
|
|
|
EmitOpcode(OP_JUMP4, sourcePos);
|
|
|
|
old_code_pos = code_pos;
|
|
code_pos += sizeof(unsigned int);
|
|
|
|
ClearPrevOpcode();
|
|
|
|
m_oldStateScript = stateScript;
|
|
stateScript = script->CreateCatchStateScript(try_begin_code_pos, code_pos);
|
|
|
|
EmitValue(val);
|
|
|
|
stateScript = m_oldStateScript;
|
|
|
|
AddJumpLocation(old_code_pos);
|
|
}
|
|
|
|
void ScriptCompiler::EmitConstArray(sval_t lhs, sval_t rhs, unsigned int sourcePos)
|
|
{
|
|
int iCount = 1;
|
|
|
|
while (1) {
|
|
iCount++;
|
|
|
|
EmitValue(lhs);
|
|
|
|
if (rhs.node[0].type != sval_constarray) {
|
|
break;
|
|
}
|
|
|
|
lhs = rhs.node[1];
|
|
rhs = rhs.node[2];
|
|
}
|
|
|
|
EmitValue(rhs);
|
|
EmitConstArrayOpcode(iCount);
|
|
}
|
|
|
|
void ScriptCompiler::EmitConstArrayOpcode(int iCount)
|
|
{
|
|
SetOpcodeVarStackOffset(OP_LOAD_CONST_ARRAY1, 1 - iCount);
|
|
EmitOpcode(OP_LOAD_CONST_ARRAY1, -1);
|
|
|
|
EmitOpcodeValue(iCount, sizeof(short));
|
|
}
|
|
|
|
void ScriptCompiler::EmitContinue(unsigned int sourcePos)
|
|
{
|
|
if (bCanContinue) {
|
|
EmitOpcode(OP_JUMP4, sourcePos);
|
|
unsigned char *pos = code_pos;
|
|
code_pos += sizeof(unsigned int);
|
|
ClearPrevOpcode();
|
|
|
|
AddContinueJumpLocation(pos);
|
|
} else {
|
|
CompileError(sourcePos, "illegal continue\n");
|
|
}
|
|
}
|
|
|
|
void ScriptCompiler::EmitDoWhileJump(sval_t while_stmt, sval_t while_expr, unsigned int sourcePos)
|
|
{
|
|
unsigned char *pos = code_pos;
|
|
int label1, label2;
|
|
|
|
if (showopcodes->integer) {
|
|
label1 = current_label++;
|
|
glbs.DPrintf("<LABEL%d>:\n", label1);
|
|
}
|
|
|
|
ClearPrevOpcode();
|
|
|
|
bool old_bCanBreak = bCanBreak;
|
|
bool old_bCanContinue = bCanContinue;
|
|
int breakCount = iBreakJumpLocCount;
|
|
int continueCount = iContinueJumpLocCount;
|
|
|
|
bCanBreak = true;
|
|
bCanContinue = true;
|
|
|
|
EmitValue(while_stmt);
|
|
|
|
ProcessContinueJumpLocations(continueCount);
|
|
|
|
bCanContinue = old_bCanContinue;
|
|
|
|
EmitValue(while_expr);
|
|
EmitVarToBool(sourcePos);
|
|
|
|
label2 = EmitNot(sourcePos);
|
|
|
|
unsigned char *jmp = code_pos;
|
|
|
|
code_pos += sizeof(unsigned int);
|
|
|
|
if (showopcodes->integer) {
|
|
glbs.DPrintf("JUMP_BACK4 <LABEL%d>\n", label1);
|
|
}
|
|
|
|
EmitJumpBack(pos, sourcePos);
|
|
|
|
ClearPrevOpcode();
|
|
|
|
if (showopcodes->integer) {
|
|
glbs.DPrintf("<LABEL%d>:\n", label2);
|
|
}
|
|
|
|
AddJumpLocation(jmp);
|
|
|
|
ProcessBreakJumpLocations(breakCount);
|
|
|
|
bCanBreak = old_bCanBreak;
|
|
}
|
|
|
|
void ScriptCompiler::EmitEof(unsigned int sourcePos)
|
|
{
|
|
if (PrevOpcode()) {
|
|
EmitOpcode(OP_DONE, -1);
|
|
}
|
|
}
|
|
|
|
void ScriptCompiler::EmitField(sval_t listener_val, sval_t field_val, unsigned int sourcePos)
|
|
{
|
|
unsigned int eventnum = 0;
|
|
unsigned int index = -1;
|
|
unsigned int prev_index;
|
|
|
|
if (field_val.node[0].stringValue) {
|
|
str name = field_val.stringValue;
|
|
str name2 = field_val.stringValue;
|
|
name2.tolower();
|
|
|
|
index = Director.AddString(name);
|
|
eventnum = Event::FindGetterEventNum(name2);
|
|
|
|
if (eventnum) {
|
|
index = Director.GetString(name2);
|
|
}
|
|
}
|
|
|
|
prev_index = GetOpcodeValue<unsigned int>(sizeof(unsigned int), sizeof(unsigned int));
|
|
|
|
if (listener_val.node[0].type != sval_store_method
|
|
|| (eventnum && BuiltinReadVariable(sourcePos, listener_val.node[1].byteValue, eventnum))) {
|
|
EmitValue(listener_val);
|
|
EmitOpcode(OP_STORE_FIELD, sourcePos);
|
|
EmitOpcodeValue(index, sizeof(unsigned int));
|
|
} else if (PrevOpcode() != (OP_LOAD_GAME_VAR + listener_val.node[1].byteValue) || prev_index != index) {
|
|
EmitOpcode(OP_STORE_GAME_VAR + listener_val.node[1].byteValue, sourcePos);
|
|
EmitOpcodeValue(index, sizeof(unsigned int));
|
|
} else {
|
|
AbsorbPrevOpcode();
|
|
EmitOpcode(OP_LOAD_STORE_GAME_VAR + listener_val.node[1].byteValue, sourcePos);
|
|
code_pos += sizeof(unsigned int);
|
|
}
|
|
}
|
|
|
|
void ScriptCompiler::EmitFloat(float value, unsigned int sourcePos)
|
|
{
|
|
// ley0k: optimization
|
|
if (value == (float)(int)value) {
|
|
return EmitInteger((unsigned int)value, sourcePos);
|
|
}
|
|
|
|
EmitOpcode(OP_STORE_FLOAT, sourcePos);
|
|
|
|
EmitOpcodeValue(value, sizeof(float));
|
|
}
|
|
|
|
void ScriptCompiler::EmitFunc1(int opcode, unsigned int sourcePos)
|
|
{
|
|
if (opcode == OP_UN_MINUS) {
|
|
ScriptVariable var;
|
|
|
|
if (EvalPrevValue(var)) {
|
|
AbsorbPrevOpcode();
|
|
var.minus();
|
|
|
|
return EmitValue(var, sourcePos);
|
|
}
|
|
}
|
|
|
|
EmitOpcode(opcode, sourcePos);
|
|
}
|
|
|
|
void ScriptCompiler::EmitFunction(int iParamCount, sval_t val, unsigned int sourcePos)
|
|
{
|
|
char *p = val.stringValue;
|
|
|
|
str filename;
|
|
str label;
|
|
bool found = false;
|
|
|
|
while (*p) {
|
|
if (p[0] == ':' && p[1] == ':') {
|
|
p[0] = 0;
|
|
|
|
filename = val.stringValue;
|
|
label = p + 2;
|
|
|
|
found = true;
|
|
|
|
break;
|
|
}
|
|
|
|
p++;
|
|
}
|
|
|
|
SetOpcodeVarStackOffset(OP_FUNC, -iParamCount);
|
|
EmitOpcode(OP_FUNC, sourcePos);
|
|
|
|
if (!found) {
|
|
label = val.stringValue;
|
|
|
|
EmitOpcodeValue(false, sizeof(bool));
|
|
EmitOpcodeValue(Director.AddString(label), sizeof(unsigned int));
|
|
} else {
|
|
EmitOpcodeValue(true, sizeof(bool));
|
|
EmitOpcodeValue(Director.AddString(filename), sizeof(unsigned int));
|
|
EmitOpcodeValue(Director.AddString(label), sizeof(unsigned int));
|
|
}
|
|
|
|
EmitOpcodeValue(iParamCount, sizeof(uint8_t));
|
|
}
|
|
|
|
void ScriptCompiler::EmitIfElseJump(sval_t if_stmt, sval_t else_stmt, unsigned int sourcePos)
|
|
{
|
|
unsigned char *jmp1, *jmp2;
|
|
int label1, label2;
|
|
|
|
label1 = EmitNot(sourcePos);
|
|
jmp1 = code_pos;
|
|
code_pos += sizeof(unsigned int);
|
|
ClearPrevOpcode();
|
|
|
|
EmitValue(if_stmt);
|
|
|
|
if (showopcodes->integer) {
|
|
label2 = current_label++;
|
|
glbs.DPrintf("JUMP <LABEL%d>\n", label2);
|
|
}
|
|
|
|
EmitOpcode(OP_JUMP4, sourcePos);
|
|
jmp2 = code_pos;
|
|
code_pos += sizeof(unsigned int);
|
|
|
|
ClearPrevOpcode();
|
|
|
|
if (showopcodes->integer) {
|
|
glbs.DPrintf("<LABEL%d>:\n", label1);
|
|
}
|
|
|
|
AddJumpLocation(jmp1);
|
|
EmitValue(else_stmt);
|
|
|
|
if (showopcodes->integer) {
|
|
glbs.DPrintf("<LABEL%d>:\n", label1);
|
|
}
|
|
|
|
AddJumpLocation(jmp2);
|
|
}
|
|
|
|
void ScriptCompiler::EmitIfJump(sval_t if_stmt, unsigned int sourcePos)
|
|
{
|
|
unsigned char *jmp;
|
|
|
|
int label = EmitNot(sourcePos);
|
|
jmp = code_pos;
|
|
code_pos += sizeof(unsigned int);
|
|
|
|
ClearPrevOpcode();
|
|
|
|
EmitValue(if_stmt);
|
|
|
|
if (showopcodes->integer) {
|
|
glbs.DPrintf("<LABEL%d>:\n", label);
|
|
}
|
|
|
|
AddJumpLocation(jmp);
|
|
}
|
|
|
|
void ScriptCompiler::EmitInteger(unsigned int value, unsigned int sourcePos)
|
|
{
|
|
if (value == 0) {
|
|
EmitOpcode(OP_STORE_INT0, sourcePos);
|
|
} else if (value < 127) {
|
|
EmitOpcode(OP_STORE_INT1, sourcePos);
|
|
EmitOpcodeValue(value, sizeof(byte));
|
|
} else if (value < 32767) {
|
|
EmitOpcode(OP_STORE_INT2, sourcePos);
|
|
EmitOpcodeValue(value, sizeof(short));
|
|
} else if (value < 8388607) {
|
|
EmitOpcode(OP_STORE_INT3, sourcePos);
|
|
EmitOpcodeValue(value, sizeof(short3));
|
|
} else {
|
|
EmitOpcode(OP_STORE_INT4, sourcePos);
|
|
EmitOpcodeValue(value, sizeof(int));
|
|
}
|
|
}
|
|
|
|
void ScriptCompiler::EmitJump(unsigned char *pos, unsigned int sourcePos)
|
|
{
|
|
EmitOpcode(OP_JUMP4, sourcePos);
|
|
AddJumpToLocation(pos);
|
|
}
|
|
|
|
void ScriptCompiler::EmitJumpBack(unsigned char *pos, unsigned int sourcePos)
|
|
{
|
|
EmitOpcode(OP_JUMP_BACK4, sourcePos);
|
|
AddJumpBackLocation(pos);
|
|
}
|
|
|
|
void ScriptCompiler::EmitLabel(str name, unsigned int sourcePos)
|
|
{
|
|
if (showopcodes->integer) {
|
|
glbs.DPrintf("<%s>:\n", name.c_str());
|
|
}
|
|
|
|
if (stateScript->AddLabel(name, code_pos)) {
|
|
CompileError(sourcePos, "Duplicate label '%s'", name.c_str());
|
|
}
|
|
}
|
|
|
|
void ScriptCompiler::EmitLabelParameterList(sval_t parameter_list, unsigned int sourcePos)
|
|
{
|
|
sval_u param;
|
|
|
|
ClearPrevOpcode();
|
|
|
|
param = parameter_list.node[0];
|
|
|
|
if (param.node && param.node[0].node[0].type != sval_none) {
|
|
EmitOpcode(OP_MARK_STACK_POS, sourcePos);
|
|
|
|
while (param.node) {
|
|
EmitParameter(param.node[0], param.node[2].sourcePosValue);
|
|
param = param.node[1];
|
|
}
|
|
|
|
EmitOpcode(OP_RESTORE_STACK_POS, sourcePos);
|
|
}
|
|
}
|
|
|
|
void ScriptCompiler::EmitLabelPrivate(str name, unsigned int sourcePos)
|
|
{
|
|
if (showopcodes->integer) {
|
|
glbs.DPrintf("<%s>:\n", name.c_str());
|
|
}
|
|
|
|
if (stateScript->AddLabel(name, code_pos, true)) {
|
|
CompileError(sourcePos, "Duplicate label '%s'", name.c_str());
|
|
}
|
|
}
|
|
|
|
void ScriptCompiler::EmitLogicJump(sval_t logic_stmt, bool isOr, unsigned int sourcePos)
|
|
{
|
|
unsigned char *jmp;
|
|
int label;
|
|
|
|
if (showopcodes->integer) {
|
|
label = current_label++;
|
|
|
|
if (isOr) {
|
|
glbs.DPrintf("BOOL_LOGICAL_OR <LABEL%d>\n", label);
|
|
} else {
|
|
glbs.DPrintf("BOOL_LOGICAL_AND <LABEL%d>\n", label);
|
|
}
|
|
}
|
|
|
|
EmitOpcode(OP_BOOL_LOGICAL_AND + isOr, sourcePos);
|
|
jmp = code_pos;
|
|
code_pos += sizeof(unsigned int);
|
|
|
|
ClearPrevOpcode();
|
|
|
|
EmitValue(logic_stmt);
|
|
EmitVarToBool(sourcePos);
|
|
|
|
if (showopcodes->integer) {
|
|
glbs.DPrintf("<LABEL%d>:\n", label);
|
|
}
|
|
|
|
AddJumpLocation(jmp);
|
|
EmitBoolToVar(sourcePos);
|
|
}
|
|
|
|
void ScriptCompiler::EmitMakeArray(sval_t val)
|
|
{
|
|
int iCount = 0;
|
|
sval_t *node;
|
|
|
|
for (node = val.node[0].node; node != NULL; iCount++, node = node[1].node) {
|
|
EmitValue(node[0]);
|
|
}
|
|
|
|
EmitConstArrayOpcode(iCount);
|
|
}
|
|
|
|
void ScriptCompiler::EmitMethodExpression(int iParamCount, int eventnum, unsigned int sourcePos)
|
|
{
|
|
if (iParamCount > 5) {
|
|
SetOpcodeVarStackOffset(OP_EXEC_METHOD_COUNT1, -iParamCount);
|
|
EmitOpcode(OP_EXEC_METHOD_COUNT1, sourcePos);
|
|
|
|
EmitOpcodeValue(iParamCount, sizeof(byte));
|
|
} else {
|
|
EmitOpcode(OP_EXEC_METHOD0 + iParamCount, sourcePos);
|
|
}
|
|
|
|
EmitOpcodeValue(eventnum, sizeof(unsigned int));
|
|
}
|
|
|
|
void ScriptCompiler::EmitNil(unsigned int sourcePos)
|
|
{
|
|
if (showopcodes->integer) {
|
|
glbs.DPrintf("\t\t%08d:\n", code_pos - code_ptr);
|
|
}
|
|
}
|
|
|
|
void ScriptCompiler::EmitNop()
|
|
{
|
|
EmitOpcode(OP_NOP, -1);
|
|
}
|
|
|
|
int ScriptCompiler::EmitNot(unsigned int sourcePos)
|
|
{
|
|
int label = 0;
|
|
|
|
if (PrevOpcode() == OP_BOOL_UN_NOT) {
|
|
AbsorbPrevOpcode();
|
|
|
|
if (showopcodes->integer) {
|
|
label = current_label++;
|
|
glbs.DPrintf("BOOL_JUMP_TRUE <LABEL%d>\n", label);
|
|
}
|
|
|
|
EmitBoolJumpTrue(sourcePos);
|
|
} else {
|
|
if (showopcodes->integer) {
|
|
label = current_label++;
|
|
glbs.DPrintf("BOOL_JUMP_FALSE <LABEL%d>\n", label);
|
|
}
|
|
|
|
EmitBoolJumpFalse(sourcePos);
|
|
}
|
|
|
|
return label;
|
|
}
|
|
|
|
void ScriptCompiler::EmitOpcode(int opcode, unsigned int sourcePos)
|
|
{
|
|
int IsExternal;
|
|
int iVarStackOffset;
|
|
|
|
if (code_pos == NULL) {
|
|
Com_Printf("Compiler not initialized !\n");
|
|
return;
|
|
}
|
|
|
|
if (script->m_ProgToSource) {
|
|
str sourceLine;
|
|
sourceinfo_t info;
|
|
|
|
info.sourcePos = sourcePos;
|
|
|
|
script->GetSourceAt(sourcePos, sourceLine, info.column, info.line);
|
|
script->m_ProgToSource->addKeyValue(code_pos) = info;
|
|
}
|
|
|
|
IsExternal = IsExternalOpcode(opcode);
|
|
iVarStackOffset = OpcodeVarStackOffset(opcode);
|
|
|
|
if (IsExternal) {
|
|
if (m_iVarStackOffset > m_iMaxExternalVarStackOffset) {
|
|
m_iMaxExternalVarStackOffset = m_iVarStackOffset;
|
|
}
|
|
|
|
m_iHasExternal = 1;
|
|
}
|
|
|
|
m_iVarStackOffset += iVarStackOffset;
|
|
|
|
if (!IsExternal) {
|
|
if (m_iVarStackOffset > m_iInternalMaxVarStackOffset) {
|
|
m_iInternalMaxVarStackOffset = m_iVarStackOffset;
|
|
}
|
|
}
|
|
|
|
/*if( m_iInternalMaxVarStackOffset + 9 * m_iMaxExternalVarStackOffset + 1 > 255 )
|
|
{
|
|
CompileError( sourcePos,
|
|
"The required variable stack size of %d exceeds the statically allocated variable stack of size %d.\nIncrease SCRIPTTHREAD_VARSTACK_SIZE to at least %d and recompile.\n",
|
|
m_iInternalMaxVarStackOffset + 9 * m_iMaxExternalVarStackOffset + 1,
|
|
255,
|
|
m_iInternalMaxVarStackOffset + 9 * m_iMaxExternalVarStackOffset + 1
|
|
);
|
|
}*/
|
|
|
|
if (showopcodes->integer) {
|
|
glbs.DPrintf(
|
|
"\t\t%08d: %s (%d) %s\n",
|
|
code_pos - code_ptr,
|
|
OpcodeName(opcode),
|
|
m_iVarStackOffset,
|
|
IsExternal ? "[external]" : ""
|
|
);
|
|
}
|
|
|
|
prev_opcode_pos = (prev_opcode_pos + 1) % 100;
|
|
prev_opcodes[prev_opcode_pos].opcode = opcode;
|
|
prev_opcodes[prev_opcode_pos].VarStackOffset = iVarStackOffset;
|
|
prev_opcodes[(prev_opcode_pos + 1) % 100].opcode = OP_PREVIOUS;
|
|
|
|
EmitOpcodeValue(opcode, sizeof(byte));
|
|
}
|
|
|
|
void ScriptCompiler::EmitParameter(sval_t lhs, unsigned int sourcePos)
|
|
{
|
|
if (lhs.node[0].type != sval_field) {
|
|
CompileError(sourcePos, "bad parameter lvalue: %d (expecting field)", lhs.node[0].type);
|
|
}
|
|
|
|
sval_u listener_val = lhs.node[1];
|
|
const char *name = lhs.node[2].stringValue;
|
|
unsigned int index = Director.AddString(name);
|
|
|
|
int eventnum = Event::FindSetterEventNum(name);
|
|
|
|
if (listener_val.node[0].type != sval_store_method
|
|
|| (eventnum && BuiltinWriteVariable(sourcePos, listener_val.node[1].byteValue, eventnum))) {
|
|
CompileError(sourcePos, "built-in field '%s' not allowed", name);
|
|
} else {
|
|
EmitOpcode(OP_STORE_PARAM, sourcePos);
|
|
EmitOpcode(OP_LOAD_GAME_VAR + listener_val.node[1].byteValue, sourcePos);
|
|
|
|
EmitOpcodeValue(index, sizeof(unsigned int));
|
|
}
|
|
}
|
|
|
|
int ScriptCompiler::EmitParameterList(sval_t event_parameter_list)
|
|
{
|
|
sval_t *node;
|
|
int iParamCount = 0;
|
|
|
|
for (node = event_parameter_list.node[1].node[0].node; node != NULL; node = node[1].node) {
|
|
if (node[0].node[0].type == sval_none) {
|
|
continue;
|
|
}
|
|
|
|
EmitValue(*node);
|
|
|
|
iParamCount++;
|
|
}
|
|
|
|
return iParamCount;
|
|
}
|
|
|
|
void ScriptCompiler::EmitRef(sval_t val, unsigned int sourcePos)
|
|
{
|
|
unsigned int index;
|
|
|
|
if (val.node[0].type != sval_field) {
|
|
if (val.node[0].type == sval_array) {
|
|
EmitRef(val.node[1], sourcePos);
|
|
EmitValue(val.node[2]);
|
|
EmitOpcode(OP_STORE_ARRAY_REF, val.node[3].sourcePosValue);
|
|
|
|
return;
|
|
} else {
|
|
CompileError(sourcePos, "bad lvalue: %d (expecting field or array)", val.node[0].type);
|
|
return;
|
|
}
|
|
}
|
|
|
|
index = Director.AddString(val.node[2].stringValue);
|
|
|
|
EmitValue(val.node[1]);
|
|
EmitOpcode(OP_STORE_FIELD_REF, sourcePos);
|
|
EmitOpcodeValue(index, sizeof(unsigned int));
|
|
}
|
|
|
|
void ScriptCompiler::EmitStatementList(sval_t val)
|
|
{
|
|
sval_t *node;
|
|
|
|
for (node = val.node[0].node; node != NULL; node = node[1].node) {
|
|
EmitValue(*node);
|
|
}
|
|
}
|
|
|
|
void ScriptCompiler::EmitString(str value, unsigned int sourcePos)
|
|
{
|
|
unsigned int index = Director.AddString(value);
|
|
|
|
if (showopcodes->integer) {
|
|
glbs.DPrintf("\t\tSTRING \"%s\"\n", value.c_str());
|
|
}
|
|
|
|
EmitOpcode(OP_STORE_STRING, sourcePos);
|
|
EmitOpcodeValue(index, sizeof(unsigned int));
|
|
}
|
|
|
|
void ScriptCompiler::EmitSwitch(sval_t val, unsigned int sourcePos)
|
|
{
|
|
bool bStartCanBreak;
|
|
int iStartBreakJumpLocCount;
|
|
StateScript *m_oldStateScript;
|
|
|
|
m_oldStateScript = stateScript;
|
|
stateScript = script->CreateSwitchStateScript();
|
|
|
|
EmitOpcode(OP_SWITCH, sourcePos);
|
|
EmitOpcodeValue(stateScript, sizeof(StateScript *));
|
|
|
|
bStartCanBreak = bCanBreak;
|
|
iStartBreakJumpLocCount = iBreakJumpLocCount;
|
|
|
|
bCanBreak = true;
|
|
|
|
EmitBreak(sourcePos);
|
|
|
|
EmitValue(val);
|
|
|
|
ProcessBreakJumpLocations(iStartBreakJumpLocCount);
|
|
|
|
bCanBreak = bStartCanBreak;
|
|
|
|
stateScript = m_oldStateScript;
|
|
}
|
|
|
|
void ScriptCompiler::EmitValue(ScriptVariable& var, unsigned int sourcePos)
|
|
{
|
|
if (var.GetType() == VARIABLE_INTEGER) {
|
|
EmitInteger(var.intValue(), sourcePos);
|
|
} else if (var.GetType() == VARIABLE_FLOAT) {
|
|
EmitFloat(var.floatValue(), sourcePos);
|
|
}
|
|
}
|
|
|
|
void ScriptCompiler::EmitValue(sval_t val)
|
|
{
|
|
__emit:
|
|
|
|
switch (val.node[0].type) {
|
|
case sval_none:
|
|
break;
|
|
|
|
case sval_and:
|
|
case sval_or:
|
|
EmitValue(val.node[1]);
|
|
EmitVarToBool(val.node[3].sourcePosValue);
|
|
EmitLogicJump(val.node[2], val.node[0].type == sval_or, val.node[3].sourcePosValue);
|
|
break;
|
|
|
|
case sval_array:
|
|
EmitValue(val.node[1]);
|
|
EmitValue(val.node[2]);
|
|
EmitOpcode(OP_STORE_ARRAY, val.node[3].sourcePosValue);
|
|
break;
|
|
|
|
case sval_assignment:
|
|
EmitValue(val.node[2]);
|
|
EmitAssignmentStatement(val.node[1], val.node[3].sourcePosValue);
|
|
break;
|
|
|
|
case sval_break:
|
|
return EmitBreak(val.node[1].sourcePosValue);
|
|
|
|
case sval_calc_vector:
|
|
EmitValue(val.node[1]);
|
|
EmitValue(val.node[2]);
|
|
EmitValue(val.node[3]);
|
|
EmitOpcode(OP_CALC_VECTOR, val.node[4].sourcePosValue);
|
|
break;
|
|
|
|
case sval_negative:
|
|
val.node[1].intValue = -val.node[1].intValue;
|
|
case sval_case:
|
|
{
|
|
sval_u case_parm = val.node[1];
|
|
|
|
if (case_parm.node[0].type == sval_store_integer) {
|
|
EmitLabel(case_parm.node[1].intValue, val.node[3].sourcePosValue);
|
|
} else if (case_parm.node[0].type == sval_store_string) {
|
|
EmitLabel(case_parm.node[1].stringValue, val.node[3].sourcePosValue);
|
|
} else if (case_parm.node[0].type == sval_func1 && case_parm.node[1].byteValue == OP_UN_MINUS) {
|
|
EmitLabel(-case_parm.node[2].node[1].intValue, val.node[3].sourcePosValue);
|
|
} else {
|
|
CompileError(
|
|
val.node[3].sourcePosValue,
|
|
"bad case value: %d (expected integer or string)",
|
|
case_parm.node[0].type
|
|
);
|
|
}
|
|
|
|
EmitLabelParameterList(val.node[2], val.node[3].sourcePosValue);
|
|
|
|
break;
|
|
}
|
|
|
|
case sval_catch:
|
|
{
|
|
unsigned char *old_code_pos = code_pos;
|
|
|
|
ClearPrevOpcode();
|
|
EmitValue(val.node[1]);
|
|
EmitCatch(val.node[2], old_code_pos, val.node[3].sourcePosValue);
|
|
break;
|
|
}
|
|
|
|
case sval_cmd:
|
|
{
|
|
str cmd = val.node[1].stringValue;
|
|
cmd.tolower();
|
|
int eventnum = Event::FindNormalEventNum(cmd);
|
|
int iParamCount = EmitParameterList(val.node[2]);
|
|
|
|
if (!eventnum) {
|
|
CompileError(val.node[3].sourcePosValue, "unknown command: %s", val.node[1].stringValue);
|
|
|
|
/*EmitOpcode( OP_STORE_LOCAL, val.node[ 3 ].sourcePosValue );
|
|
EmitFunction( iParamCount, val.node[ 1 ], val.node[ 3 ].sourcePosValue );
|
|
EmitOpcode( OP_LOAD_LOCAL_VAR, val.node[ 3 ].sourcePosValue );
|
|
|
|
*reinterpret_cast< unsigned int * >( code_pos ) = Director.AddString( "" );
|
|
code_pos += sizeof( unsigned int );*/
|
|
} else {
|
|
if (iParamCount > 5) {
|
|
SetOpcodeVarStackOffset(OP_EXEC_CMD_COUNT1, -iParamCount);
|
|
EmitOpcode(OP_EXEC_CMD_COUNT1, val.node[3].sourcePosValue);
|
|
|
|
EmitOpcodeValue(iParamCount, sizeof(byte));
|
|
} else {
|
|
EmitOpcode(OP_EXEC_CMD0 + iParamCount, val.node[3].sourcePosValue);
|
|
}
|
|
|
|
EmitOpcodeValue(eventnum, sizeof(unsigned int));
|
|
}
|
|
break;
|
|
}
|
|
|
|
case sval_cmd_default_ret:
|
|
{
|
|
str cmd = val.node[1].stringValue;
|
|
cmd.tolower();
|
|
int eventnum = Event::FindReturnEventNum(cmd);
|
|
int iParamCount = EmitParameterList(val.node[2]);
|
|
|
|
EmitOpcode(OP_STORE_LOCAL, val.node[3].sourcePosValue);
|
|
|
|
if (!eventnum) {
|
|
CompileError(val.node[3].sourcePosValue, "unknown return command: %s", val.node[1].stringValue);
|
|
//EmitFunction( iParamCount, val.node[ 1 ], val.node[ 3 ].sourcePosValue );
|
|
} else {
|
|
EmitMethodExpression(iParamCount, eventnum, val.node[3].sourcePosValue);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case sval_cmd_method:
|
|
{
|
|
str cmd = val.node[2].stringValue;
|
|
cmd.tolower();
|
|
int eventnum = Event::FindNormalEventNum(cmd);
|
|
int iParamCount = EmitParameterList(val.node[3]);
|
|
|
|
EmitValue(val.node[1]);
|
|
|
|
if (!eventnum) {
|
|
CompileError(val.node[4].sourcePosValue, "unknown command: %s", val.node[2].stringValue);
|
|
|
|
/*EmitFunction( iParamCount, val.node[ 2 ], val.node[ 4 ].sourcePosValue );
|
|
EmitOpcode( OP_LOAD_LOCAL_VAR, val.node[ 3 ].sourcePosValue );
|
|
|
|
*reinterpret_cast< unsigned int * >( code_pos ) = Director.AddString( "" );
|
|
code_pos += sizeof( unsigned int );*/
|
|
} else {
|
|
if (iParamCount > 5) {
|
|
SetOpcodeVarStackOffset(OP_EXEC_CMD_METHOD_COUNT1, -iParamCount - 1);
|
|
EmitOpcode(OP_EXEC_CMD_METHOD_COUNT1, val.node[4].sourcePosValue);
|
|
|
|
EmitOpcodeValue(iParamCount, sizeof(byte));
|
|
} else {
|
|
EmitOpcode(OP_EXEC_CMD_METHOD0 + iParamCount, val.node[4].sourcePosValue);
|
|
}
|
|
|
|
EmitOpcodeValue(eventnum, sizeof(unsigned int));
|
|
}
|
|
break;
|
|
}
|
|
|
|
case sval_cmd_method_ret:
|
|
{
|
|
str cmd = val.node[2].stringValue;
|
|
cmd.tolower();
|
|
int eventnum = Event::FindReturnEventNum(cmd);
|
|
int iParamCount = EmitParameterList(val.node[3]);
|
|
|
|
EmitValue(val.node[1]);
|
|
|
|
if (!eventnum) {
|
|
CompileError(val.node[4].sourcePosValue, "unknown return command: %s", val.node[2].stringValue);
|
|
//EmitFunction( iParamCount ,val.node[ 2 ], val.node[ 4 ].sourcePosValue );
|
|
} else {
|
|
EmitMethodExpression(iParamCount, eventnum, val.node[4].sourcePosValue);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case sval_constarray:
|
|
return EmitConstArray(val.node[1], val.node[2], val.node[3].sourcePosValue);
|
|
|
|
case sval_continue:
|
|
return EmitContinue(val.node[1].sourcePosValue);
|
|
|
|
case sval_do:
|
|
return EmitDoWhileJump(val.node[1], val.node[2], val.node[3].sourcePosValue);
|
|
|
|
case sval_field:
|
|
return EmitField(val.node[1], val.node[2], val.node[3].sourcePosValue);
|
|
|
|
case sval_func1:
|
|
EmitValue(val.node[2]);
|
|
EmitFunc1(val.node[1].byteValue, val.node[3].sourcePosValue);
|
|
break;
|
|
|
|
case sval_if:
|
|
EmitValue(val.node[1]);
|
|
EmitVarToBool(val.node[4].sourcePosValue);
|
|
EmitIfJump(val.node[2], val.node[3].sourcePosValue);
|
|
break;
|
|
|
|
case sval_ifelse:
|
|
EmitValue(val.node[1]);
|
|
EmitVarToBool(val.node[4].sourcePosValue);
|
|
EmitIfElseJump(val.node[2], val.node[3], val.node[4].sourcePosValue);
|
|
break;
|
|
|
|
case sval_label:
|
|
EmitLabel(val.node[1].stringValue, val.node[3].sourcePosValue);
|
|
EmitLabelParameterList(val.node[2], val.node[3].sourcePosValue);
|
|
break;
|
|
|
|
case sval_privatelabel:
|
|
EmitLabelPrivate(val.node[1].stringValue, val.node[3].sourcePosValue);
|
|
EmitLabelParameterList(val.node[2], val.node[3].sourcePosValue);
|
|
break;
|
|
|
|
case sval_makearray:
|
|
return EmitMakeArray(val.node[1]);
|
|
|
|
case sval_not:
|
|
EmitValue(val.node[1]);
|
|
EmitVarToBool(val.node[2].sourcePosValue);
|
|
EmitBoolNot(val.node[2].sourcePosValue);
|
|
EmitBoolToVar(val.node[2].sourcePosValue);
|
|
break;
|
|
|
|
case sval_operation:
|
|
EmitValue(val.node[2]);
|
|
EmitValue(val.node[3]);
|
|
EmitOpcode(val.node[1].byteValue, val.node[4].sourcePosValue);
|
|
break;
|
|
|
|
case sval_statement_list:
|
|
return EmitStatementList(val.node[1]);
|
|
|
|
case sval_store_float:
|
|
EmitFloat(val.node[1].floatValue, val.node[2].sourcePosValue);
|
|
break;
|
|
|
|
case sval_store_integer:
|
|
EmitInteger(val.node[1].intValue, val.node[2].sourcePosValue);
|
|
break;
|
|
|
|
case sval_store_method:
|
|
EmitOpcode(OP_STORE_GAME + val.node[1].byteValue, val.node[2].sourcePosValue);
|
|
break;
|
|
|
|
case sval_store_nil:
|
|
EmitOpcode(OP_STORE_NIL, val.node[1].sourcePosValue);
|
|
break;
|
|
|
|
case sval_store_null:
|
|
EmitOpcode(OP_STORE_NULL, val.node[1].sourcePosValue);
|
|
break;
|
|
|
|
case sval_store_string:
|
|
EmitString(val.node[1].stringValue, val.node[2].sourcePosValue);
|
|
break;
|
|
|
|
case sval_switch:
|
|
EmitValue(val.node[1]);
|
|
EmitSwitch(val.node[2], val.node[3].sourcePosValue);
|
|
break;
|
|
|
|
case sval_next:
|
|
val = val.node[1];
|
|
goto __emit; // prevent stack overflow
|
|
|
|
case sval_while:
|
|
return EmitWhileJump(val.node[1], val.node[2], val.node[3], val.node[4].sourcePosValue);
|
|
|
|
default:
|
|
CompileError(-1, "unknown type %d\n", val.node[0].type);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void ScriptCompiler::EmitVarToBool(unsigned int sourcePos)
|
|
{
|
|
int prev = PrevOpcode();
|
|
|
|
if (prev == OP_STORE_INT0) {
|
|
AbsorbPrevOpcode();
|
|
return EmitOpcode(OP_BOOL_STORE_FALSE, sourcePos);
|
|
} else if (prev > OP_STORE_INT0) {
|
|
if (prev <= OP_STORE_INT4) {
|
|
AbsorbPrevOpcode();
|
|
return EmitOpcode(OP_BOOL_STORE_TRUE, sourcePos);
|
|
}
|
|
} else if (prev == OP_BOOL_TO_VAR) {
|
|
AbsorbPrevOpcode();
|
|
return EmitNil(sourcePos);
|
|
}
|
|
|
|
return EmitOpcode(OP_UN_CAST_BOOLEAN, sourcePos);
|
|
}
|
|
|
|
void ScriptCompiler::EmitWhileJump(sval_t while_expr, sval_t while_stmt, sval_t inc_stmt, unsigned int sourcePos)
|
|
{
|
|
unsigned char *pos = code_pos;
|
|
int label1, label2;
|
|
|
|
if (showopcodes->integer) {
|
|
label1 = current_label++;
|
|
glbs.DPrintf("<LABEL%d>:\n", label1);
|
|
}
|
|
|
|
ClearPrevOpcode();
|
|
|
|
EmitValue(while_expr);
|
|
EmitVarToBool(sourcePos);
|
|
|
|
label2 = EmitNot(sourcePos);
|
|
unsigned char *jmp = code_pos;
|
|
code_pos += sizeof(unsigned int);
|
|
ClearPrevOpcode();
|
|
|
|
bool old_bCanBreak = bCanBreak;
|
|
bool old_bCanContinue = bCanContinue;
|
|
int breakCount = iBreakJumpLocCount;
|
|
int continueCount = iContinueJumpLocCount;
|
|
|
|
bCanBreak = true;
|
|
bCanContinue = true;
|
|
|
|
EmitValue(while_stmt);
|
|
ProcessContinueJumpLocations(continueCount);
|
|
|
|
bCanContinue = old_bCanContinue;
|
|
|
|
EmitValue(inc_stmt);
|
|
|
|
if (showopcodes->integer) {
|
|
glbs.DPrintf("JUMP_BACK4 <LABEL%d>\n", label1);
|
|
}
|
|
|
|
EmitJumpBack(pos, sourcePos);
|
|
|
|
ClearPrevOpcode();
|
|
|
|
if (showopcodes->integer) {
|
|
glbs.DPrintf("<LABEL%d>:\n", label2);
|
|
}
|
|
|
|
AddJumpLocation(jmp);
|
|
|
|
ProcessBreakJumpLocations(breakCount);
|
|
|
|
bCanBreak = old_bCanBreak;
|
|
}
|
|
|
|
bool ScriptCompiler::EvalPrevValue(ScriptVariable& var)
|
|
{
|
|
int intValue = 0;
|
|
float floatValue = 0.0f;
|
|
|
|
switch (PrevOpcode()) {
|
|
case OP_STORE_INT0:
|
|
intValue = 0;
|
|
break;
|
|
|
|
case OP_STORE_INT1:
|
|
intValue = GetOpcodeValue<byte>(sizeof(byte), sizeof(byte));
|
|
break;
|
|
|
|
case OP_STORE_INT2:
|
|
intValue = GetOpcodeValue<short>(sizeof(short), sizeof(short));
|
|
break;
|
|
|
|
case OP_STORE_INT3:
|
|
intValue = GetOpcodeValue<short3>(sizeof(short3), sizeof(short3));
|
|
break;
|
|
|
|
case OP_STORE_INT4:
|
|
intValue = GetOpcodeValue<int>(sizeof(int), sizeof(int));
|
|
break;
|
|
|
|
case OP_STORE_FLOAT:
|
|
intValue = GetOpcodeValue<float>(sizeof(float), sizeof(float));
|
|
var.setFloatValue(floatValue);
|
|
return true;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
var.setIntValue(intValue);
|
|
|
|
return true;
|
|
}
|
|
|
|
void ScriptCompiler::ProcessBreakJumpLocations(int iStartBreakJumpLocCount)
|
|
{
|
|
if (iBreakJumpLocCount > iStartBreakJumpLocCount) {
|
|
do {
|
|
iBreakJumpLocCount--;
|
|
|
|
unsigned int offset = code_pos - sizeof(unsigned int) - apucBreakJumpLocations[iBreakJumpLocCount];
|
|
|
|
EmitAt(apucBreakJumpLocations[iBreakJumpLocCount], offset, sizeof(unsigned int));
|
|
} while (iBreakJumpLocCount > iStartBreakJumpLocCount);
|
|
|
|
ClearPrevOpcode();
|
|
}
|
|
}
|
|
|
|
void ScriptCompiler::ProcessContinueJumpLocations(int iStartContinueJumpLocCount)
|
|
{
|
|
if (iContinueJumpLocCount > iStartContinueJumpLocCount) {
|
|
do {
|
|
iContinueJumpLocCount--;
|
|
|
|
unsigned int offset = code_pos - sizeof(unsigned int) - apucContinueJumpLocations[iContinueJumpLocCount];
|
|
|
|
EmitAt(apucContinueJumpLocations[iContinueJumpLocCount], offset, sizeof(unsigned int));
|
|
} while (iContinueJumpLocCount > iStartContinueJumpLocCount);
|
|
|
|
ClearPrevOpcode();
|
|
}
|
|
}
|
|
|
|
unsigned char *ScriptCompiler::GetPosition()
|
|
{
|
|
return code_pos;
|
|
}
|
|
|
|
void ScriptCompiler::CompileError(unsigned int sourcePos, const char *format, ...)
|
|
{
|
|
char buffer[4000];
|
|
va_list va;
|
|
|
|
va_start(va, format);
|
|
vsprintf(buffer, format, va);
|
|
va_end(va);
|
|
|
|
compileSuccess = false;
|
|
|
|
glbs.DPrintf("%s\n", buffer);
|
|
script->PrintSourcePos(sourcePos, false);
|
|
|
|
throw ScriptException(buffer);
|
|
}
|
|
|
|
int yyparse();
|
|
|
|
char *ScriptCompiler::Preprocess(char *sourceBuffer)
|
|
{
|
|
// FIXME (ley0k): Preprocessor (#defines and #includes)
|
|
|
|
return sourceBuffer;
|
|
}
|
|
|
|
void ScriptCompiler::Preclean(char *processedBuffer) {}
|
|
|
|
extern int prev_yylex;
|
|
|
|
int yyerror(const char *msg)
|
|
{
|
|
//parsedata.pos -= yyleng;
|
|
parsedata.exc.yylineno = prev_yylex != TOKEN_EOL ? yylineno : yylineno - 1;
|
|
parsedata.exc.yytext = yytext;
|
|
parsedata.exc.yytoken = msg;
|
|
|
|
//str line = ScriptCompiler::GetLine( parsedata.sourceBuffer, parsedata.exc.yylineno );
|
|
|
|
glbs.Printf("parse error:\n%s:\n", parsedata.exc.yytoken.c_str());
|
|
|
|
parsedata.gameScript->PrintSourcePos(parsedata.pos - yyleng, false);
|
|
parsedata.pos++;
|
|
|
|
return 1;
|
|
}
|
|
|
|
size_t ScriptCompiler::Parse(GameScript *gameScript, char *sourceBuffer)
|
|
{
|
|
parsedata = yyparsedata();
|
|
|
|
parsedata.sourceBuffer = sourceBuffer;
|
|
parsedata.gameScript = gameScript;
|
|
parsedata.braces_count = 0;
|
|
|
|
prev_yylex = 0;
|
|
script = gameScript;
|
|
stateScript = &gameScript->m_State;
|
|
yy_scan_string(sourceBuffer);
|
|
|
|
parsetree_init();
|
|
|
|
try {
|
|
if (yyparse() != 0 || parsedata.exc.yytoken != "") {
|
|
// an error occured
|
|
|
|
yylex_destroy();
|
|
|
|
if (!parsedata.exc.yytext) {
|
|
if (parsedata.braces_count) {
|
|
glbs.DPrintf("unmatching {} pair\n");
|
|
} else {
|
|
glbs.DPrintf("unexpected end of file found\n");
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
} catch (ScriptException& exc) {
|
|
exc;
|
|
return 0;
|
|
}
|
|
|
|
yylex_destroy();
|
|
|
|
return parsedata.total_length;
|
|
}
|
|
|
|
size_t ScriptCompiler::Compile(GameScript *gameScript, unsigned char *progBuffer)
|
|
{
|
|
size_t length;
|
|
|
|
if (progBuffer == NULL) {
|
|
glbs.DPrintf("Invalid program buffer\n");
|
|
return 0;
|
|
}
|
|
|
|
code_pos = progBuffer;
|
|
code_ptr = progBuffer;
|
|
prog_ptr = progBuffer;
|
|
|
|
gameScript->m_ProgToSource = new con_set<unsigned char *, sourceinfo_t>;
|
|
|
|
compileSuccess = true;
|
|
|
|
prev_opcodes[prev_opcode_pos].opcode = OP_PREVIOUS;
|
|
|
|
try {
|
|
EmitValue(parsedata.val);
|
|
EmitEof(-1);
|
|
|
|
if (compileSuccess) {
|
|
stateScript->AddLabel("", code_ptr);
|
|
|
|
length = code_pos - code_ptr;
|
|
} else {
|
|
length = 0;
|
|
}
|
|
|
|
prog_end_ptr = code_pos;
|
|
} catch (ScriptException& exc) {
|
|
exc;
|
|
length = 0;
|
|
prog_end_ptr = code_pos;
|
|
}
|
|
|
|
parsetree_freeall();
|
|
|
|
return length;
|
|
}
|
|
|
|
str ScriptCompiler::GetLine(str content, int line)
|
|
{
|
|
char *p;
|
|
str found;
|
|
int i = 1;
|
|
|
|
p = (char *)content.c_str();
|
|
|
|
while (*p) {
|
|
if (*p == '\n') {
|
|
i++;
|
|
}
|
|
|
|
if (i >= line) {
|
|
found = strtok(p, "\n");
|
|
break;
|
|
}
|
|
|
|
p++;
|
|
}
|
|
|
|
return found;
|
|
}
|
|
|
|
template<typename Value>
|
|
void ScriptCompiler::EmitOpcodeValue(const Value& value, size_t size)
|
|
{
|
|
Com_Memcpy(code_pos, &value, size);
|
|
code_pos += size;
|
|
}
|
|
|
|
template<typename Value>
|
|
void ScriptCompiler::EmitAt(unsigned char *location, const Value& value, size_t size)
|
|
{
|
|
Com_Memcpy(location, &value, size);
|
|
}
|
|
|
|
template<typename Value>
|
|
void ScriptCompiler::SetOpcodeValue(const Value& value)
|
|
{
|
|
Com_Memcpy(code_pos, &value, sizeof(value));
|
|
}
|
|
|
|
template<typename Value>
|
|
Value ScriptCompiler::GetOpcodeValue(size_t size) const
|
|
{
|
|
Value val {0};
|
|
Com_Memcpy(&val, code_pos, size);
|
|
return val;
|
|
}
|
|
|
|
template<typename Value>
|
|
Value ScriptCompiler::GetOpcodeValue(size_t offset, size_t size) const
|
|
{
|
|
Value val {0};
|
|
Com_Memcpy(&val, code_pos - offset, size);
|
|
return val;
|
|
}
|
|
|
|
void CompileAssemble(const char *filename, const char *outputfile)
|
|
{
|
|
GameScript *gameScript = Director.GetGameScript(filename);
|
|
Archiver arc;
|
|
|
|
if (!gameScript->m_ProgBuffer || !gameScript->m_ProgLength) {
|
|
return;
|
|
}
|
|
|
|
arc.Create(outputfile);
|
|
|
|
if (!arc.NoErrors()) {
|
|
glbs.DPrintf("Error saving to file '%s'\n", filename);
|
|
return;
|
|
}
|
|
|
|
gameScript->m_bPrecompiled = true;
|
|
gameScript->Archive(arc);
|
|
|
|
// dump the compiled script to file
|
|
arc.Close();
|
|
}
|
|
|
|
bool GetCompiledScript(GameScript *scr)
|
|
{
|
|
Archiver arc;
|
|
|
|
arc.SetSilent(true);
|
|
arc.Read(scr->Filename(), false);
|
|
|
|
if (!arc.NoErrors()) {
|
|
return false;
|
|
}
|
|
|
|
arc.SetSilent(false);
|
|
|
|
scr->Archive(arc);
|
|
|
|
arc.Close();
|
|
|
|
return true;
|
|
}
|