mirror of https://github.com/OpenMW/openmw.git
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
743 lines
18 KiB
C++
743 lines
18 KiB
C++
#include "generator.hpp"
|
|
|
|
#include <algorithm>
|
|
#include <cassert>
|
|
#include <iterator>
|
|
#include <stdexcept>
|
|
|
|
#include "literals.hpp"
|
|
|
|
namespace
|
|
{
|
|
void opPushInt(Compiler::Generator::CodeContainer& code, int value)
|
|
{
|
|
code.push_back(Compiler::Generator::segment0(0, value));
|
|
}
|
|
|
|
void opFetchIntLiteral(Compiler::Generator::CodeContainer& code)
|
|
{
|
|
code.push_back(Compiler::Generator::segment5(4));
|
|
}
|
|
|
|
void opFetchFloatLiteral(Compiler::Generator::CodeContainer& code)
|
|
{
|
|
code.push_back(Compiler::Generator::segment5(5));
|
|
}
|
|
|
|
void opIntToFloat(Compiler::Generator::CodeContainer& code)
|
|
{
|
|
code.push_back(Compiler::Generator::segment5(3));
|
|
}
|
|
|
|
void opFloatToInt(Compiler::Generator::CodeContainer& code)
|
|
{
|
|
code.push_back(Compiler::Generator::segment5(6));
|
|
}
|
|
|
|
void opStoreLocalShort(Compiler::Generator::CodeContainer& code)
|
|
{
|
|
code.push_back(Compiler::Generator::segment5(0));
|
|
}
|
|
|
|
void opStoreLocalLong(Compiler::Generator::CodeContainer& code)
|
|
{
|
|
code.push_back(Compiler::Generator::segment5(1));
|
|
}
|
|
|
|
void opStoreLocalFloat(Compiler::Generator::CodeContainer& code)
|
|
{
|
|
code.push_back(Compiler::Generator::segment5(2));
|
|
}
|
|
|
|
void opNegateInt(Compiler::Generator::CodeContainer& code)
|
|
{
|
|
code.push_back(Compiler::Generator::segment5(7));
|
|
}
|
|
|
|
void opNegateFloat(Compiler::Generator::CodeContainer& code)
|
|
{
|
|
code.push_back(Compiler::Generator::segment5(8));
|
|
}
|
|
|
|
void opAddInt(Compiler::Generator::CodeContainer& code)
|
|
{
|
|
code.push_back(Compiler::Generator::segment5(9));
|
|
}
|
|
|
|
void opAddFloat(Compiler::Generator::CodeContainer& code)
|
|
{
|
|
code.push_back(Compiler::Generator::segment5(10));
|
|
}
|
|
|
|
void opSubInt(Compiler::Generator::CodeContainer& code)
|
|
{
|
|
code.push_back(Compiler::Generator::segment5(11));
|
|
}
|
|
|
|
void opSubFloat(Compiler::Generator::CodeContainer& code)
|
|
{
|
|
code.push_back(Compiler::Generator::segment5(12));
|
|
}
|
|
|
|
void opMulInt(Compiler::Generator::CodeContainer& code)
|
|
{
|
|
code.push_back(Compiler::Generator::segment5(13));
|
|
}
|
|
|
|
void opMulFloat(Compiler::Generator::CodeContainer& code)
|
|
{
|
|
code.push_back(Compiler::Generator::segment5(14));
|
|
}
|
|
|
|
void opDivInt(Compiler::Generator::CodeContainer& code)
|
|
{
|
|
code.push_back(Compiler::Generator::segment5(15));
|
|
}
|
|
|
|
void opDivFloat(Compiler::Generator::CodeContainer& code)
|
|
{
|
|
code.push_back(Compiler::Generator::segment5(16));
|
|
}
|
|
|
|
void opIntToFloat1(Compiler::Generator::CodeContainer& code)
|
|
{
|
|
code.push_back(Compiler::Generator::segment5(17));
|
|
}
|
|
|
|
void opReturn(Compiler::Generator::CodeContainer& code)
|
|
{
|
|
code.push_back(Compiler::Generator::segment5(20));
|
|
}
|
|
|
|
void opMessageBox(Compiler::Generator::CodeContainer& code, int buttons)
|
|
{
|
|
code.push_back(Compiler::Generator::segment3(0, buttons));
|
|
}
|
|
|
|
void opReport(Compiler::Generator::CodeContainer& code)
|
|
{
|
|
code.push_back(Compiler::Generator::segment5(58));
|
|
}
|
|
|
|
void opFetchLocalShort(Compiler::Generator::CodeContainer& code)
|
|
{
|
|
code.push_back(Compiler::Generator::segment5(21));
|
|
}
|
|
|
|
void opFetchLocalLong(Compiler::Generator::CodeContainer& code)
|
|
{
|
|
code.push_back(Compiler::Generator::segment5(22));
|
|
}
|
|
|
|
void opFetchLocalFloat(Compiler::Generator::CodeContainer& code)
|
|
{
|
|
code.push_back(Compiler::Generator::segment5(23));
|
|
}
|
|
|
|
void opJumpForward(Compiler::Generator::CodeContainer& code, int offset)
|
|
{
|
|
code.push_back(Compiler::Generator::segment0(1, offset));
|
|
}
|
|
|
|
void opJumpBackward(Compiler::Generator::CodeContainer& code, int offset)
|
|
{
|
|
code.push_back(Compiler::Generator::segment0(2, offset));
|
|
}
|
|
|
|
/*
|
|
Currently unused
|
|
void opSkipOnZero (Compiler::Generator::CodeContainer& code)
|
|
{
|
|
code.push_back (Compiler::Generator::segment5 (24));
|
|
}
|
|
*/
|
|
|
|
void opSkipOnNonZero(Compiler::Generator::CodeContainer& code)
|
|
{
|
|
code.push_back(Compiler::Generator::segment5(25));
|
|
}
|
|
|
|
void opEqualInt(Compiler::Generator::CodeContainer& code)
|
|
{
|
|
code.push_back(Compiler::Generator::segment5(26));
|
|
}
|
|
|
|
void opNonEqualInt(Compiler::Generator::CodeContainer& code)
|
|
{
|
|
code.push_back(Compiler::Generator::segment5(27));
|
|
}
|
|
|
|
void opLessThanInt(Compiler::Generator::CodeContainer& code)
|
|
{
|
|
code.push_back(Compiler::Generator::segment5(28));
|
|
}
|
|
|
|
void opLessOrEqualInt(Compiler::Generator::CodeContainer& code)
|
|
{
|
|
code.push_back(Compiler::Generator::segment5(29));
|
|
}
|
|
|
|
void opGreaterThanInt(Compiler::Generator::CodeContainer& code)
|
|
{
|
|
code.push_back(Compiler::Generator::segment5(30));
|
|
}
|
|
|
|
void opGreaterOrEqualInt(Compiler::Generator::CodeContainer& code)
|
|
{
|
|
code.push_back(Compiler::Generator::segment5(31));
|
|
}
|
|
|
|
void opEqualFloat(Compiler::Generator::CodeContainer& code)
|
|
{
|
|
code.push_back(Compiler::Generator::segment5(32));
|
|
}
|
|
|
|
void opNonEqualFloat(Compiler::Generator::CodeContainer& code)
|
|
{
|
|
code.push_back(Compiler::Generator::segment5(33));
|
|
}
|
|
|
|
void opLessThanFloat(Compiler::Generator::CodeContainer& code)
|
|
{
|
|
code.push_back(Compiler::Generator::segment5(34));
|
|
}
|
|
|
|
void opLessOrEqualFloat(Compiler::Generator::CodeContainer& code)
|
|
{
|
|
code.push_back(Compiler::Generator::segment5(35));
|
|
}
|
|
|
|
void opGreaterThanFloat(Compiler::Generator::CodeContainer& code)
|
|
{
|
|
code.push_back(Compiler::Generator::segment5(36));
|
|
}
|
|
|
|
void opGreaterOrEqualFloat(Compiler::Generator::CodeContainer& code)
|
|
{
|
|
code.push_back(Compiler::Generator::segment5(37));
|
|
}
|
|
|
|
void opStoreGlobalShort(Compiler::Generator::CodeContainer& code)
|
|
{
|
|
code.push_back(Compiler::Generator::segment5(39));
|
|
}
|
|
|
|
void opStoreGlobalLong(Compiler::Generator::CodeContainer& code)
|
|
{
|
|
code.push_back(Compiler::Generator::segment5(40));
|
|
}
|
|
|
|
void opStoreGlobalFloat(Compiler::Generator::CodeContainer& code)
|
|
{
|
|
code.push_back(Compiler::Generator::segment5(41));
|
|
}
|
|
|
|
void opFetchGlobalShort(Compiler::Generator::CodeContainer& code)
|
|
{
|
|
code.push_back(Compiler::Generator::segment5(42));
|
|
}
|
|
|
|
void opFetchGlobalLong(Compiler::Generator::CodeContainer& code)
|
|
{
|
|
code.push_back(Compiler::Generator::segment5(43));
|
|
}
|
|
|
|
void opFetchGlobalFloat(Compiler::Generator::CodeContainer& code)
|
|
{
|
|
code.push_back(Compiler::Generator::segment5(44));
|
|
}
|
|
|
|
void opStoreMemberShort(Compiler::Generator::CodeContainer& code, bool global)
|
|
{
|
|
code.push_back(Compiler::Generator::segment5(global ? 65 : 59));
|
|
}
|
|
|
|
void opStoreMemberLong(Compiler::Generator::CodeContainer& code, bool global)
|
|
{
|
|
code.push_back(Compiler::Generator::segment5(global ? 66 : 60));
|
|
}
|
|
|
|
void opStoreMemberFloat(Compiler::Generator::CodeContainer& code, bool global)
|
|
{
|
|
code.push_back(Compiler::Generator::segment5(global ? 67 : 61));
|
|
}
|
|
|
|
void opFetchMemberShort(Compiler::Generator::CodeContainer& code, bool global)
|
|
{
|
|
code.push_back(Compiler::Generator::segment5(global ? 68 : 62));
|
|
}
|
|
|
|
void opFetchMemberLong(Compiler::Generator::CodeContainer& code, bool global)
|
|
{
|
|
code.push_back(Compiler::Generator::segment5(global ? 69 : 63));
|
|
}
|
|
|
|
void opFetchMemberFloat(Compiler::Generator::CodeContainer& code, bool global)
|
|
{
|
|
code.push_back(Compiler::Generator::segment5(global ? 70 : 64));
|
|
}
|
|
}
|
|
|
|
namespace Compiler::Generator
|
|
{
|
|
void pushInt(CodeContainer& code, Literals& literals, int value)
|
|
{
|
|
int index = literals.addInteger(value);
|
|
opPushInt(code, index);
|
|
opFetchIntLiteral(code);
|
|
}
|
|
|
|
void pushFloat(CodeContainer& code, Literals& literals, float value)
|
|
{
|
|
int index = literals.addFloat(value);
|
|
opPushInt(code, index);
|
|
opFetchFloatLiteral(code);
|
|
}
|
|
|
|
void pushString(CodeContainer& code, Literals& literals, const std::string& value)
|
|
{
|
|
int index = literals.addString(value);
|
|
opPushInt(code, index);
|
|
}
|
|
|
|
void assignToLocal(CodeContainer& code, char localType, int localIndex, const CodeContainer& value, char valueType)
|
|
{
|
|
opPushInt(code, localIndex);
|
|
|
|
std::copy(value.begin(), value.end(), std::back_inserter(code));
|
|
|
|
if (localType != valueType)
|
|
{
|
|
if (localType == 'f' && valueType == 'l')
|
|
{
|
|
opIntToFloat(code);
|
|
}
|
|
else if ((localType == 'l' || localType == 's') && valueType == 'f')
|
|
{
|
|
opFloatToInt(code);
|
|
}
|
|
}
|
|
|
|
switch (localType)
|
|
{
|
|
case 'f':
|
|
|
|
opStoreLocalFloat(code);
|
|
break;
|
|
|
|
case 's':
|
|
|
|
opStoreLocalShort(code);
|
|
break;
|
|
|
|
case 'l':
|
|
|
|
opStoreLocalLong(code);
|
|
break;
|
|
|
|
default:
|
|
|
|
assert(0);
|
|
}
|
|
}
|
|
|
|
void negate(CodeContainer& code, char valueType)
|
|
{
|
|
switch (valueType)
|
|
{
|
|
case 'l':
|
|
|
|
opNegateInt(code);
|
|
break;
|
|
|
|
case 'f':
|
|
|
|
opNegateFloat(code);
|
|
break;
|
|
|
|
default:
|
|
|
|
assert(0);
|
|
}
|
|
}
|
|
|
|
void add(CodeContainer& code, char valueType1, char valueType2)
|
|
{
|
|
if (valueType1 == 'l' && valueType2 == 'l')
|
|
{
|
|
opAddInt(code);
|
|
}
|
|
else
|
|
{
|
|
if (valueType1 == 'l')
|
|
opIntToFloat1(code);
|
|
|
|
if (valueType2 == 'l')
|
|
opIntToFloat(code);
|
|
|
|
opAddFloat(code);
|
|
}
|
|
}
|
|
|
|
void sub(CodeContainer& code, char valueType1, char valueType2)
|
|
{
|
|
if (valueType1 == 'l' && valueType2 == 'l')
|
|
{
|
|
opSubInt(code);
|
|
}
|
|
else
|
|
{
|
|
if (valueType1 == 'l')
|
|
opIntToFloat1(code);
|
|
|
|
if (valueType2 == 'l')
|
|
opIntToFloat(code);
|
|
|
|
opSubFloat(code);
|
|
}
|
|
}
|
|
|
|
void mul(CodeContainer& code, char valueType1, char valueType2)
|
|
{
|
|
if (valueType1 == 'l' && valueType2 == 'l')
|
|
{
|
|
opMulInt(code);
|
|
}
|
|
else
|
|
{
|
|
if (valueType1 == 'l')
|
|
opIntToFloat1(code);
|
|
|
|
if (valueType2 == 'l')
|
|
opIntToFloat(code);
|
|
|
|
opMulFloat(code);
|
|
}
|
|
}
|
|
|
|
void div(CodeContainer& code, char valueType1, char valueType2)
|
|
{
|
|
if (valueType1 == 'l' && valueType2 == 'l')
|
|
{
|
|
opDivInt(code);
|
|
}
|
|
else
|
|
{
|
|
if (valueType1 == 'l')
|
|
opIntToFloat1(code);
|
|
|
|
if (valueType2 == 'l')
|
|
opIntToFloat(code);
|
|
|
|
opDivFloat(code);
|
|
}
|
|
}
|
|
|
|
void convert(CodeContainer& code, char fromType, char toType)
|
|
{
|
|
if (fromType != toType)
|
|
{
|
|
if (fromType == 'f' && toType == 'l')
|
|
opFloatToInt(code);
|
|
else if (fromType == 'l' && toType == 'f')
|
|
opIntToFloat(code);
|
|
else
|
|
throw std::logic_error("illegal type conversion");
|
|
}
|
|
}
|
|
|
|
void exit(CodeContainer& code)
|
|
{
|
|
opReturn(code);
|
|
}
|
|
|
|
void message(CodeContainer& code, Literals& literals, const std::string& message, int buttons)
|
|
{
|
|
assert(buttons >= 0);
|
|
|
|
if (buttons >= 256)
|
|
throw std::runtime_error("A message box can't have more than 255 buttons");
|
|
|
|
int index = literals.addString(message);
|
|
|
|
opPushInt(code, index);
|
|
opMessageBox(code, buttons);
|
|
}
|
|
|
|
void report(CodeContainer& code, Literals& literals, const std::string& message)
|
|
{
|
|
int index = literals.addString(message);
|
|
|
|
opPushInt(code, index);
|
|
opReport(code);
|
|
}
|
|
|
|
void fetchLocal(CodeContainer& code, char localType, int localIndex)
|
|
{
|
|
opPushInt(code, localIndex);
|
|
|
|
switch (localType)
|
|
{
|
|
case 'f':
|
|
|
|
opFetchLocalFloat(code);
|
|
break;
|
|
|
|
case 's':
|
|
|
|
opFetchLocalShort(code);
|
|
break;
|
|
|
|
case 'l':
|
|
|
|
opFetchLocalLong(code);
|
|
break;
|
|
|
|
default:
|
|
|
|
assert(0);
|
|
}
|
|
}
|
|
|
|
void jump(CodeContainer& code, int offset)
|
|
{
|
|
if (offset > 0)
|
|
opJumpForward(code, offset);
|
|
else if (offset < 0)
|
|
opJumpBackward(code, -offset);
|
|
else
|
|
throw std::logic_error("infinite loop");
|
|
}
|
|
|
|
void jumpOnZero(CodeContainer& code, int offset)
|
|
{
|
|
opSkipOnNonZero(code);
|
|
|
|
if (offset < 0)
|
|
--offset; // compensate for skip instruction
|
|
|
|
jump(code, offset);
|
|
}
|
|
|
|
void compare(CodeContainer& code, char op, char valueType1, char valueType2)
|
|
{
|
|
if (valueType1 == 'l' && valueType2 == 'l')
|
|
{
|
|
switch (op)
|
|
{
|
|
case 'e':
|
|
opEqualInt(code);
|
|
break;
|
|
case 'n':
|
|
opNonEqualInt(code);
|
|
break;
|
|
case 'l':
|
|
opLessThanInt(code);
|
|
break;
|
|
case 'L':
|
|
opLessOrEqualInt(code);
|
|
break;
|
|
case 'g':
|
|
opGreaterThanInt(code);
|
|
break;
|
|
case 'G':
|
|
opGreaterOrEqualInt(code);
|
|
break;
|
|
|
|
default:
|
|
|
|
assert(0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (valueType1 == 'l')
|
|
opIntToFloat1(code);
|
|
|
|
if (valueType2 == 'l')
|
|
opIntToFloat(code);
|
|
|
|
switch (op)
|
|
{
|
|
case 'e':
|
|
opEqualFloat(code);
|
|
break;
|
|
case 'n':
|
|
opNonEqualFloat(code);
|
|
break;
|
|
case 'l':
|
|
opLessThanFloat(code);
|
|
break;
|
|
case 'L':
|
|
opLessOrEqualFloat(code);
|
|
break;
|
|
case 'g':
|
|
opGreaterThanFloat(code);
|
|
break;
|
|
case 'G':
|
|
opGreaterOrEqualFloat(code);
|
|
break;
|
|
|
|
default:
|
|
|
|
assert(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
void assignToGlobal(CodeContainer& code, Literals& literals, char localType, const std::string& name,
|
|
const CodeContainer& value, char valueType)
|
|
{
|
|
int index = literals.addString(name);
|
|
|
|
opPushInt(code, index);
|
|
|
|
std::copy(value.begin(), value.end(), std::back_inserter(code));
|
|
|
|
if (localType != valueType)
|
|
{
|
|
if (localType == 'f' && (valueType == 'l' || valueType == 's'))
|
|
{
|
|
opIntToFloat(code);
|
|
}
|
|
else if ((localType == 'l' || localType == 's') && valueType == 'f')
|
|
{
|
|
opFloatToInt(code);
|
|
}
|
|
}
|
|
|
|
switch (localType)
|
|
{
|
|
case 'f':
|
|
|
|
opStoreGlobalFloat(code);
|
|
break;
|
|
|
|
case 's':
|
|
|
|
opStoreGlobalShort(code);
|
|
break;
|
|
|
|
case 'l':
|
|
|
|
opStoreGlobalLong(code);
|
|
break;
|
|
|
|
default:
|
|
|
|
assert(0);
|
|
}
|
|
}
|
|
|
|
void fetchGlobal(CodeContainer& code, Literals& literals, char localType, const std::string& name)
|
|
{
|
|
int index = literals.addString(name);
|
|
|
|
opPushInt(code, index);
|
|
|
|
switch (localType)
|
|
{
|
|
case 'f':
|
|
|
|
opFetchGlobalFloat(code);
|
|
break;
|
|
|
|
case 's':
|
|
|
|
opFetchGlobalShort(code);
|
|
break;
|
|
|
|
case 'l':
|
|
|
|
opFetchGlobalLong(code);
|
|
break;
|
|
|
|
default:
|
|
|
|
assert(0);
|
|
}
|
|
}
|
|
|
|
void assignToMember(CodeContainer& code, Literals& literals, char localType, const std::string& name,
|
|
const std::string& id, const CodeContainer& value, char valueType, bool global)
|
|
{
|
|
int index = literals.addString(name);
|
|
|
|
opPushInt(code, index);
|
|
|
|
index = literals.addString(id);
|
|
|
|
opPushInt(code, index);
|
|
|
|
std::copy(value.begin(), value.end(), std::back_inserter(code));
|
|
|
|
if (localType != valueType)
|
|
{
|
|
if (localType == 'f' && (valueType == 'l' || valueType == 's'))
|
|
{
|
|
opIntToFloat(code);
|
|
}
|
|
else if ((localType == 'l' || localType == 's') && valueType == 'f')
|
|
{
|
|
opFloatToInt(code);
|
|
}
|
|
}
|
|
|
|
switch (localType)
|
|
{
|
|
case 'f':
|
|
|
|
opStoreMemberFloat(code, global);
|
|
break;
|
|
|
|
case 's':
|
|
|
|
opStoreMemberShort(code, global);
|
|
break;
|
|
|
|
case 'l':
|
|
|
|
opStoreMemberLong(code, global);
|
|
break;
|
|
|
|
default:
|
|
|
|
assert(0);
|
|
}
|
|
}
|
|
|
|
void fetchMember(CodeContainer& code, Literals& literals, char localType, const std::string& name,
|
|
const std::string& id, bool global)
|
|
{
|
|
int index = literals.addString(name);
|
|
|
|
opPushInt(code, index);
|
|
|
|
index = literals.addString(id);
|
|
|
|
opPushInt(code, index);
|
|
|
|
switch (localType)
|
|
{
|
|
case 'f':
|
|
|
|
opFetchMemberFloat(code, global);
|
|
break;
|
|
|
|
case 's':
|
|
|
|
opFetchMemberShort(code, global);
|
|
break;
|
|
|
|
case 'l':
|
|
|
|
opFetchMemberLong(code, global);
|
|
break;
|
|
|
|
default:
|
|
|
|
assert(0);
|
|
}
|
|
}
|
|
}
|