Orocos Real-Time Toolkit  2.6.0
ProgramGraphParser.cpp
00001 /***************************************************************************
00002   tag: Peter Soetens  Mon May 10 19:10:37 CEST 2004  ProgramGraphParser.cxx
00003 
00004                         ProgramGraphParser.cxx -  description
00005                            -------------------
00006     begin                : Mon May 10 2004
00007     copyright            : (C) 2004 Peter Soetens
00008     email                : peter.soetens@mech.kuleuven.ac.be
00009 
00010  ***************************************************************************
00011  *   This library is free software; you can redistribute it and/or         *
00012  *   modify it under the terms of the GNU Lesser General Public            *
00013  *   License as published by the Free Software Foundation; either          *
00014  *   version 2.1 of the License, or (at your option) any later version.    *
00015  *                                                                         *
00016  *   This library is distributed in the hope that it will be useful,       *
00017  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00018  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
00019  *   Lesser General Public License for more details.                       *
00020  *                                                                         *
00021  *   You should have received a copy of the GNU Lesser General Public      *
00022  *   License along with this library; if not, write to the Free Software   *
00023  *   Foundation, Inc., 59 Temple Place,                                    *
00024  *   Suite 330, Boston, MA  02111-1307  USA                                *
00025  *                                                                         *
00026  ***************************************************************************/
00027 
00028 #include "parser-debug.hpp"
00029 #include "parse_exception.hpp"
00030 #include "ProgramGraphParser.hpp"
00031 #include "ArgumentsParser.hpp"
00032 
00033 #include "CommandNOP.hpp"
00034 #include "CommandDataSource.hpp"
00035 #include "ConditionTrue.hpp"
00036 #include "../Logger.hpp"
00037 #include "DataSourceCondition.hpp"
00038 
00039 #include "ConditionComposite.hpp"
00040 #include "ConditionFalse.hpp"
00041 #include "ConditionOnce.hpp"
00042 #include "CommandComposite.hpp"
00043 #include "CommandBinary.hpp"
00044 
00045 #include "TryCommand.hpp"
00046 #include "FunctionFactory.hpp"
00047 #include "../TaskContext.hpp"
00048 #include "../internal/GlobalService.hpp"
00049 
00050 #include <iostream>
00051 #include <boost/bind.hpp>
00052 #include <boost/lambda/lambda.hpp>
00053 
00054 #ifdef WIN32
00055     #ifdef NDEBUG
00056         #pragma optimize( "", off)
00057     #endif
00058 #endif
00059 
00060 namespace RTT
00061 {
00062   using namespace boost;
00063   using namespace detail;
00064 
00065 
00066 
00067     namespace {
00068         boost::spirit::classic::assertion<std::string> expect_opencurly("Open curly brace '{' expected.");
00069         boost::spirit::classic::assertion<std::string> expect_closecurly("Closing curly brace '}' expected in statement block (or could not find out what this line means).");
00070         boost::spirit::classic::assertion<std::string> expect_closefunction("Closing curly brace '}' expected at end of program or function (or could not find out what this line means).");
00071         boost::spirit::classic::assertion<std::string> expect_open("Open brace '(' expected.");
00072         boost::spirit::classic::assertion<std::string> expect_close("Closing brace ')' expected.");
00073         boost::spirit::classic::assertion<std::string> expect_comma("Expected a comma separator.");
00074         boost::spirit::classic::assertion<std::string> expect_ident("Expected a valid identifier.");
00075         boost::spirit::classic::assertion<std::string> expect_semicolon("Semicolon ';' expected after statement.");
00076         boost::spirit::classic::assertion<std::string> expect_condition("Expected a boolean expression ( a condition ).");
00077         boost::spirit::classic::assertion<std::string> expect_expression("Expected an expression.");
00078         boost::spirit::classic::assertion<std::string> expect_command("Expected a command after 'do'.");
00079         boost::spirit::classic::assertion<std::string> expect_nl("Expected a newline after statement.");
00080         boost::spirit::classic::assertion<std::string> expect_eof("Invalid input in file.");
00081         boost::spirit::classic::assertion<std::string> expect_term("No valid termination claues found in do ... until { } block.");
00082     }
00083 
00084 
00085   ProgramGraphParser::ProgramGraphParser( iter_t& positer, TaskContext* t, ExecutionEngine* caller, CommonParser& cp)
00086       : rootc( t ),context(), fcontext(0), mpositer( positer ),
00087         mcallfunc(),
00088         implcond(0), mcondition(0), try_cond(0),
00089         commonparser(cp),
00090         conditionparser( rootc, caller, cp ),
00091         valuechangeparser( rootc, cp, t->provides(), caller ),
00092         expressionparser( rootc, caller, cp ),
00093         argsparser(0),
00094         peerparser(rootc, commonparser),
00095         program_builder( new FunctionGraphBuilder() ),
00096         for_init_command(0),
00097         exportf(false),globalf(false),
00098         ln_offset(0)
00099   {
00100       // putting the code in setup() works around a GCC 4.1 bug.
00101       this->setup();
00102       this->setup2();
00103   }
00104 
00105   ProgramGraphParser::~ProgramGraphParser() {
00106       // necessary in case we were used outside of our parse() methods.
00107       // we also try to remove the service, because the user will call programParserResult()
00108       // to do cleanup himself, in which case this becomes a no-op.
00109       cleanup(true);
00110   }
00111 
00112   void ProgramGraphParser::setup() {
00113     BOOST_SPIRIT_DEBUG_RULE( newline );
00114     BOOST_SPIRIT_DEBUG_RULE( openbrace );
00115     BOOST_SPIRIT_DEBUG_RULE( closebrace );
00116     BOOST_SPIRIT_DEBUG_RULE( opencurly );
00117     BOOST_SPIRIT_DEBUG_RULE( closecurly );
00118     BOOST_SPIRIT_DEBUG_RULE( semicolon );
00119     BOOST_SPIRIT_DEBUG_RULE( condition );
00120     BOOST_SPIRIT_DEBUG_RULE( terminationclause );
00121     BOOST_SPIRIT_DEBUG_RULE( jumpdestination );
00122     BOOST_SPIRIT_DEBUG_RULE( terminationpart );
00123     BOOST_SPIRIT_DEBUG_RULE( dostatement );
00124     BOOST_SPIRIT_DEBUG_RULE( trystatement );
00125     BOOST_SPIRIT_DEBUG_RULE( catchpart );
00126     BOOST_SPIRIT_DEBUG_RULE( statement );
00127     BOOST_SPIRIT_DEBUG_RULE( line );
00128     BOOST_SPIRIT_DEBUG_RULE( content );
00129     BOOST_SPIRIT_DEBUG_RULE( program );
00130     BOOST_SPIRIT_DEBUG_RULE( production );
00131     BOOST_SPIRIT_DEBUG_RULE( valuechange );
00132     BOOST_SPIRIT_DEBUG_RULE( function );
00133     BOOST_SPIRIT_DEBUG_RULE( functions );
00134     BOOST_SPIRIT_DEBUG_RULE( arguments );
00135     BOOST_SPIRIT_DEBUG_RULE( returnstatement );
00136     BOOST_SPIRIT_DEBUG_RULE( funcstatement );
00137     BOOST_SPIRIT_DEBUG_RULE( continuepart );
00138     BOOST_SPIRIT_DEBUG_RULE( callpart );
00139     BOOST_SPIRIT_DEBUG_RULE( returnpart );
00140     BOOST_SPIRIT_DEBUG_RULE( ifstatement );
00141     BOOST_SPIRIT_DEBUG_RULE( whilestatement );
00142     BOOST_SPIRIT_DEBUG_RULE( forstatement );
00143     BOOST_SPIRIT_DEBUG_RULE( breakstatement );
00144     BOOST_SPIRIT_DEBUG_RULE( ifblock );
00145     BOOST_SPIRIT_DEBUG_RULE( funcargs );
00146 
00147     //newline = ch_p( '\n' );
00148     openbrace = expect_open( ch_p('(') );
00149     closebrace = expect_close( ch_p(')') );
00150     opencurly = expect_opencurly( ch_p('{') );
00151     closecurly = expect_closecurly( ch_p('}') );
00152     semicolon = expect_semicolon( ch_p(';') );
00153     condition = expect_condition( conditionparser.parser()[ boost::bind(&ProgramGraphParser::seencondition, this) ] );
00154 
00155     // program is the production rule of this grammar.  The
00156     // production rule is the rule that the entire input should be
00157     // matched by...  This line basically means that we're finished
00158     // ;)
00159     // Zero or n functions can precede the program.
00160     production = *( program | function )[boost::bind(&ProgramGraphParser::programtext,this, _1, _2)] >> expect_eof(end_p) ;
00161 
00162     // a function is very similar to a program, but it also has a name
00163     function = (
00164             // optional visibility qualifiers:
00165        !( keyword_p( "export" )[boost::bind(&ProgramGraphParser::exportdef, this)] | keyword_p( "global" )[boost::bind(&ProgramGraphParser::globaldef, this)] | keyword_p("local") )
00166        >> (keyword_p( "function" ) | commonparser.notassertingidentifier[boost::bind( &ProgramGraphParser::seenreturntype, this, _1, _2)])
00167        >> expect_ident( commonparser.identifier[ boost::bind( &ProgramGraphParser::functiondef, this, _1, _2 ) ] )
00168        >> !funcargs
00169        >> opencurly
00170        >> content
00171        >> expect_closefunction( ch_p('}') )[ boost::bind( &ProgramGraphParser::seenfunctionend, this ) ]
00172        );
00173 
00174     // the function's definition args :
00175     funcargs = ch_p('(') >> ( !str_p("void") >> ch_p(')') | ((
00176          valuechangeparser.bareDefinitionParser()[boost::bind(&ProgramGraphParser::seenfunctionarg, this)]
00177              >> *(ch_p(',')>> valuechangeparser.bareDefinitionParser()[boost::bind(&ProgramGraphParser::seenfunctionarg, this)]) )
00178         >> closebrace ));
00179 
00180     // a program looks like "program { content }".
00181     program =
00182         keyword_p( "program" )
00183       >> expect_ident( commonparser.identifier[ boost::bind( &ProgramGraphParser::programdef, this, _1, _2 ) ] )
00184       >> opencurly
00185       >> content
00186       >> expect_closefunction( ch_p('}') )[ boost::bind( &ProgramGraphParser::seenprogramend, this ) ];
00187 
00188     // the content of a program can be any number of lines
00189     content = *line;
00190 
00191     // a line can be empty or contain a statement. Empty is
00192     // necessary, because comment's are skipped, but newline's
00193     // aren't.  So a line like "/* very interesting comment
00194     // */\n" will reach us as simply "\n"..
00195     //line = !( statement ) >> eol_p;
00196     line = statement[boost::bind(&ProgramGraphParser::noskip_eol, this )] >> commonparser.eos[boost::bind(&ProgramGraphParser::skip_eol, this )];
00197 
00198     statement = valuechange | trystatement | funcstatement | returnstatement | ifstatement | whilestatement | forstatement | breakstatement | dostatement;
00199 
00200     valuechange = valuechangeparser.parser()[ boost::bind( &ProgramGraphParser::seenvaluechange, this ) ];
00201 
00202     // take into account deprecated 'do' and 'set'
00203     dostatement = !keyword_p("do") >> !keyword_p("set") >> !keyword_p("call") >>
00204             (
00205              ( keyword_p("yield") | keyword_p("nothing"))[boost::bind(&ProgramGraphParser::seenyield,this)]
00206             | expressionparser.parser()[ boost::bind(&ProgramGraphParser::seenstatement,this) ]
00207             );
00208 
00209     // a try statement: "try xxx catch { stuff to do once on any error} "
00210     trystatement =
00211         keyword_p("try")
00212          >> expect_command ( expressionparser.parser()[ boost::bind( &ProgramGraphParser::seentrystatement, this ) ] )
00213          >> !catchpart;
00214 
00215   }
00216 
00217     void ProgramGraphParser::initBodyParser(const std::string& name, Service::shared_ptr stck, int offset) {
00218         ln_offset = offset;
00219         assert(program_builder != 0 );
00220         program_builder->startFunction(name);
00221         this->setStack( stck );
00222         this->clearParseState();
00223     }
00224 
00225     rule_t& ProgramGraphParser::programParser() {
00226         return program;
00227     }
00228 
00229     rule_t& ProgramGraphParser::functionParser() {
00230         return function;
00231     }
00232 
00233     rule_t& ProgramGraphParser::bodyParser() {
00234         // content is the bodyparser of a program or function
00235         return content;
00236     }
00237 
00238     rule_t& ProgramGraphParser::statementParser() {
00239         // line is the statement parser of a program or function
00240         return line;
00241     }
00242 
00243     ProgramInterfacePtr ProgramGraphParser::programParserResult() {
00244         ProgramInterfacePtr result;
00245         if (program_list.empty())
00246             return result;
00247         program_text = "Bug: Program Text to be set by Parser.";
00248         // set the program text in each program :
00249         program_list.front()->setText( program_text );
00250         result=program_list.front();
00251         this->cleanup(false);
00252         program_list.clear();
00253         return result;
00254     }
00255 
00256     ProgramInterfacePtr ProgramGraphParser::bodyParserResult() {
00257 
00258         // store the variables in the program's taskcontext object.
00259         valuechangeparser.store( context );
00260         valuechangeparser.reset();
00261 
00262         // Fake a 'return' statement at the last line.
00263         program_builder->returnFunction( new ConditionTrue, mpositer.get_position().line - ln_offset );
00264         program_builder->proceedToNext( mpositer.get_position().line - ln_offset);
00265         return program_builder->endFunction( mpositer.get_position().line - ln_offset );
00266     }
00267 
00268 //    ProgramInterfacePtr ProgramGraphParser::statementParserResult() {
00269 //
00270 //        // Fake a 'return' statement at the last line.
00271 //        program_builder->returnFunction( new ConditionTrue, mpositer.get_position().line - ln_offset );
00272 //        program_builder->proceedToNext( mpositer.get_position().line - ln_offset);
00273 //        return program_builder->getFunction();
00274 //    }
00275 
00276     void ProgramGraphParser::setStack(Service::shared_ptr st) {
00277         context = st;
00278         valuechangeparser.load(context);
00279     }
00280 
00281     void ProgramGraphParser::clearParseState() {
00282         exportf = false;
00283         rettype.clear();
00284     }
00285 
00286     void ProgramGraphParser::startofprogram()
00287     {
00288     }
00289 
00290   void ProgramGraphParser::programdef( iter_t begin, iter_t end )
00291   {
00292       // Now that we got the name, set everything up:
00293 
00294       std::string def(begin, end);
00295 
00296       if ( rootc->provides()->hasService( def ) )
00297           throw parse_exception_semantic_error("Service with name '" + def + "' already present in task '"+rootc->getName()+"'.");
00298 
00299       FunctionGraphPtr pi(program_builder->startFunction( def ));
00300       // ptsk becomes the owner of pi.
00301       ProgramServicePtr ptsk(new ProgramService( pi, rootc ));
00302       pi->setProgramService(ptsk);
00303       pi->setUnloadOnStop( false ); // since we assign a service, set this to false.
00304       context = ptsk;
00305       rootc->provides()->addService( ptsk );
00306   }
00307 
00308   void ProgramGraphParser::programtext( iter_t begin, iter_t end )
00309   {
00310       // we set it in the parse() function. It is set to the whole script such that line numbers are correct.
00311       //program_text = std::string(begin, end);
00312   }
00313 
00314   void ProgramGraphParser::exportdef()
00315   {
00316       exportf = true;
00317   }
00318 
00319   void ProgramGraphParser::globaldef()
00320   {
00321       globalf = true;
00322   }
00323 
00324   void ProgramGraphParser::seenreturntype( iter_t begin, iter_t end )
00325   {
00326       rettype = std::string(begin, end);
00327   }
00328   void ProgramGraphParser::functiondef( iter_t begin, iter_t end )
00329   {
00330       // store the function in our map for later
00331       // referencing.
00332       std::string funcdef(begin, end);
00333       // store the function in the TaskContext current.__functions
00334 //       TaskContext* __f = rootc->getPeer("__functions");
00335 //       if ( __f == 0 ) {
00336 //           // install the __functions if not yet present.
00337 //           __f = new TaskContext("__functions", rootc->engine() );
00338 //           rootc->connectPeers( __f );
00339 //       }
00340 
00341 //       if ( __f->hasPeer( funcdef ) )
00342       // only redefining a function twice in the same file is an error. If the function
00343       // was already added to the scripting or component interface before, we replace it
00344       // and warn about it in seenfunctionend():
00345       if ( mfuncs.count( funcdef ) )
00346           throw parse_exception_semantic_error("function " + funcdef + " redefined.");
00347 
00348       AttributeBase* retarg = 0;
00349       if ( !rettype.empty() && rettype != "void") {
00350           TypeInfo* type = TypeInfoRepository::Instance()->type( rettype );
00351           if ( type == 0 )
00352               throw_( iter_t(), "Return type '" + rettype + "' for function '"+ funcdef +"' is an unknown type." );
00353           retarg = type->buildAttribute("result");
00354       }
00355 
00356       mfuncs[funcdef] = program_builder->startFunction( funcdef );
00357       program_builder->getFunction()->setResult( retarg );
00358 
00359       rettype.clear();
00360 
00361       // Connect the new function to the relevant contexts.
00362       // 'fun' acts as a stack for storing variables.
00363       fcontext = new TaskContext(funcdef, rootc->engine() );
00364       context = fcontext->provides();
00365   }
00366 
00367   void ProgramGraphParser::seenfunctionarg()
00368   {
00369       // the ValueChangeParser stores each variable in the
00370       // current stack's repository, but we need to inform the
00371       // FunctionGraph itself about its arguments.
00372       program_builder->getFunction()->addArgument( valuechangeparser.lastDefinedValue()->clone() );
00373       valuechangeparser.clear();
00374   }
00375 
00376   void ProgramGraphParser::seenfunctionend()
00377   {
00378       // Fake a 'return' statement at the last line.
00379       program_builder->returnFunction( new ConditionTrue, mpositer.get_position().line - ln_offset );
00380       program_builder->proceedToNext( mpositer.get_position().line - ln_offset );
00381       boost::shared_ptr<ProgramInterface> mfunc = program_builder->endFunction( mpositer.get_position().line - ln_offset );
00382 
00383       // export the function in the context's interface.
00384       if (exportf) {
00385           std::map<const DataSourceBase*, DataSourceBase*> dummy;
00386           FunctionFactory* cfi = new FunctionFactory(ProgramInterfacePtr(mfunc->copy(dummy)), rootc->engine() ); // execute in the processor which has the command.
00387           if (rootc->provides()->hasMember( mfunc->getName() ) )
00388               log(Warning) << "Redefining function '"<< rootc->getName() << "." << mfunc->getName() << "': only new programs will use this new function." <<endlog();
00389           rootc->provides()->add(mfunc->getName(), cfi );
00390           Logger::log() << Logger::Info << "Exported Function '" << mfunc->getName() << "' added to task '"<< rootc->getName() << "'" <<Logger::endl;
00391       }
00392       // attach the function to the global service interface.
00393       else if (globalf){
00394           std::map<const DataSourceBase*, DataSourceBase*> dummy;
00395           FunctionFactory* cfi = new FunctionFactory(ProgramInterfacePtr(mfunc->copy(dummy)), rootc->engine() ); // execute in the processor which has the command.
00396           if (GlobalService::Instance()->provides()->hasMember( mfunc->getName() ) )
00397               log(Warning) << "Redefining function '"<< GlobalService::Instance()->getName() << "."<< mfunc->getName() << "': only new programs will use this new function." <<endlog();
00398           GlobalService::Instance()->provides()->add(mfunc->getName(), cfi );
00399           Logger::log() << Logger::Debug << "Seen Function '" << mfunc->getName() << "' for Global Service." <<Logger::endl;
00400       } else {
00401           std::map<const DataSourceBase*, DataSourceBase*> dummy;
00402           FunctionFactory* cfi = new FunctionFactory(ProgramInterfacePtr(mfunc->copy(dummy)), rootc->engine() ); // execute in the processor which has the command.
00403           if (rootc->provides("scripting")->hasMember( mfunc->getName() ) )
00404               log(Warning) << "Redefining function '"<< rootc->getName() << ".scripting."<< mfunc->getName() << "': only new programs will use this new function." <<endlog();
00405           rootc->provides("scripting")->add(mfunc->getName(), cfi );
00406           Logger::log() << Logger::Debug << "Seen Function '" << mfunc->getName() << "' for scripting service of '"<< rootc->getName() << "'" <<Logger::endl;
00407       }
00408 
00409 
00410       delete fcontext;
00411       fcontext = 0;
00412       context.reset();
00413 
00414       // reset
00415       exportf = false; globalf = false;
00416 
00417       valuechangeparser.reset();
00418   }
00419 
00420   void ProgramGraphParser::seencondition()
00421   {
00422        mcondition = conditionparser.getParseResult();
00423        assert( mcondition );
00424 
00425        // leaves the condition in the parser, if we want to use
00426        // getParseResultAsCommand();
00427        // mcondition is only used with seen*label statements,
00428        // when the command and condition are associated,
00429        // not in the branching where the evaluation of the
00430        // condition is the command.
00431   }
00432 
00433   void ProgramGraphParser::seenreturnstatement()
00434   {
00435       // return statement can happen in program and in a function
00436       program_builder->returnFunction( new ConditionTrue, mpositer.get_position().line - ln_offset );
00437       program_builder->proceedToNext(  mpositer.get_position().line - ln_offset );
00438   }
00439 
00440   void ProgramGraphParser::seenreturnvalue()
00441   {
00442       AttributeBase* ar =program_builder->getFunction()->getResult();
00443       if ( ar == 0) {
00444           throw parse_exception_syntactic_error("Returning a value in a function returning (void).");
00445       }
00446       DataSourceBase::shared_ptr expr  = expressionparser.getResult().get();
00447       expressionparser.dropResult();
00448       try {
00449           ActionInterface* assigncomm = ar->getDataSource()->updateAction( expr.get() );
00450           // assign the return value to the return argument.
00451           program_builder->setCommand( assigncomm );
00452           program_builder->proceedToNext( new ConditionTrue(), mpositer.get_position().line - ln_offset );
00453       }
00454       catch(...) {
00455           // catch exception from updateAction.
00456           throw parse_exception_syntactic_error("Could not convert '" + expr->getType() + "' to '"+ ar->getDataSource()->getType() +"' in return statement.");
00457       }
00458   }
00459 
00460   void ProgramGraphParser::seenbreakstatement()
00461   {
00462       if ( program_builder->inLoop() ) {
00463           program_builder->breakLoop();
00464           program_builder->proceedToNext( mpositer.get_position().line - ln_offset );
00465       } else
00466           throw parse_exception_syntactic_error("Illegal use of 'break'. Can only be used within for and while loops.");
00467   }
00468 
00469   void ProgramGraphParser::seenfuncidentifier( iter_t begin, iter_t end )
00470   {
00471       // store the part after 'call'
00472       std::string fname(begin, end);
00473       if ( mfuncs.count(fname) == 0 )
00474           throw parse_exception_semantic_error("calling function " + fname + " but it is not defined ( remove the 'call' keyword ).");
00475       if ( fname == program_builder->getFunction()->getName() )
00476           throw parse_exception_semantic_error("calling function " + fname + " recursively is not allowed.");
00477 
00478       mcallfunc = mfuncs[ fname ];
00479 
00480       // Parse the function's args in the programs context.
00481       argsparser = new ArgumentsParser( expressionparser, rootc, rootc->provides(),
00482                                         "this", fname );
00483       arguments = argsparser->parser();
00484 
00485   }
00486 
00487   void ProgramGraphParser::seencallfuncargs()
00488   {
00489       callfnargs = argsparser->result();
00490   }
00491 
00492   void ProgramGraphParser::seencallfuncstatement()
00493   {
00494       log(Warning) << " 'call' has been deprecated. Please remove this keyword." << endlog();
00495       // This function is called if the 'call func' is outside
00496       // a termination clause.
00497 
00498       // add it to the main program line of execution.
00499       assert( mcallfunc );
00500         try
00501             {
00502                 program_builder->setFunction( mcallfunc, callfnargs );
00503                 // only delete parser, when the args are used.
00504                 delete argsparser;
00505                 argsparser = 0;
00506                 callfnargs.clear();
00507             }
00508         catch( const wrong_number_of_args_exception& e )
00509             {
00510                 throw parse_exception_wrong_number_of_arguments
00511                     ( rootc->getName(), mcallfunc->getName(), e.wanted, e.received );
00512             }
00513         catch( const wrong_types_of_args_exception& e )
00514             {
00515                 throw parse_exception_wrong_type_of_argument
00516                     ( rootc->getName(), mcallfunc->getName(), e.whicharg, e.expected_, e.received_ );
00517             }
00518         catch( ... )
00519             {
00520                 assert( false );
00521             }
00522 
00523       // The exit node of the function is already connected
00524       // to program->nextNode().
00525       program_builder->proceedToNext(mpositer.get_position().line - ln_offset);
00526   }
00527 
00528     void ProgramGraphParser::skip_eol() {
00529         commonparser.skipeol = true;
00530     }
00531 
00532     void ProgramGraphParser::noskip_eol() {
00533         commonparser.skipeol = false;
00534     }
00535 
00536     void ProgramGraphParser::startcatchpart() {
00537         // we saved the try_cond in the previous try statement,
00538         // now process like it said if ( try_cond ) then {...}
00539         assert( try_cond );
00540         program_builder->startIfStatement( try_cond, mpositer.get_position().line - ln_offset );
00541         try_cond = 0;
00542     }
00543 
00544     void ProgramGraphParser::seencatchpart() {
00545         this->endifblock();
00546         this->endifstatement(); // there is no 'else' part, so close the statement
00547     }
00548 
00549     void ProgramGraphParser::seenifstatement() {
00550         assert(mcondition);
00551         // transform the evaluation in a command, and pass the result
00552         // as a condition
00553         std::pair<ActionInterface*, ConditionInterface*> comcon;
00554         comcon = conditionparser.getParseResultAsCommand();
00555         program_builder->setCommand( comcon.first );
00556         program_builder->startIfStatement( comcon.second, mpositer.get_position().line - ln_offset );
00557 
00558         // we did not need this.
00559         delete mcondition;
00560         mcondition = 0;
00561     }
00562 
00563     void ProgramGraphParser::endifblock() {
00564         program_builder->endIfBlock(mpositer.get_position().line - ln_offset);
00565     }
00566 
00567 
00568     void ProgramGraphParser::endifstatement() {
00569         program_builder->endElseBlock(mpositer.get_position().line - ln_offset);
00570     }
00571 
00572     void ProgramGraphParser::seenwhilestatement() {
00573         // analogous to seenifstatement
00574         // the evaluation is a command.
00575         assert(mcondition);
00576         std::pair<ActionInterface*, ConditionInterface*> comcon;
00577         comcon = conditionparser.getParseResultAsCommand();
00578         program_builder->setCommand( comcon.first );
00579         program_builder->startWhileStatement( comcon.second, mpositer.get_position().line - ln_offset );
00580 
00581         delete mcondition;
00582         mcondition = 0;
00583     }
00584 
00585     void ProgramGraphParser::endwhilestatement() {
00586         program_builder->endWhileBlock(mpositer.get_position().line - ln_offset);
00587     }
00588 
00589 
00590     void ProgramGraphParser::seenforinit()
00591     {
00592         // the for loop is different from the while and if branch
00593         // structures in that it places an init command before the loop.
00594       ActionInterface* ac = 0;
00595       std::vector<ActionInterface*> acv = valuechangeparser.assignCommands();
00596       // and not forget to reset()..
00597       valuechangeparser.clear();
00598       if ( acv.size() == 1) {
00599           ac = acv.front();
00600       }
00601       else if (acv.size() > 1) {
00602           ac = new CommandComposite( acv );
00603       }
00604       for_init_command = ac;
00605     }
00606 
00607     void ProgramGraphParser::seenforinit_expr()
00608     {
00609         DataSourceBase::shared_ptr expr = expressionparser.getResult();
00610         expressionparser.dropResult();
00611         for_init_command = new CommandDataSource( expr );
00612     }
00613 
00614     void ProgramGraphParser::seenforincr()
00615     {
00616         DataSourceBase::shared_ptr expr = expressionparser.getResult();
00617         expressionparser.dropResult();
00618         for_incr_command.push( new CommandDataSource( expr ) );
00619     }
00620 
00621     void ProgramGraphParser::seenemptyforincr()
00622     {
00623         for_incr_command.push( 0 );
00624     }
00625 
00626     void ProgramGraphParser::seenforstatement() {
00627         assert( mcondition );
00628 
00629         // first insert the initialisation command.
00630         if ( for_init_command )
00631             {
00632                 program_builder->setCommand( for_init_command );
00633                 program_builder->proceedToNext( new ConditionTrue, mpositer.get_position().line - ln_offset );
00634             }
00635         for_init_command = 0;
00636 
00637         // A for is nothing more than a while loop...
00638         std::pair<ActionInterface*, ConditionInterface*> comcon;
00639         comcon = conditionparser.getParseResultAsCommand();
00640         program_builder->setCommand( comcon.first );
00641         program_builder->startWhileStatement( comcon.second, mpositer.get_position().line - ln_offset );
00642         delete mcondition;
00643         mcondition = 0;
00644     }
00645 
00646     void ProgramGraphParser::endforstatement() {
00647         // the last statement is a _conditional_ increment of the 'counter'
00648         ActionInterface* incr = for_incr_command.top();
00649         for_incr_command.pop();
00650         // is null or an action to increment
00651         if ( incr )
00652             {
00653                 program_builder->setCommand( incr );
00654                 // Since a valuechange does not add edges, we use this variant
00655                 // to create one.
00656                 program_builder->proceedToNext( new ConditionTrue, mpositer.get_position().line - ln_offset );
00657             }
00658         program_builder->endWhileBlock(mpositer.get_position().line - ln_offset);
00659     }
00660 
00661   void ProgramGraphParser::seenprogramend()
00662   {
00663       // Fake a 'return' statement at the last line.
00664       program_builder->returnFunction( new ConditionTrue, mpositer.get_position().line - ln_offset );
00665       program_builder->proceedToNext( mpositer.get_position().line - ln_offset );
00666       program_list.push_back(program_builder->endFunction( mpositer.get_position().line - ln_offset ) );
00667 
00668       // store the variables in the program's taskcontext object.
00669       valuechangeparser.store( context );
00670       valuechangeparser.reset();
00671   }
00672 
00673   std::vector< ProgramInterfacePtr > ProgramGraphParser::parse( iter_t& begin, iter_t end )
00674   {
00675       // end is not used !
00676     iter_t begin_copy = begin;
00677     skip_parser_t skip_parser = comment_p( "#" ) | comment_p( "//" ) | comment_p( "/*", "*/" ) | (space_p - eol_p) | commonparser.skipper;
00678     iter_pol_t iter_policy( skip_parser );
00679     scanner_pol_t policies( iter_policy );
00680     scanner_t scanner( begin, end, policies );
00681     program_list.clear();
00682 
00683     // todo :Add a universal collect/collectIfDone/ret(sendh, args) operationfactoryparts.
00684     //rootc->add("collect",&ProgramGraphParser::collectHandler, this)
00685 
00686     try {
00687       if ( ! production.parse( scanner ) )
00688       {
00689           // This gets shown if we didn't even get the chance to throw an exception :
00690         cleanup(true);
00691         throw file_parse_exception(new parse_exception_syntactic_error( " no valid input found." ),
00692                                    mpositer.get_position().file, mpositer.get_position().line,
00693                                    mpositer.get_position().column );
00694       }
00695       program_text = std::string( begin_copy, begin ); // begin is by reference.
00696       // set the program text in each program :
00697       for (std::vector<FunctionGraphPtr>::iterator it= program_list.begin();it!=program_list.end();++it)
00698           (*it)->setText( program_text );
00699       this->cleanup(false);
00700       std::vector<ProgramInterfacePtr> result;
00701       for (std::vector<FunctionGraphPtr>::iterator it= program_list.begin();it!=program_list.end();++it)
00702           result.push_back( *it );
00703       program_list.clear();
00704       return result;
00705     }
00706     catch( const parser_error<std::string, iter_t>& e )
00707         {
00708             cleanup(true);
00709             program_list.clear();
00710             throw file_parse_exception(
00711                 new parse_exception_syntactic_error( e.descriptor ),
00712                 mpositer.get_position().file, mpositer.get_position().line,
00713                 mpositer.get_position().column );
00714 
00715         }
00716     // Catch our Orocos exceptions
00717     catch( const parse_exception& e )
00718     {
00719         cleanup(true);
00720       program_list.clear();
00721       throw file_parse_exception(
00722                 e.copy(), mpositer.get_position().file,
00723                 mpositer.get_position().line, mpositer.get_position().column );
00724     }
00725   }
00726 
00727   std::vector< ProgramInterfacePtr > ProgramGraphParser::parseFunction( iter_t& begin, iter_t end )
00728   {
00729       // end is not used !
00730     iter_t begin_copy = begin;
00731     //skip_parser_t skip_parser = SKIP_PARSER;
00732     //iter_pol_t iter_policy( skip_parser );
00733     iter_pol_t iter_policy( ( comment_p( "#" ) | comment_p( "//" ) | comment_p( "/*", "*/" ) | (space_p - eol_p) | commonparser.skipper  ) );
00734     scanner_pol_t policies( iter_policy );
00735     scanner_t scanner( begin, end, policies );
00736 
00737     std::vector< ProgramInterfacePtr > function_list;
00738 
00739     try {
00740       if ( ! functions.parse( scanner ) )
00741       {
00742           // This gets shown if we didn't even get the chance to throw an exception :
00743         cleanup(false);
00744         throw file_parse_exception(new parse_exception_syntactic_error( " no valid input found." ),
00745                                    mpositer.get_position().file, mpositer.get_position().line,
00746                                    mpositer.get_position().column );
00747       }
00748       program_text = std::string( begin_copy, begin ); // begin is by reference.
00749       // set the program text in each function :
00750       for (funcmap::iterator it= mfuncs.begin();it!=mfuncs.end();++it) {
00751           it->second->setText( program_text );      // set text.
00752           function_list.push_back( it->second );
00753       }
00754 
00755       this->cleanup(false);
00756       return function_list;
00757     }
00758     // Catch Boost::Spirit exceptions
00759     catch( const parser_error<std::string, iter_t>& e )
00760         {
00761             cleanup(false);
00762             throw file_parse_exception(
00763                 new parse_exception_syntactic_error( e.descriptor ),
00764                 mpositer.get_position().file, mpositer.get_position().line,
00765                 mpositer.get_position().column );
00766 
00767         }
00768     // Catch our Orocos exceptions
00769     catch( const parse_exception& e )
00770     {
00771         cleanup(false);
00772         throw file_parse_exception(
00773                 e.copy(), mpositer.get_position().file,
00774                 mpositer.get_position().line, mpositer.get_position().column );
00775     }
00776   }
00777 
00778   void ProgramGraphParser::cleanup(bool unload_service)
00779   {
00780       if (unload_service && rootc && context)
00781           rootc->provides()->removeService( context->getName() );
00782       // after an exception, we can be in any state, so cleanup
00783       // all temp objects.
00784       delete argsparser;
00785       argsparser = 0;
00786       delete implcond;
00787       implcond = 0;
00788       delete mcondition;
00789       mcondition = 0;
00790       delete try_cond;
00791       try_cond = 0;
00792       delete for_init_command;
00793       for_init_command = 0;
00794       while (!for_incr_command.empty() ) {
00795           delete for_incr_command.top();
00796           for_incr_command.pop();
00797       }
00798       // cleanup all functions :
00799       delete fcontext;
00800       fcontext = 0;
00801       exportf = false; globalf = false;
00802       rettype.clear();
00803       if ( rootc == 0)
00804           return;
00805 //       TaskContext* __f = rootc->getPeer("__functions");
00806 //       if ( __f != 0 ) {
00807 //           // first remove rootc from __f itself
00808 //           rootc->disconnectPeers( __f->getName() );
00809 //           delete __f;
00810 //       }
00811       while ( ! mfuncs.empty() ) {
00812           mfuncs.erase( mfuncs.begin() );
00813       }
00814       context.reset();
00815 
00816       valuechangeparser.reset();
00817       conditionparser.reset();
00818       peerparser.reset();
00819   }
00820 
00821   void ProgramGraphParser::seentrystatement()
00822   {
00823       // a try expression/method call.
00824       ActionInterface*   command;
00825       DataSourceBase::shared_ptr expr  = expressionparser.getResult().get();
00826       expressionparser.dropResult();
00827       DataSource<bool>* bexpr = dynamic_cast<DataSource<bool>*>(expr.get());
00828       if (bexpr == 0) {
00829           // if not returning a bool, the try is useless.
00830           command = new CommandDataSource( expr );
00831           try_cond = new ConditionFalse(); // never execute catch part.
00832           program_builder->setCommand(command);
00833       } else {
00834           command = new CommandDataSourceBool( bexpr );
00835 
00836           // try-wrap the asyn or dispatch command, store the result in try_cond.
00837           TryCommand* trycommand =  new TryCommand( command );
00838           // returns true if failure :
00839           TryCommandResult* tryresult = new TryCommandResult( trycommand->result(), true );
00840           program_builder->setCommand( trycommand );
00841           try_cond = tryresult; // save try_cond for catch part (ie true if failure)
00842       }
00843       if ( program_builder->buildEdges() == 0 )
00844           program_builder->proceedToNext( new ConditionTrue(), mpositer.get_position().line - ln_offset );
00845       else
00846           program_builder->proceedToNext( mpositer.get_position().line - ln_offset );      // we get the data from commandparser
00847   }
00848 
00849   void ProgramGraphParser::seenstatement()
00850   {
00851       // an expression/method call (former do).
00852       DataSourceBase::shared_ptr expr  = expressionparser.getResult().get();
00853       expressionparser.dropResult();
00854       DataSource<bool>* bexpr = dynamic_cast<DataSource<bool>*>(expr.get());
00855       if (bexpr)
00856           program_builder->setCommand( new CommandDataSourceBool( bexpr ) );
00857       else
00858           program_builder->setCommand( new CommandDataSource( expr ) );
00859       if ( program_builder->buildEdges() == 0 )
00860           program_builder->proceedToNext( new ConditionTrue(), mpositer.get_position().line - ln_offset );
00861       else
00862           program_builder->proceedToNext( mpositer.get_position().line - ln_offset );
00863   }
00864 
00865   void ProgramGraphParser::seenyield()
00866   {
00867       // a yield branch
00868       program_builder->setCommand( new CommandNOP );
00869       program_builder->proceedToNext( new ConditionOnce(false), mpositer.get_position().line - ln_offset );
00870   }
00871 
00872   void ProgramGraphParser::seenvaluechange()
00873   {
00874     // some value changes generate a command, we need to add it to
00875     // the program.
00876       ActionInterface* ac = 0;
00877       std::vector<ActionInterface*> acv = valuechangeparser.assignCommands();
00878       // and not forget to reset()..
00879       valuechangeparser.clear();
00880       if ( acv.size() == 1) {
00881           ac = acv.front();
00882       }
00883       else if (acv.size() > 1) {
00884           ac = new CommandComposite(acv);
00885       }
00886       if (ac) {
00887           program_builder->setCommand( ac );
00888           // Since a valuechange does not add edges, we use this variant
00889           // to create one.
00890           program_builder->proceedToNext( new ConditionTrue, mpositer.get_position().line - ln_offset );
00891       }
00892   }
00893 
00894     void ProgramGraphParser::seencallfunclabel( iter_t begin, iter_t end )
00895     {
00896           // Used for "call xyz"
00897           // lookup mcallfunc
00898           seenfuncidentifier( begin, end );
00899 
00900           assert( mcondition );
00901           assert( mcallfunc );
00902           program_builder->appendFunction( mcondition, mcallfunc, callfnargs);
00903           mcondition = 0;
00904 
00905     }
00906 
00907     void ProgramGraphParser::seencontinue( )
00908     {
00909         // @todo complete the impl for for/while loops.
00910         // Used for "continue"
00911         assert ( mcondition );
00912 
00913         // connect to next node under given condition.
00914         program_builder->addConditionEdge( mcondition, program_builder->nextNode() );
00915 
00916         mcondition = 0;
00917       }
00918 }