mirror of
				https://github.com/OpenMW/openmw.git
				synced 2025-11-04 12:26:39 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			855 lines
		
	
	
	
		
			23 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			855 lines
		
	
	
	
		
			23 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"
 | 
						|
 | 
						|
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 2;
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            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
 | 
						|
                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;
 | 
						|
 | 
						|
        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_open)
 | 
						|
        {
 | 
						|
            if (mNextOperand)
 | 
						|
            {
 | 
						|
                mOperators.push_back ('(');
 | 
						|
                mTokenLoc = loc;
 | 
						|
                return true;
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                // no comma was used between arguments
 | 
						|
                scanner.putbackKeyword (code, loc);
 | 
						|
                return false;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        if (code==Scanner::S_close && !mNextOperand)
 | 
						|
        {
 | 
						|
            if (isOpen())
 | 
						|
            {
 | 
						|
                close();
 | 
						|
                return true;
 | 
						|
            }
 | 
						|
 | 
						|
            mTokenLoc = loc;
 | 
						|
            scanner.putbackSpecial (code, loc);
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
 | 
						|
        if (!mNextOperand)
 | 
						|
        {
 | 
						|
            mTokenLoc = loc;
 | 
						|
            char c = 0; // comparison
 | 
						|
 | 
						|
            switch (code)
 | 
						|
            {
 | 
						|
                case Scanner::S_plus: 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)
 | 
						|
    {
 | 
						|
        bool optional = false;
 | 
						|
        int optionalCount = 0;
 | 
						|
 | 
						|
        ExprParser parser (getErrorHandler(), getContext(), mLocals, mLiterals, true);
 | 
						|
        StringParser stringParser (getErrorHandler(), getContext(), mLiterals);
 | 
						|
        DiscardParser discardParser (getErrorHandler(), getContext());
 | 
						|
 | 
						|
        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 if (*iter=='X')
 | 
						|
            {
 | 
						|
                parser.reset();
 | 
						|
 | 
						|
                parser.setOptional (true);
 | 
						|
 | 
						|
                scanner.scan (parser);
 | 
						|
 | 
						|
                if (parser.isEmpty())
 | 
						|
                    break;
 | 
						|
            }
 | 
						|
            else if (*iter=='z')
 | 
						|
            {
 | 
						|
                discardParser.reset();
 | 
						|
                discardParser.setOptional (true);
 | 
						|
 | 
						|
                scanner.scan (discardParser);
 | 
						|
 | 
						|
                if (discardParser.isEmpty())
 | 
						|
                    break;
 | 
						|
            }
 | 
						|
            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;
 | 
						|
    }
 | 
						|
}
 |