mirror of
https://github.com/vcmi/vcmi.git
synced 2024-11-28 08:48:48 +02:00
* most of WoG's ERM parsed, a few minor things left + body needs to be parsed a bit deeper.
This commit is contained in:
parent
31b4906daa
commit
caf50fc278
@ -52,21 +52,45 @@ void ERMParser::parseFile()
|
||||
}
|
||||
//parse file
|
||||
char lineBuf[1024];
|
||||
int lineNum = 1;
|
||||
parsedLine = 1;
|
||||
std::string wholeLine; //used for buffering multiline lines
|
||||
bool inString = false;
|
||||
while(file.good())
|
||||
{
|
||||
//reading line
|
||||
file.getline(lineBuf, ARRAY_COUNT(lineBuf));
|
||||
if(file.gcount() == ARRAY_COUNT(lineBuf))
|
||||
{
|
||||
tlog1 << "Encountered a problem during parsing " << srcFile << " too long line " << lineNum << "\n";
|
||||
tlog1 << "Encountered a problem during parsing " << srcFile << " too long line (" << parsedLine << ")\n";
|
||||
}
|
||||
//parsing
|
||||
parseLine(lineBuf);
|
||||
|
||||
switch(classifyLine(lineBuf, inString))
|
||||
{
|
||||
case ERMParser::COMMAND_FULL:
|
||||
case ERMParser::COMMENT:
|
||||
{
|
||||
parseLine(lineBuf);
|
||||
}
|
||||
break;
|
||||
case ERMParser::UNFINISHED_STRING:
|
||||
{
|
||||
if(!inString)
|
||||
wholeLine = "";
|
||||
inString = true;
|
||||
wholeLine += lineBuf;
|
||||
}
|
||||
break;
|
||||
case ERMParser::END_OF_STRING:
|
||||
{
|
||||
inString = false;
|
||||
wholeLine += lineBuf;
|
||||
parseLine(wholeLine);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
//loop end
|
||||
++lineNum;
|
||||
++parsedLine;
|
||||
}
|
||||
}
|
||||
|
||||
@ -87,8 +111,20 @@ namespace ERM
|
||||
valT val;
|
||||
};
|
||||
|
||||
typedef std::vector<iexpT> identifierT;
|
||||
struct TArithmeticOp
|
||||
{
|
||||
iexpT lhs, rhs;
|
||||
char opcode;
|
||||
};
|
||||
|
||||
typedef boost::variant<iexpT, TArithmeticOp > TIdentifierInternal;
|
||||
typedef std::vector< TIdentifierInternal > identifierT;
|
||||
|
||||
struct TComparison
|
||||
{
|
||||
std::string compSign;
|
||||
iexpT lhs, rhs;
|
||||
};
|
||||
|
||||
struct conditionT;
|
||||
typedef
|
||||
@ -97,19 +133,18 @@ namespace ERM
|
||||
>
|
||||
conditionNodeT;
|
||||
|
||||
|
||||
struct conditionT
|
||||
{
|
||||
typedef boost::variant<
|
||||
TComparison,
|
||||
int>
|
||||
Tcond; //comparison or condition flag
|
||||
char ctype;
|
||||
std::string cond;
|
||||
Tcond cond;
|
||||
conditionNodeT rhs;
|
||||
};
|
||||
|
||||
std::ostream & operator << (std::ostream & out, const conditionT & cond)
|
||||
{
|
||||
static char sym[] = {'&', '|', 'X', '/'};
|
||||
return out << sym[cond.ctype] << cond.cond << cond.rhs;
|
||||
}
|
||||
|
||||
struct triggerT
|
||||
{
|
||||
std::string name;
|
||||
@ -179,32 +214,68 @@ namespace ERM
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void iexpPrinter(const iexpT exp)
|
||||
{
|
||||
if(exp.varsym.is_initialized())
|
||||
{
|
||||
tlog2 << exp.varsym.get() << " ";
|
||||
}
|
||||
if(exp.val.is_initialized())
|
||||
{
|
||||
boost::apply_visitor(UNT(), exp.val.get());
|
||||
}
|
||||
}
|
||||
|
||||
struct IdentifierVisitor : boost::static_visitor<>
|
||||
{
|
||||
void operator()(iexpT const& iexp) const
|
||||
{
|
||||
iexpPrinter(iexp);
|
||||
}
|
||||
void operator()(TArithmeticOp const& arop) const
|
||||
{
|
||||
iexpPrinter(arop.lhs);
|
||||
tlog2 << " " << arop.opcode << " ";
|
||||
iexpPrinter(arop.rhs);
|
||||
}
|
||||
};
|
||||
|
||||
void identifierPrinter(const boost::optional<identifierT> & id)
|
||||
{
|
||||
if(id.is_initialized())
|
||||
{
|
||||
tlog2 << "identifier: ";
|
||||
BOOST_FOREACH(iexpT x, *id)
|
||||
BOOST_FOREACH(TIdentifierInternal x, id.get())
|
||||
{
|
||||
tlog2 << "\\";
|
||||
if(x.varsym.is_initialized())
|
||||
{
|
||||
tlog2 << x.varsym.get() << " ";
|
||||
}
|
||||
if(x.val.is_initialized())
|
||||
{
|
||||
boost::apply_visitor(UNT(), x.val.get());
|
||||
}
|
||||
boost::apply_visitor(IdentifierVisitor(), x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ConditionCondPrinter : boost::static_visitor<>
|
||||
{
|
||||
void operator()(TComparison const& cmp) const
|
||||
{
|
||||
iexpPrinter(cmp.lhs);
|
||||
tlog2 << " " << cmp.compSign << " ";
|
||||
iexpPrinter(cmp.rhs);
|
||||
}
|
||||
void operator()(int const& flag) const
|
||||
{
|
||||
tlog2 << "condflag " << flag;
|
||||
}
|
||||
};
|
||||
|
||||
void conditionPrinter(const boost::optional<conditionT> & cond)
|
||||
{
|
||||
if(cond.is_initialized())
|
||||
{
|
||||
conditionT condp = cond.get();
|
||||
tlog2 << " condition: " << condp.cond << " cond type: " << condp.ctype << " rhs:";
|
||||
tlog2 << " condition: ";
|
||||
boost::apply_visitor(ConditionCondPrinter(), condp.cond);
|
||||
tlog2 << " cond type: " << condp.ctype << " rhs:";
|
||||
|
||||
//recursive call
|
||||
if(condp.rhs.is_initialized())
|
||||
@ -282,6 +353,13 @@ BOOST_FUSION_ADAPT_STRUCT(
|
||||
(ERM::iexpT::valT, val)
|
||||
)
|
||||
|
||||
BOOST_FUSION_ADAPT_STRUCT(
|
||||
ERM::TArithmeticOp,
|
||||
(ERM::iexpT, lhs)
|
||||
(char, opcode)
|
||||
(ERM::iexpT, rhs)
|
||||
)
|
||||
|
||||
BOOST_FUSION_ADAPT_STRUCT(
|
||||
ERM::triggerT,
|
||||
(std::string, name)
|
||||
@ -289,10 +367,17 @@ BOOST_FUSION_ADAPT_STRUCT(
|
||||
(boost::optional<ERM::conditionT>, condition)
|
||||
)
|
||||
|
||||
BOOST_FUSION_ADAPT_STRUCT(
|
||||
ERM::TComparison,
|
||||
(ERM::iexpT, lhs)
|
||||
(std::string, compSign)
|
||||
(ERM::iexpT, rhs)
|
||||
)
|
||||
|
||||
BOOST_FUSION_ADAPT_STRUCT(
|
||||
ERM::conditionT,
|
||||
(char, ctype)
|
||||
(std::string, cond)
|
||||
(ERM::conditionT::Tcond, cond)
|
||||
(ERM::conditionNodeT, rhs)
|
||||
)
|
||||
|
||||
@ -337,22 +422,23 @@ namespace ERM
|
||||
comment %= *(qi::char_);
|
||||
commentLine %= ~qi::char_('!') >> comment;
|
||||
cmdName %= qi::repeat(2)[qi::char_];
|
||||
identifier %= (iexp % qi::lit('/'));
|
||||
|
||||
|
||||
condition %= qi::char_("&|X/") > *qi::char_("0-9a-zA-Z<>=-") > -condition;
|
||||
arithmeticOp %= iexp >> qi::char_ >> iexp;
|
||||
//identifier is usually a vector of i-expressions but VR receiver performs arithmetic operations on it
|
||||
identifier %= (iexp | arithmeticOp) % qi::lit('/');
|
||||
comparison %= iexp >> (*qi::char_("<=>")) >> iexp;
|
||||
condition %= qi::char_("&|X/") >> (comparison | qi::int_) >> -condition;
|
||||
|
||||
trigger %= cmdName >> -identifier >> -condition > qi::lit(";"); /////
|
||||
string %= qi::lexeme['^' >> *(qi::char_ - '^') >> '^'];
|
||||
body %= qi::lit(":") > *( qi::char_("a-zA-Z0-9/ @*?%+-:|&-") | string | macro) > qi::lit(";");
|
||||
body %= qi::lit(":") > *( qi::char_("a-zA-Z0-9/ @*?%+-:|&=><-") | string | macro) > qi::lit(";");
|
||||
instruction %= cmdName >> -identifier >> -condition >> body;
|
||||
receiver %= cmdName >> -identifier >> -condition >> body;
|
||||
receiver %= cmdName >> -identifier >> -condition >> body; //receiver without body exists... change needed
|
||||
postOBtrigger %= qi::lit("$OB") >> -identifier >> -condition > qi::lit(";");
|
||||
command %= (qi::lit("!") >>
|
||||
(
|
||||
(qi::lit("?") >> trigger) |
|
||||
(qi::lit("!") >> instruction) |
|
||||
(qi::lit("#") >> receiver) |
|
||||
((qi::lit("!") | qi::lit("d!") | qi::lit(" !")) >> receiver) |
|
||||
(qi::lit("#") >> instruction) |
|
||||
postOBtrigger
|
||||
) >> comment
|
||||
);
|
||||
@ -397,10 +483,12 @@ namespace ERM
|
||||
|
||||
qi::rule<Iterator, std::string()> macro;
|
||||
qi::rule<Iterator, iexpT()> iexp;
|
||||
qi::rule<Iterator, TArithmeticOp()> arithmeticOp;
|
||||
qi::rule<Iterator, std::string()> comment;
|
||||
qi::rule<Iterator, std::string()> commentLine;
|
||||
qi::rule<Iterator, std::string()> cmdName;
|
||||
qi::rule<Iterator, identifierT()> identifier;
|
||||
qi::rule<Iterator, TComparison()> comparison;
|
||||
qi::rule<Iterator, conditionT()> condition;
|
||||
qi::rule<Iterator, triggerT()> trigger;
|
||||
qi::rule<Iterator, bodyTbody()> body;
|
||||
@ -412,7 +500,7 @@ namespace ERM
|
||||
};
|
||||
};
|
||||
|
||||
void ERMParser::parseLine( std::string line )
|
||||
void ERMParser::parseLine( const std::string & line )
|
||||
{
|
||||
std::string::const_iterator beg = line.begin(),
|
||||
end = line.end();
|
||||
@ -423,12 +511,63 @@ void ERMParser::parseLine( std::string line )
|
||||
bool r = qi::parse(beg, end, ERMgrammar, AST);
|
||||
if(!r || beg != end)
|
||||
{
|
||||
tlog1 << "Parse error for line " << line << std::endl;
|
||||
tlog1 << "Parse error for line (" << parsedLine << ") : " << line << std::endl;
|
||||
tlog1 << "\tCannot parse: " << std::string(beg, end) << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
//parsing succeeded
|
||||
ERM::printLineAST(AST);
|
||||
//ERM::printLineAST(AST);
|
||||
}
|
||||
}
|
||||
|
||||
ERMParser::ELineType ERMParser::classifyLine( const std::string & line, bool inString ) const
|
||||
{
|
||||
ERMParser::ELineType ret;
|
||||
if(line[0] == '!')
|
||||
{
|
||||
if(countHatsBeforeSemicolon(line) % 2 == 1)
|
||||
{
|
||||
ret = ERMParser::UNFINISHED_STRING;
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = ERMParser::COMMAND_FULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(inString)
|
||||
{
|
||||
if(countHatsBeforeSemicolon(line) % 2 == 1)
|
||||
{
|
||||
ret = ERMParser::END_OF_STRING;
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = ERMParser::UNFINISHED_STRING;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = ERMParser::COMMENT;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ERMParser::countHatsBeforeSemicolon( const std::string & line ) const
|
||||
{
|
||||
//CHECK: omit macros? or anything else?
|
||||
int numOfHats = 0; //num of '^' before ';'
|
||||
//check for unmatched ^
|
||||
BOOST_FOREACH(char c, line)
|
||||
{
|
||||
if(c == ';')
|
||||
break;
|
||||
if(c == '^')
|
||||
++numOfHats;
|
||||
}
|
||||
return numOfHats;
|
||||
}
|
||||
|
@ -6,8 +6,11 @@ class ERMParser
|
||||
private:
|
||||
std::string srcFile;
|
||||
int parsedLine;
|
||||
enum ELineType{COMMAND_FULL, COMMENT, UNFINISHED_STRING, END_OF_STRING};
|
||||
int countHatsBeforeSemicolon(const std::string & line) const;
|
||||
ELineType classifyLine(const std::string & line, bool inString) const;
|
||||
void parseLine(const std::string & line);
|
||||
|
||||
void parseLine(std::string line);
|
||||
|
||||
public:
|
||||
ERMParser(std::string file);
|
||||
|
Loading…
Reference in New Issue
Block a user