diff --git a/CMakeLists.txt b/CMakeLists.txt index 1cbf84210..76dd9c82f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -105,12 +105,23 @@ set(MISC_HEADER components/misc/fileops.hpp components/misc/slice_array.hpp components/misc/stringops.hpp) -source_group(misc FILES ${MISC} ${MISC_HEADER}) +source_group(misc FILES ${MISC} ${MISC_HEADER}) + +file(GLOB COMPILER components/compiler/*.cpp) +file(GLOB COMPILER_HEADER components/compiler/*.hpp) +source_group(compiler FILES ${COMPILER} ${COMPILER_HEADER}) + +file(GLOB INTERPRETER components/interpreter/*.cpp) +file(GLOB INTERPRETER_HEADER components/interpreter/*.hpp) +source_group(interpreter FILES ${INTERPRETER} ${INTERPRETER_HEADER}) + -set(COMPONENTS ${BSA} ${NIF} ${NIFOGRE} ${ESM_STORE} ${OGRE} ${INPUT} ${MISC}) +set(COMPONENTS ${BSA} ${NIF} ${NIFOGRE} ${ESM_STORE} ${OGRE} ${INPUT} ${MISC} ${COMPILER} + ${INTERPRETER}) set(COMPONENTS_HEADER ${BSA_HEADER} ${NIF_HEADER} ${NIFOGRE_HEADER} ${ESM_STORE_HEADER} - ${ESM_HEADER} ${OGRE_HEADER} ${INPUT_HEADER} ${MISC_HEADER}) - + ${ESM_HEADER} ${OGRE_HEADER} ${INPUT_HEADER} ${MISC_HEADER} ${COMPILER_HEADER} + ${INTERPRETER_HEADER}) + # source directory: libs set(MANGLE_VFS libs/mangle/vfs/servers/ogre_vfs.cpp) @@ -203,3 +214,22 @@ if (APPLE) MACOSX_BUNDLE_BUNDLE_NAME "OpenMW" ) endif (APPLE) + +# Tools +option(BUILD_MWCOMPILER "build standalone Morrowind script compiler" ON) + +if (BUILD_MWCOMPILER) +set(TOOLS_MWCOMPILER ${COMPILER} apps/mwcompiler/main.cpp apps/mwcompiler/context.cpp + apps/mwcompiler/context.hpp) + +add_executable(mwcompiler ${TOOLS_MWCOMPILER}) +endif() + +option(BUILD_MWINTERPRETER "build standalone Morrowind script code interpreter" ON) + +if (BUILD_MWINTERPRETER) +set(TOOLS_MWINTERPRETER ${INTERPRETER} apps/mwinterpreter/main.cpp + apps/mwinterpreter/context.cpp apps/mwinterpreter/context.hpp) + +add_executable(mwinterpreter ${TOOLS_MWINTERPRETER}) +endif() diff --git a/apps/mwcompiler/context.cpp b/apps/mwcompiler/context.cpp new file mode 100644 index 000000000..72e3aa490 --- /dev/null +++ b/apps/mwcompiler/context.cpp @@ -0,0 +1,11 @@ + +#include "context.hpp" + +namespace SACompiler +{ + bool Context::canDeclareLocals() const + { + return true; + } +} + diff --git a/apps/mwcompiler/context.hpp b/apps/mwcompiler/context.hpp new file mode 100644 index 000000000..23188ecf2 --- /dev/null +++ b/apps/mwcompiler/context.hpp @@ -0,0 +1,18 @@ +#ifndef MWCOMPILER_CONTEXT_H_INCLUDED +#define MWCOMPILER_CONTEXT_H_INCLUDED + +#include + +namespace SACompiler +{ + class Context : public Compiler::Context + { + public: + + virtual bool canDeclareLocals() const; + ///< Is the compiler allowed to declare local variables? + }; +} + +#endif + diff --git a/apps/mwcompiler/main.cpp b/apps/mwcompiler/main.cpp new file mode 100644 index 000000000..a19a50ab3 --- /dev/null +++ b/apps/mwcompiler/main.cpp @@ -0,0 +1,78 @@ +// Stand-alone MW-script compiler + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "context.hpp" + +int main (int argc, char **argv) +{ + try + { + SACompiler::Context context; + Compiler::StreamErrorHandler errorHandler (std::cout); + Compiler::FileParser parser (errorHandler, context); + + std::string filename = argc>1 ? argv[1] : "test.mwscript"; + + try + { + std::ifstream file (filename.c_str()); + + if (!file.is_open()) + { + std::cout << "can't open script file: " << filename << std::endl; + return 1; + } + + Compiler::Scanner scanner (errorHandler, file); + + scanner.scan (parser); + } + catch (const Compiler::SourceException&) + { + // ignore exception (problem has already been reported to the user) + } + + if (errorHandler.countErrors() || errorHandler.countWarnings()) + { + std::cout + << errorHandler.countErrors() << " error(s), " + << errorHandler.countWarnings() << " warning(s)" << std::endl + << std::endl; + } + + if (errorHandler.isGood()) + { + std::vector code; + parser.getCode (code); + + std::ofstream codeFile ((filename + ".code").c_str()); + + codeFile.write (reinterpret_cast (&code[0]), + code.size()*sizeof (Interpreter::Type_Code)); + + std::ofstream localFile ((filename + ".locals").c_str()); + + parser.getLocals().write (localFile); + + return 0; + } + + return 1; + } + catch (const std::exception &e) + { + std::cout << "\nERROR: " << e.what() << std::endl; + return 1; + } +} + diff --git a/apps/mwinterpreter/context.cpp b/apps/mwinterpreter/context.cpp new file mode 100644 index 000000000..09d70ff3e --- /dev/null +++ b/apps/mwinterpreter/context.cpp @@ -0,0 +1,102 @@ + +#include "context.hpp" + +#include +#include +#include +#include + +namespace SAInterpreter +{ + Context::Context (const std::string& filename) + { + std::ifstream file (filename.c_str()); + + if (!file.is_open()) + throw std::runtime_error ("can't open locals file: " + filename); + + std::size_t shortSize, longSize, floatSize; + + file >> shortSize >> longSize >> floatSize; + + mShorts.resize (shortSize, 0); + mLongs.resize (longSize, 0); + mFloats.resize (floatSize, 0.0); + + std::size_t size = shortSize + longSize + floatSize; + + mNames.resize (size); + + for (std::size_t i=0; i> mNames[i]; + } + + int Context::getLocalShort (int index) const + { + assert (index>=0); + return mShorts.at (index); + } + + int Context::getLocalLong (int index) const + { + assert (index>=0); + return mLongs.at (index); + } + + float Context::getLocalFloat (int index) const + { + assert (index>=0); + return mFloats.at (index); + } + + void Context::setLocalShort (int index, int value) + { + assert (index>=0); + mShorts.at (index) = value; + } + + void Context::setLocalLong (int index, int value) + { + assert (index>=0); + mLongs.at (index) = value; + } + + void Context::setLocalFloat (int index, float value) + { + assert (index>=0); + mFloats.at (index) = value; + } + + void Context::messageBox (const std::string& message, + const std::vector& buttons) + { + std::cout << "message box: " << message << std::endl; + for (std::size_t i=0; i::const_iterator iter (mShorts.begin()); + iter!=mShorts.end(); ++iter) + std::cout << mNames[i++] << ": " << *iter << std::endl; + + std::cout << "local longs:" << std::endl; + + for (std::vector::const_iterator iter (mLongs.begin()); + iter!=mLongs.end(); ++iter) + std::cout << mNames[i++] << ": " << *iter << std::endl; + + std::cout << "local floats:" << std::endl; + + for (std::vector::const_iterator iter (mFloats.begin()); + iter!=mFloats.end(); ++iter) + std::cout << mNames[i++] << ": " << *iter << std::endl; + + } +} + diff --git a/apps/mwinterpreter/context.hpp b/apps/mwinterpreter/context.hpp new file mode 100644 index 000000000..0f7ce5cef --- /dev/null +++ b/apps/mwinterpreter/context.hpp @@ -0,0 +1,46 @@ +#ifndef SAINTERPRETER_CONTEXT_H_INCLUDED +#define SAINTERPRETER_CONTEXT_H_INCLUDED + +#include +#include + +#include +#include + +namespace SAInterpreter +{ + class Context : public Interpreter::Context + { + std::vector mShorts; + std::vector mLongs; + std::vector mFloats; + std::vector mNames; + + public: + + Context (const std::string& filename); + ///< Create context from file + /// \note A context for an integreted interpreter will typically not + /// configure at construction, but will offer a separate function. + + virtual int getLocalShort (int index) const; + + virtual int getLocalLong (int index) const; + + virtual float getLocalFloat (int index) const; + + virtual void setLocalShort (int index, int value); + + virtual void setLocalLong (int index, int value); + + virtual void setLocalFloat (int index, float value); + + virtual void messageBox (const std::string& message, + const std::vector& buttons); + + void report(); + ///< Write state to std::cout + }; +} + +#endif diff --git a/apps/mwinterpreter/main.cpp b/apps/mwinterpreter/main.cpp new file mode 100644 index 000000000..e8f0c8783 --- /dev/null +++ b/apps/mwinterpreter/main.cpp @@ -0,0 +1,57 @@ +// Stand-alone MW-script code interpreter + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "context.hpp" + +int main (int argc, char **argv) +{ + try + { + std::string filename = argc>1 ? argv[1] : "test.mwscript"; + + std::string localfilename = filename + ".locals"; + + SAInterpreter::Context context (localfilename); + Interpreter::Interpreter interpreter (context); + Interpreter::installOpcodes (interpreter); + + std::string codefilename = filename + ".code"; + + std::ifstream codefile (codefilename.c_str()); + + if (!codefile.is_open()) + { + std::cout << "can't open code file: " << codefilename << std::endl; + return 1; + } + + std::vector code (4); + + codefile.read (reinterpret_cast (&code[0]), 4 * sizeof (Interpreter::Type_Code)); + + unsigned int size = code[0] + code[1] + code[2] + code[3]; + + code.resize (4+size); + + codefile.read (reinterpret_cast (&code[4]), size * sizeof (Interpreter::Type_Code)); + + interpreter.run (&code[0], size+4); + + context.report(); + } + catch (const std::exception &e) + { + std::cout << "\nERROR: " << e.what() << std::endl; + return 1; + } +} + diff --git a/components/compiler/context.hpp b/components/compiler/context.hpp new file mode 100644 index 000000000..d5284ffc9 --- /dev/null +++ b/components/compiler/context.hpp @@ -0,0 +1,18 @@ +#ifndef COMPILER_CONTEXT_H_INCLUDED +#define COMPILER_CONTEXT_H_INCLUDED + +namespace Compiler +{ + class Context + { + public: + + virtual ~Context() {} + + virtual bool canDeclareLocals() const = 0; + ///< Is the compiler allowed to declare local variables? + }; +} + +#endif + diff --git a/components/compiler/controlparser.cpp b/components/compiler/controlparser.cpp new file mode 100644 index 000000000..4715d3214 --- /dev/null +++ b/components/compiler/controlparser.cpp @@ -0,0 +1,250 @@ + +#include "controlparser.hpp" + +#include +#include +#include + +#include "scanner.hpp" +#include "generator.hpp" + +namespace Compiler +{ + bool ControlParser::parseIfBody (int keyword, const TokenLoc& loc, Scanner& scanner) + { + if (keyword==Scanner::K_endif || keyword==Scanner::K_elseif || + keyword==Scanner::K_else) + { + std::pair entry; + + if (mState!=IfElseBodyState) + mExprParser.append (entry.first); + + std::copy (mCodeBlock.begin(), mCodeBlock.end(), + std::back_inserter (entry.second)); + + mIfCode.push_back (entry); + + mCodeBlock.clear(); + + if (keyword==Scanner::K_endif) + { + // store code for if-cascade + Codes codes; + + for (IfCodes::reverse_iterator iter (mIfCode.rbegin()); + iter!=mIfCode.rend(); ++iter) + { + Codes block; + + if (iter!=mIfCode.rbegin()) + Generator::jump (iter->second, codes.size()+1); + + if (!iter->first.empty()) + { + // if or elseif + std::copy (iter->first.begin(), iter->first.end(), + std::back_inserter (block)); + Generator::jumpOnZero (block, iter->second.size()+1); + } + + std::copy (iter->second.begin(), iter->second.end(), + std::back_inserter (block)); + + std::swap (codes, block); + + std::copy (block.begin(), block.end(), std::back_inserter (codes)); + } + + std::copy (codes.begin(), codes.end(), std::back_inserter (mCode)); + + mIfCode.clear(); + mState = IfEndifState; + } + else if (keyword==Scanner::K_elseif) + { + mExprParser.reset(); + scanner.scan (mExprParser); + + mState = IfElseifEndState; + } + else if (keyword==Scanner::K_else) + { + mState = IfElseEndState; + } + + return true; + } + else if (keyword==Scanner::K_if || keyword==Scanner::K_while) + { + // nested + ControlParser parser (getErrorHandler(), getContext(), mLocals, mLiterals); + + if (parser.parseKeyword (keyword, loc, scanner)) + scanner.scan (parser); + + parser.appendCode (mCodeBlock); + + return true; + } + else + { + mLineParser.reset(); + if (mLineParser.parseKeyword (keyword, loc, scanner)) + scanner.scan (mLineParser); + + return true; + } + + return false; + } + + bool ControlParser::parseWhileBody (int keyword, const TokenLoc& loc, Scanner& scanner) + { + if (keyword==Scanner::K_endwhile) + { + Codes loop; + + Codes expr; + mExprParser.append (expr); + + Generator::jump (loop, -mCodeBlock.size()-expr.size()); + + std::copy (expr.begin(), expr.end(), std::back_inserter (mCode)); + + Codes skip; + + Generator::jumpOnZero (skip, mCodeBlock.size()+loop.size()+1); + + std::copy (skip.begin(), skip.end(), std::back_inserter (mCode)); + + std::copy (mCodeBlock.begin(), mCodeBlock.end(), std::back_inserter (mCode)); + + Codes loop2; + + Generator::jump (loop2, -mCodeBlock.size()-expr.size()-skip.size()); + + if (loop.size()!=loop2.size()) + throw std::logic_error ( + "internal compiler error: failed to generate a while loop"); + + std::copy (loop2.begin(), loop2.end(), std::back_inserter (mCode)); + + mState = WhileEndwhileState; + return true; + } + else if (keyword==Scanner::K_if || keyword==Scanner::K_while) + { + // nested + ControlParser parser (getErrorHandler(), getContext(), mLocals, mLiterals); + + if (parser.parseKeyword (keyword, loc, scanner)) + scanner.scan (parser); + + parser.appendCode (mCodeBlock); + + return true; + } + else + { + mLineParser.reset(); + if (mLineParser.parseKeyword (keyword, loc, scanner)) + scanner.scan (mLineParser); + + return true; + } + + return false; + } + + ControlParser::ControlParser (ErrorHandler& errorHandler, Context& context, Locals& locals, + Literals& literals) + : Parser (errorHandler, context), mLocals (locals), mLiterals (literals), + mLineParser (errorHandler, context, locals, literals, mCodeBlock), + mExprParser (errorHandler, context, locals, literals), + mState (StartState) + { + + } + + void ControlParser::appendCode (std::vector& code) const + { + std::copy (mCode.begin(), mCode.end(), std::back_inserter (code)); + } + + bool ControlParser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner) + { + if (mState==StartState) + { + if (keyword==Scanner::K_if) + { + mExprParser.reset(); + scanner.scan (mExprParser); + + mState = IfEndState; + return true; + } + else if (keyword==Scanner::K_while) + { + mExprParser.reset(); + scanner.scan (mExprParser); + + mState = WhileEndState; + return true; + } + } + else if (mState==IfBodyState || mState==IfElseifBodyState || mState==IfElseBodyState) + { + if (parseIfBody (keyword, loc, scanner)) + return true; + } + else if (mState==WhileBodyState) + { + if ( parseWhileBody (keyword, loc, scanner)) + return true; + } + + return Parser::parseKeyword (keyword, loc, scanner); + } + + bool ControlParser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner) + { + if (code==Scanner::S_newline) + { + switch (mState) + { + case IfEndState: mState = IfBodyState; return true; + case IfElseifEndState: mState = IfElseifBodyState; return true; + case IfElseEndState: mState = IfElseBodyState; return true; + + case WhileEndState: mState = WhileBodyState; return true; + + case IfBodyState: + case IfElseifBodyState: + case IfElseBodyState: + case WhileBodyState: + + return true; // empty line + + case IfEndifState: + case WhileEndwhileState: + + return false; + + default: ; + } + + } + + return Parser::parseSpecial (code, loc, scanner); + } + + void ControlParser::reset() + { + mCode.clear(); + mCodeBlock.clear(); + mIfCode.clear(); + mState = StartState; + } +} + diff --git a/components/compiler/controlparser.hpp b/components/compiler/controlparser.hpp new file mode 100644 index 000000000..17b3137c7 --- /dev/null +++ b/components/compiler/controlparser.hpp @@ -0,0 +1,70 @@ +#ifndef COMPILER_CONTROLPARSER_H_INCLUDED +#define COMPILER_CONTROLPARSER_H_INCLUDED + +#include + +#include + +#include "parser.hpp" +#include "exprparser.hpp" +#include "lineparser.hpp" + +namespace Compiler +{ + class Locals; + class Literals; + + // Control structure parser + + class ControlParser : public Parser + { + enum State + { + StartState, + IfEndState, IfBodyState, + IfElseifEndState, IfElseifBodyState, + IfElseEndState, IfElseBodyState, + IfEndifState, + WhileEndState, WhileBodyState, + WhileEndwhileState + }; + + typedef std::vector Codes; + typedef std::vector > IfCodes; + + Locals& mLocals; + Literals& mLiterals; + Codes mCode; + Codes mCodeBlock; + IfCodes mIfCode; // condition, body + LineParser mLineParser; + ExprParser mExprParser; + State mState; + + bool parseIfBody (int keyword, const TokenLoc& loc, Scanner& scanner); + + bool parseWhileBody (int keyword, const TokenLoc& loc, Scanner& scanner); + + public: + + ControlParser (ErrorHandler& errorHandler, Context& context, Locals& locals, + Literals& literals); + + void appendCode (std::vector& code) const; + ///< store generated code in \æ code. + + 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(); + ///< Reset parser to clean state. + }; +} + +#endif + diff --git a/components/compiler/errorhandler.cpp b/components/compiler/errorhandler.cpp new file mode 100644 index 000000000..ee13c837d --- /dev/null +++ b/components/compiler/errorhandler.cpp @@ -0,0 +1,65 @@ + +#include "errorhandler.hpp" + +namespace Compiler +{ + // constructor + + ErrorHandler::ErrorHandler() : mWarnings (0), mErrors (0) {} + + // destructor + + ErrorHandler::~ErrorHandler() {} + + // Was compiling successful? + + bool ErrorHandler::isGood() const + { + return mErrors==0; + } + + // Return number of errors + + int ErrorHandler::countErrors() const + { + return mErrors; + } + + // Return number of warnings + + int ErrorHandler::countWarnings() const + { + return mWarnings; + } + + // Generate a warning message. + + void ErrorHandler::warning (const std::string& message, const TokenLoc& loc) + { + ++mWarnings; + report (message, loc, WarningMessage); + } + + // Generate an error message. + + void ErrorHandler::error (const std::string& message, const TokenLoc& loc) + { + ++mErrors; + report (message, loc, ErrorMessage); + } + + // Generate an error message for an unexpected EOF. + + void ErrorHandler::endOfFile() + { + ++mErrors; + report ("unexpected end of file", ErrorMessage); + } + + // Remove all previous error/warning events + + void ErrorHandler::reset() + { + mErrors = mWarnings = 0; + } +} diff --git a/components/compiler/errorhandler.hpp b/components/compiler/errorhandler.hpp new file mode 100644 index 000000000..636eb996a --- /dev/null +++ b/components/compiler/errorhandler.hpp @@ -0,0 +1,68 @@ + +#ifndef COMPILER_ERRORHANDLER_H_INCLUDED +#define COMPILER_ERRORHANDLER_H_INCLUDED + +#include + +namespace Compiler +{ + struct TokenLoc; + + /// \brief Error handling + /// + /// This class collects errors and provides an interface for reporting them to the user. + + class ErrorHandler + { + int mWarnings; + int mErrors; + + protected: + + enum Type + { + WarningMessage, ErrorMessage + }; + + private: + + // mutators + + virtual void report (const std::string& message, const TokenLoc& loc, Type type) = 0; + ///< Report error to the user. + + virtual void report (const std::string& message, Type type) = 0; + ///< Report a file related error + + public: + + ErrorHandler(); + ///< constructor + + virtual ~ErrorHandler(); + ///< destructor + + bool isGood() const; + ///< Was compiling successful? + + int countErrors() const; + ///< Return number of errors + + int countWarnings() const; + ///< Return number of warnings + + void warning (const std::string& message, const TokenLoc& loc); + ///< Generate a warning message. + + void error (const std::string& message, const TokenLoc& loc); + ///< Generate an error message. + + void endOfFile(); + ///< Generate an error message for an unexpected EOF. + + virtual void reset(); + ///< Remove all previous error/warning events + }; +} + +#endif diff --git a/components/compiler/exception.hpp b/components/compiler/exception.hpp new file mode 100644 index 000000000..3f0ff4c55 --- /dev/null +++ b/components/compiler/exception.hpp @@ -0,0 +1,32 @@ +#ifndef COMPILER_EXCEPTION_H_INCLUDED +#define COMPILER_EXCEPTION_H_INCLUDED + +#include + +namespace Compiler +{ + /// \brief Exception: Error while parsing the source + + class SourceException : public std::exception + { + virtual const char *what() const throw() { return "compile error";} + ///< Return error message + }; + + /// \brief Exception: File error + + class FileException : public SourceException + { + virtual const char *what() const throw() { return "can't read file"; } + ///< Return error message + }; + + /// \brief Exception: EOF condition encountered + + class EOFException : public SourceException + { virtual const char *what() const throw() { return "end of file"; } + ///< Return error message + }; +} + +#endif diff --git a/components/compiler/exprparser.cpp b/components/compiler/exprparser.cpp new file mode 100644 index 000000000..658e9334a --- /dev/null +++ b/components/compiler/exprparser.cpp @@ -0,0 +1,481 @@ + +#include "exprparser.hpp" + +#include +#include +#include +#include + +#include "generator.hpp" +#include "scanner.hpp" +#include "errorhandler.hpp" +#include "locals.hpp" +#include "stringparser.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 (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 + 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(); + } + + void ExprParser::parseArguments (const std::string& arguments, Scanner& scanner) + { + parseArguments (arguments, scanner, mCode); + } + + ExprParser::ExprParser (ErrorHandler& errorHandler, Context& context, Locals& locals, + Literals& literals, bool argument) + : Parser (errorHandler, context), mLocals (locals), mLiterals (literals), + mNextOperand (true), mFirst (true), mArgument (argument) + {} + + bool ExprParser::parseInt (int value, const TokenLoc& loc, Scanner& scanner) + { + mFirst = false; + + if (mNextOperand) + { + 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) + { + mFirst = false; + + if (mNextOperand) + { + 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) + { + mFirst = false; + + if (mNextOperand) + { + char type = mLocals.getType (name); + + if (type!=' ') + { + Generator::fetchLocal (mCode, type, mLocals.getIndex (name)); + mNextOperand = false; + mOperands.push_back (type=='f' ? 'f' : 'l'); + 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) + { + mFirst = false; + + if (mNextOperand) + { + if (keyword==Scanner::K_getsquareroot) + { + mTokenLoc = loc; + parseArguments ("f", scanner); + + Generator::squareRoot (mCode); + mOperands.push_back ('f'); + + 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 (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: pushBinaryOperator ('+'); return true; + case Scanner::S_minus: pushBinaryOperator ('-'); return true; + 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; + } + + char ExprParser::append (std::vector& 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]; + } + + void ExprParser::parseArguments (const std::string& arguments, Scanner& scanner, + std::vector& code, bool invert) + { + ExprParser parser (getErrorHandler(), getContext(), mLocals, mLiterals, true); + StringParser stringParser (getErrorHandler(), getContext(), mLiterals); + + std::stack > stack; + + for (std::string::const_iterator iter (arguments.begin()); iter!=arguments.end(); + ++iter) + { + if (*iter=='S') + { + stringParser.reset(); + scanner.scan (stringParser); + + if (invert) + { + std::vector tmp; + stringParser.append (tmp); + + stack.push (tmp); + } + else + stringParser.append (code); + } + else + { + parser.reset(); + scanner.scan (parser); + + std::vector tmp; + + char type = parser.append (tmp); + + if (type!=*iter) + Generator::convert (tmp, type, *iter); + + if (invert) + stack.push (tmp); + else + std::copy (tmp.begin(), tmp.end(), std::back_inserter (code)); + } + } + + while (!stack.empty()) + { + std::vector& tmp = stack.top(); + + std::copy (tmp.begin(), tmp.end(), std::back_inserter (code)); + + stack.pop(); + } + } +} + diff --git a/components/compiler/exprparser.hpp b/components/compiler/exprparser.hpp new file mode 100644 index 000000000..4ac74075c --- /dev/null +++ b/components/compiler/exprparser.hpp @@ -0,0 +1,102 @@ +#ifndef COMPILER_EXPRPARSER_H_INCLUDED +#define COMPILER_EXPRPARSER_H_INCLUDED + +#include + +#include + +#include "parser.hpp" +#include "tokenloc.hpp" + +namespace Compiler +{ + class Locals; + class Literals; + + class ExprParser : public Parser + { + Locals& mLocals; + Literals& mLiterals; + std::vector mOperands; + std::vector mOperators; + bool mNextOperand; + TokenLoc mTokenLoc; + std::vector mCode; + bool mFirst; + bool mArgument; + + int getPriority (char op) const; + + char getOperandType (int Index = 0) const; + + char getOperator() const; + + bool isOpen() const; + + void popOperator(); + + void popOperand(); + + void replaceBinaryOperands(); + + void pop(); + + void pushIntegerLiteral (int value); + + void pushFloatLiteral (float value); + + void pushBinaryOperator (char c); + + void close(); + + void parseArguments (const std::string& arguments, Scanner& scanner); + + public: + + ExprParser (ErrorHandler& errorHandler, Context& context, Locals& locals, + Literals& literals, bool argument = false); + ///< constructor + /// \param argument Parser is used to parse function- or instruction- + /// arguments (this influences the precedence rules). + + char getType() const; + ///< Return type of parsed expression ('l' integer, 'f' float) + + virtual bool parseInt (int value, const TokenLoc& loc, Scanner& scanner); + ///< Handle an int token. + /// \return fetch another token? + + virtual bool parseFloat (float value, const TokenLoc& loc, Scanner& scanner); + ///< Handle a float token. + /// \return fetch another token? + + 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(); + ///< Reset parser to clean state. + + char append (std::vector& code); + ///< Generate code for parsed expression. + /// \return Type ('l': integer, 'f': float) + + void parseArguments (const std::string& arguments, Scanner& scanner, + std::vector& code, bool invert = false); + ///< Parse sequence of arguments specified by \a arguments. + /// \param arguments Each character represents one arguments ('l': integer, + /// 'f': float, 'S': string) + /// \param invert Store arguments in reverted order. + }; +} + +#endif diff --git a/components/compiler/fileparser.cpp b/components/compiler/fileparser.cpp new file mode 100644 index 000000000..f8b3d0e61 --- /dev/null +++ b/components/compiler/fileparser.cpp @@ -0,0 +1,103 @@ +#include "fileparser.hpp" + +#include + +#include "tokenloc.hpp" +#include "scanner.hpp" + +namespace Compiler +{ + FileParser::FileParser (ErrorHandler& errorHandler, Context& context) + : Parser (errorHandler, context), + mScriptParser (errorHandler, context, mLocals, true), + mState (BeginState) + {} + + std::string FileParser::getName() const + { + return mName; + } + + void FileParser::getCode (std::vector& code) const + { + mScriptParser.getCode (code); + } + + const Locals& FileParser::getLocals() const + { + return mLocals; + } + + bool FileParser::parseName (const std::string& name, const TokenLoc& loc, + Scanner& scanner) + { + if (mState==NameState) + { + mName = name; + mState = BeginCompleteState; + return true; + } + + if (mState==EndNameState) + { + // optional repeated name after end statement + if (mName!=name) + reportWarning ("Names for script " + mName + " do not match", loc); + + mState = EndCompleteState; + return true; + } + + return Parser::parseName (name, loc, scanner); + } + + bool FileParser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner) + { + if (mState==BeginState && keyword==Scanner::K_begin) + { + mState = NameState; + return true; + } + + return Parser::parseKeyword (keyword, loc, scanner); + } + + bool FileParser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner) + { + if (code==Scanner::S_newline) + { + if (mState==BeginCompleteState) + { + // parse the script body + mScriptParser.reset(); + + scanner.scan (mScriptParser); + + mState = EndNameState; + return true; + } + + if (mState==EndCompleteState || mState==EndNameState) + { + // we are done here -> ignore the rest of the script + return false; + } + } + + return Parser::parseSpecial (code, loc, scanner); + } + + void FileParser::parseEOF (Scanner& scanner) + { + if (mState!=EndNameState && mState!=EndCompleteState) + Parser::parseEOF (scanner); + } + + void FileParser::reset() + { + mState = BeginState; + mName.clear(); + mScriptParser.reset(); + } +} + diff --git a/components/compiler/fileparser.hpp b/components/compiler/fileparser.hpp new file mode 100644 index 000000000..13c7fb537 --- /dev/null +++ b/components/compiler/fileparser.hpp @@ -0,0 +1,60 @@ +#ifndef COMPILER_FILEPARSER_H_INCLUDED +#define COMPILER_FILEPARSER_H_INCLUDED + +#include "parser.hpp" +#include "scriptparser.hpp" +#include "locals.hpp" +#include "literals.hpp" + +namespace Compiler +{ + // Top-level parser, to be used for global scripts, local scripts and targeted scripts + + class FileParser : public Parser + { + enum State + { + BeginState, NameState, BeginCompleteState, EndNameState, + EndCompleteState + }; + + ScriptParser mScriptParser; + State mState; + std::string mName; + Locals mLocals; + + public: + + FileParser (ErrorHandler& errorHandler, Context& context); + + std::string getName() const; + ///< Return script name. + + void getCode (std::vector& code) const; + ///< store generated code in \æ code. + + const Locals& getLocals() const; + ///< get local variable declarations. + + 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. + + void reset(); + ///< Reset parser to clean state. + }; +} + +#endif diff --git a/components/compiler/generator.cpp b/components/compiler/generator.cpp new file mode 100644 index 000000000..0f5a9a478 --- /dev/null +++ b/components/compiler/generator.cpp @@ -0,0 +1,552 @@ + +#include "generator.hpp" + +#include +#include +#include +#include + +#include "literals.hpp" + +namespace +{ + Interpreter::Type_Code segment0 (unsigned int c, unsigned int arg0) + { + assert (c<64); + return (c<<24) | (arg0 & 0xffffff); + } + + Interpreter::Type_Code segment1 (unsigned int c, unsigned int arg0, unsigned int arg1) + { + assert (c<64); + return 0x40000000 | (c<<24) | ((arg0 & 0xfff)<<12) | (arg1 & 0xfff); + } + + Interpreter::Type_Code segment2 (unsigned int c, unsigned int arg0) + { + assert (c<1024); + return 0x80000000 | (c<<20) | (arg0 & 0xfffff); + } + + Interpreter::Type_Code segment3 (unsigned int c, unsigned int arg0) + { + assert (c<1024); + return 0xc0000000 | (c<<20) | (arg0 & 0xffff); + } + + Interpreter::Type_Code segment4 (unsigned int c, unsigned int arg0, unsigned int arg1) + { + assert (c<1024); + return 0xc4000000 | (c<<16) | ((arg0 & 0xff)<<8) | (arg1 & 0xff); + } + + Interpreter::Type_Code segment5 (unsigned int c) + { + assert (c<67108864); + return 0xc8000000 | c; + } + + void opPushInt (Compiler::Generator::CodeContainer& code, int value) + { + code.push_back (segment0 (0, value)); + } + + void opFetchIntLiteral (Compiler::Generator::CodeContainer& code) + { + code.push_back (segment5 (4)); + } + + void opFetchFloatLiteral (Compiler::Generator::CodeContainer& code) + { + code.push_back (segment5 (5)); + } + + void opIntToFloat (Compiler::Generator::CodeContainer& code) + { + code.push_back (segment5 (3)); + } + + void opFloatToInt (Compiler::Generator::CodeContainer& code) + { + code.push_back (segment5 (6)); + } + + void opStoreLocalShort (Compiler::Generator::CodeContainer& code) + { + code.push_back (segment5 (0)); + } + + void opStoreLocalLong (Compiler::Generator::CodeContainer& code) + { + code.push_back (segment5 (1)); + } + + void opStoreLocalFloat (Compiler::Generator::CodeContainer& code) + { + code.push_back (segment5 (2)); + } + + void opNegateInt (Compiler::Generator::CodeContainer& code) + { + code.push_back (segment5 (7)); + } + + void opNegateFloat (Compiler::Generator::CodeContainer& code) + { + code.push_back (segment5 (8)); + } + + void opAddInt (Compiler::Generator::CodeContainer& code) + { + code.push_back (segment5 (9)); + } + + void opAddFloat (Compiler::Generator::CodeContainer& code) + { + code.push_back (segment5 (10)); + } + + void opSubInt (Compiler::Generator::CodeContainer& code) + { + code.push_back (segment5 (11)); + } + + void opSubFloat (Compiler::Generator::CodeContainer& code) + { + code.push_back (segment5 (12)); + } + + void opMulInt (Compiler::Generator::CodeContainer& code) + { + code.push_back (segment5 (13)); + } + + void opMulFloat (Compiler::Generator::CodeContainer& code) + { + code.push_back (segment5 (14)); + } + + void opDivInt (Compiler::Generator::CodeContainer& code) + { + code.push_back (segment5 (15)); + } + + void opDivFloat (Compiler::Generator::CodeContainer& code) + { + code.push_back (segment5 (16)); + } + + void opIntToFloat1 (Compiler::Generator::CodeContainer& code) + { + code.push_back (segment5 (17)); + } + + void opFloatToInt1 (Compiler::Generator::CodeContainer& code) + { + code.push_back (segment5 (18)); + } + + void opSquareRoot (Compiler::Generator::CodeContainer& code) + { + code.push_back (segment5 (19)); + } + + void opReturn (Compiler::Generator::CodeContainer& code) + { + code.push_back (segment5 (20)); + } + + void opMessageBox (Compiler::Generator::CodeContainer& code, int buttons) + { + code.push_back (segment3 (0, buttons)); + } + + void opFetchLocalShort (Compiler::Generator::CodeContainer& code) + { + code.push_back (segment5 (21)); + } + + void opFetchLocalLong (Compiler::Generator::CodeContainer& code) + { + code.push_back (segment5 (22)); + } + + void opFetchLocalFloat (Compiler::Generator::CodeContainer& code) + { + code.push_back (segment5 (23)); + } + + void opJumpForward (Compiler::Generator::CodeContainer& code, int offset) + { + code.push_back (segment0 (1, offset)); + } + + void opJumpBackward (Compiler::Generator::CodeContainer& code, int offset) + { + code.push_back (segment0 (2, offset)); + } + + void opSkipOnZero (Compiler::Generator::CodeContainer& code) + { + code.push_back (segment5 (24)); + } + + void opSkipOnNonZero (Compiler::Generator::CodeContainer& code) + { + code.push_back (segment5 (25)); + } + + void opEqualInt (Compiler::Generator::CodeContainer& code) + { + code.push_back (segment5 (26)); + } + + void opNonEqualInt (Compiler::Generator::CodeContainer& code) + { + code.push_back (segment5 (27)); + } + + void opLessThanInt (Compiler::Generator::CodeContainer& code) + { + code.push_back (segment5 (28)); + } + + void opLessOrEqualInt (Compiler::Generator::CodeContainer& code) + { + code.push_back (segment5 (29)); + } + + void opGreaterThanInt (Compiler::Generator::CodeContainer& code) + { + code.push_back (segment5 (30)); + } + + void opGreaterOrEqualInt (Compiler::Generator::CodeContainer& code) + { + code.push_back (segment5 (31)); + } + + void opEqualFloat (Compiler::Generator::CodeContainer& code) + { + code.push_back (segment5 (32)); + } + + void opNonEqualFloat (Compiler::Generator::CodeContainer& code) + { + code.push_back (segment5 (33)); + } + + void opLessThanFloat (Compiler::Generator::CodeContainer& code) + { + code.push_back (segment5 (34)); + } + + void opLessOrEqualFloat (Compiler::Generator::CodeContainer& code) + { + code.push_back (segment5 (35)); + } + + void opGreaterThanFloat (Compiler::Generator::CodeContainer& code) + { + code.push_back (segment5 (36)); + } + + void opGreaterOrEqualFloat (Compiler::Generator::CodeContainer& code) + { + code.push_back (segment5 (37)); + } +} + +namespace Compiler +{ + namespace Generator + { + void pushInt (CodeContainer& code, Literals& literals, int value) + { + int index = literals.addInteger (value); + opPushInt (code, index); + opFetchIntLiteral (code); + } + + void pushFloat (CodeContainer& code, Literals& literals, float value) + { + int index = literals.addFloat (value); + opPushInt (code, index); + opFetchFloatLiteral (code); + } + + void pushString (CodeContainer& code, Literals& literals, const std::string& value) + { + int index = literals.addString (value); + opPushInt (code, index); + } + + void assignToLocal (CodeContainer& code, char localType, + int localIndex, const CodeContainer& value, char valueType) + { + opPushInt (code, localIndex); + + std::copy (value.begin(), value.end(), std::back_inserter (code)); + + if (localType!=valueType) + { + if (localType=='f' && valueType=='l') + { + opIntToFloat (code); + } + else if ((localType=='l' || localType=='s') && valueType=='f') + { + opFloatToInt (code); + } + } + + switch (localType) + { + case 'f': + + opStoreLocalFloat (code); + break; + + case 's': + + opStoreLocalShort (code); + break; + + case 'l': + + opStoreLocalLong (code); + break; + + default: + + assert (0); + } + } + + void negate (CodeContainer& code, char valueType) + { + switch (valueType) + { + case 'l': + + opNegateInt (code); + break; + + case 'f': + + opNegateFloat (code); + break; + + default: + + assert (0); + } + } + + void add (CodeContainer& code, char valueType1, char valueType2) + { + if (valueType1=='l' && valueType2=='l') + { + opAddInt (code); + } + else + { + if (valueType1=='l') + opIntToFloat1 (code); + + if (valueType2=='l') + opIntToFloat (code); + + opAddFloat (code); + } + } + + void sub (CodeContainer& code, char valueType1, char valueType2) + { + if (valueType1=='l' && valueType2=='l') + { + opSubInt (code); + } + else + { + if (valueType1=='l') + opIntToFloat1 (code); + + if (valueType2=='l') + opIntToFloat (code); + + opSubFloat (code); + } + } + + void mul (CodeContainer& code, char valueType1, char valueType2) + { + if (valueType1=='l' && valueType2=='l') + { + opMulInt (code); + } + else + { + if (valueType1=='l') + opIntToFloat1 (code); + + if (valueType2=='l') + opIntToFloat (code); + + opMulFloat (code); + } + } + + void div (CodeContainer& code, char valueType1, char valueType2) + { + if (valueType1=='l' && valueType2=='l') + { + opDivInt (code); + } + else + { + if (valueType1=='l') + opIntToFloat1 (code); + + if (valueType2=='l') + opIntToFloat (code); + + opDivFloat (code); + } + } + + void convert (CodeContainer& code, char fromType, char toType) + { + if (fromType!=toType) + { + if (fromType=='f' && toType=='l') + opFloatToInt (code); + else if (fromType=='l' && toType=='f') + opIntToFloat (code); + else + throw std::logic_error ("illegal type conversion"); + } + } + + void squareRoot (CodeContainer& code) + { + opSquareRoot (code); + } + + void exit (CodeContainer& code) + { + opReturn (code); + } + + void message (CodeContainer& code, Literals& literals, const std::string& message, + int buttons) + { + assert (buttons==0); + + int index = literals.addString (message); + + opPushInt (code, index); + opMessageBox (code, buttons); + } + + void fetchLocal (CodeContainer& code, char localType, int localIndex) + { + opPushInt (code, localIndex); + + switch (localType) + { + case 'f': + + opFetchLocalFloat (code); + break; + + case 's': + + opFetchLocalShort (code); + break; + + case 'l': + + opFetchLocalLong (code); + break; + + default: + + assert (0); + } + } + + void jump (CodeContainer& code, int offset) + { + if (offset>0) + opJumpForward (code, offset); + else if (offset<0) + opJumpBackward (code, -offset); + else + throw std::logic_error ("inifite loop"); + } + + void jumpOnZero (CodeContainer& code, int offset) + { + opSkipOnNonZero (code); + + if (offset<0) + --offset; // compensate for skip instruction + + jump (code, offset); + } + + void jumpOnNonZero (CodeContainer& code, int offset) + { + opSkipOnZero (code); + + if (offset<0) + --offset; // compensate for skip instruction + + jump (code, offset); + } + + void compare (CodeContainer& code, char op, char valueType1, char valueType2) + { + if (valueType1=='l' && valueType2=='l') + { + switch (op) + { + case 'e': opEqualInt (code); break; + case 'n': opNonEqualInt (code); break; + case 'l': opLessThanInt (code); break; + case 'L': opLessOrEqualInt (code); break; + case 'g': opGreaterThanInt (code); break; + case 'G': opGreaterOrEqualInt (code); break; + + default: + + assert (0); + } + } + else + { + if (valueType1=='l') + opIntToFloat1 (code); + + if (valueType2=='l') + opIntToFloat (code); + + switch (op) + { + case 'e': opEqualFloat (code); break; + case 'n': opNonEqualFloat (code); break; + case 'l': opLessThanFloat (code); break; + case 'L': opLessOrEqualFloat (code); break; + case 'g': opGreaterThanFloat (code); break; + case 'G': opGreaterOrEqualFloat (code); break; + + default: + + assert (0); + } + } + } + } +} + diff --git a/components/compiler/generator.hpp b/components/compiler/generator.hpp new file mode 100644 index 000000000..708516cc7 --- /dev/null +++ b/components/compiler/generator.hpp @@ -0,0 +1,58 @@ +#ifndef COMPILER_GENERATOR_H_INCLUDED +#define COMPILER_GENERATOR_H_INCLUDED + +#include +#include + +#include + +namespace Compiler +{ + class Literals; + + namespace Generator + { + typedef std::vector CodeContainer; + + void pushInt (CodeContainer& code, Literals& literals, int value); + + void pushFloat (CodeContainer& code, Literals& literals, float value); + + void pushString (CodeContainer& code, Literals& literals, const std::string& value); + + void assignToLocal (CodeContainer& code, char localType, + int localIndex, const CodeContainer& value, char valueType); + + void negate (CodeContainer& code, char valueType); + + void add (CodeContainer& code, char valueType1, char valueType2); + + void sub (CodeContainer& code, char valueType1, char valueType2); + + void mul (CodeContainer& code, char valueType1, char valueType2); + + void div (CodeContainer& code, char valueType1, char valueType2); + + void convert (CodeContainer& code, char fromType, char toType); + + void squareRoot (CodeContainer& code); + + void exit (CodeContainer& code); + + void message (CodeContainer& code, Literals& literals, const std::string& message, + int buttons); + + void fetchLocal (CodeContainer& code, char localType, int localIndex); + + void jump (CodeContainer& code, int offset); + + void jumpOnZero (CodeContainer& code, int offset); + + void jumpOnNonZero (CodeContainer& code, int offset); + + void compare (CodeContainer& code, char op, char valueType1, char valueType2); + } +} + +#endif + diff --git a/components/compiler/lineparser.cpp b/components/compiler/lineparser.cpp new file mode 100644 index 000000000..50f89ab7f --- /dev/null +++ b/components/compiler/lineparser.cpp @@ -0,0 +1,177 @@ + +#include "lineparser.hpp" + +#include "scanner.hpp" +#include "context.hpp" +#include "errorhandler.hpp" +#include "skipparser.hpp" +#include "locals.hpp" +#include "generator.hpp" + +namespace Compiler +{ + LineParser::LineParser (ErrorHandler& errorHandler, Context& context, Locals& locals, + Literals& literals, std::vector& code) + : Parser (errorHandler, context), mLocals (locals), mLiterals (literals), mCode (code), + mState (BeginState), mExprParser (errorHandler, context, locals, literals) + {} + + bool LineParser::parseInt (int value, const TokenLoc& loc, Scanner& scanner) + { + return Parser::parseInt (value, loc, scanner); + } + + bool LineParser::parseFloat (float value, const TokenLoc& loc, Scanner& scanner) + { + return Parser::parseFloat (value, loc, scanner); + } + + bool LineParser::parseName (const std::string& name, const TokenLoc& loc, + Scanner& scanner) + { + if (mState==ShortState || mState==LongState || mState==FloatState) + { + if (!getContext().canDeclareLocals()) + { + getErrorHandler().error ("local variables can't be declared in this context", loc); + SkipParser skip (getErrorHandler(), getContext()); + scanner.scan (skip); + return false; + } + + std::string name2 = toLower (name); + + char type = mLocals.getType (name2); + + if (type!=' ') + { + getErrorHandler().error ("can't re-declare local variable", loc); + SkipParser skip (getErrorHandler(), getContext()); + scanner.scan (skip); + return false; + } + + mLocals.declare (mState==ShortState ? 's' : (mState==LongState ? 'l' : 'f'), + name2); + + mState = EndState; + return true; + } + + if (mState==SetState) + { + // local variable? + std::string name2 = toLower (name); + char type = mLocals.getType (name2); + if (type!=' ') + { + mName = name2; + mState = SetLocalVarState; + return true; + } + + getErrorHandler().error ("unknown variable", loc); + SkipParser skip (getErrorHandler(), getContext()); + scanner.scan (skip); + return false; + } + + if (mState==MessageState || mState==MessageCommaState) + { + std::string arguments; + + for (std::size_t i=0; i code; + char type = mExprParser.append (code); + + Generator::assignToLocal (mCode, mLocals.getType (mName), + mLocals.getIndex (mName), code, type); + + mState = EndState; + return true; + } + + return Parser::parseKeyword (keyword, loc, scanner); + } + + bool LineParser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner) + { + if (code==Scanner::S_newline && mState==EndState) + return false; + + if (code==Scanner::S_comma && mState==MessageState) + { + mState = MessageCommaState; + return true; + } + + return Parser::parseSpecial (code, loc, scanner); + } + + void LineParser::reset() + { + mState = BeginState; + mName.clear(); + } +} + diff --git a/components/compiler/lineparser.hpp b/components/compiler/lineparser.hpp new file mode 100644 index 000000000..0af222d86 --- /dev/null +++ b/components/compiler/lineparser.hpp @@ -0,0 +1,67 @@ +#ifndef COMPILER_LINEPARSER_H_INCLUDED +#define COMPILER_LINEPARSER_H_INCLUDED + +#include + +#include + +#include "parser.hpp" +#include "exprparser.hpp" + +namespace Compiler +{ + class Locals; + class Literals; + + /// \brief Line parser, to be used in console scripts and as part of ScriptParser + + class LineParser : public Parser + { + enum State + { + BeginState, + ShortState, LongState, FloatState, + SetState, SetLocalVarState, + MessageState, MessageCommaState, + EndState + }; + + Locals& mLocals; + Literals& mLiterals; + std::vector& mCode; + State mState; + std::string mName; + ExprParser mExprParser; + + public: + + LineParser (ErrorHandler& errorHandler, Context& context, Locals& locals, + Literals& literals, std::vector& code); + + virtual bool parseInt (int value, const TokenLoc& loc, Scanner& scanner); + ///< Handle an int token. + /// \return fetch another token? + + virtual bool parseFloat (float value, const TokenLoc& loc, Scanner& scanner); + ///< Handle a float token. + /// \return fetch another token? + + 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(); + ///< Reset parser to clean state. + }; +} + +#endif diff --git a/components/compiler/literals.cpp b/components/compiler/literals.cpp new file mode 100644 index 000000000..626b03afb --- /dev/null +++ b/components/compiler/literals.cpp @@ -0,0 +1,94 @@ + +#include "literals.hpp" + +#include + +namespace Compiler +{ + int Literals::getIntegerSize() const + { + return mIntegers.size() * sizeof (Interpreter::Type_Integer); + } + + int Literals::getFloatSize() const + { + return mFloats.size() * sizeof (Interpreter::Type_Float); + } + + int Literals::getStringSize() const + { + int size = 0; + + for (std::vector::const_iterator iter (mStrings.begin()); + iter!=mStrings.end(); ++iter) + size += static_cast (iter->size()) + 1; + + if (size % 4) // padding + size += 4 - size % 4; + + return size; + } + + void Literals::append (std::vector& code) const + { + for (std::vector::const_iterator iter (mIntegers.begin()); + iter!=mIntegers.end(); ++iter) + code.push_back (*reinterpret_cast (&*iter)); + + for (std::vector::const_iterator iter (mFloats.begin()); + iter!=mFloats.end(); ++iter) + code.push_back (*reinterpret_cast (&*iter)); + + int stringBlockSize = getStringSize(); + int size = static_cast (code.size()); + + code.resize (size+stringBlockSize/4); + + int offset = 0; + + for (std::vector::const_iterator iter (mStrings.begin()); + iter!=mStrings.end(); ++iter) + { + int stringSize = iter->size()+1; + + std::copy (iter->c_str(), iter->c_str()+stringSize, + reinterpret_cast (&code[size]) + offset); + offset += stringSize; + } + } + + int Literals::addInteger (Interpreter::Type_Integer value) + { + int index = static_cast (mIntegers.size()); + + mIntegers.push_back (value); + + return index; + } + + int Literals::addFloat (Interpreter::Type_Float value) + { + int index = static_cast (mFloats.size()); + + mFloats.push_back (value); + + return index; + } + + int Literals::addString (const std::string& value) + { + int index = static_cast (mStrings.size()); + + mStrings.push_back (value); + + return index; + } + + void Literals::clear() + { + mIntegers.clear(); + mFloats.clear(); + mStrings.clear(); + } +} + diff --git a/components/compiler/literals.hpp b/components/compiler/literals.hpp new file mode 100644 index 000000000..b18c86479 --- /dev/null +++ b/components/compiler/literals.hpp @@ -0,0 +1,49 @@ +#ifndef COMPILER_LITERALS_H_INCLUDED +#define COMPILER_LITERALS_H_INCLUDED + +#include +#include + +#include + +namespace Compiler +{ + /// \brief Literal values. + + class Literals + { + std::vector mIntegers; + std::vector mFloats; + std::vector mStrings; + + public: + + int getIntegerSize() const; + ///< Return size of integer block (in bytes). + + int getFloatSize() const; + ///< Return size of float block (in bytes). + + int getStringSize() const; + ///< Return size of string block (in bytes). + + void append (std::vector& code) const; + ///< Apepnd literal blocks to code. + /// \note code blocks will be padded for 32-bit alignment. + + int addInteger (Interpreter::Type_Integer value); + ///< add integer liternal and return index. + + int addFloat (Interpreter::Type_Float value); + ///< add float literal and return value. + + int addString (const std::string& value); + ///< add string literal and return value. + + void clear(); + ///< remove all literals. + }; +} + +#endif + diff --git a/components/compiler/locals.cpp b/components/compiler/locals.cpp new file mode 100644 index 000000000..d93e73849 --- /dev/null +++ b/components/compiler/locals.cpp @@ -0,0 +1,110 @@ + +#include "locals.hpp" + +#include +#include +#include +#include +#include + +namespace Compiler +{ + const std::vector& Locals::get (char type) const + { + switch (type) + { + case 's': return mShorts; + case 'l': return mLongs; + case 'f': return mFloats; + } + + throw std::logic_error ("unknown variable type"); + } + + int Locals::searchIndex (char type, const std::string& name) const + { + const std::vector& collection = get (type); + + std::vector::const_iterator iter = + std::find (collection.begin(), collection.end(), name); + + if (iter==collection.end()) + return -1; + + return iter-collection.begin(); + } + + bool Locals::search (char type, const std::string& name) const + { + return searchIndex (type, name)!=-1; + } + + std::vector& Locals::get (char type) + { + switch (type) + { + case 's': return mShorts; + case 'l': return mLongs; + case 'f': return mFloats; + } + + throw std::logic_error ("unknown variable type"); + } + + char Locals::getType (const std::string& name) const + { + if (search ('s', name)) + return 's'; + + if (search ('l', name)) + return 'l'; + + if (search ('f', name)) + return 'f'; + + return ' '; + } + + int Locals::getIndex (const std::string& name) const + { + int index = searchIndex ('s', name); + + if (index!=-1) + return index; + + index = searchIndex ('l', name); + + if (index!=-1) + return index; + + return searchIndex ('f', name); + } + + void Locals::write (std::ostream& localFile) const + { + localFile + << get ('s').size() << ' ' + << get ('l').size() << ' ' + << get ('f').size() << std::endl; + + std::copy (get ('s').begin(), get ('s').end(), + std::ostream_iterator (localFile, " ")); + std::copy (get ('l').begin(), get ('l').end(), + std::ostream_iterator (localFile, " ")); + std::copy (get ('f').begin(), get ('f').end(), + std::ostream_iterator (localFile, " ")); + } + + void Locals::declare (char type, const std::string& name) + { + get (type).push_back (name); + } + + void Locals::clear() + { + get ('s').clear(); + get ('l').clear(); + get ('f').clear(); + } +} + diff --git a/components/compiler/locals.hpp b/components/compiler/locals.hpp new file mode 100644 index 000000000..fdd0a6b29 --- /dev/null +++ b/components/compiler/locals.hpp @@ -0,0 +1,45 @@ +#ifndef COMPILER_LOCALS_H_INCLUDED +#define COMPILER_LOCALS_H_INCLUDED + +#include +#include +#include + +namespace Compiler +{ + /// \brief Local variable declarations + + class Locals + { + std::vector mShorts; + std::vector mLongs; + std::vector mFloats; + + const std::vector& get (char type) const; + + int searchIndex (char type, const std::string& name) const; + + bool search (char type, const std::string& name) const; + + std::vector& get (char type); + + public: + + char getType (const std::string& name) const; + ///< 's': short, 'l': long, 'f': float, ' ': does not exist. + + int getIndex (const std::string& name) const; + ///< return index for local variable \a name (-1: does not exist). + + void write (std::ostream& localFile) const; + ///< write declarations to file. + + void declare (char type, const std::string& name); + ///< declares a variable. + + void clear(); + ///< remove all declarations. + }; +} + +#endif diff --git a/components/compiler/output.cpp b/components/compiler/output.cpp new file mode 100644 index 000000000..46e04b8dc --- /dev/null +++ b/components/compiler/output.cpp @@ -0,0 +1,74 @@ + +#include "output.hpp" + +#include +#include +#include + +#include "locals.hpp" + +namespace Compiler +{ + Output::Output (Locals& locals) : mLocals (locals) {} + + void Output::getCode (std::vector& code) const + { + code.clear(); + + // header + code.push_back (static_cast (mCode.size())); + + assert (mLiterals.getIntegerSize()%4==0); + code.push_back (static_cast (mLiterals.getIntegerSize()/4)); + + assert (mLiterals.getFloatSize()%4==0); + code.push_back (static_cast (mLiterals.getFloatSize()/4)); + + assert (mLiterals.getStringSize()%4==0); + code.push_back (static_cast (mLiterals.getStringSize()/4)); + + // code + std::copy (mCode.begin(), mCode.end(), std::back_inserter (code)); + + // literals + mLiterals.append (code); + } + + const Literals& Output::getLiterals() const + { + return mLiterals; + } + + const std::vector& Output::getCode() const + { + return mCode; + } + + const Locals& Output::getLocals() const + { + return mLocals; + } + + Literals& Output::getLiterals() + { + return mLiterals; + } + + std::vector& Output::getCode() + { + return mCode; + } + + Locals& Output::getLocals() + { + return mLocals; + } + + void Output::clear() + { + mLiterals.clear(); + mCode.clear(); + mLocals.clear(); + } +} + diff --git a/components/compiler/output.hpp b/components/compiler/output.hpp new file mode 100644 index 000000000..37b88cee9 --- /dev/null +++ b/components/compiler/output.hpp @@ -0,0 +1,44 @@ +#ifndef COMPILER_OUTPUT_H_INCLUDED +#define COMPILER_OUTPUT_H_INCLUDED + +#include "literals.hpp" + +#include + +#include + +namespace Compiler +{ + class Locals; + + class Output + { + Literals mLiterals; + std::vector mCode; + Locals& mLocals; + + public: + + Output (Locals& locals); + + void getCode (std::vector& code) const; + ///< store generated code in \æ code. + + const Literals& getLiterals() const; + + const Locals& getLocals() const; + + const std::vector& getCode() const; + + Literals& getLiterals(); + + std::vector& getCode(); + + Locals& getLocals(); + + void clear(); + }; +} + +#endif + diff --git a/components/compiler/parser.cpp b/components/compiler/parser.cpp new file mode 100644 index 000000000..2c7686589 --- /dev/null +++ b/components/compiler/parser.cpp @@ -0,0 +1,139 @@ + +#include "parser.hpp" + +#include +#include + +#include "errorhandler.hpp" +#include "exception.hpp" + +namespace Compiler +{ + // Report the error and throw an exception. + + void Parser::reportSeriousError (const std::string& message, const TokenLoc& loc) + { + mErrorHandler.error (message, loc); + throw SourceException(); + } + + // Report the error + + void Parser::reportError (const std::string& message, const TokenLoc& loc) + { + mErrorHandler.error (message, loc); + } + + // Report the warning without throwing an exception. + + void Parser::reportWarning (const std::string& message, const TokenLoc& loc) + { + mErrorHandler.warning (message, loc); + } + + // Report an unexpected EOF condition. + + void Parser::reportEOF() + { + mErrorHandler.endOfFile(); + throw EOFException(); + } + + // Return error handler + + ErrorHandler& Parser::getErrorHandler() + { + return mErrorHandler; + } + + // Return context + + Context& Parser::getContext() + { + return mContext; + } + + std::string Parser::toLower (const std::string& name) + { + std::string lowerCase; + + std::transform (name.begin(), name.end(), std::back_inserter (lowerCase), + (int(*)(int)) std::tolower); + + return lowerCase; + } + + Parser::Parser (ErrorHandler& errorHandler, Context& context) + : mErrorHandler (errorHandler), mContext (context) + {} + + // destructor + + Parser::~Parser() {} + + // Handle an int token. + // \return fetch another token? + // + // - Default-implementation: Report an error. + + bool Parser::parseInt (int value, const TokenLoc& loc, Scanner& scanner) + { + reportSeriousError ("Unexpected numeric value", loc); + return false; + } + + // Handle a float token. + // \return fetch another token? + // + // - Default-implementation: Report an error. + + bool Parser::parseFloat (float value, const TokenLoc& loc, Scanner& scanner) + { + reportSeriousError ("Unexpected floating point value", loc); + return false; + } + + // Handle a name token. + // \return fetch another token? + // + // - Default-implementation: Report an error. + + bool Parser::parseName (const std::string& name, const TokenLoc& loc, + Scanner& scanner) + { + reportSeriousError ("Unexpected name", loc); + return false; + } + + // Handle a keyword token. + // \return fetch another token? + // + // - Default-implementation: Report an error. + + bool Parser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner) + { + reportSeriousError ("Unexpected keyword", loc); + return false; + } + + // Handle a special character token. + // \return fetch another token? + // + // - Default-implementation: Report an error. + + bool Parser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner) + { + reportSeriousError ("Unexpected special token", loc); + return false; + } + + // Handle an EOF token. + // + // - Default-implementation: Report an error. + + void Parser::parseEOF (Scanner& scanner) + { + reportEOF(); + } +} + diff --git a/components/compiler/parser.hpp b/components/compiler/parser.hpp new file mode 100644 index 000000000..88268eb22 --- /dev/null +++ b/components/compiler/parser.hpp @@ -0,0 +1,90 @@ +#ifndef COMPILER_PARSER_H_INCLUDED +#define COMPILER_PARSER_H_INCLUDED + +#include + +namespace Compiler +{ + class Scanner; + class TokenLoc; + class ErrorHandler; + class Context; + + /// \brief Parser base class + /// + /// This class defines a callback-parser. + + class Parser + { + ErrorHandler& mErrorHandler; + Context& mContext; + + protected: + + void reportSeriousError (const std::string& message, const TokenLoc& loc); + ///< Report the error and throw a exception. + + void reportError (const std::string& message, const TokenLoc& loc); + ///< Report the error + + void reportWarning (const std::string& message, const TokenLoc& loc); + ///< Report the warning without throwing an exception. + + void reportEOF(); + ///< Report an unexpected EOF condition. + + ErrorHandler& getErrorHandler(); + ///< Return error handler + + Context& getContext(); + ///< Return context + + static std::string toLower (const std::string& name); + + public: + + Parser (ErrorHandler& errorHandler, Context& context); + ///< constructor + + virtual ~Parser(); + ///< destructor + + virtual bool parseInt (int value, const TokenLoc& loc, Scanner& scanner); + ///< Handle an int token. + /// \return fetch another token? + /// + /// - Default-implementation: Report an error. + + virtual bool parseFloat (float value, const TokenLoc& loc, Scanner& scanner); + ///< Handle a float token. + /// \return fetch another token? + /// + /// - Default-implementation: Report an error. + + virtual bool parseName (const std::string& name, const TokenLoc& loc, + Scanner& scanner); + ///< Handle a name token. + /// \return fetch another token? + /// + /// - Default-implementation: Report an error. + + virtual bool parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner); + ///< Handle a keyword token. + /// \return fetch another token? + /// + /// - Default-implementation: Report an error. + + virtual bool parseSpecial (int code, const TokenLoc& loc, Scanner& scanner); + ///< Handle a special character token. + /// \return fetch another token? + /// + /// - Default-implementation: Report an error. + + virtual void parseEOF (Scanner& scanner); + ///< Handle EOF token. + /// + /// - Default-implementation: Report an error. + }; +} + +#endif diff --git a/components/compiler/scanner.cpp b/components/compiler/scanner.cpp new file mode 100644 index 000000000..e4869cba5 --- /dev/null +++ b/components/compiler/scanner.cpp @@ -0,0 +1,492 @@ + +#include "scanner.hpp" + +#include +#include +#include + +#include "exception.hpp" +#include "errorhandler.hpp" +#include "parser.hpp" + +namespace Compiler +{ + bool Scanner::get (char& c) + { + mStream.get (c); + + if (!mStream.good()) + return false; + + mPrevLoc =mLoc; + + if (c=='\n') + { + mLoc.mColumn = 0; + ++mLoc.mLine; + mLoc.mLiteral.clear(); + } + else + { + ++mLoc.mColumn; + mLoc.mLiteral += c; + } + + return true; + } + + void Scanner::putback (char c) + { + mStream.putback (c); + mLoc = mPrevLoc; + } + + bool Scanner::scanToken (Parser& parser) + { + switch (mPutback) + { + case Putback_Special: + + mPutback = Putback_None; + return parser.parseSpecial (mPutbackCode, mPutbackLoc, *this); + + case Putback_Integer: + + mPutback = Putback_None; + return parser.parseInt (mPutbackInteger, mPutbackLoc, *this); + + case Putback_Float: + + mPutback = Putback_None; + return parser.parseFloat (mPutbackFloat, mPutbackLoc, *this); + + case Putback_Name: + + mPutback = Putback_None; + return parser.parseName (mPutbackName, mPutbackLoc, *this); + + case Putback_Keyword: + + mPutback = Putback_None; + return parser.parseKeyword (mPutbackCode, mPutbackLoc, *this); + + case Putback_None: + + break; + } + + char c; + + if (!get (c)) + { + parser.parseEOF (*this); + return false; + } + else if (c==';') + { + while (get (c)) + { + if (c=='\n') + { + putback (c); + break; + } + } + + mLoc.mLiteral.clear(); + + return true; + } + else if (isWhitespace (c)) + { + mLoc.mLiteral.clear(); + return true; + } + else if (std::isdigit (c)) + { + bool cont = false; + + if (scanInt (c, parser, cont)) + { + mLoc.mLiteral.clear(); + return cont; + } + } + else if (std::isalpha (c) || c=='_' || c=='"') + { + bool cont = false; + + if (scanName (c, parser, cont)) + { + mLoc.mLiteral.clear(); + return cont; + } + } + else if (c==13) // linux compatibility hack + { + return true; + } + else + { + bool cont = false; + + if (scanSpecial (c, parser, cont)) + { + mLoc.mLiteral.clear(); + return cont; + } + } + + TokenLoc loc (mLoc); + mLoc.mLiteral.clear(); + + mErrorHandler.error ("syntax error", loc); + throw SourceException(); + } + + bool Scanner::scanInt (char c, Parser& parser, bool& cont) + { + std::string value; + + value += c; + bool empty = false; + + bool error = false; + + while (get (c)) + { + if (std::isdigit (c)) + { + value += c; + empty = false; + } + else if (std::isalpha (c) || c=='_') + error = true; + else if (c=='.' && !error) + { + return scanFloat (value, parser, cont); + } + else + { + putback (c); + break; + } + } + + if (empty || error) + return false; + + TokenLoc loc (mLoc); + mLoc.mLiteral.clear(); + + std::istringstream stream (value); + + int intValue = 0; + stream >> intValue; + + cont = parser.parseInt (intValue, loc, *this); + return true; + } + + bool Scanner::scanFloat (const std::string& intValue, Parser& parser, bool& cont) + { + std::string value = intValue + "."; + + char c; + + bool empty = intValue.empty() || intValue=="-"; + bool error = false; + + while (get (c)) + { + if (std::isdigit (c)) + { + value += c; + empty = false; + } + else if (std::isalpha (c) || c=='_') + error = true; + else + { + putback (c); + break; + } + } + + if (empty || error) + return false; + + TokenLoc loc (mLoc); + mLoc.mLiteral.clear(); + + std::istringstream stream (value); + + float floatValue = 0; + stream >> floatValue; + + cont = parser.parseFloat (floatValue, loc, *this); + return true; + } + + bool Scanner::scanName (char c, Parser& parser, bool& cont) + { + static const char *keywords[] = + { + "begin", "end", + "short", "long", "float", + "if", "endif", "else", "elseif", + "while", "endwhile", + "return", + "messagebox", + "set", "to", + "getsquareroot", + 0 + }; + + std::string name; + + if (!scanName (c, name)) + return false; + + TokenLoc loc (mLoc); + mLoc.mLiteral.clear(); + + if (name.size()>=2 && name[0]=='"' && name[name.size()-1]=='"') + { + name = name.substr (1, name.size()-2); + cont = parser.parseName (name, loc, *this); + return true; + } + + int i = 0; + + std::string lowerCase; + lowerCase.reserve (name.size()); + + std::transform (name.begin(), name.end(), std::back_inserter (lowerCase), + (int(*)(int)) std::tolower); + + for (; keywords[i]; ++i) + if (lowerCase==keywords[i]) + break; + + cont = + keywords[i] ? parser.parseKeyword (i, loc, *this) : parser.parseName (name, loc, *this); + + return true; + } + + bool Scanner::scanName (char c, std::string& name) + { + bool first = false; + bool error = false; + + name.clear(); + + putback (c); + + while (get (c)) + { + if (!name.empty() && name[0]=='"') + { + if (c=='"') + { + name += c; + break; + } + else if (c=='\\') + { + if (!get (c)) + { + mErrorHandler.error ("incomplete escape sequence", mLoc); + break; + } + } + else if (c=='\n') + { + mErrorHandler.error ("incomplete string or name", mLoc); + break; + } + } + else if (!(c=='"' && name.empty())) + { + if (!(std::isalpha (c) || std::isdigit (c) || c=='_')) + { + putback (c); + break; + } + + if (first && std::isdigit (c)) + error = true; + } + + name += c; + first = false; + } + + return !error; + } + + bool Scanner::scanSpecial (char c, Parser& parser, bool& cont) + { + int special = -1; + + if (c=='\n') + special = S_newline; + else if (c=='(') + special = S_open; + else if (c==')') + special = S_close; + else if (c=='=') + { + if (get (c)) + { + if (c=='=') + special = S_cmpEQ; + else + { + putback (c); + return false; + } + } + else + { + putback (c); + return false; + } + } + else if (c=='!') + { + if (get (c)) + { + if (c=='=') + special = S_cmpNE; + else + { + putback (c); + return false; + } + } + else + return false; + } + else if (c=='-') + { + if (get (c)) + { + if (c=='>') + special = S_ref; + else + { + putback (c); + special = S_minus; + } + } + else + special = S_minus; + } + else if (c=='<') + { + if (get (c)) + { + if (c=='=') + special = S_cmpLE; + else + { + putback (c); + special = S_cmpLT; + } + } + else + special = S_cmpLT; + } + else if (c=='>') + { + if (get (c)) + { + if (c=='=') + special = S_cmpGE; + else + { + putback (c); + special = S_cmpGT; + } + } + else + special = S_cmpGT; + } + else if (c==',') + special = S_comma; + else if (c=='+') + special = S_plus; + else if (c=='*') + special = S_mult; + else if (c=='/') + special = S_div; + else + return false; + + if (special==S_newline) + mLoc.mLiteral = ""; + + TokenLoc loc (mLoc); + mLoc.mLiteral.clear(); + + cont = parser.parseSpecial (special, loc, *this); + + return true; + } + + bool Scanner::isWhitespace (char c) + { + return c==' ' || c=='\t'; + } + + // constructor + + Scanner::Scanner (ErrorHandler& errorHandler, std::istream& inputStream) + : mErrorHandler (errorHandler), mStream (inputStream), mPutback (Putback_None) + { + } + + void Scanner::scan (Parser& parser) + { + while (scanToken (parser)); + } + + void Scanner::putbackSpecial (int code, const TokenLoc& loc) + { + mPutback = Putback_Special; + mPutbackCode = code; + mPutbackLoc = loc; + } + + void Scanner::putbackInt (int value, const TokenLoc& loc) + { + mPutback = Putback_Integer; + mPutbackInteger = value; + mPutbackLoc = loc; + } + + void Scanner::putbackFloat (float value, const TokenLoc& loc) + { + mPutback = Putback_Float; + mPutbackFloat = value; + mPutbackLoc = loc; + } + + void Scanner::putbackName (const std::string& name, const TokenLoc& loc) + { + mPutback = Putback_Name; + mPutbackName = name; + mPutbackLoc = loc; + } + + void Scanner::putbackKeyword (int keyword, const TokenLoc& loc) + { + mPutback = Putback_Keyword; + mPutbackCode = keyword; + mPutbackLoc = loc; + } +} + diff --git a/components/compiler/scanner.hpp b/components/compiler/scanner.hpp new file mode 100644 index 000000000..2dd13d5df --- /dev/null +++ b/components/compiler/scanner.hpp @@ -0,0 +1,113 @@ +#ifndef COMPILER_SCANNER_H_INCLUDED +#define COMPILER_SCANNER_H_INCLUDED + +#include +#include + +#include "tokenloc.hpp" + +namespace Compiler +{ + class ErrorHandler; + class Parser; + + /// \brief Scanner + /// + /// This class translate a char-stream to a token stream (delivered via + /// parser-callbacks). + + class Scanner + { + enum putback_type + { + Putback_None, Putback_Special, Putback_Integer, Putback_Float, + Putback_Name, Putback_Keyword + }; + + ErrorHandler& mErrorHandler; + TokenLoc mLoc; + TokenLoc mPrevLoc; + std::istream& mStream; + putback_type mPutback; + int mPutbackCode; + int mPutbackInteger; + float mPutbackFloat; + std::string mPutbackName; + TokenLoc mPutbackLoc; + + public: + + enum keyword + { + K_begin, K_end, + K_short, K_long, K_float, + K_if, K_endif, K_else, K_elseif, + K_while, K_endwhile, + K_return, + K_messagebox, + K_set, K_to, + K_getsquareroot + }; + + enum special + { + S_newline, + S_open, S_close, + S_cmpEQ, S_cmpNE, S_cmpLT, S_cmpLE, S_cmpGT, S_cmpGE, + S_plus, S_minus, S_mult, S_div, + S_comma, + S_ref + }; + + private: + + // not implemented + + Scanner (const Scanner&); + Scanner& operator= (const Scanner&); + + bool get (char& c); + + void putback (char c); + + bool scanToken (Parser& parser); + + bool scanInt (char c, Parser& parser, bool& cont); + + bool scanFloat (const std::string& intValue, Parser& parser, bool& cont); + + bool scanName (char c, Parser& parser, bool& cont); + + bool scanName (char c, std::string& name); + + bool scanSpecial (char c, Parser& parser, bool& cont); + + static bool isWhitespace (char c); + + public: + + Scanner (ErrorHandler& errorHandler, std::istream& inputStream); + ///< constructor + + void scan (Parser& parser); + ///< Scan a token and deliver it to the parser. + + void putbackSpecial (int code, const TokenLoc& loc); + ///< put back a special token + + void putbackInt (int value, const TokenLoc& loc); + ///< put back an integer token + + void putbackFloat (float value, const TokenLoc& loc); + ///< put back a float token + + void putbackName (const std::string& name, const TokenLoc& loc); + ///< put back a name toekn + + void putbackKeyword (int keyword, const TokenLoc& loc); + ///< put back a keyword token + + }; +} + +#endif diff --git a/components/compiler/scriptparser.cpp b/components/compiler/scriptparser.cpp new file mode 100644 index 000000000..ce142847f --- /dev/null +++ b/components/compiler/scriptparser.cpp @@ -0,0 +1,80 @@ + +#include "scriptparser.hpp" + +#include "scanner.hpp" + +namespace Compiler +{ + ScriptParser::ScriptParser (ErrorHandler& errorHandler, Context& context, + Locals& locals, bool end) + : Parser (errorHandler, context), mOutput (locals), + mLineParser (errorHandler, context, locals, mOutput.getLiterals(), mOutput.getCode()), + mControlParser (errorHandler, context, locals, mOutput.getLiterals()), + mEnd (end) + {} + + void ScriptParser::getCode (std::vector& code) const + { + mOutput.getCode (code); + } + + bool ScriptParser::parseName (const std::string& name, const TokenLoc& loc, + Scanner& scanner) + { + mLineParser.reset(); + if (mLineParser.parseName (name, loc, scanner)) + scanner.scan (mLineParser); + + return true; + } + + bool ScriptParser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner) + { + if (keyword==Scanner::K_while || keyword==Scanner::K_if) + { + mControlParser.reset(); + if (mControlParser.parseKeyword (keyword, loc, scanner)) + scanner.scan (mControlParser); + + mControlParser.appendCode (mOutput.getCode()); + + return true; + } + + if (keyword==Scanner::K_end && mEnd) + { + return false; + } + + mLineParser.reset(); + if (mLineParser.parseKeyword (keyword, loc, scanner)) + scanner.scan (mLineParser); + + return true; + } + + bool ScriptParser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner) + { + if (code==Scanner::S_newline) // empty line + return true; + + mLineParser.reset(); + if (mLineParser.parseSpecial (code, loc, scanner)) + scanner.scan (mLineParser); + + return true; + } + + void ScriptParser::parseEOF (Scanner& scanner) + { + if (mEnd) + Parser::parseEOF (scanner); + } + + void ScriptParser::reset() + { + mLineParser.reset(); + mOutput.clear(); + } +} + diff --git a/components/compiler/scriptparser.hpp b/components/compiler/scriptparser.hpp new file mode 100644 index 000000000..bb4809dab --- /dev/null +++ b/components/compiler/scriptparser.hpp @@ -0,0 +1,54 @@ +#ifndef COMPILER_SCRIPTPARSER_H_INCLUDED +#define COMPILER_SCRIPTPARSER_H_INCLUDED + + +#include "parser.hpp" +#include "lineparser.hpp" +#include "controlparser.hpp" +#include "output.hpp" + +namespace Compiler +{ + class Locals; + + // Script parser, to be used in dialogue scripts and as part of FileParser + + class ScriptParser : public Parser + { + Output mOutput; + LineParser mLineParser; + ControlParser mControlParser; + bool mEnd; + + public: + + /// \param end of script is marked by end keyword. + ScriptParser (ErrorHandler& errorHandler, Context& context, Locals& locals, + bool end = false); + + void getCode (std::vector& code) const; + ///< store generated code in \æ code. + + 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. + + void reset(); + ///< Reset parser to clean state. + }; +} + +#endif + diff --git a/components/compiler/skipparser.cpp b/components/compiler/skipparser.cpp new file mode 100644 index 000000000..2d09b63ba --- /dev/null +++ b/components/compiler/skipparser.cpp @@ -0,0 +1,41 @@ + +#include "skipparser.hpp" + +#include "scanner.hpp" + +namespace Compiler +{ + SkipParser::SkipParser (ErrorHandler& errorHandler, Context& context) + : Parser (errorHandler, context) + {} + + bool SkipParser::parseInt (int value, const TokenLoc& loc, Scanner& scanner) + { + return true; + } + + bool SkipParser::parseFloat (float value, const TokenLoc& loc, Scanner& scanner) + { + return true; + } + + bool SkipParser::parseName (const std::string& name, const TokenLoc& loc, + Scanner& scanner) + { + return true; + } + + bool SkipParser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner) + { + return true; + } + + bool SkipParser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner) + { + if (code==Scanner::S_newline) + return false; + + return true; + } +} + diff --git a/components/compiler/skipparser.hpp b/components/compiler/skipparser.hpp new file mode 100644 index 000000000..17f1c8d98 --- /dev/null +++ b/components/compiler/skipparser.hpp @@ -0,0 +1,42 @@ +#ifndef COMPILER_SKIPPARSER_H_INCLUDED +#define COMPILER_SKIPPARSER_H_INCLUDED + +#include "parser.hpp" + +namespace Compiler +{ + // \brief Skip parser for skipping a line + // + // This parser is mainly intended for skipping the rest of a faulty line. + + class SkipParser : public Parser + { + public: + + SkipParser (ErrorHandler& errorHandler, Context& context); + + virtual bool parseInt (int value, const TokenLoc& loc, Scanner& scanner); + ///< Handle an int token. + /// \return fetch another token? + + virtual bool parseFloat (float value, const TokenLoc& loc, Scanner& scanner); + ///< Handle a float token. + /// \return fetch another token? + + 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? + }; +} + +#endif + diff --git a/components/compiler/streamerrorhandler.cpp b/components/compiler/streamerrorhandler.cpp new file mode 100644 index 000000000..a0777be68 --- /dev/null +++ b/components/compiler/streamerrorhandler.cpp @@ -0,0 +1,38 @@ + +#include "streamerrorhandler.hpp" + +#include "tokenloc.hpp" + +namespace Compiler +{ + // Report error to the user. + + void StreamErrorHandler::report (const std::string& message, const TokenLoc& loc, + Type type) + { + if (type==ErrorMessage) + mStream << "error "; + else + mStream << "warning "; + + mStream + << "line " << loc.mLine << ", column " << loc.mColumn + << " (" << loc.mLiteral << ")" << std::endl + << " " << message << std::endl; + } + + // Report a file related error + + void StreamErrorHandler::report (const std::string& message, Type type) + { + if (type==ErrorMessage) + mStream << "error "; + else + mStream << "warning "; + + mStream + << "file:" << std::endl + << " " << message << std::endl; + } + + StreamErrorHandler::StreamErrorHandler (std::ostream& ErrorStream) : mStream (ErrorStream) {} } diff --git a/components/compiler/streamerrorhandler.hpp b/components/compiler/streamerrorhandler.hpp new file mode 100644 index 000000000..ce4d4c6c6 --- /dev/null +++ b/components/compiler/streamerrorhandler.hpp @@ -0,0 +1,37 @@ + +#ifndef COMPILER_STREAMERRORHANDLER_H_INCLUDED +#define COMPILER_STREAMERRORHANDLER_H_INCLUDED + +#include + +#include "errorhandler.hpp" + +namespace Compiler +{ + /// \brief Error handler implementation: Write errors into stream + + class StreamErrorHandler : public ErrorHandler + { + std::ostream& mStream; + + // not implemented + + StreamErrorHandler (const StreamErrorHandler&); + StreamErrorHandler& operator= (const StreamErrorHandler&); + + virtual void report (const std::string& message, const TokenLoc& loc, Type type); + ///< Report error to the user. + + virtual void report (const std::string& message, Type type); + ///< Report a file related error + + public: + + // constructors + + StreamErrorHandler (std::ostream& ErrorStream); + ///< constructor + }; +} + +#endif diff --git a/components/compiler/stringparser.cpp b/components/compiler/stringparser.cpp new file mode 100644 index 000000000..4d3e6935b --- /dev/null +++ b/components/compiler/stringparser.cpp @@ -0,0 +1,52 @@ + +#include "stringparser.hpp" + +#include +#include + +#include "scanner.hpp" +#include "generator.hpp" + +namespace Compiler +{ + StringParser::StringParser (ErrorHandler& errorHandler, Context& context, Literals& literals) + : Parser (errorHandler, context), mLiterals (literals), mState (StartState) + { + + } + + bool StringParser::parseName (const std::string& name, const TokenLoc& loc, + Scanner& scanner) + { + if (mState==StartState || mState==CommaState) + { + Generator::pushString (mCode, mLiterals, name); + return false; + } + + return Parser::parseName (name, loc, scanner); + } + + bool StringParser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner) + { + if (code==Scanner::S_comma && mState==StartState) + { + mState = CommaState; + return true; + } + + return Parser::parseSpecial (code, loc, scanner); + } + + void StringParser::append (std::vector& code) + { + std::copy (mCode.begin(), mCode.end(), std::back_inserter (code)); + } + + void StringParser::reset() + { + mState = StartState; + mCode.clear(); + } +} + diff --git a/components/compiler/stringparser.hpp b/components/compiler/stringparser.hpp new file mode 100644 index 000000000..9d128e2d3 --- /dev/null +++ b/components/compiler/stringparser.hpp @@ -0,0 +1,46 @@ +#ifndef COMPILER_STRINGPARSER_H_INCLUDED +#define COMPILER_STRINGPARSER_H_INCLUDED + +#include + +#include + +#include "parser.hpp" + +namespace Compiler +{ + class Literals; + + class StringParser : public Parser + { + enum State + { + StartState, CommaState + }; + + Literals& mLiterals; + State mState; + std::vector mCode; + + public: + + StringParser (ErrorHandler& errorHandler, Context& context, Literals& literals); + + virtual bool parseName (const std::string& name, const TokenLoc& loc, + Scanner& scanner); + ///< Handle a name 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 append (std::vector& code); + ///< Append code for parsed string. + + void reset(); + ///< Reset parser to clean state. + }; +} + +#endif diff --git a/components/compiler/tokenloc.hpp b/components/compiler/tokenloc.hpp new file mode 100644 index 000000000..62b5cdee5 --- /dev/null +++ b/components/compiler/tokenloc.hpp @@ -0,0 +1,20 @@ +#ifndef COMPILER_TOKENLOC_H_INCLUDED +#define COMPILER_TOKENLOC_H_INCLUDED + +#include + +namespace Compiler +{ + /// \brief Location of a token in a source file + + struct TokenLoc + { + int mColumn; + int mLine; + std::string mLiteral; + + TokenLoc() : mColumn (0), mLine (0), mLiteral ("") {} + }; +} + +#endif // TOKENLOC_H_INCLUDED diff --git a/components/esm_store/reclists.hpp b/components/esm_store/reclists.hpp index 196ce6063..75d4e67ae 100644 --- a/components/esm_store/reclists.hpp +++ b/components/esm_store/reclists.hpp @@ -131,6 +131,35 @@ namespace ESMS } } }; + + template + struct ScriptListT : RecList + { + typedef std::map MapType; + + MapType list; + + // Load one object of this type + void load(ESMReader &esm, const std::string &id) + { + X ref; + ref.load (esm); + + std::string realId = ref.data.name.toString(); + + std::swap (list[realId], ref); + } + + // Find the given object ID, or return NULL if not found. + const X* find(const std::string &id) const + { + if(list.find(id) == list.end()) + return NULL; + return &list.find(id)->second; + } + + int getSize() { return list.size(); } + }; /* We need special lists for: diff --git a/components/esm_store/store.hpp b/components/esm_store/store.hpp index 44bd87279..7c4971ac4 100644 --- a/components/esm_store/store.hpp +++ b/components/esm_store/store.hpp @@ -73,7 +73,7 @@ namespace ESMS //RecListT lands; //RecListT landTexts; //RecListT magicEffects; - //RecListT