Something went wrong. Try again.
OpenMW is an open implementation of the Morrowind engine
Something went wrong. Try again.
17 kB · 520 lines
C++
at master
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521#include "lineparser.hpp"
#include <memory>
#include <components/esm/refid.hpp>#include <components/misc/strings/lower.hpp>
#include "context.hpp"#include "declarationparser.hpp"#include "errorhandler.hpp"#include "extensions.hpp"#include "generator.hpp"#include "locals.hpp"#include "scanner.hpp"#include "skipparser.hpp"
namespace Compiler{ void LineParser::parseExpression(Scanner& scanner, const TokenLoc& loc) { mExprParser.reset();
if (!mExplicit.empty()) { mExprParser.parseName(mExplicit, loc, scanner); if (mState == MemberState) mExprParser.parseSpecial(Scanner::S_member, loc, scanner); else mExprParser.parseSpecial(Scanner::S_ref, loc, scanner); }
scanner.scan(mExprParser);
char type = mExprParser.append(mCode); mState = EndState;
switch (type) { case 'l':
Generator::report(mCode, mLiterals, "%d"); break;
case 'f':
Generator::report(mCode, mLiterals, "%f"); break;
default:
throw std::runtime_error("Unknown expression result type"); } }
LineParser::LineParser(ErrorHandler& errorHandler, const Context& context, Locals& locals, Literals& literals, std::vector<Interpreter::Type_Code>& code, bool allowExpression) : Parser(errorHandler, context) , mLocals(locals) , mLiterals(literals) , mCode(code) , mState(BeginState) , mReferenceMember(false) , mType(0) , mExprParser(errorHandler, context, locals, literals) , mAllowExpression(allowExpression) { }
bool LineParser::parseInt(int value, const TokenLoc& loc, Scanner& scanner) { if (mAllowExpression && mState == BeginState) { scanner.putbackInt(value, loc); parseExpression(scanner, loc); return true; } else if (mState == SetState) { // Allow ints to be used as variable names return parseName(loc.mLiteral, loc, scanner); }
return Parser::parseInt(value, loc, scanner); }
bool LineParser::parseFloat(float value, const TokenLoc& loc, Scanner& scanner) { if (mAllowExpression && mState == BeginState) { scanner.putbackFloat(value, loc); parseExpression(scanner, loc); return true; }
return Parser::parseFloat(value, loc, scanner); }
bool LineParser::parseName(const std::string& name, const TokenLoc& loc, Scanner& scanner) { if (mState == SetState) { std::string name2 = Misc::StringUtils::lowerCase(name); mName = name2;
// local variable? char type = mLocals.getType(name2); if (type != ' ') { mType = type; mState = SetLocalVarState; return true; }
type = getContext().getGlobalType(name2); if (type != ' ') { mType = type; mState = SetGlobalVarState; return true; }
mState = SetPotentialMemberVarState; return true; }
if (mState == SetMemberVarState) { mMemberName = Misc::StringUtils::lowerCase(name); std::pair<char, bool> type = getContext().getMemberType(mMemberName, ESM::RefId::stringRefId(mName));
if (type.first != ' ') { mState = SetMemberVarState2; mType = type.first; mReferenceMember = type.second; return true; }
getErrorHandler().error("Unknown variable", loc); SkipParser skip(getErrorHandler(), getContext()); scanner.scan(skip); return false; }
if (mState == MessageState) { GetArgumentsFromMessageFormat processor; processor.process(name); std::string arguments = processor.getArguments();
if (!arguments.empty()) { mExprParser.reset(); mExprParser.parseArguments(arguments, scanner, mCode, -1, true); }
mName = name;
mState = MessageButtonState; // Every message box button argument is essentially a string literal. // Morrowind's console breaks if it encounters a special character // that can't possibly start a string. // It uses the last valid literal in the buffer for the rest of the buttons. // ...Nope. Let's throw instead. scanner.enableExpectName(); scanner.enableTolerantNames(); return true; }
if (mState == MessageButtonState) { mPotentialButtons.push_back(name); // Replicate the fascinating behavior where, to determine the number of actual buttons, // Morrowind counts the number of quoted arguments. if (loc.mLiteral.size() >= 2 && loc.mLiteral.front() == '"' && loc.mLiteral.back() == '"') ++mNumButtons; // This might have been a string that started with a . or a -, restore this scanner.enableExpectName(); return true; }
if (mState == BeginState && getContext().isId(ESM::RefId::stringRefId(name))) { mState = PotentialExplicitState; mExplicit = Misc::StringUtils::lowerCase(name); return true; }
if (mState == BeginState && mAllowExpression) { std::string name2 = Misc::StringUtils::lowerCase(name);
char type = mLocals.getType(name2);
if (type != ' ') { scanner.putbackName(name, loc); parseExpression(scanner, loc); return true; }
type = getContext().getGlobalType(name2);
if (type != ' ') { scanner.putbackName(name, loc); parseExpression(scanner, loc); return true; } }
return Parser::parseName(name, loc, scanner); }
bool LineParser::parseKeyword(int keyword, const TokenLoc& loc, Scanner& scanner) { if (mState == MessageState) { 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 std::string name = loc.mLiteral; if (name.size() >= 2 && name[0] == '"' && name[name.size() - 1] == '"') name = name.substr(1, name.size() - 2); return parseName(name, loc, scanner); } } }
if (mState == SetMemberVarState) { mMemberName = loc.mLiteral; std::pair<char, bool> type = getContext().getMemberType(mMemberName, ESM::RefId::stringRefId(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", loc); SkipParser skip(getErrorHandler(), getContext()); scanner.scan(skip); return false; }
if (mState == SetState || mState == MessageButtonState) { // Allow keywords to be used as variable names and as buttons. return parseName(loc.mLiteral, loc, scanner); }
if (mState == BeginState || mState == ExplicitState) { // check for custom extensions if (const Extensions* extensions = getContext().getExtensions()) { std::string argumentType;
bool hasExplicit = mState == ExplicitState; if (extensions->isInstruction(keyword, argumentType, hasExplicit)) { if (!hasExplicit && mState == ExplicitState) { getErrorHandler().warning("Stray explicit reference", loc); mExplicit.clear(); }
std::vector<Interpreter::Type_Code> code; int optionals = mExprParser.parseArguments(argumentType, scanner, code, keyword); mCode.insert(mCode.end(), code.begin(), code.end()); extensions->generateInstructionCode(keyword, mCode, mLiterals, mExplicit, optionals);
SkipParser skip(getErrorHandler(), getContext(), true); scanner.scan(skip);
mState = EndState; return true; }
char returnType; if (extensions->isFunction(keyword, returnType, argumentType, hasExplicit)) { if (!hasExplicit && mState == ExplicitState) { getErrorHandler().warning("Stray explicit reference", loc); mExplicit.clear(); }
if (mAllowExpression) { scanner.putbackKeyword(keyword, loc); parseExpression(scanner, loc); } else { std::vector<Interpreter::Type_Code> code; int optionals = mExprParser.parseArguments(argumentType, scanner, code, keyword); mCode.insert(mCode.end(), code.begin(), code.end()); extensions->generateFunctionCode(keyword, mCode, mLiterals, mExplicit, optionals);
SkipParser skip(getErrorHandler(), getContext(), true); scanner.scan(skip); } mState = EndState; return true; } } }
if (mState == ExplicitState) { // drop stray explicit reference getErrorHandler().warning("Stray explicit reference", loc); mState = BeginState; mExplicit.clear(); }
if (mState == BeginState) { switch (keyword) { case Scanner::K_short: case Scanner::K_long: case Scanner::K_float: { if (!getContext().canDeclareLocals()) { getErrorHandler().error("Local variables cannot 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 false; }
case Scanner::K_set: mState = SetState; return true;
case Scanner::K_messagebox:
mState = MessageState; scanner.enableStrictKeywords(); return true;
case Scanner::K_return:
Generator::exit(mCode); mState = EndState; return true;
case Scanner::K_else:
getErrorHandler().warning("Stray else", loc); mState = EndState; return true;
case Scanner::K_endif:
getErrorHandler().warning("Stray endif", loc); mState = EndState; return true;
case Scanner::K_begin:
getErrorHandler().warning("Stray begin", loc); mState = EndState; return true; } } else if (mState == SetLocalVarState && keyword == Scanner::K_to) { mExprParser.reset(); scanner.scan(mExprParser);
std::vector<Interpreter::Type_Code> code; char type = mExprParser.append(code);
Generator::assignToLocal(mCode, mLocals.getType(mName), mLocals.getIndex(mName), code, type);
mState = EndState; return true; } else if (mState == SetGlobalVarState && keyword == Scanner::K_to) { mExprParser.reset(); scanner.scan(mExprParser);
std::vector<Interpreter::Type_Code> code; char type = mExprParser.append(code);
Generator::assignToGlobal(mCode, mLiterals, mType, mName, code, type);
mState = EndState; return true; } else if (mState == SetMemberVarState2 && keyword == Scanner::K_to) { mExprParser.reset(); scanner.scan(mExprParser);
std::vector<Interpreter::Type_Code> code; char type = mExprParser.append(code);
Generator::assignToMember(mCode, mLiterals, mType, mMemberName, mName, code, type, !mReferenceMember);
mState = EndState; return true; }
return Parser::parseKeyword(keyword, loc, scanner); }
bool LineParser::parseSpecial(int code, const TokenLoc& loc, Scanner& scanner) { if (mState == EndState && code == Scanner::S_open) { getErrorHandler().warning("Stray '[' or '(' at the end of the line", loc); return true; }
if (code == Scanner::S_newline && (mState == EndState || mState == BeginState)) return false;
if (code == Scanner::S_ref && mState == SetPotentialMemberVarState) { getErrorHandler().warning("Stray explicit reference", loc); mState = SetState; return true; }
if (code == Scanner::S_ref && mState == PotentialExplicitState) { mState = ExplicitState; return true; }
if (code == Scanner::S_member && mState == PotentialExplicitState && mAllowExpression) { mState = MemberState; parseExpression(scanner, loc); mState = EndState; return true; }
if (code == Scanner::S_newline && mState == MessageButtonState) { if (mNumButtons > 10) { getErrorHandler().warning("Only 10 message box buttons can be used", loc); mNumButtons = 10; } for (unsigned int i = 0; i < mNumButtons; ++i) Generator::pushString(mCode, mLiterals, mPotentialButtons[i]); Generator::message(mCode, mLiterals, mName, mNumButtons); mPotentialButtons.clear(); mNumButtons = 0; return false; }
if (code == Scanner::S_member && mState == SetPotentialMemberVarState) { mState = SetMemberVarState; return true; }
if (mAllowExpression && mState == BeginState && (code == Scanner::S_open || code == Scanner::S_minus || code == Scanner::S_plus)) { scanner.putbackSpecial(code, loc); parseExpression(scanner, loc); mState = EndState; return true; }
return Parser::parseSpecial(code, loc, scanner); }
void LineParser::reset() { mState = BeginState; mName.clear(); mExplicit.clear(); }
void GetArgumentsFromMessageFormat::visitedPlaceholder( Placeholder placeholder, int /*flags*/, int /*width*/, int /*precision*/, Notation /*notation*/) { switch (placeholder) { case StringPlaceholder: mArguments += 'S'; break; case IntegerPlaceholder: mArguments += 'l'; break; case FloatPlaceholder: mArguments += 'f'; break; default: break; } }
}