mirror of
https://github.com/OpenMW/openmw.git
synced 2025-01-21 09:53:52 +00:00
Merge branch 'script'
Conflicts: readme.txt
This commit is contained in:
commit
6ac64bbe15
54 changed files with 1129 additions and 286 deletions
|
@ -38,7 +38,7 @@ opencs_units (model/tools
|
||||||
|
|
||||||
opencs_units_noqt (model/tools
|
opencs_units_noqt (model/tools
|
||||||
mandatoryid skillcheck classcheck factioncheck racecheck soundcheck regioncheck
|
mandatoryid skillcheck classcheck factioncheck racecheck soundcheck regioncheck
|
||||||
birthsigncheck spellcheck referenceablecheck
|
birthsigncheck spellcheck referenceablecheck scriptcheck
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
103
apps/opencs/model/tools/scriptcheck.cpp
Normal file
103
apps/opencs/model/tools/scriptcheck.cpp
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
|
||||||
|
#include "scriptcheck.hpp"
|
||||||
|
|
||||||
|
#include <components/compiler/tokenloc.hpp>
|
||||||
|
#include <components/compiler/scanner.hpp>
|
||||||
|
#include <components/compiler/fileparser.hpp>
|
||||||
|
#include <components/compiler/exception.hpp>
|
||||||
|
#include <components/compiler/extensions0.hpp>
|
||||||
|
|
||||||
|
#include "../world/data.hpp"
|
||||||
|
|
||||||
|
void CSMTools::ScriptCheckStage::report (const std::string& message, const Compiler::TokenLoc& loc,
|
||||||
|
Type type)
|
||||||
|
{
|
||||||
|
std::ostringstream stream;
|
||||||
|
|
||||||
|
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Script, mId);
|
||||||
|
|
||||||
|
stream << id.toString() << "|";
|
||||||
|
|
||||||
|
if (type==ErrorMessage)
|
||||||
|
stream << "error ";
|
||||||
|
else
|
||||||
|
stream << "warning ";
|
||||||
|
|
||||||
|
stream
|
||||||
|
<< "script " << mFile
|
||||||
|
<< ", line " << loc.mLine << ", column " << loc.mColumn
|
||||||
|
<< " (" << loc.mLiteral << "): " << message;
|
||||||
|
|
||||||
|
mMessages->push_back (stream.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMTools::ScriptCheckStage::report (const std::string& message, Type type)
|
||||||
|
{
|
||||||
|
std::ostringstream stream;
|
||||||
|
|
||||||
|
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Script, mId);
|
||||||
|
|
||||||
|
stream << id.toString() << "|";
|
||||||
|
|
||||||
|
if (type==ErrorMessage)
|
||||||
|
stream << "error: ";
|
||||||
|
else
|
||||||
|
stream << "warning: ";
|
||||||
|
|
||||||
|
stream << message;
|
||||||
|
|
||||||
|
mMessages->push_back (stream.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
CSMTools::ScriptCheckStage::ScriptCheckStage (const CSMWorld::Data& data)
|
||||||
|
: mData (data), mContext (data), mMessages (0)
|
||||||
|
{
|
||||||
|
/// \todo add an option to configure warning mode
|
||||||
|
setWarningsMode (0);
|
||||||
|
|
||||||
|
Compiler::registerExtensions (mExtensions);
|
||||||
|
mContext.setExtensions (&mExtensions);
|
||||||
|
}
|
||||||
|
|
||||||
|
int CSMTools::ScriptCheckStage::setup()
|
||||||
|
{
|
||||||
|
mContext.clear();
|
||||||
|
mMessages = 0;
|
||||||
|
mId.clear();
|
||||||
|
|
||||||
|
return mData.getScripts().getSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMTools::ScriptCheckStage::perform (int stage, std::vector<std::string>& messages)
|
||||||
|
{
|
||||||
|
mMessages = &messages;
|
||||||
|
mId = mData.getScripts().getId (stage);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
mFile = mData.getScripts().getRecord (stage).get().mId;
|
||||||
|
std::istringstream input (mData.getScripts().getRecord (stage).get().mScriptText);
|
||||||
|
|
||||||
|
Compiler::Scanner scanner (*this, input, mContext.getExtensions());
|
||||||
|
|
||||||
|
Compiler::FileParser parser (*this, mContext);
|
||||||
|
|
||||||
|
scanner.scan (parser);
|
||||||
|
}
|
||||||
|
catch (const Compiler::SourceException&)
|
||||||
|
{
|
||||||
|
// error has already been reported via error handler
|
||||||
|
}
|
||||||
|
catch (const std::exception& error)
|
||||||
|
{
|
||||||
|
std::ostringstream stream;
|
||||||
|
|
||||||
|
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Script, mId);
|
||||||
|
|
||||||
|
stream << id.toString() << "|Critical compile error: " << error.what();
|
||||||
|
|
||||||
|
messages.push_back (stream.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
mMessages = 0;
|
||||||
|
}
|
41
apps/opencs/model/tools/scriptcheck.hpp
Normal file
41
apps/opencs/model/tools/scriptcheck.hpp
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
#ifndef CSM_TOOLS_SCRIPTCHECK_H
|
||||||
|
#define CSM_TOOLS_SCRIPTCHECK_H
|
||||||
|
|
||||||
|
#include <components/compiler/errorhandler.hpp>
|
||||||
|
#include <components/compiler/extensions.hpp>
|
||||||
|
|
||||||
|
#include "../doc/stage.hpp"
|
||||||
|
|
||||||
|
#include "../world/scriptcontext.hpp"
|
||||||
|
|
||||||
|
namespace CSMTools
|
||||||
|
{
|
||||||
|
/// \brief VerifyStage: make sure that scripts compile
|
||||||
|
class ScriptCheckStage : public CSMDoc::Stage, private Compiler::ErrorHandler
|
||||||
|
{
|
||||||
|
const CSMWorld::Data& mData;
|
||||||
|
Compiler::Extensions mExtensions;
|
||||||
|
CSMWorld::ScriptContext mContext;
|
||||||
|
std::string mId;
|
||||||
|
std::string mFile;
|
||||||
|
std::vector<std::string> *mMessages;
|
||||||
|
|
||||||
|
virtual void report (const std::string& message, const Compiler::TokenLoc& loc, Type type);
|
||||||
|
///< Report error to the user.
|
||||||
|
|
||||||
|
virtual void report (const std::string& message, Type type);
|
||||||
|
///< Report a file related error
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
ScriptCheckStage (const CSMWorld::Data& data);
|
||||||
|
|
||||||
|
virtual int setup();
|
||||||
|
///< \return number of steps
|
||||||
|
|
||||||
|
virtual void perform (int stage, std::vector<std::string>& messages);
|
||||||
|
///< Messages resulting from this tage will be appended to \a messages.
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -20,6 +20,7 @@
|
||||||
#include "birthsigncheck.hpp"
|
#include "birthsigncheck.hpp"
|
||||||
#include "spellcheck.hpp"
|
#include "spellcheck.hpp"
|
||||||
#include "referenceablecheck.hpp"
|
#include "referenceablecheck.hpp"
|
||||||
|
#include "scriptcheck.hpp"
|
||||||
|
|
||||||
CSMDoc::Operation *CSMTools::Tools::get (int type)
|
CSMDoc::Operation *CSMTools::Tools::get (int type)
|
||||||
{
|
{
|
||||||
|
@ -77,6 +78,8 @@ CSMDoc::Operation *CSMTools::Tools::getVerifier()
|
||||||
mVerifier->appendStage (new SpellCheckStage (mData.getSpells()));
|
mVerifier->appendStage (new SpellCheckStage (mData.getSpells()));
|
||||||
|
|
||||||
mVerifier->appendStage (new ReferenceableCheckStage (mData.getReferenceables().getDataSet(), mData.getRaces(), mData.getClasses(), mData.getFactions()));
|
mVerifier->appendStage (new ReferenceableCheckStage (mData.getReferenceables().getDataSet(), mData.getRaces(), mData.getClasses(), mData.getFactions()));
|
||||||
|
|
||||||
|
mVerifier->appendStage (new ScriptCheckStage (mData));
|
||||||
}
|
}
|
||||||
|
|
||||||
return mVerifier;
|
return mVerifier;
|
||||||
|
|
|
@ -5,23 +5,92 @@
|
||||||
|
|
||||||
#include <components/misc/stringops.hpp>
|
#include <components/misc/stringops.hpp>
|
||||||
|
|
||||||
|
#include <components/compiler/quickfileparser.hpp>
|
||||||
|
#include <components/compiler/nullerrorhandler.hpp>
|
||||||
|
#include <components/compiler/scanner.hpp>
|
||||||
|
|
||||||
#include "data.hpp"
|
#include "data.hpp"
|
||||||
|
|
||||||
CSMWorld::ScriptContext::ScriptContext (const Data& data) : mData (data), mIdsUpdated (false) {}
|
CSMWorld::ScriptContext::ScriptContext (const Data& data) : mData (data), mIdsUpdated (false) {}
|
||||||
|
|
||||||
bool CSMWorld::ScriptContext::canDeclareLocals() const
|
bool CSMWorld::ScriptContext::canDeclareLocals() const
|
||||||
{
|
{
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
char CSMWorld::ScriptContext::getGlobalType (const std::string& name) const
|
char CSMWorld::ScriptContext::getGlobalType (const std::string& name) const
|
||||||
{
|
{
|
||||||
|
int index = mData.getGlobals().searchId (name);
|
||||||
|
|
||||||
|
if (index!=-1)
|
||||||
|
{
|
||||||
|
switch (mData.getGlobals().getRecord (index).get().mValue.getType())
|
||||||
|
{
|
||||||
|
case ESM::VT_Short: return 's';
|
||||||
|
case ESM::VT_Long: return 'l';
|
||||||
|
case ESM::VT_Float: return 'f';
|
||||||
|
|
||||||
|
default: return ' ';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return ' ';
|
return ' ';
|
||||||
}
|
}
|
||||||
|
|
||||||
char CSMWorld::ScriptContext::getMemberType (const std::string& name, const std::string& id) const
|
std::pair<char, bool> CSMWorld::ScriptContext::getMemberType (const std::string& name,
|
||||||
|
const std::string& id) const
|
||||||
{
|
{
|
||||||
return ' ';
|
/// \todo invalidate locals cache on change to scripts
|
||||||
|
|
||||||
|
std::string id2 = Misc::StringUtils::lowerCase (id);
|
||||||
|
|
||||||
|
int index = mData.getScripts().searchId (id2);
|
||||||
|
bool reference = false;
|
||||||
|
|
||||||
|
if (index!=-1)
|
||||||
|
{
|
||||||
|
// ID is not a script ID. Search for a matching referenceable instead.
|
||||||
|
index = mData.getReferenceables().searchId (id2);
|
||||||
|
|
||||||
|
if (index!=-1)
|
||||||
|
{
|
||||||
|
// Referenceable found.
|
||||||
|
int columnIndex = mData.getReferenceables().searchColumnIndex (Columns::ColumnId_Script);
|
||||||
|
|
||||||
|
if (columnIndex!=-1)
|
||||||
|
{
|
||||||
|
id2 = Misc::StringUtils::lowerCase (mData.getReferenceables().
|
||||||
|
getData (index, columnIndex).toString().toUtf8().constData());
|
||||||
|
|
||||||
|
if (!id2.empty())
|
||||||
|
{
|
||||||
|
// Referenceable has a script -> use it.
|
||||||
|
index = mData.getScripts().searchId (id2);
|
||||||
|
reference = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index==-1)
|
||||||
|
return std::make_pair (' ', false);
|
||||||
|
|
||||||
|
std::map<std::string, Compiler::Locals>::iterator iter = mLocals.find (id2);
|
||||||
|
|
||||||
|
if (iter==mLocals.end())
|
||||||
|
{
|
||||||
|
Compiler::Locals locals;
|
||||||
|
|
||||||
|
Compiler::NullErrorHandler errorHandler;
|
||||||
|
std::istringstream stream (mData.getScripts().getRecord (index).get().mScriptText);
|
||||||
|
Compiler::QuickFileParser parser (errorHandler, *this, locals);
|
||||||
|
Compiler::Scanner scanner (errorHandler, stream, getExtensions());
|
||||||
|
scanner.scan (parser);
|
||||||
|
|
||||||
|
iter = mLocals.insert (std::make_pair (id2, locals)).first;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::make_pair (iter->second.getType (Misc::StringUtils::lowerCase (name)), reference);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CSMWorld::ScriptContext::isId (const std::string& name) const
|
bool CSMWorld::ScriptContext::isId (const std::string& name) const
|
||||||
|
@ -31,6 +100,7 @@ bool CSMWorld::ScriptContext::isId (const std::string& name) const
|
||||||
mIds = mData.getIds();
|
mIds = mData.getIds();
|
||||||
|
|
||||||
std::for_each (mIds.begin(), mIds.end(), &Misc::StringUtils::lowerCase);
|
std::for_each (mIds.begin(), mIds.end(), &Misc::StringUtils::lowerCase);
|
||||||
|
std::sort (mIds.begin(), mIds.end());
|
||||||
|
|
||||||
mIdsUpdated = true;
|
mIdsUpdated = true;
|
||||||
}
|
}
|
||||||
|
@ -38,7 +108,19 @@ bool CSMWorld::ScriptContext::isId (const std::string& name) const
|
||||||
return std::binary_search (mIds.begin(), mIds.end(), Misc::StringUtils::lowerCase (name));
|
return std::binary_search (mIds.begin(), mIds.end(), Misc::StringUtils::lowerCase (name));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CSMWorld::ScriptContext::isJournalId (const std::string& name) const
|
||||||
|
{
|
||||||
|
return mData.getJournals().searchId (name)!=-1;
|
||||||
|
}
|
||||||
|
|
||||||
void CSMWorld::ScriptContext::invalidateIds()
|
void CSMWorld::ScriptContext::invalidateIds()
|
||||||
{
|
{
|
||||||
mIdsUpdated = false;
|
mIdsUpdated = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CSMWorld::ScriptContext::clear()
|
||||||
|
{
|
||||||
|
mIds.clear();
|
||||||
|
mIdsUpdated = false;
|
||||||
|
mLocals.clear();
|
||||||
|
}
|
|
@ -3,8 +3,10 @@
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
#include <components/compiler/context.hpp>
|
#include <components/compiler/context.hpp>
|
||||||
|
#include <components/compiler/locals.hpp>
|
||||||
|
|
||||||
namespace CSMWorld
|
namespace CSMWorld
|
||||||
{
|
{
|
||||||
|
@ -15,6 +17,7 @@ namespace CSMWorld
|
||||||
const Data& mData;
|
const Data& mData;
|
||||||
mutable std::vector<std::string> mIds;
|
mutable std::vector<std::string> mIds;
|
||||||
mutable bool mIdsUpdated;
|
mutable bool mIdsUpdated;
|
||||||
|
mutable std::map<std::string, Compiler::Locals> mLocals;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
@ -26,13 +29,23 @@ namespace CSMWorld
|
||||||
virtual char getGlobalType (const std::string& name) const;
|
virtual char getGlobalType (const std::string& name) const;
|
||||||
///< 'l: long, 's': short, 'f': float, ' ': does not exist.
|
///< 'l: long, 's': short, 'f': float, ' ': does not exist.
|
||||||
|
|
||||||
virtual char getMemberType (const std::string& name, const std::string& id) const;
|
virtual std::pair<char, bool> getMemberType (const std::string& name,
|
||||||
///< 'l: long, 's': short, 'f': float, ' ': does not exist.
|
const std::string& id) const;
|
||||||
|
///< Return type of member variable \a name in script \a id or in script of reference of
|
||||||
|
/// \a id
|
||||||
|
/// \return first: 'l: long, 's': short, 'f': float, ' ': does not exist.
|
||||||
|
/// second: true: script of reference
|
||||||
|
|
||||||
virtual bool isId (const std::string& name) const;
|
virtual bool isId (const std::string& name) const;
|
||||||
///< Does \a name match an ID, that can be referenced?
|
///< Does \a name match an ID, that can be referenced?
|
||||||
|
|
||||||
|
virtual bool isJournalId (const std::string& name) const;
|
||||||
|
///< Does \a name match a journal ID?
|
||||||
|
|
||||||
void invalidateIds();
|
void invalidateIds();
|
||||||
|
|
||||||
|
void clear();
|
||||||
|
///< Remove all cached data.
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -155,6 +155,7 @@ OMW::Engine::Engine(Files::ConfigurationManager& configurationManager)
|
||||||
, mSkipMenu (false)
|
, mSkipMenu (false)
|
||||||
, mUseSound (true)
|
, mUseSound (true)
|
||||||
, mCompileAll (false)
|
, mCompileAll (false)
|
||||||
|
, mWarningsMode (1)
|
||||||
, mScriptContext (0)
|
, mScriptContext (0)
|
||||||
, mFSStrict (false)
|
, mFSStrict (false)
|
||||||
, mScriptConsoleMode (false)
|
, mScriptConsoleMode (false)
|
||||||
|
@ -424,7 +425,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
|
||||||
mScriptContext->setExtensions (&mExtensions);
|
mScriptContext->setExtensions (&mExtensions);
|
||||||
|
|
||||||
mEnvironment.setScriptManager (new MWScript::ScriptManager (MWBase::Environment::get().getWorld()->getStore(),
|
mEnvironment.setScriptManager (new MWScript::ScriptManager (MWBase::Environment::get().getWorld()->getStore(),
|
||||||
mVerboseScripts, *mScriptContext));
|
mVerboseScripts, *mScriptContext, mWarningsMode));
|
||||||
|
|
||||||
// Create game mechanics system
|
// Create game mechanics system
|
||||||
MWMechanics::MechanicsManager* mechanics = new MWMechanics::MechanicsManager;
|
MWMechanics::MechanicsManager* mechanics = new MWMechanics::MechanicsManager;
|
||||||
|
@ -612,8 +613,12 @@ void OMW::Engine::setStartupScript (const std::string& path)
|
||||||
mStartupScript = path;
|
mStartupScript = path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void OMW::Engine::setActivationDistanceOverride (int distance)
|
void OMW::Engine::setActivationDistanceOverride (int distance)
|
||||||
{
|
{
|
||||||
mActivationDistanceOverride = distance;
|
mActivationDistanceOverride = distance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OMW::Engine::setWarningsMode (int mode)
|
||||||
|
{
|
||||||
|
mWarningsMode = mode;
|
||||||
|
}
|
|
@ -74,6 +74,7 @@ namespace OMW
|
||||||
bool mSkipMenu;
|
bool mSkipMenu;
|
||||||
bool mUseSound;
|
bool mUseSound;
|
||||||
bool mCompileAll;
|
bool mCompileAll;
|
||||||
|
int mWarningsMode;
|
||||||
std::string mFocusName;
|
std::string mFocusName;
|
||||||
std::map<std::string,std::string> mFallbackMap;
|
std::map<std::string,std::string> mFallbackMap;
|
||||||
bool mScriptConsoleMode;
|
bool mScriptConsoleMode;
|
||||||
|
@ -181,6 +182,8 @@ namespace OMW
|
||||||
/// Override the game setting specified activation distance.
|
/// Override the game setting specified activation distance.
|
||||||
void setActivationDistanceOverride (int distance);
|
void setActivationDistanceOverride (int distance);
|
||||||
|
|
||||||
|
void setWarningsMode (int mode);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Files::ConfigurationManager& mCfgMgr;
|
Files::ConfigurationManager& mCfgMgr;
|
||||||
};
|
};
|
||||||
|
|
|
@ -136,6 +136,13 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
|
||||||
("script-run", bpo::value<std::string>()->default_value(""),
|
("script-run", bpo::value<std::string>()->default_value(""),
|
||||||
"select a file containing a list of console commands that is executed on startup")
|
"select a file containing a list of console commands that is executed on startup")
|
||||||
|
|
||||||
|
("script-warn", bpo::value<int>()->implicit_value (1)
|
||||||
|
->default_value (1),
|
||||||
|
"handling of warnings when compiling scripts\n"
|
||||||
|
"\t0 - ignore warning\n"
|
||||||
|
"\t1 - show warning but consider script as correctly compiled anyway\n"
|
||||||
|
"\t2 - treat warnings as errors")
|
||||||
|
|
||||||
("skip-menu", bpo::value<bool>()->implicit_value(true)
|
("skip-menu", bpo::value<bool>()->implicit_value(true)
|
||||||
->default_value(false), "skip main menu on game startup")
|
->default_value(false), "skip main menu on game startup")
|
||||||
|
|
||||||
|
@ -241,6 +248,7 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
|
||||||
engine.setScriptConsoleMode (variables["script-console"].as<bool>());
|
engine.setScriptConsoleMode (variables["script-console"].as<bool>());
|
||||||
engine.setStartupScript (variables["script-run"].as<std::string>());
|
engine.setStartupScript (variables["script-run"].as<std::string>());
|
||||||
engine.setActivationDistanceOverride (variables["activate-dist"].as<int>());
|
engine.setActivationDistanceOverride (variables["activate-dist"].as<int>());
|
||||||
|
engine.setWarningsMode (variables["script-warn"].as<int>());
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
|
|
||||||
#include "../mwworld/esmstore.hpp"
|
#include "../mwworld/esmstore.hpp"
|
||||||
|
|
||||||
|
#include <components/esm/loaddial.hpp>
|
||||||
|
|
||||||
#include <components/compiler/locals.hpp>
|
#include <components/compiler/locals.hpp>
|
||||||
|
|
||||||
#include "../mwbase/environment.hpp"
|
#include "../mwbase/environment.hpp"
|
||||||
|
@ -28,16 +30,32 @@ namespace MWScript
|
||||||
return MWBase::Environment::get().getWorld()->getGlobalVariableType (name);
|
return MWBase::Environment::get().getWorld()->getGlobalVariableType (name);
|
||||||
}
|
}
|
||||||
|
|
||||||
char CompilerContext::getMemberType (const std::string& name, const std::string& id) const
|
std::pair<char, bool> CompilerContext::getMemberType (const std::string& name,
|
||||||
|
const std::string& id) const
|
||||||
|
{
|
||||||
|
std::string script;
|
||||||
|
bool reference = false;
|
||||||
|
|
||||||
|
if (const ESM::Script *scriptRecord =
|
||||||
|
MWBase::Environment::get().getWorld()->getStore().get<ESM::Script>().search (id))
|
||||||
|
{
|
||||||
|
script = scriptRecord->mId;
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getPtr (id, false);
|
MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getPtr (id, false);
|
||||||
|
|
||||||
std::string script = MWWorld::Class::get (ptr).getScript (ptr);
|
script = MWWorld::Class::get (ptr).getScript (ptr);
|
||||||
|
reference = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (script.empty())
|
char type = ' ';
|
||||||
return ' ';
|
|
||||||
|
|
||||||
return MWBase::Environment::get().getScriptManager()->getLocals (script).getType (name);
|
if (!script.empty())
|
||||||
|
type = MWBase::Environment::get().getScriptManager()->getLocals (script).getType (
|
||||||
|
Misc::StringUtils::lowerCase (name));
|
||||||
|
|
||||||
|
return std::make_pair (type, reference);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CompilerContext::isId (const std::string& name) const
|
bool CompilerContext::isId (const std::string& name) const
|
||||||
|
@ -67,4 +85,14 @@ namespace MWScript
|
||||||
store.get<ESM::Static>().search (name) ||
|
store.get<ESM::Static>().search (name) ||
|
||||||
store.get<ESM::Weapon>().search (name);
|
store.get<ESM::Weapon>().search (name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CompilerContext::isJournalId (const std::string& name) const
|
||||||
|
{
|
||||||
|
const MWWorld::ESMStore &store =
|
||||||
|
MWBase::Environment::get().getWorld()->getStore();
|
||||||
|
|
||||||
|
const ESM::Dialogue *topic = store.get<ESM::Dialogue>().search (name);
|
||||||
|
|
||||||
|
return topic && topic->mType==ESM::Dialogue::Journal;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,11 +30,18 @@ namespace MWScript
|
||||||
/// 'l: long, 's': short, 'f': float, ' ': does not exist.
|
/// 'l: long, 's': short, 'f': float, ' ': does not exist.
|
||||||
virtual char getGlobalType (const std::string& name) const;
|
virtual char getGlobalType (const std::string& name) const;
|
||||||
|
|
||||||
virtual char getMemberType (const std::string& name, const std::string& id) const;
|
virtual std::pair<char, bool> getMemberType (const std::string& name,
|
||||||
///< 'l: long, 's': short, 'f': float, ' ': does not exist.
|
const std::string& id) const;
|
||||||
|
///< Return type of member variable \a name in script \a id or in script of reference of
|
||||||
|
/// \a id
|
||||||
|
/// \return first: 'l: long, 's': short, 'f': float, ' ': does not exist.
|
||||||
|
/// second: true: script of reference
|
||||||
|
|
||||||
virtual bool isId (const std::string& name) const;
|
virtual bool isId (const std::string& name) const;
|
||||||
///< Does \a name match an ID, that can be referenced?
|
///< Does \a name match an ID, that can be referenced?
|
||||||
|
|
||||||
|
virtual bool isJournalId (const std::string& name) const;
|
||||||
|
///< Does \a name match a journal ID?
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -52,7 +52,9 @@ op 0x20023: AiFollow, explicit reference
|
||||||
op 0x20024: AiFollowCell
|
op 0x20024: AiFollowCell
|
||||||
op 0x20025: AiFollowCell, explicit reference
|
op 0x20025: AiFollowCell, explicit reference
|
||||||
op 0x20026: ModRegion
|
op 0x20026: ModRegion
|
||||||
opcodes 0x20027-0x3ffff unused
|
op 0x20027: RemoveSoulGem
|
||||||
|
op 0x20028: RemoveSoulGem, explicit reference
|
||||||
|
opcodes 0x20029-0x3ffff unused
|
||||||
|
|
||||||
Segment 4:
|
Segment 4:
|
||||||
(not implemented yet)
|
(not implemented yet)
|
||||||
|
@ -308,8 +310,8 @@ op 0x20001f1: GetDetected
|
||||||
op 0x20001f2: GetDetected, explicit reference
|
op 0x20001f2: GetDetected, explicit reference
|
||||||
op 0x20001f3: AddSoulGem
|
op 0x20001f3: AddSoulGem
|
||||||
op 0x20001f4: AddSoulGem, explicit reference
|
op 0x20001f4: AddSoulGem, explicit reference
|
||||||
op 0x20001f5: RemoveSoulGem
|
op 0x20001f5: unused
|
||||||
op 0x20001f6: RemoveSoulGem, explicit reference
|
op 0x20001f6: unused
|
||||||
op 0x20001f7: PlayBink
|
op 0x20001f7: PlayBink
|
||||||
op 0x20001f8: Drop
|
op 0x20001f8: Drop
|
||||||
op 0x20001f9: Drop, explicit reference
|
op 0x20001f9: Drop, explicit reference
|
||||||
|
@ -381,5 +383,7 @@ op 0x200023a: StartCombat
|
||||||
op 0x200023b: StartCombatExplicit
|
op 0x200023b: StartCombatExplicit
|
||||||
op 0x200023c: StopCombat
|
op 0x200023c: StopCombat
|
||||||
op 0x200023d: StopCombatExplicit
|
op 0x200023d: StopCombatExplicit
|
||||||
|
op 0x200023e: GetPcInJail
|
||||||
|
op 0x200023f: GetPcTraveling
|
||||||
|
|
||||||
opcodes 0x200023e-0x3ffffff unused
|
opcodes 0x2000240-0x3ffffff unused
|
||||||
|
|
|
@ -148,4 +148,25 @@ namespace MWScript
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Locals& GlobalScripts::getLocals (const std::string& name)
|
||||||
|
{
|
||||||
|
std::string name2 = Misc::StringUtils::lowerCase (name);
|
||||||
|
std::map<std::string, std::pair<bool, Locals> >::iterator iter =
|
||||||
|
mScripts.find (name2);
|
||||||
|
|
||||||
|
if (iter==mScripts.end())
|
||||||
|
{
|
||||||
|
if (const ESM::Script *script = mStore.get<ESM::Script>().find (name))
|
||||||
|
{
|
||||||
|
Locals locals;
|
||||||
|
|
||||||
|
locals.configure (*script);
|
||||||
|
|
||||||
|
iter = mScripts.insert (std::make_pair (name, std::make_pair (false, locals))).first;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return iter->second.second;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,6 +52,10 @@ namespace MWScript
|
||||||
///< Records for variables that do not exist are dropped silently.
|
///< Records for variables that do not exist are dropped silently.
|
||||||
///
|
///
|
||||||
/// \return Known type?
|
/// \return Known type?
|
||||||
|
|
||||||
|
Locals& getLocals (const std::string& name);
|
||||||
|
///< If the script \a name has not been added as a global script yet, it is added
|
||||||
|
/// automatically, but is not set to running state.
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -54,6 +54,47 @@ namespace MWScript
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Locals& InterpreterContext::getMemberLocals (std::string& id, bool global)
|
||||||
|
const
|
||||||
|
{
|
||||||
|
if (global)
|
||||||
|
{
|
||||||
|
return MWBase::Environment::get().getScriptManager()->getGlobalScripts().
|
||||||
|
getLocals (id);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const MWWorld::Ptr ptr = getReference (id, false);
|
||||||
|
|
||||||
|
id = MWWorld::Class::get (ptr).getScript (ptr);
|
||||||
|
|
||||||
|
ptr.getRefData().setLocals (
|
||||||
|
*MWBase::Environment::get().getWorld()->getStore().get<ESM::Script>().find (id));
|
||||||
|
|
||||||
|
return ptr.getRefData().getLocals();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Locals& InterpreterContext::getMemberLocals (std::string& id, bool global)
|
||||||
|
{
|
||||||
|
if (global)
|
||||||
|
{
|
||||||
|
return MWBase::Environment::get().getScriptManager()->getGlobalScripts().
|
||||||
|
getLocals (id);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const MWWorld::Ptr ptr = getReference (id, false);
|
||||||
|
|
||||||
|
id = MWWorld::Class::get (ptr).getScript (ptr);
|
||||||
|
|
||||||
|
ptr.getRefData().setLocals (
|
||||||
|
*MWBase::Environment::get().getWorld()->getStore().get<ESM::Script>().find (id));
|
||||||
|
|
||||||
|
return ptr.getRefData().getLocals();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
InterpreterContext::InterpreterContext (
|
InterpreterContext::InterpreterContext (
|
||||||
MWScript::Locals *locals, MWWorld::Ptr reference)
|
MWScript::Locals *locals, MWWorld::Ptr reference)
|
||||||
: mLocals (locals), mReference (reference),
|
: mLocals (locals), mReference (reference),
|
||||||
|
@ -407,82 +448,80 @@ namespace MWScript
|
||||||
MWBase::Environment::get().getWorld()->disable (ref);
|
MWBase::Environment::get().getWorld()->disable (ref);
|
||||||
}
|
}
|
||||||
|
|
||||||
int InterpreterContext::getMemberShort (const std::string& id, const std::string& name) const
|
int InterpreterContext::getMemberShort (const std::string& id, const std::string& name,
|
||||||
|
bool global) const
|
||||||
{
|
{
|
||||||
const MWWorld::Ptr ptr = getReference (id, false);
|
std::string scriptId (id);
|
||||||
|
|
||||||
std::string scriptId = MWWorld::Class::get (ptr).getScript (ptr);
|
const Locals& locals = getMemberLocals (scriptId, global);
|
||||||
|
|
||||||
int index = MWBase::Environment::get().getScriptManager()->getLocalIndex (scriptId, name, 's');
|
int index = MWBase::Environment::get().getScriptManager()->getLocalIndex (
|
||||||
|
scriptId, name, 's');
|
||||||
|
|
||||||
ptr.getRefData().setLocals (
|
return locals.mShorts[index];
|
||||||
*MWBase::Environment::get().getWorld()->getStore().get<ESM::Script>().find (scriptId));
|
|
||||||
return ptr.getRefData().getLocals().mShorts[index];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int InterpreterContext::getMemberLong (const std::string& id, const std::string& name) const
|
int InterpreterContext::getMemberLong (const std::string& id, const std::string& name,
|
||||||
|
bool global) const
|
||||||
{
|
{
|
||||||
const MWWorld::Ptr ptr = getReference (id, false);
|
std::string scriptId (id);
|
||||||
|
|
||||||
std::string scriptId = MWWorld::Class::get (ptr).getScript (ptr);
|
const Locals& locals = getMemberLocals (scriptId, global);
|
||||||
|
|
||||||
int index = MWBase::Environment::get().getScriptManager()->getLocalIndex (scriptId, name, 'l');
|
int index = MWBase::Environment::get().getScriptManager()->getLocalIndex (
|
||||||
|
scriptId, name, 'l');
|
||||||
|
|
||||||
ptr.getRefData().setLocals (
|
return locals.mLongs[index];
|
||||||
*MWBase::Environment::get().getWorld()->getStore().get<ESM::Script>().find (scriptId));
|
|
||||||
return ptr.getRefData().getLocals().mLongs[index];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
float InterpreterContext::getMemberFloat (const std::string& id, const std::string& name) const
|
float InterpreterContext::getMemberFloat (const std::string& id, const std::string& name,
|
||||||
|
bool global) const
|
||||||
{
|
{
|
||||||
const MWWorld::Ptr ptr = getReference (id, false);
|
std::string scriptId (id);
|
||||||
|
|
||||||
std::string scriptId = MWWorld::Class::get (ptr).getScript (ptr);
|
const Locals& locals = getMemberLocals (scriptId, global);
|
||||||
|
|
||||||
int index = MWBase::Environment::get().getScriptManager()->getLocalIndex (scriptId, name, 'f');
|
int index = MWBase::Environment::get().getScriptManager()->getLocalIndex (
|
||||||
|
scriptId, name, 'f');
|
||||||
|
|
||||||
ptr.getRefData().setLocals (
|
return locals.mFloats[index];
|
||||||
*MWBase::Environment::get().getWorld()->getStore().get<ESM::Script>().find (scriptId));
|
|
||||||
return ptr.getRefData().getLocals().mFloats[index];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void InterpreterContext::setMemberShort (const std::string& id, const std::string& name, int value)
|
void InterpreterContext::setMemberShort (const std::string& id, const std::string& name,
|
||||||
|
int value, bool global)
|
||||||
{
|
{
|
||||||
const MWWorld::Ptr ptr = getReference (id, false);
|
std::string scriptId (id);
|
||||||
|
|
||||||
std::string scriptId = MWWorld::Class::get (ptr).getScript (ptr);
|
Locals& locals = getMemberLocals (scriptId, global);
|
||||||
|
|
||||||
int index = MWBase::Environment::get().getScriptManager()->getLocalIndex (scriptId, name, 's');
|
int index =
|
||||||
|
MWBase::Environment::get().getScriptManager()->getLocalIndex (scriptId, name, 's');
|
||||||
|
|
||||||
ptr.getRefData().setLocals (
|
locals.mShorts[index] = value;
|
||||||
*MWBase::Environment::get().getWorld()->getStore().get<ESM::Script>().find (scriptId));
|
|
||||||
ptr.getRefData().getLocals().mShorts[index] = value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void InterpreterContext::setMemberLong (const std::string& id, const std::string& name, int value)
|
void InterpreterContext::setMemberLong (const std::string& id, const std::string& name, int value, bool global)
|
||||||
{
|
{
|
||||||
const MWWorld::Ptr ptr = getReference (id, false);
|
std::string scriptId (id);
|
||||||
|
|
||||||
std::string scriptId = MWWorld::Class::get (ptr).getScript (ptr);
|
Locals& locals = getMemberLocals (scriptId, global);
|
||||||
|
|
||||||
int index = MWBase::Environment::get().getScriptManager()->getLocalIndex (scriptId, name, 'l');
|
int index =
|
||||||
|
MWBase::Environment::get().getScriptManager()->getLocalIndex (scriptId, name, 'l');
|
||||||
|
|
||||||
ptr.getRefData().setLocals (
|
locals.mLongs[index] = value;
|
||||||
*MWBase::Environment::get().getWorld()->getStore().get<ESM::Script>().find (scriptId));
|
|
||||||
ptr.getRefData().getLocals().mLongs[index] = value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void InterpreterContext::setMemberFloat (const std::string& id, const std::string& name, float value)
|
void InterpreterContext::setMemberFloat (const std::string& id, const std::string& name, float value, bool global)
|
||||||
{
|
{
|
||||||
const MWWorld::Ptr ptr = getReference (id, false);
|
std::string scriptId (id);
|
||||||
|
|
||||||
std::string scriptId = MWWorld::Class::get (ptr).getScript (ptr);
|
Locals& locals = getMemberLocals (scriptId, global);
|
||||||
|
|
||||||
int index = MWBase::Environment::get().getScriptManager()->getLocalIndex (scriptId, name, 'f');
|
int index =
|
||||||
|
MWBase::Environment::get().getScriptManager()->getLocalIndex (scriptId, name, 'f');
|
||||||
|
|
||||||
ptr.getRefData().setLocals (
|
locals.mFloats[index] = value;
|
||||||
*MWBase::Environment::get().getWorld()->getStore().get<ESM::Script>().find (scriptId));
|
|
||||||
ptr.getRefData().getLocals().mFloats[index] = value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MWWorld::Ptr InterpreterContext::getReference(bool required)
|
MWWorld::Ptr InterpreterContext::getReference(bool required)
|
||||||
|
|
|
@ -37,6 +37,12 @@ namespace MWScript
|
||||||
|
|
||||||
const MWWorld::Ptr getReference (const std::string& id, bool activeOnly, bool doThrow=true) const;
|
const MWWorld::Ptr getReference (const std::string& id, bool activeOnly, bool doThrow=true) const;
|
||||||
|
|
||||||
|
const Locals& getMemberLocals (std::string& id, bool global) const;
|
||||||
|
///< \a id is changed to the respective script ID, if \a id wasn't a script ID before
|
||||||
|
|
||||||
|
Locals& getMemberLocals (std::string& id, bool global);
|
||||||
|
///< \a id is changed to the respective script ID, if \a id wasn't a script ID before
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
InterpreterContext (MWScript::Locals *locals, MWWorld::Ptr reference);
|
InterpreterContext (MWScript::Locals *locals, MWWorld::Ptr reference);
|
||||||
|
@ -138,17 +144,17 @@ namespace MWScript
|
||||||
|
|
||||||
virtual void disable (const std::string& id = "");
|
virtual void disable (const std::string& id = "");
|
||||||
|
|
||||||
virtual int getMemberShort (const std::string& id, const std::string& name) const;
|
virtual int getMemberShort (const std::string& id, const std::string& name, bool global) const;
|
||||||
|
|
||||||
virtual int getMemberLong (const std::string& id, const std::string& name) const;
|
virtual int getMemberLong (const std::string& id, const std::string& name, bool global) const;
|
||||||
|
|
||||||
virtual float getMemberFloat (const std::string& id, const std::string& name) const;
|
virtual float getMemberFloat (const std::string& id, const std::string& name, bool global) const;
|
||||||
|
|
||||||
virtual void setMemberShort (const std::string& id, const std::string& name, int value);
|
virtual void setMemberShort (const std::string& id, const std::string& name, int value, bool global);
|
||||||
|
|
||||||
virtual void setMemberLong (const std::string& id, const std::string& name, int value);
|
virtual void setMemberLong (const std::string& id, const std::string& name, int value, bool global);
|
||||||
|
|
||||||
virtual void setMemberFloat (const std::string& id, const std::string& name, float value);
|
virtual void setMemberFloat (const std::string& id, const std::string& name, float value, bool global);
|
||||||
|
|
||||||
MWWorld::Ptr getReference(bool required=true);
|
MWWorld::Ptr getReference(bool required=true);
|
||||||
///< Reference, that the script is running from (can be empty)
|
///< Reference, that the script is running from (can be empty)
|
||||||
|
|
|
@ -365,17 +365,21 @@ namespace MWScript
|
||||||
};
|
};
|
||||||
|
|
||||||
template<class R>
|
template<class R>
|
||||||
class OpRemoveSoulGem : public Interpreter::Opcode0
|
class OpRemoveSoulGem : public Interpreter::Opcode1
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
virtual void execute (Interpreter::Runtime& runtime)
|
virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0)
|
||||||
{
|
{
|
||||||
MWWorld::Ptr ptr = R()(runtime);
|
MWWorld::Ptr ptr = R()(runtime);
|
||||||
|
|
||||||
std::string soul = runtime.getStringLiteral (runtime[0].mInteger);
|
std::string soul = runtime.getStringLiteral (runtime[0].mInteger);
|
||||||
runtime.pop();
|
runtime.pop();
|
||||||
|
|
||||||
|
// throw away additional arguments
|
||||||
|
for (unsigned int i=0; i<arg0; ++i)
|
||||||
|
runtime.pop();
|
||||||
|
|
||||||
MWWorld::ContainerStore& store = MWWorld::Class::get (ptr).getContainerStore (ptr);
|
MWWorld::ContainerStore& store = MWWorld::Class::get (ptr).getContainerStore (ptr);
|
||||||
for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)
|
for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)
|
||||||
{
|
{
|
||||||
|
@ -818,6 +822,28 @@ namespace MWScript
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class OpGetPcInJail : public Interpreter::Opcode0
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
virtual void execute (Interpreter::Runtime &runtime)
|
||||||
|
{
|
||||||
|
/// \todo implement jail check
|
||||||
|
runtime.push (0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class OpGetPcTraveling : public Interpreter::Opcode0
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
virtual void execute (Interpreter::Runtime &runtime)
|
||||||
|
{
|
||||||
|
/// \todo implement traveling check
|
||||||
|
runtime.push (0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
void installOpcodes (Interpreter::Interpreter& interpreter)
|
void installOpcodes (Interpreter::Interpreter& interpreter)
|
||||||
{
|
{
|
||||||
interpreter.installSegment5 (Compiler::Misc::opcodeXBox, new OpXBox);
|
interpreter.installSegment5 (Compiler::Misc::opcodeXBox, new OpXBox);
|
||||||
|
@ -850,8 +876,8 @@ namespace MWScript
|
||||||
interpreter.installSegment5 (Compiler::Misc::opcodeGetEffectExplicit, new OpGetEffect<ExplicitRef>);
|
interpreter.installSegment5 (Compiler::Misc::opcodeGetEffectExplicit, new OpGetEffect<ExplicitRef>);
|
||||||
interpreter.installSegment5 (Compiler::Misc::opcodeAddSoulGem, new OpAddSoulGem<ImplicitRef>);
|
interpreter.installSegment5 (Compiler::Misc::opcodeAddSoulGem, new OpAddSoulGem<ImplicitRef>);
|
||||||
interpreter.installSegment5 (Compiler::Misc::opcodeAddSoulGemExplicit, new OpAddSoulGem<ExplicitRef>);
|
interpreter.installSegment5 (Compiler::Misc::opcodeAddSoulGemExplicit, new OpAddSoulGem<ExplicitRef>);
|
||||||
interpreter.installSegment5 (Compiler::Misc::opcodeRemoveSoulGem, new OpRemoveSoulGem<ImplicitRef>);
|
interpreter.installSegment3 (Compiler::Misc::opcodeRemoveSoulGem, new OpRemoveSoulGem<ImplicitRef>);
|
||||||
interpreter.installSegment5 (Compiler::Misc::opcodeRemoveSoulGemExplicit, new OpRemoveSoulGem<ExplicitRef>);
|
interpreter.installSegment3 (Compiler::Misc::opcodeRemoveSoulGemExplicit, new OpRemoveSoulGem<ExplicitRef>);
|
||||||
interpreter.installSegment5 (Compiler::Misc::opcodeDrop, new OpDrop<ImplicitRef>);
|
interpreter.installSegment5 (Compiler::Misc::opcodeDrop, new OpDrop<ImplicitRef>);
|
||||||
interpreter.installSegment5 (Compiler::Misc::opcodeDropExplicit, new OpDrop<ExplicitRef>);
|
interpreter.installSegment5 (Compiler::Misc::opcodeDropExplicit, new OpDrop<ExplicitRef>);
|
||||||
interpreter.installSegment5 (Compiler::Misc::opcodeDropSoulGem, new OpDropSoulGem<ImplicitRef>);
|
interpreter.installSegment5 (Compiler::Misc::opcodeDropSoulGem, new OpDropSoulGem<ImplicitRef>);
|
||||||
|
@ -888,6 +914,8 @@ namespace MWScript
|
||||||
interpreter.installSegment5 (Compiler::Misc::opcodeCastExplicit, new OpCast<ExplicitRef>);
|
interpreter.installSegment5 (Compiler::Misc::opcodeCastExplicit, new OpCast<ExplicitRef>);
|
||||||
interpreter.installSegment5 (Compiler::Misc::opcodeExplodeSpell, new OpExplodeSpell<ImplicitRef>);
|
interpreter.installSegment5 (Compiler::Misc::opcodeExplodeSpell, new OpExplodeSpell<ImplicitRef>);
|
||||||
interpreter.installSegment5 (Compiler::Misc::opcodeExplodeSpellExplicit, new OpExplodeSpell<ExplicitRef>);
|
interpreter.installSegment5 (Compiler::Misc::opcodeExplodeSpellExplicit, new OpExplodeSpell<ExplicitRef>);
|
||||||
|
interpreter.installSegment5 (Compiler::Misc::opcodeGetPcInJail, new OpGetPcInJail);
|
||||||
|
interpreter.installSegment5 (Compiler::Misc::opcodeGetPcTraveling, new OpGetPcTraveling);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,22 +7,28 @@
|
||||||
#include <exception>
|
#include <exception>
|
||||||
|
|
||||||
#include <components/esm/loadscpt.hpp>
|
#include <components/esm/loadscpt.hpp>
|
||||||
#include "../mwworld/esmstore.hpp"
|
|
||||||
|
#include <components/misc/stringops.hpp>
|
||||||
|
|
||||||
#include <components/compiler/scanner.hpp>
|
#include <components/compiler/scanner.hpp>
|
||||||
#include <components/compiler/context.hpp>
|
#include <components/compiler/context.hpp>
|
||||||
#include <components/compiler/exception.hpp>
|
#include <components/compiler/exception.hpp>
|
||||||
|
#include <components/compiler/quickfileparser.hpp>
|
||||||
|
|
||||||
|
#include "../mwworld/esmstore.hpp"
|
||||||
|
|
||||||
#include "extensions.hpp"
|
#include "extensions.hpp"
|
||||||
|
|
||||||
namespace MWScript
|
namespace MWScript
|
||||||
{
|
{
|
||||||
ScriptManager::ScriptManager (const MWWorld::ESMStore& store, bool verbose,
|
ScriptManager::ScriptManager (const MWWorld::ESMStore& store, bool verbose,
|
||||||
Compiler::Context& compilerContext)
|
Compiler::Context& compilerContext, int warningsMode)
|
||||||
: mErrorHandler (std::cerr), mStore (store), mVerbose (verbose),
|
: mErrorHandler (std::cerr), mStore (store), mVerbose (verbose),
|
||||||
mCompilerContext (compilerContext), mParser (mErrorHandler, mCompilerContext),
|
mCompilerContext (compilerContext), mParser (mErrorHandler, mCompilerContext),
|
||||||
mOpcodesInstalled (false), mGlobalScripts (store)
|
mOpcodesInstalled (false), mGlobalScripts (store)
|
||||||
{}
|
{
|
||||||
|
mErrorHandler.setWarningsMode (warningsMode);
|
||||||
|
}
|
||||||
|
|
||||||
bool ScriptManager::compile (const std::string& name)
|
bool ScriptManager::compile (const std::string& name)
|
||||||
{
|
{
|
||||||
|
@ -138,37 +144,33 @@ namespace MWScript
|
||||||
|
|
||||||
Compiler::Locals& ScriptManager::getLocals (const std::string& name)
|
Compiler::Locals& ScriptManager::getLocals (const std::string& name)
|
||||||
{
|
{
|
||||||
|
std::string name2 = Misc::StringUtils::lowerCase (name);
|
||||||
|
|
||||||
{
|
{
|
||||||
ScriptCollection::iterator iter = mScripts.find (name);
|
ScriptCollection::iterator iter = mScripts.find (name2);
|
||||||
|
|
||||||
if (iter!=mScripts.end())
|
if (iter!=mScripts.end())
|
||||||
return iter->second.second;
|
return iter->second.second;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
std::map<std::string, Compiler::Locals>::iterator iter = mOtherLocals.find (name);
|
std::map<std::string, Compiler::Locals>::iterator iter = mOtherLocals.find (name2);
|
||||||
|
|
||||||
if (iter!=mOtherLocals.end())
|
if (iter!=mOtherLocals.end())
|
||||||
return iter->second;
|
return iter->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (const ESM::Script *script = mStore.get<ESM::Script>().find (name2))
|
||||||
|
{
|
||||||
Compiler::Locals locals;
|
Compiler::Locals locals;
|
||||||
|
|
||||||
if (const ESM::Script *script = mStore.get<ESM::Script>().find (name))
|
std::istringstream stream (script->mScriptText);
|
||||||
{
|
Compiler::QuickFileParser parser (mErrorHandler, mCompilerContext, locals);
|
||||||
int index = 0;
|
Compiler::Scanner scanner (mErrorHandler, stream, mCompilerContext.getExtensions());
|
||||||
|
scanner.scan (parser);
|
||||||
for (int i=0; i<script->mData.mNumShorts; ++i)
|
|
||||||
locals.declare ('s', script->mVarNames[index++]);
|
|
||||||
|
|
||||||
for (int i=0; i<script->mData.mNumLongs; ++i)
|
|
||||||
locals.declare ('l', script->mVarNames[index++]);
|
|
||||||
|
|
||||||
for (int i=0; i<script->mData.mNumFloats; ++i)
|
|
||||||
locals.declare ('f', script->mVarNames[index++]);
|
|
||||||
|
|
||||||
std::map<std::string, Compiler::Locals>::iterator iter =
|
std::map<std::string, Compiler::Locals>::iterator iter =
|
||||||
mOtherLocals.insert (std::make_pair (name, locals)).first;
|
mOtherLocals.insert (std::make_pair (name2, locals)).first;
|
||||||
|
|
||||||
return iter->second;
|
return iter->second;
|
||||||
}
|
}
|
||||||
|
@ -214,8 +216,10 @@ namespace MWScript
|
||||||
throw std::runtime_error ("invalid variable type");
|
throw std::runtime_error ("invalid variable type");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string variable2 = Misc::StringUtils::lowerCase (variable);
|
||||||
|
|
||||||
for (int i=0; i<size; ++i)
|
for (int i=0; i<size; ++i)
|
||||||
if (script->mVarNames.at (i+offset)==variable)
|
if (Misc::StringUtils::lowerCase (script->mVarNames.at (i+offset))==variable2)
|
||||||
return i;
|
return i;
|
||||||
|
|
||||||
throw std::runtime_error ("unable to access local variable " + variable + " of " + scriptId);
|
throw std::runtime_error ("unable to access local variable " + variable + " of " + scriptId);
|
||||||
|
|
|
@ -52,7 +52,7 @@ namespace MWScript
|
||||||
public:
|
public:
|
||||||
|
|
||||||
ScriptManager (const MWWorld::ESMStore& store, bool verbose,
|
ScriptManager (const MWWorld::ESMStore& store, bool verbose,
|
||||||
Compiler::Context& compilerContext);
|
Compiler::Context& compilerContext, int warningsMode);
|
||||||
|
|
||||||
virtual void run (const std::string& name, Interpreter::Context& interpreterContext);
|
virtual void run (const std::string& name, Interpreter::Context& interpreterContext);
|
||||||
///< Run the script with the given name (compile first, if not compiled yet)
|
///< Run the script with the given name (compile first, if not compiled yet)
|
||||||
|
|
|
@ -59,7 +59,8 @@ add_component_dir (files
|
||||||
add_component_dir (compiler
|
add_component_dir (compiler
|
||||||
context controlparser errorhandler exception exprparser extensions fileparser generator
|
context controlparser errorhandler exception exprparser extensions fileparser generator
|
||||||
lineparser literals locals output parser scanner scriptparser skipparser streamerrorhandler
|
lineparser literals locals output parser scanner scriptparser skipparser streamerrorhandler
|
||||||
stringparser tokenloc nullerrorhandler opcodes extensions0
|
stringparser tokenloc nullerrorhandler opcodes extensions0 declarationparser
|
||||||
|
quickfileparser
|
||||||
)
|
)
|
||||||
|
|
||||||
add_component_dir (interpreter
|
add_component_dir (interpreter
|
||||||
|
|
|
@ -33,11 +33,18 @@ namespace Compiler
|
||||||
virtual char getGlobalType (const std::string& name) const = 0;
|
virtual char getGlobalType (const std::string& name) const = 0;
|
||||||
///< 'l: long, 's': short, 'f': float, ' ': does not exist.
|
///< 'l: long, 's': short, 'f': float, ' ': does not exist.
|
||||||
|
|
||||||
virtual char getMemberType (const std::string& name, const std::string& id) const = 0;
|
virtual std::pair<char, bool> getMemberType (const std::string& name,
|
||||||
///< 'l: long, 's': short, 'f': float, ' ': does not exist.
|
const std::string& id) const = 0;
|
||||||
|
///< Return type of member variable \a name in script \a id or in script of reference of
|
||||||
|
/// \a id
|
||||||
|
/// \return first: 'l: long, 's': short, 'f': float, ' ': does not exist.
|
||||||
|
/// second: true: script of reference
|
||||||
|
|
||||||
virtual bool isId (const std::string& name) const = 0;
|
virtual bool isId (const std::string& name) const = 0;
|
||||||
///< Does \a name match an ID, that can be referenced?
|
///< Does \a name match an ID, that can be referenced?
|
||||||
|
|
||||||
|
virtual bool isJournalId (const std::string& name) const = 0;
|
||||||
|
///< Does \a name match a journal ID?
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
|
|
||||||
#include "scanner.hpp"
|
#include "scanner.hpp"
|
||||||
#include "generator.hpp"
|
#include "generator.hpp"
|
||||||
|
#include "errorhandler.hpp"
|
||||||
|
#include "skipparser.hpp"
|
||||||
|
|
||||||
namespace Compiler
|
namespace Compiler
|
||||||
{
|
{
|
||||||
|
@ -70,7 +72,7 @@ namespace Compiler
|
||||||
}
|
}
|
||||||
else if (keyword==Scanner::K_else)
|
else if (keyword==Scanner::K_else)
|
||||||
{
|
{
|
||||||
mState = IfElseEndState;
|
mState = IfElseJunkState; /// \todo should be IfElseEndState; add an option for that
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -106,7 +108,7 @@ namespace Compiler
|
||||||
Codes expr;
|
Codes expr;
|
||||||
mExprParser.append (expr);
|
mExprParser.append (expr);
|
||||||
|
|
||||||
Generator::jump (loop, -static_cast<int> (mCodeBlock.size()-expr.size()));
|
Generator::jump (loop, -static_cast<int> (mCodeBlock.size()+expr.size()));
|
||||||
|
|
||||||
std::copy (expr.begin(), expr.end(), std::back_inserter (mCode));
|
std::copy (expr.begin(), expr.end(), std::back_inserter (mCode));
|
||||||
|
|
||||||
|
@ -120,7 +122,7 @@ namespace Compiler
|
||||||
|
|
||||||
Codes loop2;
|
Codes loop2;
|
||||||
|
|
||||||
Generator::jump (loop2, -static_cast<int> (mCodeBlock.size()-expr.size()-skip.size()));
|
Generator::jump (loop2, -static_cast<int> (mCodeBlock.size()+expr.size()+skip.size()));
|
||||||
|
|
||||||
if (loop.size()!=loop2.size())
|
if (loop.size()!=loop2.size())
|
||||||
throw std::logic_error (
|
throw std::logic_error (
|
||||||
|
@ -153,7 +155,7 @@ namespace Compiler
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ControlParser::ControlParser (ErrorHandler& errorHandler, Context& context, Locals& locals,
|
ControlParser::ControlParser (ErrorHandler& errorHandler, const Context& context, Locals& locals,
|
||||||
Literals& literals)
|
Literals& literals)
|
||||||
: Parser (errorHandler, context), mLocals (locals), mLiterals (literals),
|
: Parser (errorHandler, context), mLocals (locals), mLiterals (literals),
|
||||||
mLineParser (errorHandler, context, locals, literals, mCodeBlock),
|
mLineParser (errorHandler, context, locals, literals, mCodeBlock),
|
||||||
|
@ -186,8 +188,11 @@ namespace Compiler
|
||||||
{
|
{
|
||||||
if (mState==StartState)
|
if (mState==StartState)
|
||||||
{
|
{
|
||||||
if (keyword==Scanner::K_if)
|
if (keyword==Scanner::K_if || keyword==Scanner::K_elseif)
|
||||||
{
|
{
|
||||||
|
if (keyword==Scanner::K_elseif)
|
||||||
|
getErrorHandler().warning ("elseif without matching if", loc);
|
||||||
|
|
||||||
mExprParser.reset();
|
mExprParser.reset();
|
||||||
scanner.scan (mExprParser);
|
scanner.scan (mExprParser);
|
||||||
|
|
||||||
|
@ -203,7 +208,8 @@ namespace Compiler
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (mState==IfBodyState || mState==IfElseifBodyState || mState==IfElseBodyState)
|
else if (mState==IfBodyState || mState==IfElseifBodyState || mState==IfElseBodyState ||
|
||||||
|
mState==IfElseJunkState)
|
||||||
{
|
{
|
||||||
if (parseIfBody (keyword, loc, scanner))
|
if (parseIfBody (keyword, loc, scanner))
|
||||||
return true;
|
return true;
|
||||||
|
@ -226,6 +232,7 @@ namespace Compiler
|
||||||
case IfEndState: mState = IfBodyState; return true;
|
case IfEndState: mState = IfBodyState; return true;
|
||||||
case IfElseifEndState: mState = IfElseifBodyState; return true;
|
case IfElseifEndState: mState = IfElseifBodyState; return true;
|
||||||
case IfElseEndState: mState = IfElseBodyState; return true;
|
case IfElseEndState: mState = IfElseBodyState; return true;
|
||||||
|
case IfElseJunkState: mState = IfElseBodyState; return true;
|
||||||
|
|
||||||
case WhileEndState: mState = WhileBodyState; return true;
|
case WhileEndState: mState = WhileBodyState; return true;
|
||||||
|
|
||||||
|
@ -243,7 +250,13 @@ namespace Compiler
|
||||||
|
|
||||||
default: ;
|
default: ;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else if (code==Scanner::S_open && mState==IfElseJunkState)
|
||||||
|
{
|
||||||
|
SkipParser skip (getErrorHandler(), getContext());
|
||||||
|
scanner.scan (skip);
|
||||||
|
mState = IfElseBodyState;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Parser::parseSpecial (code, loc, scanner);
|
return Parser::parseSpecial (code, loc, scanner);
|
||||||
|
|
|
@ -26,7 +26,8 @@ namespace Compiler
|
||||||
IfElseEndState, IfElseBodyState,
|
IfElseEndState, IfElseBodyState,
|
||||||
IfEndifState,
|
IfEndifState,
|
||||||
WhileEndState, WhileBodyState,
|
WhileEndState, WhileBodyState,
|
||||||
WhileEndwhileState
|
WhileEndwhileState,
|
||||||
|
IfElseJunkState
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::vector<Interpreter::Type_Code> Codes;
|
typedef std::vector<Interpreter::Type_Code> Codes;
|
||||||
|
@ -47,7 +48,7 @@ namespace Compiler
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
ControlParser (ErrorHandler& errorHandler, Context& context, Locals& locals,
|
ControlParser (ErrorHandler& errorHandler, const Context& context, Locals& locals,
|
||||||
Literals& literals);
|
Literals& literals);
|
||||||
|
|
||||||
void appendCode (std::vector<Interpreter::Type_Code>& code) const;
|
void appendCode (std::vector<Interpreter::Type_Code>& code) const;
|
||||||
|
|
83
components/compiler/declarationparser.cpp
Normal file
83
components/compiler/declarationparser.cpp
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
|
||||||
|
#include "declarationparser.hpp"
|
||||||
|
|
||||||
|
#include <components/misc/stringops.hpp>
|
||||||
|
|
||||||
|
#include "scanner.hpp"
|
||||||
|
#include "errorhandler.hpp"
|
||||||
|
#include "skipparser.hpp"
|
||||||
|
#include "locals.hpp"
|
||||||
|
|
||||||
|
Compiler::DeclarationParser::DeclarationParser (ErrorHandler& errorHandler, const Context& context,
|
||||||
|
Locals& locals)
|
||||||
|
: Parser (errorHandler, context), mLocals (locals), mState (State_Begin), mType (0)
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool Compiler::DeclarationParser::parseName (const std::string& name, const TokenLoc& loc,
|
||||||
|
Scanner& scanner)
|
||||||
|
{
|
||||||
|
if (mState==State_Name)
|
||||||
|
{
|
||||||
|
std::string name2 = Misc::StringUtils::lowerCase (name);
|
||||||
|
|
||||||
|
char type = mLocals.getType (name2);
|
||||||
|
|
||||||
|
if (type!=' ')
|
||||||
|
{
|
||||||
|
/// \todo add option to make re-declared local variables an error
|
||||||
|
getErrorHandler().warning ("can't re-declare local variable (ignoring declaration)",
|
||||||
|
loc);
|
||||||
|
|
||||||
|
mState = State_End;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
mLocals.declare (mType, name2);
|
||||||
|
|
||||||
|
mState = State_End;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Parser::parseName (name, loc, scanner);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Compiler::DeclarationParser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner)
|
||||||
|
{
|
||||||
|
if (mState==State_Begin)
|
||||||
|
{
|
||||||
|
switch (keyword)
|
||||||
|
{
|
||||||
|
case Scanner::K_short: mType = 's'; break;
|
||||||
|
case Scanner::K_long: mType = 'l'; break;
|
||||||
|
case Scanner::K_float: mType = 'f'; break;
|
||||||
|
default: mType = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mType)
|
||||||
|
{
|
||||||
|
mState = State_Name;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (mState==State_Name)
|
||||||
|
{
|
||||||
|
// allow keywords to be used as local variable names. MW script compiler, you suck!
|
||||||
|
/// \todo option to disable this atrocity.
|
||||||
|
return parseName (loc.mLiteral, loc, scanner);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Parser::parseKeyword (keyword, loc, scanner);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Compiler::DeclarationParser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner)
|
||||||
|
{
|
||||||
|
if (code==Scanner::S_newline && mState==State_End)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return Parser::parseSpecial (code, loc, scanner);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Compiler::DeclarationParser::reset()
|
||||||
|
{
|
||||||
|
mState = State_Begin;
|
||||||
|
}
|
43
components/compiler/declarationparser.hpp
Normal file
43
components/compiler/declarationparser.hpp
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
#ifndef COMPILER_DECLARATIONPARSER_H_INCLUDED
|
||||||
|
#define COMPILER_DECLARATIONPARSER_H_INCLUDED
|
||||||
|
|
||||||
|
#include "parser.hpp"
|
||||||
|
|
||||||
|
namespace Compiler
|
||||||
|
{
|
||||||
|
class Locals;
|
||||||
|
|
||||||
|
class DeclarationParser : public Parser
|
||||||
|
{
|
||||||
|
enum State
|
||||||
|
{
|
||||||
|
State_Begin, State_Name, State_End
|
||||||
|
};
|
||||||
|
|
||||||
|
Locals& mLocals;
|
||||||
|
State mState;
|
||||||
|
char mType;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
DeclarationParser (ErrorHandler& errorHandler, const Context& context, Locals& locals);
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -5,7 +5,7 @@ namespace Compiler
|
||||||
{
|
{
|
||||||
// constructor
|
// constructor
|
||||||
|
|
||||||
ErrorHandler::ErrorHandler() : mWarnings (0), mErrors (0) {}
|
ErrorHandler::ErrorHandler() : mWarnings (0), mErrors (0), mWarningsMode (1) {}
|
||||||
|
|
||||||
// destructor
|
// destructor
|
||||||
|
|
||||||
|
@ -35,10 +35,15 @@ namespace Compiler
|
||||||
// Generate a warning message.
|
// Generate a warning message.
|
||||||
|
|
||||||
void ErrorHandler::warning (const std::string& message, const TokenLoc& loc)
|
void ErrorHandler::warning (const std::string& message, const TokenLoc& loc)
|
||||||
|
{
|
||||||
|
if (mWarningsMode==1)
|
||||||
{
|
{
|
||||||
++mWarnings;
|
++mWarnings;
|
||||||
report (message, loc, WarningMessage);
|
report (message, loc, WarningMessage);
|
||||||
}
|
}
|
||||||
|
else if (mWarningsMode==2)
|
||||||
|
error (message, loc);
|
||||||
|
}
|
||||||
|
|
||||||
// Generate an error message.
|
// Generate an error message.
|
||||||
|
|
||||||
|
@ -62,4 +67,9 @@ namespace Compiler
|
||||||
{
|
{
|
||||||
mErrors = mWarnings = 0;
|
mErrors = mWarnings = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ErrorHandler::setWarningsMode (int mode)
|
||||||
|
{
|
||||||
|
mWarningsMode = mode;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ namespace Compiler
|
||||||
{
|
{
|
||||||
int mWarnings;
|
int mWarnings;
|
||||||
int mErrors;
|
int mErrors;
|
||||||
|
int mWarningsMode;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
|
@ -62,6 +63,9 @@ namespace Compiler
|
||||||
|
|
||||||
virtual void reset();
|
virtual void reset();
|
||||||
///< Remove all previous error/warning events
|
///< Remove all previous error/warning events
|
||||||
|
|
||||||
|
void setWarningsMode (int mode);
|
||||||
|
///< // 0 ignore, 1 rate as warning, 2 rate as error
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
#include <stack>
|
#include <stack>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
|
|
||||||
|
#include <components/misc/stringops.hpp>
|
||||||
|
|
||||||
#include "generator.hpp"
|
#include "generator.hpp"
|
||||||
#include "scanner.hpp"
|
#include "scanner.hpp"
|
||||||
#include "errorhandler.hpp"
|
#include "errorhandler.hpp"
|
||||||
|
@ -14,7 +16,6 @@
|
||||||
#include "stringparser.hpp"
|
#include "stringparser.hpp"
|
||||||
#include "extensions.hpp"
|
#include "extensions.hpp"
|
||||||
#include "context.hpp"
|
#include "context.hpp"
|
||||||
#include <components/misc/stringops.hpp>
|
|
||||||
|
|
||||||
namespace Compiler
|
namespace Compiler
|
||||||
{
|
{
|
||||||
|
@ -203,21 +204,22 @@ namespace Compiler
|
||||||
std::string name2 = Misc::StringUtils::lowerCase (name);
|
std::string name2 = Misc::StringUtils::lowerCase (name);
|
||||||
std::string id = Misc::StringUtils::lowerCase (mExplicit);
|
std::string id = Misc::StringUtils::lowerCase (mExplicit);
|
||||||
|
|
||||||
char type = getContext().getMemberType (name2, id);
|
std::pair<char, bool> type = getContext().getMemberType (name2, id);
|
||||||
|
|
||||||
if (type!=' ')
|
if (type.first!=' ')
|
||||||
{
|
{
|
||||||
Generator::fetchMember (mCode, mLiterals, type, name2, id);
|
Generator::fetchMember (mCode, mLiterals, type.first, name2, id, !type.second);
|
||||||
|
|
||||||
mNextOperand = false;
|
mNextOperand = false;
|
||||||
mExplicit.clear();
|
mExplicit.clear();
|
||||||
mOperands.push_back (type=='f' ? 'f' : 'l');
|
mOperands.push_back (type.first=='f' ? 'f' : 'l');
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ExprParser::ExprParser (ErrorHandler& errorHandler, Context& context, Locals& locals,
|
ExprParser::ExprParser (ErrorHandler& errorHandler, const Context& context, Locals& locals,
|
||||||
Literals& literals, bool argument)
|
Literals& literals, bool argument)
|
||||||
: Parser (errorHandler, context), mLocals (locals), mLiterals (literals),
|
: Parser (errorHandler, context), mLocals (locals), mLiterals (literals),
|
||||||
mNextOperand (true), mFirst (true), mArgument (argument), mRefOp (false), mMemberOp (false)
|
mNextOperand (true), mFirst (true), mArgument (argument), mRefOp (false), mMemberOp (false)
|
||||||
|
@ -308,6 +310,22 @@ namespace Compiler
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// die in a fire, Morrowind script compiler!
|
||||||
|
if (const Extensions *extensions = getContext().getExtensions())
|
||||||
|
{
|
||||||
|
if (getContext().isJournalId (name2))
|
||||||
|
{
|
||||||
|
// JournalID used as an argument. Use the index of that JournalID
|
||||||
|
Generator::pushString (mCode, mLiterals, name2);
|
||||||
|
int keyword = extensions->searchKeyword ("getjournalindex");
|
||||||
|
extensions->generateFunctionCode (keyword, mCode, mLiterals, mExplicit, 0);
|
||||||
|
mNextOperand = false;
|
||||||
|
mOperands.push_back ('l');
|
||||||
|
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (mExplicit.empty() && getContext().isId (name2))
|
if (mExplicit.empty() && getContext().isId (name2))
|
||||||
{
|
{
|
||||||
mExplicit = name2;
|
mExplicit = name2;
|
||||||
|
@ -326,6 +344,31 @@ namespace Compiler
|
||||||
|
|
||||||
bool ExprParser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner)
|
bool ExprParser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner)
|
||||||
{
|
{
|
||||||
|
if (const Extensions *extensions = getContext().getExtensions())
|
||||||
|
{
|
||||||
|
std::string argumentType; // ignored
|
||||||
|
bool hasExplicit = false; // ignored
|
||||||
|
if (extensions->isInstruction (keyword, argumentType, hasExplicit))
|
||||||
|
{
|
||||||
|
// pretend this is not a keyword
|
||||||
|
return parseName (loc.mLiteral, loc, scanner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keyword==Scanner::K_end || keyword==Scanner::K_begin ||
|
||||||
|
keyword==Scanner::K_short || keyword==Scanner::K_long ||
|
||||||
|
keyword==Scanner::K_float || keyword==Scanner::K_if ||
|
||||||
|
keyword==Scanner::K_endif || keyword==Scanner::K_else ||
|
||||||
|
keyword==Scanner::K_elseif || keyword==Scanner::K_while ||
|
||||||
|
keyword==Scanner::K_endwhile || keyword==Scanner::K_return ||
|
||||||
|
keyword==Scanner::K_messagebox || keyword==Scanner::K_set ||
|
||||||
|
keyword==Scanner::K_to || keyword==Scanner::K_startscript ||
|
||||||
|
keyword==Scanner::K_stopscript || keyword==Scanner::K_enable ||
|
||||||
|
keyword==Scanner::K_disable)
|
||||||
|
{
|
||||||
|
return parseName (loc.mLiteral, loc, scanner);
|
||||||
|
}
|
||||||
|
|
||||||
mFirst = false;
|
mFirst = false;
|
||||||
|
|
||||||
if (!mExplicit.empty())
|
if (!mExplicit.empty())
|
||||||
|
@ -368,8 +411,15 @@ namespace Compiler
|
||||||
char returnType;
|
char returnType;
|
||||||
std::string argumentType;
|
std::string argumentType;
|
||||||
|
|
||||||
if (extensions->isFunction (keyword, returnType, argumentType, true))
|
bool hasExplicit = true;
|
||||||
|
if (extensions->isFunction (keyword, returnType, argumentType, hasExplicit))
|
||||||
{
|
{
|
||||||
|
if (!hasExplicit)
|
||||||
|
{
|
||||||
|
getErrorHandler().warning ("stray explicit reference (ignoring it)", loc);
|
||||||
|
mExplicit.clear();
|
||||||
|
}
|
||||||
|
|
||||||
start();
|
start();
|
||||||
|
|
||||||
mTokenLoc = loc;
|
mTokenLoc = loc;
|
||||||
|
@ -490,7 +540,9 @@ namespace Compiler
|
||||||
char returnType;
|
char returnType;
|
||||||
std::string argumentType;
|
std::string argumentType;
|
||||||
|
|
||||||
if (extensions->isFunction (keyword, returnType, argumentType, false))
|
bool hasExplicit = false;
|
||||||
|
|
||||||
|
if (extensions->isFunction (keyword, returnType, argumentType, hasExplicit))
|
||||||
{
|
{
|
||||||
mTokenLoc = loc;
|
mTokenLoc = loc;
|
||||||
int optionals = parseArguments (argumentType, scanner);
|
int optionals = parseArguments (argumentType, scanner);
|
||||||
|
@ -518,6 +570,14 @@ namespace Compiler
|
||||||
{
|
{
|
||||||
if (!mExplicit.empty())
|
if (!mExplicit.empty())
|
||||||
{
|
{
|
||||||
|
if (mRefOp && code==Scanner::S_open)
|
||||||
|
{
|
||||||
|
/// \todo add option to disable this workaround
|
||||||
|
mOperators.push_back ('(');
|
||||||
|
mTokenLoc = loc;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (!mRefOp && code==Scanner::S_ref)
|
if (!mRefOp && code==Scanner::S_ref)
|
||||||
{
|
{
|
||||||
mRefOp = true;
|
mRefOp = true;
|
||||||
|
@ -687,11 +747,11 @@ namespace Compiler
|
||||||
{
|
{
|
||||||
optional = true;
|
optional = true;
|
||||||
}
|
}
|
||||||
else if (*iter=='S' || *iter=='c')
|
else if (*iter=='S' || *iter=='c' || *iter=='x')
|
||||||
{
|
{
|
||||||
stringParser.reset();
|
stringParser.reset();
|
||||||
|
|
||||||
if (optional)
|
if (optional || *iter=='x')
|
||||||
stringParser.setOptional (true);
|
stringParser.setOptional (true);
|
||||||
|
|
||||||
if (*iter=='c') stringParser.smashCase();
|
if (*iter=='c') stringParser.smashCase();
|
||||||
|
@ -700,6 +760,8 @@ namespace Compiler
|
||||||
if (optional && stringParser.isEmpty())
|
if (optional && stringParser.isEmpty())
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
if (*iter!='x')
|
||||||
|
{
|
||||||
if (invert)
|
if (invert)
|
||||||
{
|
{
|
||||||
std::vector<Interpreter::Type_Code> tmp;
|
std::vector<Interpreter::Type_Code> tmp;
|
||||||
|
@ -713,6 +775,7 @@ namespace Compiler
|
||||||
if (optional)
|
if (optional)
|
||||||
++optionalCount;
|
++optionalCount;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
parser.reset();
|
parser.reset();
|
||||||
|
|
|
@ -58,7 +58,7 @@ namespace Compiler
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
ExprParser (ErrorHandler& errorHandler, Context& context, Locals& locals,
|
ExprParser (ErrorHandler& errorHandler, const Context& context, Locals& locals,
|
||||||
Literals& literals, bool argument = false);
|
Literals& literals, bool argument = false);
|
||||||
///< constructor
|
///< constructor
|
||||||
/// \param argument Parser is used to parse function- or instruction-
|
/// \param argument Parser is used to parse function- or instruction-
|
||||||
|
@ -101,6 +101,7 @@ namespace Compiler
|
||||||
/// \param arguments Each character represents one arguments ('l': integer,
|
/// \param arguments Each character represents one arguments ('l': integer,
|
||||||
/// 'f': float, 'S': string, 'c': string (case smashed), '/': following arguments are
|
/// 'f': float, 'S': string, 'c': string (case smashed), '/': following arguments are
|
||||||
/// optional)
|
/// optional)
|
||||||
|
/// 'x': optional string that will be ignored (die in a fire, MW script compiler!)
|
||||||
/// \param invert Store arguments in reverted order.
|
/// \param invert Store arguments in reverted order.
|
||||||
/// \return number of optional arguments
|
/// \return number of optional arguments
|
||||||
};
|
};
|
||||||
|
|
|
@ -22,7 +22,7 @@ namespace Compiler
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Extensions::isFunction (int keyword, char& returnType, std::string& argumentType,
|
bool Extensions::isFunction (int keyword, char& returnType, std::string& argumentType,
|
||||||
bool explicitReference) const
|
bool& explicitReference) const
|
||||||
{
|
{
|
||||||
std::map<int, Function>::const_iterator iter = mFunctions.find (keyword);
|
std::map<int, Function>::const_iterator iter = mFunctions.find (keyword);
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ namespace Compiler
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (explicitReference && iter->second.mCodeExplicit==-1)
|
if (explicitReference && iter->second.mCodeExplicit==-1)
|
||||||
return false;
|
explicitReference = false;
|
||||||
|
|
||||||
returnType = iter->second.mReturn;
|
returnType = iter->second.mReturn;
|
||||||
argumentType = iter->second.mArguments;
|
argumentType = iter->second.mArguments;
|
||||||
|
@ -38,7 +38,7 @@ namespace Compiler
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Extensions::isInstruction (int keyword, std::string& argumentType,
|
bool Extensions::isInstruction (int keyword, std::string& argumentType,
|
||||||
bool explicitReference) const
|
bool& explicitReference) const
|
||||||
{
|
{
|
||||||
std::map<int, Instruction>::const_iterator iter = mInstructions.find (keyword);
|
std::map<int, Instruction>::const_iterator iter = mInstructions.find (keyword);
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ namespace Compiler
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (explicitReference && iter->second.mCodeExplicit==-1)
|
if (explicitReference && iter->second.mCodeExplicit==-1)
|
||||||
return false;
|
explicitReference = false;
|
||||||
|
|
||||||
argumentType = iter->second.mArguments;
|
argumentType = iter->second.mArguments;
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -47,13 +47,17 @@ namespace Compiler
|
||||||
/// - keyword must be all lower case.
|
/// - keyword must be all lower case.
|
||||||
|
|
||||||
bool isFunction (int keyword, char& returnType, std::string& argumentType,
|
bool isFunction (int keyword, char& returnType, std::string& argumentType,
|
||||||
bool explicitReference) const;
|
bool& explicitReference) const;
|
||||||
///< Is this keyword registered with a function? If yes, return return and argument
|
///< Is this keyword registered with a function? If yes, return return and argument
|
||||||
/// types.
|
/// types.
|
||||||
|
/// \param explicitReference In: has explicit reference; Out: set to false, if
|
||||||
|
/// explicit reference is not available for this instruction.
|
||||||
|
|
||||||
bool isInstruction (int keyword, std::string& argumentType,
|
bool isInstruction (int keyword, std::string& argumentType,
|
||||||
bool explicitReference) const;
|
bool& explicitReference) const;
|
||||||
///< Is this keyword registered with a function? If yes, return argument types.
|
///< Is this keyword registered with a function? If yes, return argument types.
|
||||||
|
/// \param explicitReference In: has explicit reference; Out: set to false, if
|
||||||
|
/// explicit reference is not available for this instruction.
|
||||||
|
|
||||||
void registerFunction (const std::string& keyword, char returnType,
|
void registerFunction (const std::string& keyword, char returnType,
|
||||||
const std::string& argumentType, int code, int codeExplicit = -1);
|
const std::string& argumentType, int code, int codeExplicit = -1);
|
||||||
|
|
|
@ -41,7 +41,7 @@ namespace Compiler
|
||||||
opcodeAiEscortCellExplicit);
|
opcodeAiEscortCellExplicit);
|
||||||
extensions.registerInstruction ("aiwander", "fff/llllllllll", opcodeAiWander,
|
extensions.registerInstruction ("aiwander", "fff/llllllllll", opcodeAiWander,
|
||||||
opcodeAiWanderExplicit);
|
opcodeAiWanderExplicit);
|
||||||
extensions.registerInstruction ("aifollow", "cffff/l", opcodeAiFollow,
|
extensions.registerInstruction ("aifollow", "cffff/llllllll", opcodeAiFollow,
|
||||||
opcodeAiFollowExplicit);
|
opcodeAiFollowExplicit);
|
||||||
extensions.registerInstruction ("aifollowcell", "ccffff/l", opcodeAiFollowCell,
|
extensions.registerInstruction ("aifollowcell", "ccffff/l", opcodeAiFollowCell,
|
||||||
opcodeAiFollowCellExplicit);
|
opcodeAiFollowCellExplicit);
|
||||||
|
@ -62,7 +62,7 @@ namespace Compiler
|
||||||
extensions.registerInstruction ("toggleai", "", opcodeToggleAI, opcodeToggleAI);
|
extensions.registerInstruction ("toggleai", "", opcodeToggleAI, opcodeToggleAI);
|
||||||
extensions.registerInstruction ("tai", "", opcodeToggleAI, opcodeToggleAI);
|
extensions.registerInstruction ("tai", "", opcodeToggleAI, opcodeToggleAI);
|
||||||
extensions.registerInstruction("startcombat", "c", opcodeStartCombat, opcodeStartCombatExplicit);
|
extensions.registerInstruction("startcombat", "c", opcodeStartCombat, opcodeStartCombatExplicit);
|
||||||
extensions.registerInstruction("stopcombat", "", opcodeStopCombat, opcodeStopCombatExplicit);
|
extensions.registerInstruction("stopcombat", "x", opcodeStopCombat, opcodeStopCombatExplicit);
|
||||||
extensions.registerFunction ("gethello", 'l', "", opcodeGetHello, opcodeGetHelloExplicit);
|
extensions.registerFunction ("gethello", 'l', "", opcodeGetHello, opcodeGetHelloExplicit);
|
||||||
extensions.registerFunction ("getfight", 'l', "", opcodeGetFight, opcodeGetFightExplicit);
|
extensions.registerFunction ("getfight", 'l', "", opcodeGetFight, opcodeGetFightExplicit);
|
||||||
extensions.registerFunction ("getflee", 'l', "", opcodeGetFlee, opcodeGetFleeExplicit);
|
extensions.registerFunction ("getflee", 'l', "", opcodeGetFlee, opcodeGetFleeExplicit);
|
||||||
|
@ -253,7 +253,7 @@ namespace Compiler
|
||||||
extensions.registerFunction ("getlocked", 'l', "", opcodeGetLocked, opcodeGetLockedExplicit);
|
extensions.registerFunction ("getlocked", 'l', "", opcodeGetLocked, opcodeGetLockedExplicit);
|
||||||
extensions.registerFunction ("geteffect", 'l', "S", opcodeGetEffect, opcodeGetEffectExplicit);
|
extensions.registerFunction ("geteffect", 'l', "S", opcodeGetEffect, opcodeGetEffectExplicit);
|
||||||
extensions.registerInstruction ("addsoulgem", "cc", opcodeAddSoulGem, opcodeAddSoulGemExplicit);
|
extensions.registerInstruction ("addsoulgem", "cc", opcodeAddSoulGem, opcodeAddSoulGemExplicit);
|
||||||
extensions.registerInstruction ("removesoulgem", "c", opcodeRemoveSoulGem, opcodeRemoveSoulGemExplicit);
|
extensions.registerInstruction ("removesoulgem", "c/l", opcodeRemoveSoulGem, opcodeRemoveSoulGemExplicit);
|
||||||
extensions.registerInstruction ("drop", "cl", opcodeDrop, opcodeDropExplicit);
|
extensions.registerInstruction ("drop", "cl", opcodeDrop, opcodeDropExplicit);
|
||||||
extensions.registerInstruction ("dropsoulgem", "c", opcodeDropSoulGem, opcodeDropSoulGemExplicit);
|
extensions.registerInstruction ("dropsoulgem", "c", opcodeDropSoulGem, opcodeDropSoulGemExplicit);
|
||||||
extensions.registerFunction ("getattacked", 'l', "", opcodeGetAttacked, opcodeGetAttackedExplicit);
|
extensions.registerFunction ("getattacked", 'l', "", opcodeGetAttacked, opcodeGetAttackedExplicit);
|
||||||
|
@ -276,6 +276,8 @@ namespace Compiler
|
||||||
extensions.registerInstruction("togglegodmode", "", opcodeToggleGodMode);
|
extensions.registerInstruction("togglegodmode", "", opcodeToggleGodMode);
|
||||||
extensions.registerInstruction ("disablelevitation", "", opcodeDisableLevitation);
|
extensions.registerInstruction ("disablelevitation", "", opcodeDisableLevitation);
|
||||||
extensions.registerInstruction ("enablelevitation", "", opcodeEnableLevitation);
|
extensions.registerInstruction ("enablelevitation", "", opcodeEnableLevitation);
|
||||||
|
extensions.registerFunction ("getpcinjail", 'l', "", opcodeGetPcInJail);
|
||||||
|
extensions.registerFunction ("getpctraveling", 'l', "", opcodeGetPcTraveling);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -396,7 +398,7 @@ namespace Compiler
|
||||||
extensions.registerInstruction ("setpccrimelevel", "f", opcodeSetPCCrimeLevel);
|
extensions.registerInstruction ("setpccrimelevel", "f", opcodeSetPCCrimeLevel);
|
||||||
extensions.registerInstruction ("modpccrimelevel", "f", opcodeModPCCrimeLevel);
|
extensions.registerInstruction ("modpccrimelevel", "f", opcodeModPCCrimeLevel);
|
||||||
|
|
||||||
extensions.registerInstruction ("addspell", "c", opcodeAddSpell, opcodeAddSpellExplicit);
|
extensions.registerInstruction ("addspell", "cx", opcodeAddSpell, opcodeAddSpellExplicit);
|
||||||
extensions.registerInstruction ("removespell", "c", opcodeRemoveSpell,
|
extensions.registerInstruction ("removespell", "c", opcodeRemoveSpell,
|
||||||
opcodeRemoveSpellExplicit);
|
opcodeRemoveSpellExplicit);
|
||||||
extensions.registerInstruction ("removespelleffects", "c", opcodeRemoveSpellEffects,
|
extensions.registerInstruction ("removespelleffects", "c", opcodeRemoveSpellEffects,
|
||||||
|
|
|
@ -260,34 +260,34 @@ namespace
|
||||||
code.push_back (Compiler::Generator::segment5 (44));
|
code.push_back (Compiler::Generator::segment5 (44));
|
||||||
}
|
}
|
||||||
|
|
||||||
void opStoreMemberShort (Compiler::Generator::CodeContainer& code)
|
void opStoreMemberShort (Compiler::Generator::CodeContainer& code, bool global)
|
||||||
{
|
{
|
||||||
code.push_back (Compiler::Generator::segment5 (59));
|
code.push_back (Compiler::Generator::segment5 (global ? 65 : 59));
|
||||||
}
|
}
|
||||||
|
|
||||||
void opStoreMemberLong (Compiler::Generator::CodeContainer& code)
|
void opStoreMemberLong (Compiler::Generator::CodeContainer& code, bool global)
|
||||||
{
|
{
|
||||||
code.push_back (Compiler::Generator::segment5 (60));
|
code.push_back (Compiler::Generator::segment5 (global ? 66 : 60));
|
||||||
}
|
}
|
||||||
|
|
||||||
void opStoreMemberFloat (Compiler::Generator::CodeContainer& code)
|
void opStoreMemberFloat (Compiler::Generator::CodeContainer& code, bool global)
|
||||||
{
|
{
|
||||||
code.push_back (Compiler::Generator::segment5 (61));
|
code.push_back (Compiler::Generator::segment5 (global ? 67 : 61));
|
||||||
}
|
}
|
||||||
|
|
||||||
void opFetchMemberShort (Compiler::Generator::CodeContainer& code)
|
void opFetchMemberShort (Compiler::Generator::CodeContainer& code, bool global)
|
||||||
{
|
{
|
||||||
code.push_back (Compiler::Generator::segment5 (62));
|
code.push_back (Compiler::Generator::segment5 (global ? 68 : 62));
|
||||||
}
|
}
|
||||||
|
|
||||||
void opFetchMemberLong (Compiler::Generator::CodeContainer& code)
|
void opFetchMemberLong (Compiler::Generator::CodeContainer& code, bool global)
|
||||||
{
|
{
|
||||||
code.push_back (Compiler::Generator::segment5 (63));
|
code.push_back (Compiler::Generator::segment5 (global ? 69 : 63));
|
||||||
}
|
}
|
||||||
|
|
||||||
void opFetchMemberFloat (Compiler::Generator::CodeContainer& code)
|
void opFetchMemberFloat (Compiler::Generator::CodeContainer& code, bool global)
|
||||||
{
|
{
|
||||||
code.push_back (Compiler::Generator::segment5 (64));
|
code.push_back (Compiler::Generator::segment5 (global ? 70 : 64));
|
||||||
}
|
}
|
||||||
|
|
||||||
void opRandom (Compiler::Generator::CodeContainer& code)
|
void opRandom (Compiler::Generator::CodeContainer& code)
|
||||||
|
@ -593,7 +593,7 @@ namespace Compiler
|
||||||
else if (offset<0)
|
else if (offset<0)
|
||||||
opJumpBackward (code, -offset);
|
opJumpBackward (code, -offset);
|
||||||
else
|
else
|
||||||
throw std::logic_error ("inifite loop");
|
throw std::logic_error ("infinite loop");
|
||||||
}
|
}
|
||||||
|
|
||||||
void jumpOnZero (CodeContainer& code, int offset)
|
void jumpOnZero (CodeContainer& code, int offset)
|
||||||
|
@ -738,7 +738,8 @@ namespace Compiler
|
||||||
}
|
}
|
||||||
|
|
||||||
void assignToMember (CodeContainer& code, Literals& literals, char localType,
|
void assignToMember (CodeContainer& code, Literals& literals, char localType,
|
||||||
const std::string& name, const std::string& id, const CodeContainer& value, char valueType)
|
const std::string& name, const std::string& id, const CodeContainer& value,
|
||||||
|
char valueType, bool global)
|
||||||
{
|
{
|
||||||
int index = literals.addString (name);
|
int index = literals.addString (name);
|
||||||
|
|
||||||
|
@ -766,17 +767,17 @@ namespace Compiler
|
||||||
{
|
{
|
||||||
case 'f':
|
case 'f':
|
||||||
|
|
||||||
opStoreMemberFloat (code);
|
opStoreMemberFloat (code, global);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 's':
|
case 's':
|
||||||
|
|
||||||
opStoreMemberShort (code);
|
opStoreMemberShort (code, global);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'l':
|
case 'l':
|
||||||
|
|
||||||
opStoreMemberLong (code);
|
opStoreMemberLong (code, global);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -786,7 +787,7 @@ namespace Compiler
|
||||||
}
|
}
|
||||||
|
|
||||||
void fetchMember (CodeContainer& code, Literals& literals, char localType,
|
void fetchMember (CodeContainer& code, Literals& literals, char localType,
|
||||||
const std::string& name, const std::string& id)
|
const std::string& name, const std::string& id, bool global)
|
||||||
{
|
{
|
||||||
int index = literals.addString (name);
|
int index = literals.addString (name);
|
||||||
|
|
||||||
|
@ -800,17 +801,17 @@ namespace Compiler
|
||||||
{
|
{
|
||||||
case 'f':
|
case 'f':
|
||||||
|
|
||||||
opFetchMemberFloat (code);
|
opFetchMemberFloat (code, global);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 's':
|
case 's':
|
||||||
|
|
||||||
opFetchMemberShort (code);
|
opFetchMemberShort (code, global);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'l':
|
case 'l':
|
||||||
|
|
||||||
opFetchMemberLong (code);
|
opFetchMemberLong (code, global);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -102,10 +102,12 @@ namespace Compiler
|
||||||
const std::string& name);
|
const std::string& name);
|
||||||
|
|
||||||
void assignToMember (CodeContainer& code, Literals& literals, char memberType,
|
void assignToMember (CodeContainer& code, Literals& literals, char memberType,
|
||||||
const std::string& name, const std::string& id, const CodeContainer& value, char valueType);
|
const std::string& name, const std::string& id, const CodeContainer& value, char valueType, bool global);
|
||||||
|
///< \param global Member of a global script instead of a script of a reference.
|
||||||
|
|
||||||
void fetchMember (CodeContainer& code, Literals& literals, char memberType,
|
void fetchMember (CodeContainer& code, Literals& literals, char memberType,
|
||||||
const std::string& name, const std::string& id);
|
const std::string& name, const std::string& id, bool global);
|
||||||
|
///< \param global Member of a global script instead of a script of a reference.
|
||||||
|
|
||||||
void random (CodeContainer& code);
|
void random (CodeContainer& code);
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
|
|
||||||
#include "lineparser.hpp"
|
#include "lineparser.hpp"
|
||||||
|
|
||||||
|
#include <components/misc/stringops.hpp>
|
||||||
|
|
||||||
#include "scanner.hpp"
|
#include "scanner.hpp"
|
||||||
#include "context.hpp"
|
#include "context.hpp"
|
||||||
#include "errorhandler.hpp"
|
#include "errorhandler.hpp"
|
||||||
|
@ -8,7 +10,7 @@
|
||||||
#include "locals.hpp"
|
#include "locals.hpp"
|
||||||
#include "generator.hpp"
|
#include "generator.hpp"
|
||||||
#include "extensions.hpp"
|
#include "extensions.hpp"
|
||||||
#include <components/misc/stringops.hpp>
|
#include "declarationparser.hpp"
|
||||||
|
|
||||||
namespace Compiler
|
namespace Compiler
|
||||||
{
|
{
|
||||||
|
@ -48,7 +50,7 @@ namespace Compiler
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LineParser::LineParser (ErrorHandler& errorHandler, Context& context, Locals& locals,
|
LineParser::LineParser (ErrorHandler& errorHandler, const Context& context, Locals& locals,
|
||||||
Literals& literals, std::vector<Interpreter::Type_Code>& code, bool allowExpression)
|
Literals& literals, std::vector<Interpreter::Type_Code>& code, bool allowExpression)
|
||||||
: Parser (errorHandler, context), mLocals (locals), mLiterals (literals), mCode (code),
|
: Parser (errorHandler, context), mLocals (locals), mLiterals (literals), mCode (code),
|
||||||
mState (BeginState), mExprParser (errorHandler, context, locals, literals),
|
mState (BeginState), mExprParser (errorHandler, context, locals, literals),
|
||||||
|
@ -82,33 +84,9 @@ namespace Compiler
|
||||||
bool LineParser::parseName (const std::string& name, const TokenLoc& loc,
|
bool LineParser::parseName (const std::string& name, const TokenLoc& loc,
|
||||||
Scanner& scanner)
|
Scanner& scanner)
|
||||||
{
|
{
|
||||||
if (mState==ShortState || mState==LongState || mState==FloatState)
|
if (mState==PotentialEndState)
|
||||||
{
|
{
|
||||||
if (!getContext().canDeclareLocals())
|
getErrorHandler().warning ("stay string argument (ignoring it)", loc);
|
||||||
{
|
|
||||||
getErrorHandler().error ("local variables can't be declared in this context", loc);
|
|
||||||
SkipParser skip (getErrorHandler(), getContext());
|
|
||||||
scanner.scan (skip);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string name2 = Misc::StringUtils::lowerCase (name);
|
|
||||||
|
|
||||||
char type = mLocals.getType (name2);
|
|
||||||
|
|
||||||
if (type!=' ')
|
|
||||||
{
|
|
||||||
/// \todo add option to make re-declared local variables an error
|
|
||||||
getErrorHandler().warning ("can't re-declare local variable", loc);
|
|
||||||
SkipParser skip (getErrorHandler(), getContext());
|
|
||||||
scanner.scan (skip);
|
|
||||||
mState = EndState;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
mLocals.declare (mState==ShortState ? 's' : (mState==LongState ? 'l' : 'f'),
|
|
||||||
name2);
|
|
||||||
|
|
||||||
mState = EndState;
|
mState = EndState;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -142,12 +120,13 @@ namespace Compiler
|
||||||
if (mState==SetMemberVarState)
|
if (mState==SetMemberVarState)
|
||||||
{
|
{
|
||||||
mMemberName = name;
|
mMemberName = name;
|
||||||
char type = getContext().getMemberType (mMemberName, mName);
|
std::pair<char, bool> type = getContext().getMemberType (mMemberName, mName);
|
||||||
|
|
||||||
if (type!=' ')
|
if (type.first!=' ')
|
||||||
{
|
{
|
||||||
mState = SetMemberVarState2;
|
mState = SetMemberVarState2;
|
||||||
mType = type;
|
mType = type.first;
|
||||||
|
mReferenceMember = type.second;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -240,6 +219,34 @@ namespace Compiler
|
||||||
|
|
||||||
bool LineParser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner)
|
bool LineParser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner)
|
||||||
{
|
{
|
||||||
|
if (mState==SetMemberVarState)
|
||||||
|
{
|
||||||
|
mMemberName = loc.mLiteral;
|
||||||
|
std::pair<char, bool> type = getContext().getMemberType (mMemberName, mName);
|
||||||
|
|
||||||
|
if (type.first!=' ')
|
||||||
|
{
|
||||||
|
mState = SetMemberVarState2;
|
||||||
|
mType = type.first;
|
||||||
|
mReferenceMember = type.second;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mState==SetPotentialMemberVarState && keyword==Scanner::K_to)
|
||||||
|
{
|
||||||
|
getErrorHandler().warning ("unknown variable (ignoring set instruction)", loc);
|
||||||
|
SkipParser skip (getErrorHandler(), getContext());
|
||||||
|
scanner.scan (skip);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mState==SetState)
|
||||||
|
{
|
||||||
|
// allow keywords to be used as variable names when assigning a value to a variable.
|
||||||
|
return parseName (loc.mLiteral, loc, scanner);
|
||||||
|
}
|
||||||
|
|
||||||
if (mState==BeginState || mState==ExplicitState)
|
if (mState==BeginState || mState==ExplicitState)
|
||||||
{
|
{
|
||||||
switch (keyword)
|
switch (keyword)
|
||||||
|
@ -247,13 +254,13 @@ namespace Compiler
|
||||||
case Scanner::K_enable:
|
case Scanner::K_enable:
|
||||||
|
|
||||||
Generator::enable (mCode, mLiterals, mExplicit);
|
Generator::enable (mCode, mLiterals, mExplicit);
|
||||||
mState = EndState;
|
mState = PotentialEndState;
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case Scanner::K_disable:
|
case Scanner::K_disable:
|
||||||
|
|
||||||
Generator::disable (mCode, mLiterals, mExplicit);
|
Generator::disable (mCode, mLiterals, mExplicit);
|
||||||
mState = EndState;
|
mState = PotentialEndState;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -262,8 +269,15 @@ namespace Compiler
|
||||||
{
|
{
|
||||||
std::string argumentType;
|
std::string argumentType;
|
||||||
|
|
||||||
if (extensions->isInstruction (keyword, argumentType, mState==ExplicitState))
|
bool hasExplicit = mState==ExplicitState;
|
||||||
|
if (extensions->isInstruction (keyword, argumentType, hasExplicit))
|
||||||
{
|
{
|
||||||
|
if (!hasExplicit && mState==ExplicitState)
|
||||||
|
{
|
||||||
|
getErrorHandler().warning ("stray explicit reference (ignoring it)", loc);
|
||||||
|
mExplicit.clear();
|
||||||
|
}
|
||||||
|
|
||||||
int optionals = mExprParser.parseArguments (argumentType, scanner, mCode, true);
|
int optionals = mExprParser.parseArguments (argumentType, scanner, mCode, true);
|
||||||
|
|
||||||
extensions->generateInstructionCode (keyword, mCode, mLiterals, mExplicit, optionals);
|
extensions->generateInstructionCode (keyword, mCode, mLiterals, mExplicit, optionals);
|
||||||
|
@ -287,9 +301,16 @@ namespace Compiler
|
||||||
char returnType;
|
char returnType;
|
||||||
std::string argumentType;
|
std::string argumentType;
|
||||||
|
|
||||||
if (extensions->isFunction (keyword, returnType, argumentType,
|
bool hasExplicit = !mExplicit.empty();
|
||||||
!mExplicit.empty()))
|
|
||||||
|
if (extensions->isFunction (keyword, returnType, argumentType, hasExplicit))
|
||||||
{
|
{
|
||||||
|
if (!hasExplicit && !mExplicit.empty())
|
||||||
|
{
|
||||||
|
getErrorHandler().warning ("stray explicit reference (ignoring it)", loc);
|
||||||
|
mExplicit.clear();
|
||||||
|
}
|
||||||
|
|
||||||
scanner.putbackKeyword (keyword, loc);
|
scanner.putbackKeyword (keyword, loc);
|
||||||
parseExpression (scanner, loc);
|
parseExpression (scanner, loc);
|
||||||
mState = EndState;
|
mState = EndState;
|
||||||
|
@ -299,13 +320,38 @@ namespace Compiler
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mState==ExplicitState)
|
||||||
|
{
|
||||||
|
// drop stray explicit reference
|
||||||
|
getErrorHandler().warning ("stray explicit reference (ignoring it)", loc);
|
||||||
|
mState = BeginState;
|
||||||
|
mExplicit.clear();
|
||||||
|
}
|
||||||
|
|
||||||
if (mState==BeginState)
|
if (mState==BeginState)
|
||||||
{
|
{
|
||||||
switch (keyword)
|
switch (keyword)
|
||||||
{
|
{
|
||||||
case Scanner::K_short: mState = ShortState; return true;
|
case Scanner::K_short:
|
||||||
case Scanner::K_long: mState = LongState; return true;
|
case Scanner::K_long:
|
||||||
case Scanner::K_float: mState = FloatState; return true;
|
case Scanner::K_float:
|
||||||
|
{
|
||||||
|
if (!getContext().canDeclareLocals())
|
||||||
|
{
|
||||||
|
getErrorHandler().error (
|
||||||
|
"local variables can't be declared in this context", loc);
|
||||||
|
SkipParser skip (getErrorHandler(), getContext());
|
||||||
|
scanner.scan (skip);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
DeclarationParser declaration (getErrorHandler(), getContext(), mLocals);
|
||||||
|
if (declaration.parseKeyword (keyword, loc, scanner))
|
||||||
|
scanner.scan (declaration);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
case Scanner::K_set: mState = SetState; return true;
|
case Scanner::K_set: mState = SetState; return true;
|
||||||
case Scanner::K_messagebox: mState = MessageState; return true;
|
case Scanner::K_messagebox: mState = MessageState; return true;
|
||||||
|
|
||||||
|
@ -328,6 +374,24 @@ namespace Compiler
|
||||||
Generator::stopScript (mCode);
|
Generator::stopScript (mCode);
|
||||||
mState = EndState;
|
mState = EndState;
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
case Scanner::K_else:
|
||||||
|
|
||||||
|
getErrorHandler().warning ("stay else (ignoring it)", loc);
|
||||||
|
mState = EndState;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case Scanner::K_endif:
|
||||||
|
|
||||||
|
getErrorHandler().warning ("stay endif (ignoring it)", loc);
|
||||||
|
mState = EndState;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case Scanner::K_begin:
|
||||||
|
|
||||||
|
getErrorHandler().warning ("stay begin (ignoring it)", loc);
|
||||||
|
mState = EndState;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (mState==SetLocalVarState && keyword==Scanner::K_to)
|
else if (mState==SetLocalVarState && keyword==Scanner::K_to)
|
||||||
|
@ -365,7 +429,8 @@ namespace Compiler
|
||||||
std::vector<Interpreter::Type_Code> code;
|
std::vector<Interpreter::Type_Code> code;
|
||||||
char type = mExprParser.append (code);
|
char type = mExprParser.append (code);
|
||||||
|
|
||||||
Generator::assignToMember (mCode, mLiterals, mType, mMemberName, mName, code, type);
|
Generator::assignToMember (mCode, mLiterals, mType, mMemberName, mName, code, type,
|
||||||
|
!mReferenceMember);
|
||||||
|
|
||||||
mState = EndState;
|
mState = EndState;
|
||||||
return true;
|
return true;
|
||||||
|
@ -389,7 +454,8 @@ namespace Compiler
|
||||||
|
|
||||||
bool LineParser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner)
|
bool LineParser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner)
|
||||||
{
|
{
|
||||||
if (code==Scanner::S_newline && (mState==EndState || mState==BeginState))
|
if (code==Scanner::S_newline &&
|
||||||
|
(mState==EndState || mState==BeginState || mState==PotentialEndState))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (code==Scanner::S_comma && mState==MessageState)
|
if (code==Scanner::S_comma && mState==MessageState)
|
||||||
|
|
|
@ -20,11 +20,10 @@ namespace Compiler
|
||||||
enum State
|
enum State
|
||||||
{
|
{
|
||||||
BeginState,
|
BeginState,
|
||||||
ShortState, LongState, FloatState,
|
|
||||||
SetState, SetLocalVarState, SetGlobalVarState, SetPotentialMemberVarState,
|
SetState, SetLocalVarState, SetGlobalVarState, SetPotentialMemberVarState,
|
||||||
SetMemberVarState, SetMemberVarState2,
|
SetMemberVarState, SetMemberVarState2,
|
||||||
MessageState, MessageCommaState, MessageButtonState, MessageButtonCommaState,
|
MessageState, MessageCommaState, MessageButtonState, MessageButtonCommaState,
|
||||||
EndState,
|
EndState, PotentialEndState /* may have a stray string argument */,
|
||||||
PotentialExplicitState, ExplicitState, MemberState
|
PotentialExplicitState, ExplicitState, MemberState
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -34,6 +33,7 @@ namespace Compiler
|
||||||
State mState;
|
State mState;
|
||||||
std::string mName;
|
std::string mName;
|
||||||
std::string mMemberName;
|
std::string mMemberName;
|
||||||
|
bool mReferenceMember;
|
||||||
int mButtons;
|
int mButtons;
|
||||||
std::string mExplicit;
|
std::string mExplicit;
|
||||||
char mType;
|
char mType;
|
||||||
|
@ -44,7 +44,7 @@ namespace Compiler
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
LineParser (ErrorHandler& errorHandler, Context& context, Locals& locals,
|
LineParser (ErrorHandler& errorHandler, const Context& context, Locals& locals,
|
||||||
Literals& literals, std::vector<Interpreter::Type_Code>& code,
|
Literals& literals, std::vector<Interpreter::Type_Code>& code,
|
||||||
bool allowExpression = false);
|
bool allowExpression = false);
|
||||||
///< \param allowExpression Allow lines consisting of a naked expression
|
///< \param allowExpression Allow lines consisting of a naked expression
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
|
|
||||||
|
#include <components/misc/stringops.hpp>
|
||||||
|
|
||||||
namespace Compiler
|
namespace Compiler
|
||||||
{
|
{
|
||||||
const std::vector<std::string>& Locals::get (char type) const
|
const std::vector<std::string>& Locals::get (char type) const
|
||||||
|
@ -97,7 +99,7 @@ namespace Compiler
|
||||||
|
|
||||||
void Locals::declare (char type, const std::string& name)
|
void Locals::declare (char type, const std::string& name)
|
||||||
{
|
{
|
||||||
get (type).push_back (name);
|
get (type).push_back (Misc::StringUtils::lowerCase (name));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Locals::clear()
|
void Locals::clear()
|
||||||
|
|
|
@ -203,8 +203,8 @@ namespace Compiler
|
||||||
const int opcodeGetEffectExplicit = 0x20001d0;
|
const int opcodeGetEffectExplicit = 0x20001d0;
|
||||||
const int opcodeAddSoulGem = 0x20001f3;
|
const int opcodeAddSoulGem = 0x20001f3;
|
||||||
const int opcodeAddSoulGemExplicit = 0x20001f4;
|
const int opcodeAddSoulGemExplicit = 0x20001f4;
|
||||||
const int opcodeRemoveSoulGem = 0x20001f5;
|
const int opcodeRemoveSoulGem = 0x20027;
|
||||||
const int opcodeRemoveSoulGemExplicit = 0x20001f6;
|
const int opcodeRemoveSoulGemExplicit = 0x20028;
|
||||||
const int opcodeDrop = 0x20001f8;
|
const int opcodeDrop = 0x20001f8;
|
||||||
const int opcodeDropExplicit = 0x20001f9;
|
const int opcodeDropExplicit = 0x20001f9;
|
||||||
const int opcodeDropSoulGem = 0x20001fa;
|
const int opcodeDropSoulGem = 0x20001fa;
|
||||||
|
@ -245,6 +245,8 @@ namespace Compiler
|
||||||
const int opcodeCastExplicit = 0x2000228;
|
const int opcodeCastExplicit = 0x2000228;
|
||||||
const int opcodeExplodeSpell = 0x2000229;
|
const int opcodeExplodeSpell = 0x2000229;
|
||||||
const int opcodeExplodeSpellExplicit = 0x200022a;
|
const int opcodeExplodeSpellExplicit = 0x200022a;
|
||||||
|
const int opcodeGetPcInJail = 0x200023e;
|
||||||
|
const int opcodeGetPcTraveling = 0x200023f;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Sky
|
namespace Sky
|
||||||
|
|
|
@ -52,7 +52,7 @@ namespace Compiler
|
||||||
|
|
||||||
// Return context
|
// Return context
|
||||||
|
|
||||||
Context& Parser::getContext()
|
const Context& Parser::getContext() const
|
||||||
{
|
{
|
||||||
return mContext;
|
return mContext;
|
||||||
}
|
}
|
||||||
|
@ -64,7 +64,7 @@ namespace Compiler
|
||||||
return lowerCase;
|
return lowerCase;
|
||||||
}
|
}
|
||||||
|
|
||||||
Parser::Parser (ErrorHandler& errorHandler, Context& context)
|
Parser::Parser (ErrorHandler& errorHandler, const Context& context)
|
||||||
: mErrorHandler (errorHandler), mContext (context), mOptional (false), mEmpty (true)
|
: mErrorHandler (errorHandler), mContext (context), mOptional (false), mEmpty (true)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ namespace Compiler
|
||||||
class Parser
|
class Parser
|
||||||
{
|
{
|
||||||
ErrorHandler& mErrorHandler;
|
ErrorHandler& mErrorHandler;
|
||||||
Context& mContext;
|
const Context& mContext;
|
||||||
bool mOptional;
|
bool mOptional;
|
||||||
bool mEmpty;
|
bool mEmpty;
|
||||||
|
|
||||||
|
@ -38,14 +38,14 @@ namespace Compiler
|
||||||
ErrorHandler& getErrorHandler();
|
ErrorHandler& getErrorHandler();
|
||||||
///< Return error handler
|
///< Return error handler
|
||||||
|
|
||||||
Context& getContext();
|
const Context& getContext() const;
|
||||||
///< Return context
|
///< Return context
|
||||||
|
|
||||||
static std::string toLower (const std::string& name);
|
static std::string toLower (const std::string& name);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Parser (ErrorHandler& errorHandler, Context& context);
|
Parser (ErrorHandler& errorHandler, const Context& context);
|
||||||
///< constructor
|
///< constructor
|
||||||
|
|
||||||
virtual ~Parser();
|
virtual ~Parser();
|
||||||
|
|
52
components/compiler/quickfileparser.cpp
Normal file
52
components/compiler/quickfileparser.cpp
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
|
||||||
|
#include "quickfileparser.hpp"
|
||||||
|
|
||||||
|
#include "skipparser.hpp"
|
||||||
|
#include "scanner.hpp"
|
||||||
|
|
||||||
|
Compiler::QuickFileParser::QuickFileParser (ErrorHandler& errorHandler, const Context& context,
|
||||||
|
Locals& locals)
|
||||||
|
: Parser (errorHandler, context), mDeclarationParser (errorHandler, context, locals)
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool Compiler::QuickFileParser::parseName (const std::string& name, const TokenLoc& loc,
|
||||||
|
Scanner& scanner)
|
||||||
|
{
|
||||||
|
SkipParser skip (getErrorHandler(), getContext());
|
||||||
|
scanner.scan (skip);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Compiler::QuickFileParser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner)
|
||||||
|
{
|
||||||
|
if (keyword==Scanner::K_end)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (keyword==Scanner::K_short || keyword==Scanner::K_long || keyword==Scanner::K_float)
|
||||||
|
{
|
||||||
|
mDeclarationParser.reset();
|
||||||
|
scanner.putbackKeyword (keyword, loc);
|
||||||
|
scanner.scan (mDeclarationParser);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
SkipParser skip (getErrorHandler(), getContext());
|
||||||
|
scanner.scan (skip);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Compiler::QuickFileParser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner)
|
||||||
|
{
|
||||||
|
if (code!=Scanner::S_newline)
|
||||||
|
{
|
||||||
|
SkipParser skip (getErrorHandler(), getContext());
|
||||||
|
scanner.scan (skip);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Compiler::QuickFileParser::parseEOF (Scanner& scanner)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
39
components/compiler/quickfileparser.hpp
Normal file
39
components/compiler/quickfileparser.hpp
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
#ifndef COMPILER_QUICKFILEPARSER_H_INCLUDED
|
||||||
|
#define COMPILER_QUICKFILEPARSER_H_INCLUDED
|
||||||
|
|
||||||
|
#include "parser.hpp"
|
||||||
|
#include "declarationparser.hpp"
|
||||||
|
|
||||||
|
namespace Compiler
|
||||||
|
{
|
||||||
|
class Locals;
|
||||||
|
|
||||||
|
/// \brief File parser variant that ignores everything but variable declarations
|
||||||
|
class QuickFileParser : public Parser
|
||||||
|
{
|
||||||
|
DeclarationParser mDeclarationParser;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
QuickFileParser (ErrorHandler& errorHandler, const Context& context, Locals& locals);
|
||||||
|
|
||||||
|
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.
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -370,9 +370,9 @@ namespace Compiler
|
||||||
|
|
||||||
if (c=='\n')
|
if (c=='\n')
|
||||||
special = S_newline;
|
special = S_newline;
|
||||||
else if (c=='(')
|
else if (c=='(' || c=='[') /// \todo option to disable the use of [ as alias for (
|
||||||
special = S_open;
|
special = S_open;
|
||||||
else if (c==')')
|
else if (c==')' || c==']') /// \todo option to disable the use of ] as alias for )
|
||||||
special = S_close;
|
special = S_close;
|
||||||
else if (c=='.')
|
else if (c=='.')
|
||||||
{
|
{
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
namespace Compiler
|
namespace Compiler
|
||||||
{
|
{
|
||||||
ScriptParser::ScriptParser (ErrorHandler& errorHandler, Context& context,
|
ScriptParser::ScriptParser (ErrorHandler& errorHandler, const Context& context,
|
||||||
Locals& locals, bool end)
|
Locals& locals, bool end)
|
||||||
: Parser (errorHandler, context), mOutput (locals),
|
: Parser (errorHandler, context), mOutput (locals),
|
||||||
mLineParser (errorHandler, context, locals, mOutput.getLiterals(), mOutput.getCode()),
|
mLineParser (errorHandler, context, locals, mOutput.getLiterals(), mOutput.getCode()),
|
||||||
|
@ -32,7 +32,7 @@ namespace Compiler
|
||||||
|
|
||||||
bool ScriptParser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner)
|
bool ScriptParser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner)
|
||||||
{
|
{
|
||||||
if (keyword==Scanner::K_while || keyword==Scanner::K_if)
|
if (keyword==Scanner::K_while || keyword==Scanner::K_if || keyword==Scanner::K_elseif)
|
||||||
{
|
{
|
||||||
mControlParser.reset();
|
mControlParser.reset();
|
||||||
if (mControlParser.parseKeyword (keyword, loc, scanner))
|
if (mControlParser.parseKeyword (keyword, loc, scanner))
|
||||||
|
@ -71,6 +71,12 @@ namespace Compiler
|
||||||
if (code==Scanner::S_newline) // empty line
|
if (code==Scanner::S_newline) // empty line
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
if (code==Scanner::S_open) /// \todo Option to switch this off
|
||||||
|
{
|
||||||
|
scanner.putbackSpecial (code, loc);
|
||||||
|
return parseKeyword (Scanner::K_if, loc, scanner);
|
||||||
|
}
|
||||||
|
|
||||||
mLineParser.reset();
|
mLineParser.reset();
|
||||||
if (mLineParser.parseSpecial (code, loc, scanner))
|
if (mLineParser.parseSpecial (code, loc, scanner))
|
||||||
scanner.scan (mLineParser);
|
scanner.scan (mLineParser);
|
||||||
|
|
|
@ -23,7 +23,7 @@ namespace Compiler
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/// \param end of script is marked by end keyword.
|
/// \param end of script is marked by end keyword.
|
||||||
ScriptParser (ErrorHandler& errorHandler, Context& context, Locals& locals,
|
ScriptParser (ErrorHandler& errorHandler, const Context& context, Locals& locals,
|
||||||
bool end = false);
|
bool end = false);
|
||||||
|
|
||||||
void getCode (std::vector<Interpreter::Type_Code>& code) const;
|
void getCode (std::vector<Interpreter::Type_Code>& code) const;
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
namespace Compiler
|
namespace Compiler
|
||||||
{
|
{
|
||||||
SkipParser::SkipParser (ErrorHandler& errorHandler, Context& context)
|
SkipParser::SkipParser (ErrorHandler& errorHandler, const Context& context)
|
||||||
: Parser (errorHandler, context)
|
: Parser (errorHandler, context)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ namespace Compiler
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
SkipParser (ErrorHandler& errorHandler, Context& context);
|
SkipParser (ErrorHandler& errorHandler, const Context& context);
|
||||||
|
|
||||||
virtual bool parseInt (int value, const TokenLoc& loc, Scanner& scanner);
|
virtual bool parseInt (int value, const TokenLoc& loc, Scanner& scanner);
|
||||||
///< Handle an int token.
|
///< Handle an int token.
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
|
|
||||||
namespace Compiler
|
namespace Compiler
|
||||||
{
|
{
|
||||||
StringParser::StringParser (ErrorHandler& errorHandler, Context& context, Literals& literals)
|
StringParser::StringParser (ErrorHandler& errorHandler, const Context& context, Literals& literals)
|
||||||
: Parser (errorHandler, context), mLiterals (literals), mState (StartState), mSmashCase (false)
|
: Parser (errorHandler, context), mLiterals (literals), mState (StartState), mSmashCase (false)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,7 @@ namespace Compiler
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
StringParser (ErrorHandler& errorHandler, Context& context, Literals& literals);
|
StringParser (ErrorHandler& errorHandler, const Context& context, Literals& literals);
|
||||||
|
|
||||||
virtual bool parseName (const std::string& name, const TokenLoc& loc,
|
virtual bool parseName (const std::string& name, const TokenLoc& loc,
|
||||||
Scanner& scanner);
|
Scanner& scanner);
|
||||||
|
|
|
@ -96,17 +96,17 @@ namespace Interpreter
|
||||||
|
|
||||||
virtual void disable (const std::string& id = "") = 0;
|
virtual void disable (const std::string& id = "") = 0;
|
||||||
|
|
||||||
virtual int getMemberShort (const std::string& id, const std::string& name) const = 0;
|
virtual int getMemberShort (const std::string& id, const std::string& name, bool global) const = 0;
|
||||||
|
|
||||||
virtual int getMemberLong (const std::string& id, const std::string& name) const = 0;
|
virtual int getMemberLong (const std::string& id, const std::string& name, bool global) const = 0;
|
||||||
|
|
||||||
virtual float getMemberFloat (const std::string& id, const std::string& name) const = 0;
|
virtual float getMemberFloat (const std::string& id, const std::string& name, bool global) const = 0;
|
||||||
|
|
||||||
virtual void setMemberShort (const std::string& id, const std::string& name, int value) = 0;
|
virtual void setMemberShort (const std::string& id, const std::string& name, int value, bool global) = 0;
|
||||||
|
|
||||||
virtual void setMemberLong (const std::string& id, const std::string& name, int value) = 0;
|
virtual void setMemberLong (const std::string& id, const std::string& name, int value, bool global) = 0;
|
||||||
|
|
||||||
virtual void setMemberFloat (const std::string& id, const std::string& name, float value)
|
virtual void setMemberFloat (const std::string& id, const std::string& name, float value, bool global)
|
||||||
= 0;
|
= 0;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -127,5 +127,11 @@ op 61: store stack[0] in member float stack[2] of object with ID stack[1]
|
||||||
op 62: replace stack[0] with member short stack[1] of object with ID stack[0]
|
op 62: replace stack[0] with member short stack[1] of object with ID stack[0]
|
||||||
op 63: replace stack[0] with member short stack[1] of object with ID stack[0]
|
op 63: replace stack[0] with member short stack[1] of object with ID stack[0]
|
||||||
op 64: replace stack[0] with member short stack[1] of object with ID stack[0]
|
op 64: replace stack[0] with member short stack[1] of object with ID stack[0]
|
||||||
opcodes 65-33554431 unused
|
op 65: store stack[0] in member short stack[2] of global script with ID stack[1]
|
||||||
|
op 66: store stack[0] in member long stack[2] of global script with ID stack[1]
|
||||||
|
op 67: store stack[0] in member float stack[2] of global script with ID stack[1]
|
||||||
|
op 68: replace stack[0] with member short stack[1] of global script with ID stack[0]
|
||||||
|
op 69: replace stack[0] with member short stack[1] of global script with ID stack[0]
|
||||||
|
op 70: replace stack[0] with member short stack[1] of global script with ID stack[0]
|
||||||
|
opcodes 71-33554431 unused
|
||||||
opcodes 33554432-67108863 reserved for extensions
|
opcodes 33554432-67108863 reserved for extensions
|
||||||
|
|
|
@ -40,12 +40,18 @@ namespace Interpreter
|
||||||
interpreter.installSegment5 (42, new OpFetchGlobalShort);
|
interpreter.installSegment5 (42, new OpFetchGlobalShort);
|
||||||
interpreter.installSegment5 (43, new OpFetchGlobalLong);
|
interpreter.installSegment5 (43, new OpFetchGlobalLong);
|
||||||
interpreter.installSegment5 (44, new OpFetchGlobalFloat);
|
interpreter.installSegment5 (44, new OpFetchGlobalFloat);
|
||||||
interpreter.installSegment5 (59, new OpStoreMemberShort);
|
interpreter.installSegment5 (59, new OpStoreMemberShort (false));
|
||||||
interpreter.installSegment5 (60, new OpStoreMemberLong);
|
interpreter.installSegment5 (60, new OpStoreMemberLong (false));
|
||||||
interpreter.installSegment5 (61, new OpStoreMemberFloat);
|
interpreter.installSegment5 (61, new OpStoreMemberFloat (false));
|
||||||
interpreter.installSegment5 (62, new OpFetchMemberShort);
|
interpreter.installSegment5 (62, new OpFetchMemberShort (false));
|
||||||
interpreter.installSegment5 (63, new OpFetchMemberLong);
|
interpreter.installSegment5 (63, new OpFetchMemberLong (false));
|
||||||
interpreter.installSegment5 (64, new OpFetchMemberFloat);
|
interpreter.installSegment5 (64, new OpFetchMemberFloat (false));
|
||||||
|
interpreter.installSegment5 (65, new OpStoreMemberShort (true));
|
||||||
|
interpreter.installSegment5 (66, new OpStoreMemberLong (true));
|
||||||
|
interpreter.installSegment5 (67, new OpStoreMemberFloat (true));
|
||||||
|
interpreter.installSegment5 (68, new OpFetchMemberShort (true));
|
||||||
|
interpreter.installSegment5 (69, new OpFetchMemberLong (true));
|
||||||
|
interpreter.installSegment5 (70, new OpFetchMemberFloat (true));
|
||||||
|
|
||||||
// math
|
// math
|
||||||
interpreter.installSegment5 (9, new OpAddInt<Type_Integer>);
|
interpreter.installSegment5 (9, new OpAddInt<Type_Integer>);
|
||||||
|
|
|
@ -208,8 +208,12 @@ namespace Interpreter
|
||||||
|
|
||||||
class OpStoreMemberShort : public Opcode0
|
class OpStoreMemberShort : public Opcode0
|
||||||
{
|
{
|
||||||
|
bool mGlobal;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
OpStoreMemberShort (bool global) : mGlobal (global) {}
|
||||||
|
|
||||||
virtual void execute (Runtime& runtime)
|
virtual void execute (Runtime& runtime)
|
||||||
{
|
{
|
||||||
Type_Integer data = runtime[0].mInteger;
|
Type_Integer data = runtime[0].mInteger;
|
||||||
|
@ -218,7 +222,7 @@ namespace Interpreter
|
||||||
index = runtime[2].mInteger;
|
index = runtime[2].mInteger;
|
||||||
std::string variable = runtime.getStringLiteral (index);
|
std::string variable = runtime.getStringLiteral (index);
|
||||||
|
|
||||||
runtime.getContext().setMemberShort (id, variable, data);
|
runtime.getContext().setMemberShort (id, variable, data, mGlobal);
|
||||||
|
|
||||||
runtime.pop();
|
runtime.pop();
|
||||||
runtime.pop();
|
runtime.pop();
|
||||||
|
@ -228,8 +232,12 @@ namespace Interpreter
|
||||||
|
|
||||||
class OpStoreMemberLong : public Opcode0
|
class OpStoreMemberLong : public Opcode0
|
||||||
{
|
{
|
||||||
|
bool mGlobal;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
OpStoreMemberLong (bool global) : mGlobal (global) {}
|
||||||
|
|
||||||
virtual void execute (Runtime& runtime)
|
virtual void execute (Runtime& runtime)
|
||||||
{
|
{
|
||||||
Type_Integer data = runtime[0].mInteger;
|
Type_Integer data = runtime[0].mInteger;
|
||||||
|
@ -238,7 +246,7 @@ namespace Interpreter
|
||||||
index = runtime[2].mInteger;
|
index = runtime[2].mInteger;
|
||||||
std::string variable = runtime.getStringLiteral (index);
|
std::string variable = runtime.getStringLiteral (index);
|
||||||
|
|
||||||
runtime.getContext().setMemberLong (id, variable, data);
|
runtime.getContext().setMemberLong (id, variable, data, mGlobal);
|
||||||
|
|
||||||
runtime.pop();
|
runtime.pop();
|
||||||
runtime.pop();
|
runtime.pop();
|
||||||
|
@ -248,8 +256,12 @@ namespace Interpreter
|
||||||
|
|
||||||
class OpStoreMemberFloat : public Opcode0
|
class OpStoreMemberFloat : public Opcode0
|
||||||
{
|
{
|
||||||
|
bool mGlobal;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
OpStoreMemberFloat (bool global) : mGlobal (global) {}
|
||||||
|
|
||||||
virtual void execute (Runtime& runtime)
|
virtual void execute (Runtime& runtime)
|
||||||
{
|
{
|
||||||
Type_Float data = runtime[0].mFloat;
|
Type_Float data = runtime[0].mFloat;
|
||||||
|
@ -258,7 +270,7 @@ namespace Interpreter
|
||||||
index = runtime[2].mInteger;
|
index = runtime[2].mInteger;
|
||||||
std::string variable = runtime.getStringLiteral (index);
|
std::string variable = runtime.getStringLiteral (index);
|
||||||
|
|
||||||
runtime.getContext().setMemberFloat (id, variable, data);
|
runtime.getContext().setMemberFloat (id, variable, data, mGlobal);
|
||||||
|
|
||||||
runtime.pop();
|
runtime.pop();
|
||||||
runtime.pop();
|
runtime.pop();
|
||||||
|
@ -268,8 +280,12 @@ namespace Interpreter
|
||||||
|
|
||||||
class OpFetchMemberShort : public Opcode0
|
class OpFetchMemberShort : public Opcode0
|
||||||
{
|
{
|
||||||
|
bool mGlobal;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
OpFetchMemberShort (bool global) : mGlobal (global) {}
|
||||||
|
|
||||||
virtual void execute (Runtime& runtime)
|
virtual void execute (Runtime& runtime)
|
||||||
{
|
{
|
||||||
Type_Integer index = runtime[0].mInteger;
|
Type_Integer index = runtime[0].mInteger;
|
||||||
|
@ -278,15 +294,19 @@ namespace Interpreter
|
||||||
std::string variable = runtime.getStringLiteral (index);
|
std::string variable = runtime.getStringLiteral (index);
|
||||||
runtime.pop();
|
runtime.pop();
|
||||||
|
|
||||||
int value = runtime.getContext().getMemberShort (id, variable);
|
int value = runtime.getContext().getMemberShort (id, variable, mGlobal);
|
||||||
runtime[0].mInteger = value;
|
runtime[0].mInteger = value;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class OpFetchMemberLong : public Opcode0
|
class OpFetchMemberLong : public Opcode0
|
||||||
{
|
{
|
||||||
|
bool mGlobal;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
OpFetchMemberLong (bool global) : mGlobal (global) {}
|
||||||
|
|
||||||
virtual void execute (Runtime& runtime)
|
virtual void execute (Runtime& runtime)
|
||||||
{
|
{
|
||||||
Type_Integer index = runtime[0].mInteger;
|
Type_Integer index = runtime[0].mInteger;
|
||||||
|
@ -295,15 +315,19 @@ namespace Interpreter
|
||||||
std::string variable = runtime.getStringLiteral (index);
|
std::string variable = runtime.getStringLiteral (index);
|
||||||
runtime.pop();
|
runtime.pop();
|
||||||
|
|
||||||
int value = runtime.getContext().getMemberLong (id, variable);
|
int value = runtime.getContext().getMemberLong (id, variable, mGlobal);
|
||||||
runtime[0].mInteger = value;
|
runtime[0].mInteger = value;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class OpFetchMemberFloat : public Opcode0
|
class OpFetchMemberFloat : public Opcode0
|
||||||
{
|
{
|
||||||
|
bool mGlobal;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
OpFetchMemberFloat (bool global) : mGlobal (global) {}
|
||||||
|
|
||||||
virtual void execute (Runtime& runtime)
|
virtual void execute (Runtime& runtime)
|
||||||
{
|
{
|
||||||
Type_Integer index = runtime[0].mInteger;
|
Type_Integer index = runtime[0].mInteger;
|
||||||
|
@ -312,7 +336,7 @@ namespace Interpreter
|
||||||
std::string variable = runtime.getStringLiteral (index);
|
std::string variable = runtime.getStringLiteral (index);
|
||||||
runtime.pop();
|
runtime.pop();
|
||||||
|
|
||||||
float value = runtime.getContext().getMemberFloat (id, variable);
|
float value = runtime.getContext().getMemberFloat (id, variable, mGlobal);
|
||||||
runtime[0].mFloat = value;
|
runtime[0].mFloat = value;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -67,7 +67,13 @@ Allowed options:
|
||||||
--script-run arg select a file containing a list of
|
--script-run arg select a file containing a list of
|
||||||
console commands that is executed on
|
console commands that is executed on
|
||||||
startup
|
startup
|
||||||
--new-game [=arg(=1)] (=0) activate char gen/new game mechanics
|
--script-warn [=arg(=1)] (=1) handling of warnings when compiling
|
||||||
|
scripts
|
||||||
|
0 - ignore warning
|
||||||
|
1 - show warning but consider script as
|
||||||
|
correctly compiled anyway
|
||||||
|
2 - treat warnings as errors
|
||||||
|
--skip-menu [=arg(=1)] (=0) skip main menu on game startup
|
||||||
--fs-strict [=arg(=1)] (=0) strict file system handling (no case
|
--fs-strict [=arg(=1)] (=0) strict file system handling (no case
|
||||||
folding)
|
folding)
|
||||||
--encoding arg (=win1252) Character encoding used in OpenMW game
|
--encoding arg (=win1252) Character encoding used in OpenMW game
|
||||||
|
|
Loading…
Reference in a new issue