Testing new vm/language WIP
This commit is contained in:
parent
6e44ebc358
commit
939a04d3c7
@ -20,11 +20,14 @@
|
||||
#include "dialogues/TextPrompt.h"
|
||||
#include "dialogues/ConfirmPrompt.h"
|
||||
#include "simulation/Simulation.h"
|
||||
#include "virtualmachine/VirtualMachine.h"
|
||||
#include "game/GameModel.h"
|
||||
#include "LuaScriptHelper.h"
|
||||
#include "client/HTTP.h"
|
||||
|
||||
//#include "virtualmachine/VirtualMachine.h"
|
||||
#include "pim/Parser.h"
|
||||
#include "pim/Machine.h"
|
||||
|
||||
#include "LuaBit.h"
|
||||
|
||||
#include "LuaWindow.h"
|
||||
@ -665,17 +668,24 @@ void LuaScriptInterface::initElementsAPI()
|
||||
}
|
||||
}
|
||||
|
||||
vm::VirtualMachine * LuaScriptInterface::updateVirtualMachines[PT_NUM];
|
||||
pim::VirtualMachine * LuaScriptInterface::updateVirtualMachines[PT_NUM];
|
||||
|
||||
int LuaScriptInterface::updateVM(UPDATE_FUNC_ARGS)
|
||||
{
|
||||
vm::VirtualMachine * vMachine = updateVirtualMachines[parts[i].type];
|
||||
pim::VirtualMachine * machine = updateVirtualMachines[parts[i].type];
|
||||
|
||||
machine->CSPush(i);
|
||||
machine->CSPush(x);
|
||||
machine->CSPush(y);
|
||||
machine->Call(0);
|
||||
|
||||
|
||||
/*vm::VirtualMachine * vMachine = updateVirtualMachines[parts[i].type];
|
||||
|
||||
vm::word w;
|
||||
int argAddr = 0, argCount = 5;
|
||||
vMachine->sim = sim;
|
||||
|
||||
/* Set up call. */
|
||||
vMachine->OpPUSH(w); //Pointless null in stack
|
||||
w.int4 = (argCount + 2) * sizeof(vm::word);
|
||||
vMachine->OpENTER(w);
|
||||
@ -696,7 +706,7 @@ int LuaScriptInterface::updateVM(UPDATE_FUNC_ARGS)
|
||||
w.int4 = (argCount + 2) * sizeof(vm::word);
|
||||
vMachine->OpLEAVE(w);
|
||||
vMachine->OpPOP(w); //Pop pointless null
|
||||
vMachine->End();
|
||||
vMachine->End();*/
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1017,7 +1027,7 @@ int LuaScriptInterface::elements_property(lua_State * l)
|
||||
}
|
||||
else if(lua_type(l, 3) == LUA_TLIGHTUSERDATA)
|
||||
{
|
||||
updateVirtualMachines[id] = (vm::VirtualMachine*)lua_touserdata(l, 3);
|
||||
updateVirtualMachines[id] = (pim::VirtualMachine*)lua_touserdata(l, 3);
|
||||
luacon_sim->elements[id].Update = &updateVM;
|
||||
}
|
||||
else if(lua_type(l, 3) == LUA_TBOOLEAN && !lua_toboolean(l, 3))
|
||||
@ -1142,7 +1152,7 @@ void LuaScriptInterface::initVirtualMachineAPI()
|
||||
|
||||
int LuaScriptInterface::virtualMachine_loadProgram(lua_State * l)
|
||||
{
|
||||
luaL_checktype(l, 1, LUA_TSTRING);
|
||||
/*luaL_checktype(l, 1, LUA_TSTRING);
|
||||
|
||||
vm::VirtualMachine * newVM = new vm::VirtualMachine(1);
|
||||
try
|
||||
@ -1156,7 +1166,19 @@ int LuaScriptInterface::virtualMachine_loadProgram(lua_State * l)
|
||||
{
|
||||
return luaL_error(l, "Unable to load program");
|
||||
}
|
||||
lua_pushlightuserdata(l, newVM);
|
||||
lua_pushlightuserdata(l, newVM);*/
|
||||
std::string programSource(lua_tostring(l, 1));
|
||||
std::stringstream input(programSource);
|
||||
|
||||
|
||||
pim::compiler::Parser * parser = new pim::compiler::Parser(input);
|
||||
|
||||
std::vector<unsigned char> programData = parser->Compile();
|
||||
|
||||
pim::VirtualMachine * machine = new pim::VirtualMachine(luacon_sim);
|
||||
machine->LoadProgram(programData);
|
||||
|
||||
lua_pushlightuserdata(l, machine);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -23,7 +23,7 @@ namespace ui
|
||||
class Window;
|
||||
}
|
||||
|
||||
namespace vm
|
||||
namespace pim
|
||||
{
|
||||
class VirtualMachine;
|
||||
}
|
||||
@ -65,7 +65,7 @@ class LuaScriptInterface: public CommandInterface {
|
||||
static int renderer_colourMode(lua_State * l);
|
||||
|
||||
//Elements
|
||||
static vm::VirtualMachine * updateVirtualMachines[PT_NUM];
|
||||
static pim::VirtualMachine * updateVirtualMachines[PT_NUM];
|
||||
static int updateVM(UPDATE_FUNC_ARGS);
|
||||
//
|
||||
void initElementsAPI();
|
||||
|
11
src/dialogues/LegacyDialogues.h
Normal file
11
src/dialogues/LegacyDialogues.h
Normal file
@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
//Legacy blocking prompts
|
||||
//This are not implemented here, but rather in the engine bootstrapper
|
||||
bool ConfirmUI(std::string title, std::string message, std::string confirmText) {}
|
||||
|
||||
void ErrorUI(std::string title, std::string message) {}
|
||||
|
||||
void InformationUI(std::string title, std::string message) {}
|
||||
|
||||
std::string MessagePromptUI(std::string title, std::string message, std::string text, std::string placeholder) {}
|
30
src/interface/LuaProgressBar.h
Normal file
30
src/interface/LuaProgressBar.h
Normal file
@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
extern "C" {
|
||||
#include "lua.h"
|
||||
#include "lauxlib.h"
|
||||
#include "lualib.h"
|
||||
}
|
||||
|
||||
#include "LuaLuna.h"
|
||||
#include "LuaComponent.h"
|
||||
|
||||
namespace ui
|
||||
{
|
||||
class ProgressBar;
|
||||
}
|
||||
|
||||
class LuaScriptInterface;
|
||||
|
||||
class LuaProgressBar: public LuaComponent
|
||||
{
|
||||
ui::ProgressBar * progressBar;
|
||||
int onValueChangedFunction;
|
||||
int value(lua_State * l);
|
||||
public:
|
||||
static const char className[];
|
||||
static Luna<LuaProgressBar>::RegType methods[];
|
||||
|
||||
LuaProgressBar(lua_State * l);
|
||||
~LuaProgressBar();
|
||||
};
|
326
src/pim/Generator.cpp
Normal file
326
src/pim/Generator.cpp
Normal file
@ -0,0 +1,326 @@
|
||||
//Code generator for bytecode
|
||||
#include <sstream>
|
||||
#include "Format.h"
|
||||
#include "Generator.h"
|
||||
#include "Opcodes.h"
|
||||
namespace pim
|
||||
{
|
||||
namespace compiler
|
||||
{
|
||||
Generator::Generator() :
|
||||
output(std::cout),
|
||||
labelCounter(0),
|
||||
programCounter(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Generator::defineLabel(std::string label)
|
||||
{
|
||||
Label newLabel;
|
||||
newLabel.Name = label;
|
||||
newLabel.Position = programCounter;//program.size();
|
||||
labelPositions.push_back(newLabel);
|
||||
}
|
||||
|
||||
void Generator::writeOpcode(int opcode)
|
||||
{
|
||||
programCounter++;
|
||||
program.push_back(opcode);
|
||||
}
|
||||
|
||||
void Generator::writeConstant(std::string constant)
|
||||
{
|
||||
writeConstant(format::StringToNumber<int>(constant));
|
||||
}
|
||||
|
||||
void Generator::writeConstant(int constant)
|
||||
{
|
||||
program.push_back(constant & 0xFF);
|
||||
program.push_back((constant>>8) & 0xFF);
|
||||
program.push_back((constant>>16) & 0xFF);
|
||||
program.push_back((constant>>24) & 0xFF);
|
||||
}
|
||||
|
||||
void Generator::writeConstantPlaceholder(std::string label)
|
||||
{
|
||||
placeholders.push_back(Placeholder(program.size(), label));
|
||||
program.push_back(0);
|
||||
program.push_back(0);
|
||||
program.push_back(0);
|
||||
program.push_back(0);
|
||||
}
|
||||
|
||||
void Generator::writeConstantPlaceholder(int * value)
|
||||
{
|
||||
valuePlaceholders.push_back(ValuePlaceholder(program.size(), value));
|
||||
program.push_back(0);
|
||||
program.push_back(0);
|
||||
program.push_back(0);
|
||||
program.push_back(0);
|
||||
}
|
||||
|
||||
std::vector<unsigned char> Generator::Finish()
|
||||
{
|
||||
for(std::vector<Placeholder>::iterator iter = placeholders.begin(), end = placeholders.end(); iter != end; ++iter)
|
||||
{
|
||||
bool found = false;
|
||||
Placeholder cPosition = *iter;
|
||||
for(std::vector<Label>::iterator iter2 = labelPositions.begin(), end2 = labelPositions.end(); iter2 != end2; ++iter2)
|
||||
{
|
||||
Label cLabel = *iter2;
|
||||
if(cPosition.second == cLabel.Name)
|
||||
{
|
||||
std::cout << "Setting placeholder at " << cPosition.first << " with " << cLabel.Position << " for" << cPosition.second << std::endl;
|
||||
found = true;
|
||||
program[cPosition.first] = cLabel.Position & 0xFF;
|
||||
program[cPosition.first+1] = (cLabel.Position >> 8) & 0xFF;
|
||||
program[cPosition.first+2] = (cLabel.Position >> 16) & 0xFF;
|
||||
program[cPosition.first+3] = (cLabel.Position >> 24) & 0xFF;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!found)
|
||||
throw SymbolNotFoundException(cPosition.second);
|
||||
}
|
||||
|
||||
for(std::vector<ValuePlaceholder>::iterator iter = valuePlaceholders.begin(), end = valuePlaceholders.end(); iter != end; ++iter)
|
||||
{
|
||||
ValuePlaceholder cPosition = *iter;
|
||||
int value = *cPosition.second;
|
||||
|
||||
std::cout << "Setting value placeholder at " << cPosition.first << " with " << value << std::endl;
|
||||
|
||||
|
||||
program[cPosition.first] = value & 0xFF;
|
||||
program[cPosition.first+1] = (value >> 8) & 0xFF;
|
||||
program[cPosition.first+2] = (value >> 16) & 0xFF;
|
||||
program[cPosition.first+3] = (value >> 24) & 0xFF;
|
||||
}
|
||||
|
||||
return program;
|
||||
}
|
||||
|
||||
std::string Generator::UniqueLabel(std::string prefix)
|
||||
{
|
||||
std::stringstream label;
|
||||
label << prefix;
|
||||
label << "_";
|
||||
label << labelCounter;
|
||||
label << "_";
|
||||
return label.str();
|
||||
}
|
||||
|
||||
void Generator::PushScope(std::string label)
|
||||
{
|
||||
scopes.push(currentScope);
|
||||
Scope * prevScope = currentScope;
|
||||
currentScope = new Scope();
|
||||
defineLabel(label);
|
||||
}
|
||||
|
||||
void Generator::PushLocalScope(std::string label)
|
||||
{
|
||||
scopes.push(currentScope);
|
||||
Scope * prevScope = currentScope;
|
||||
currentScope = new Scope();
|
||||
currentScope->Definitions.insert(currentScope->Definitions.begin(), prevScope->Definitions.begin(), prevScope->Definitions.end());
|
||||
currentScope->FrameSize = prevScope->FrameSize;
|
||||
defineLabel(label);
|
||||
}
|
||||
|
||||
void Generator::PopScope()
|
||||
{
|
||||
|
||||
writeOpcode(Opcode::Return);
|
||||
writeConstant(currentScope->LocalFrameSize);
|
||||
currentScope = scopes.top();
|
||||
scopes.pop();
|
||||
}
|
||||
|
||||
void Generator::ScopeLabel(std::string label)
|
||||
{
|
||||
//defineLabelwriteOpcode("." << label);
|
||||
defineLabel(label);
|
||||
}
|
||||
|
||||
void Generator::LocalEnter()
|
||||
{
|
||||
writeOpcode(Opcode::LocalEnter);
|
||||
writeConstantPlaceholder(&(currentScope->LocalFrameSize));
|
||||
}
|
||||
|
||||
void Generator::ScopeVariableType(int type)
|
||||
{
|
||||
variableType = type;
|
||||
}
|
||||
|
||||
void Generator::ScopeVariable(std::string label)
|
||||
{
|
||||
currentScope->Definitions.push_back(Definition(label, variableType, currentScope->FrameSize));
|
||||
currentScope->FrameSize += 4;
|
||||
currentScope->LocalFrameSize += 4;
|
||||
}
|
||||
|
||||
void Generator::PushVariableAddress(std::string label)
|
||||
{
|
||||
//writeOpcode("address"); << " " << currentScope->GetDefinition(label).StackPosition
|
||||
}
|
||||
|
||||
void Generator::LoadVariable(std::string label)
|
||||
{
|
||||
writeOpcode(Opcode::Load);
|
||||
writeConstant(currentScope->GetDefinition(label).StackPosition);
|
||||
}
|
||||
|
||||
void Generator::StoreVariable(std::string label)
|
||||
{
|
||||
writeOpcode(Opcode::Store);
|
||||
writeConstant(currentScope->GetDefinition(label).StackPosition);
|
||||
}
|
||||
|
||||
void Generator::Constant(std::string constant)
|
||||
{
|
||||
writeOpcode(Opcode::Constant);
|
||||
writeConstant(constant);
|
||||
}
|
||||
|
||||
void Generator::Increment(std::string constant)
|
||||
{
|
||||
writeOpcode(Opcode::Increment);
|
||||
writeConstant(constant);
|
||||
}
|
||||
|
||||
void Generator::Discard()
|
||||
{
|
||||
writeOpcode(Opcode::Discard);
|
||||
}
|
||||
|
||||
void Generator::Duplicate()
|
||||
{
|
||||
writeOpcode(Opcode::Duplicate);
|
||||
}
|
||||
|
||||
void Generator::Add()
|
||||
{
|
||||
writeOpcode(Opcode::Add);
|
||||
}
|
||||
|
||||
void Generator::Subtract()
|
||||
{
|
||||
writeOpcode(Opcode::Subtract);
|
||||
}
|
||||
|
||||
void Generator::Multiply()
|
||||
{
|
||||
writeOpcode(Opcode::Multiply);
|
||||
}
|
||||
|
||||
void Generator::Divide()
|
||||
{
|
||||
writeOpcode(Opcode::Divide);
|
||||
}
|
||||
|
||||
void Generator::Modulus()
|
||||
{
|
||||
writeOpcode(Opcode::Modulus);
|
||||
}
|
||||
|
||||
void Generator::Negate()
|
||||
{
|
||||
writeOpcode(Opcode::Negate);
|
||||
}
|
||||
|
||||
void Generator::CreateParticle()
|
||||
{
|
||||
writeOpcode(Opcode::Create);
|
||||
|
||||
}
|
||||
|
||||
void Generator::TransformParticle()
|
||||
{
|
||||
writeOpcode(Opcode::Transform);
|
||||
}
|
||||
|
||||
void Generator::GetParticle()
|
||||
{
|
||||
writeOpcode(Opcode::Get);
|
||||
}
|
||||
|
||||
void Generator::GetPosition()
|
||||
{
|
||||
writeOpcode(Opcode::Position);
|
||||
}
|
||||
|
||||
void Generator::KillParticle()
|
||||
{
|
||||
writeOpcode(Opcode::Kill);
|
||||
}
|
||||
|
||||
|
||||
void Generator::IntegerToDecimal()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Generator::DecimalToInteger()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
void Generator::JumpEqual(std::string label)
|
||||
{
|
||||
writeOpcode(Opcode::JumpEqual);
|
||||
writeConstantPlaceholder(label);
|
||||
}
|
||||
|
||||
void Generator::JumpNotEqual(std::string label)
|
||||
{
|
||||
writeOpcode(Opcode::JumpNotEqual);
|
||||
writeConstantPlaceholder(label);
|
||||
}
|
||||
|
||||
void Generator::JumpGreater(std::string label)
|
||||
{
|
||||
writeOpcode(Opcode::JumpGreater);
|
||||
writeConstantPlaceholder(label);
|
||||
}
|
||||
|
||||
void Generator::JumpGreaterEqual(std::string label)
|
||||
{
|
||||
writeOpcode(Opcode::JumpGreaterEqual);
|
||||
writeConstantPlaceholder(label);
|
||||
}
|
||||
|
||||
void Generator::JumpLess(std::string label)
|
||||
{
|
||||
writeOpcode(Opcode::JumpLess);
|
||||
writeConstantPlaceholder(label);
|
||||
}
|
||||
|
||||
void Generator::JumpLessEqual(std::string label)
|
||||
{
|
||||
writeOpcode(Opcode::JumpLessEqual);
|
||||
writeConstantPlaceholder(label);
|
||||
}
|
||||
|
||||
void Generator::Jump(std::string label)
|
||||
{
|
||||
writeOpcode(Opcode::Jump);
|
||||
writeConstantPlaceholder(label);
|
||||
}
|
||||
|
||||
|
||||
void Generator::Call(int arguments, std::string label)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Generator::Return()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
164
src/pim/Generator.h
Normal file
164
src/pim/Generator.h
Normal file
@ -0,0 +1,164 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
#include <stack>
|
||||
#include <iostream>
|
||||
#include "Token.h"
|
||||
namespace pim
|
||||
{
|
||||
namespace compiler
|
||||
{
|
||||
class VariableNotFoundException: public std::exception
|
||||
{
|
||||
char * error;
|
||||
public:
|
||||
VariableNotFoundException(std::string variable) {
|
||||
error = strdup(std::string("Could not find the variable \""+variable+"\" in the current scope").c_str());
|
||||
}
|
||||
const char * what() const throw()
|
||||
{
|
||||
return error;
|
||||
}
|
||||
~VariableNotFoundException() throw() {};
|
||||
};
|
||||
|
||||
class SymbolNotFoundException: public std::exception
|
||||
{
|
||||
char * error;
|
||||
public:
|
||||
SymbolNotFoundException(std::string variable) {
|
||||
error = strdup(std::string("Could not find the symbol \""+variable+"\".").c_str());
|
||||
}
|
||||
const char * what() const throw()
|
||||
{
|
||||
return error;
|
||||
}
|
||||
~SymbolNotFoundException() throw() {};
|
||||
};
|
||||
class Definition
|
||||
{
|
||||
public:
|
||||
enum { Integer = Token::IntegerSymbol, Decimal = Token::DecimalSymbol };
|
||||
std::string Name;
|
||||
int Type;
|
||||
int StackPosition;
|
||||
Definition(std::string name, int type, int position) :
|
||||
Type(type),
|
||||
Name(name),
|
||||
StackPosition(position)
|
||||
{
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
struct Label
|
||||
{
|
||||
std::string Name;
|
||||
int Position;
|
||||
};
|
||||
|
||||
class Scope
|
||||
{
|
||||
public:
|
||||
std::vector<Definition> Definitions;
|
||||
std::vector<Label> Labels;
|
||||
int FrameSize;
|
||||
int LocalFrameSize;
|
||||
Scope():
|
||||
FrameSize(0),
|
||||
LocalFrameSize(0)
|
||||
{
|
||||
|
||||
}
|
||||
Definition GetDefinition(std::string name)
|
||||
{
|
||||
for(std::vector<Definition>::iterator iter = Definitions.begin(), end = Definitions.end(); iter != end; ++iter)
|
||||
{
|
||||
if((*iter).Name == name)
|
||||
return *iter;
|
||||
}
|
||||
throw VariableNotFoundException(name);
|
||||
}
|
||||
};
|
||||
|
||||
class Generator
|
||||
{
|
||||
int variableType;
|
||||
std::stack<Scope*> scopes;
|
||||
Scope * currentScope;
|
||||
std::ostream & output;
|
||||
int labelCounter;
|
||||
int programCounter;
|
||||
|
||||
typedef std::pair<int, std::string> Placeholder;
|
||||
std::vector<Placeholder> placeholders;
|
||||
|
||||
typedef std::pair<int, int*> ValuePlaceholder;
|
||||
std::vector<ValuePlaceholder> valuePlaceholders;
|
||||
|
||||
std::vector<Label> labelPositions;
|
||||
|
||||
std::vector<unsigned char> program;
|
||||
|
||||
void defineLabel(std::string label);
|
||||
void writeOpcode(int opcode);
|
||||
void writeConstant(std::string constant);
|
||||
void writeConstant(int constant);
|
||||
void writeConstantPlaceholder(std::string label);
|
||||
void writeConstantPlaceholder(int * value);
|
||||
|
||||
public:
|
||||
Generator();
|
||||
|
||||
std::vector<unsigned char> Finish();
|
||||
|
||||
std::string UniqueLabel(std::string prefix);
|
||||
|
||||
void PushScope(std::string label);
|
||||
void PushLocalScope(std::string label);
|
||||
void LocalEnter();
|
||||
void PopScope();
|
||||
|
||||
void ScopeLabel(std::string label);
|
||||
void ScopeVariableType(int type);
|
||||
void ScopeVariable(std::string label);
|
||||
|
||||
void PushVariableAddress(std::string label);
|
||||
// void Store();
|
||||
void LoadVariable(std::string label);
|
||||
void StoreVariable(std::string label);
|
||||
|
||||
void Duplicate();
|
||||
void Discard();
|
||||
void Constant(std::string constant);
|
||||
void Increment(std::string constant);
|
||||
void Add();
|
||||
void Subtract();
|
||||
void Multiply();
|
||||
void Divide();
|
||||
void Modulus();
|
||||
void Negate();
|
||||
|
||||
void TransformParticle();
|
||||
void CreateParticle();
|
||||
void GetParticle();
|
||||
void GetPosition();
|
||||
void KillParticle();
|
||||
|
||||
void IntegerToDecimal();
|
||||
void DecimalToInteger();
|
||||
|
||||
void JumpEqual(std::string label);
|
||||
void JumpNotEqual(std::string label);
|
||||
void JumpGreater(std::string label);
|
||||
void JumpGreaterEqual(std::string label);
|
||||
void JumpLess(std::string label);
|
||||
void JumpLessEqual(std::string label);
|
||||
void Jump(std::string label);
|
||||
|
||||
void Call(int arguments, std::string label);
|
||||
void Return();
|
||||
};
|
||||
}
|
||||
}
|
271
src/pim/Machine.cpp
Normal file
271
src/pim/Machine.cpp
Normal file
@ -0,0 +1,271 @@
|
||||
//Virtual machine
|
||||
|
||||
#include <iostream>
|
||||
#include "Machine.h"
|
||||
#include "Opcodes.h"
|
||||
#include "simulation/Simulation.h"
|
||||
namespace pim
|
||||
{
|
||||
/*unsigned char * rom;
|
||||
int romSize;
|
||||
int romMask;
|
||||
|
||||
unsigned char * ram;
|
||||
int ramSize;
|
||||
int ramMask;
|
||||
|
||||
int programStack;
|
||||
int callStack;*/
|
||||
|
||||
VirtualMachine::VirtualMachine(Simulation * simulation) :
|
||||
rom(NULL),
|
||||
ram(NULL),
|
||||
sim(simulation)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void VirtualMachine::LoadProgram(std::vector<unsigned char> programData)
|
||||
{
|
||||
int lastBit = 0;
|
||||
|
||||
romSize = programData.size();
|
||||
|
||||
for (lastBit = 0; romSize > (1 << lastBit); lastBit++ ) { }
|
||||
romSize = 1 << lastBit;
|
||||
romMask = romSize - 1;
|
||||
|
||||
rom = new Instruction[romSize];
|
||||
|
||||
int programPosition = 0;
|
||||
int pc = 0;
|
||||
while(programPosition < programData.size())
|
||||
{
|
||||
int argSize = 0;
|
||||
Instruction instruction;
|
||||
instruction.Opcode = programData[programPosition++];
|
||||
if(argSize = OpcodeArgSize(instruction.Opcode))
|
||||
{
|
||||
if(argSize == 4)
|
||||
{
|
||||
int tempInt = 0;
|
||||
tempInt |= programData[programPosition];
|
||||
tempInt |= programData[programPosition+1] << 8;
|
||||
tempInt |= programData[programPosition+2] << 16;
|
||||
tempInt |= programData[programPosition+3] << 24;
|
||||
|
||||
|
||||
std::cout << "Got integer " << tempInt << std::endl;
|
||||
//std::cout << "Got byte " << (int)(programData[programPosition]) << std::endl;
|
||||
//std::cout << "Got byte " << (int)(programData[programPosition+1]) << std::endl;
|
||||
//std::cout << "Got byte " << (int)(programData[programPosition+2]) << std::endl;
|
||||
//std::cout << "Got byte " << (int)(programData[programPosition+3]) << std::endl;
|
||||
|
||||
//*(int*)&rom[programPosition] = tempInt;
|
||||
instruction.Parameter.Integer = tempInt;
|
||||
|
||||
programPosition += 4;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
instruction.Parameter.Integer = 0;
|
||||
}
|
||||
rom[pc++] = instruction;
|
||||
}
|
||||
romSize = pc;
|
||||
//std::copy(programData.begin(), programData.end(), rom);
|
||||
|
||||
|
||||
ramSize = 1024;
|
||||
ramMask = ramSize - 1;
|
||||
|
||||
ram = new unsigned char[ramSize];
|
||||
programStack = ramSize-1;
|
||||
callStack = ramSize-260;
|
||||
|
||||
framePointer = callStack;
|
||||
callStack += WORDSIZE; //Since there's nothing on the stack, it shouldn't point to the item on the bottom
|
||||
}
|
||||
|
||||
int VirtualMachine::OpcodeArgSize(int opcode)
|
||||
{
|
||||
switch(opcode)
|
||||
{
|
||||
case Opcode::Load:
|
||||
case Opcode::Store:
|
||||
case Opcode::Constant:
|
||||
case Opcode::Increment:
|
||||
case Opcode::JumpEqual:
|
||||
case Opcode::JumpNotEqual:
|
||||
case Opcode::JumpGreater:
|
||||
case Opcode::JumpGreaterEqual:
|
||||
case Opcode::JumpLess:
|
||||
case Opcode::JumpLessEqual:
|
||||
case Opcode::Jump:
|
||||
case Opcode::Return:
|
||||
case Opcode::LocalEnter:
|
||||
return 4;
|
||||
case Opcode::Discard:
|
||||
case Opcode::Duplicate:
|
||||
case Opcode::Add:
|
||||
case Opcode::Subtract:
|
||||
case Opcode::Multiply:
|
||||
case Opcode::Divide:
|
||||
case Opcode::Modulus:
|
||||
case Opcode::Negate:
|
||||
case Opcode::Create:
|
||||
case Opcode::Transform:
|
||||
case Opcode::Get:
|
||||
case Opcode::Position:
|
||||
case Opcode::Kill:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void VirtualMachine::Run()
|
||||
{
|
||||
//std::cout << "CS: " << callStack << " PS: " << programStack << std::endl;
|
||||
//std::string names[] = { "Load", "Store", "Constant", "Increment", "Discard", "Duplicate", "Add", "Subtract", "Multiply", "Divide", "Modulus", "Negate", "Create", "Transform", "Get", "Position", "Kill", "JumpEqual", "JumpNotEqual", "JumpGreater", "JumpGreaterEqual", "JumpLess", "JumpLessEqual", "Jump", "Return", "LocalEnter"};
|
||||
|
||||
Word temp1;
|
||||
Word temp2;
|
||||
Word temp3;
|
||||
Word temp4;
|
||||
int temp;
|
||||
while(programCounter < romSize)
|
||||
{
|
||||
Word argument = rom[programCounter].Parameter;
|
||||
//std::cerr << programCounter << "\t" << names[rom[programCounter].Opcode] << "\t" << argument.Integer << std::endl;//"\t";
|
||||
switch(rom[programCounter].Opcode)
|
||||
{
|
||||
case Opcode::Load:
|
||||
PSPush(CSA(argument.Integer));
|
||||
break;
|
||||
case Opcode::Store:
|
||||
CSA(argument.Integer) = PSPop();
|
||||
break;
|
||||
case Opcode::Constant:
|
||||
PSPush(argument);
|
||||
break;
|
||||
case Opcode::Increment:
|
||||
PS().Integer += argument.Integer;
|
||||
break;
|
||||
case Opcode::Discard:
|
||||
programStack += WORDSIZE;
|
||||
break;
|
||||
case Opcode::Duplicate:
|
||||
PSPush(PS());
|
||||
break;
|
||||
case Opcode::Add:
|
||||
PSPush(PSPop().Integer + PSPop().Integer);
|
||||
break;
|
||||
case Opcode::Subtract:
|
||||
temp1 = PSPop();
|
||||
PSPush(PSPop().Integer - temp1.Integer);
|
||||
break;
|
||||
case Opcode::Multiply:
|
||||
PSPush(PSPop().Integer * PSPop().Integer);
|
||||
break;
|
||||
case Opcode::Divide:
|
||||
temp1 = PSPop();
|
||||
PSPush(PSPop().Integer / temp1.Integer);
|
||||
break;
|
||||
case Opcode::Modulus:
|
||||
temp1 = PSPop();
|
||||
PSPush(PSPop().Integer % temp1.Integer);
|
||||
break;
|
||||
case Opcode::Negate:
|
||||
PS().Integer = -PS().Integer;
|
||||
break;
|
||||
case Opcode::Create:
|
||||
temp1 = PSPop();
|
||||
temp2 = PSPop();
|
||||
temp3 = PSPop();
|
||||
PSPush(sim->create_part(PSPop().Integer, temp3.Integer, temp2.Integer, temp1.Integer));
|
||||
break;
|
||||
case Opcode::Transform:
|
||||
PSPop();
|
||||
PSPop();
|
||||
PSPush((Word)-1);
|
||||
break;
|
||||
case Opcode::Get:
|
||||
temp1 = PSPop();
|
||||
temp2 = PSPop();
|
||||
if(temp1.Integer < 0 || temp1.Integer >= YRES || temp2.Integer < 0 || temp2.Integer >= XRES || !(temp = sim->pmap[temp1.Integer][temp2.Integer]))
|
||||
{
|
||||
PSPush(-1);
|
||||
break;
|
||||
}
|
||||
PSPush(temp>>8);
|
||||
break;
|
||||
case Opcode::Position:
|
||||
temp1 = PSPop();
|
||||
if(temp1.Integer < 0 || temp1.Integer >= NPART || !sim->parts[temp1.Integer].type)
|
||||
{
|
||||
PSPush(-1);
|
||||
PSPush(-1);
|
||||
break;
|
||||
}
|
||||
PSPush((int)sim->parts[temp1.Integer].x);
|
||||
PSPush((int)sim->parts[temp1.Integer].y);
|
||||
break;
|
||||
case Opcode::Kill:
|
||||
sim->kill_part(PSPop().Integer);
|
||||
PSPush((Word)0);
|
||||
break;
|
||||
case Opcode::JumpEqual:
|
||||
if(PSPop().Integer == PSPop().Integer)
|
||||
programCounter = argument.Integer-1;
|
||||
break;
|
||||
case Opcode::JumpNotEqual:
|
||||
if(PSPop().Integer != PSPop().Integer)
|
||||
programCounter = argument.Integer-1;
|
||||
break;
|
||||
case Opcode::JumpGreater:
|
||||
temp1 = PSPop();
|
||||
if(PSPop().Integer > temp1.Integer)
|
||||
programCounter = argument.Integer-1;
|
||||
break;
|
||||
case Opcode::JumpGreaterEqual:
|
||||
temp1 = PSPop();
|
||||
if(PSPop().Integer >= temp1.Integer)
|
||||
programCounter = argument.Integer-1;
|
||||
break;
|
||||
case Opcode::JumpLess:
|
||||
temp1 = PSPop();
|
||||
if(PSPop().Integer < temp1.Integer)
|
||||
programCounter = argument.Integer-1;
|
||||
break;
|
||||
case Opcode::JumpLessEqual:
|
||||
temp1 = PSPop();
|
||||
if(PSPop().Integer <= temp1.Integer)
|
||||
programCounter = argument.Integer-1;
|
||||
break;
|
||||
case Opcode::Jump:
|
||||
programCounter = argument.Integer-1;
|
||||
break;
|
||||
case Opcode::Return:
|
||||
callStack += argument.Integer;
|
||||
break;
|
||||
case Opcode::LocalEnter:
|
||||
callStack -= argument.Integer;
|
||||
break;
|
||||
}
|
||||
//std::cout << programStack << std::endl;
|
||||
programCounter++;
|
||||
}
|
||||
//std::cout << "CS: " << callStack << " PS: " << programStack << std::endl;
|
||||
}
|
||||
|
||||
void VirtualMachine::Call(std::string entryPoint)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void VirtualMachine::Call(int entryPoint)
|
||||
{
|
||||
programCounter = entryPoint;
|
||||
Run();
|
||||
}
|
||||
}
|
88
src/pim/Machine.h
Normal file
88
src/pim/Machine.h
Normal file
@ -0,0 +1,88 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
class Simulation;
|
||||
namespace pim
|
||||
{
|
||||
union Word
|
||||
{
|
||||
int Integer;
|
||||
float Decimal;
|
||||
|
||||
Word(int integer) : Integer(integer) {}
|
||||
Word(float decimal) : Decimal(decimal) {}
|
||||
Word() {}
|
||||
};
|
||||
struct Instruction
|
||||
{
|
||||
int Opcode;
|
||||
Word Parameter;
|
||||
};
|
||||
class VirtualMachine
|
||||
{
|
||||
|
||||
#define WORDSIZE 4
|
||||
|
||||
//#define OPDEF(name) void op##name(int parameter);
|
||||
//#include "Opcodes.inl"
|
||||
//#undef OPDEF
|
||||
|
||||
Simulation * sim;
|
||||
|
||||
Instruction * rom;
|
||||
int romSize;
|
||||
int romMask;
|
||||
|
||||
unsigned char * ram;
|
||||
int ramSize;
|
||||
int ramMask;
|
||||
|
||||
#define CSA(argument) (*((Word*)&ram[framePointer-argument]))
|
||||
#define CS() (*((Word*)&ram[callStack]))
|
||||
#define PS() (*((Word*)&ram[programStack]))
|
||||
|
||||
int programStack; //Points to the item on top of the Program Stack
|
||||
int callStack; //Points to the item on top of the call stack
|
||||
int framePointer; //Points to the bottom (first item) on the current frame of the call stack
|
||||
|
||||
//Instruction * instructions;
|
||||
|
||||
int programCounter;
|
||||
|
||||
|
||||
public:
|
||||
VirtualMachine(Simulation * sim);
|
||||
int OpcodeArgSize(int opcode);
|
||||
void LoadProgram(std::vector<unsigned char> programData);
|
||||
void Run();
|
||||
void Call(std::string entryPoint);
|
||||
void Call(int entryPoint);
|
||||
inline void PSPush(Word word)
|
||||
{
|
||||
programStack -= WORDSIZE;
|
||||
PS() = word;
|
||||
}
|
||||
|
||||
inline Word PSPop()
|
||||
{
|
||||
Word word = PS();
|
||||
programStack += WORDSIZE;
|
||||
return word;
|
||||
}
|
||||
|
||||
inline void CSPush(Word word)
|
||||
{
|
||||
callStack -= WORDSIZE;
|
||||
CS() = word;
|
||||
}
|
||||
|
||||
inline Word CSPop()
|
||||
{
|
||||
Word word = CS();
|
||||
callStack += WORDSIZE;
|
||||
return word;
|
||||
}
|
||||
};
|
||||
}
|
12
src/pim/Opcodes.h
Normal file
12
src/pim/Opcodes.h
Normal file
@ -0,0 +1,12 @@
|
||||
namespace pim
|
||||
{
|
||||
struct Opcode
|
||||
{
|
||||
enum
|
||||
{
|
||||
#define OPDEF(name) name,
|
||||
#include "Opcodes.inl"
|
||||
#undef OPDEF
|
||||
};
|
||||
};
|
||||
}
|
26
src/pim/Opcodes.inl
Normal file
26
src/pim/Opcodes.inl
Normal file
@ -0,0 +1,26 @@
|
||||
OPDEF(Load)
|
||||
OPDEF(Store)
|
||||
OPDEF(Constant)
|
||||
OPDEF(Increment)
|
||||
OPDEF(Discard)
|
||||
OPDEF(Duplicate)
|
||||
OPDEF(Add)
|
||||
OPDEF(Subtract)
|
||||
OPDEF(Multiply)
|
||||
OPDEF(Divide)
|
||||
OPDEF(Modulus)
|
||||
OPDEF(Negate)
|
||||
OPDEF(Create)
|
||||
OPDEF(Transform)
|
||||
OPDEF(Get)
|
||||
OPDEF(Position)
|
||||
OPDEF(Kill)
|
||||
OPDEF(JumpEqual)
|
||||
OPDEF(JumpNotEqual)
|
||||
OPDEF(JumpGreater)
|
||||
OPDEF(JumpGreaterEqual)
|
||||
OPDEF(JumpLess)
|
||||
OPDEF(JumpLessEqual)
|
||||
OPDEF(Jump)
|
||||
OPDEF(Return)
|
||||
OPDEF(LocalEnter)
|
646
src/pim/Parser.cpp
Normal file
646
src/pim/Parser.cpp
Normal file
@ -0,0 +1,646 @@
|
||||
//Syntax analyser
|
||||
#include "Parser.h"
|
||||
namespace pim
|
||||
{
|
||||
namespace compiler
|
||||
{
|
||||
Parser::Parser(std::stringstream & source_) :
|
||||
source(source_)
|
||||
{
|
||||
scanner = new Scanner(source);
|
||||
generator = new Generator();
|
||||
|
||||
token = scanner->NextToken();
|
||||
|
||||
}
|
||||
|
||||
std::vector<unsigned char> Parser::Compile()
|
||||
{
|
||||
program();
|
||||
return generator->Finish();
|
||||
}
|
||||
|
||||
/*
|
||||
<program> ::= <function list>
|
||||
*/
|
||||
void Parser::program()
|
||||
{
|
||||
functionList();
|
||||
}
|
||||
|
||||
/*
|
||||
<function list> ::= <function> | <function> <function list>
|
||||
*/
|
||||
void Parser::functionList()
|
||||
{
|
||||
function();
|
||||
while(look(Token::FunctionSymbol))
|
||||
function();
|
||||
}
|
||||
|
||||
/*
|
||||
<function> ::= function identifier ( <declaration list> ) <block> end
|
||||
*/
|
||||
void Parser::function()
|
||||
{
|
||||
std::string functionName;
|
||||
|
||||
expect(Token::FunctionSymbol);
|
||||
|
||||
functionName = token.Source;
|
||||
//generator->ScopeLabel(functionName); //Function name
|
||||
generator->PushScope(functionName);
|
||||
expect(Token::Identifier);
|
||||
|
||||
expect(Token::LeftBracket);
|
||||
if(!accept(Token::RightBracket))
|
||||
{
|
||||
argumentList();
|
||||
expect(Token::RightBracket);
|
||||
}
|
||||
block();
|
||||
expect(Token::EndSymbol);
|
||||
generator->Return();
|
||||
|
||||
generator->PopScope();
|
||||
}
|
||||
|
||||
/*
|
||||
<function call> ::= identifier ( <expression list> )
|
||||
*/
|
||||
void Parser::functionCall()
|
||||
{
|
||||
std::string functionName;
|
||||
|
||||
functionName = token.Source;
|
||||
expect(Token::Identifier);
|
||||
expect(Token::LeftBracket);
|
||||
expressionList();
|
||||
expect(Token::RightBracket);
|
||||
//generator->Call(functionName);
|
||||
}
|
||||
|
||||
/*
|
||||
<block> ::= <declaration list> <statement list>
|
||||
*/
|
||||
void Parser::block()
|
||||
{
|
||||
if(look(Token::IntegerSymbol) || look(Token::DecimalSymbol) || look(Token::ParticleSymbol))
|
||||
declarationList();
|
||||
statementList();
|
||||
}
|
||||
|
||||
/*
|
||||
<argument list> ::= <argument> | <argument> , <argument list>
|
||||
*/
|
||||
void Parser::argumentList()
|
||||
{
|
||||
argument();
|
||||
while(accept(Token::CommaSymbol))
|
||||
argument();
|
||||
}
|
||||
|
||||
/*
|
||||
<argument> ::= integer identifier | decimal identifier | particle identifier
|
||||
*/
|
||||
void Parser::argument()
|
||||
{
|
||||
generator->ScopeVariableType(token.Symbol);
|
||||
if(!accept(Token::IntegerSymbol))
|
||||
if(!accept(Token::DecimalSymbol))
|
||||
if(!accept(Token::ParticleSymbol))
|
||||
throw ParserExpectException(token, "type name");
|
||||
generator->ScopeVariable(token.Source);
|
||||
expect(Token::Identifier);
|
||||
}
|
||||
|
||||
/*
|
||||
<declaration list> ::= <declaration> | <declaration> , <declaration list>
|
||||
*/
|
||||
void Parser::declarationList()
|
||||
{
|
||||
declaration();
|
||||
while(accept(Token::CommaSymbol))
|
||||
declaration();
|
||||
}
|
||||
|
||||
/*
|
||||
<declaration> ::= integer <identifier list> | decimal <identifier list> | particle <identifier list>
|
||||
*/
|
||||
void Parser::declaration()
|
||||
{
|
||||
generator->ScopeVariableType(token.Symbol);
|
||||
if(!accept(Token::IntegerSymbol))
|
||||
if(!accept(Token::DecimalSymbol))
|
||||
if(!accept(Token::ParticleSymbol))
|
||||
throw ParserExpectException(token, "type name");
|
||||
identifierList();
|
||||
}
|
||||
|
||||
/*
|
||||
<identifier list> ::= identifier | identifier , <identifier list>
|
||||
*/
|
||||
void Parser::identifierList()
|
||||
{
|
||||
generator->ScopeVariable(token.Source);
|
||||
expect(Token::Identifier);
|
||||
while(accept(Token::CommaSymbol))
|
||||
{
|
||||
generator->ScopeVariable(token.Source);
|
||||
expect(Token::Identifier);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
<statement list> ::= <statement> | <statement> <statement list>
|
||||
*/
|
||||
void Parser::statementList()
|
||||
{
|
||||
statement();
|
||||
while(!look(Token::EndSymbol))
|
||||
statement();
|
||||
}
|
||||
|
||||
/*
|
||||
<statement> ::= <neighbour statement> | <if statement> | <assignment statement> | <function call> | <particle action> | break | continue
|
||||
*/
|
||||
void Parser::statement()
|
||||
{
|
||||
//generator->Begin(NonTerminal::Statement);
|
||||
if(look(Token::NeighbourSymbol))
|
||||
{
|
||||
neighbourStatement();
|
||||
}
|
||||
else if(look(Token::IfSymbol))
|
||||
{
|
||||
ifStatement();
|
||||
}
|
||||
else if(look(Token::CreateSymbol) || look(Token::KillSymbol) || look(Token::GetSymbol) || look(Token::TransformSymbol))
|
||||
{
|
||||
particleAction();
|
||||
generator->Discard();
|
||||
}
|
||||
else if(look(Token::BreakSymbol))
|
||||
{
|
||||
expect(Token::BreakSymbol);
|
||||
generator->Jump(breakLabel);
|
||||
}
|
||||
else if(look(Token::ContinueSymbol))
|
||||
{
|
||||
expect(Token::ContinueSymbol);
|
||||
generator->Jump(continueLabel);
|
||||
}
|
||||
else if(look(Token::Identifier))
|
||||
{
|
||||
assigmentStatement();
|
||||
}
|
||||
//generator->End(NonTerminal::Statement);
|
||||
}
|
||||
|
||||
/*
|
||||
<particle action> ::= <kill statement> | <create statement> | <transform statement>
|
||||
*/
|
||||
void Parser::particleAction()
|
||||
{
|
||||
if(look(Token::KillSymbol))
|
||||
{
|
||||
killStatement();
|
||||
}
|
||||
else if(look(Token::CreateSymbol))
|
||||
{
|
||||
createStatement();
|
||||
}
|
||||
else if(look(Token::TransformSymbol))
|
||||
{
|
||||
transformStatement();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
<kill statement> ::= kill ( <expression> )
|
||||
*/
|
||||
void Parser::killStatement()
|
||||
{
|
||||
expect(Token::KillSymbol);
|
||||
expect(Token::LeftBracket);
|
||||
expression();
|
||||
expect(Token::RightBracket);
|
||||
generator->KillParticle();
|
||||
}
|
||||
|
||||
/*
|
||||
<create statement> ::= create ( <expression>, <expression>, <expression>, <expression> )
|
||||
*/
|
||||
void Parser::createStatement()
|
||||
{
|
||||
expect(Token::CreateSymbol);
|
||||
expect(Token::LeftBracket);
|
||||
expression();
|
||||
expect(Token::CommaSymbol);
|
||||
expression();
|
||||
expect(Token::CommaSymbol);
|
||||
expression();
|
||||
expect(Token::CommaSymbol);
|
||||
expression();
|
||||
expect(Token::RightBracket);
|
||||
generator->CreateParticle();
|
||||
}
|
||||
|
||||
/*
|
||||
<transform statement> ::= transform ( <expression>, <expression> )
|
||||
*/
|
||||
void Parser::transformStatement()
|
||||
{
|
||||
expect(Token::TransformSymbol);
|
||||
expect(Token::LeftBracket);
|
||||
expression();
|
||||
expect(Token::CommaSymbol);
|
||||
expression();
|
||||
expect(Token::RightBracket);
|
||||
generator->TransformParticle();
|
||||
}
|
||||
|
||||
/*
|
||||
<get statement> ::= get ( <expression>, <expression> )
|
||||
*/
|
||||
void Parser::getStatement()
|
||||
{
|
||||
expect(Token::GetSymbol);
|
||||
expect(Token::LeftBracket);
|
||||
expression();
|
||||
expect(Token::CommaSymbol);
|
||||
expression();
|
||||
expect(Token::RightBracket);
|
||||
generator->GetParticle();
|
||||
}
|
||||
|
||||
/*
|
||||
<neighbour statement> ::= neighbour identifier for <expression> do <block> end | neighbour identifier for <expression>, <expression> do <block> end
|
||||
*/
|
||||
void Parser::neighbourStatement()
|
||||
{
|
||||
std::string neighbourVariable;
|
||||
std::string loopLabel = generator->UniqueLabel("neighbour");
|
||||
std::string xVar = loopLabel+"X";
|
||||
std::string xMin = loopLabel+"minX";
|
||||
std::string xMax = loopLabel+"maxX";
|
||||
std::string yVar = loopLabel+"Y";
|
||||
std::string yMax = loopLabel+"maxY";
|
||||
breakLabel = loopLabel+"End";
|
||||
continueLabel = loopLabel+"Next";
|
||||
|
||||
expect(Token::NeighbourSymbol);
|
||||
|
||||
generator->PushLocalScope(loopLabel+"Start");
|
||||
neighbourVariable = token.Source;
|
||||
expect(Token::Identifier);
|
||||
generator->ScopeVariableType(Token::IntegerConstant);
|
||||
generator->ScopeVariable(neighbourVariable);
|
||||
generator->ScopeVariable(xVar);
|
||||
generator->ScopeVariable(yVar);
|
||||
generator->ScopeVariable(xMin);
|
||||
generator->ScopeVariable(xMax);
|
||||
generator->ScopeVariable(yMax);
|
||||
|
||||
generator->LocalEnter();
|
||||
|
||||
expect(Token::OfSymbol);
|
||||
|
||||
//Initialise position
|
||||
expression();
|
||||
generator->GetPosition();
|
||||
generator->Duplicate();
|
||||
generator->Increment("-1");
|
||||
generator->StoreVariable(yVar);
|
||||
generator->Increment("1");
|
||||
generator->StoreVariable(yMax);
|
||||
|
||||
generator->Duplicate();
|
||||
generator->Increment("-1");
|
||||
generator->Duplicate();
|
||||
generator->StoreVariable(xVar);
|
||||
generator->StoreVariable(xMin);
|
||||
generator->Increment("1");
|
||||
generator->StoreVariable(xMax);
|
||||
|
||||
//if(accept(Token::CommaSymbol))
|
||||
// expression();
|
||||
expect(Token::DoSymbol);
|
||||
|
||||
generator->ScopeLabel(loopLabel+"Next");
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//Check X
|
||||
generator->LoadVariable(xVar);
|
||||
generator->LoadVariable(xMax);
|
||||
//generator->Duplicate(); //Duplicate xvar so it can be used for incrementing
|
||||
|
||||
generator->JumpLessEqual(loopLabel+"Begin");
|
||||
//if(xVar > xMax) {
|
||||
|
||||
//Reset X, increment Y
|
||||
generator->LoadVariable(xMin);
|
||||
generator->StoreVariable(xVar);
|
||||
|
||||
generator->LoadVariable(yVar);
|
||||
generator->Increment("1");
|
||||
generator->Duplicate();
|
||||
generator->StoreVariable(yVar);
|
||||
|
||||
|
||||
//Check Y
|
||||
generator->LoadVariable(yMax);
|
||||
generator->JumpGreater(loopLabel+"End");
|
||||
|
||||
//}
|
||||
|
||||
//Start of loop
|
||||
generator->ScopeLabel(loopLabel+"Begin");
|
||||
|
||||
generator->LoadVariable(xVar);
|
||||
generator->LoadVariable(yVar);
|
||||
generator->GetParticle();
|
||||
generator->StoreVariable(neighbourVariable);
|
||||
|
||||
block();
|
||||
|
||||
//Increment X
|
||||
generator->LoadVariable(xVar);
|
||||
generator->Increment("1");
|
||||
generator->StoreVariable(xVar);
|
||||
|
||||
//Next element
|
||||
generator->Jump(loopLabel+"Next");
|
||||
|
||||
generator->ScopeLabel(loopLabel+"End");
|
||||
generator->Return();
|
||||
generator->PopScope();
|
||||
expect(Token::EndSymbol);
|
||||
}
|
||||
|
||||
/*
|
||||
<if statement> ::= if <condition> then <block> end
|
||||
*/
|
||||
void Parser::ifStatement()
|
||||
{
|
||||
//generator->Begin(NonTerminal::IfStatement);
|
||||
expect(Token::IfSymbol);
|
||||
condition();
|
||||
expect(Token::ThenSymbol);
|
||||
block();
|
||||
expect(Token::EndSymbol);
|
||||
//generator->End(NonTerminal::IfStatement);
|
||||
}
|
||||
|
||||
/*
|
||||
<condition> ::= identifier <conditional operator> identifier | identifier <conditional operator> numberConstant | numberConstant <conditional operator> identifier | numberConstant <conditional operator> numberConstant
|
||||
*/
|
||||
void Parser::condition()
|
||||
{
|
||||
//generator->Begin(NonTerminal::Condition);
|
||||
if(look(Token::Identifier))
|
||||
{
|
||||
conditionalOperator();
|
||||
if(!accept(Token::Identifier) && !accept(Token::IntegerConstant) && !accept(Token::DecimalConstant))
|
||||
throw ParserExpectException(token, "identifier or constant");
|
||||
}
|
||||
else if(look(Token::DecimalConstant) || look(Token::IntegerConstant))
|
||||
{
|
||||
conditionalOperator();
|
||||
if(!accept(Token::Identifier) && !accept(Token::IntegerConstant) && !accept(Token::DecimalConstant))
|
||||
throw ParserExpectException(token, "identifier or constant");
|
||||
}
|
||||
else
|
||||
{
|
||||
throw ParserExpectException(token, "condition");
|
||||
}
|
||||
//generator->End(NonTerminal::Condition);
|
||||
}
|
||||
|
||||
/*
|
||||
<conditional operator> ::= > | >= | == | != | < | <=
|
||||
*/
|
||||
void Parser::conditionalOperator()
|
||||
{
|
||||
//generator->Begin(NonTerminal::ConditionalOperator);
|
||||
if(!accept(Token::GreaterSymbol))
|
||||
if(!accept(Token::GreaterEqualSymbol))
|
||||
if(!accept(Token::EqualSymbol))
|
||||
if(!accept(Token::NotEqualSymbol))
|
||||
if(!accept(Token::LessSymbol))
|
||||
if(!accept(Token::LessEqualSymbol))
|
||||
throw ParserExpectException(token, "conditional operator");
|
||||
//generator->End(NonTerminal::ConditionalOperator);
|
||||
}
|
||||
|
||||
/*
|
||||
<assigment statement> ::= identifier = <expression>
|
||||
*/
|
||||
void Parser::assigmentStatement()
|
||||
{
|
||||
std::string variable = token.Source;
|
||||
//generator->PushVariableAddress(token.Source);
|
||||
expect(Token::Identifier);
|
||||
expect(Token::AssignSymbol);
|
||||
expression();
|
||||
//generator->Store();
|
||||
generator->StoreVariable(variable);
|
||||
}
|
||||
|
||||
/*
|
||||
<expression list> ::= <expression> | <expression> , <expression list>
|
||||
*/
|
||||
void Parser::expressionList()
|
||||
{
|
||||
//generator->Begin(NonTerminal::ExpressionList);
|
||||
expression();
|
||||
while(accept(Token::CommaSymbol))
|
||||
expression();
|
||||
//generator->End(NonTerminal::ExpressionList);
|
||||
}
|
||||
|
||||
/*
|
||||
<expression> ::= <term> | <expression> + <term> | <expression> - <term>
|
||||
*/
|
||||
void Parser::expression()
|
||||
{
|
||||
term();
|
||||
int as = token.Symbol;
|
||||
while(accept(Token::PlusSymbol) || accept(Token::MinusSymbol))
|
||||
{
|
||||
term();
|
||||
if(as == Token::PlusSymbol)
|
||||
generator->Add();
|
||||
else if(as == Token::MinusSymbol)
|
||||
generator->Subtract();
|
||||
}
|
||||
//generator->End(NonTerminal::Expression);
|
||||
}
|
||||
|
||||
/*
|
||||
<term> ::= <factor> | <term> * <factor> | <term> / <factor>
|
||||
*/
|
||||
void Parser::term()
|
||||
{
|
||||
//generator->Begin(NonTerminal::Term);
|
||||
factor();
|
||||
int md = token.Symbol;
|
||||
while(accept(Token::MultiplySymbol) || accept(Token::DivideSymbol))
|
||||
{
|
||||
factor();
|
||||
if(md == Token::MultiplySymbol)
|
||||
generator->Multiply();
|
||||
else if(md == Token::DivideSymbol)
|
||||
generator->Divide();
|
||||
}
|
||||
//generator->End(NonTerminal::Term);
|
||||
}
|
||||
|
||||
/*
|
||||
<factor> ::= <variable value> | - <variable value> | numberConstant | - numberConstant | ( <expression> ) | - ( <expression> )
|
||||
*/
|
||||
void Parser::factor()
|
||||
{
|
||||
bool doNegate = false;
|
||||
std::string factor = token.Source;
|
||||
if(accept(Token::MinusSymbol))
|
||||
{
|
||||
factor = token.Source;
|
||||
doNegate = true;
|
||||
}
|
||||
if(accept(Token::IntegerConstant) || accept(Token::DecimalConstant))
|
||||
{
|
||||
if(doNegate)
|
||||
{
|
||||
doNegate = false;
|
||||
generator->Constant("-" + factor);
|
||||
}
|
||||
else
|
||||
generator->Constant(factor);
|
||||
}
|
||||
else if(accept(Token::LeftBracket))
|
||||
{
|
||||
expression();
|
||||
expect(Token::RightBracket);
|
||||
}
|
||||
else
|
||||
{
|
||||
variableValue();
|
||||
}
|
||||
if(doNegate)
|
||||
generator->Negate();
|
||||
/*if(!accept(Token::Identifier))
|
||||
{
|
||||
if(!accept(Token::IntegerConstant))
|
||||
{
|
||||
if(!accept(Token::DecimalConstant))
|
||||
{
|
||||
if(!accept(Token::LeftBracket))
|
||||
{
|
||||
throw ParserExpectException(token, "identifier or constant");
|
||||
}
|
||||
else
|
||||
{
|
||||
expression();
|
||||
expect(Token::RightBracket);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(doNegate)
|
||||
{
|
||||
doNegate = false;
|
||||
generator->Constant("-" + factor);
|
||||
}
|
||||
else
|
||||
generator->Constant(factor);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(doNegate)
|
||||
{
|
||||
doNegate = false;
|
||||
generator->Constant("-" + factor);
|
||||
}
|
||||
else
|
||||
generator->Constant(factor);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
generator->LoadVariable(factor);
|
||||
}
|
||||
if(doNegate)
|
||||
{
|
||||
generator->Negate();
|
||||
}*/
|
||||
}
|
||||
|
||||
/*
|
||||
<variable value> ::= <function call> | identifier | <particle action>
|
||||
*/
|
||||
void Parser::variableValue()
|
||||
{
|
||||
std::string variable = token.Source;
|
||||
if(accept(Token::Identifier))
|
||||
{
|
||||
if(look(Token::LeftBracket))
|
||||
{
|
||||
back();
|
||||
functionCall();
|
||||
}
|
||||
else
|
||||
{
|
||||
generator->LoadVariable(variable);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
particleAction();
|
||||
}
|
||||
}
|
||||
|
||||
bool Parser::accept(int symbol)
|
||||
{
|
||||
if(symbol == token.Symbol)
|
||||
{
|
||||
//generator->Insert(token);
|
||||
lastToken = token;
|
||||
if(previousTokens.size())
|
||||
{
|
||||
token = previousTokens.top();
|
||||
previousTokens.pop();
|
||||
}
|
||||
else
|
||||
token = scanner->NextToken();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool Parser::look(int symbol)
|
||||
{
|
||||
if(symbol == token.Symbol)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void Parser::back()
|
||||
{
|
||||
previousTokens.push(token);
|
||||
token = lastToken;
|
||||
}
|
||||
|
||||
void Parser::expect(int symbol)
|
||||
{
|
||||
if(!accept(symbol))
|
||||
throw ParserExpectException(token, symbol);
|
||||
}
|
||||
}
|
||||
}
|
78
src/pim/Parser.h
Normal file
78
src/pim/Parser.h
Normal file
@ -0,0 +1,78 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
#include "Scanner.h"
|
||||
#include "Generator.h"
|
||||
#include "Token.h"
|
||||
namespace pim
|
||||
{
|
||||
namespace compiler
|
||||
{
|
||||
class ParserExpectException: public std::exception
|
||||
{
|
||||
char * error;
|
||||
public:
|
||||
ParserExpectException(Token token, int expectingSymbol) {
|
||||
error = strdup(std::string("Expecting " + Token::SymbolNames[expectingSymbol] + " got " + token.Source).c_str());
|
||||
}
|
||||
ParserExpectException(Token token, std::string expectingString) {
|
||||
error = strdup(std::string("Expecting " + expectingString + " got " + token.Source).c_str());
|
||||
}
|
||||
const char * what() const throw()
|
||||
{
|
||||
return error;
|
||||
}
|
||||
~ParserExpectException() throw() {};
|
||||
};
|
||||
class Parser
|
||||
{
|
||||
std::stringstream & source;
|
||||
Generator * generator;
|
||||
Scanner * scanner;
|
||||
Token token;
|
||||
Token lastToken;
|
||||
std::string breakLabel;
|
||||
std::string continueLabel;
|
||||
std::stack<Token> previousTokens;
|
||||
|
||||
void program();
|
||||
void functionList();
|
||||
void function();
|
||||
void functionCall();
|
||||
void block();
|
||||
void argumentList();
|
||||
void argument();
|
||||
void declarationList();
|
||||
void declaration();
|
||||
void identifierList();
|
||||
void statementList();
|
||||
void statement();
|
||||
void neighbourStatement();
|
||||
void ifStatement();
|
||||
void condition();
|
||||
void conditionalOperator();
|
||||
void assigmentStatement();
|
||||
void particleAction();
|
||||
void killStatement();
|
||||
void getStatement();
|
||||
void createStatement();
|
||||
void transformStatement();
|
||||
void expressionList();
|
||||
void expression();
|
||||
void term();
|
||||
void factor();
|
||||
void variableValue();
|
||||
|
||||
bool accept(int symbol);
|
||||
bool look(int symbol);
|
||||
void back();
|
||||
void expect(int symbol);
|
||||
public:
|
||||
Parser(std::stringstream & source_);
|
||||
|
||||
std::vector<unsigned char> Compile();
|
||||
};
|
||||
}
|
||||
}
|
178
src/pim/Scanner.cpp
Normal file
178
src/pim/Scanner.cpp
Normal file
@ -0,0 +1,178 @@
|
||||
//Lexical analyser
|
||||
#include <algorithm>
|
||||
#include "Scanner.h"
|
||||
|
||||
namespace pim
|
||||
{
|
||||
namespace compiler
|
||||
{
|
||||
Scanner::Scanner(std::stringstream & source_) :
|
||||
source(source_)
|
||||
{
|
||||
nextCharacter();
|
||||
}
|
||||
|
||||
Token Scanner::NextToken()
|
||||
{
|
||||
//Read whitespace, newlines and comments
|
||||
while(
|
||||
cChar == ' ' || cChar == '\t' ||
|
||||
cChar == '\r' || cChar == '\n' ||
|
||||
cChar == '/')
|
||||
{
|
||||
if(cChar == '/')
|
||||
{
|
||||
nextCharacter();
|
||||
if(cChar == '/')
|
||||
{
|
||||
while(cChar != '\n' && cChar != '\r')
|
||||
nextCharacter();
|
||||
}
|
||||
else
|
||||
return Token(Token::DivideSymbol, "/", cLine);
|
||||
}
|
||||
|
||||
if(cChar == '\r')
|
||||
{
|
||||
nextCharacter();
|
||||
if(cChar == '\n')
|
||||
cLine++;
|
||||
else
|
||||
continue;
|
||||
}
|
||||
else if(cChar == '\n')
|
||||
cLine++;
|
||||
|
||||
nextCharacter();
|
||||
}
|
||||
|
||||
if(std::isalpha(cChar)) //Read alphanumeric symbols
|
||||
{
|
||||
cToken.clear();
|
||||
while(std::isalpha(cChar) || std::isdigit(cChar))
|
||||
{
|
||||
cToken.push_back(cChar);
|
||||
nextCharacter();
|
||||
}
|
||||
|
||||
std::transform(cToken.begin(), cToken.end(), cToken.begin(), ::tolower);
|
||||
|
||||
for(int i = 0; i < Token::SymbolNumber; i++)
|
||||
if(Token::SymbolNames[i] == cToken)
|
||||
return Token(i, cToken, cLine);
|
||||
return Token(Token::Identifier, cToken, cLine);
|
||||
}
|
||||
else if(std::isdigit(cChar)) //Read numeric constants
|
||||
{
|
||||
bool decimal = false;
|
||||
cToken.clear();
|
||||
while(std::isdigit(cChar))
|
||||
{
|
||||
cToken.push_back(cChar);
|
||||
nextCharacter();
|
||||
}
|
||||
if(cChar == '.')
|
||||
{
|
||||
decimal = true;
|
||||
cToken.push_back(cChar);
|
||||
nextCharacter();
|
||||
while(std::isdigit(cChar))
|
||||
{
|
||||
cToken.push_back(cChar);
|
||||
nextCharacter();
|
||||
}
|
||||
}
|
||||
if(decimal)
|
||||
return Token(Token::DecimalConstant, cToken, cLine);
|
||||
return Token(Token::IntegerConstant, cToken, cLine);
|
||||
}
|
||||
else if(cChar == '=')
|
||||
{
|
||||
nextCharacter();
|
||||
if(cChar == '=')
|
||||
{
|
||||
nextCharacter();
|
||||
return Token(Token::EqualSymbol, "==", cLine);
|
||||
}
|
||||
return Token(Token::AssignSymbol, "=", cLine);
|
||||
}
|
||||
else if(cChar == '!')
|
||||
{
|
||||
nextCharacter();
|
||||
if(cChar == '=')
|
||||
return Token(Token::NotEqualSymbol, "==", cLine);
|
||||
}
|
||||
else if(cChar == '(')
|
||||
{
|
||||
nextCharacter();
|
||||
return Token(Token::LeftBracket, "(", cLine);
|
||||
}
|
||||
else if(cChar == ')')
|
||||
{
|
||||
nextCharacter();
|
||||
return Token(Token::RightBracket, ")", cLine);
|
||||
}
|
||||
else if(cChar == '/')
|
||||
{
|
||||
nextCharacter();
|
||||
return Token(Token::DivideSymbol, "/", cLine);
|
||||
}
|
||||
else if(cChar == '*')
|
||||
{
|
||||
nextCharacter();
|
||||
return Token(Token::MultiplySymbol, "*", cLine);
|
||||
}
|
||||
else if(cChar == '+')
|
||||
{
|
||||
nextCharacter();
|
||||
return Token(Token::PlusSymbol, "+", cLine);
|
||||
}
|
||||
else if(cChar == '-')
|
||||
{
|
||||
nextCharacter();
|
||||
return Token(Token::MinusSymbol, "-", cLine);
|
||||
}
|
||||
else if(cChar == '%')
|
||||
{
|
||||
nextCharacter();
|
||||
return Token(Token::ModuloSymbol, "%", cLine);
|
||||
}
|
||||
else if(cChar == '<')
|
||||
{
|
||||
nextCharacter();
|
||||
if(cChar == '=')
|
||||
{
|
||||
return Token(Token::LessEqualSymbol, "<=", cLine);
|
||||
}
|
||||
return Token(Token::LessSymbol, "<", cLine);
|
||||
}
|
||||
else if(cChar == '>')
|
||||
{
|
||||
nextCharacter();
|
||||
if(cChar == '=')
|
||||
{
|
||||
return Token(Token::GreaterEqualSymbol, ">=", cLine);
|
||||
}
|
||||
return Token(Token::GreaterSymbol, ">", cLine);
|
||||
}
|
||||
else if(cChar == ',')
|
||||
{
|
||||
nextCharacter();
|
||||
return Token(Token::CommaSymbol, ",", cLine);
|
||||
}
|
||||
else
|
||||
{
|
||||
nextCharacter();
|
||||
return Token(Token::InvalidSymbol, std::string(1, cChar), cLine);
|
||||
}
|
||||
}
|
||||
|
||||
void Scanner::nextCharacter()
|
||||
{
|
||||
if(source.good())
|
||||
cChar = source.get();
|
||||
else
|
||||
cChar = 0;
|
||||
}
|
||||
}
|
||||
}
|
22
src/pim/Scanner.h
Normal file
22
src/pim/Scanner.h
Normal file
@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include "Token.h"
|
||||
namespace pim
|
||||
{
|
||||
namespace compiler
|
||||
{
|
||||
class Scanner
|
||||
{
|
||||
char cChar;
|
||||
int cLine;
|
||||
std::string cToken;
|
||||
std::stringstream & source;
|
||||
void nextCharacter();
|
||||
public:
|
||||
Scanner(std::stringstream & source_);
|
||||
Token NextToken();
|
||||
};
|
||||
}
|
||||
}
|
47
src/pim/Token.cpp
Normal file
47
src/pim/Token.cpp
Normal file
@ -0,0 +1,47 @@
|
||||
#include "Token.h"
|
||||
|
||||
namespace pim
|
||||
{
|
||||
namespace compiler
|
||||
{
|
||||
std::string Token::SymbolNames[] = {
|
||||
"=",
|
||||
"function",
|
||||
"(",
|
||||
")",
|
||||
"/",
|
||||
"*",
|
||||
"+",
|
||||
"-",
|
||||
"%",
|
||||
"INTEGER",
|
||||
"DECIMAL",
|
||||
"PARTICLE",
|
||||
"integer",
|
||||
"decimal",
|
||||
"particle",
|
||||
"is",
|
||||
"<",
|
||||
"<=",
|
||||
">",
|
||||
">=",
|
||||
"==",
|
||||
"!=",
|
||||
"neighbour",
|
||||
"do",
|
||||
"of",
|
||||
"break",
|
||||
"continue",
|
||||
"if",
|
||||
"then",
|
||||
"end",
|
||||
"kill",
|
||||
"create",
|
||||
"transform",
|
||||
"get",
|
||||
"IDENTIFIER",
|
||||
",",
|
||||
"INVALID SYMBOL"
|
||||
};
|
||||
}
|
||||
}
|
79
src/pim/Token.h
Normal file
79
src/pim/Token.h
Normal file
@ -0,0 +1,79 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
namespace pim
|
||||
{
|
||||
namespace compiler
|
||||
{
|
||||
class Token
|
||||
{
|
||||
public:
|
||||
static std::string SymbolNames[];
|
||||
|
||||
enum
|
||||
{
|
||||
AssignSymbol = 0,
|
||||
FunctionSymbol,
|
||||
|
||||
LeftBracket,
|
||||
RightBracket,
|
||||
DivideSymbol,
|
||||
MultiplySymbol,
|
||||
PlusSymbol,
|
||||
MinusSymbol,
|
||||
ModuloSymbol,
|
||||
|
||||
IntegerConstant,
|
||||
DecimalConstant,
|
||||
ParticleConstant,
|
||||
|
||||
IntegerSymbol,
|
||||
DecimalSymbol,
|
||||
ParticleSymbol,
|
||||
|
||||
IsSymbol,
|
||||
LessSymbol,
|
||||
LessEqualSymbol,
|
||||
GreaterSymbol,
|
||||
GreaterEqualSymbol,
|
||||
NotEqualSymbol,
|
||||
EqualSymbol,
|
||||
|
||||
NeighbourSymbol,
|
||||
DoSymbol,
|
||||
OfSymbol,
|
||||
BreakSymbol,
|
||||
ContinueSymbol,
|
||||
IfSymbol,
|
||||
ThenSymbol,
|
||||
EndSymbol,
|
||||
|
||||
KillSymbol,
|
||||
CreateSymbol,
|
||||
TransformSymbol,
|
||||
GetSymbol,
|
||||
|
||||
Identifier,
|
||||
|
||||
CommaSymbol,
|
||||
|
||||
InvalidSymbol,
|
||||
|
||||
SymbolNumber
|
||||
};
|
||||
int Symbol;
|
||||
int LineNumber;
|
||||
std::string Source;
|
||||
|
||||
Token(int symbol = InvalidSymbol, std::string source = "HERP DERP", int lineNumber = 0) :
|
||||
Symbol(symbol),
|
||||
Source(source),
|
||||
LineNumber(lineNumber) {}
|
||||
|
||||
std::string GetName()
|
||||
{
|
||||
return SymbolNames[Symbol];
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
21
src/tests/PowderInteractionMachine.cpp
Normal file
21
src/tests/PowderInteractionMachine.cpp
Normal file
@ -0,0 +1,21 @@
|
||||
#ifdef TEST
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include "pim/Parser.h"
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
std::ifstream file("test.p");
|
||||
|
||||
std::stringstream buffer;
|
||||
|
||||
buffer << file.rdbuf();
|
||||
file.close();
|
||||
|
||||
pim::compiler::Parser * parser = new pim::compiler::Parser(buffer);
|
||||
|
||||
parser->Compile();
|
||||
}
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user