#pragma once #include <boost/spirit/home/support/unused.hpp> #include <boost/spirit/include/qi.hpp> #include <boost/spirit/include/phoenix_core.hpp> #include <boost/spirit/include/phoenix_operator.hpp> #include <boost/spirit/include/phoenix_fusion.hpp> #include <boost/spirit/include/phoenix_stl.hpp> #include <boost/spirit/include/phoenix_object.hpp> #include <boost/fusion/include/adapt_struct.hpp> namespace spirit = boost::spirit; namespace qi = boost::spirit::qi; namespace ascii = spirit::ascii; namespace phoenix = boost::phoenix; /* * ERMParser.h, part of VCMI engine * * Authors: listed in file AUTHORS in main folder * * License: GNU General Public License v2.0 or later * Full text of license available in license.txt file, in main folder * */ class CERMPreprocessor { std::string fname; std::ifstream file; int lineNo; enum {INVALID, ERM, VERM} version; void getline(std::string &ret); public: CERMPreprocessor(const std::string &Fname); std::string retreiveCommandLine(); int getCurLineNo() const { return lineNo; } }; //various classes that represent ERM/VERM AST namespace ERM { struct TStringConstant { std::string str; }; struct TMacroUsage { std::string macro; }; // //macro with '?', for write only // struct TQMacroUsage // { // std::string qmacro; // }; //definition of a macro struct TMacroDef { std::string macro; }; typedef std::string TCmdName; struct TVarExpNotMacro { typedef boost::optional<int> Tval; boost::optional<char> questionMark; std::string varsym; Tval val; }; typedef boost::variant<TVarExpNotMacro, TMacroUsage> TVarExp; //write-only variable expression struct TVarpExp { TVarExp var; }; //i-expression (identifier expression) - an integral constant, variable symbol or array symbol typedef boost::variant<TVarExp, int> TIexp; struct TArithmeticOp { TIexp lhs, rhs; char opcode; }; struct TVRLogic { char opcode; TIexp var; }; struct TVRArithmetic { char opcode; TIexp rhs; }; struct TSemiCompare { std::string compSign; TIexp rhs; }; struct TCurriedString { TIexp iexp; TStringConstant string; }; struct TVarConcatString { TVarExp var; TStringConstant string; }; typedef boost::variant<TVarConcatString, TStringConstant, TCurriedString, TSemiCompare, TMacroDef, TIexp, TVarpExp, boost::spirit::unused_type> TBodyOptionItem; typedef std::vector<TBodyOptionItem> TNormalBodyOptionList; struct TNormalBodyOption { char optionCode; TNormalBodyOptionList params; }; typedef boost::variant<TVRLogic, TVRArithmetic, TNormalBodyOption> TBodyOption; typedef boost::variant<TIexp, TArithmeticOp > TIdentifierInternal; typedef std::vector< TIdentifierInternal > Tidentifier; struct TComparison { std::string compSign; TIexp lhs, rhs; }; struct Tcondition; typedef boost::optional< boost::recursive_wrapper<Tcondition> > TconditionNode; struct Tcondition { typedef boost::variant< TComparison, int> Tcond; //comparison or condition flag char ctype; Tcond cond; TconditionNode rhs; }; struct TTriggerBase { bool pre; //if false it's !$ post-trigger, elsewise it's !# (pre)trigger TCmdName name; boost::optional<Tidentifier> identifier; boost::optional<Tcondition> condition; }; struct Ttrigger : TTriggerBase { Ttrigger() { pre = true; } }; struct TPostTrigger : TTriggerBase { TPostTrigger() { pre = false; } }; //a dirty workaround for preprocessor magic that prevents the use types with comma in it in BOOST_FUSION_ADAPT_STRUCT //see http://comments.gmane.org/gmane.comp.lib.boost.user/62501 for some info // //moreover, I encountered a quite serious bug in boost: http://boost.2283326.n4.nabble.com/container-hpp-111-error-C2039-value-type-is-not-a-member-of-td3352328.html //not sure how serious it is... //typedef boost::variant<char, TStringConstant, TMacroUsage, TMacroDef> bodyItem; typedef std::vector<TBodyOption> Tbody; struct Tinstruction { TCmdName name; boost::optional<Tidentifier> identifier; boost::optional<Tcondition> condition; Tbody body; }; struct Treceiver { TCmdName name; boost::optional<Tidentifier> identifier; boost::optional<Tcondition> condition; boost::optional<Tbody> body; }; struct Tcommand { typedef boost::variant< Ttrigger, Tinstruction, Treceiver, TPostTrigger > Tcmd; Tcmd cmd; std::string comment; }; //vector expression typedef boost::variant<Tcommand, std::string, boost::spirit::unused_type> TERMline; typedef std::string TVModifier; //'`', ',', ',@', '#'' struct TSymbol { std::vector<TVModifier> symModifier; std::string sym; }; //for #'symbol expression enum EVOtions{VEXP, SYMBOL, CHAR, DOUBLE, INT, TCMD, STRINGC}; struct TVExp; typedef boost::variant<boost::recursive_wrapper<TVExp>, TSymbol, char, double, int, Tcommand, TStringConstant > TVOption; //options in v-expression //v-expression struct TVExp { std::vector<TVModifier> modifier; std::vector<TVOption> children; }; //script line typedef boost::variant<TVExp, TERMline> TLine; } struct LineInfo { ERM::TLine tl; int realLineNum; }; class ERMParser { private: std::string srcFile; void repairEncoding(char * str, int len) const; //removes nonstandard ascii characters from string void repairEncoding(std::string & str) const; //removes nonstandard ascii characters from string enum ELineType{COMMAND_FULL, COMMENT, UNFINISHED, END_OF}; int countHatsBeforeSemicolon(const std::string & line) const; ERM::TLine parseLine(const std::string & line, int realLineNo); public: ERMParser(std::string file); std::vector<LineInfo> parseFile(); static ERM::TLine parseLine(const std::string & line); };