You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
openmw-tes3mp/components/compiler/exprparser.cpp

880 lines
24 KiB
C++

#include "exprparser.hpp"
#include <stdexcept>
#include <cassert>
#include <algorithm>
#include <stack>
#include <iterator>
#include <components/misc/stringops.hpp>
#include "generator.hpp"
#include "scanner.hpp"
#include "errorhandler.hpp"
#include "locals.hpp"
#include "stringparser.hpp"
#include "extensions.hpp"
#include "context.hpp"
#include "discardparser.hpp"
#include "junkparser.hpp"
namespace Compiler
{
int ExprParser::getPriority (char op) const
{
switch (op)
{
case '(':
return 0;
case 'e': // ==
case 'n': // !=
case 'l': // <
case 'L': // <=
case 'g': // <
case 'G': // >=
return 1;
case '+':
case '-':
return 2;
case '*':
case '/':
return 3;
case 'm':
return 4;
}
return 0;
}
char ExprParser::getOperandType (int Index) const
{
assert (!mOperands.empty());
assert (Index>=0);
assert (Index<static_cast<int> (mOperands.size()));
return mOperands[mOperands.size()-1-Index];
}
char ExprParser::getOperator() const
{
assert (!mOperators.empty());
return mOperators[mOperators.size()-1];
}
bool ExprParser::isOpen() const
{
return std::find (mOperators.begin(), mOperators.end(), '(')!=mOperators.end();
}
void ExprParser::popOperator()
{
assert (!mOperators.empty());
mOperators.resize (mOperators.size()-1);
}
void ExprParser::popOperand()
{
assert (!mOperands.empty());
mOperands.resize (mOperands.size()-1);
}
void ExprParser::replaceBinaryOperands()
{
char t1 = getOperandType (1);
char t2 = getOperandType();
popOperand();
popOperand();
if (t1==t2)
mOperands.push_back (t1);
else if (t1=='f' || t2=='f')
mOperands.push_back ('f');
else
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);
std::string id = Misc::StringUtils::lowerCase (mExplicit);
std::pair<char, bool> type = getContext().getMemberType (name2, id);
if (type.first!=' ')
{
Generator::fetchMember (mCode, mLiterals, type.first, name2, id, !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
{
// no comma was used between arguments
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
{
// no comma was used between arguments
scanner.putbackFloat (value, loc);
return false;
}
}
bool ExprParser::parseName (const std::string& name, const TokenLoc& loc,
Scanner& scanner)
{
if (!mExplicit.empty())
{
if (mMemberOp && handleMemberAccess (name))
return true;
return Parser::parseName (name, loc, scanner);
}
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;
}
// 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 true;
}
}
if (mExplicit.empty() && getContext().isId (name2))
{
mExplicit = name2;
return true;
}
}
else
{
// no comma was used between arguments
scanner.putbackName (name, loc);
return false;
}
return Parser::parseName (name, loc, scanner);
}
bool ExprParser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner)
{
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 (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;
if (!mExplicit.empty())
{
if (mRefOp && mNextOperand)
{
if (keyword==Scanner::K_getdisabled)
{
start();
mTokenLoc = loc;
Generator::getDisabled (mCode, mLiterals, mExplicit);
mOperands.push_back ('l');
mExplicit.clear();
mRefOp = false;
std::vector<Interpreter::Type_Code> ignore;
parseArguments ("x", scanner, ignore);
mNextOperand = false;
return true;
}
else if (keyword==Scanner::K_getdistance)
{
start();
mTokenLoc = loc;
parseArguments ("c", scanner);
Generator::getDistance (mCode, mLiterals, mExplicit);
mOperands.push_back ('f');
mExplicit.clear();
mRefOp = false;
mNextOperand = false;
return true;
}
else if (keyword==Scanner::K_scriptrunning)
{
start();
mTokenLoc = loc;
parseArguments ("c", scanner);
Generator::scriptRunning (mCode);
mOperands.push_back ('l');
mExplicit.clear();
mRefOp = false;
mNextOperand = false;
return true;
}
// 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 (ignoring it)", 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)
{
if (keyword==Scanner::K_getsquareroot)
{
start();
mTokenLoc = loc;
parseArguments ("f", scanner);
Generator::squareRoot (mCode);
mOperands.push_back ('f');
mNextOperand = false;
return true;
}
else if (keyword==Scanner::K_menumode)
{
start();
mTokenLoc = loc;
Generator::menuMode (mCode);
mOperands.push_back ('l');
mNextOperand = false;
return true;
}
else if (keyword==Scanner::K_random)
{
start();
mTokenLoc = loc;
parseArguments ("l", scanner);
Generator::random (mCode);
mOperands.push_back ('l');
mNextOperand = false;
return true;
}
else if (keyword==Scanner::K_scriptrunning)
{
start();
mTokenLoc = loc;
parseArguments ("c", scanner);
Generator::scriptRunning (mCode);
mOperands.push_back ('l');
mNextOperand = false;
return true;
}
else if (keyword==Scanner::K_getdistance)
{
start();
mTokenLoc = loc;
parseArguments ("c", scanner);
Generator::getDistance (mCode, mLiterals, "");
mOperands.push_back ('f');
mNextOperand = false;
return true;
}
else if (keyword==Scanner::K_getsecondspassed)
{
start();
mTokenLoc = loc;
Generator::getSecondsPassed (mCode);
mOperands.push_back ('f');
mNextOperand = false;
return true;
}
else if (keyword==Scanner::K_getdisabled)
{
start();
mTokenLoc = loc;
Generator::getDisabled (mCode, mLiterals, "");
mOperands.push_back ('l');
std::vector<Interpreter::Type_Code> ignore;
parseArguments ("x", scanner, ignore);
mNextOperand = false;
return true;
}
else
{
// 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
{
// no comma was used between arguments
scanner.putbackKeyword (keyword, loc);
return false;
}
return Parser::parseKeyword (keyword, loc, scanner);
}
bool ExprParser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner)
{
if (!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);
}
if (code==Scanner::S_comma)
{
mTokenLoc = loc;
if (mFirst)
{
// leading comma
mFirst = false;
return true;
}
// end marker
scanner.putbackSpecial (code, loc);
return false;
}
mFirst = false;
if (code==Scanner::S_newline)
{
// end marker
mTokenLoc = loc;
scanner.putbackSpecial (code, loc);
return false;
}
if (code==Scanner::S_minus && mNextOperand)
{
// unary
mOperators.push_back ('m');
mTokenLoc = loc;
return true;
}
if (code ==Scanner::S_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
{
// no comma was used between arguments
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 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 (std::string::const_iterator iter (arguments.begin()); iter!=arguments.end();
++iter)
{
if (*iter=='/')
{
optional = true;
}
else if (*iter=='S' || *iter=='c' || *iter=='x')
{
stringParser.reset();
if (optional || *iter=='x')
stringParser.setOptional (true);
if (*iter=='c') stringParser.smashCase();
scanner.scan (stringParser);
if (optional && stringParser.isEmpty())
break;
if (*iter!='x')
{
std::vector<Interpreter::Type_Code> tmp;
stringParser.append (tmp);
stack.push (tmp);
if (optional)
++optionalCount;
}
else
getErrorHandler().warning("Ignoring extra argument", mTokenLoc);
}
else if (*iter=='X')
{
parser.reset();
parser.setOptional (true);
scanner.scan (parser);
if (parser.isEmpty())
break;
else
getErrorHandler().warning("Ignoring extra argument", mTokenLoc);
}
else if (*iter=='z')
{
discardParser.reset();
discardParser.setOptional (true);
scanner.scan (discardParser);
if (discardParser.isEmpty())
break;
else
getErrorHandler().warning("Ignoring extra argument", mTokenLoc);
}
else if (*iter=='j')
{
/// \todo disable this when operating in strict mode
junkParser.reset();
scanner.scan (junkParser);
}
else
{
parser.reset();
if (optional)
parser.setOptional (true);
scanner.scan (parser);
if (optional && parser.isEmpty())
break;
std::vector<Interpreter::Type_Code> tmp;
char type = parser.append (tmp);
if (type!=*iter)
Generator::convert (tmp, type, *iter);
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;
}
}