Something went wrong. Try again.
OpenMW is an open implementation of the Morrowind engine
Something went wrong. Try again.
21 kB · 762 lines
C++
at master
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763#include "exprparser.hpp"
#include <algorithm>#include <cassert>#include <iterator>#include <sstream>#include <stack>#include <stdexcept>
#include <components/esm/refid.hpp>#include <components/misc/strings/lower.hpp>
#include "context.hpp"#include "discardparser.hpp"#include "errorhandler.hpp"#include "extensions.hpp"#include "generator.hpp"#include "junkparser.hpp"#include "locals.hpp"#include "scanner.hpp"#include "stringparser.hpp"
namespace Compiler{ int ExprParser::getPriority(char op) { switch (op) { case '(':
return 0;
case 'e': // == case 'n': // != case 'l': // < case 'L': // <= case 'g': // < case 'G': // >=
return 1;
case '+': case '-':
return 2;
case '*': case '/':
return 3;
case 'm':
return 4; }
return 0; }
char ExprParser::getOperandType(int index) const { assert(!mOperands.empty()); assert(index >= 0); assert(index < static_cast<int>(mOperands.size())); return mOperands[mOperands.size() - 1 - index]; }
char ExprParser::getOperator() const { assert(!mOperators.empty()); return mOperators[mOperators.size() - 1]; }
bool ExprParser::isOpen() const { return std::find(mOperators.begin(), mOperators.end(), '(') != mOperators.end(); }
void ExprParser::popOperator() { assert(!mOperators.empty()); mOperators.resize(mOperators.size() - 1); }
void ExprParser::popOperand() { assert(!mOperands.empty()); mOperands.resize(mOperands.size() - 1); }
void ExprParser::replaceBinaryOperands() { char t1 = getOperandType(1); char t2 = getOperandType();
popOperand(); popOperand();
if (t1 == t2) mOperands.push_back(t1); else if (t1 == 'f' || t2 == 'f') mOperands.push_back('f'); else throw std::logic_error("Failed to determine result operand type"); }
void ExprParser::pop() { char op = getOperator();
switch (op) { case 'm':
Generator::negate(mCode, getOperandType()); popOperator(); break;
case '+':
Generator::add(mCode, getOperandType(1), getOperandType()); popOperator(); replaceBinaryOperands(); break;
case '-':
Generator::sub(mCode, getOperandType(1), getOperandType()); popOperator(); replaceBinaryOperands(); break;
case '*':
Generator::mul(mCode, getOperandType(1), getOperandType()); popOperator(); replaceBinaryOperands(); break;
case '/':
Generator::div(mCode, getOperandType(1), getOperandType()); popOperator(); replaceBinaryOperands(); break;
case 'e': case 'n': case 'l': case 'L': case 'g': case 'G':
Generator::compare(mCode, op, getOperandType(1), getOperandType()); popOperator(); popOperand(); popOperand(); mOperands.push_back('l'); break;
default:
throw std::logic_error("Unknown operator"); } }
void ExprParser::pushIntegerLiteral(int value) { mNextOperand = false; mOperands.push_back('l'); Generator::pushInt(mCode, mLiterals, value); }
void ExprParser::pushFloatLiteral(float value) { mNextOperand = false; mOperands.push_back('f'); Generator::pushFloat(mCode, mLiterals, value); }
void ExprParser::pushBinaryOperator(char c) { while (!mOperators.empty() && getPriority(getOperator()) >= getPriority(c)) pop();
mOperators.push_back(c); mNextOperand = true; }
void ExprParser::close() { while (getOperator() != '(') pop();
popOperator(); }
int ExprParser::parseArguments(const std::string& arguments, Scanner& scanner) { return parseArguments(arguments, scanner, mCode); }
bool ExprParser::handleMemberAccess(const std::string& name) { mMemberOp = false;
std::string name2 = Misc::StringUtils::lowerCase(name); auto id = ESM::RefId::stringRefId(Misc::StringUtils::lowerCase(mExplicit));
std::pair<char, bool> type = getContext().getMemberType(name2, id);
if (type.first != ' ') { Generator::fetchMember(mCode, mLiterals, type.first, name2, id.getRefIdString(), !type.second);
mNextOperand = false; mExplicit.clear(); mOperands.push_back(type.first == 'f' ? 'f' : 'l'); return true; }
return false; }
ExprParser::ExprParser( ErrorHandler& errorHandler, const Context& context, Locals& locals, Literals& literals, bool argument) : Parser(errorHandler, context) , mLocals(locals) , mLiterals(literals) , mNextOperand(true) , mFirst(true) , mArgument(argument) , mRefOp(false) , mMemberOp(false) { }
bool ExprParser::parseInt(int value, const TokenLoc& loc, Scanner& scanner) { if (!mExplicit.empty()) return Parser::parseInt(value, loc, scanner);
mFirst = false;
if (mNextOperand) { start();
pushIntegerLiteral(value); mTokenLoc = loc; return true; } else { scanner.putbackInt(value, loc); return false; } }
bool ExprParser::parseFloat(float value, const TokenLoc& loc, Scanner& scanner) { if (!mExplicit.empty()) return Parser::parseFloat(value, loc, scanner);
mFirst = false;
if (mNextOperand) { start();
pushFloatLiteral(value); mTokenLoc = loc; return true; } else { scanner.putbackFloat(value, loc); return false; } }
bool ExprParser::parseName(const std::string& name, const TokenLoc& loc, Scanner& scanner) { if (!mExplicit.empty()) { if (!mRefOp) { if (mMemberOp && handleMemberAccess(name)) return true;
return Parser::parseName(name, loc, scanner); } else { mExplicit.clear(); getErrorHandler().warning("Stray explicit reference", loc); } }
mFirst = false;
if (mNextOperand) { start();
std::string name2 = Misc::StringUtils::lowerCase(name);
char type = mLocals.getType(name2);
if (type != ' ') { Generator::fetchLocal(mCode, type, mLocals.getIndex(name2)); mNextOperand = false; mOperands.push_back(type == 'f' ? 'f' : 'l'); return true; }
type = getContext().getGlobalType(name2);
if (type != ' ') { Generator::fetchGlobal(mCode, mLiterals, type, name2); mNextOperand = false; mOperands.push_back(type == 'f' ? 'f' : 'l'); return true; }
if (mExplicit.empty() && getContext().isId(ESM::RefId::stringRefId(name2))) { mExplicit = std::move(name2); return true; }
// This is terrible, but of course we must have this for legacy content. // Convert the string to a number even if it's impossible and use it as a number literal. // Can't use stof/atof or to_string out of locale concerns. float number; std::stringstream stream(name2); stream >> number; stream.str(std::string()); stream.clear(); stream << number;
pushFloatLiteral(number); mTokenLoc = loc; getErrorHandler().warning("Parsing a non-variable string as a number: " + stream.str(), loc); return true; } else { scanner.putbackName(name, loc); return false; } }
bool ExprParser::parseKeyword(int keyword, const TokenLoc& loc, Scanner& scanner) { if (const Extensions* extensions = getContext().getExtensions()) { char returnType; // ignored std::string argumentType; // ignored bool hasExplicit = false; // ignored bool isInstruction = extensions->isInstruction(keyword, argumentType, hasExplicit);
if (isInstruction || (mExplicit.empty() && extensions->isFunction(keyword, returnType, argumentType, hasExplicit))) { std::string name = loc.mLiteral; if (name.size() >= 2 && name[0] == '"' && name[name.size() - 1] == '"') name = name.substr(1, name.size() - 2); if (isInstruction || mLocals.getType(Misc::StringUtils::lowerCase(name)) != ' ') { // pretend this is not a keyword return parseName(name, 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) { return parseName(loc.mLiteral, loc, scanner); }
mFirst = false;
if (!mExplicit.empty()) { if (mRefOp && mNextOperand) {
// check for custom extensions if (const Extensions* extensions = getContext().getExtensions()) { char returnType; std::string argumentType;
bool hasExplicit = true; if (extensions->isFunction(keyword, returnType, argumentType, hasExplicit)) { if (!hasExplicit) { getErrorHandler().warning("Stray explicit reference", loc); mExplicit.clear(); }
start();
mTokenLoc = loc; int optionals = parseArguments(argumentType, scanner);
extensions->generateFunctionCode(keyword, mCode, mLiterals, mExplicit, optionals); mOperands.push_back(returnType); mExplicit.clear(); mRefOp = false;
mNextOperand = false; return true; } } }
return Parser::parseKeyword(keyword, loc, scanner); }
if (mNextOperand) { // check for custom extensions if (const Extensions* extensions = getContext().getExtensions()) { start();
char returnType; std::string argumentType;
bool hasExplicit = false;
if (extensions->isFunction(keyword, returnType, argumentType, hasExplicit)) { mTokenLoc = loc; int optionals = parseArguments(argumentType, scanner);
extensions->generateFunctionCode(keyword, mCode, mLiterals, "", optionals); mOperands.push_back(returnType);
mNextOperand = false; return true; } } } else { scanner.putbackKeyword(keyword, loc); return false; }
return Parser::parseKeyword(keyword, loc, scanner); }
bool ExprParser::parseSpecial(int code, const TokenLoc& loc, Scanner& scanner) { 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) { mRefOp = true; return true; }
if (!mMemberOp && code == Scanner::S_member) { mMemberOp = true; return true; }
return Parser::parseSpecial(code, loc, scanner); }
mFirst = false;
if (code == Scanner::S_newline) { // end marker if (mTokenLoc.mLiteral.empty()) mTokenLoc = loc; scanner.putbackSpecial(code, loc); return false; }
if (code == Scanner::S_minus && mNextOperand) { // unary mOperators.push_back('m'); mTokenLoc = loc; return true; }
if (code == Scanner::S_plus && mNextOperand) { // Also unary, but +, just ignore it mTokenLoc = loc; return true; }
if (code == Scanner::S_open) { if (mNextOperand) { mOperators.push_back('('); mTokenLoc = loc; return true; } else { scanner.putbackSpecial(code, loc); return false; } }
if (code == Scanner::S_close && !mNextOperand) { if (isOpen()) { close(); return true; }
mTokenLoc = loc; scanner.putbackSpecial(code, loc); return false; }
if (!mNextOperand) { mTokenLoc = loc; char c = 0; // comparison
switch (code) { case Scanner::S_plus: c = '+'; break; case Scanner::S_minus: c = '-'; break; case Scanner::S_mult: pushBinaryOperator('*'); return true; case Scanner::S_div: pushBinaryOperator('/'); return true; case Scanner::S_cmpEQ: c = 'e'; break; case Scanner::S_cmpNE: c = 'n'; break; case Scanner::S_cmpLT: c = 'l'; break; case Scanner::S_cmpLE: c = 'L'; break; case Scanner::S_cmpGT: c = 'g'; break; case Scanner::S_cmpGE: c = 'G'; break; }
if (c) { if (mArgument && !isOpen()) { // expression ends here // Thank you Morrowind for this rotten syntax :( scanner.putbackSpecial(code, loc); return false; }
pushBinaryOperator(c); return true; } }
return Parser::parseSpecial(code, loc, scanner); }
void ExprParser::reset() { mOperands.clear(); mOperators.clear(); mNextOperand = true; mCode.clear(); mFirst = true; mExplicit.clear(); mRefOp = false; mMemberOp = false; Parser::reset(); }
char ExprParser::append(std::vector<Interpreter::Type_Code>& code) { if (mOperands.empty() && mOperators.empty()) { getErrorHandler().error("Missing expression", mTokenLoc); return 'l'; }
if (mNextOperand || mOperands.empty()) { getErrorHandler().error("Syntax error in expression", mTokenLoc); return 'l'; }
while (!mOperators.empty()) pop();
std::copy(mCode.begin(), mCode.end(), std::back_inserter(code));
assert(mOperands.size() == 1); return mOperands[0]; }
int ExprParser::parseArguments(const std::string& arguments, Scanner& scanner, std::vector<Interpreter::Type_Code>& code, int ignoreKeyword, bool expectNames) { bool optional = false; int optionalCount = 0;
ExprParser parser(getErrorHandler(), getContext(), mLocals, mLiterals, true); StringParser stringParser(getErrorHandler(), getContext(), mLiterals); DiscardParser discardParser(getErrorHandler(), getContext()); JunkParser junkParser(getErrorHandler(), getContext(), ignoreKeyword);
std::stack<std::vector<Interpreter::Type_Code>> stack;
for (char argument : arguments) { if (argument == '/') { optional = true; } else if (argument == 'S' || argument == 'c' || argument == 'x') { stringParser.reset();
if (optional || argument == 'x') stringParser.setOptional(true);
if (argument == 'c') stringParser.smashCase(); if (argument == 'x') stringParser.discard(); scanner.enableExpectName(); scanner.scan(stringParser);
if ((optional || argument == 'x') && stringParser.isEmpty()) break;
if (argument != 'x') { std::vector<Interpreter::Type_Code> tmp; stringParser.append(tmp);
stack.push(tmp);
if (optional) ++optionalCount; } else getErrorHandler().warning("Extra argument", stringParser.getTokenLoc()); } else if (argument == 'X') { parser.reset();
parser.setOptional(true);
scanner.scan(parser);
if (parser.isEmpty()) break; else getErrorHandler().warning("Extra argument", parser.getTokenLoc()); } else if (argument == 'z') { discardParser.reset(); discardParser.setOptional(true);
scanner.scan(discardParser);
if (discardParser.isEmpty()) break; else getErrorHandler().warning("Extra argument", discardParser.getTokenLoc()); } else if (argument == 'j') { /// \todo disable this when operating in strict mode junkParser.reset();
scanner.scan(junkParser); } else { parser.reset();
if (optional) parser.setOptional(true); if (expectNames) scanner.enableExpectName();
scanner.scan(parser);
if (optional && parser.isEmpty()) break;
std::vector<Interpreter::Type_Code> tmp;
char type = parser.append(tmp);
if (type != argument) Generator::convert(tmp, type, argument);
stack.push(tmp);
if (optional) ++optionalCount; } }
while (!stack.empty()) { std::vector<Interpreter::Type_Code>& tmp = stack.top();
std::copy(tmp.begin(), tmp.end(), std::back_inserter(code));
stack.pop(); }
return optionalCount; }
const TokenLoc& ExprParser::getTokenLoc() const { return mTokenLoc; }}