Merge branch 'master' of git://github.com/korslund/openmw
Conflicts: CMakeLists.txt apps/openmw/main.cpp game/engine.cppactorid
commit
ee6c933156
@ -1,3 +1,3 @@
|
||||
[submodule "mangle"]
|
||||
path = mangle
|
||||
[submodule "libs/mangle"]
|
||||
path = libs/mangle
|
||||
url = git://github.com/korslund/mangle.git
|
||||
|
@ -0,0 +1,5 @@
|
||||
project(clientconsole)
|
||||
find_package(Boost REQUIRED COMPONENTS system)
|
||||
link_directories(${Boost_LIBRARY_DIRS})
|
||||
add_executable(clientconsole client.cpp)
|
||||
target_link_libraries(clientconsole ${Boost_LIBRARIES})
|
@ -0,0 +1,142 @@
|
||||
#include <iostream>
|
||||
#include <boost/array.hpp>
|
||||
#include <boost/asio.hpp>
|
||||
|
||||
#include <libs/mangle/tools/str_exception.hpp>
|
||||
|
||||
using boost::asio::ip::tcp;
|
||||
|
||||
#ifdef WIN32
|
||||
#pragma warning( disable : 4966 )
|
||||
#endif
|
||||
|
||||
class Client
|
||||
{
|
||||
protected:
|
||||
struct Header
|
||||
{
|
||||
char magic[4];
|
||||
boost::uint32_t dataLength;
|
||||
};
|
||||
|
||||
boost::asio::io_service mIOService;
|
||||
tcp::socket* mpSocket;
|
||||
|
||||
public:
|
||||
|
||||
bool connect(const char* port)
|
||||
{
|
||||
tcp::resolver resolver(mIOService);
|
||||
tcp::resolver::query query("localhost", port);
|
||||
tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
|
||||
tcp::resolver::iterator end;
|
||||
|
||||
mpSocket = new tcp::socket(mIOService);
|
||||
boost::system::error_code error = boost::asio::error::host_not_found;
|
||||
while (error && endpoint_iterator != end)
|
||||
{
|
||||
mpSocket->close();
|
||||
mpSocket->connect(*endpoint_iterator++, error);
|
||||
}
|
||||
|
||||
return (error) ? false : true;
|
||||
}
|
||||
void disconnect()
|
||||
{
|
||||
mpSocket->close();
|
||||
mIOService.stop();
|
||||
}
|
||||
|
||||
bool send (const char* msg)
|
||||
{
|
||||
const size_t slen = strlen(msg);
|
||||
const size_t plen = sizeof(Header) + slen + 1;
|
||||
|
||||
std::vector<char> packet(plen);
|
||||
Header* pHeader = reinterpret_cast<Header*>(&packet[0]);
|
||||
strncpy(pHeader->magic, "OMW0", 4);
|
||||
pHeader->dataLength = slen + 1; // Include the null terminator
|
||||
strncpy(&packet[8], msg, pHeader->dataLength);
|
||||
|
||||
boost::system::error_code ec;
|
||||
boost::asio::write(*mpSocket, boost::asio::buffer(packet),
|
||||
boost::asio::transfer_all(), ec);
|
||||
if (ec)
|
||||
std::cout << "Error: " << ec.message() << std::endl;
|
||||
|
||||
return !ec;
|
||||
}
|
||||
|
||||
bool receive (std::string& reply)
|
||||
{
|
||||
Header header;
|
||||
boost::system::error_code error;
|
||||
mpSocket->read_some(boost::asio::buffer(&header, sizeof(Header)), error);
|
||||
|
||||
if (error != boost::asio::error::eof)
|
||||
{
|
||||
if (strncmp(header.magic, "OMW0", 4) == 0)
|
||||
{
|
||||
std::vector<char> msg;
|
||||
msg.resize(header.dataLength);
|
||||
|
||||
boost::system::error_code error;
|
||||
mpSocket->read_some(boost::asio::buffer(&msg[0], header.dataLength), error);
|
||||
if (!error)
|
||||
{
|
||||
reply = &msg[0];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
throw str_exception("Unexpected header!");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
std::cout << "OpenMW client console" << std::endl;
|
||||
std::cout << "=====================" << std::endl;
|
||||
std::cout << "Type 'quit' to exit." << std::endl;
|
||||
std::cout << "Connecting...";
|
||||
|
||||
Client client;
|
||||
if (client.connect("27917"))
|
||||
{
|
||||
std::cout << "success." << std::endl;
|
||||
|
||||
bool bDone = false;
|
||||
do
|
||||
{
|
||||
std::cout << "Client> ";
|
||||
std::string buffer;
|
||||
std::getline(std::cin, buffer);
|
||||
|
||||
if (buffer == "quit")
|
||||
bDone = true;
|
||||
else
|
||||
{
|
||||
if (client.send(buffer.c_str()))
|
||||
{
|
||||
std::string reply;
|
||||
if (client.receive(reply))
|
||||
std::cout << "Server: " << reply << std::endl;
|
||||
else
|
||||
bDone = true;
|
||||
}
|
||||
else
|
||||
bDone = true;
|
||||
}
|
||||
|
||||
} while (!bDone);
|
||||
|
||||
client.disconnect();
|
||||
}
|
||||
else
|
||||
std::cout << "failed." << std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
project(MWCompiler)
|
||||
|
||||
set(TOOLS_MWCOMPILER ${COMPILER}
|
||||
main.cpp
|
||||
context.cpp
|
||||
context.hpp)
|
||||
|
||||
add_executable(mwcompiler ${TOOLS_MWCOMPILER})
|
||||
|
@ -0,0 +1,16 @@
|
||||
|
||||
#include "context.hpp"
|
||||
|
||||
namespace SACompiler
|
||||
{
|
||||
bool Context::canDeclareLocals() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
char Context::getGlobalType (const std::string& name) const
|
||||
{
|
||||
return ' ';
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,21 @@
|
||||
#ifndef MWCOMPILER_CONTEXT_H_INCLUDED
|
||||
#define MWCOMPILER_CONTEXT_H_INCLUDED
|
||||
|
||||
#include <components/compiler/context.hpp>
|
||||
|
||||
namespace SACompiler
|
||||
{
|
||||
class Context : public Compiler::Context
|
||||
{
|
||||
public:
|
||||
|
||||
virtual bool canDeclareLocals() const;
|
||||
///< Is the compiler allowed to declare local variables?
|
||||
|
||||
virtual char getGlobalType (const std::string& name) const;
|
||||
///< 'l: long, 's': short, 'f': float, ' ': does not exist.
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -0,0 +1,78 @@
|
||||
// Stand-alone MW-script compiler
|
||||
|
||||
#include <exception>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
|
||||
#include <components/interpreter/types.hpp>
|
||||
#include <components/compiler/streamerrorhandler.hpp>
|
||||
#include <components/compiler/scanner.hpp>
|
||||
#include <components/compiler/fileparser.hpp>
|
||||
#include <components/compiler/exception.hpp>
|
||||
|
||||
#include "context.hpp"
|
||||
|
||||
int main (int argc, char **argv)
|
||||
{
|
||||
try
|
||||
{
|
||||
SACompiler::Context context;
|
||||
Compiler::StreamErrorHandler errorHandler (std::cout);
|
||||
Compiler::FileParser parser (errorHandler, context);
|
||||
|
||||
std::string filename = argc>1 ? argv[1] : "test.mwscript";
|
||||
|
||||
try
|
||||
{
|
||||
std::ifstream file (filename.c_str());
|
||||
|
||||
if (!file.is_open())
|
||||
{
|
||||
std::cout << "can't open script file: " << filename << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
Compiler::Scanner scanner (errorHandler, file);
|
||||
|
||||
scanner.scan (parser);
|
||||
}
|
||||
catch (const Compiler::SourceException&)
|
||||
{
|
||||
// ignore exception (problem has already been reported to the user)
|
||||
}
|
||||
|
||||
if (errorHandler.countErrors() || errorHandler.countWarnings())
|
||||
{
|
||||
std::cout
|
||||
<< errorHandler.countErrors() << " error(s), "
|
||||
<< errorHandler.countWarnings() << " warning(s)" << std::endl
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
if (errorHandler.isGood())
|
||||
{
|
||||
std::vector<Interpreter::Type_Code> code;
|
||||
parser.getCode (code);
|
||||
|
||||
std::ofstream codeFile ((filename + ".code").c_str());
|
||||
|
||||
codeFile.write (reinterpret_cast<const char *> (&code[0]),
|
||||
code.size()*sizeof (Interpreter::Type_Code));
|
||||
|
||||
std::ofstream localFile ((filename + ".locals").c_str());
|
||||
|
||||
parser.getLocals().write (localFile);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
std::cout << "\nERROR: " << e.what() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,9 @@
|
||||
project(MWInterpreter)
|
||||
|
||||
set(TOOLS_MWINTERPRETER
|
||||
${INTERPRETER}
|
||||
main.cpp
|
||||
context.cpp
|
||||
context.hpp)
|
||||
|
||||
add_executable(mwinterpreter ${TOOLS_MWINTERPRETER})
|
@ -0,0 +1,137 @@
|
||||
|
||||
#include "context.hpp"
|
||||
|
||||
#include <cassert>
|
||||
#include <fstream>
|
||||
#include <stdexcept>
|
||||
#include <iostream>
|
||||
|
||||
namespace SAInterpreter
|
||||
{
|
||||
Context::Context (const std::string& filename)
|
||||
{
|
||||
std::ifstream file (filename.c_str());
|
||||
|
||||
if (!file.is_open())
|
||||
throw std::runtime_error ("can't open locals file: " + filename);
|
||||
|
||||
std::size_t shortSize, longSize, floatSize;
|
||||
|
||||
file >> shortSize >> longSize >> floatSize;
|
||||
|
||||
mShorts.resize (shortSize, 0);
|
||||
mLongs.resize (longSize, 0);
|
||||
mFloats.resize (floatSize, 0.0);
|
||||
|
||||
std::size_t size = shortSize + longSize + floatSize;
|
||||
|
||||
mNames.resize (size);
|
||||
|
||||
for (std::size_t i=0; i<size; ++i)
|
||||
file >> mNames[i];
|
||||
}
|
||||
|
||||
int Context::getLocalShort (int index) const
|
||||
{
|
||||
assert (index>=0);
|
||||
return mShorts.at (index);
|
||||
}
|
||||
|
||||
int Context::getLocalLong (int index) const
|
||||
{
|
||||
assert (index>=0);
|
||||
return mLongs.at (index);
|
||||
}
|
||||
|
||||
float Context::getLocalFloat (int index) const
|
||||
{
|
||||
assert (index>=0);
|
||||
return mFloats.at (index);
|
||||
}
|
||||
|
||||
void Context::setLocalShort (int index, int value)
|
||||
{
|
||||
assert (index>=0);
|
||||
mShorts.at (index) = value;
|
||||
}
|
||||
|
||||
void Context::setLocalLong (int index, int value)
|
||||
{
|
||||
assert (index>=0);
|
||||
mLongs.at (index) = value;
|
||||
}
|
||||
|
||||
void Context::setLocalFloat (int index, float value)
|
||||
{
|
||||
assert (index>=0);
|
||||
mFloats.at (index) = value;
|
||||
}
|
||||
|
||||
void Context::messageBox (const std::string& message,
|
||||
const std::vector<std::string>& buttons)
|
||||
{
|
||||
std::cout << "message box: " << message << std::endl;
|
||||
for (std::size_t i=0; i<buttons.size(); ++i)
|
||||
std::cout << " button " << i << ": " << buttons[i] << std::endl;
|
||||
}
|
||||
|
||||
bool Context::menuMode()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int Context::getGlobalShort (const std::string& name) const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Context::getGlobalLong (const std::string& name) const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
float Context::getGlobalFloat (const std::string& name) const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Context::setGlobalShort (const std::string& name, int value) {}
|
||||
|
||||
void Context::setGlobalLong (const std::string& name, int value) {}
|
||||
|
||||
void Context::setGlobalFloat (const std::string& name, float value) {}
|
||||
|
||||
bool Context::isScriptRunning (const std::string& name)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void Context::startScript (const std::string& name) {}
|
||||
|
||||
void Context::stopScript (const std::string& name) {}
|
||||
|
||||
void Context::report()
|
||||
{
|
||||
std::size_t i = 0;
|
||||
|
||||
std::cout << "local shorts:" << std::endl;
|
||||
|
||||
for (std::vector<Interpreter::Type_Short>::const_iterator iter (mShorts.begin());
|
||||
iter!=mShorts.end(); ++iter)
|
||||
std::cout << mNames[i++] << ": " << *iter << std::endl;
|
||||
|
||||
std::cout << "local longs:" << std::endl;
|
||||
|
||||
for (std::vector<Interpreter::Type_Integer>::const_iterator iter (mLongs.begin());
|
||||
iter!=mLongs.end(); ++iter)
|
||||
std::cout << mNames[i++] << ": " << *iter << std::endl;
|
||||
|
||||
std::cout << "local floats:" << std::endl;
|
||||
|
||||
for (std::vector<Interpreter::Type_Float>::const_iterator iter (mFloats.begin());
|
||||
iter!=mFloats.end(); ++iter)
|
||||
std::cout << mNames[i++] << ": " << *iter << std::endl;
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,66 @@
|
||||
#ifndef SAINTERPRETER_CONTEXT_H_INCLUDED
|
||||
#define SAINTERPRETER_CONTEXT_H_INCLUDED
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <components/interpreter/context.hpp>
|
||||
#include <components/interpreter/types.hpp>
|
||||
|
||||
namespace SAInterpreter
|
||||
{
|
||||
class Context : public Interpreter::Context
|
||||
{
|
||||
std::vector<Interpreter::Type_Short> mShorts;
|
||||
std::vector<Interpreter::Type_Integer> mLongs;
|
||||
std::vector<Interpreter::Type_Float> mFloats;
|
||||
std::vector<std::string> mNames;
|
||||
|
||||
public:
|
||||
|
||||
Context (const std::string& filename);
|
||||
///< Create context from file
|
||||
/// \note A context for an integreted interpreter will typically not
|
||||
/// configure at construction, but will offer a separate function.
|
||||
|
||||
virtual int getLocalShort (int index) const;
|
||||
|
||||
virtual int getLocalLong (int index) const;
|
||||
|
||||
virtual float getLocalFloat (int index) const;
|
||||
|
||||
virtual void setLocalShort (int index, int value);
|
||||
|
||||
virtual void setLocalLong (int index, int value);
|
||||
|
||||
virtual void setLocalFloat (int index, float value);
|
||||
|
||||
virtual void messageBox (const std::string& message,
|
||||
const std::vector<std::string>& buttons);
|
||||
|
||||
virtual bool menuMode();
|
||||
|
||||
virtual int getGlobalShort (const std::string& name) const;
|
||||
|
||||
virtual int getGlobalLong (const std::string& name) const;
|
||||
|
||||
virtual float getGlobalFloat (const std::string& name) const;
|
||||
|
||||
virtual void setGlobalShort (const std::string& name, int value);
|
||||
|
||||
virtual void setGlobalLong (const std::string& name, int value);
|
||||
|
||||
virtual void setGlobalFloat (const std::string& name, float value);
|
||||
|
||||
virtual bool isScriptRunning (const std::string& name);
|
||||
|
||||
virtual void startScript (const std::string& name);
|
||||
|
||||
virtual void stopScript (const std::string& name);
|
||||
|
||||
void report();
|
||||
///< Write state to std::cout
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,57 @@
|
||||
// Stand-alone MW-script code interpreter
|
||||
|
||||
#include <exception>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
#include <components/interpreter/interpreter.hpp>
|
||||
#include <components/interpreter/context.hpp>
|
||||
#include <components/interpreter/types.hpp>
|
||||
#include <components/interpreter/installopcodes.hpp>
|
||||
|
||||
#include "context.hpp"
|
||||
|
||||
int main (int argc, char **argv)
|
||||
{
|
||||
try
|
||||
{
|
||||
std::string filename = argc>1 ? argv[1] : "test.mwscript";
|
||||
|
||||
std::string localfilename = filename + ".locals";
|
||||
|
||||
SAInterpreter::Context context (localfilename);
|
||||
Interpreter::Interpreter interpreter (context);
|
||||
Interpreter::installOpcodes (interpreter);
|
||||
|
||||
std::string codefilename = filename + ".code";
|
||||
|
||||
std::ifstream codefile (codefilename.c_str());
|
||||
|
||||
if (!codefile.is_open())
|
||||
{
|
||||
std::cout << "can't open code file: " << codefilename << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::vector<Interpreter::Type_Code> code (4);
|
||||
|
||||
codefile.read (reinterpret_cast<char *> (&code[0]), 4 * sizeof (Interpreter::Type_Code));
|
||||
|
||||
unsigned int size = code[0] + code[1] + code[2] + code[3];
|
||||
|
||||
code.resize (4+size);
|
||||
|
||||
codefile.read (reinterpret_cast<char *> (&code[4]), size * sizeof (Interpreter::Type_Code));
|
||||
|
||||
interpreter.run (&code[0], size+4);
|
||||
|
||||
context.report();
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
std::cout << "\nERROR: " << e.what() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,217 @@
|
||||
#include "engine.hpp"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <components/misc/fileops.hpp>
|
||||
#include <components/bsa/bsa_archive.hpp>
|
||||
|
||||
#include "mwinput/inputmanager.hpp"
|
||||
|
||||
#include "mwscript/scriptmanager.hpp"
|
||||
#include "mwscript/compilercontext.hpp"
|
||||
#include "mwscript/interpretercontext.hpp"
|
||||
#include "mwscript/extensions.hpp"
|
||||
#include "mwscript/globalscripts.hpp"
|
||||
|
||||
#include "mwsound/soundmanager.hpp"
|
||||
|
||||
#include "mwworld/world.hpp"
|
||||
#include "mwworld/ptr.hpp"
|
||||
#include "mwworld/environment.hpp"
|
||||
|
||||
void OMW::Engine::executeLocalScripts()
|
||||
{
|
||||
for (MWWorld::World::ScriptList::const_iterator iter (
|
||||
mEnvironment.mWorld->getLocalScripts().begin());
|
||||
iter!=mEnvironment.mWorld->getLocalScripts().end(); ++iter)
|
||||
{
|
||||
MWScript::InterpreterContext interpreterContext (mEnvironment,
|
||||
&iter->second.getRefData().getLocals(), MWWorld::Ptr (iter->second));
|
||||
mScriptManager->run (iter->first, interpreterContext);
|
||||
}
|
||||
}
|
||||
|
||||
bool OMW::Engine::frameStarted(const Ogre::FrameEvent& evt)
|
||||
{
|
||||
// console
|
||||
processCommands();
|
||||
|
||||
// local scripts
|
||||
executeLocalScripts();
|
||||
|
||||
// global scripts
|
||||
mEnvironment.mGlobalScripts->run (mEnvironment);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void OMW::Engine::processCommands()
|
||||
{
|
||||
Command cmd;
|
||||
while (mCommandQueue.try_pop_front(cmd))
|
||||
{
|
||||
///\todo Add actual processing of the received command strings
|
||||
std::cout << "Command: '" << cmd.mCommand << "'" << std::endl;
|
||||
|
||||
///\todo Replace with real output. For now, echo back the string in uppercase
|
||||
std::string reply(cmd.mCommand);
|
||||
std::transform(reply.begin(), reply.end(), reply.begin(), toupper);
|
||||
cmd.mReplyFunction(reply);
|
||||
}
|
||||
}
|
||||
|
||||
OMW::Engine::Engine()
|
||||
: mDebug (false), mVerboseScripts (false), mNewGame (false), mScriptManager (0),
|
||||
mScriptContext (0)
|
||||
{
|
||||
mspCommandServer.reset(
|
||||
new OMW::CommandServer::Server(&mCommandQueue, kCommandServerPort));
|
||||
}
|
||||
|
||||
OMW::Engine::~Engine()
|
||||
{
|
||||
// mspCommandServer->stop();
|
||||
delete mEnvironment.mWorld;
|
||||
delete mEnvironment.mSoundManager;
|
||||
delete mEnvironment.mGlobalScripts;
|
||||
delete mScriptManager;
|
||||
delete mScriptContext;
|
||||
}
|
||||
|
||||
// Load all BSA files in data directory.
|
||||
|
||||
void OMW::Engine::loadBSA()
|
||||
{
|
||||
boost::filesystem::directory_iterator end;
|
||||
|
||||
for (boost::filesystem::directory_iterator iter (mDataDir); iter!=end; ++iter)
|
||||
{
|
||||
if (boost::filesystem::extension (iter->path())==".bsa")
|
||||
{
|
||||
std::cout << "Adding " << iter->path().string() << std::endl;
|
||||
addBSA(iter->path().file_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// add resources directory
|
||||
// \note This function works recursively.
|
||||
|
||||
void OMW::Engine::addResourcesDirectory (const boost::filesystem::path& path)
|
||||
{
|
||||
mOgre.getRoot()->addResourceLocation (path.file_string(), "FileSystem",
|
||||
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, true);
|
||||
}
|
||||
|
||||
// Set data dir
|
||||
|
||||
void OMW::Engine::setDataDir (const boost::filesystem::path& dataDir)
|
||||
{
|
||||
mDataDir = boost::filesystem::system_complete (dataDir);
|
||||
}
|
||||
|
||||
// Set start cell name (only interiors for now)
|
||||
|
||||
void OMW::Engine::setCell (const std::string& cellName)
|
||||
{
|
||||
mCellName = cellName;
|
||||
}
|
||||
|
||||
// Set master file (esm)
|
||||
// - If the given name does not have an extension, ".esm" is added automatically
|
||||
// - Currently OpenMW only supports one master at the same time.
|
||||
|
||||
void OMW::Engine::addMaster (const std::string& master)
|
||||
{
|
||||
assert (mMaster.empty());
|
||||
mMaster = master;
|
||||
|
||||
// Append .esm if not already there
|
||||
std::string::size_type sep = mMaster.find_last_of (".");
|
||||
if (sep == std::string::npos)
|
||||
{
|
||||
mMaster += ".esm";
|
||||
}
|
||||
}
|
||||
|
||||
void OMW::Engine::enableDebugMode()
|
||||
{
|
||||
mDebug = true;
|
||||
}
|
||||
|
||||
void OMW::Engine::enableVerboseScripts()
|
||||
{
|
||||
mVerboseScripts = true;
|
||||
}
|
||||
|
||||
void OMW::Engine::setNewGame()
|
||||
{
|
||||
mNewGame = true;
|
||||
}
|
||||
|
||||
// Initialise and enter main loop.
|
||||
|
||||
void OMW::Engine::go()
|
||||
{
|
||||
assert (!mEnvironment.mWorld);
|
||||
assert (!mDataDir.empty());
|
||||
assert (!mCellName.empty());
|
||||
assert (!mMaster.empty());
|
||||
|
||||
std::cout << "Hello, fellow traveler!\n";
|
||||
|
||||
std::cout << "Your data directory for today is: " << mDataDir << "\n";
|
||||
|
||||
std::cout << "Initializing OGRE\n";
|
||||
|
||||
const char* plugCfg = "plugins.cfg";
|
||||
|
||||
mOgre.configure(!isFile("ogre.cfg"), plugCfg, false);
|
||||
|
||||
addResourcesDirectory (mDataDir / "Meshes");
|
||||
addResourcesDirectory (mDataDir / "Textures");
|
||||
|
||||
// Create the window
|
||||
mOgre.createWindow("OpenMW");
|
||||
|
||||
loadBSA();
|
||||
|
||||
// Create the world
|
||||
mEnvironment.mWorld = new MWWorld::World (mOgre, mDataDir, mMaster, mCellName, mNewGame);
|
||||
|
||||
mEnvironment.mSoundManager = new MWSound::SoundManager;
|
||||
|
||||
MWScript::registerExtensions (mExtensions);
|
||||
|
||||
mScriptContext = new MWScript::CompilerContext (MWScript::CompilerContext::Type_Full,
|
||||
mEnvironment);
|
||||
mScriptContext->setExtensions (&mExtensions);
|
||||
|
||||
mScriptManager = new MWScript::ScriptManager (mEnvironment.mWorld->getStore(), mVerboseScripts,
|
||||
*mScriptContext);
|
||||
|
||||
mEnvironment.mGlobalScripts = new MWScript::GlobalScripts (mEnvironment.mWorld->getStore(),
|
||||
*mScriptManager);
|
||||
|
||||
std::cout << "Setting up input system\n";
|
||||
|
||||
// Sets up the input system
|
||||
MWInput::MWInputManager input(mOgre, mEnvironment.mWorld->getPlayerPos(), mDebug);
|
||||
|
||||
// Launch the console server
|
||||
std::cout << "Starting command server on port " << kCommandServerPort << std::endl;
|
||||
mspCommandServer->start();
|
||||
|
||||
std::cout << "\nStart! Press Q/ESC or close window to exit.\n";
|
||||
|
||||
mOgre.getRoot()->addFrameListener (this);
|
||||
|
||||
// Start the main rendering loop
|
||||
mOgre.start();
|
||||
|
||||
std::cout << "\nThat's all for now!\n";
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,112 @@
|
||||
#ifndef ENGINE_H
|
||||
#define ENGINE_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#include <OgreFrameListener.h>
|
||||
|
||||
#include <components/engine/ogre/renderer.hpp>
|
||||
#include <components/misc/tsdeque.hpp>
|
||||
#include <components/commandserver/server.hpp>
|
||||
#include <components/commandserver/command.hpp>
|
||||
#include <components/compiler/extensions.hpp>
|
||||
|
||||
#include "mwworld/environment.hpp"
|
||||
|
||||
namespace Compiler
|
||||
{
|
||||
class Context;
|
||||
}
|
||||
|
||||
namespace MWScript
|
||||
{
|
||||
class ScriptManager;
|
||||
}
|
||||
|
||||
namespace MWSound
|
||||
{
|
||||
class SoundManager;
|
||||
}
|
||||
|
||||
namespace MWWorld
|
||||
{
|
||||
class World;
|
||||
}
|
||||
|
||||
namespace OMW
|
||||
{
|
||||
/// \brief Main engine class, that brings together all the components of OpenMW
|
||||
|
||||
class Engine : private Ogre::FrameListener
|
||||
{
|
||||
enum { kCommandServerPort = 27917 };
|
||||
|
||||
boost::filesystem::path mDataDir;
|
||||
Render::OgreRenderer mOgre;
|
||||
std::string mCellName;
|
||||
std::string mMaster;
|
||||
bool mDebug;
|
||||
bool mVerboseScripts;
|
||||
bool mNewGame;
|
||||
|
||||
TsDeque<OMW::Command> mCommandQueue;
|
||||
std::auto_ptr<OMW::CommandServer::Server> mspCommandServer;
|
||||
|
||||
MWWorld::Environment mEnvironment;
|
||||
MWScript::ScriptManager *mScriptManager;
|
||||
Compiler::Extensions mExtensions;
|
||||
Compiler::Context *mScriptContext;
|
||||
|
||||
// not implemented
|
||||
Engine (const Engine&);
|
||||
Engine& operator= (const Engine&);
|
||||
|
||||
/// add resources directory
|
||||
/// \note This function works recursively.
|
||||
void addResourcesDirectory (const boost::filesystem::path& path);
|
||||
|
||||
/// Load all BSA files in data directory.
|
||||
void loadBSA();
|
||||
|
||||
void executeLocalScripts();
|
||||
|
||||
virtual bool frameStarted(const Ogre::FrameEvent& evt);
|
||||
|
||||
/// Process pending commands
|
||||
void processCommands();
|
||||
|
||||
public:
|
||||
|
||||
Engine();
|
||||
|
||||
~Engine();
|
||||
|
||||
/// Set data dir
|
||||
void setDataDir (const boost::filesystem::path& dataDir);
|
||||
|
||||
/// Set start cell name (only interiors for now)
|
||||
void setCell (const std::string& cellName);
|
||||
|
||||
/// Set master file (esm)
|
||||
/// - If the given name does not have an extension, ".esm" is added automatically
|
||||
/// - Currently OpenMW only supports one master at the same time.
|
||||
void addMaster (const std::string& master);
|
||||
|
||||
/// Enable debug mode:
|
||||
/// - non-exclusive input
|
||||
void enableDebugMode();
|
||||
|
||||
/// Enable verbose script output
|
||||
void enableVerboseScripts();
|
||||
|
||||
/// Start as a new game.
|
||||
void setNewGame();
|
||||
|
||||
/// Initialise and enter main loop.
|
||||
void go();
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,22 @@
|
||||
#ifndef GAME_RENDER_CELL_H
|
||||
#define GAME_RENDER_CELL_H
|
||||
|
||||
namespace MWRender
|
||||
{
|
||||
class CellRender
|
||||
{
|
||||
public:
|
||||
|
||||
virtual ~CellRender() {};
|
||||
|
||||
/// Make the cell visible. Load the cell if necessary.
|
||||
virtual void show() = 0;
|
||||
|
||||
/// Remove the cell from rendering, but don't remove it from
|
||||
/// memory.
|
||||
virtual void hide() = 0;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -1,7 +1,7 @@
|
||||
#ifndef _GAME_RENDER_MWSCENE_H
|
||||
#define _GAME_RENDER_MWSCENE_H
|
||||
|
||||
#include "ogre/renderer.hpp"
|
||||
#include "components/engine/ogre/renderer.hpp"
|
||||
|
||||
namespace Ogre
|
||||
{
|
@ -0,0 +1,93 @@
|
||||
#include "sky.hpp"
|
||||
#include "Caelum.h"
|
||||
|
||||
namespace MWRender
|
||||
{
|
||||
//
|
||||
// Implements a Caelum sky with default settings.
|
||||
//
|
||||
// Note: this is intended as a temporary solution to provide some form of
|
||||
// sky rendering. This code will obviously need significant tailoring to
|
||||
// support fidelity with Morrowind's rendering. Before doing major work
|
||||
// on this class, more research should be done to determine whether
|
||||
// Caelum or another plug-in such as SkyX would be best for the long-term.
|
||||
//
|
||||
class CaelumManager : public SkyManager
|
||||
{
|
||||
protected:
|
||||
Caelum::CaelumSystem* mpCaelumSystem;
|
||||
|
||||
public:
|
||||
CaelumManager (Ogre::RenderWindow* pRenderWindow,
|
||||
Ogre::Camera* pCamera);
|
||||
virtual ~CaelumManager ();
|
||||
};
|
||||
|
||||
CaelumManager::CaelumManager (Ogre::RenderWindow* pRenderWindow,
|
||||
Ogre::Camera* pCamera)
|
||||
: mpCaelumSystem (NULL)
|
||||
{
|
||||
using namespace Ogre;
|
||||
using namespace Caelum;
|
||||
|
||||
assert(pCamera);
|
||||
assert(pRenderWindow);
|
||||
|
||||
// Load the Caelum resources
|
||||
//
|
||||
ResourceGroupManager::getSingleton().addResourceLocation("resources/caelum", "FileSystem", "Caelum");
|
||||
ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
|
||||
|
||||
// Load the Caelum resources
|
||||
//
|
||||
Ogre::SceneManager* pScene = pCamera->getSceneManager();
|
||||
Caelum::CaelumSystem::CaelumComponent componentMask = CaelumSystem::CAELUM_COMPONENTS_DEFAULT;
|
||||
mpCaelumSystem = new Caelum::CaelumSystem (Root::getSingletonPtr(), pScene, componentMask);
|
||||
|
||||
// Set time acceleration.
|
||||
mpCaelumSystem->getUniversalClock()->setTimeScale(128);
|
||||
|
||||
// Disable fog since OpenMW is handling OGRE fog elsewhere
|
||||
mpCaelumSystem->setManageSceneFog(false);
|
||||
|
||||
// Change the camera far distance to make sure the sky is not clipped
|
||||
pCamera->setFarClipDistance(50000);
|
||||
|
||||
// Register Caelum as an OGRE listener
|
||||
pRenderWindow->addListener(mpCaelumSystem);
|
||||
Root::getSingletonPtr()->addFrameListener(mpCaelumSystem);
|
||||
}
|
||||
|
||||
CaelumManager::~CaelumManager()
|
||||
{
|
||||
if (mpCaelumSystem)
|
||||
mpCaelumSystem->shutdown (false);
|
||||
}
|
||||
|
||||
/// Creates and connects the sky rendering component to OGRE.
|
||||
///
|
||||
/// \return NULL on failure.
|
||||
///
|
||||
SkyManager* SkyManager::create (Ogre::RenderWindow* pRenderWindow,
|
||||
Ogre::Camera* pCamera)
|
||||
{
|
||||
SkyManager* pSkyManager = NULL;
|
||||
|
||||
try
|
||||
{
|
||||
pSkyManager = new CaelumManager(pRenderWindow, pCamera);
|
||||
}
|
||||
catch (Ogre::Exception& e)
|
||||
{
|
||||
std::cout << "\nOGRE Exception when attempting to add sky: "
|
||||
<< e.getFullDescription().c_str() << std::endl;
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cout << "\nException when attempting to add sky: "
|
||||
<< e.what() << std::endl;
|
||||
}
|
||||
|
||||
return pSkyManager;
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
#ifndef _GAME_RENDER_SKY_H
|
||||
#define _GAME_RENDER_SKY_H
|
||||
|
||||
namespace Ogre
|
||||
{
|
||||
class RenderWindow;
|
||||
class Camera;
|
||||
}
|
||||
|
||||
namespace MWRender
|
||||
{
|
||||
///
|
||||
/// Interface for the sky rendering system
|
||||
///
|
||||
class SkyManager
|
||||
{
|
||||
public:
|
||||
static SkyManager* create (Ogre::RenderWindow* pRenderWindow,
|
||||
Ogre::Camera* pCamera);
|
||||
virtual ~SkyManager() {}
|
||||
};
|
||||
}
|
||||
|
||||
#endif // _GAME_RENDER_SKY_H
|
@ -0,0 +1,44 @@
|
||||
|
||||
#include "cellextensions.hpp"
|
||||
|
||||
#include <components/compiler/extensions.hpp>
|
||||
|
||||
#include <components/interpreter/interpreter.hpp>
|
||||
#include <components/interpreter/runtime.hpp>
|
||||
#include <components/interpreter/opcodes.hpp>
|
||||
|
||||
#include "../mwworld/world.hpp"
|
||||
|
||||
#include "interpretercontext.hpp"
|
||||
|
||||
namespace MWScript
|
||||
{
|
||||
namespace Cell
|
||||
{
|
||||
class OpCellChanged : public Interpreter::Opcode0
|
||||
{
|
||||
public:
|
||||
|
||||
virtual void execute (Interpreter::Runtime& runtime)
|
||||
{
|
||||
InterpreterContext& context
|
||||
= static_cast<InterpreterContext&> (runtime.getContext());
|
||||
|
||||
runtime.push (context.getWorld().hasCellChanged() ? 1 : 0);
|
||||
}
|
||||
};
|
||||
|
||||
const int opcodeCellChanged = 0x2000000;
|
||||
|
||||
void registerExtensions (Compiler::Extensions& extensions)
|
||||
{
|
||||
extensions.registerFunction ("cellchanged", 'l', "", opcodeCellChanged);
|
||||
}
|
||||
|
||||
void installOpcodes (Interpreter::Interpreter& interpreter)
|
||||
{
|
||||
interpreter.installSegment5 (opcodeCellChanged, new OpCellChanged);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,27 @@
|
||||
#ifndef GAME_SCRIPT_CELLEXTENSIONS_H
|
||||
#define GAME_SCRIPT_CELLEXTENSIONS_H
|
||||
|
||||
namespace Compiler
|
||||
{
|
||||
class Extensions;
|
||||
}
|
||||
|
||||
namespace Interpreter
|
||||
{
|
||||
class Interpreter;
|
||||
}
|
||||
|
||||
namespace MWScript
|
||||
{
|
||||
/// \brief cell-related script functionality
|
||||
namespace Cell
|
||||
{
|
||||
void registerExtensions (Compiler::Extensions& extensions);
|
||||
|
||||
void installOpcodes (Interpreter::Interpreter& interpreter);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -0,0 +1,35 @@
|
||||
|
||||
#include "compilercontext.hpp"
|
||||
|
||||
#include "../mwworld/environment.hpp"
|
||||
#include "../mwworld/world.hpp"
|
||||
|
||||
namespace MWScript
|
||||
{
|
||||
CompilerContext::CompilerContext (Type type, const MWWorld::Environment& environment)
|
||||
: mType (type), mEnvironment (environment)
|
||||
{}
|
||||
|
||||
bool CompilerContext::canDeclareLocals() const
|
||||
{
|
||||
return mType==Type_Full;
|
||||
}
|
||||
|
||||
char CompilerContext::getGlobalType (const std::string& name) const
|
||||
{
|
||||
if (const ESM::Global *global = mEnvironment.mWorld->getStore().globals.find (name))
|
||||
{
|
||||
switch (global->type)
|
||||
{
|
||||
case ESM::VT_Short: return 's';
|
||||
case ESM::VT_Int: return 'l';
|
||||
case ESM::VT_Float: return 'f';
|
||||
|
||||
default: return ' ';
|
||||
}
|
||||
}
|
||||
|
||||
return ' ';
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,43 @@
|
||||
#ifndef GAME_SCRIPT_COMPILERCONTEXT_H
|
||||
#define GAME_SCRIPT_COMPILERCONTEXT_H
|
||||
|
||||
#include <components/compiler/context.hpp>
|
||||
|
||||
namespace MWWorld
|
||||
{
|
||||
class Environment;
|
||||
}
|
||||
|
||||
namespace MWScript
|
||||
{
|
||||
class CompilerContext : public Compiler::Context
|
||||
{
|
||||
public:
|
||||
|
||||
enum Type
|
||||
{
|
||||
Type_Full, // global, local, targetted
|
||||
Type_Dialgoue,
|
||||
Type_Console
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
Type mType;
|
||||
const MWWorld::Environment& mEnvironment;
|
||||
|
||||
public:
|
||||
|
||||
CompilerContext (Type type, const MWWorld::Environment& environment);
|
||||
|
||||
/// Is the compiler allowed to declare local variables?
|
||||
virtual bool canDeclareLocals() const;
|
||||
|
||||
/// 'l: long, 's': short, 'f': float, ' ': does not exist.
|
||||
virtual char getGlobalType (const std::string& name) const;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -0,0 +1,37 @@
|
||||
OpenMW Extensions:
|
||||
|
||||
Segment 0:
|
||||
(not implemented yet)
|
||||
opcodes 0x20-0x3f unused
|
||||
|
||||
Segment 1:
|
||||
(not implemented yet)
|
||||
opcodes 0x20-0x3f unused
|
||||
|
||||
Segment 2:
|
||||
(not implemented yet)
|
||||
opcodes 0x200-0x3ff unused
|
||||
|
||||
Segment 3:
|
||||
(not implemented yet)
|
||||
opcodes 0x200-0x3ff unused
|
||||
|
||||
Segment 4:
|
||||
(not implemented yet)
|
||||
opcodes 0x200-0x3ff unused
|
||||
|
||||
Segment 5:
|
||||
op 2000000: CellChanged
|
||||
op 2000001: Say
|
||||
op 2000002: SayDone
|
||||
op 2000003: StreamMusic
|
||||
op 2000004: PlaySound
|
||||
op 2000005: PlaySoundVP
|
||||
op 2000006: PlaySound3D
|
||||
op 2000007: PlaySound3DVP
|
||||
op 2000008: PlayLoopSound3D
|
||||
op 2000009: PlayLoopSound3DVP
|
||||
op 200000a: StopSound
|
||||
op 200000b: GetSoundPlaying
|
||||
opcodes 0x200000c-0x3ffffff unused
|
||||
|
@ -0,0 +1,16 @@
|
||||
|
||||
#include "extensions.hpp"
|
||||
|
||||
#include "../mwsound/extensions.hpp"
|
||||
|
||||
#include "cellextensions.hpp"
|
||||
|
||||
namespace MWScript
|
||||
{
|
||||
void registerExtensions (Compiler::Extensions& extensions)
|
||||
{
|
||||
Cell::registerExtensions (extensions);
|
||||
MWSound::registerExtensions (extensions);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,14 @@
|
||||
#ifndef GAME_SCRIPT_EXTENSIONS_H
|
||||
#define GAME_SCRIPT_EXTENSIONS_H
|
||||
|
||||
namespace Compiler
|
||||
{
|
||||
class Extensions;
|
||||
}
|
||||
|
||||
namespace MWScript
|
||||
{
|
||||
void registerExtensions (Compiler::Extensions& extensions);
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,69 @@
|
||||
|
||||
#include "globalscripts.hpp"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include "interpretercontext.hpp"
|
||||
#include "scriptmanager.hpp"
|
||||
|
||||
namespace MWScript
|
||||
{
|
||||
GlobalScripts::GlobalScripts (const ESMS::ESMStore& store, ScriptManager& scriptManager)
|
||||
: mStore (store), mScriptManager (scriptManager)
|
||||
{
|
||||
addScript ("Main");
|
||||
|
||||
for (ESMS::RecListT<ESM::StartScript>::MapType::const_iterator iter
|
||||
(store.startScripts.list.begin());
|
||||
iter != store.startScripts.list.end(); ++iter)
|
||||
addScript (iter->second.script);
|
||||
}
|
||||
|
||||
void GlobalScripts::addScript (const std::string& name)
|
||||
{
|
||||
if (mScripts.find (name)==mScripts.end())
|
||||
if (const ESM::Script *script = mStore.scripts.find (name))
|
||||
{
|
||||
Locals locals;
|
||||
|
||||
locals.configure (*script);
|
||||
|
||||
mScripts.insert (std::make_pair (name, std::make_pair (true, locals)));
|
||||
}
|
||||
}
|
||||
|
||||
void GlobalScripts::removeScript (const std::string& name)
|
||||
{
|
||||
std::map<std::string, std::pair<bool, Locals> >::iterator iter = mScripts.find (name);
|
||||
|
||||
if (iter!=mScripts.end())
|
||||
iter->second.first = false;
|
||||
}
|
||||
|
||||
bool GlobalScripts::isRunning (const std::string& name) const
|
||||
{
|
||||
std::map<std::string, std::pair<bool, Locals> >::const_iterator iter =
|
||||
mScripts.find (name);
|
||||
|
||||
if (iter==mScripts.end())
|
||||
return false;
|
||||
|
||||
return iter->second.first;
|
||||
}
|
||||
|
||||
void GlobalScripts::run (MWWorld::Environment& environment)
|
||||
{
|
||||
for (std::map<std::string, std::pair<bool, Locals> >::iterator iter (mScripts.begin());
|
||||
iter!=mScripts.end(); ++iter)
|
||||
{
|
||||
if (iter->second.first)
|
||||
{
|
||||
MWScript::InterpreterContext interpreterContext (environment,
|
||||
&iter->second.second, MWWorld::Ptr());
|
||||
mScriptManager.run (iter->first, interpreterContext);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,45 @@
|
||||
#ifndef GAME_SCRIPT_GLOBALSCRIPTS_H
|
||||
#define GAME_SCRIPT_GLOBALSCRIPTS_H
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
#include "locals.hpp"
|
||||
|
||||
namespace ESMS
|
||||
{
|
||||
struct ESMStore;
|
||||
}
|
||||
|
||||
namespace MWWorld
|
||||
{
|
||||
class Environment;
|
||||
}
|
||||
|
||||
namespace MWScript
|
||||
{
|
||||
class ScriptManager;
|
||||
|
||||
class GlobalScripts
|
||||
{
|
||||
const ESMS::ESMStore& mStore;
|
||||
ScriptManager& mScriptManager;
|
||||
std::map<std::string, std::pair<bool, Locals> > mScripts; // running, local variables
|
||||
|
||||
public:
|
||||
|
||||
GlobalScripts (const ESMS::ESMStore& store, ScriptManager& scriptManager);
|
||||
|
||||
void addScript (const std::string& name);
|
||||
|
||||
void removeScript (const std::string& name);
|
||||
|
||||
bool isRunning (const std::string& name) const;
|
||||
|
||||
void run (MWWorld::Environment& environment);
|
||||
///< run all active global scripts
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -0,0 +1,155 @@
|
||||
|
||||
#include "interpretercontext.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
#include <iostream>
|
||||
|
||||
#include <components/interpreter/types.hpp>
|
||||
|
||||
#include "../mwworld/world.hpp"
|
||||
|
||||
#include "locals.hpp"
|
||||
#include "globalscripts.hpp"
|
||||
|
||||
namespace MWScript
|
||||
{
|
||||
InterpreterContext::InterpreterContext (MWWorld::Environment& environment,
|
||||
MWScript::Locals *locals, MWWorld::Ptr reference)
|
||||
: mEnvironment (environment), mLocals (locals), mReference (reference)
|
||||
{}
|
||||
|
||||
int InterpreterContext::getLocalShort (int index) const
|
||||
{
|
||||
if (!mLocals)
|
||||
throw std::runtime_error ("local variables not available in this context");
|
||||
|
||||
return mLocals->mShorts.at (index);
|
||||
}
|
||||
|
||||
int InterpreterContext::getLocalLong (int index) const
|
||||
{
|
||||
if (!mLocals)
|
||||
throw std::runtime_error ("local variables not available in this context");
|
||||
|
||||
return mLocals->mLongs.at (index);
|
||||
}
|
||||
|
||||
float InterpreterContext::getLocalFloat (int index) const
|
||||
{
|
||||
if (!mLocals)
|
||||
throw std::runtime_error ("local variables not available in this context");
|
||||
|
||||
return mLocals->mFloats.at (index);
|
||||
}
|
||||
|
||||
void InterpreterContext::setLocalShort (int index, int value)
|
||||
{
|
||||
if (!mLocals)
|
||||
throw std::runtime_error ("local variables not available in this context");
|
||||
|
||||
mLocals->mShorts.at (index) = value;
|
||||
}
|
||||
|
||||
void InterpreterContext::setLocalLong (int index, int value)
|
||||
{
|
||||
if (!mLocals)
|
||||
throw std::runtime_error ("local variables not available in this context");
|
||||
|
||||
mLocals->mLongs.at (index) = value;
|
||||
}
|
||||
|
||||
void InterpreterContext::setLocalFloat (int index, float value)
|
||||
{
|
||||
if (!mLocals)
|
||||
throw std::runtime_error ("local variables not available in this context");
|
||||
|
||||
mLocals->mFloats.at (index) = value;
|
||||
}
|
||||
|
||||
void InterpreterContext::messageBox (const std::string& message,
|
||||
const std::vector<std::string>& buttons)
|
||||
{
|
||||
std::cout << "message box: " << message << std::endl;
|
||||
|
||||
if (!buttons.empty())
|
||||
std::cerr << "error: message box buttons not supported" << std::endl;
|
||||
}
|
||||
|
||||
bool InterpreterContext::menuMode()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int InterpreterContext::getGlobalShort (const std::string& name) const
|
||||
{
|
||||
Interpreter::Type_Data value = mEnvironment.mWorld->getGlobalVariable (name);
|
||||
return static_cast<Interpreter::Type_Short> (
|
||||
*reinterpret_cast<Interpreter::Type_Integer *> (&value));
|
||||
}
|
||||
|
||||
int InterpreterContext::getGlobalLong (const std::string& name) const
|
||||
{
|
||||
// a global long is internally a float.
|
||||
Interpreter::Type_Data value = mEnvironment.mWorld->getGlobalVariable (name);
|
||||
return static_cast<Interpreter::Type_Integer> (
|
||||
*reinterpret_cast<Interpreter::Type_Float *> (&value));
|
||||
}
|
||||
|
||||
float InterpreterContext::getGlobalFloat (const std::string& name) const
|
||||
{
|
||||
Interpreter::Type_Data value = mEnvironment.mWorld->getGlobalVariable (name);
|
||||
return *reinterpret_cast<Interpreter::Type_Float *> (&value);
|
||||
}
|
||||
|
||||
void InterpreterContext::setGlobalShort (const std::string& name, int value)
|
||||
{
|
||||
mEnvironment.mWorld->getGlobalVariable (name) =
|
||||
*reinterpret_cast<Interpreter::Type_Data *> (&value);
|
||||
}
|
||||
|
||||
void InterpreterContext::setGlobalLong (const std::string& name, int value)
|
||||
{
|
||||
// a global long is internally a float.
|
||||
float value2 = value;
|
||||
|
||||
mEnvironment.mWorld->getGlobalVariable (name) =
|
||||
*reinterpret_cast<Interpreter::Type_Data *> (&value2);
|
||||
}
|
||||
|
||||
void InterpreterContext::setGlobalFloat (const std::string& name, float value)
|
||||
{
|
||||
mEnvironment.mWorld->getGlobalVariable (name) =
|
||||
*reinterpret_cast<Interpreter::Type_Data *> (&value);
|
||||
}
|
||||
|
||||
bool InterpreterContext::isScriptRunning (const std::string& name)
|
||||
{
|
||||
return mEnvironment.mGlobalScripts->isRunning (name);
|
||||
}
|
||||
|
||||
void InterpreterContext::startScript (const std::string& name)
|
||||
{
|
||||
mEnvironment.mGlobalScripts->addScript (name);
|
||||
}
|
||||
|
||||
void InterpreterContext::stopScript (const std::string& name)
|
||||
{
|
||||
mEnvironment.mGlobalScripts->removeScript (name);
|
||||
}
|
||||
|
||||
MWWorld::World& InterpreterContext::getWorld()
|
||||
{
|
||||
return *mEnvironment.mWorld;
|
||||
}
|
||||
|
||||
MWSound::SoundManager& InterpreterContext::getSoundManager()
|
||||
{
|
||||
return *mEnvironment.mSoundManager;
|
||||
}
|
||||
|
||||
MWWorld::Ptr InterpreterContext::getReference()
|
||||
{
|
||||
return mReference;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,81 @@
|
||||
#ifndef GAME_SCRIPT_INTERPRETERCONTEXT_H
|
||||
#define GAME_SCRIPT_INTERPRETERCONTEXT_H
|
||||
|
||||
#include <components/interpreter/context.hpp>
|
||||
|
||||
#include "../mwworld/ptr.hpp"
|
||||
#include "../mwworld/environment.hpp"
|
||||
|
||||
namespace MWWorld
|
||||
{
|
||||
class World;
|
||||
}
|
||||
|
||||
namespace MWSound
|
||||
{
|
||||
class SoundManager;
|
||||
}
|
||||
|
||||
namespace MWScript
|
||||
{
|
||||
struct Locals;
|
||||
|
||||
class InterpreterContext : public Interpreter::Context
|
||||
{
|
||||
MWWorld::Environment& mEnvironment;
|
||||
Locals *mLocals;
|
||||
MWWorld::Ptr mReference;
|
||||
|
||||
public:
|
||||
|
||||
InterpreterContext (MWWorld::Environment& environment,
|
||||
MWScript::Locals *locals, MWWorld::Ptr reference);
|
||||
///< The ownership of \a locals is not transferred. 0-pointer allowed.
|
||||
|
||||
virtual int getLocalShort (int index) const;
|
||||
|
||||
virtual int getLocalLong (int index) const;
|
||||
|
||||
virtual float getLocalFloat (int index) const;
|
||||
|
||||
virtual void setLocalShort (int index, int value);
|
||||
|
||||
virtual void setLocalLong (int index, int value);
|
||||
|
||||
virtual void setLocalFloat (int index, float value);
|
||||
|
||||
virtual void messageBox (const std::string& message,
|
||||
const std::vector<std::string>& buttons);
|
||||
|
||||
virtual bool menuMode();
|
||||
|
||||
virtual int getGlobalShort (const std::string& name) const;
|
||||
|
||||
virtual int getGlobalLong (const std::string& name) const;
|
||||
|
||||
virtual float getGlobalFloat (const std::string& name) const;
|
||||
|
||||
virtual void setGlobalShort (const std::string& name, int value);
|
||||
|
||||
virtual void setGlobalLong (const std::string& name, int value);
|
||||
|
||||
virtual void setGlobalFloat (const std::string& name, float value);
|
||||
|
||||
virtual bool isScriptRunning (const std::string& name);
|
||||
|
||||
virtual void startScript (const std::string& name);
|
||||
|
||||
virtual void stopScript (const std::string& name);
|
||||
|
||||
MWWorld::World& getWorld();
|
||||
|
||||
MWSound::SoundManager& getSoundManager();
|
||||
|
||||
MWWorld::Ptr getReference();
|
||||
///< Reference, that the script is running from (can be empty)
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -0,0 +1,30 @@
|
||||
#ifndef GAME_SCRIPT_LOCALS_H
|
||||
#define GAME_SCRIPT_LOCALS_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <components/esm/loadscpt.hpp>
|
||||
#include <components/interpreter/types.hpp>
|
||||
|
||||
namespace MWScript
|
||||
{
|
||||
struct Locals
|
||||
{
|
||||
std::vector<Interpreter::Type_Short> mShorts;
|
||||
std::vector<Interpreter::Type_Integer> mLongs;
|
||||
std::vector<Interpreter::Type_Float> mFloats;
|
||||
|
||||
void configure (const ESM::Script& script)
|
||||
{
|
||||
mShorts.clear();
|
||||
mShorts.resize (script.data.numShorts, 0);
|
||||
mLongs.clear();
|
||||
mLongs.resize (script.data.numLongs, 0);
|
||||
mFloats.clear();
|
||||
mFloats.resize (script.data.numFloats, 0);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -0,0 +1,127 @@
|
||||
|
||||
#include "scriptmanager.hpp"
|
||||
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <exception>
|
||||
|
||||
#include <components/esm/loadscpt.hpp>
|
||||
#include <components/esm_store/store.hpp>
|
||||
|
||||
#include <components/compiler/scanner.hpp>
|
||||
#include <components/compiler/context.hpp>
|
||||
|
||||
#include <components/interpreter/installopcodes.hpp>
|
||||
#include <components/interpreter/interpreter.hpp>
|
||||
|
||||
#include "../mwsound/extensions.hpp"
|
||||
|
||||
#include "cellextensions.hpp"
|
||||
|
||||
namespace MWScript
|
||||
{
|
||||
ScriptManager::ScriptManager (const ESMS::ESMStore& store, bool verbose,
|
||||
Compiler::Context& compilerContext)
|
||||
: mErrorHandler (std::cerr), mStore (store), mVerbose (verbose),
|
||||
mCompilerContext (compilerContext), mParser (mErrorHandler, mCompilerContext)
|
||||
{}
|
||||
|
||||
bool ScriptManager::compile (const std::string& name)
|
||||
{
|
||||
mParser.reset();
|
||||
mErrorHandler.reset();
|
||||
|
||||
bool Success = true;
|
||||
|
||||
if (const ESM::Script *script = mStore.scripts.find (name))
|
||||
{
|
||||
if (mVerbose)
|
||||
std::cout << "compiling script: " << name << std::endl;
|
||||
|
||||
try
|
||||
{
|
||||
std::istringstream input (script->scriptText);
|
||||
|
||||
Compiler::Scanner scanner (mErrorHandler, input, mCompilerContext.getExtensions());
|
||||
|
||||
scanner.scan (mParser);
|
||||
|
||||
if (!mErrorHandler.isGood())
|
||||
Success = false;
|
||||
}
|
||||
catch (const std::exception& error)
|
||||
{
|
||||
Success = false;
|
||||
}
|
||||
|
||||
if (!Success && mVerbose)
|
||||
{
|
||||
std::cerr
|
||||
<< "compiling failed: " << name << std::endl
|
||||
<< script->scriptText
|
||||
<< std::endl << std::endl;
|
||||
}
|
||||
|
||||
if (Success)
|
||||
{
|
||||
std::vector<Interpreter::Type_Code> code;
|
||||
mParser.getCode (code);
|
||||
mScripts.insert (std::make_pair (name, code));
|
||||
|
||||
// TODO sanity check on generated locals
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ScriptManager::run (const std::string& name, Interpreter::Context& interpreterContext)
|
||||
{
|
||||
// compile script
|
||||
std::map<std::string, std::vector<Interpreter::Type_Code> >::iterator iter =
|
||||
mScripts.find (name);
|
||||
|
||||
if (iter==mScripts.end())
|
||||
{
|
||||
if (!compile (name))
|
||||
{
|
||||
// failed -> ignore script from now on.
|
||||
std::vector<Interpreter::Type_Code> empty;
|
||||
mScripts.insert (std::make_pair (name, empty));
|
||||
return;
|
||||
}
|
||||
|
||||
iter = mScripts.find (name);
|
||||
assert (iter!=mScripts.end());
|
||||
}
|
||||
|
||||
// execute script
|
||||
if (!iter->second.empty())
|
||||
try
|
||||
{
|
||||
Interpreter::Interpreter interpreter (interpreterContext);
|
||||
installOpcodes (interpreter);
|
||||
interpreter.run (&iter->second[0], iter->second.size());
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::cerr << "exeution of script " << name << " failed." << std::endl;
|
||||
|
||||
if (mVerbose)
|
||||
std::cerr << "(" << e.what() << ")" << std::endl;
|
||||
|
||||
iter->second.clear(); // don't execute again.
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptManager::installOpcodes (Interpreter::Interpreter& interpreter)
|
||||
{
|
||||
Interpreter::installOpcodes (interpreter);
|
||||
Cell::installOpcodes (interpreter);
|
||||
MWSound::installOpcodes (interpreter);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,55 @@
|
||||
#ifndef GAME_SCRIPT_SCRIPTMANAGER_H
|
||||
#define GAME_SCRIPT_SCRIPTMANAGER_H
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include <components/compiler/streamerrorhandler.hpp>
|
||||
#include <components/compiler/fileparser.hpp>
|
||||
|
||||
#include <components/interpreter/types.hpp>
|
||||
|
||||
namespace ESMS
|
||||
{
|
||||
struct ESMStore;
|
||||
}
|
||||
|
||||
namespace Compiler
|
||||
{
|
||||
class Context;
|
||||
}
|
||||
|
||||
namespace Interpreter
|
||||
{
|
||||
class Context;
|
||||
class Interpreter;
|
||||
}
|
||||
|
||||
namespace MWScript
|
||||
{
|
||||
class ScriptManager
|
||||
{
|
||||
Compiler::StreamErrorHandler mErrorHandler;
|
||||
const ESMS::ESMStore& mStore;
|
||||
bool mVerbose;
|
||||
Compiler::Context& mCompilerContext;
|
||||
Compiler::FileParser mParser;
|
||||
|
||||
std::map<std::string, std::vector<Interpreter::Type_Code> > mScripts;
|
||||
|
||||
bool compile (const std::string& name);
|
||||
|
||||
public:
|
||||
|
||||
ScriptManager (const ESMS::ESMStore& store, bool verbose,
|
||||
Compiler::Context& compilerContext);
|
||||
|
||||
void run (const std::string& name, Interpreter::Context& interpreterContext);
|
||||
|
||||
static void installOpcodes (Interpreter::Interpreter& interpreter);
|
||||
};
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -0,0 +1,236 @@
|
||||
|
||||
#include "extensions.hpp"
|
||||
|
||||
#include <components/compiler/extensions.hpp>
|
||||
|
||||
#include <components/interpreter/interpreter.hpp>
|
||||
#include <components/interpreter/runtime.hpp>
|
||||
#include <components/interpreter/opcodes.hpp>
|
||||
|
||||
#include "../mwscript/interpretercontext.hpp"
|
||||
|
||||
#include "../mwworld/world.hpp"
|
||||
|
||||
#include "soundmanager.hpp"
|
||||
|
||||
namespace MWSound
|
||||
{
|
||||
namespace Script
|
||||
{
|
||||
class OpSay : public Interpreter::Opcode0
|
||||
{
|
||||
public:
|
||||
|
||||
virtual void execute (Interpreter::Runtime& runtime)
|
||||
{
|
||||
MWScript::InterpreterContext& context
|
||||
= static_cast<MWScript::InterpreterContext&> (runtime.getContext());
|
||||
|
||||
std::string file = runtime.getStringLiteral (runtime[0]);
|
||||
runtime.pop();
|
||||
|
||||
std::string text = runtime.getStringLiteral (runtime[0]);
|
||||
runtime.pop();
|
||||
|
||||
context.getSoundManager().say (context.getReference(), file, text,
|
||||
context);
|
||||
}
|
||||
};
|
||||
|
||||
class OpSayDone : public Interpreter::Opcode0
|
||||
{
|
||||
public:
|
||||
|
||||
virtual void execute (Interpreter::Runtime& runtime)
|
||||
{
|
||||
MWScript::InterpreterContext& context
|
||||
= static_cast<MWScript::InterpreterContext&> (runtime.getContext());
|
||||
|
||||
runtime.push (context.getSoundManager().sayDone (context.getReference(),
|
||||
context));
|
||||
}
|
||||
};
|
||||
|
||||
class OpStreamMusic : public Interpreter::Opcode0
|
||||
{
|
||||
public:
|
||||
|
||||
virtual void execute (Interpreter::Runtime& runtime)
|
||||
{
|
||||
MWScript::InterpreterContext& context
|
||||
= static_cast<MWScript::InterpreterContext&> (runtime.getContext());
|
||||
|
||||
std::string sound = runtime.getStringLiteral (runtime[0]);
|
||||
runtime.pop();
|
||||
|
||||
context.getSoundManager().streamMusic (sound, context);
|
||||
}
|
||||
};
|
||||
|
||||
class OpPlaySound : public Interpreter::Opcode0
|
||||
{
|
||||
public:
|
||||
|
||||
virtual void execute (Interpreter::Runtime& runtime)
|
||||
{
|
||||
MWScript::InterpreterContext& context
|
||||
= static_cast<MWScript::InterpreterContext&> (runtime.getContext());
|
||||
|
||||
std::string sound = runtime.getStringLiteral (runtime[0]);
|
||||
runtime.pop();
|
||||
|
||||
context.getSoundManager().playSound (sound, 1.0, 1.0, context);
|
||||
}
|
||||
};
|
||||
|
||||
class OpPlaySoundVP : public Interpreter::Opcode0
|
||||
{
|
||||
public:
|
||||
|
||||
virtual void execute (Interpreter::Runtime& runtime)
|
||||
{
|
||||
MWScript::InterpreterContext& context
|
||||
= static_cast<MWScript::InterpreterContext&> (runtime.getContext());
|
||||
|
||||
std::string sound = runtime.getStringLiteral (runtime[0]);
|
||||
runtime.pop();
|
||||
|
||||
float volume = *reinterpret_cast<float *> (&runtime[0]);
|
||||
runtime.pop();
|
||||
|
||||
float pitch = *reinterpret_cast<float *> (&runtime[0]);
|
||||
runtime.pop();
|
||||
|
||||
context.getSoundManager().playSound (sound, volume, pitch, context);
|
||||
}
|
||||
};
|
||||
|
||||
class OpPlaySound3D : public Interpreter::Opcode0
|
||||
{
|
||||
bool mLoop;
|
||||
|
||||
public:
|
||||
|
||||
OpPlaySound3D (bool loop) : mLoop (loop) {}
|
||||
|
||||
virtual void execute (Interpreter::Runtime& runtime)
|
||||
{
|
||||
MWScript::InterpreterContext& context
|
||||
= static_cast<MWScript::InterpreterContext&> (runtime.getContext());
|
||||
|
||||
std::string sound = runtime.getStringLiteral (runtime[0]);
|
||||
runtime.pop();
|
||||
|
||||
context.getSoundManager().playSound3D (context.getReference(), sound,
|
||||
1.0, 1.0, mLoop, context);
|
||||
}
|
||||
};
|
||||
|
||||
class OpPlaySoundVP3D : public Interpreter::Opcode0
|
||||
{
|
||||
bool mLoop;
|
||||
|
||||
public:
|
||||
|
||||
OpPlaySoundVP3D (bool loop) : mLoop (loop) {}
|
||||
|
||||
virtual void execute (Interpreter::Runtime& runtime)
|
||||
{
|
||||
MWScript::InterpreterContext& context
|
||||
= static_cast<MWScript::InterpreterContext&> (runtime.getContext());
|
||||
|
||||
std::string sound = runtime.getStringLiteral (runtime[0]);
|
||||
runtime.pop();
|
||||
|
||||
float volume = *reinterpret_cast<float *> (&runtime[0]);
|
||||
runtime.pop();
|
||||
|
||||
float pitch = *reinterpret_cast<float *> (&runtime[0]);
|
||||
runtime.pop();
|
||||
|
||||
context.getSoundManager().playSound3D (context.getReference(), sound, volume,
|
||||
pitch, mLoop, context);
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
class OpStopSound : public Interpreter::Opcode0
|
||||
{
|
||||
public:
|
||||
|
||||
virtual void execute (Interpreter::Runtime& runtime)
|
||||
{
|
||||
MWScript::InterpreterContext& context
|
||||
= static_cast<MWScript::InterpreterContext&> (runtime.getContext());
|
||||
|
||||
std::string sound = runtime.getStringLiteral (runtime[0]);
|
||||
runtime.pop();
|
||||
|
||||
context.getSoundManager().stopSound3D (context.getReference(), sound, context);
|
||||
}
|
||||
};
|
||||
|
||||
class OpGetSoundPlaying : public Interpreter::Opcode0
|
||||
{
|
||||
public:
|
||||
|
||||
virtual void execute (Interpreter::Runtime& runtime)
|
||||
{
|
||||
MWScript::InterpreterContext& context
|
||||
= static_cast<MWScript::InterpreterContext&> (runtime.getContext());
|
||||
|
||||
int index = runtime[0];
|
||||
runtime.pop();
|
||||
|
||||
runtime.push (context.getSoundManager().getSoundPlaying (
|
||||
context.getReference(), runtime.getStringLiteral (index), context));
|
||||
}
|
||||
};
|
||||
|
||||
const int opcodeSay = 0x2000001;
|
||||
const int opcodeSayDone = 0x2000002;
|
||||
const int opcodeStreamMusic = 0x2000003;
|
||||
const int opcodePlaySound = 0x2000004;
|
||||
const int opcodePlaySoundVP = 0x2000005;
|
||||
const int opcodePlaySound3D = 0x2000006;
|
||||
const int opcodePlaySound3DVP = 0x2000007;
|
||||
const int opcodePlayLoopSound3D = 0x2000008;
|
||||
const int opcodePlayLoopSound3DVP = 0x2000009;
|
||||
const int opcodeStopSound = 0x200000a;
|
||||
const int opcodeGetSoundPlaying = 0x200000b;
|
||||
}
|
||||
|
||||
void registerExtensions (Compiler::Extensions& extensions)
|
||||
{
|
||||
extensions.registerInstruction ("say", "SS", Script::opcodeSay);
|
||||
extensions.registerFunction ("saydone", 'l', "", Script::opcodeSayDone);
|
||||
extensions.registerInstruction ("streammusic", "S", Script::opcodeStreamMusic);
|
||||
extensions.registerInstruction ("playsound", "c", Script::opcodePlaySound);
|
||||
extensions.registerInstruction ("playsoundvp", "cff", Script::opcodePlaySoundVP);
|
||||
extensions.registerInstruction ("playsound3d", "c", Script::opcodePlaySound3D);
|
||||
extensions.registerInstruction ("playsound3dvp", "cff", Script::opcodePlaySound3DVP);
|
||||
extensions.registerInstruction ("playloopsound3d", "c", Script::opcodePlayLoopSound3D);
|
||||
extensions.registerInstruction ("playloopsound3dvp", "cff",
|
||||
Script::opcodePlayLoopSound3DVP);
|
||||
extensions.registerInstruction ("stopsound", "c", Script::opcodeStopSound);
|
||||
extensions.registerFunction ("getsoundplaying", 'l', "c", Script::opcodeGetSoundPlaying);
|
||||
}
|
||||
|
||||
void installOpcodes (Interpreter::Interpreter& interpreter)
|
||||
{
|
||||
interpreter.installSegment5 (Script::opcodeSay, new Script::OpSay);
|
||||
interpreter.installSegment5 (Script::opcodeSayDone, new Script::OpSayDone);
|
||||
interpreter.installSegment5 (Script::opcodeStreamMusic, new Script::OpStreamMusic);
|
||||
interpreter.installSegment5 (Script::opcodePlaySound, new Script::OpPlaySound);
|
||||
interpreter.installSegment5 (Script::opcodePlaySoundVP, new Script::OpPlaySoundVP);
|
||||
interpreter.installSegment5 (Script::opcodePlaySound3D, new Script::OpPlaySound3D (false));
|
||||
interpreter.installSegment5 (Script::opcodePlaySound3DVP,
|
||||
new Script::OpPlaySoundVP3D (false));
|
||||
interpreter.installSegment5 (Script::opcodePlayLoopSound3D,
|
||||
new Script::OpPlaySound3D (true));
|
||||
interpreter.installSegment5 (Script::opcodePlayLoopSound3DVP,
|
||||
new Script::OpPlaySoundVP3D (true));
|
||||
interpreter.installSegment5 (Script::opcodeStopSound, new Script::OpStopSound);
|
||||
interpreter.installSegment5 (Script::opcodeGetSoundPlaying, new Script::OpGetSoundPlaying);
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
#ifndef GAME_SOUND_EXTENSIONS_H
|
||||
#define GAME_SOUND_EXTENSIONS_H
|
||||
|
||||
namespace Compiler
|
||||
{
|
||||
class Extensions;
|
||||
}
|
||||
|
||||
namespace Interpreter
|
||||
{
|
||||
class Interpreter;
|
||||
}
|
||||
|
||||
namespace MWSound
|
||||
{
|
||||
// Script-extensions related to sound
|
||||
|
||||
void registerExtensions (Compiler::Extensions& extensions);
|
||||
|
||||
void installOpcodes (Interpreter::Interpreter& interpreter);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -0,0 +1,71 @@
|
||||
|
||||
#include "soundmanager.hpp"
|
||||
|
||||
#include <iostream> // TODO remove this line, once the real code is in place.
|
||||
|
||||
#include <components/interpreter/context.hpp>
|
||||
|
||||
namespace MWSound
|
||||
{
|
||||
void SoundManager::say (MWWorld::Ptr reference, const std::string& filename,
|
||||
const std::string& text, Interpreter::Context& context)
|
||||
{
|
||||
std::cout << "sound effect: " << reference.getRefData().getHandle() << " is speaking" << std::endl;
|
||||
|
||||
context.messageBox (text);
|
||||
}
|
||||
|
||||
bool SoundManager::sayDone (MWWorld::Ptr reference, Interpreter::Context& context) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void SoundManager::streamMusic (const std::string& filename, Interpreter::Context& context)
|
||||
{
|
||||
std::cout << "sound effect: playing music" << filename << std::endl;
|
||||
}
|
||||
|
||||
void SoundManager::playSound (const std::string& soundId, float volume, float pitch,
|
||||
Interpreter::Context& context)
|
||||
{
|
||||
std::cout
|
||||
<< "sound effect: playing sound " << soundId
|
||||
<< " at volume " << volume << ", at pitch " << pitch
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
void SoundManager::playSound3D (MWWorld::Ptr reference, const std::string& soundId,
|
||||
float volume, float pitch, bool loop, Interpreter::Context& context)
|
||||
{
|
||||
std::cout
|
||||
<< "sound effect: playing sound " << soundId
|
||||
<< " from " << reference.getRefData().getHandle()
|
||||
<< " at volume " << volume << ", at pitch " << pitch
|
||||
<< std::endl;
|
||||
|
||||
mSounds[reference.getRefData().getHandle()] = soundId;
|
||||
}
|
||||
|
||||
void SoundManager::stopSound3D (MWWorld::Ptr reference, const std::string& soundId,
|
||||
Interpreter::Context& context)
|
||||
{
|
||||
std::cout
|
||||
<< "sound effect : stop playing sound " << soundId
|
||||
<< " from " << reference.getRefData().getHandle() << std::endl;
|
||||
|
||||
mSounds[reference.getRefData().getHandle()] = "";
|
||||
}
|
||||
|
||||
bool SoundManager::getSoundPlaying (MWWorld::Ptr reference, const std::string& soundId,
|
||||
Interpreter::Context& context) const
|
||||
{
|
||||
std::map<std::string, std::string>::const_iterator iter =
|
||||
mSounds.find (reference.getRefData().getHandle());
|
||||
|
||||
if (iter==mSounds.end())
|
||||
return false;
|
||||
|
||||
return iter->second==soundId;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,68 @@
|
||||
#ifndef GAME_SOUND_SOUNDMANAGER_H
|
||||
#define GAME_SOUND_SOUNDMANAGER_H
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
#include "../mwworld/ptr.hpp"
|
||||
|
||||
namespace Interpreter
|
||||
{
|
||||
class Context;
|
||||
}
|
||||
|
||||
namespace MWSound
|
||||
{
|
||||
// Note to the sound implementor (can be removed once the implementation is complete):
|
||||
//
|
||||
// - the dummy implementation allows only one sound effect per object at a time. I am
|
||||
// not sure, if that is what Morrowind does. Beyond the dummy code in this class the script
|
||||
// system does not make any assumption about the number of sound effects.
|
||||
//
|
||||
// - all text-output (error messages and such) must be directed through the
|
||||
// context.messageBox interface.
|
||||
//
|
||||
// - the -> script syntax is not implemented yet ( script instructions of the type
|
||||
// npc_x -> say "file", "text"
|
||||
// aren't working)
|
||||
|
||||
class SoundManager
|
||||
{
|
||||
std::map<std::string, std::string> mSounds; // object, sound (for testing only)
|
||||
|
||||
public:
|
||||
|
||||
void say (MWWorld::Ptr reference, const std::string& filename,
|
||||
const std::string& text, Interpreter::Context& context);
|
||||
///< Make an actor say some text.
|
||||
/// \param filename name of a sound file in "Sound/Vo/" in the data directory.
|
||||
/// \param text Subtitle
|
||||
|
||||
bool sayDone (MWWorld::Ptr reference, Interpreter::Context& context) const;
|
||||
///< Is actor not speaking?
|
||||
|
||||
void streamMusic (const std::string& filename, Interpreter::Context& context);
|
||||
///< Play a soundifle
|
||||
/// \param filename name of a sound file in "Music/" in the data directory.
|
||||
|
||||
void playSound (const std::string& soundId, float volume, float pitch,
|
||||
Interpreter::Context& context);
|
||||
///< Play a sound, independently of 3D-position
|
||||
|
||||
void playSound3D (MWWorld::Ptr reference, const std::string& soundId,
|
||||
float volume, float pitch, bool loop, Interpreter::Context& context);
|
||||
///< Play a sound from an object
|
||||
|
||||
void stopSound3D (MWWorld::Ptr reference, const std::string& soundId,
|
||||
Interpreter::Context& context);
|
||||
///< Stop the given object from playing the given sound.
|
||||
|
||||
bool getSoundPlaying (MWWorld::Ptr reference, const std::string& soundId,
|
||||
Interpreter::Context& context) const;
|
||||
///< Is the given sound currently playing on the given object?
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -0,0 +1,30 @@
|
||||
#ifndef GAME_WORLD_INVIRONMENT_H
|
||||
#define GAME_WORLD_INVIRONMENT_H
|
||||
|
||||
namespace MWSound
|
||||
{
|
||||
class SoundManager;
|
||||
}
|
||||
|
||||
namespace MWScript
|
||||
{
|
||||
class GlobalScripts;
|
||||
}
|
||||
|
||||
namespace MWWorld
|
||||
{
|
||||
class World;
|
||||
|
||||
///< Collection of script-accessable sub-systems
|
||||
struct Environment
|
||||
{
|
||||
Environment() : mWorld (0), mSoundManager (0), mGlobalScripts (0) {}
|
||||
|
||||
World *mWorld;
|
||||
MWSound::SoundManager *mSoundManager;
|
||||
MWScript::GlobalScripts *mGlobalScripts;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -0,0 +1,55 @@
|
||||
#ifndef GAME_MWWORLD_PTR_H
|
||||
#define GAME_MWWORLD_PTR_H
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include <boost/any.hpp>
|
||||
|
||||
#include <components/esm_store/cell_store.hpp>
|
||||
|
||||
#include "refdata.hpp"
|
||||
|
||||
namespace MWWorld
|
||||
{
|
||||
/// \brief Pointer to a LiveCellRef
|
||||
|
||||
class Ptr
|
||||
{
|
||||
boost::any mPtr;
|
||||
ESM::CellRef *mCellRef;
|
||||
RefData *mRefData;
|
||||
|
||||
public:
|
||||
|
||||
Ptr() : mCellRef (0), mRefData (0) {}
|
||||
|
||||
template<typename T>
|
||||
Ptr (ESMS::LiveCellRef<T, RefData> *liveCellRef)
|
||||
{
|
||||
mPtr = liveCellRef;
|
||||
mCellRef = &liveCellRef->ref;
|
||||
mRefData = &liveCellRef->mData;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
ESMS::LiveCellRef<T, RefData> *get() const
|
||||
{
|
||||
return boost::any_cast<const ESMS::LiveCellRef<T, RefData>*> (mPtr);
|
||||
}
|
||||
|
||||
ESM::CellRef& getCellRef() const
|
||||
{
|
||||
assert (mCellRef);
|
||||
return *mCellRef;
|
||||
}
|
||||
|
||||
RefData& getRefData() const
|
||||
{
|
||||
assert (mRefData);
|
||||
return *mRefData;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -0,0 +1,54 @@
|
||||
#ifndef GAME_MWWORLD_REFDATA_H
|
||||
#define GAME_MWWORLD_REFDATA_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "../mwscript/locals.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
class Script;
|
||||
}
|
||||
|
||||
namespace MWWorld
|
||||
{
|
||||
class RefData
|
||||
{
|
||||
std::string mHandle;
|
||||
|
||||
MWScript::Locals mLocals; // if we find the overhead of heaving a locals
|
||||
// object in the refdata of refs without a script,
|
||||
// we can make this a pointer later.
|
||||
bool mHasLocals;
|
||||
|
||||
public:
|
||||
|
||||
RefData() : mHasLocals (false) {}
|
||||
|
||||
std::string getHandle()
|
||||
{
|
||||
return mHandle;
|
||||
}
|
||||
|
||||
void setLocals (const ESM::Script& script)
|
||||
{
|
||||
if (!mHasLocals)
|
||||
{
|
||||
mLocals.configure (script);
|
||||
mHasLocals = true;
|
||||
}
|
||||
}
|
||||
|
||||
void setHandle (const std::string& handle)
|
||||
{
|
||||
mHandle = handle;
|
||||
}
|
||||
|
||||
MWScript::Locals& getLocals()
|
||||
{
|
||||
return mLocals;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,150 @@
|
||||
|
||||
#include "world.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "components/bsa/bsa_archive.hpp"
|
||||
#include "components/engine/ogre/renderer.hpp"
|
||||
|
||||
#include "apps/openmw/mwrender/sky.hpp"
|
||||
#include "apps/openmw/mwrender/interior.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
template<typename T>
|
||||
void listCellScripts (const ESMS::ESMStore& store,
|
||||
ESMS::CellRefList<T, MWWorld::RefData>& cellRefList, MWWorld::World::ScriptList& scriptList)
|
||||
{
|
||||
for (typename ESMS::CellRefList<T, MWWorld::RefData>::List::iterator iter (
|
||||
cellRefList.list.begin());
|
||||
iter!=cellRefList.list.end(); ++iter)
|
||||
{
|
||||
if (!iter->base->script.empty())
|
||||
{
|
||||
if (const ESM::Script *script = store.scripts.find (iter->base->script))
|
||||
{
|
||||
iter->mData.setLocals (*script);
|
||||
|
||||
scriptList.push_back (
|
||||
std::make_pair (iter->base->script, MWWorld::Ptr (&*iter)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace MWWorld
|
||||
{
|
||||
void World::insertInteriorScripts (ESMS::CellStore<RefData>& cell)
|
||||
{
|
||||
listCellScripts (mStore, cell.activators, mLocalScripts);
|
||||
listCellScripts (mStore, cell.potions, mLocalScripts);
|
||||
listCellScripts (mStore, cell.appas, mLocalScripts);
|
||||
listCellScripts (mStore, cell.armors, mLocalScripts);
|
||||
listCellScripts (mStore, cell.books, mLocalScripts);
|
||||
listCellScripts (mStore, cell.clothes, mLocalScripts);
|
||||
listCellScripts (mStore, cell.containers, mLocalScripts);
|
||||
listCellScripts (mStore, cell.creatures, mLocalScripts);
|
||||
listCellScripts (mStore, cell.doors, mLocalScripts);
|
||||
listCellScripts (mStore, cell.ingreds, mLocalScripts);
|
||||
listCellScripts (mStore, cell.lights, mLocalScripts);
|
||||
listCellScripts (mStore, cell.lockpicks, mLocalScripts);
|
||||
listCellScripts (mStore, cell.miscItems, mLocalScripts);
|
||||
listCellScripts (mStore, cell.npcs, mLocalScripts);
|
||||
listCellScripts (mStore, cell.probes, mLocalScripts);
|
||||
listCellScripts (mStore, cell.repairs, mLocalScripts);
|
||||
listCellScripts (mStore, cell.weapons, mLocalScripts);
|
||||
}
|
||||
|
||||
World::World (Render::OgreRenderer& renderer, const boost::filesystem::path& dataDir,
|
||||
const std::string& master, const std::string& startCell, bool newGame)
|
||||
: mSkyManager (0), mScene (renderer), mPlayerPos (mScene.getCamera())
|
||||
{
|
||||
boost::filesystem::path masterPath (dataDir);
|
||||
masterPath /= master;
|
||||
|
||||
std::cout << "Loading ESM " << masterPath.string() << "\n";
|
||||
|
||||
// This parses the ESM file and loads a sample cell
|
||||
mEsm.open (masterPath.file_string());
|
||||
mStore.load (mEsm);
|
||||
|
||||
mInteriors[startCell].loadInt (startCell, mStore, mEsm);
|
||||
|
||||
insertInteriorScripts (mInteriors[startCell]);
|
||||
|
||||
// global variables
|
||||
for (ESMS::RecListT<ESM::Global>::MapType::const_iterator iter
|
||||
(mStore.globals.list.begin());
|
||||
iter != mStore.globals.list.end(); ++iter)
|
||||
mGlobalVariables.insert (std::make_pair (iter->first, iter->second.value));
|
||||
|
||||
if (newGame)
|
||||
{
|
||||
// set new game mark
|
||||
float newGameState = 1;
|
||||
mGlobalVariables["chargenstate"] =
|
||||
*reinterpret_cast<Interpreter::Type_Data *> (&newGameState);
|
||||
}
|
||||
|
||||
std::cout << "\nSetting up cell rendering\n";
|
||||
|
||||
// This connects the cell data with the rendering scene.
|
||||
mActiveCells.insert (std::make_pair (&mInteriors[startCell],
|
||||
new MWRender::InteriorCellRender (mInteriors[startCell], mScene)));
|
||||
|
||||
// Load the cell and insert it into the renderer
|
||||
for (CellRenderCollection::iterator iter (mActiveCells.begin());
|
||||
iter!=mActiveCells.end(); ++iter)
|
||||
iter->second->show();
|
||||
|
||||
// Optionally enable the sky
|
||||
// if (mEnableSky)
|
||||
// mpSkyManager = MWRender::SkyManager::create(renderer.getWindow(), mScene.getCamera());
|
||||
|
||||
}
|
||||
|
||||
World::~World()
|
||||
{
|
||||
for (CellRenderCollection::iterator iter (mActiveCells.begin());
|
||||
iter!=mActiveCells.end(); ++iter)
|
||||
delete iter->second;
|
||||
|
||||
for (CellRenderCollection::iterator iter (mBufferedCells.begin());
|
||||
iter!=mBufferedCells.end(); ++iter)
|
||||
delete iter->second;
|
||||
|
||||
delete mSkyManager;
|
||||
}
|
||||
|
||||
MWRender::PlayerPos& World::getPlayerPos()
|
||||
{
|
||||
return mPlayerPos;
|
||||
}
|
||||
|
||||
ESMS::ESMStore& World::getStore()
|
||||
{
|
||||
return mStore;
|
||||
}
|
||||
|
||||
const World::ScriptList& World::getLocalScripts() const
|
||||
{
|
||||
return mLocalScripts;
|
||||
}
|
||||
|
||||
bool World::hasCellChanged() const
|
||||
{
|
||||
// Cell change not implemented yet.
|
||||
return false;
|
||||
}
|
||||
|
||||
Interpreter::Type_Data& World::getGlobalVariable (const std::string& name)
|
||||
{
|
||||
std::map<std::string, Interpreter::Type_Data>::iterator iter = mGlobalVariables.find (name);
|
||||
|
||||
if (iter==mGlobalVariables.end())
|
||||
throw std::runtime_error ("unknown global variable: " + name);
|
||||
|
||||
return iter->second;
|
||||
}
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
#ifndef GAME_MWWORLD_WORLD_H
|
||||
#define GAME_MWWORLD_WORLD_H
|
||||
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#include <components/esm_store/cell_store.hpp>
|
||||
|
||||
#include <components/interpreter/types.hpp>
|
||||
|
||||
#include "../mwrender/playerpos.hpp"
|
||||
#include "../mwrender/mwscene.hpp"
|
||||
|
||||
#include "refdata.hpp"
|
||||
#include "ptr.hpp"
|
||||
|
||||
namespace Render
|
||||
{
|
||||
class OgreRenderer;
|
||||
}
|
||||
|
||||
namespace MWRender
|
||||
{
|
||||
class SkyManager;
|
||||
class CellRender;
|
||||
}
|
||||
|
||||
namespace MWWorld
|
||||
{
|
||||
/// \brief The game world and its visual representation
|
||||
|
||||
class World
|
||||
{
|
||||
public:
|
||||
|
||||
typedef std::vector<std::pair<std::string, Ptr> > ScriptList;
|
||||
|
||||
private:
|
||||
|
||||
typedef ESMS::CellStore<RefData> CellStore;
|
||||
typedef std::map<CellStore *, MWRender::CellRender *> CellRenderCollection;
|
||||
|
||||
MWRender::SkyManager* mSkyManager;
|
||||
MWRender::MWScene mScene;
|
||||
MWRender::PlayerPos mPlayerPos;
|
||||
CellRenderCollection mActiveCells;
|
||||
CellRenderCollection mBufferedCells; // loaded, but not active (buffering not implementd yet)
|
||||
ESM::ESMReader mEsm;
|
||||
ESMS::ESMStore mStore;
|
||||
std::map<std::string, CellStore> mInteriors;
|
||||
ScriptList mLocalScripts;
|
||||
std::map<std::string, Interpreter::Type_Data> mGlobalVariables;
|
||||
|
||||
// not implemented
|
||||
World (const World&);
|
||||
World& operator= (const World&);
|
||||
|
||||
void insertInteriorScripts (ESMS::CellStore<RefData>& cell);
|
||||
|
||||
public:
|
||||
|
||||
World (Render::OgreRenderer& renderer, const boost::filesystem::path& master,
|
||||
const std::string& dataDir, const std::string& startCell, bool newGame);
|
||||
|
||||
~World();
|
||||
|
||||
MWRender::PlayerPos& getPlayerPos();
|
||||
|
||||
ESMS::ESMStore& getStore();
|
||||
|
||||
const ScriptList& getLocalScripts() const;
|
||||
///< Names and local variable state of all local scripts in active cells.
|
||||
|
||||
bool hasCellChanged() const;
|
||||
///< Has the player moved to a different cell, since the last frame?
|
||||
|
||||
Interpreter::Type_Data& getGlobalVariable (const std::string& name);
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,21 @@
|
||||
#ifndef COMMANDSERVER_COMMAND_HPP
|
||||
#define COMMANDSERVER_COMMAND_HPP
|
||||
|
||||
#include <boost/function.hpp>
|
||||
|
||||
namespace OMW
|
||||
{
|
||||
///
|
||||
/// A Command is currently defined as a string input that, when processed,
|
||||
/// will generate a string output. The string output is passed to the
|
||||
/// mReplyFunction as soon as the command has been processed.
|
||||
///
|
||||
class Command
|
||||
{
|
||||
public:
|
||||
std::string mCommand;
|
||||
boost::function1<void, std::string> mReplyFunction;
|
||||
};
|
||||
}
|
||||
|
||||
#endif //COMMANDSERVER_COMMAND_HPP
|
@ -0,0 +1,201 @@
|
||||
|
||||
#include "server.hpp"
|
||||
|
||||
using boost::asio::ip::tcp;
|
||||
|
||||
#include <libs/mangle/tools/str_exception.hpp>
|
||||
|
||||
//
|
||||
// Namespace for containing implementation details that the
|
||||
// rest of OpenMW doesn't need to worry about
|
||||
//
|
||||
namespace OMW { namespace CommandServer { namespace Detail {
|
||||
|
||||
struct Header
|
||||
{
|
||||
char magic[4];
|
||||
size_t dataLength;
|
||||
} header;
|
||||
|
||||
///
|
||||
/// Tracks an active connection to the CommandServer
|
||||
///
|
||||
class Connection
|
||||
{
|
||||
public:
|
||||
Connection (boost::asio::io_service& io_service, Server* pServer);
|
||||
|
||||
void start();
|
||||
void stop();
|
||||
|
||||
tcp::socket& socket();
|
||||
void reply (std::string s);
|
||||
|
||||
protected:
|
||||
void handle ();
|
||||
|
||||
tcp::socket mSocket;
|
||||
Server* mpServer;
|
||||
boost::thread* mpThread;
|
||||
};
|
||||
|
||||
Connection::Connection (boost::asio::io_service& io_service, Server* pServer)
|
||||
: mSocket (io_service)
|
||||
, mpServer (pServer)
|
||||
{
|
||||
}
|
||||
|
||||
void Connection::start()
|
||||
{
|
||||
mpThread = new boost::thread(boost::bind(&Connection::handle, this));
|
||||
}
|
||||
|
||||
///
|
||||
/// Stops and disconnects the connection
|
||||
///
|
||||
void Connection::stop()
|
||||
{
|
||||
mSocket.close();
|
||||
mpThread->join();
|
||||
}
|
||||
|
||||
tcp::socket& Connection::socket()
|
||||
{
|
||||
return mSocket;
|
||||
}
|
||||
|
||||
void Connection::reply (std::string reply)
|
||||
{
|
||||
const size_t plen = sizeof(Header) + reply.length() + 1;
|
||||
|
||||
std::vector<char> packet(plen);
|
||||
Header* pHeader = reinterpret_cast<Header*>(&packet[0]);
|
||||
strncpy(pHeader->magic, "OMW0", 4);
|
||||
pHeader->dataLength = reply.length() + 1; // Include the null terminator
|
||||
strncpy(&packet[8], reply.c_str(), pHeader->dataLength);
|
||||
|
||||
boost::system::error_code ec;
|
||||
boost::asio::write(mSocket, boost::asio::buffer(packet),
|
||||
boost::asio::transfer_all(), ec);
|
||||
if (ec)
|
||||
std::cout << "Error: " << ec.message() << std::endl;
|
||||
}
|
||||
|
||||
void Connection::handle ()
|
||||
{
|
||||
bool bDone = false;
|
||||
while (!bDone)
|
||||
{
|
||||
// Read the header
|
||||
boost::system::error_code error;
|
||||
mSocket.read_some(boost::asio::buffer(&header, sizeof(Header)), error);
|
||||
|
||||
if (error != boost::asio::error::eof)
|
||||
{
|
||||
if (strncmp(header.magic, "OMW0", 4) == 0)
|
||||
{
|
||||
std::vector<char> msg;
|
||||
msg.resize(header.dataLength);
|
||||
|
||||
boost::system::error_code error;
|
||||
mSocket.read_some(boost::asio::buffer(&msg[0], header.dataLength), error);
|
||||
if (!error)
|
||||
mpServer->postCommand(this, &msg[0]);
|
||||
else
|
||||
bDone = true;
|
||||
}
|
||||
else
|
||||
throw str_exception("Unexpected header!");
|
||||
}
|
||||
else
|
||||
bDone = true;
|
||||
}
|
||||
mpServer->removeConnection(this);
|
||||
}
|
||||
|
||||
}}}
|
||||
|
||||
namespace OMW { namespace CommandServer {
|
||||
|
||||
using namespace Detail;
|
||||
|
||||
Server::Server (Deque* pCommandQueue, const int port)
|
||||
: mAcceptor (mIOService, tcp::endpoint(tcp::v4(), port))
|
||||
, mbStopping (false)
|
||||
, mpCommandQueue (pCommandQueue)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void Server::start()
|
||||
{
|
||||
mIOService.run();
|
||||
mpThread = new boost::thread(boost::bind(&Server::threadMain, this));
|
||||
}
|
||||
|
||||
void Server::stop()
|
||||
{
|
||||
// (1) Stop accepting new connections
|
||||
// (2) Wait for the listener thread to finish
|
||||
mAcceptor.close();
|
||||
mpThread->join();
|
||||
|
||||
// Now that no new connections are possible, close any existing
|
||||
// open connections
|
||||
{
|
||||
boost::mutex::scoped_lock lock(mConnectionsMutex);
|
||||
mbStopping = true;
|
||||
for (ConnectionSet::iterator it = mConnections.begin();
|
||||
it != mConnections.end();
|
||||
++it)
|
||||
{
|
||||
(*it)->stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Server::removeConnection (Connection* ptr)
|
||||
{
|
||||
// If the server is shutting down (rather the client closing the
|
||||
// connection), don't remove the connection from the list: that
|
||||
// would corrupt the iterator the server is using to shutdown all
|
||||
// clients.
|
||||
if (!mbStopping)
|
||||
{
|
||||
boost::mutex::scoped_lock lock(mConnectionsMutex);
|
||||
std::set<Connection*>::iterator it = mConnections.find(ptr);
|
||||
if (it != mConnections.end())
|
||||
mConnections.erase(it);
|
||||
}
|
||||
delete ptr;
|
||||
}
|
||||
|
||||
void Server::postCommand (Connection* pConnection, const char* s)
|
||||
{
|
||||
Command cmd;
|
||||
cmd.mCommand = s;
|
||||
cmd.mReplyFunction = std::bind1st(std::mem_fun(&Connection::reply), pConnection);
|
||||
mpCommandQueue->push_back(cmd);
|
||||
}
|
||||
|
||||
void Server::threadMain()
|
||||
{
|
||||
// Loop until accept() fails, which will cause the break statement to be hit
|
||||
while (true)
|
||||
{
|
||||
std::auto_ptr<Connection> spConnection(new Connection(mAcceptor.io_service(), this));
|
||||
boost::system::error_code ec;
|
||||
mAcceptor.accept(spConnection->socket(), ec);
|
||||
if (!ec)
|
||||
{
|
||||
boost::mutex::scoped_lock lock(mConnectionsMutex);
|
||||
mConnections.insert(spConnection.get());
|
||||
spConnection->start();
|
||||
spConnection.release();
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}}
|
@ -0,0 +1,63 @@
|
||||
#ifndef CONSOLESERVER_H
|
||||
#define CONSOLESERVER_H
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <deque>
|
||||
#include <set>
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/thread.hpp>
|
||||
|
||||
#include "components/misc/tsdeque.hpp"
|
||||
#include "components/commandserver/command.hpp"
|
||||
|
||||
namespace OMW { namespace CommandServer
|
||||
{
|
||||
//
|
||||
// Forward Declarations
|
||||
//
|
||||
namespace Detail
|
||||
{
|
||||
class Connection;
|
||||
}
|
||||
|
||||
//
|
||||
// Server that opens a port to listen for string commands which will be
|
||||
// put into the deque provided in the Server constructor.
|
||||
//
|
||||
class Server
|
||||
{
|
||||
public:
|
||||
typedef TsDeque<Command> Deque;
|
||||
|
||||
Server (Deque* pCommandQueue, const int port);
|
||||
|
||||
void start();
|
||||
void stop();
|
||||
|
||||
protected:
|
||||
friend class Detail::Connection;
|
||||
typedef std::set<Detail::Connection*> ConnectionSet;
|
||||
|
||||
void removeConnection (Detail::Connection* ptr);
|
||||
void postCommand (Detail::Connection*, const char* s);
|
||||
|
||||
void threadMain();
|
||||
|
||||
// Objects used to set up the listening server
|
||||
boost::asio::io_service mIOService;
|
||||
boost::asio::ip::tcp::acceptor mAcceptor;
|
||||
boost::thread* mpThread;
|
||||
bool mbStopping;
|
||||
|
||||
// Track active connections
|
||||
ConnectionSet mConnections;
|
||||
mutable boost::mutex mConnectionsMutex;
|
||||
|
||||
// Pointer to command queue
|
||||
Deque* mpCommandQueue;
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#endif // CONSOLESERVER_H
|
@ -0,0 +1,39 @@
|
||||
#ifndef COMPILER_CONTEXT_H_INCLUDED
|
||||
#define COMPILER_CONTEXT_H_INCLUDED
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace Compiler
|
||||
{
|
||||
class Extensions;
|
||||
|
||||
class Context
|
||||
{
|
||||
const Extensions *mExtensions;
|
||||
|
||||
public:
|
||||
|
||||
Context() : mExtensions (0) {}
|
||||
|
||||
virtual ~Context() {}
|
||||
|
||||
virtual bool canDeclareLocals() const = 0;
|
||||
///< Is the compiler allowed to declare local variables?
|
||||
|
||||
void setExtensions (const Extensions *extensions = 0)
|
||||
{
|
||||
mExtensions = extensions;
|
||||
}
|
||||
|
||||
const Extensions *getExtensions() const
|
||||
{
|
||||
return mExtensions;
|
||||
}
|
||||
|
||||
virtual char getGlobalType (const std::string& name) const = 0;
|
||||
///< 'l: long, 's': short, 'f': float, ' ': does not exist.
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -0,0 +1,250 @@
|
||||
|
||||
#include "controlparser.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "scanner.hpp"
|
||||
#include "generator.hpp"
|
||||
|
||||
namespace Compiler
|
||||
{
|
||||
bool ControlParser::parseIfBody (int keyword, const TokenLoc& loc, Scanner& scanner)
|
||||
{
|
||||
if (keyword==Scanner::K_endif || keyword==Scanner::K_elseif ||
|
||||
keyword==Scanner::K_else)
|
||||
{
|
||||
std::pair<Codes, Codes> entry;
|
||||
|
||||
if (mState!=IfElseBodyState)
|
||||
mExprParser.append (entry.first);
|
||||
|
||||
std::copy (mCodeBlock.begin(), mCodeBlock.end(),
|
||||
std::back_inserter (entry.second));
|
||||
|
||||
mIfCode.push_back (entry);
|
||||
|
||||
mCodeBlock.clear();
|
||||
|
||||
if (keyword==Scanner::K_endif)
|
||||
{
|
||||
// store code for if-cascade
|
||||
Codes codes;
|
||||
|
||||
for (IfCodes::reverse_iterator iter (mIfCode.rbegin());
|
||||
iter!=mIfCode.rend(); ++iter)
|
||||
{
|
||||
Codes block;
|
||||
|
||||
if (iter!=mIfCode.rbegin())
|
||||
Generator::jump (iter->second, codes.size()+1);
|
||||
|
||||
if (!iter->first.empty())
|
||||
{
|
||||
// if or elseif
|
||||
std::copy (iter->first.begin(), iter->first.end(),
|
||||
std::back_inserter (block));
|
||||
Generator::jumpOnZero (block, iter->second.size()+1);
|
||||
}
|
||||
|
||||
std::copy (iter->second.begin(), iter->second.end(),
|
||||
std::back_inserter (block));
|
||||
|
||||
std::swap (codes, block);
|
||||
|
||||
std::copy (block.begin(), block.end(), std::back_inserter (codes));
|
||||
}
|
||||
|
||||
std::copy (codes.begin(), codes.end(), std::back_inserter (mCode));
|
||||
|
||||
mIfCode.clear();
|
||||
mState = IfEndifState;
|
||||
}
|
||||
else if (keyword==Scanner::K_elseif)
|
||||
{
|
||||
mExprParser.reset();
|
||||
scanner.scan (mExprParser);
|
||||
|
||||
mState = IfElseifEndState;
|
||||
}
|
||||
else if (keyword==Scanner::K_else)
|
||||
{
|
||||
mState = IfElseEndState;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (keyword==Scanner::K_if || keyword==Scanner::K_while)
|
||||
{
|
||||
// nested
|
||||
ControlParser parser (getErrorHandler(), getContext(), mLocals, mLiterals);
|
||||
|
||||
if (parser.parseKeyword (keyword, loc, scanner))
|
||||
scanner.scan (parser);
|
||||
|
||||
parser.appendCode (mCodeBlock);
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
mLineParser.reset();
|
||||
if (mLineParser.parseKeyword (keyword, loc, scanner))
|
||||
scanner.scan (mLineParser);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ControlParser::parseWhileBody (int keyword, const TokenLoc& loc, Scanner& scanner)
|
||||
{
|
||||
if (keyword==Scanner::K_endwhile)
|
||||
{
|
||||
Codes loop;
|
||||
|
||||
Codes expr;
|
||||
mExprParser.append (expr);
|
||||
|
||||
Generator::jump (loop, -mCodeBlock.size()-expr.size());
|
||||
|
||||
std::copy (expr.begin(), expr.end(), std::back_inserter (mCode));
|
||||
|
||||
Codes skip;
|
||||
|
||||
Generator::jumpOnZero (skip, mCodeBlock.size()+loop.size()+1);
|
||||
|
||||
std::copy (skip.begin(), skip.end(), std::back_inserter (mCode));
|
||||
|
||||
std::copy (mCodeBlock.begin(), mCodeBlock.end(), std::back_inserter (mCode));
|
||||
|
||||
Codes loop2;
|
||||
|
||||
Generator::jump (loop2, -mCodeBlock.size()-expr.size()-skip.size());
|
||||
|
||||
if (loop.size()!=loop2.size())
|
||||
throw std::logic_error (
|
||||
"internal compiler error: failed to generate a while loop");
|
||||
|
||||
std::copy (loop2.begin(), loop2.end(), std::back_inserter (mCode));
|
||||
|
||||
mState = WhileEndwhileState;
|
||||
return true;
|
||||
}
|
||||
else if (keyword==Scanner::K_if || keyword==Scanner::K_while)
|
||||
{
|
||||
// nested
|
||||
ControlParser parser (getErrorHandler(), getContext(), mLocals, mLiterals);
|
||||
|
||||
if (parser.parseKeyword (keyword, loc, scanner))
|
||||
scanner.scan (parser);
|
||||
|
||||
parser.appendCode (mCodeBlock);
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
mLineParser.reset();
|
||||
if (mLineParser.parseKeyword (keyword, loc, scanner))
|
||||
scanner.scan (mLineParser);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
ControlParser::ControlParser (ErrorHandler& errorHandler, Context& context, Locals& locals,
|
||||
Literals& literals)
|
||||
: Parser (errorHandler, context), mLocals (locals), mLiterals (literals),
|
||||
mLineParser (errorHandler, context, locals, literals, mCodeBlock),
|
||||
mExprParser (errorHandler, context, locals, literals),
|
||||
mState (StartState)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void ControlParser::appendCode (std::vector<Interpreter::Type_Code>& code) const
|
||||
{
|
||||
std::copy (mCode.begin(), mCode.end(), std::back_inserter (code));
|
||||
}
|
||||
|
||||
bool ControlParser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner)
|
||||
{
|
||||
if (mState==StartState)
|
||||
{
|
||||
if (keyword==Scanner::K_if)
|
||||
{
|
||||
mExprParser.reset();
|
||||
scanner.scan (mExprParser);
|
||||
|
||||
mState = IfEndState;
|
||||
return true;
|
||||
}
|
||||
else if (keyword==Scanner::K_while)
|
||||
{
|
||||
mExprParser.reset();
|
||||
scanner.scan (mExprParser);
|
||||
|
||||
mState = WhileEndState;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (mState==IfBodyState || mState==IfElseifBodyState || mState==IfElseBodyState)
|
||||
{
|
||||
if (parseIfBody (keyword, loc, scanner))
|
||||
return true;
|
||||
}
|
||||
else if (mState==WhileBodyState)
|
||||
{
|
||||
if ( parseWhileBody (keyword, loc, scanner))
|
||||
return true;
|
||||
}
|
||||
|
||||
return Parser::parseKeyword (keyword, loc, scanner);
|
||||
}
|
||||
|
||||
bool ControlParser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner)
|
||||
{
|
||||
if (code==Scanner::S_newline)
|
||||
{
|
||||
switch (mState)
|
||||
{
|
||||
case IfEndState: mState = IfBodyState; return true;
|
||||
case IfElseifEndState: mState = IfElseifBodyState; return true;
|
||||
case IfElseEndState: mState = IfElseBodyState; return true;
|
||||
|
||||
case WhileEndState: mState = WhileBodyState; return true;
|
||||
|
||||
case IfBodyState:
|
||||
case IfElseifBodyState:
|
||||
case IfElseBodyState:
|
||||
case WhileBodyState:
|
||||
|
||||
return true; // empty line
|
||||
|
||||
case IfEndifState:
|
||||
case WhileEndwhileState:
|
||||
|
||||
return false;
|
||||
|
||||
default: ;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return Parser::parseSpecial (code, loc, scanner);
|
||||
}
|
||||
|
||||
void ControlParser::reset()
|
||||
{
|
||||
mCode.clear();
|
||||
mCodeBlock.clear();
|
||||
mIfCode.clear();
|
||||
mState = StartState;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,70 @@
|
||||
#ifndef COMPILER_CONTROLPARSER_H_INCLUDED
|
||||
#define COMPILER_CONTROLPARSER_H_INCLUDED
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <components/interpreter/types.hpp>
|
||||
|
||||
#include "parser.hpp"
|
||||
#include "exprparser.hpp"
|
||||
#include "lineparser.hpp"
|
||||
|
||||
namespace Compiler
|
||||
{
|
||||
class Locals;
|
||||
class Literals;
|
||||
|
||||
// Control structure parser
|
||||
|
||||
class ControlParser : public Parser
|
||||
{
|
||||
enum State
|
||||
{
|
||||
StartState,
|
||||
IfEndState, IfBodyState,
|
||||
IfElseifEndState, IfElseifBodyState,
|
||||
IfElseEndState, IfElseBodyState,
|
||||
IfEndifState,
|
||||
WhileEndState, WhileBodyState,
|
||||
WhileEndwhileState
|
||||
};
|
||||
|
||||
typedef std::vector<Interpreter::Type_Code> Codes;
|
||||
typedef std::vector<std::pair<Codes, Codes> > IfCodes;
|
||||
|
||||
Locals& mLocals;
|
||||
Literals& mLiterals;
|
||||
Codes mCode;
|
||||
Codes mCodeBlock;
|
||||
IfCodes mIfCode; // condition, body
|
||||
LineParser mLineParser;
|
||||
ExprParser mExprParser;
|
||||
State mState;
|
||||
|
||||
bool parseIfBody (int keyword, const TokenLoc& loc, Scanner& scanner);
|
||||
|
||||
bool parseWhileBody (int keyword, const TokenLoc& loc, Scanner& scanner);
|
||||
|
||||
public:
|
||||
|
||||
ControlParser (ErrorHandler& errorHandler, Context& context, Locals& locals,
|
||||
Literals& literals);
|
||||
|
||||
void appendCode (std::vector<Interpreter::Type_Code>& code) const;
|
||||
///< store generated code in \æ code.
|
||||
|
||||
virtual bool parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner);
|
||||
///< Handle a keyword token.
|
||||
/// \return fetch another token?
|
||||
|
||||
virtual bool parseSpecial (int code, const TokenLoc& loc, Scanner& scanner);
|
||||
///< Handle a special character token.
|
||||
/// \return fetch another token?
|
||||
|
||||
void reset();
|
||||
///< Reset parser to clean state.
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -0,0 +1,65 @@
|
||||
|
||||
#include "errorhandler.hpp"
|
||||
|
||||
namespace Compiler
|
||||
{
|
||||
// constructor
|
||||
|
||||
ErrorHandler::ErrorHandler() : mWarnings (0), mErrors (0) {}
|
||||
|
||||
// destructor
|
||||
|
||||
ErrorHandler::~ErrorHandler() {}
|
||||
|
||||
// Was compiling successful?
|
||||
|
||||
bool ErrorHandler::isGood() const
|
||||
{
|
||||
return mErrors==0;
|
||||
}
|
||||
|
||||
// Return number of errors
|
||||
|
||||
int ErrorHandler::countErrors() const
|
||||
{
|
||||
return mErrors;
|
||||
}
|
||||
|
||||
// Return number of warnings
|
||||
|
||||
int ErrorHandler::countWarnings() const
|
||||
{
|
||||
return mWarnings;
|
||||
}
|
||||
|
||||
// Generate a warning message.
|
||||
|
||||
void ErrorHandler::warning (const std::string& message, const TokenLoc& loc)
|
||||
{
|
||||
++mWarnings;
|
||||
report (message, loc, WarningMessage);
|
||||
}
|
||||
|
||||
// Generate an error message.
|
||||
|
||||
void ErrorHandler::error (const std::string& message, const TokenLoc& loc)
|
||||
{
|
||||
++mErrors;
|
||||
report (message, loc, ErrorMessage);
|
||||
}
|
||||
|
||||
// Generate an error message for an unexpected EOF.
|
||||
|
||||
void ErrorHandler::endOfFile()
|
||||
{
|
||||
++mErrors;
|
||||
report ("unexpected end of file", ErrorMessage);
|
||||
}
|
||||
|
||||
// Remove all previous error/warning events
|
||||
|
||||
void ErrorHandler::reset()
|
||||
{
|
||||
mErrors = mWarnings = 0;
|
||||
}
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
|
||||
#ifndef COMPILER_ERRORHANDLER_H_INCLUDED
|
||||
#define COMPILER_ERRORHANDLER_H_INCLUDED
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace Compiler
|
||||
{
|
||||
struct TokenLoc;
|
||||
|
||||
/// \brief Error handling
|
||||
///
|
||||
/// This class collects errors and provides an interface for reporting them to the user.
|
||||
|
||||
class ErrorHandler
|
||||
{
|
||||
int mWarnings;
|
||||
int mErrors;
|
||||
|
||||
protected:
|
||||
|
||||
enum Type
|
||||
{
|
||||
WarningMessage, ErrorMessage
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
// mutators
|
||||
|
||||
virtual void report (const std::string& message, const TokenLoc& loc, Type type) = 0;
|
||||
///< Report error to the user.
|
||||
|
||||
virtual void report (const std::string& message, Type type) = 0;
|
||||
///< Report a file related error
|
||||
|
||||
public:
|
||||
|
||||
ErrorHandler();
|
||||
///< constructor
|
||||
|
||||
virtual ~ErrorHandler();
|
||||
///< destructor
|
||||
|
||||
bool isGood() const;
|
||||
///< Was compiling successful?
|
||||
|
||||
int countErrors() const;
|
||||
///< Return number of errors
|
||||
|
||||
int countWarnings() const;
|
||||
///< Return number of warnings
|
||||
|
||||
void warning (const std::string& message, const TokenLoc& loc);
|
||||
///< Generate a warning message.
|
||||
|
||||
void error (const std::string& message, const TokenLoc& loc);
|
||||
///< Generate an error message.
|
||||
|
||||
void endOfFile();
|
||||
///< Generate an error message for an unexpected EOF.
|
||||
|
||||
virtual void reset();
|
||||
///< Remove all previous error/warning events
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,32 @@
|
||||
#ifndef COMPILER_EXCEPTION_H_INCLUDED
|
||||
#define COMPILER_EXCEPTION_H_INCLUDED
|
||||
|
||||
#include <exception>
|
||||
|
||||
namespace Compiler
|
||||
{
|
||||
/// \brief Exception: Error while parsing the source
|
||||
|
||||
class SourceException : public std::exception
|
||||
{
|
||||
virtual const char *what() const throw() { return "compile error";}
|
||||
///< Return error message
|
||||
};
|
||||
|
||||
/// \brief Exception: File error
|
||||
|
||||
class FileException : public SourceException
|
||||
{
|
||||
virtual const char *what() const throw() { return "can't read file"; }
|
||||
///< Return error message
|
||||
};
|
||||
|
||||
/// \brief Exception: EOF condition encountered
|
||||
|
||||
class EOFException : public SourceException
|
||||
{ virtual const char *what() const throw() { return "end of file"; }
|
||||
///< Return error message
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,551 @@
|
||||
|
||||
#include "exprparser.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
#include <cassert>
|
||||
#include <algorithm>
|
||||
#include <stack>
|
||||
#include <iterator>
|
||||
|
||||
#include "generator.hpp"
|
||||
#include "scanner.hpp"
|
||||
#include "errorhandler.hpp"
|
||||
#include "locals.hpp"
|
||||
#include "stringparser.hpp"
|
||||
#include "extensions.hpp"
|
||||
#include "context.hpp"
|
||||
|
||||
namespace Compiler
|
||||
{
|
||||
int ExprParser::getPriority (char op) const
|
||||
{
|
||||
switch (op)
|
||||
{
|
||||
case '(':
|
||||
|
||||
return 0;
|
||||
|
||||
case 'e': // ==
|
||||
case 'n': // !=
|
||||
case 'l': // <
|
||||
case 'L': // <=
|
||||
case 'g': // <
|
||||
case 'G': // >=
|
||||
|
||||
return 1;
|
||||
|
||||
case '+':
|
||||
case '-':
|
||||
|
||||
return 2;
|
||||
|
||||
case '*':
|
||||
case '/':
|
||||
|
||||
return 3;
|
||||
|
||||
case 'm':
|
||||
|
||||
return 4;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
char ExprParser::getOperandType (int Index) const
|
||||
{
|
||||
assert (!mOperands.empty());
|
||||
assert (Index>=0);
|
||||
assert (Index<static_cast<int> (mOperands.size()));
|
||||
return mOperands[mOperands.size()-1-Index];
|
||||
}
|
||||
|
||||
char ExprParser::getOperator() const
|
||||
{
|
||||
assert (!mOperators.empty());
|
||||
return mOperators[mOperators.size()-1];
|
||||
}
|
||||
|
||||
bool ExprParser::isOpen() const
|
||||
{
|
||||
return std::find (mOperators.begin(), mOperators.end(), '(')!=mOperators.end();
|
||||
}
|
||||
|
||||
void ExprParser::popOperator()
|
||||
{
|
||||
assert (!mOperators.empty());
|
||||
mOperators.resize (mOperators.size()-1);
|
||||
}
|
||||
|
||||
void ExprParser::popOperand()
|
||||
{
|
||||
assert (!mOperands.empty());
|
||||
mOperands.resize (mOperands.size()-1);
|
||||
}
|
||||
|
||||
void ExprParser::replaceBinaryOperands()
|
||||
{
|
||||
char t1 = getOperandType (1);
|
||||
char t2 = getOperandType();
|
||||
|
||||
popOperand();
|
||||
popOperand();
|
||||
|
||||
if (t1==t2)
|
||||
mOperands.push_back (t1);
|
||||
else if (t1=='f' || t2=='f')
|
||||
mOperands.push_back ('f');
|
||||
else
|
||||
std::logic_error ("failed to determine result operand type");
|
||||
}
|
||||
|
||||
void ExprParser::pop()
|
||||
{
|
||||
char op = getOperator();
|
||||
|
||||
switch (op)
|
||||
{
|
||||
case 'm':
|
||||
|
||||
Generator::negate (mCode, getOperandType());
|
||||
popOperator();
|
||||
break;
|
||||
|
||||
case '+':
|
||||
|
||||
Generator::add (mCode, getOperandType (1), getOperandType());
|
||||
popOperator();
|
||||
replaceBinaryOperands();
|
||||
break;
|
||||
|
||||
case '-':
|
||||
|
||||
Generator::sub (mCode, getOperandType (1), getOperandType());
|
||||
popOperator();
|
||||
replaceBinaryOperands();
|
||||
break;
|
||||
|
||||
case '*':
|
||||
|
||||
Generator::mul (mCode, getOperandType (1), getOperandType());
|
||||
popOperator();
|
||||
replaceBinaryOperands();
|
||||
break;
|
||||
|
||||
case '/':
|
||||
|
||||
Generator::div (mCode, getOperandType (1), getOperandType());
|
||||
popOperator();
|
||||
replaceBinaryOperands();
|
||||
break;
|
||||
|
||||
case 'e':
|
||||
case 'n':
|
||||
case 'l':
|
||||
case 'L':
|
||||
case 'g':
|
||||
case 'G':
|
||||
|
||||
Generator::compare (mCode, op, getOperandType (1), getOperandType());
|
||||
popOperator();
|
||||
popOperand();
|
||||
popOperand();
|
||||
mOperands.push_back ('l');
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
throw std::logic_error ("unknown operator");
|
||||
}
|
||||
}
|
||||
|
||||
void ExprParser::pushIntegerLiteral (int value)
|
||||
{
|
||||
mNextOperand = false;
|
||||
mOperands.push_back ('l');
|
||||
Generator::pushInt (mCode, mLiterals, value);
|
||||
}
|
||||
|
||||
void ExprParser::pushFloatLiteral (float value)
|
||||
{
|
||||
mNextOperand = false;
|
||||
mOperands.push_back ('f');
|
||||
Generator::pushFloat (mCode, mLiterals, value);
|
||||
}
|
||||
|
||||
void ExprParser::pushBinaryOperator (char c)
|
||||
{
|
||||
while (!mOperators.empty() && getPriority (getOperator())>=getPriority (c))
|
||||
pop();
|
||||
|
||||
mOperators.push_back (c);
|
||||
mNextOperand = true;
|
||||
}
|
||||
|
||||
void ExprParser::close()
|
||||
{
|
||||
while (getOperator()!='(')
|
||||
pop();
|
||||
|
||||
popOperator();
|
||||
}
|
||||
|
||||
void ExprParser::parseArguments (const std::string& arguments, Scanner& scanner)
|
||||
{
|
||||
parseArguments (arguments, scanner, mCode);
|
||||
}
|
||||
|
||||
ExprParser::ExprParser (ErrorHandler& errorHandler, Context& context, Locals& locals,
|
||||
Literals& literals, bool argument)
|
||||
: Parser (errorHandler, context), mLocals (locals), mLiterals (literals),
|
||||
mNextOperand (true), mFirst (true), mArgument (argument)
|
||||
{}
|
||||
|
||||
bool ExprParser::parseInt (int value, const TokenLoc& loc, Scanner& scanner)
|
||||
{
|
||||
mFirst = false;
|
||||
|
||||
if (mNextOperand)
|
||||
{
|
||||
pushIntegerLiteral (value);
|
||||
mTokenLoc = loc;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// no comma was used between arguments
|
||||
scanner.putbackInt (value, loc);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool ExprParser::parseFloat (float value, const TokenLoc& loc, Scanner& scanner)
|
||||
{
|
||||
mFirst = false;
|
||||
|
||||
if (mNextOperand)
|
||||
{
|
||||
pushFloatLiteral (value);
|
||||
mTokenLoc = loc;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// no comma was used between arguments
|
||||
scanner.putbackFloat (value, loc);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool ExprParser::parseName (const std::string& name, const TokenLoc& loc,
|
||||
Scanner& scanner)
|
||||
{
|
||||
mFirst = false;
|
||||
|
||||
if (mNextOperand)
|
||||
{
|
||||
std::string name2 = toLower (name);
|
||||
|
||||
char type = mLocals.getType (name2);
|
||||
|
||||
if (type!=' ')
|
||||
{
|
||||
Generator::fetchLocal (mCode, type, mLocals.getIndex (name2));
|
||||
mNextOperand = false;
|
||||
mOperands.push_back (type=='f' ? 'f' : 'l');
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
type = getContext().getGlobalType (name2);
|
||||
|
||||
if (type!=' ')
|
||||
{
|
||||
Generator::fetchGlobal (mCode, mLiterals, type, name2);
|
||||
mNextOperand = false;
|
||||
mOperands.push_back (type=='f' ? 'f' : 'l');
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// no comma was used between arguments
|
||||
scanner.putbackName (name, loc);
|
||||
return false;
|
||||
}
|
||||
|
||||
return Parser::parseName (name, loc, scanner);
|
||||
}
|
||||
|
||||
bool ExprParser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner)
|
||||
{
|
||||
mFirst = false;
|
||||
|
||||
if (mNextOperand)
|
||||
{
|
||||
if (keyword==Scanner::K_getsquareroot)
|
||||
{
|
||||
mTokenLoc = loc;
|
||||
parseArguments ("f", scanner);
|
||||
|
||||
Generator::squareRoot (mCode);
|
||||
mOperands.push_back ('f');
|
||||
|
||||
mNextOperand = false;
|
||||
return true;
|
||||
}
|
||||
else if (keyword==Scanner::K_menumode)
|
||||
{
|
||||
mTokenLoc = loc;
|
||||
|
||||
Generator::menuMode (mCode);
|
||||
mOperands.push_back ('l');
|
||||
|
||||
mNextOperand = false;
|
||||
return true;
|
||||
}
|
||||
else if (keyword==Scanner::K_random)
|
||||
{
|
||||
mTokenLoc = loc;
|
||||
parseArguments ("l", scanner);
|
||||
|
||||
Generator::random (mCode);
|
||||
mOperands.push_back ('l');
|
||||
|
||||
mNextOperand = false;
|
||||
return true;
|
||||
}
|
||||
else if (keyword==Scanner::K_scriptrunning)
|
||||
{
|
||||
mTokenLoc = loc;
|
||||
parseArguments ("c", scanner);
|
||||
|
||||
Generator::scriptRunning (mCode);
|
||||
mOperands.push_back ('l');
|
||||
|
||||
mNextOperand = false;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// check for custom extensions
|
||||
if (const Extensions *extensions = getContext().getExtensions())
|
||||
{
|
||||
char returnType;
|
||||
std::string argumentType;
|
||||
|
||||
if (extensions->isFunction (keyword, returnType, argumentType))
|
||||
{
|
||||
mTokenLoc = loc;
|
||||
parseArguments (argumentType, scanner);
|
||||
|
||||
extensions->generateFunctionCode (keyword, mCode);
|
||||
mOperands.push_back (returnType);
|
||||
|
||||
mNextOperand = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// no comma was used between arguments
|
||||
scanner.putbackKeyword (keyword, loc);
|
||||
return false;
|
||||
}
|
||||
|
||||
return Parser::parseKeyword (keyword, loc, scanner);
|
||||
}
|
||||
|
||||
bool ExprParser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner)
|
||||
{
|
||||
if (code==Scanner::S_comma)
|
||||
{
|
||||
mTokenLoc = loc;
|
||||
|
||||
if (mFirst)
|
||||
{
|
||||
// leading comma
|
||||
mFirst = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// end marker
|
||||
scanner.putbackSpecial (code, loc);
|
||||
return false;
|
||||
}
|
||||
|
||||
mFirst = false;
|
||||
|
||||
if (code==Scanner::S_newline)
|
||||
{
|
||||
// end marker
|
||||
mTokenLoc = loc;
|
||||
scanner.putbackSpecial (code, loc);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (code==Scanner::S_minus && mNextOperand)
|
||||
{
|
||||
// unary
|
||||
mOperators.push_back ('m');
|
||||
mTokenLoc = loc;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (code==Scanner::S_open)
|
||||
{
|
||||
if (mNextOperand)
|
||||
{
|
||||
mOperators.push_back ('(');
|
||||
mTokenLoc = loc;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// no comma was used between arguments
|
||||
scanner.putbackKeyword (code, loc);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (code==Scanner::S_close && !mNextOperand)
|
||||
{
|
||||
if (isOpen())
|
||||
{
|
||||
close();
|
||||
return true;
|
||||
}
|
||||
|
||||
mTokenLoc = loc;
|
||||
scanner.putbackSpecial (code, loc);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!mNextOperand)
|
||||
{
|
||||
mTokenLoc = loc;
|
||||
char c = 0; // comparison
|
||||
|
||||
switch (code)
|
||||
{
|
||||
case Scanner::S_plus: pushBinaryOperator ('+'); return true;
|
||||
case Scanner::S_minus: pushBinaryOperator ('-'); return true;
|
||||
case Scanner::S_mult: pushBinaryOperator ('*'); return true;
|
||||
case Scanner::S_div: pushBinaryOperator ('/'); return true;
|
||||
case Scanner::S_cmpEQ: c = 'e'; break;
|
||||
case Scanner::S_cmpNE: c = 'n'; break;
|
||||
case Scanner::S_cmpLT: c = 'l'; break;
|
||||
case Scanner::S_cmpLE: c = 'L'; break;
|
||||
case Scanner::S_cmpGT: c = 'g'; break;
|
||||
case Scanner::S_cmpGE: c = 'G'; break;
|
||||
}
|
||||
|
||||
if (c)
|
||||
{
|
||||
if (mArgument && !isOpen())
|
||||
{
|
||||
// expression ends here
|
||||
// Thank you Morrowind for this rotten syntax :(
|
||||
scanner.putbackSpecial (code, loc);
|
||||
return false;
|
||||
}
|
||||
|
||||
pushBinaryOperator (c);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return Parser::parseSpecial (code, loc, scanner);
|
||||
}
|
||||
|
||||
void ExprParser::reset()
|
||||
{
|
||||
mOperands.clear();
|
||||
mOperators.clear();
|
||||
mNextOperand = true;
|
||||
mCode.clear();
|
||||
mFirst = true;
|
||||
}
|
||||
|
||||
char ExprParser::append (std::vector<Interpreter::Type_Code>& code)
|
||||
{
|
||||
if (mOperands.empty() && mOperators.empty())
|
||||
{
|
||||
getErrorHandler().error ("missing expression", mTokenLoc);
|
||||
return 'l';
|
||||
}
|
||||
|
||||
if (mNextOperand || mOperands.empty())
|
||||
{
|
||||
getErrorHandler().error ("syntax error in expression", mTokenLoc);
|
||||
return 'l';
|
||||
}
|
||||
|
||||
while (!mOperators.empty())
|
||||
pop();
|
||||
|
||||
std::copy (mCode.begin(), mCode.end(), std::back_inserter (code));
|
||||
|
||||
assert (mOperands.size()==1);
|
||||
return mOperands[0];
|
||||
}
|
||||
|
||||
void ExprParser::parseArguments (const std::string& arguments, Scanner& scanner,
|
||||
std::vector<Interpreter::Type_Code>& code, bool invert)
|
||||
{
|
||||
ExprParser parser (getErrorHandler(), getContext(), mLocals, mLiterals, true);
|
||||
StringParser stringParser (getErrorHandler(), getContext(), mLiterals);
|
||||
|
||||
std::stack<std::vector<Interpreter::Type_Code> > stack;
|
||||
|
||||
for (std::string::const_iterator iter (arguments.begin()); iter!=arguments.end();
|
||||
++iter)
|
||||
{
|
||||
if (*iter=='S' || *iter=='c')
|
||||
{
|
||||
stringParser.reset();
|
||||
if (*iter=='c') stringParser.smashCase();
|
||||
scanner.scan (stringParser);
|
||||
|
||||
if (invert)
|
||||
{
|
||||
std::vector<Interpreter::Type_Code> tmp;
|
||||
stringParser.append (tmp);
|
||||
|
||||
stack.push (tmp);
|
||||
}
|
||||
else
|
||||
stringParser.append (code);
|
||||
}
|
||||
else
|
||||
{
|
||||
parser.reset();
|
||||
scanner.scan (parser);
|
||||
|
||||
std::vector<Interpreter::Type_Code> tmp;
|
||||
|
||||
char type = parser.append (tmp);
|
||||
|
||||
if (type!=*iter)
|
||||
Generator::convert (tmp, type, *iter);
|
||||
|
||||
if (invert)
|
||||
stack.push (tmp);
|
||||
else
|
||||
std::copy (tmp.begin(), tmp.end(), std::back_inserter (code));
|
||||
}
|
||||
}
|
||||
|
||||
while (!stack.empty())
|
||||
{
|
||||
std::vector<Interpreter::Type_Code>& tmp = stack.top();
|
||||
|
||||
std::copy (tmp.begin(), tmp.end(), std::back_inserter (code));
|
||||
|
||||
stack.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,102 @@
|
||||
#ifndef COMPILER_EXPRPARSER_H_INCLUDED
|
||||
#define COMPILER_EXPRPARSER_H_INCLUDED
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <components/interpreter/types.hpp>
|
||||
|
||||
#include "parser.hpp"
|
||||
#include "tokenloc.hpp"
|
||||
|
||||
namespace Compiler
|
||||
{
|
||||
class Locals;
|
||||
class Literals;
|
||||
|
||||
class ExprParser : public Parser
|
||||
{
|
||||
Locals& mLocals;
|
||||
Literals& mLiterals;
|
||||
std::vector<char> mOperands;
|
||||
std::vector<char> mOperators;
|
||||
bool mNextOperand;
|
||||
TokenLoc mTokenLoc;
|
||||
std::vector<Interpreter::Type_Code> mCode;
|
||||
bool mFirst;
|
||||
bool mArgument;
|
||||
|
||||
int getPriority (char op) const;
|
||||
|
||||
char getOperandType (int Index = 0) const;
|
||||
|
||||
char getOperator() const;
|
||||
|
||||
bool isOpen() const;
|
||||
|
||||
void popOperator();
|
||||
|
||||
void popOperand();
|
||||
|
||||
void replaceBinaryOperands();
|
||||
|
||||
void pop();
|
||||
|
||||
void pushIntegerLiteral (int value);
|
||||
|
||||
void pushFloatLiteral (float value);
|
||||
|
||||
void pushBinaryOperator (char c);
|
||||
|
||||
void close();
|
||||
|
||||
void parseArguments (const std::string& arguments, Scanner& scanner);
|
||||
|
||||
public:
|
||||
|
||||
ExprParser (ErrorHandler& errorHandler, Context& context, Locals& locals,
|
||||
Literals& literals, bool argument = false);
|
||||
///< constructor
|
||||
/// \param argument Parser is used to parse function- or instruction-
|
||||
/// arguments (this influences the precedence rules).
|
||||
|
||||
char getType() const;
|
||||
///< Return type of parsed expression ('l' integer, 'f' float)
|
||||
|
||||
virtual bool parseInt (int value, const TokenLoc& loc, Scanner& scanner);
|
||||
///< Handle an int token.
|
||||
/// \return fetch another token?
|
||||
|
||||
virtual bool parseFloat (float value, const TokenLoc& loc, Scanner& scanner);
|
||||
///< Handle a float token.
|
||||
/// \return fetch another token?
|
||||
|
||||
virtual bool parseName (const std::string& name, const TokenLoc& loc,
|
||||
Scanner& scanner);
|
||||
///< Handle a name token.
|
||||
/// \return fetch another token?
|
||||
|
||||
virtual bool parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner);
|
||||
///< Handle a keyword token.
|
||||
/// \return fetch another token?
|
||||
|
||||
virtual bool parseSpecial (int code, const TokenLoc& loc, Scanner& scanner);
|
||||
///< Handle a special character token.
|
||||
/// \return fetch another token?
|
||||
|
||||
void reset();
|
||||
///< Reset parser to clean state.
|
||||
|
||||
char append (std::vector<Interpreter::Type_Code>& code);
|
||||
///< Generate code for parsed expression.
|
||||
/// \return Type ('l': integer, 'f': float)
|
||||
|
||||
void parseArguments (const std::string& arguments, Scanner& scanner,
|
||||
std::vector<Interpreter::Type_Code>& code, bool invert = false);
|
||||
///< Parse sequence of arguments specified by \a arguments.
|
||||
/// \param arguments Each character represents one arguments ('l': integer,
|
||||
/// 'f': float, 'S': string, 'c': string (case smashed))
|
||||
/// \param invert Store arguments in reverted order.
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,101 @@
|
||||
|
||||
#include "extensions.hpp"
|
||||
|
||||
#include <cassert>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "generator.hpp"
|
||||
|
||||
namespace Compiler
|
||||
{
|
||||
Extensions::Extensions() : mNextKeywordIndex (-1) {}
|
||||
|
||||
int Extensions::searchKeyword (const std::string& keyword) const
|
||||
{
|
||||
std::map<std::string, int>::const_iterator iter = mKeywords.find (keyword);
|
||||
|
||||
if (iter==mKeywords.end())
|
||||
return 0;
|
||||
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
bool Extensions::isFunction (int keyword, char& returnType, std::string& argumentType) const
|
||||
{
|
||||
std::map<int, Function>::const_iterator iter = mFunctions.find (keyword);
|
||||
|
||||
if (iter==mFunctions.end())
|
||||
return false;
|
||||
|
||||
returnType = iter->second.mReturn;
|
||||
argumentType = iter->second.mArguments;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Extensions::isInstruction (int keyword, std::string& argumentType) const
|
||||
{
|
||||
std::map<int, Instruction>::const_iterator iter = mInstructions.find (keyword);
|
||||
|
||||
if (iter==mInstructions.end())
|
||||
return false;
|
||||
|
||||
argumentType = iter->second.mArguments;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Extensions::registerFunction (const std::string& keyword, char returnType,
|
||||
const std::string& argumentType, int segment5code)
|
||||
{
|
||||
assert (segment5code>=33554432 && segment5code<=67108863);
|
||||
|
||||
int code = mNextKeywordIndex--;
|
||||
|
||||
mKeywords.insert (std::make_pair (keyword, code));
|
||||
|
||||
Function function;
|
||||
function.mReturn = returnType;
|
||||
function.mArguments = argumentType;
|
||||
function.mCode = segment5code;
|
||||
|
||||
mFunctions.insert (std::make_pair (code, function));
|
||||
}
|
||||
|
||||
void Extensions::registerInstruction (const std::string& keyword,
|
||||
const std::string& argumentType, int segment5code)
|
||||
{
|
||||
assert (segment5code>=33554432 && segment5code<=67108863);
|
||||
|
||||
int code = mNextKeywordIndex--;
|
||||
|
||||
mKeywords.insert (std::make_pair (keyword, code));
|
||||
|
||||
Instruction instruction;
|
||||
instruction.mArguments = argumentType;
|
||||
instruction.mCode = segment5code;
|
||||
|
||||
mInstructions.insert (std::make_pair (code, instruction));
|
||||
}
|
||||
|
||||
void Extensions::generateFunctionCode (int keyword, std::vector<Interpreter::Type_Code>& code)
|
||||
const
|
||||
{
|
||||
std::map<int, Function>::const_iterator iter = mFunctions.find (keyword);
|
||||
|
||||
if (iter==mFunctions.end())
|
||||
throw std::logic_error ("unknown custom function keyword");
|
||||
|
||||
code.push_back (Generator::segment5 (iter->second.mCode));
|
||||
}
|
||||
|
||||
void Extensions::generateInstructionCode (int keyword,
|
||||
std::vector<Interpreter::Type_Code>& code)
|
||||
const
|
||||
{
|
||||
std::map<int, Instruction>::const_iterator iter = mInstructions.find (keyword);
|
||||
|
||||
if (iter==mInstructions.end())
|
||||
throw std::logic_error ("unknown custom instruction keyword");
|
||||
|
||||
code.push_back (Generator::segment5 (iter->second.mCode));
|
||||
}
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
#ifndef COMPILER_EXTENSIONS_H_INCLUDED
|
||||
#define COMPILER_EXTENSINOS_H_INCLUDED
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include <components/interpreter/types.hpp>
|
||||
|
||||
namespace Compiler
|
||||
{
|
||||
/// \brief Collection of compiler extensions
|
||||
|
||||
class Extensions
|
||||
{
|
||||
struct Function
|
||||
{
|
||||
char mReturn;
|
||||
std::string mArguments;
|
||||
int mCode;
|
||||
};
|
||||
|
||||
struct Instruction
|
||||
{
|
||||
std::string mArguments;
|
||||
int mCode;
|
||||
};
|
||||
|
||||
int mNextKeywordIndex;
|
||||
std::map<std::string, int> mKeywords;
|
||||
std::map<int, Function> mFunctions;
|
||||
std::map<int, Instruction> mInstructions;
|
||||
|
||||
public:
|
||||
|
||||
Extensions();
|
||||
|
||||
int searchKeyword (const std::string& keyword) const;
|
||||
///< Return extension keyword code, that is assigned to the string \a keyword.
|
||||
/// - if no match is found 0 is returned.
|
||||
/// - keyword must be all lower case.
|
||||
|
||||
bool isFunction (int keyword, char& returnType, std::string& argumentType) const;
|
||||
///< Is this keyword registered with a function? If yes, return return and argument
|
||||
/// types.
|
||||
|
||||
bool isInstruction (int keyword, std::string& argumentType) const;
|
||||
///< Is this keyword registered with a function? If yes, return argument types.
|
||||
|
||||
void registerFunction (const std::string& keyword, char returnType,
|
||||
const std::string& argumentType, int segment5code);
|
||||
///< Register a custom function
|
||||
/// - keyword must be all lower case.
|
||||
/// - keyword must be unique
|
||||
/// \note Currently only segment 5 opcodes are supported.
|
||||
|
||||
void registerInstruction (const std::string& keyword,
|
||||
const std::string& argumentType, int segment5code);
|
||||
///< Register a custom instruction
|
||||
/// - keyword must be all lower case.
|
||||
/// - keyword must be unique
|
||||
/// \note Currently only segment 5 opcodes are supported.
|
||||
|
||||
void generateFunctionCode (int keyword, std::vector<Interpreter::Type_Code>& code)
|
||||
const;
|
||||
///< Append code for function to \a code.
|
||||
|
||||
void generateInstructionCode (int keyword, std::vector<Interpreter::Type_Code>& code)
|
||||
const;
|
||||
///< Append code for function to \a code.
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -0,0 +1,103 @@
|
||||
#include "fileparser.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "tokenloc.hpp"
|
||||
#include "scanner.hpp"
|
||||
|
||||
namespace Compiler
|
||||
{
|
||||
FileParser::FileParser (ErrorHandler& errorHandler, Context& context)
|
||||
: Parser (errorHandler, context),
|
||||
mScriptParser (errorHandler, context, mLocals, true),
|
||||
mState (BeginState)
|
||||
{}
|
||||
|
||||
std::string FileParser::getName() const
|
||||
{
|
||||
return mName;
|
||||
}
|
||||
|
||||
void FileParser::getCode (std::vector<Interpreter::Type_Code>& code) const
|
||||
{
|
||||
mScriptParser.getCode (code);
|
||||
}
|
||||
|
||||
const Locals& FileParser::getLocals() const
|
||||
{
|
||||
return mLocals;
|
||||
}
|
||||
|
||||
bool FileParser::parseName (const std::string& name, const TokenLoc& loc,
|
||||
Scanner& scanner)
|
||||
{
|
||||
if (mState==NameState)
|
||||
{
|
||||
mName = name;
|
||||
mState = BeginCompleteState;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (mState==EndNameState)
|
||||
{
|
||||
// optional repeated name after end statement
|
||||
if (mName!=name)
|
||||
reportWarning ("Names for script " + mName + " do not match", loc);
|
||||
|
||||
mState = EndCompleteState;
|
||||
return true;
|
||||
}
|
||||
|
||||
return Parser::parseName (name, loc, scanner);
|
||||
}
|
||||
|
||||
bool FileParser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner)
|
||||
{
|
||||
if (mState==BeginState && keyword==Scanner::K_begin)
|
||||
{
|
||||
mState = NameState;
|
||||
return true;
|
||||
}
|
||||
|
||||
return Parser::parseKeyword (keyword, loc, scanner);
|
||||
}
|
||||
|
||||
bool FileParser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner)
|
||||
{
|
||||
if (code==Scanner::S_newline)
|
||||
{
|
||||
if (mState==BeginCompleteState)
|
||||
{
|
||||
// parse the script body
|
||||
mScriptParser.reset();
|
||||
|
||||
scanner.scan (mScriptParser);
|
||||
|
||||
mState = EndNameState;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (mState==EndCompleteState || mState==EndNameState)
|
||||
{
|
||||
// we are done here -> ignore the rest of the script
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return Parser::parseSpecial (code, loc, scanner);
|
||||
}
|
||||
|
||||
void FileParser::parseEOF (Scanner& scanner)
|
||||
{
|
||||
if (mState!=EndNameState && mState!=EndCompleteState)
|
||||
Parser::parseEOF (scanner);
|
||||
}
|
||||
|
||||
void FileParser::reset()
|
||||
{
|
||||
mState = BeginState;
|
||||
mName.clear();
|
||||
mScriptParser.reset();
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,60 @@
|
||||
#ifndef COMPILER_FILEPARSER_H_INCLUDED
|
||||
#define COMPILER_FILEPARSER_H_INCLUDED
|
||||
|
||||
#include "parser.hpp"
|
||||
#include "scriptparser.hpp"
|
||||
#include "locals.hpp"
|
||||
#include "literals.hpp"
|
||||
|
||||
namespace Compiler
|
||||
{
|
||||
// Top-level parser, to be used for global scripts, local scripts and targeted scripts
|
||||
|
||||
class FileParser : public Parser
|
||||
{
|
||||
enum State
|
||||
{
|
||||
BeginState, NameState, BeginCompleteState, EndNameState,
|
||||
EndCompleteState
|
||||
};
|
||||
|
||||
ScriptParser mScriptParser;
|
||||
State mState;
|
||||
std::string mName;
|
||||
Locals mLocals;
|
||||
|
||||
public:
|
||||
|
||||
FileParser (ErrorHandler& errorHandler, Context& context);
|
||||
|
||||
std::string getName() const;
|
||||
///< Return script name.
|
||||
|
||||
void getCode (std::vector<Interpreter::Type_Code>& code) const;
|
||||
///< store generated code in \æ code.
|
||||
|
||||
const Locals& getLocals() const;
|
||||
///< get local variable declarations.
|
||||
|
||||
virtual bool parseName (const std::string& name, const TokenLoc& loc,
|
||||
Scanner& scanner);
|
||||
///< Handle a name token.
|
||||
/// \return fetch another token?
|
||||
|
||||
virtual bool parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner);
|
||||
///< Handle a keyword token.
|
||||
/// \return fetch another token?
|
||||
|
||||
virtual bool parseSpecial (int code, const TokenLoc& loc, Scanner& scanner);
|
||||
///< Handle a special character token.
|
||||
/// \return fetch another token?
|
||||
|
||||
virtual void parseEOF (Scanner& scanner);
|
||||
///< Handle EOF token.
|
||||
|
||||
void reset();
|
||||
///< Reset parser to clean state.
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,670 @@
|
||||
|
||||
#include "generator.hpp"
|
||||
|
||||
#include <cassert>
|
||||
#include <algorithm>
|
||||
#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 opFloatToInt1 (Compiler::Generator::CodeContainer& code)
|
||||
{
|
||||
code.push_back (Compiler::Generator::segment5 (18));
|
||||
}
|
||||
|
||||
void opSquareRoot (Compiler::Generator::CodeContainer& code)
|
||||
{
|
||||
code.push_back (Compiler::Generator::segment5 (19));
|
||||
}
|
||||
|
||||
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 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));
|
||||
}
|
||||
|
||||
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 opMenuMode (Compiler::Generator::CodeContainer& code)
|
||||
{
|
||||
code.push_back (Compiler::Generator::segment5 (38));
|
||||
}
|
||||
|
||||
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 opRandom (Compiler::Generator::CodeContainer& code)
|
||||
{
|
||||
code.push_back (Compiler::Generator::segment5 (45));
|
||||
}
|
||||
|
||||
void opScriptRunning (Compiler::Generator::CodeContainer& code)
|
||||
{
|
||||
code.push_back (Compiler::Generator::segment5 (46));
|
||||
}
|
||||
|
||||
void opStartScript (Compiler::Generator::CodeContainer& code)
|
||||
{
|
||||
code.push_back (Compiler::Generator::segment5 (47));
|
||||
}
|
||||
|
||||
void opStopScript (Compiler::Generator::CodeContainer& code)
|
||||
{
|
||||
code.push_back (Compiler::Generator::segment5 (48));
|
||||
}
|
||||
}
|
||||
|
||||
namespace Compiler
|
||||
{
|
||||
namespace 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 squareRoot (CodeContainer& code)
|
||||
{
|
||||
opSquareRoot (code);
|
||||
}
|
||||
|
||||
void exit (CodeContainer& code)
|
||||
{
|
||||
opReturn (code);
|
||||
}
|
||||
|
||||
void message (CodeContainer& code, Literals& literals, const std::string& message,
|
||||
int buttons)
|
||||
{
|
||||
assert (buttons==0);
|
||||
|
||||
int index = literals.addString (message);
|
||||
|
||||
opPushInt (code, index);
|
||||
opMessageBox (code, buttons);
|
||||
}
|
||||
|
||||
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 ("inifite loop");
|
||||
}
|
||||
|
||||
void jumpOnZero (CodeContainer& code, int offset)
|
||||
{
|
||||
opSkipOnNonZero (code);
|
||||
|
||||
if (offset<0)
|
||||
--offset; // compensate for skip instruction
|
||||
|
||||
jump (code, offset);
|
||||
}
|
||||
|
||||
void jumpOnNonZero (CodeContainer& code, int offset)
|
||||
{
|
||||
opSkipOnZero (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 menuMode (CodeContainer& code)
|
||||
{
|
||||
opMenuMode (code);
|
||||
}
|
||||
|
||||
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')
|
||||
{
|
||||
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 random (CodeContainer& code)
|
||||
{
|
||||
opRandom (code);
|
||||
}
|
||||
|
||||
void scriptRunning (CodeContainer& code)
|
||||
{
|
||||
opScriptRunning (code);
|
||||
}
|
||||
|
||||
void startScript (CodeContainer& code)
|
||||
{
|
||||
opStartScript (code);
|
||||
}
|
||||
|
||||
void stopScript (CodeContainer& code)
|
||||
{
|
||||
opStopScript (code);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,113 @@
|
||||
#ifndef COMPILER_GENERATOR_H_INCLUDED
|
||||
#define COMPILER_GENERATOR_H_INCLUDED
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <cassert>
|
||||
|
||||
#include <components/interpreter/types.hpp>
|
||||
|
||||
namespace Compiler
|
||||
{
|
||||
class Literals;
|
||||
|
||||
namespace Generator
|
||||
{
|
||||
typedef std::vector<Interpreter::Type_Code> CodeContainer;
|
||||
|
||||
inline Interpreter::Type_Code segment0 (unsigned int c, unsigned int arg0)
|
||||
{
|
||||
assert (c<64);
|
||||
return (c<<24) | (arg0 & 0xffffff);
|
||||
}
|
||||
|
||||
inline Interpreter::Type_Code segment1 (unsigned int c, unsigned int arg0,
|
||||
unsigned int arg1)
|
||||
{
|
||||
assert (c<64);
|
||||
return 0x40000000 | (c<<24) | ((arg0 & 0xfff)<<12) | (arg1 & 0xfff);
|
||||
}
|
||||
|
||||
inline Interpreter::Type_Code segment2 (unsigned int c, unsigned int arg0)
|
||||
{
|
||||
assert (c<1024);
|
||||
return 0x80000000 | (c<<20) | (arg0 & 0xfffff);
|
||||
}
|
||||
|
||||
inline Interpreter::Type_Code segment3 (unsigned int c, unsigned int arg0)
|
||||
{
|
||||
assert (c<1024);
|
||||
return 0xc0000000 | (c<<20) | (arg0 & 0xffff);
|
||||
}
|
||||
|
||||
inline Interpreter::Type_Code segment4 (unsigned int c, unsigned int arg0,
|
||||
unsigned int arg1)
|
||||
{
|
||||
assert (c<1024);
|
||||
return 0xc4000000 | (c<<16) | ((arg0 & 0xff)<<8) | (arg1 & 0xff);
|
||||
}
|
||||
|
||||
inline Interpreter::Type_Code segment5 (unsigned int c)
|
||||
{
|
||||
assert (c<67108864);
|
||||
return 0xc8000000 | c;
|
||||
}
|
||||
|
||||
void pushInt (CodeContainer& code, Literals& literals, int value);
|
||||
|
||||
void pushFloat (CodeContainer& code, Literals& literals, float value);
|
||||
|
||||
void pushString (CodeContainer& code, Literals& literals, const std::string& value);
|
||||
|
||||
void assignToLocal (CodeContainer& code, char localType,
|
||||
int localIndex, const CodeContainer& value, char valueType);
|
||||
|
||||
void negate (CodeContainer& code, char valueType);
|
||||
|
||||
void add (CodeContainer& code, char valueType1, char valueType2);
|
||||
|
||||
void sub (CodeContainer& code, char valueType1, char valueType2);
|
||||
|
||||
void mul (CodeContainer& code, char valueType1, char valueType2);
|
||||
|
||||
void div (CodeContainer& code, char valueType1, char valueType2);
|
||||
|
||||
void convert (CodeContainer& code, char fromType, char toType);
|
||||
|
||||
void squareRoot (CodeContainer& code);
|
||||
|
||||
void exit (CodeContainer& code);
|
||||
|
||||
void message (CodeContainer& code, Literals& literals, const std::string& message,
|
||||
int buttons);
|
||||
|
||||
void fetchLocal (CodeContainer& code, char localType, int localIndex);
|
||||
|
||||
void jump (CodeContainer& code, int offset);
|
||||
|
||||
void jumpOnZero (CodeContainer& code, int offset);
|
||||
|
||||
void jumpOnNonZero (CodeContainer& code, int offset);
|
||||
|
||||
void compare (CodeContainer& code, char op, char valueType1, char valueType2);
|
||||
|
||||
void menuMode (CodeContainer& code);
|
||||
|
||||
void assignToGlobal (CodeContainer& code, Literals& literals, char localType,
|
||||
const std::string& name, const CodeContainer& value, char valueType);
|
||||
|
||||
void fetchGlobal (CodeContainer& code, Literals& literals, char localType,
|
||||
const std::string& name);
|
||||
|
||||
void random (CodeContainer& code);
|
||||
|
||||
void scriptRunning (CodeContainer& code);
|
||||
|
||||
void startScript (CodeContainer& code);
|
||||
|
||||
void stopScript (CodeContainer& code);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -0,0 +1,230 @@
|
||||
|
||||
#include "lineparser.hpp"
|
||||
|
||||
#include "scanner.hpp"
|
||||
#include "context.hpp"
|
||||
#include "errorhandler.hpp"
|
||||
#include "skipparser.hpp"
|
||||
#include "locals.hpp"
|
||||
#include "generator.hpp"
|
||||
#include "extensions.hpp"
|
||||
|
||||
namespace Compiler
|
||||
{
|
||||
LineParser::LineParser (ErrorHandler& errorHandler, Context& context, Locals& locals,
|
||||
Literals& literals, std::vector<Interpreter::Type_Code>& code)
|
||||
: Parser (errorHandler, context), mLocals (locals), mLiterals (literals), mCode (code),
|
||||
mState (BeginState), mExprParser (errorHandler, context, locals, literals)
|
||||
{}
|
||||
|
||||
bool LineParser::parseInt (int value, const TokenLoc& loc, Scanner& scanner)
|
||||
{
|
||||
return Parser::parseInt (value, loc, scanner);
|
||||
}
|
||||
|
||||
bool LineParser::parseFloat (float value, const TokenLoc& loc, Scanner& scanner)
|
||||
{
|
||||
return Parser::parseFloat (value, loc, scanner);
|
||||
}
|
||||
|
||||
bool LineParser::parseName (const std::string& name, const TokenLoc& loc,
|
||||
Scanner& scanner)
|
||||
{
|
||||
if (mState==ShortState || mState==LongState || mState==FloatState)
|
||||
{
|
||||
if (!getContext().canDeclareLocals())
|
||||
{
|
||||
getErrorHandler().error ("local variables can't be declared in this context", loc);
|
||||
SkipParser skip (getErrorHandler(), getContext());
|
||||
scanner.scan (skip);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string name2 = toLower (name);
|
||||
|
||||
char type = mLocals.getType (name2);
|
||||
|
||||
if (type!=' ')
|
||||
{
|
||||
getErrorHandler().error ("can't re-declare local variable", loc);
|
||||
SkipParser skip (getErrorHandler(), getContext());
|
||||
scanner.scan (skip);
|
||||
return false;
|
||||
}
|
||||
|
||||
mLocals.declare (mState==ShortState ? 's' : (mState==LongState ? 'l' : 'f'),
|
||||
name2);
|
||||
|
||||
mState = EndState;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (mState==SetState)
|
||||
{
|
||||
std::string name2 = toLower (name);
|
||||
|
||||
// local variable?
|
||||
char type = mLocals.getType (name2);
|
||||
if (type!=' ')
|
||||
{
|
||||
mName = name2;
|
||||
mState = SetLocalVarState;
|
||||
return true;
|
||||
}
|
||||
|
||||
type = getContext().getGlobalType (name2);
|
||||
if (type!=' ')
|
||||
{
|
||||
mName = name2;
|
||||
mType = type;
|
||||
mState = SetGlobalVarState;
|
||||
return true;
|
||||
}
|
||||
|
||||
getErrorHandler().error ("unknown variable", loc);
|
||||
SkipParser skip (getErrorHandler(), getContext());
|
||||
scanner.scan (skip);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mState==MessageState || mState==MessageCommaState)
|
||||
{
|
||||
std::string arguments;
|
||||
|
||||
for (std::size_t i=0; i<name.size(); ++i)
|
||||
{
|
||||
if (name[i]=='%')
|
||||
{
|
||||
++i;
|
||||
if (i<name.size())
|
||||
{
|
||||
if (name[i]=='G' || name[i]=='g')
|
||||
{
|
||||
arguments += "l";
|
||||
}
|
||||
else if (name[i]=='S' || name[i]=='s')
|
||||
{
|
||||
arguments += 'S';
|
||||
}
|
||||
else if (name[i]=='.' || name[i]=='f')
|
||||
{
|
||||
arguments += 'f';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!arguments.empty())
|
||||
{
|
||||
mExprParser.reset();
|
||||
mExprParser.parseArguments (arguments, scanner, mCode, true);
|
||||
}
|
||||
|
||||
Generator::message (mCode, mLiterals, name, 0);
|
||||
mState = EndState;
|
||||
return false;
|
||||
}
|
||||
|
||||
return Parser::parseName (name, loc, scanner);
|
||||
}
|
||||
|
||||
bool LineParser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner)
|
||||
{
|
||||
if (mState==BeginState)
|
||||
{
|
||||
switch (keyword)
|
||||
{
|
||||
case Scanner::K_short: mState = ShortState; return true;
|
||||
case Scanner::K_long: mState = LongState; return true;
|
||||
case Scanner::K_float: mState = FloatState; return true;
|
||||
case Scanner::K_set: mState = SetState; return true;
|
||||
case Scanner::K_messagebox: mState = MessageState; return true;
|
||||
|
||||
case Scanner::K_return:
|
||||
|
||||
Generator::exit (mCode);
|
||||
mState = EndState;
|
||||
return true;
|
||||
|
||||
case Scanner::K_startscript:
|
||||
|
||||
mExprParser.parseArguments ("c", scanner, mCode, true);
|
||||
Generator::startScript (mCode);
|
||||
mState = EndState;
|
||||
return true;
|
||||
|
||||
case Scanner::K_stopscript:
|
||||
|
||||
mExprParser.parseArguments ("c", scanner, mCode, true);
|
||||
Generator::stopScript (mCode);
|
||||
mState = EndState;
|
||||
return true;
|
||||
}
|
||||
|
||||
// check for custom extensions
|
||||
if (const Extensions *extensions = getContext().getExtensions())
|
||||
{
|
||||
std::string argumentType;
|
||||
|
||||
if (extensions->isInstruction (keyword, argumentType))
|
||||
{
|
||||
mExprParser.parseArguments (argumentType, scanner, mCode, true);
|
||||
|
||||
extensions->generateInstructionCode (keyword, mCode);
|
||||
mState = EndState;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (mState==SetLocalVarState && keyword==Scanner::K_to)
|
||||
{
|
||||
mExprParser.reset();
|
||||
scanner.scan (mExprParser);
|
||||
|
||||
std::vector<Interpreter::Type_Code> code;
|
||||
char type = mExprParser.append (code);
|
||||
|
||||
Generator::assignToLocal (mCode, mLocals.getType (mName),
|
||||
mLocals.getIndex (mName), code, type);
|
||||
|
||||
mState = EndState;
|
||||
return true;
|
||||
}
|
||||
else if (mState==SetGlobalVarState && keyword==Scanner::K_to)
|
||||
{
|
||||
mExprParser.reset();
|
||||
scanner.scan (mExprParser);
|
||||
|
||||
std::vector<Interpreter::Type_Code> code;
|
||||
char type = mExprParser.append (code);
|
||||
|
||||
Generator::assignToGlobal (mCode, mLiterals, mType, mName, code, type);
|
||||
|
||||
mState = EndState;
|
||||
return true;
|
||||
}
|
||||
|
||||
return Parser::parseKeyword (keyword, loc, scanner);
|
||||
}
|
||||
|
||||
bool LineParser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner)
|
||||
{
|
||||
if (code==Scanner::S_newline && mState==EndState)
|
||||
return false;
|
||||
|
||||
if (code==Scanner::S_comma && mState==MessageState)
|
||||
{
|
||||
mState = MessageCommaState;
|
||||
return true;
|
||||
}
|
||||
|
||||
return Parser::parseSpecial (code, loc, scanner);
|
||||
}
|
||||
|
||||
void LineParser::reset()
|
||||
{
|
||||
mState = BeginState;
|
||||
mName.clear();
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,68 @@
|
||||
#ifndef COMPILER_LINEPARSER_H_INCLUDED
|
||||
#define COMPILER_LINEPARSER_H_INCLUDED
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <components/interpreter/types.hpp>
|
||||
|
||||
#include "parser.hpp"
|
||||
#include "exprparser.hpp"
|
||||
|
||||
namespace Compiler
|
||||
{
|
||||
class Locals;
|
||||
class Literals;
|
||||
|
||||
/// \brief Line parser, to be used in console scripts and as part of ScriptParser
|
||||
|
||||
class LineParser : public Parser
|
||||
{
|
||||
enum State
|
||||
{
|
||||
BeginState,
|
||||
ShortState, LongState, FloatState,
|
||||
SetState, SetLocalVarState, SetGlobalVarState,
|
||||
MessageState, MessageCommaState,
|
||||
EndState
|
||||
};
|
||||
|
||||
Locals& mLocals;
|
||||
Literals& mLiterals;
|
||||
std::vector<Interpreter::Type_Code>& mCode;
|
||||
State mState;
|
||||
std::string mName;
|
||||
char mType;
|
||||
ExprParser mExprParser;
|
||||
|
||||
public:
|
||||
|
||||
LineParser (ErrorHandler& errorHandler, Context& context, Locals& locals,
|
||||
Literals& literals, std::vector<Interpreter::Type_Code>& code);
|
||||
|
||||
virtual bool parseInt (int value, const TokenLoc& loc, Scanner& scanner);
|
||||
///< Handle an int token.
|
||||
/// \return fetch another token?
|
||||
|
||||
virtual bool parseFloat (float value, const TokenLoc& loc, Scanner& scanner);
|
||||
///< Handle a float token.
|
||||
/// \return fetch another token?
|
||||
|
||||
virtual bool parseName (const std::string& name, const TokenLoc& loc,
|
||||
Scanner& scanner);
|
||||
///< Handle a name token.
|
||||
/// \return fetch another token?
|
||||
|
||||
virtual bool parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner);
|
||||
///< Handle a keyword token.
|
||||
/// \return fetch another token?
|
||||
|
||||
virtual bool parseSpecial (int code, const TokenLoc& loc, Scanner& scanner);
|
||||
///< Handle a special character token.
|
||||
/// \return fetch another token?
|
||||
|
||||
void reset();
|
||||
///< Reset parser to clean state.
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,94 @@
|
||||
|
||||
#include "literals.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace Compiler
|
||||
{
|
||||
int Literals::getIntegerSize() const
|
||||
{
|
||||
return mIntegers.size() * sizeof (Interpreter::Type_Integer);
|
||||
}
|
||||
|
||||
int Literals::getFloatSize() const
|
||||
{
|
||||
return mFloats.size() * sizeof (Interpreter::Type_Float);
|
||||
}
|
||||
|
||||
int Literals::getStringSize() const
|
||||
{
|
||||
int size = 0;
|
||||
|
||||
for (std::vector<std::string>::const_iterator iter (mStrings.begin());
|
||||
iter!=mStrings.end(); ++iter)
|
||||
size += static_cast<int> (iter->size()) + 1;
|
||||
|
||||
if (size % 4) // padding
|
||||
size += 4 - size % 4;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
void Literals::append (std::vector<Interpreter::Type_Code>& code) const
|
||||
{
|
||||
for (std::vector<Interpreter::Type_Integer>::const_iterator iter (mIntegers.begin());
|
||||
iter!=mIntegers.end(); ++iter)
|
||||
code.push_back (*reinterpret_cast<const Interpreter::Type_Code *> (&*iter));
|
||||
|
||||
for (std::vector<Interpreter::Type_Float>::const_iterator iter (mFloats.begin());
|
||||
iter!=mFloats.end(); ++iter)
|
||||
code.push_back (*reinterpret_cast<const Interpreter::Type_Code *> (&*iter));
|
||||
|
||||
int stringBlockSize = getStringSize();
|
||||
int size = static_cast<int> (code.size());
|
||||
|
||||
code.resize (size+stringBlockSize/4);
|
||||
|
||||
int offset = 0;
|
||||
|
||||
for (std::vector<std::string>::const_iterator iter (mStrings.begin());
|
||||
iter!=mStrings.end(); ++iter)
|
||||
{
|
||||
int stringSize = iter->size()+1;
|
||||
|
||||
std::copy (iter->c_str(), iter->c_str()+stringSize,
|
||||
reinterpret_cast<char *> (&code[size]) + offset);
|
||||
offset += stringSize;
|
||||
}
|
||||
}
|
||||
|
||||
int Literals::addInteger (Interpreter::Type_Integer value)
|
||||
{
|
||||
int index = static_cast<int> (mIntegers.size());
|
||||
|
||||
mIntegers.push_back (value);
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
int Literals::addFloat (Interpreter::Type_Float value)
|
||||
{
|
||||
int index = static_cast<int> (mFloats.size());
|
||||
|
||||
mFloats.push_back (value);
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
int Literals::addString (const std::string& value)
|
||||
{
|
||||
int index = static_cast<int> (mStrings.size());
|
||||
|
||||
mStrings.push_back (value);
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
void Literals::clear()
|
||||
{
|
||||
mIntegers.clear();
|
||||
mFloats.clear();
|
||||
mStrings.clear();
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,49 @@
|
||||
#ifndef COMPILER_LITERALS_H_INCLUDED
|
||||
#define COMPILER_LITERALS_H_INCLUDED
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <components/interpreter/types.hpp>
|
||||
|
||||
namespace Compiler
|
||||
{
|
||||
/// \brief Literal values.
|
||||
|
||||
class Literals
|
||||
{
|
||||
std::vector<Interpreter::Type_Integer> mIntegers;
|
||||
std::vector<Interpreter::Type_Float> mFloats;
|
||||
std::vector<std::string> mStrings;
|
||||
|
||||
public:
|
||||
|
||||
int getIntegerSize() const;
|
||||
///< Return size of integer block (in bytes).
|
||||
|
||||
int getFloatSize() const;
|
||||
///< Return size of float block (in bytes).
|
||||
|
||||
int getStringSize() const;
|
||||
///< Return size of string block (in bytes).
|
||||
|
||||
void append (std::vector<Interpreter::Type_Code>& code) const;
|
||||
///< Apepnd literal blocks to code.
|
||||
/// \note code blocks will be padded for 32-bit alignment.
|
||||
|
||||
int addInteger (Interpreter::Type_Integer value);
|
||||
///< add integer liternal and return index.
|
||||
|
||||
int addFloat (Interpreter::Type_Float value);
|
||||
///< add float literal and return value.
|
||||
|
||||
int addString (const std::string& value);
|
||||
///< add string literal and return value.
|
||||
|
||||
void clear();
|
||||
///< remove all literals.
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -0,0 +1,110 @@
|
||||
|
||||
#include "locals.hpp"
|
||||
|
||||
#include <cassert>
|
||||
#include <stdexcept>
|
||||
#include <algorithm>
|
||||
#include <ostream>
|
||||
#include <iterator>
|
||||
|
||||
namespace Compiler
|
||||
{
|
||||
const std::vector<std::string>& Locals::get (char type) const
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case 's': return mShorts;
|
||||
case 'l': return mLongs;
|
||||
case 'f': return mFloats;
|
||||
}
|
||||
|
||||
throw std::logic_error ("unknown variable type");
|
||||
}
|
||||
|
||||
int Locals::searchIndex (char type, const std::string& name) const
|
||||
{
|
||||
const std::vector<std::string>& collection = get (type);
|
||||
|
||||
std::vector<std::string>::const_iterator iter =
|
||||
std::find (collection.begin(), collection.end(), name);
|
||||
|
||||
if (iter==collection.end())
|
||||
return -1;
|
||||
|
||||
return iter-collection.begin();
|
||||
}
|
||||
|
||||
bool Locals::search (char type, const std::string& name) const
|
||||
{
|
||||
return searchIndex (type, name)!=-1;
|
||||
}
|
||||
|
||||
std::vector<std::string>& Locals::get (char type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case 's': return mShorts;
|
||||
case 'l': return mLongs;
|
||||
case 'f': return mFloats;
|
||||
}
|
||||
|
||||
throw std::logic_error ("unknown variable type");
|
||||
}
|
||||
|
||||
char Locals::getType (const std::string& name) const
|
||||
{
|
||||
if (search ('s', name))
|
||||
return 's';
|
||||
|
||||
if (search ('l', name))
|
||||
return 'l';
|
||||
|
||||
if (search ('f', name))
|
||||
return 'f';
|
||||
|
||||
return ' ';
|
||||
}
|
||||
|
||||
int Locals::getIndex (const std::string& name) const
|
||||
{
|
||||
int index = searchIndex ('s', name);
|
||||
|
||||
if (index!=-1)
|
||||
return index;
|
||||
|
||||
index = searchIndex ('l', name);
|
||||
|
||||
if (index!=-1)
|
||||
return index;
|
||||
|
||||
return searchIndex ('f', name);
|
||||
}
|
||||
|
||||
void Locals::write (std::ostream& localFile) const
|
||||
{
|
||||
localFile
|
||||
<< get ('s').size() << ' '
|
||||
<< get ('l').size() << ' '
|
||||
<< get ('f').size() << std::endl;
|
||||
|
||||
std::copy (get ('s').begin(), get ('s').end(),
|
||||
std::ostream_iterator<std::string> (localFile, " "));
|
||||
std::copy (get ('l').begin(), get ('l').end(),
|
||||
std::ostream_iterator<std::string> (localFile, " "));
|
||||
std::copy (get ('f').begin(), get ('f').end(),
|
||||
std::ostream_iterator<std::string> (localFile, " "));
|
||||
}
|
||||
|
||||
void Locals::declare (char type, const std::string& name)
|
||||
{
|
||||
get (type).push_back (name);
|
||||
}
|
||||
|
||||
void Locals::clear()
|
||||
{
|
||||
get ('s').clear();
|
||||
get ('l').clear();
|
||||
get ('f').clear();
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,45 @@
|
||||
#ifndef COMPILER_LOCALS_H_INCLUDED
|
||||
#define COMPILER_LOCALS_H_INCLUDED
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <iosfwd>
|
||||
|
||||
namespace Compiler
|
||||
{
|
||||
/// \brief Local variable declarations
|
||||
|
||||
class Locals
|
||||
{
|
||||
std::vector<std::string> mShorts;
|
||||
std::vector<std::string> mLongs;
|
||||
std::vector<std::string> mFloats;
|
||||
|
||||
const std::vector<std::string>& get (char type) const;
|
||||
|
||||
int searchIndex (char type, const std::string& name) const;
|
||||
|
||||
bool search (char type, const std::string& name) const;
|
||||
|
||||
std::vector<std::string>& get (char type);
|
||||
|
||||
public:
|
||||
|
||||
char getType (const std::string& name) const;
|
||||
///< 's': short, 'l': long, 'f': float, ' ': does not exist.
|
||||
|
||||
int getIndex (const std::string& name) const;
|
||||
///< return index for local variable \a name (-1: does not exist).
|
||||
|
||||
void write (std::ostream& localFile) const;
|
||||
///< write declarations to file.
|
||||
|
||||
void declare (char type, const std::string& name);
|
||||
///< declares a variable.
|
||||
|
||||
void clear();
|
||||
///< remove all declarations.
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,74 @@
|
||||
|
||||
#include "output.hpp"
|
||||
|
||||
#include <cassert>
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
|
||||
#include "locals.hpp"
|
||||
|
||||
namespace Compiler
|
||||
{
|
||||
Output::Output (Locals& locals) : mLocals (locals) {}
|
||||
|
||||
void Output::getCode (std::vector<Interpreter::Type_Code>& code) const
|
||||
{
|
||||
code.clear();
|
||||
|
||||
// header
|
||||
code.push_back (static_cast<Interpreter::Type_Code> (mCode.size()));
|
||||
|
||||
assert (mLiterals.getIntegerSize()%4==0);
|
||||
code.push_back (static_cast<Interpreter::Type_Code> (mLiterals.getIntegerSize()/4));
|
||||
|
||||
assert (mLiterals.getFloatSize()%4==0);
|
||||
code.push_back (static_cast<Interpreter::Type_Code> (mLiterals.getFloatSize()/4));
|
||||
|
||||
assert (mLiterals.getStringSize()%4==0);
|
||||
code.push_back (static_cast<Interpreter::Type_Code> (mLiterals.getStringSize()/4));
|
||||
|
||||
// code
|
||||
std::copy (mCode.begin(), mCode.end(), std::back_inserter (code));
|
||||
|
||||
// literals
|
||||
mLiterals.append (code);
|
||||
}
|
||||
|
||||
const Literals& Output::getLiterals() const
|
||||
{
|
||||
return mLiterals;
|
||||
}
|
||||
|
||||
const std::vector<Interpreter::Type_Code>& Output::getCode() const
|
||||
{
|
||||
return mCode;
|
||||
}
|
||||
|
||||
const Locals& Output::getLocals() const
|
||||
{
|
||||
return mLocals;
|
||||
}
|
||||
|
||||
Literals& Output::getLiterals()
|
||||
{
|
||||
return mLiterals;
|
||||
}
|
||||
|
||||
std::vector<Interpreter::Type_Code>& Output::getCode()
|
||||
{
|
||||
return mCode;
|
||||
}
|
||||
|
||||
Locals& Output::getLocals()
|
||||
{
|
||||
return mLocals;
|
||||
}
|
||||
|
||||
void Output::clear()
|
||||
{
|
||||
mLiterals.clear();
|
||||
mCode.clear();
|
||||
mLocals.clear();
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,44 @@
|
||||
#ifndef COMPILER_OUTPUT_H_INCLUDED
|
||||
#define COMPILER_OUTPUT_H_INCLUDED
|
||||
|
||||
#include "literals.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <components/interpreter/types.hpp>
|
||||
|
||||
namespace Compiler
|
||||
{
|
||||
class Locals;
|
||||
|
||||
class Output
|
||||
{
|
||||
Literals mLiterals;
|
||||
std::vector<Interpreter::Type_Code> mCode;
|
||||
Locals& mLocals;
|
||||
|
||||
public:
|
||||
|
||||
Output (Locals& locals);
|
||||
|
||||
void getCode (std::vector<Interpreter::Type_Code>& code) const;
|
||||
///< store generated code in \æ code.
|
||||
|
||||
const Literals& getLiterals() const;
|
||||
|
||||
const Locals& getLocals() const;
|
||||
|
||||
const std::vector<Interpreter::Type_Code>& getCode() const;
|
||||
|
||||
Literals& getLiterals();
|
||||
|
||||
std::vector<Interpreter::Type_Code>& getCode();
|
||||
|
||||
Locals& getLocals();
|
||||
|
||||
void clear();
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -0,0 +1,140 @@
|
||||
|
||||
#include "parser.hpp"
|
||||
|
||||
#include <cctype>
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
|
||||
#include "errorhandler.hpp"
|
||||
#include "exception.hpp"
|
||||
|
||||
namespace Compiler
|
||||
{
|
||||
// Report the error and throw an exception.
|
||||
|
||||
void Parser::reportSeriousError (const std::string& message, const TokenLoc& loc)
|
||||
{
|
||||
mErrorHandler.error (message, loc);
|
||||
throw SourceException();
|
||||
}
|
||||
|
||||
// Report the error
|
||||
|
||||
void Parser::reportError (const std::string& message, const TokenLoc& loc)
|
||||
{
|
||||
mErrorHandler.error (message, loc);
|
||||
}
|
||||
|
||||
// Report the warning without throwing an exception.
|
||||
|
||||
void Parser::reportWarning (const std::string& message, const TokenLoc& loc)
|
||||
{
|
||||
mErrorHandler.warning (message, loc);
|
||||
}
|
||||
|
||||
// Report an unexpected EOF condition.
|
||||
|
||||
void Parser::reportEOF()
|
||||
{
|
||||
mErrorHandler.endOfFile();
|
||||
throw EOFException();
|
||||
}
|
||||
|
||||
// Return error handler
|
||||
|
||||
ErrorHandler& Parser::getErrorHandler()
|
||||
{
|
||||
return mErrorHandler;
|
||||
}
|
||||
|
||||
// Return context
|
||||
|
||||
Context& Parser::getContext()
|
||||
{
|
||||
return mContext;
|
||||
}
|
||||
|
||||
std::string Parser::toLower (const std::string& name)
|
||||
{
|
||||
std::string lowerCase;
|
||||
|
||||
std::transform (name.begin(), name.end(), std::back_inserter (lowerCase),
|
||||
(int(*)(int)) std::tolower);
|
||||
|
||||
return lowerCase;
|
||||
}
|
||||
|
||||
Parser::Parser (ErrorHandler& errorHandler, Context& context)
|
||||
: mErrorHandler (errorHandler), mContext (context)
|
||||
{}
|
||||
|
||||
// destructor
|
||||
|
||||
Parser::~Parser() {}
|
||||
|
||||
// Handle an int token.
|
||||
// \return fetch another token?
|
||||
//
|
||||
// - Default-implementation: Report an error.
|
||||
|
||||
bool Parser::parseInt (int value, const TokenLoc& loc, Scanner& scanner)
|
||||
{
|
||||
reportSeriousError ("Unexpected numeric value", loc);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Handle a float token.
|
||||
// \return fetch another token?
|
||||
//
|
||||
// - Default-implementation: Report an error.
|
||||
|
||||
bool Parser::parseFloat (float value, const TokenLoc& loc, Scanner& scanner)
|
||||
{
|
||||
reportSeriousError ("Unexpected floating point value", loc);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Handle a name token.
|
||||
// \return fetch another token?
|
||||
//
|
||||
// - Default-implementation: Report an error.
|
||||
|
||||
bool Parser::parseName (const std::string& name, const TokenLoc& loc,
|
||||
Scanner& scanner)
|
||||
{
|
||||
reportSeriousError ("Unexpected name", loc);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Handle a keyword token.
|
||||
// \return fetch another token?
|
||||
//
|
||||
// - Default-implementation: Report an error.
|
||||
|
||||
bool Parser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner)
|
||||
{
|
||||
reportSeriousError ("Unexpected keyword", loc);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Handle a special character token.
|
||||
// \return fetch another token?
|
||||
//
|
||||
// - Default-implementation: Report an error.
|
||||
|
||||
bool Parser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner)
|
||||
{
|
||||
reportSeriousError ("Unexpected special token", loc);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Handle an EOF token.
|
||||
//
|
||||
// - Default-implementation: Report an error.
|
||||
|
||||
void Parser::parseEOF (Scanner& scanner)
|
||||
{
|
||||
reportEOF();
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,90 @@
|
||||
#ifndef COMPILER_PARSER_H_INCLUDED
|
||||
#define COMPILER_PARSER_H_INCLUDED
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace Compiler
|
||||
{
|
||||
class Scanner;
|
||||
struct TokenLoc;
|
||||
class ErrorHandler;
|
||||
class Context;
|
||||
|
||||
/// \brief Parser base class
|
||||
///
|
||||
/// This class defines a callback-parser.
|
||||
|
||||
class Parser
|
||||
{
|
||||
ErrorHandler& mErrorHandler;
|
||||
Context& mContext;
|
||||
|
||||
protected:
|
||||
|
||||
void reportSeriousError (const std::string& message, const TokenLoc& loc);
|
||||
///< Report the error and throw a exception.
|
||||
|
||||
void reportError (const std::string& message, const TokenLoc& loc);
|
||||
///< Report the error
|
||||
|
||||
void reportWarning (const std::string& message, const TokenLoc& loc);
|
||||
///< Report the warning without throwing an exception.
|
||||
|
||||
void reportEOF();
|
||||
///< Report an unexpected EOF condition.
|
||||
|
||||
ErrorHandler& getErrorHandler();
|
||||
///< Return error handler
|
||||
|
||||
Context& getContext();
|
||||
///< Return context
|
||||
|
||||
static std::string toLower (const std::string& name);
|
||||
|
||||
public:
|
||||
|
||||
Parser (ErrorHandler& errorHandler, Context& context);
|
||||
///< constructor
|
||||
|
||||
virtual ~Parser();
|
||||
///< destructor
|
||||
|
||||
virtual bool parseInt (int value, const TokenLoc& loc, Scanner& scanner);
|
||||
///< Handle an int token.
|
||||
/// \return fetch another token?
|
||||
///
|
||||
/// - Default-implementation: Report an error.
|
||||
|
||||
virtual bool parseFloat (float value, const TokenLoc& loc, Scanner& scanner);
|
||||
///< Handle a float token.
|
||||
/// \return fetch another token?
|
||||
///
|
||||
/// - Default-implementation: Report an error.
|
||||
|
||||
virtual bool parseName (const std::string& name, const TokenLoc& loc,
|
||||
Scanner& scanner);
|
||||
///< Handle a name token.
|
||||
/// \return fetch another token?
|
||||
///
|
||||
/// - Default-implementation: Report an error.
|
||||
|
||||
virtual bool parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner);
|
||||
///< Handle a keyword token.
|
||||
/// \return fetch another token?
|
||||
///
|
||||
/// - Default-implementation: Report an error.
|
||||
|
||||
virtual bool parseSpecial (int code, const TokenLoc& loc, Scanner& scanner);
|
||||
///< Handle a special character token.
|
||||
/// \return fetch another token?
|
||||
///
|
||||
/// - Default-implementation: Report an error.
|
||||
|
||||
virtual void parseEOF (Scanner& scanner);
|
||||
///< Handle EOF token.
|
||||
///
|
||||
/// - Default-implementation: Report an error.
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,513 @@
|
||||
|
||||
#include "scanner.hpp"
|
||||
|
||||
#include <cctype>
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
|
||||
#include "exception.hpp"
|
||||
#include "errorhandler.hpp"
|
||||
#include "parser.hpp"
|
||||
#include "extensions.hpp"
|
||||
|
||||
namespace Compiler
|
||||
{
|
||||
bool Scanner::get (char& c)
|
||||
{
|
||||
mStream.get (c);
|
||||
|
||||
if (!mStream.good())
|
||||
return false;
|
||||
|
||||
mPrevLoc =mLoc;
|
||||
|
||||
if (c=='\n')
|
||||
{
|
||||
mLoc.mColumn = 0;
|
||||
++mLoc.mLine;
|
||||
mLoc.mLiteral.clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
++mLoc.mColumn;
|
||||
mLoc.mLiteral += c;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Scanner::putback (char c)
|
||||
{
|
||||
mStream.putback (c);
|
||||
mLoc = mPrevLoc;
|
||||
}
|
||||
|
||||
bool Scanner::scanToken (Parser& parser)
|
||||
{
|
||||
switch (mPutback)
|
||||
{
|
||||
case Putback_Special:
|
||||
|
||||
mPutback = Putback_None;
|
||||
return parser.parseSpecial (mPutbackCode, mPutbackLoc, *this);
|
||||
|
||||
case Putback_Integer:
|
||||
|
||||
mPutback = Putback_None;
|
||||
return parser.parseInt (mPutbackInteger, mPutbackLoc, *this);
|
||||
|
||||
case Putback_Float:
|
||||
|
||||
mPutback = Putback_None;
|
||||
return parser.parseFloat (mPutbackFloat, mPutbackLoc, *this);
|
||||
|
||||
case Putback_Name:
|
||||
|
||||
mPutback = Putback_None;
|
||||
return parser.parseName (mPutbackName, mPutbackLoc, *this);
|
||||
|
||||
case Putback_Keyword:
|
||||
|
||||
mPutback = Putback_None;
|
||||
return parser.parseKeyword (mPutbackCode, mPutbackLoc, *this);
|
||||
|
||||
case Putback_None:
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
char c;
|
||||
|
||||
if (!get (c))
|
||||
{
|
||||
parser.parseEOF (*this);
|
||||
return false;
|
||||
}
|
||||
else if (c==';')
|
||||
{
|
||||
while (get (c))
|
||||
{
|
||||
if (c=='\n')
|
||||
{
|
||||
putback (c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mLoc.mLiteral.clear();
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (isWhitespace (c))
|
||||
{
|
||||
mLoc.mLiteral.clear();
|
||||
return true;
|
||||
}
|
||||
else if (std::isdigit (c))
|
||||
{
|
||||
bool cont = false;
|
||||
|
||||
if (scanInt (c, parser, cont))
|
||||
{
|
||||
mLoc.mLiteral.clear();
|
||||
return cont;
|
||||
}
|
||||
}
|
||||
else if (std::isalpha (c) || c=='_' || c=='"')
|
||||
{
|
||||
bool cont = false;
|
||||
|
||||
if (scanName (c, parser, cont))
|
||||
{
|
||||
mLoc.mLiteral.clear();
|
||||
return cont;
|
||||
}
|
||||
}
|
||||
else if (c==13) // linux compatibility hack
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
bool cont = false;
|
||||
|
||||
if (scanSpecial (c, parser, cont))
|
||||
{
|
||||
mLoc.mLiteral.clear();
|
||||
return cont;
|
||||
}
|
||||
}
|
||||
|
||||
TokenLoc loc (mLoc);
|
||||
mLoc.mLiteral.clear();
|
||||
|
||||
mErrorHandler.error ("syntax error", loc);
|
||||
throw SourceException();
|
||||
}
|
||||
|
||||
bool Scanner::scanInt (char c, Parser& parser, bool& cont)
|
||||
{
|
||||
std::string value;
|
||||
|
||||
value += c;
|
||||
bool empty = false;
|
||||
|
||||
bool error = false;
|
||||
|
||||
while (get (c))
|
||||
{
|
||||
if (std::isdigit (c))
|
||||
{
|
||||
value += c;
|
||||
empty = false;
|
||||
}
|
||||
else if (std::isalpha (c) || c=='_')
|
||||
error = true;
|
||||
else if (c=='.' && !error)
|
||||
{
|
||||
return scanFloat (value, parser, cont);
|
||||
}
|
||||
else
|
||||
{
|
||||
putback (c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty || error)
|
||||
return false;
|
||||
|
||||
TokenLoc loc (mLoc);
|
||||
mLoc.mLiteral.clear();
|
||||
|
||||
std::istringstream stream (value);
|
||||
|
||||
int intValue = 0;
|
||||
stream >> intValue;
|
||||
|
||||
cont = parser.parseInt (intValue, loc, *this);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Scanner::scanFloat (const std::string& intValue, Parser& parser, bool& cont)
|
||||
{
|
||||
std::string value = intValue + ".";
|
||||
|
||||
char c;
|
||||
|
||||
bool empty = intValue.empty() || intValue=="-";
|
||||
bool error = false;
|
||||
|
||||
while (get (c))
|
||||
{
|
||||
if (std::isdigit (c))
|
||||
{
|
||||
value += c;
|
||||
empty = false;
|
||||
}
|
||||
else if (std::isalpha (c) || c=='_')
|
||||
error = true;
|
||||
else
|
||||
{
|
||||
putback (c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty || error)
|
||||
return false;
|
||||
|
||||
TokenLoc loc (mLoc);
|
||||
mLoc.mLiteral.clear();
|
||||
|
||||
std::istringstream stream (value);
|
||||
|
||||
float floatValue = 0;
|
||||
stream >> floatValue;
|
||||
|
||||
cont = parser.parseFloat (floatValue, loc, *this);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Scanner::scanName (char c, Parser& parser, bool& cont)
|
||||
{
|
||||
static const char *keywords[] =
|
||||
{
|
||||
"begin", "end",
|
||||
"short", "long", "float",
|
||||
"if", "endif", "else", "elseif",
|
||||
"while", "endwhile",
|
||||
"return",
|
||||
"messagebox",
|
||||
"set", "to",
|
||||
"getsquareroot",
|
||||
"menumode",
|
||||
"random",
|
||||
"startscript", "stopscript", "scriptrunning",
|
||||
0
|
||||
};
|
||||
|
||||
std::string name;
|
||||
|
||||
if (!scanName (c, name))
|
||||
return false;
|
||||
|
||||
TokenLoc loc (mLoc);
|
||||
mLoc.mLiteral.clear();
|
||||
|
||||
if (name.size()>=2 && name[0]=='"' && name[name.size()-1]=='"')
|
||||
{
|
||||
name = name.substr (1, name.size()-2);
|
||||
cont = parser.parseName (name, loc, *this);
|
||||
return true;
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
|
||||
std::string lowerCase;
|
||||
lowerCase.reserve (name.size());
|
||||
|
||||
std::transform (name.begin(), name.end(), std::back_inserter (lowerCase),
|
||||
(int(*)(int)) std::tolower);
|
||||
|
||||
for (; keywords[i]; ++i)
|
||||
if (lowerCase==keywords[i])
|
||||
break;
|
||||
|
||||
if (keywords[i])
|
||||
{
|
||||
cont = parser.parseKeyword (i, loc, *this);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (mExtensions)
|
||||
{
|
||||
if (int keyword = mExtensions->searchKeyword (lowerCase))
|
||||
{
|
||||
cont = parser.parseKeyword (keyword, loc, *this);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
cont = parser.parseName (name, loc, *this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Scanner::scanName (char c, std::string& name)
|
||||
{
|
||||
bool first = false;
|
||||
bool error = false;
|
||||
|
||||
name.clear();
|
||||
|
||||
putback (c);
|
||||
|
||||
while (get (c))
|
||||
{
|
||||
if (!name.empty() && name[0]=='"')
|
||||
{
|
||||
if (c=='"')
|
||||
{
|
||||
name += c;
|
||||
break;
|
||||
}
|
||||
else if (c=='\\')
|
||||
{
|
||||
if (!get (c))
|
||||
{
|
||||
mErrorHandler.error ("incomplete escape sequence", mLoc);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (c=='\n')
|
||||
{
|
||||
mErrorHandler.error ("incomplete string or name", mLoc);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (!(c=='"' && name.empty()))
|
||||
{
|
||||
if (!(std::isalpha (c) || std::isdigit (c) || c=='_'))
|
||||
{
|
||||
putback (c);
|
||||
break;
|
||||
}
|
||||
|
||||
if (first && std::isdigit (c))
|
||||
error = true;
|
||||
}
|
||||
|
||||
name += c;
|
||||
first = false;
|
||||
}
|
||||
|
||||
return !error;
|
||||
}
|
||||
|
||||
bool Scanner::scanSpecial (char c, Parser& parser, bool& cont)
|
||||
{
|
||||
int special = -1;
|
||||
|
||||
if (c=='\n')
|
||||
special = S_newline;
|
||||
else if (c=='(')
|
||||
special = S_open;
|
||||
else if (c==')')
|
||||
special = S_close;
|
||||
else if (c=='=')
|
||||
{
|
||||
if (get (c))
|
||||
{
|
||||
if (c=='=')
|
||||
special = S_cmpEQ;
|
||||
else
|
||||
{
|
||||
putback (c);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
putback (c);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (c=='!')
|
||||
{
|
||||
if (get (c))
|
||||
{
|
||||
if (c=='=')
|
||||
special = S_cmpNE;
|
||||
else
|
||||
{
|
||||
putback (c);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
else if (c=='-')
|
||||
{
|
||||
if (get (c))
|
||||
{
|
||||
if (c=='>')
|
||||
special = S_ref;
|
||||
else
|
||||
{
|
||||
putback (c);
|
||||
special = S_minus;
|
||||
}
|
||||
}
|
||||
else
|
||||
special = S_minus;
|
||||
}
|
||||
else if (c=='<')
|
||||
{
|
||||
if (get (c))
|
||||
{
|
||||
if (c=='=')
|
||||
special = S_cmpLE;
|
||||
else
|
||||
{
|
||||
putback (c);
|
||||
special = S_cmpLT;
|
||||
}
|
||||
}
|
||||
else
|
||||
special = S_cmpLT;
|
||||
}
|
||||
else if (c=='>')
|
||||
{
|
||||
if (get (c))
|
||||
{
|
||||
if (c=='=')
|
||||
special = S_cmpGE;
|
||||
else
|
||||
{
|
||||
putback (c);
|
||||
special = S_cmpGT;
|
||||
}
|
||||
}
|
||||
else
|
||||
special = S_cmpGT;
|
||||
}
|
||||
else if (c==',')
|
||||
special = S_comma;
|
||||
else if (c=='+')
|
||||
special = S_plus;
|
||||
else if (c=='*')
|
||||
special = S_mult;
|
||||
else if (c=='/')
|
||||
special = S_div;
|
||||
else
|
||||
return false;
|
||||
|
||||
if (special==S_newline)
|
||||
mLoc.mLiteral = "<newline>";
|
||||
|
||||
TokenLoc loc (mLoc);
|
||||
mLoc.mLiteral.clear();
|
||||
|
||||
cont = parser.parseSpecial (special, loc, *this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Scanner::isWhitespace (char c)
|
||||
{
|
||||
return c==' ' || c=='\t';
|
||||
}
|
||||
|
||||
// constructor
|
||||
|
||||
Scanner::Scanner (ErrorHandler& errorHandler, std::istream& inputStream,
|
||||
const Extensions *extensions)
|
||||
: mErrorHandler (errorHandler), mStream (inputStream), mExtensions (extensions),
|
||||
mPutback (Putback_None)
|
||||
{
|
||||
}
|
||||
|
||||
void Scanner::scan (Parser& parser)
|
||||
{
|
||||
while (scanToken (parser));
|
||||
}
|
||||
|
||||
void Scanner::putbackSpecial (int code, const TokenLoc& loc)
|
||||
{
|
||||
mPutback = Putback_Special;
|
||||
mPutbackCode = code;
|
||||
mPutbackLoc = loc;
|
||||
}
|
||||
|
||||
void Scanner::putbackInt (int value, const TokenLoc& loc)
|
||||
{
|
||||
mPutback = Putback_Integer;
|
||||
mPutbackInteger = value;
|
||||
mPutbackLoc = loc;
|
||||
}
|
||||
|
||||
void Scanner::putbackFloat (float value, const TokenLoc& loc)
|
||||
{
|
||||
mPutback = Putback_Float;
|
||||
mPutbackFloat = value;
|
||||
mPutbackLoc = loc;
|
||||
}
|
||||
|
||||
void Scanner::putbackName (const std::string& name, const TokenLoc& loc)
|
||||
{
|
||||
mPutback = Putback_Name;
|
||||
mPutbackName = name;
|
||||
mPutbackLoc = loc;
|
||||
}
|
||||
|
||||
void Scanner::putbackKeyword (int keyword, const TokenLoc& loc)
|
||||
{
|
||||
mPutback = Putback_Keyword;
|
||||
mPutbackCode = keyword;
|
||||
mPutbackLoc = loc;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,119 @@
|
||||
#ifndef COMPILER_SCANNER_H_INCLUDED
|
||||
#define COMPILER_SCANNER_H_INCLUDED
|
||||
|
||||
#include <string>
|
||||
#include <iosfwd>
|
||||
|
||||
#include "tokenloc.hpp"
|
||||
|
||||
namespace Compiler
|
||||
{
|
||||
class ErrorHandler;
|
||||
class Parser;
|
||||
class Extensions;
|
||||
|
||||
/// \brief Scanner
|
||||
///
|
||||
/// This class translate a char-stream to a token stream (delivered via
|
||||
/// parser-callbacks).
|
||||
|
||||
class Scanner
|
||||
{
|
||||
enum putback_type
|
||||
{
|
||||
Putback_None, Putback_Special, Putback_Integer, Putback_Float,
|
||||
Putback_Name, Putback_Keyword
|
||||
};
|
||||
|
||||
ErrorHandler& mErrorHandler;
|
||||
TokenLoc mLoc;
|
||||
TokenLoc mPrevLoc;
|
||||
std::istream& mStream;
|
||||
const Extensions *mExtensions;
|
||||
putback_type mPutback;
|
||||
int mPutbackCode;
|
||||
int mPutbackInteger;
|
||||
float mPutbackFloat;
|
||||
std::string mPutbackName;
|
||||
TokenLoc mPutbackLoc;
|
||||
|
||||
public:
|
||||
|
||||
enum keyword
|
||||
{
|
||||
K_begin, K_end,
|
||||
K_short, K_long, K_float,
|
||||
K_if, K_endif, K_else, K_elseif,
|
||||
K_while, K_endwhile,
|
||||
K_return,
|
||||
K_messagebox,
|
||||
K_set, K_to,
|
||||
K_getsquareroot,
|
||||
K_menumode,
|
||||
K_random,
|
||||
K_startscript, K_stopscript, K_scriptrunning
|
||||
};
|
||||
|
||||
enum special
|
||||
{
|
||||
S_newline,
|
||||
S_open, S_close,
|
||||
S_cmpEQ, S_cmpNE, S_cmpLT, S_cmpLE, S_cmpGT, S_cmpGE,
|
||||
S_plus, S_minus, S_mult, S_div,
|
||||
S_comma,
|
||||
S_ref
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
// not implemented
|
||||
|
||||
Scanner (const Scanner&);
|
||||
Scanner& operator= (const Scanner&);
|
||||
|
||||
bool get (char& c);
|
||||
|
||||
void putback (char c);
|
||||
|
||||
bool scanToken (Parser& parser);
|
||||
|
||||
bool scanInt (char c, Parser& parser, bool& cont);
|
||||
|
||||
bool scanFloat (const std::string& intValue, Parser& parser, bool& cont);
|
||||
|
||||
bool scanName (char c, Parser& parser, bool& cont);
|
||||
|
||||
bool scanName (char c, std::string& name);
|
||||
|
||||
bool scanSpecial (char c, Parser& parser, bool& cont);
|
||||
|
||||
static bool isWhitespace (char c);
|
||||
|
||||
public:
|
||||
|
||||
Scanner (ErrorHandler& errorHandler, std::istream& inputStream,
|
||||
const Extensions *extensions = 0);
|
||||
///< constructor
|
||||
|
||||
void scan (Parser& parser);
|
||||
///< Scan a token and deliver it to the parser.
|
||||
|
||||
void putbackSpecial (int code, const TokenLoc& loc);
|
||||
///< put back a special token
|
||||
|
||||
void putbackInt (int value, const TokenLoc& loc);
|
||||
///< put back an integer token
|
||||
|
||||
void putbackFloat (float value, const TokenLoc& loc);
|
||||
///< put back a float token
|
||||
|
||||
void putbackName (const std::string& name, const TokenLoc& loc);
|
||||
///< put back a name toekn
|
||||
|
||||
void putbackKeyword (int keyword, const TokenLoc& loc);
|
||||
///< put back a keyword token
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,80 @@
|
||||
|
||||
#include "scriptparser.hpp"
|
||||
|
||||
#include "scanner.hpp"
|
||||
|
||||
namespace Compiler
|
||||
{
|
||||
ScriptParser::ScriptParser (ErrorHandler& errorHandler, Context& context,
|
||||
Locals& locals, bool end)
|
||||
: Parser (errorHandler, context), mOutput (locals),
|
||||
mLineParser (errorHandler, context, locals, mOutput.getLiterals(), mOutput.getCode()),
|
||||
mControlParser (errorHandler, context, locals, mOutput.getLiterals()),
|
||||
mEnd (end)
|
||||
{}
|
||||
|
||||
void ScriptParser::getCode (std::vector<Interpreter::Type_Code>& code) const
|
||||
{
|
||||
mOutput.getCode (code);
|
||||
}
|
||||
|
||||
bool ScriptParser::parseName (const std::string& name, const TokenLoc& loc,
|
||||
Scanner& scanner)
|
||||
{
|
||||
mLineParser.reset();
|
||||
if (mLineParser.parseName (name, loc, scanner))
|
||||
scanner.scan (mLineParser);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ScriptParser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner)
|
||||
{
|
||||
if (keyword==Scanner::K_while || keyword==Scanner::K_if)
|
||||
{
|
||||
mControlParser.reset();
|
||||
if (mControlParser.parseKeyword (keyword, loc, scanner))
|
||||
scanner.scan (mControlParser);
|
||||
|
||||
mControlParser.appendCode (mOutput.getCode());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (keyword==Scanner::K_end && mEnd)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
mLineParser.reset();
|
||||
if (mLineParser.parseKeyword (keyword, loc, scanner))
|
||||
scanner.scan (mLineParser);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ScriptParser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner)
|
||||
{
|
||||
if (code==Scanner::S_newline) // empty line
|
||||
return true;
|
||||
|
||||
mLineParser.reset();
|
||||
if (mLineParser.parseSpecial (code, loc, scanner))
|
||||
scanner.scan (mLineParser);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ScriptParser::parseEOF (Scanner& scanner)
|
||||
{
|
||||
if (mEnd)
|
||||
Parser::parseEOF (scanner);
|
||||
}
|
||||
|
||||
void ScriptParser::reset()
|
||||
{
|
||||
mLineParser.reset();
|
||||
mOutput.clear();
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,54 @@
|
||||
#ifndef COMPILER_SCRIPTPARSER_H_INCLUDED
|
||||
#define COMPILER_SCRIPTPARSER_H_INCLUDED
|
||||
|
||||
|
||||
#include "parser.hpp"
|
||||
#include "lineparser.hpp"
|
||||
#include "controlparser.hpp"
|
||||
#include "output.hpp"
|
||||
|
||||
namespace Compiler
|
||||
{
|
||||
class Locals;
|
||||
|
||||
// Script parser, to be used in dialogue scripts and as part of FileParser
|
||||
|
||||
class ScriptParser : public Parser
|
||||
{
|
||||
Output mOutput;
|
||||
LineParser mLineParser;
|
||||
ControlParser mControlParser;
|
||||
bool mEnd;
|
||||
|
||||
public:
|
||||
|
||||
/// \param end of script is marked by end keyword.
|
||||
ScriptParser (ErrorHandler& errorHandler, Context& context, Locals& locals,
|
||||
bool end = false);
|
||||
|
||||
void getCode (std::vector<Interpreter::Type_Code>& code) const;
|
||||
///< store generated code in \æ code.
|
||||
|
||||
virtual bool parseName (const std::string& name, const TokenLoc& loc,
|
||||
Scanner& scanner);
|
||||
///< Handle a name token.
|
||||
/// \return fetch another token?
|
||||
|
||||
virtual bool parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner);
|
||||
///< Handle a keyword token.
|
||||
/// \return fetch another token?
|
||||
|
||||
virtual bool parseSpecial (int code, const TokenLoc& loc, Scanner& scanner);
|
||||
///< Handle a special character token.
|
||||
/// \return fetch another token?
|
||||
|
||||
virtual void parseEOF (Scanner& scanner);
|
||||
///< Handle EOF token.
|
||||
|
||||
void reset();
|
||||
///< Reset parser to clean state.
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -0,0 +1,41 @@
|
||||
|
||||
#include "skipparser.hpp"
|
||||
|
||||
#include "scanner.hpp"
|
||||
|
||||
namespace Compiler
|
||||
{
|
||||
SkipParser::SkipParser (ErrorHandler& errorHandler, Context& context)
|
||||
: Parser (errorHandler, context)
|
||||
{}
|
||||
|
||||
bool SkipParser::parseInt (int value, const TokenLoc& loc, Scanner& scanner)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SkipParser::parseFloat (float value, const TokenLoc& loc, Scanner& scanner)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SkipParser::parseName (const std::string& name, const TokenLoc& loc,
|
||||
Scanner& scanner)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SkipParser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SkipParser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner)
|
||||
{
|
||||
if (code==Scanner::S_newline)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,42 @@
|
||||
#ifndef COMPILER_SKIPPARSER_H_INCLUDED
|
||||
#define COMPILER_SKIPPARSER_H_INCLUDED
|
||||
|
||||
#include "parser.hpp"
|
||||
|
||||
namespace Compiler
|
||||
{
|
||||
// \brief Skip parser for skipping a line
|
||||
//
|
||||
// This parser is mainly intended for skipping the rest of a faulty line.
|
||||
|
||||
class SkipParser : public Parser
|
||||
{
|
||||
public:
|
||||
|
||||
SkipParser (ErrorHandler& errorHandler, Context& context);
|
||||
|
||||
virtual bool parseInt (int value, const TokenLoc& loc, Scanner& scanner);
|
||||
///< Handle an int token.
|
||||
/// \return fetch another token?
|
||||
|
||||
virtual bool parseFloat (float value, const TokenLoc& loc, Scanner& scanner);
|
||||
///< Handle a float token.
|
||||
/// \return fetch another token?
|
||||
|
||||
virtual bool parseName (const std::string& name, const TokenLoc& loc,
|
||||
Scanner& scanner);
|
||||
///< Handle a name token.
|
||||
/// \return fetch another token?
|
||||
|
||||
virtual bool parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner);
|
||||
///< Handle a keyword token.
|
||||
/// \return fetch another token?
|
||||
|
||||
virtual bool parseSpecial (int code, const TokenLoc& loc, Scanner& scanner);
|
||||
///< Handle a special character token.
|
||||
/// \return fetch another token?
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue