2011-03-19 19:06:46 +02:00
# include "ERMParser.h"
# include <boost/spirit/include/qi.hpp>
# include <boost/bind.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>
2011-03-20 20:09:55 +02:00
# include <boost/fusion/include/adapt_struct.hpp>
2011-03-19 19:06:46 +02:00
# include <fstream>
namespace spirit = boost : : spirit ;
namespace qi = boost : : spirit : : qi ;
2011-03-20 20:09:55 +02:00
namespace ascii = spirit : : ascii ;
namespace phoenix = boost : : phoenix ;
2011-03-19 19:06:46 +02:00
2011-03-23 21:41:29 +02:00
//Greenspun's Tenth Rule of Programming:
//Any sufficiently complicated C or Fortran program contains an ad hoc, informally-specified,
//bug-ridden, slow implementation of half of Common Lisp.
//actually these macros help in dealing with boost::variant
# define BEGIN_TYPE_CASE(UN) struct UN : boost::static_visitor<> \
{
# define FOR_TYPE(TYPE, VAR) void operator()(TYPE const& VAR) const
# define DO_TYPE_CASE(UN, VAR) };boost::apply_visitor(UN(), VAR);
2011-03-19 19:06:46 +02:00
ERMParser : : ERMParser ( std : : string file )
: srcFile ( file )
{ }
void ERMParser : : parseFile ( )
{
2011-03-20 00:27:05 +02:00
std : : ifstream file ( srcFile . c_str ( ) ) ;
2011-03-19 19:06:46 +02:00
if ( ! file . is_open ( ) )
{
tlog1 < < " File " < < srcFile < < " not found or unable to open \n " ;
return ;
}
//check header
char header [ 5 ] ;
file . getline ( header , ARRAY_COUNT ( header ) ) ;
if ( std : : string ( header ) ! = " ZVSE " )
{
tlog1 < < " File " < < srcFile < < " has wrong header \n " ;
return ;
}
//parse file
char lineBuf [ 1024 ] ;
int lineNum = 1 ;
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 " ;
}
//parsing
parseLine ( lineBuf ) ;
//loop end
+ + lineNum ;
}
}
void callme ( char const & i )
{
std : : cout < < " fd " ;
}
2011-03-20 20:09:55 +02:00
namespace ERM
{
2011-03-21 22:34:44 +02:00
//i-expression (identifier expression) - an integral constant, variable symbol or array symbol
struct iexpT
{
typedef boost : : optional < boost : : variant < int , std : : string > > valT ;
boost : : optional < std : : string > varsym ;
valT val ;
} ;
typedef std : : vector < iexpT > identifierT ;
struct conditionT ;
typedef
boost : : optional <
boost : : recursive_wrapper < conditionT >
>
conditionNodeT ;
2011-03-20 20:09:55 +02:00
2011-03-21 22:34:44 +02:00
struct conditionT
{
enum ECondType { AND = 0 , OR , XOR , LAST } ctype ;
std : : string 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 ;
}
2011-03-20 20:09:55 +02:00
struct triggerT
{
std : : string name ;
boost : : optional < identifierT > identifier ;
2011-03-21 22:34:44 +02:00
boost : : optional < conditionT > condition ;
2011-03-20 20:09:55 +02:00
} ;
//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 , std : : string > bodyItem ;
typedef std : : vector < bodyItem > bodyTbody ;
struct instructionT
{
std : : string name ;
boost : : optional < identifierT > identifier ;
2011-03-21 22:34:44 +02:00
boost : : optional < conditionT > condition ;
2011-03-20 20:09:55 +02:00
bodyTbody body ;
} ;
struct receiverT
{
std : : string name ;
boost : : optional < identifierT > identifier ;
2011-03-21 22:34:44 +02:00
boost : : optional < conditionT > condition ;
2011-03-20 20:09:55 +02:00
bodyTbody body ;
} ;
struct postOBtriggerT
{
boost : : optional < identifierT > identifier ;
2011-03-21 22:34:44 +02:00
boost : : optional < conditionT > condition ;
2011-03-20 20:09:55 +02:00
} ;
typedef boost : : variant <
2011-03-23 21:41:29 +02:00
triggerT ,
instructionT ,
receiverT ,
postOBtriggerT
>
commandTcmd ;
struct commandT
{
commandTcmd cmd ;
std : : string comment ;
} ;
2011-03-20 20:09:55 +02:00
2011-03-23 21:41:29 +02:00
typedef boost : : variant < commandT , std : : string , qi : : unused_type > lineT ;
2011-03-20 20:09:55 +02:00
//console printer
2011-03-23 21:41:29 +02:00
void identifierPrinter ( const boost : : optional < identifierT > & id )
2011-03-20 20:09:55 +02:00
{
2011-03-23 21:41:29 +02:00
if ( id . is_initialized ( ) )
2011-03-20 20:09:55 +02:00
{
2011-03-23 21:41:29 +02:00
tlog2 < < " identifier: " ;
BOOST_FOREACH ( iexpT x , * id )
{
tlog2 < < " \\ " < < x . varsym < < x . val ;
}
2011-03-20 20:09:55 +02:00
}
}
2011-03-23 21:41:29 +02:00
void conditionPrinter ( const boost : : optional < conditionT > & cond )
2011-03-20 20:09:55 +02:00
{
2011-03-23 21:41:29 +02:00
if ( cond . is_initialized ( ) )
tlog2 < < " condition: " < < * cond ;
}
2011-03-20 20:09:55 +02:00
2011-03-23 21:41:29 +02:00
struct ERMprinter : boost : : static_visitor < >
{
void operator ( ) ( commandT const & cmd ) const
2011-03-20 20:09:55 +02:00
{
2011-03-23 21:41:29 +02:00
2011-03-20 20:09:55 +02:00
}
2011-03-23 21:41:29 +02:00
void operator ( ) ( std : : string const & nothing ) const
2011-03-20 20:09:55 +02:00
{
2011-03-23 21:41:29 +02:00
//tlog2 << "comment line" << std::endl;
2011-03-20 20:09:55 +02:00
}
2011-03-23 21:41:29 +02:00
void operator ( ) ( qi : : unused_type const & nothing ) const
2011-03-20 20:09:55 +02:00
{
2011-03-23 21:41:29 +02:00
//tlog2 << "Empty line" << std::endl;
2011-03-20 20:09:55 +02:00
}
} ;
2011-03-23 21:41:29 +02:00
void printLineAST ( const lineT & ast )
2011-03-20 20:09:55 +02:00
{
2011-03-23 21:41:29 +02:00
tlog2 < < " " ;
BEGIN_TYPE_CASE ( psa )
FOR_TYPE ( commandT , cmd )
2011-03-20 20:09:55 +02:00
{
2011-03-23 21:41:29 +02:00
BEGIN_TYPE_CASE ( cmt )
FOR_TYPE ( triggerT , trig )
{
tlog2 < < " trigger: " < < trig . name ;
identifierPrinter ( trig . identifier ) ;
conditionPrinter ( trig . condition ) ;
}
FOR_TYPE ( instructionT , trig )
{
tlog2 < < " instruction: " < < trig . name ;
identifierPrinter ( trig . identifier ) ;
conditionPrinter ( trig . condition ) ;
tlog2 < < " body items: " ;
BOOST_FOREACH ( bodyItem bi , trig . body )
{
tlog2 < < " " < < bi ;
}
}
FOR_TYPE ( receiverT , trig )
{
tlog2 < < " receiver: " < < trig . name ;
2011-03-20 20:09:55 +02:00
2011-03-23 21:41:29 +02:00
identifierPrinter ( trig . identifier ) ;
conditionPrinter ( trig . condition ) ;
}
FOR_TYPE ( postOBtriggerT , trig )
{
tlog2 < < " post OB trigger; " ;
identifierPrinter ( trig . identifier ) ;
conditionPrinter ( trig . condition ) ;
}
DO_TYPE_CASE ( cmt , cmd . cmd ) ;
std : : cout < < " Line comment: " < < cmd . comment < < std : : endl ;
2011-03-20 20:09:55 +02:00
}
2011-03-23 21:41:29 +02:00
FOR_TYPE ( std : : string , comment )
2011-03-20 20:09:55 +02:00
{
}
2011-03-23 21:41:29 +02:00
FOR_TYPE ( qi : : unused_type , nothing )
{
}
DO_TYPE_CASE ( psa , ast ) ;
2011-03-20 20:09:55 +02:00
}
}
2011-03-21 22:34:44 +02:00
BOOST_FUSION_ADAPT_STRUCT (
ERM : : iexpT ,
( boost : : optional < std : : string > , varsym )
( ERM : : iexpT : : valT , val )
)
2011-03-20 20:09:55 +02:00
BOOST_FUSION_ADAPT_STRUCT (
ERM : : triggerT ,
( std : : string , name )
( boost : : optional < ERM : : identifierT > , identifier )
2011-03-21 22:34:44 +02:00
( boost : : optional < ERM : : conditionT > , condition )
2011-03-20 20:09:55 +02:00
)
2011-03-21 22:34:44 +02:00
BOOST_FUSION_ADAPT_STRUCT (
ERM : : conditionT ,
( std : : string , cond )
( ERM : : conditionNodeT , rhs )
)
2011-03-20 20:09:55 +02:00
BOOST_FUSION_ADAPT_STRUCT (
ERM : : instructionT ,
( std : : string , name )
( boost : : optional < ERM : : identifierT > , identifier )
2011-03-21 22:34:44 +02:00
( boost : : optional < ERM : : conditionT > , condition )
2011-03-20 20:09:55 +02:00
( ERM : : bodyTbody , body )
)
BOOST_FUSION_ADAPT_STRUCT (
ERM : : receiverT ,
( std : : string , name )
( boost : : optional < ERM : : identifierT > , identifier )
2011-03-21 22:34:44 +02:00
( boost : : optional < ERM : : conditionT > , condition )
2011-03-20 20:09:55 +02:00
( ERM : : bodyTbody , body )
)
BOOST_FUSION_ADAPT_STRUCT (
ERM : : postOBtriggerT ,
( boost : : optional < ERM : : identifierT > , identifier )
2011-03-21 22:34:44 +02:00
( boost : : optional < ERM : : conditionT > , condition )
2011-03-20 20:09:55 +02:00
)
2011-03-23 21:41:29 +02:00
BOOST_FUSION_ADAPT_STRUCT (
ERM : : commandT ,
( ERM : : commandTcmd , cmd )
( std : : string , comment )
)
2011-03-20 20:09:55 +02:00
namespace ERM
{
template < typename Iterator >
struct ERM_grammar : qi : : grammar < Iterator , lineT ( ) >
{
ERM_grammar ( ) : ERM_grammar : : base_type ( rline , " ERM script line " )
{
2011-03-21 22:34:44 +02:00
macro % = qi : : lit ( ' $ ' ) > > * ( qi : : char_ - ' $ ' ) > > qi : : lit ( ' $ ' ) ;
iexp % = - ( * qi : : char_ ( " a-z " ) - ' u ' ) > > - ( qi : : int_ | macro ) ;
2011-03-23 21:41:29 +02:00
comment % = * ( qi : : char_ ) ;
commentLine % = ~ qi : : char_ ( ' ! ' ) > > comment ;
2011-03-21 22:34:44 +02:00
cmdName % = qi : : repeat ( 2 ) [ qi : : char_ ] ;
identifier % = ( iexp % qi : : lit ( ' / ' ) ) ;
condition % = ( qi : : lit ( ' & ' ) | qi : : lit ( ' | ' ) | qi : : lit ( ' X ' ) | qi : : lit ( ' / ' ) ) > * qi : : char_ ( " 0-9a-zA-Z<>=- " ) > - 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 ( " ; " ) ;
2011-03-20 20:09:55 +02:00
instruction % = cmdName > > - identifier > > - condition > > body ;
receiver % = cmdName > > - identifier > > - condition > > body ;
2011-03-21 22:34:44 +02:00
postOBtrigger % = qi : : lit ( " $OB " ) > > - identifier > > - condition > qi : : lit ( " ; " ) ;
2011-03-23 21:41:29 +02:00
command % = ( qi : : lit ( " ! " ) > >
2011-03-20 20:09:55 +02:00
(
2011-03-23 21:41:29 +02:00
( qi : : lit ( " ? " ) > > trigger ) |
( qi : : lit ( " ! " ) > > instruction ) |
( qi : : lit ( " # " ) > > receiver ) |
2011-03-20 20:09:55 +02:00
postOBtrigger
) > > comment
) ;
rline % =
(
2011-03-23 21:41:29 +02:00
command | commentLine | spirit : : eps
) > spirit : : eoi ;
2011-03-20 20:09:55 +02:00
//error handling
string . name ( " string constant " ) ;
2011-03-21 22:34:44 +02:00
iexp . name ( " i-expression " ) ;
2011-03-20 20:09:55 +02:00
comment . name ( " comment " ) ;
commentLine . name ( " comment line " ) ;
cmdName . name ( " name of a command " ) ;
identifier . name ( " identifier " ) ;
condition . name ( " condition " ) ;
trigger . name ( " trigger " ) ;
body . name ( " body " ) ;
instruction . name ( " instruction " ) ;
receiver . name ( " receiver " ) ;
postOBtrigger . name ( " post OB trigger " ) ;
command . name ( " command " ) ;
rline . name ( " script line " ) ;
qi : : on_error < qi : : fail >
(
rline
, std : : cout //or phoenix::ref(std::count), is there any difference?
< < phoenix : : val ( " Error! Expecting " )
< < qi : : _4 // what failed?
< < phoenix : : val ( " here: \" " )
< < phoenix : : construct < std : : string > ( qi : : _3 , qi : : _2 ) // iterators to error-pos, end
< < phoenix : : val ( " \" " )
< < std : : endl
) ;
}
qi : : rule < Iterator , std : : string ( ) > string ;
2011-03-21 22:34:44 +02:00
qi : : rule < Iterator , std : : string ( ) > macro ;
qi : : rule < Iterator , iexpT ( ) > iexp ;
2011-03-23 21:41:29 +02:00
qi : : rule < Iterator , std : : string ( ) > comment ;
qi : : rule < Iterator , std : : string ( ) > commentLine ;
2011-03-20 20:09:55 +02:00
qi : : rule < Iterator , std : : string ( ) > cmdName ;
qi : : rule < Iterator , identifierT ( ) > identifier ;
2011-03-21 22:34:44 +02:00
qi : : rule < Iterator , conditionT ( ) > condition ;
2011-03-20 20:09:55 +02:00
qi : : rule < Iterator , triggerT ( ) > trigger ;
qi : : rule < Iterator , bodyTbody ( ) > body ;
qi : : rule < Iterator , instructionT ( ) > instruction ;
qi : : rule < Iterator , receiverT ( ) > receiver ;
qi : : rule < Iterator , postOBtriggerT ( ) > postOBtrigger ;
qi : : rule < Iterator , commandT ( ) > command ;
qi : : rule < Iterator , lineT ( ) > rline ;
} ;
} ;
2011-03-19 19:06:46 +02:00
void ERMParser : : parseLine ( std : : string line )
{
2011-03-20 20:09:55 +02:00
std : : string : : const_iterator beg = line . begin ( ) ,
2011-03-19 19:06:46 +02:00
end = line . end ( ) ;
2011-03-20 20:09:55 +02:00
ERM : : ERM_grammar < std : : string : : const_iterator > ERMgrammar ;
ERM : : lineT AST ;
2011-03-23 21:41:29 +02:00
// bool r = qi::parse(beg, end, ERMgrammar, AST);
// if(!r || beg != end)
// {
// tlog1 << "Parse error for line " << line << std::endl;
// tlog1 << "\tCannot parse: " << std::string(beg, end) << std::endl;
// }
// else
// {
// //parsing succeeded
// ERM::printLineAST(AST);
// }
2011-03-19 19:06:46 +02:00
}