Orocos Real-Time Toolkit  2.9.0
ProgramGraphParser.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  tag: Peter Soetens Mon May 10 19:10:37 CEST 2004 ProgramGraphParser.cxx
3 
4  ProgramGraphParser.cxx - description
5  -------------------
6  begin : Mon May 10 2004
7  copyright : (C) 2004 Peter Soetens
8  email : peter.soetens@mech.kuleuven.ac.be
9 
10  ***************************************************************************
11  * This library is free software; you can redistribute it and/or *
12  * modify it under the terms of the GNU Lesser General Public *
13  * License as published by the Free Software Foundation; either *
14  * version 2.1 of the License, or (at your option) any later version. *
15  * *
16  * This library is distributed in the hope that it will be useful, *
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
19  * Lesser General Public License for more details. *
20  * *
21  * You should have received a copy of the GNU Lesser General Public *
22  * License along with this library; if not, write to the Free Software *
23  * Foundation, Inc., 59 Temple Place, *
24  * Suite 330, Boston, MA 02111-1307 USA *
25  * *
26  ***************************************************************************/
27 
28 #include "parser-debug.hpp"
29 #include "parse_exception.hpp"
30 #include "ProgramGraphParser.hpp"
31 #include "ArgumentsParser.hpp"
32 
33 #include "CommandNOP.hpp"
34 #include "CommandDataSource.hpp"
35 #include "ConditionTrue.hpp"
36 #include "../Logger.hpp"
37 #include "DataSourceCondition.hpp"
38 
39 #include "ConditionComposite.hpp"
40 #include "ConditionFalse.hpp"
41 #include "ConditionOnce.hpp"
42 #include "CommandComposite.hpp"
43 #include "CommandBinary.hpp"
44 
45 #include "TryCommand.hpp"
46 #include "FunctionFactory.hpp"
47 #include "../TaskContext.hpp"
48 #include "../internal/GlobalService.hpp"
49 
50 #include <iostream>
51 #include <boost/bind.hpp>
52 #include <boost/lambda/lambda.hpp>
53 
54 #ifdef WIN32
55  #ifdef NDEBUG
56  #pragma optimize( "", off)
57  #endif
58 #endif
59 
60 namespace RTT
61 {
62  using namespace boost;
63  using namespace detail;
64 
65 
66 
67  namespace {
68  boost::spirit::classic::assertion<std::string> expect_opencurly("Open curly brace '{' expected.");
69  boost::spirit::classic::assertion<std::string> expect_closecurly("Closing curly brace '}' expected in statement block (or could not find out what this line means).");
70  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).");
71  boost::spirit::classic::assertion<std::string> expect_open("Open brace '(' expected.");
72  boost::spirit::classic::assertion<std::string> expect_close("Closing brace ')' expected.");
73  boost::spirit::classic::assertion<std::string> expect_comma("Expected a comma separator.");
74  boost::spirit::classic::assertion<std::string> expect_ident("Expected a valid identifier.");
75  boost::spirit::classic::assertion<std::string> expect_semicolon("Semicolon ';' expected after statement.");
76  boost::spirit::classic::assertion<std::string> expect_condition("Expected a boolean expression ( a condition ).");
77  boost::spirit::classic::assertion<std::string> expect_expression("Expected an expression.");
78  boost::spirit::classic::assertion<std::string> expect_command("Expected a command after 'do'.");
79  boost::spirit::classic::assertion<std::string> expect_nl("Expected a newline after statement.");
80  boost::spirit::classic::assertion<std::string> expect_eof("Invalid input in file.");
81  boost::spirit::classic::assertion<std::string> expect_term("No valid termination claues found in do ... until { } block.");
82  }
83 
84 
86  : rootc( t ),context(), fcontext(), mpositer( positer ),
87  mcallfunc(),
88  implcond(0), mcondition(0), try_cond(0),
89  commonparser(cp),
90  conditionparser( rootc, caller, cp ),
91  valuechangeparser( rootc, cp, t->provides(), caller ),
92  expressionparser( rootc, caller, cp ),
93  argsparser(0),
94  peerparser(rootc, commonparser),
95  program_builder( new FunctionGraphBuilder() ),
96  for_init_command(0),
97  exportf(false),globalf(false),
98  ln_offset(0)
99  {
100  // putting the code in setup() works around a GCC 4.1 bug.
101  this->setup();
102  this->setup2();
103  }
104 
106  // necessary in case we were used outside of our parse() methods.
107  // we also try to remove the service, because the user will call programParserResult()
108  // to do cleanup himself, in which case this becomes a no-op.
109  cleanup(true);
110  }
111 
112  void ProgramGraphParser::setup() {
113  BOOST_SPIRIT_DEBUG_RULE( newline );
114  BOOST_SPIRIT_DEBUG_RULE( openbrace );
115  BOOST_SPIRIT_DEBUG_RULE( closebrace );
116  BOOST_SPIRIT_DEBUG_RULE( opencurly );
117  BOOST_SPIRIT_DEBUG_RULE( closecurly );
118  BOOST_SPIRIT_DEBUG_RULE( semicolon );
119  BOOST_SPIRIT_DEBUG_RULE( condition );
120  BOOST_SPIRIT_DEBUG_RULE( terminationclause );
121  BOOST_SPIRIT_DEBUG_RULE( jumpdestination );
122  BOOST_SPIRIT_DEBUG_RULE( terminationpart );
123  BOOST_SPIRIT_DEBUG_RULE( dostatement );
124  BOOST_SPIRIT_DEBUG_RULE( trystatement );
125  BOOST_SPIRIT_DEBUG_RULE( catchpart );
126  BOOST_SPIRIT_DEBUG_RULE( statement );
127  BOOST_SPIRIT_DEBUG_RULE( line );
128  BOOST_SPIRIT_DEBUG_RULE( content );
129  BOOST_SPIRIT_DEBUG_RULE( program );
130  BOOST_SPIRIT_DEBUG_RULE( production );
131  BOOST_SPIRIT_DEBUG_RULE( valuechange );
132  BOOST_SPIRIT_DEBUG_RULE( function );
133  BOOST_SPIRIT_DEBUG_RULE( functions );
134  BOOST_SPIRIT_DEBUG_RULE( arguments );
135  BOOST_SPIRIT_DEBUG_RULE( returnstatement );
136  BOOST_SPIRIT_DEBUG_RULE( funcstatement );
137  BOOST_SPIRIT_DEBUG_RULE( continuepart );
138  BOOST_SPIRIT_DEBUG_RULE( callpart );
139  BOOST_SPIRIT_DEBUG_RULE( returnpart );
140  BOOST_SPIRIT_DEBUG_RULE( ifstatement );
141  BOOST_SPIRIT_DEBUG_RULE( whilestatement );
142  BOOST_SPIRIT_DEBUG_RULE( forstatement );
143  BOOST_SPIRIT_DEBUG_RULE( breakstatement );
144  BOOST_SPIRIT_DEBUG_RULE( ifblock );
145  BOOST_SPIRIT_DEBUG_RULE( funcargs );
146 
147  //newline = ch_p( '\n' );
148  openbrace = expect_open( ch_p('(') );
149  closebrace = expect_close( ch_p(')') );
150  opencurly = expect_opencurly( ch_p('{') );
151  closecurly = expect_closecurly( ch_p('}') );
152  semicolon = expect_semicolon( ch_p(';') );
153  condition = expect_condition( conditionparser.parser()[ boost::bind(&ProgramGraphParser::seencondition, this) ] );
154 
155  // program is the production rule of this grammar. The
156  // production rule is the rule that the entire input should be
157  // matched by... This line basically means that we're finished
158  // ;)
159  // Zero or n functions can precede the program.
160  production = *( program | function )[boost::bind(&ProgramGraphParser::programtext,this, _1, _2)] >> expect_eof(end_p) ;
161 
162  // a function is very similar to a program, but it also has a name
163  function = (
164  // optional visibility qualifiers:
165  !( keyword_p( "export" )[boost::bind(&ProgramGraphParser::exportdef, this)] | keyword_p( "global" )[boost::bind(&ProgramGraphParser::globaldef, this)] | keyword_p("local") )[boost::bind(&ProgramGraphParser::seenvalidinput, this)]
166  >> (keyword_p( "function" )[boost::bind(&ProgramGraphParser::seenvalidinput, this)] | commonparser.notassertingidentifier[boost::bind( &ProgramGraphParser::seenreturntype, this, _1, _2)] )
167  >> expect_ident( commonparser.identifier[boost::bind(&ProgramGraphParser::seenvalidinput, this)][ boost::bind( &ProgramGraphParser::functiondef, this, _1, _2 ) ] )
168  >> !funcargs
169  >> opencurly
170  >> content
171  >> expect_closefunction( ch_p('}') )[ boost::bind( &ProgramGraphParser::seenfunctionend, this ) ]
172  );
173 
174  // the function's definition args :
175  funcargs = ch_p('(') >> ( !str_p("void") >> ch_p(')') | ((
176  valuechangeparser.bareDefinitionParser()[boost::bind(&ProgramGraphParser::seenfunctionarg, this)]
177  >> *(ch_p(',')>> valuechangeparser.bareDefinitionParser()[boost::bind(&ProgramGraphParser::seenfunctionarg, this)]) )
178  >> closebrace ));
179 
180  // a program looks like "program { content }".
181  program =
182  keyword_p( "program" )[boost::bind(&ProgramGraphParser::seenvalidinput, this)]
183  >> expect_ident( commonparser.identifier[ boost::bind( &ProgramGraphParser::programdef, this, _1, _2 ) ] )
184  >> opencurly
185  >> content
186  >> expect_closefunction( ch_p('}') )[ boost::bind( &ProgramGraphParser::seenprogramend, this ) ];
187 
188  // the content of a program can be any number of lines
189  content = *line;
190 
191  // a line can be empty or contain a statement. Empty is
192  // necessary, because comment's are skipped, but newline's
193  // aren't. So a line like "/* very interesting comment
194  // */\n" will reach us as simply "\n"..
195  //line = !( statement ) >> eol_p;
196  line = statement[boost::bind(&ProgramGraphParser::noskip_eol, this )] >> commonparser.eos[boost::bind(&ProgramGraphParser::skip_eol, this )];
197 
198  statement = valuechange | trystatement | funcstatement | returnstatement | ifstatement | whilestatement | forstatement | breakstatement | dostatement;
199 
200  valuechange = valuechangeparser.parser()[ boost::bind( &ProgramGraphParser::seenvaluechange, this ) ];
201 
202  // take into account deprecated 'do' and 'set'
203  dostatement = !keyword_p("do") >> !keyword_p("set") >> !keyword_p("call") >>
204  (
205  ( keyword_p("yield") | keyword_p("nothing"))[boost::bind(&ProgramGraphParser::seenyield,this)]
206  | expressionparser.parser()[ boost::bind(&ProgramGraphParser::seenstatement,this) ]
207  );
208 
209  // a try statement: "try xxx catch { stuff to do once on any error} "
210  trystatement =
211  keyword_p("try")
212  >> expect_command ( expressionparser.parser()[ boost::bind( &ProgramGraphParser::seentrystatement, this ) ] )
213  >> !catchpart;
214 
215  }
216 
217  void ProgramGraphParser::initBodyParser(const std::string& name, Service::shared_ptr stck, int offset) {
218  ln_offset = offset;
219  assert(program_builder != 0 );
220  program_builder->startFunction(name);
221  this->setStack( stck );
222  this->clearParseState();
223  }
224 
226  return program;
227  }
228 
230  return function;
231  }
232 
234  // content is the bodyparser of a program or function
235  return content;
236  }
237 
239  // line is the statement parser of a program or function
240  return line;
241  }
242 
244  ProgramInterfacePtr result;
245  if (program_list.empty())
246  return result;
247  program_text = "Bug: Program Text to be set by Parser.";
248  // set the program text in each program :
249  program_list.front()->setText( program_text );
250  result=program_list.front();
251  this->cleanup(false);
252  program_list.clear();
253  return result;
254  }
255 
257 
258  // store the variables in the program's taskcontext object.
259  valuechangeparser.store( context );
260  valuechangeparser.reset();
261 
262  // Fake a 'return' statement at the last line.
263  program_builder->returnFunction( new ConditionTrue, mpositer.get_position().line - ln_offset );
264  program_builder->proceedToNext( mpositer.get_position().line - ln_offset);
265  return program_builder->endFunction( mpositer.get_position().line - ln_offset );
266  }
267 
268 // ProgramInterfacePtr ProgramGraphParser::statementParserResult() {
269 //
270 // // Fake a 'return' statement at the last line.
271 // program_builder->returnFunction( new ConditionTrue, mpositer.get_position().line - ln_offset );
272 // program_builder->proceedToNext( mpositer.get_position().line - ln_offset);
273 // return program_builder->getFunction();
274 // }
275 
277  return parserused;
278  }
279 
280  void ProgramGraphParser::setStack(Service::shared_ptr st) {
281  context = st;
282  valuechangeparser.load(context);
283  }
284 
285  void ProgramGraphParser::clearParseState() {
286  exportf = false;
287  parserused = false;
288  rettype.clear();
289  }
290 
291  void ProgramGraphParser::startofprogram()
292  {
293  }
294 
295  void ProgramGraphParser::programdef( iter_t begin, iter_t end )
296  {
297  // Now that we got the name, set everything up:
298 
299  std::string def(begin, end);
300 
301  if ( rootc->provides()->hasService( def ) )
302  throw parse_exception_semantic_error("Service with name '" + def + "' already present in task '"+rootc->getName()+"'.");
303 
304  FunctionGraphPtr pi(program_builder->startFunction( def ));
305  // ptsk becomes the owner of pi.
306  ProgramServicePtr ptsk(new ProgramService( pi, rootc ));
307  pi->setProgramService(ptsk);
308  pi->setUnloadOnStop( false ); // since we assign a service, set this to false.
309  context = ptsk;
310  rootc->provides()->addService( ptsk );
311  }
312 
313  void ProgramGraphParser::programtext( iter_t begin, iter_t end )
314  {
315  // we set it in the parse() function. It is set to the whole script such that line numbers are correct.
316  //program_text = std::string(begin, end);
317  }
318 
319  void ProgramGraphParser::exportdef()
320  {
321  exportf = true;
322  }
323 
324  void ProgramGraphParser::globaldef()
325  {
326  globalf = true;
327  }
328 
329  void ProgramGraphParser::seenreturntype( iter_t begin, iter_t end )
330  {
331  rettype = std::string(begin, end);
332  }
333  void ProgramGraphParser::functiondef( iter_t begin, iter_t end )
334  {
335  // store the function in our map for later
336  // referencing.
337  std::string funcdef(begin, end);
338  // store the function in the TaskContext current.__functions
339 // TaskContext* __f = rootc->getPeer("__functions");
340 // if ( __f == 0 ) {
341 // // install the __functions if not yet present.
342 // __f = new TaskContext("__functions", rootc->engine() );
343 // rootc->connectPeers( __f );
344 // }
345 
346 // if ( __f->hasPeer( funcdef ) )
347  // only redefining a function twice in the same file is an error. If the function
348  // was already added to the scripting or component interface before, we replace it
349  // and warn about it in seenfunctionend():
350  if ( mfuncs.count( funcdef ) )
351  throw parse_exception_semantic_error("function " + funcdef + " redefined.");
352 
353  AttributeBase* retarg = 0;
354  if ( !rettype.empty() && rettype != "void") {
355  TypeInfo* type = TypeInfoRepository::Instance()->type( rettype );
356  if ( type == 0 )
357  throw_( iter_t(), "Return type '" + rettype + "' for function '"+ funcdef +"' is an unknown type." );
358  retarg = type->buildAttribute("result");
359  }
360 
361  mfuncs[funcdef] = program_builder->startFunction( funcdef );
362  program_builder->getFunction()->setResult( retarg );
363 
364  rettype.clear();
365 
366  // Connect the new function to the relevant contexts.
367  // 'fun' acts as a stack for storing variables.
368  fcontext.reset( new Service(funcdef) );
369  context = fcontext;
370  }
371 
372  void ProgramGraphParser::seenfunctionarg()
373  {
374  // the ValueChangeParser stores each variable in the
375  // current stack's repository, but we need to inform the
376  // FunctionGraph itself about its arguments.
377  program_builder->getFunction()->addArgument( valuechangeparser.lastDefinedValue()->clone() );
378  valuechangeparser.clear();
379  }
380 
381  void ProgramGraphParser::seenfunctionend()
382  {
383  // Fake a 'return' statement at the last line.
384  program_builder->returnFunction( new ConditionTrue, mpositer.get_position().line - ln_offset );
385  program_builder->proceedToNext( mpositer.get_position().line - ln_offset );
386  boost::shared_ptr<ProgramInterface> mfunc = program_builder->endFunction( mpositer.get_position().line - ln_offset );
387 
388  // export the function in the context's interface.
389  if (exportf) {
390  std::map<const DataSourceBase*, DataSourceBase*> dummy;
391  FunctionFactory* cfi = new FunctionFactory(ProgramInterfacePtr(mfunc->copy(dummy)), rootc->engine() ); // execute in the processor which has the command.
392  if (rootc->provides()->hasMember( mfunc->getName() ) )
393  log(Warning) << "Redefining function '"<< rootc->getName() << "." << mfunc->getName() << "': only new programs will use this new function." <<endlog();
394  rootc->provides()->add(mfunc->getName(), cfi );
395  Logger::log() << Logger::Info << "Exported Function '" << mfunc->getName() << "' added to task '"<< rootc->getName() << "'" <<Logger::endl;
396  }
397  // attach the function to the global service interface.
398  else if (globalf){
399  std::map<const DataSourceBase*, DataSourceBase*> dummy;
400  FunctionFactory* cfi = new FunctionFactory(ProgramInterfacePtr(mfunc->copy(dummy)), rootc->engine() ); // execute in the processor which has the command.
401  if (GlobalService::Instance()->provides()->hasMember( mfunc->getName() ) )
402  log(Warning) << "Redefining function '"<< GlobalService::Instance()->getName() << "."<< mfunc->getName() << "': only new programs will use this new function." <<endlog();
403  GlobalService::Instance()->provides()->add(mfunc->getName(), cfi );
404  Logger::log() << Logger::Debug << "Seen Function '" << mfunc->getName() << "' for Global Service." <<Logger::endl;
405  } else {
406  std::map<const DataSourceBase*, DataSourceBase*> dummy;
407  FunctionFactory* cfi = new FunctionFactory(ProgramInterfacePtr(mfunc->copy(dummy)), rootc->engine() ); // execute in the processor which has the command.
408  if (rootc->provides("scripting")->hasMember( mfunc->getName() ) )
409  log(Warning) << "Redefining function '"<< rootc->getName() << ".scripting."<< mfunc->getName() << "': only new programs will use this new function." <<endlog();
410  rootc->provides("scripting")->add(mfunc->getName(), cfi );
411  Logger::log() << Logger::Debug << "Seen Function '" << mfunc->getName() << "' for scripting service of '"<< rootc->getName() << "'" <<Logger::endl;
412  }
413 
414  fcontext.reset();
415  context.reset();
416 
417  // reset
418  exportf = false; globalf = false;
419 
420  valuechangeparser.reset();
421  }
422 
423  void ProgramGraphParser::seenvalidinput() {
424  parserused = true;
425  }
426 
427  void ProgramGraphParser::seencondition()
428  {
429  mcondition = conditionparser.getParseResult();
430  assert( mcondition );
431 
432  // leaves the condition in the parser, if we want to use
433  // getParseResultAsCommand();
434  // mcondition is only used with seen*label statements,
435  // when the command and condition are associated,
436  // not in the branching where the evaluation of the
437  // condition is the command.
438  }
439 
440  void ProgramGraphParser::seenreturnstatement()
441  {
442  // return statement can happen in program and in a function
443  program_builder->returnFunction( new ConditionTrue, mpositer.get_position().line - ln_offset );
444  program_builder->proceedToNext( mpositer.get_position().line - ln_offset );
445  }
446 
447  void ProgramGraphParser::seenreturnvalue()
448  {
449  AttributeBase* ar =program_builder->getFunction()->getResult();
450  if ( ar == 0) {
451  throw parse_exception_syntactic_error("Returning a value in a function returning (void).");
452  }
453  DataSourceBase::shared_ptr expr = expressionparser.getResult().get();
454  expressionparser.dropResult();
455  try {
456  ActionInterface* assigncomm = ar->getDataSource()->updateAction( expr.get() );
457  // assign the return value to the return argument.
458  program_builder->setCommand( assigncomm );
459  program_builder->proceedToNext( new ConditionTrue(), mpositer.get_position().line - ln_offset );
460  }
461  catch(...) {
462  // catch exception from updateAction.
463  throw parse_exception_syntactic_error("Could not convert '" + expr->getType() + "' to '"+ ar->getDataSource()->getType() +"' in return statement.");
464  }
465  }
466 
467  void ProgramGraphParser::seenbreakstatement()
468  {
469  if ( program_builder->inLoop() ) {
470  program_builder->breakLoop();
471  program_builder->proceedToNext( mpositer.get_position().line - ln_offset );
472  } else
473  throw parse_exception_syntactic_error("Illegal use of 'break'. Can only be used within for and while loops.");
474  }
475 
476  void ProgramGraphParser::seenfuncidentifier( iter_t begin, iter_t end )
477  {
478  // store the part after 'call'
479  std::string fname(begin, end);
480  if ( mfuncs.count(fname) == 0 )
481  throw parse_exception_semantic_error("calling function " + fname + " but it is not defined ( remove the 'call' keyword ).");
482  if ( fname == program_builder->getFunction()->getName() )
483  throw parse_exception_semantic_error("calling function " + fname + " recursively is not allowed.");
484 
485  mcallfunc = mfuncs[ fname ];
486 
487  // Parse the function's args in the programs context.
488  argsparser = new ArgumentsParser( expressionparser, rootc, rootc->provides(),
489  "this", fname );
490  arguments = argsparser->parser();
491 
492  }
493 
494  void ProgramGraphParser::seencallfuncargs()
495  {
496  callfnargs = argsparser->result();
497  }
498 
499  void ProgramGraphParser::seencallfuncstatement()
500  {
501  log(Warning) << " 'call' has been deprecated. Please remove this keyword." << endlog();
502  // This function is called if the 'call func' is outside
503  // a termination clause.
504 
505  // add it to the main program line of execution.
506  assert( mcallfunc );
507  try
508  {
509  program_builder->setFunction( mcallfunc, callfnargs );
510  // only delete parser, when the args are used.
511  delete argsparser;
512  argsparser = 0;
513  callfnargs.clear();
514  }
515  catch( const wrong_number_of_args_exception& e )
516  {
518  ( rootc->getName(), mcallfunc->getName(), e.wanted, e.received );
519  }
520  catch( const wrong_types_of_args_exception& e )
521  {
523  ( rootc->getName(), mcallfunc->getName(), e.whicharg, e.expected_, e.received_ );
524  }
525  catch( ... )
526  {
527  assert( false );
528  }
529 
530  // The exit node of the function is already connected
531  // to program->nextNode().
532  program_builder->proceedToNext(mpositer.get_position().line - ln_offset);
533  }
534 
535  void ProgramGraphParser::skip_eol() {
536  commonparser.skipeol = true;
537  }
538 
539  void ProgramGraphParser::noskip_eol() {
540  commonparser.skipeol = false;
541  }
542 
543  void ProgramGraphParser::startcatchpart() {
544  // we saved the try_cond in the previous try statement,
545  // now process like it said if ( try_cond ) then {...}
546  assert( try_cond );
547  program_builder->startIfStatement( try_cond, mpositer.get_position().line - ln_offset );
548  try_cond = 0;
549  }
550 
551  void ProgramGraphParser::seencatchpart() {
552  this->endifblock();
553  this->endifstatement(); // there is no 'else' part, so close the statement
554  }
555 
556  void ProgramGraphParser::seenifstatement() {
557  assert(mcondition);
558  // transform the evaluation in a command, and pass the result
559  // as a condition
560  std::pair<ActionInterface*, ConditionInterface*> comcon;
561  comcon = conditionparser.getParseResultAsCommand();
562  program_builder->setCommand( comcon.first );
563  program_builder->startIfStatement( comcon.second, mpositer.get_position().line - ln_offset );
564 
565  // we did not need this.
566  delete mcondition;
567  mcondition = 0;
568  }
569 
570  void ProgramGraphParser::endifblock() {
571  program_builder->endIfBlock(mpositer.get_position().line - ln_offset);
572  }
573 
574 
575  void ProgramGraphParser::endifstatement() {
576  program_builder->endElseBlock(mpositer.get_position().line - ln_offset);
577  }
578 
579  void ProgramGraphParser::seenwhilestatement() {
580  // analogous to seenifstatement
581  // the evaluation is a command.
582  assert(mcondition);
583  std::pair<ActionInterface*, ConditionInterface*> comcon;
584  comcon = conditionparser.getParseResultAsCommand();
585  program_builder->setCommand( comcon.first );
586  program_builder->startWhileStatement( comcon.second, mpositer.get_position().line - ln_offset );
587 
588  delete mcondition;
589  mcondition = 0;
590  }
591 
592  void ProgramGraphParser::endwhilestatement() {
593  program_builder->endWhileBlock(mpositer.get_position().line - ln_offset);
594  }
595 
596 
597  void ProgramGraphParser::seenforinit()
598  {
599  // the for loop is different from the while and if branch
600  // structures in that it places an init command before the loop.
601  ActionInterface* ac = 0;
602  std::vector<ActionInterface*> acv = valuechangeparser.assignCommands();
603  // and not forget to reset()..
604  valuechangeparser.clear();
605  if ( acv.size() == 1) {
606  ac = acv.front();
607  }
608  else if (acv.size() > 1) {
609  ac = new CommandComposite( acv );
610  }
611  for_init_command = ac;
612  }
613 
614  void ProgramGraphParser::seenforinit_expr()
615  {
616  DataSourceBase::shared_ptr expr = expressionparser.getResult();
617  expressionparser.dropResult();
618  for_init_command = new CommandDataSource( expr );
619  }
620 
621  void ProgramGraphParser::seenforincr()
622  {
623  DataSourceBase::shared_ptr expr = expressionparser.getResult();
624  expressionparser.dropResult();
625  for_incr_command.push( new CommandDataSource( expr ) );
626  }
627 
628  void ProgramGraphParser::seenemptyforincr()
629  {
630  for_incr_command.push( 0 );
631  }
632 
633  void ProgramGraphParser::seenforstatement() {
634  assert( mcondition );
635 
636  // first insert the initialisation command.
637  if ( for_init_command )
638  {
639  program_builder->setCommand( for_init_command );
640  program_builder->proceedToNext( new ConditionTrue, mpositer.get_position().line - ln_offset );
641  }
642  for_init_command = 0;
643 
644  // A for is nothing more than a while loop...
645  std::pair<ActionInterface*, ConditionInterface*> comcon;
646  comcon = conditionparser.getParseResultAsCommand();
647  program_builder->setCommand( comcon.first );
648  program_builder->startWhileStatement( comcon.second, mpositer.get_position().line - ln_offset );
649  delete mcondition;
650  mcondition = 0;
651  }
652 
653  void ProgramGraphParser::endforstatement() {
654  // the last statement is a _conditional_ increment of the 'counter'
655  ActionInterface* incr = for_incr_command.top();
656  for_incr_command.pop();
657  // is null or an action to increment
658  if ( incr )
659  {
660  program_builder->setCommand( incr );
661  // Since a valuechange does not add edges, we use this variant
662  // to create one.
663  program_builder->proceedToNext( new ConditionTrue, mpositer.get_position().line - ln_offset );
664  }
665  program_builder->endWhileBlock(mpositer.get_position().line - ln_offset);
666  }
667 
668  void ProgramGraphParser::seenprogramend()
669  {
670  // Fake a 'return' statement at the last line.
671  program_builder->returnFunction( new ConditionTrue, mpositer.get_position().line - ln_offset );
672  program_builder->proceedToNext( mpositer.get_position().line - ln_offset );
673  program_list.push_back(program_builder->endFunction( mpositer.get_position().line - ln_offset ) );
674 
675  // store the variables in the program's taskcontext object.
676  valuechangeparser.store( context );
677  valuechangeparser.reset();
678  }
679 
680  std::vector< ProgramInterfacePtr > ProgramGraphParser::parse( iter_t& begin, iter_t end )
681  {
682  // end is not used !
683  iter_t begin_copy = begin;
684  skip_parser_t skip_parser = comment_p( "#" ) | comment_p( "//" ) | comment_p( "/*", "*/" ) | (space_p - eol_p) | commonparser.skipper;
685  iter_pol_t iter_policy( skip_parser );
686  scanner_pol_t policies( iter_policy );
687  scanner_t scanner( begin, end, policies );
688  program_list.clear();
689 
690  // todo :Add a universal collect/collectIfDone/ret(sendh, args) operationfactoryparts.
691  //rootc->add("collect",&ProgramGraphParser::collectHandler, this)
692 
693  try {
694  if ( ! production.parse( scanner ) )
695  {
696  // This gets shown if we didn't even get the chance to throw an exception :
697  cleanup(true);
698  throw file_parse_exception(new parse_exception_syntactic_error( " no valid input found." ),
699  mpositer.get_position().file, mpositer.get_position().line,
700  mpositer.get_position().column );
701  }
702  program_text = std::string( begin_copy, begin ); // begin is by reference.
703  // set the program text in each program :
704  for (std::vector<FunctionGraphPtr>::iterator it= program_list.begin();it!=program_list.end();++it)
705  (*it)->setText( program_text );
706  this->cleanup(false);
707  std::vector<ProgramInterfacePtr> result;
708  for (std::vector<FunctionGraphPtr>::iterator it= program_list.begin();it!=program_list.end();++it)
709  result.push_back( *it );
710  program_list.clear();
711  return result;
712  }
713  catch( const parser_error<std::string, iter_t>& e )
714  {
715  cleanup(true);
716  program_list.clear();
717  throw file_parse_exception(
718  new parse_exception_syntactic_error( e.descriptor ),
719  mpositer.get_position().file, mpositer.get_position().line,
720  mpositer.get_position().column );
721 
722  }
723  // Catch our Orocos exceptions
724  catch( const parse_exception& e )
725  {
726  cleanup(true);
727  program_list.clear();
728  throw file_parse_exception(
729  e.copy(), mpositer.get_position().file,
730  mpositer.get_position().line, mpositer.get_position().column );
731  }
732  }
733 
734  std::vector< ProgramInterfacePtr > ProgramGraphParser::parseFunction( iter_t& begin, iter_t end )
735  {
736  // end is not used !
737  iter_t begin_copy = begin;
738  //skip_parser_t skip_parser = SKIP_PARSER;
739  //iter_pol_t iter_policy( skip_parser );
740  iter_pol_t iter_policy( ( comment_p( "#" ) | comment_p( "//" ) | comment_p( "/*", "*/" ) | (space_p - eol_p) | commonparser.skipper ) );
741  scanner_pol_t policies( iter_policy );
742  scanner_t scanner( begin, end, policies );
743 
744  std::vector< ProgramInterfacePtr > function_list;
745 
746  try {
747  if ( ! functions.parse( scanner ) )
748  {
749  // This gets shown if we didn't even get the chance to throw an exception :
750  cleanup(false);
751  throw file_parse_exception(new parse_exception_syntactic_error( " no valid input found." ),
752  mpositer.get_position().file, mpositer.get_position().line,
753  mpositer.get_position().column );
754  }
755  program_text = std::string( begin_copy, begin ); // begin is by reference.
756  // set the program text in each function :
757  for (funcmap::iterator it= mfuncs.begin();it!=mfuncs.end();++it) {
758  it->second->setText( program_text ); // set text.
759  function_list.push_back( it->second );
760  }
761 
762  this->cleanup(false);
763  return function_list;
764  }
765  // Catch Boost::Spirit exceptions
766  catch( const parser_error<std::string, iter_t>& e )
767  {
768  cleanup(false);
769  throw file_parse_exception(
770  new parse_exception_syntactic_error( e.descriptor ),
771  mpositer.get_position().file, mpositer.get_position().line,
772  mpositer.get_position().column );
773 
774  }
775  // Catch our Orocos exceptions
776  catch( const parse_exception& e )
777  {
778  cleanup(false);
779  throw file_parse_exception(
780  e.copy(), mpositer.get_position().file,
781  mpositer.get_position().line, mpositer.get_position().column );
782  }
783  }
784 
785  void ProgramGraphParser::cleanup(bool unload_service)
786  {
787  if (unload_service && rootc && context)
788  rootc->provides()->removeService( context->getName() );
789  // after an exception, we can be in any state, so cleanup
790  // all temp objects.
791  delete argsparser;
792  argsparser = 0;
793  delete implcond;
794  implcond = 0;
795  delete mcondition;
796  mcondition = 0;
797  delete try_cond;
798  try_cond = 0;
799  delete for_init_command;
800  for_init_command = 0;
801  while (!for_incr_command.empty() ) {
802  delete for_incr_command.top();
803  for_incr_command.pop();
804  }
805  // cleanup all functions :
806  fcontext.reset();
807  exportf = false; globalf = false;
808  rettype.clear();
809  if ( rootc == 0)
810  return;
811 // TaskContext* __f = rootc->getPeer("__functions");
812 // if ( __f != 0 ) {
813 // // first remove rootc from __f itself
814 // rootc->disconnectPeers( __f->getName() );
815 // delete __f;
816 // }
817  while ( ! mfuncs.empty() ) {
818  mfuncs.erase( mfuncs.begin() );
819  }
820  context.reset();
821 
822  valuechangeparser.reset();
823  conditionparser.reset();
824  peerparser.reset();
825  }
826 
827  void ProgramGraphParser::seentrystatement()
828  {
829  // a try expression/method call.
830  ActionInterface* command;
831  DataSourceBase::shared_ptr expr = expressionparser.getResult().get();
832  expressionparser.dropResult();
833  DataSource<bool>* bexpr = dynamic_cast<DataSource<bool>*>(expr.get());
834  if (bexpr == 0) {
835  // if not returning a bool, the try is useless.
836  command = new CommandDataSource( expr );
837  try_cond = new ConditionFalse(); // never execute catch part.
838  program_builder->setCommand(command);
839  } else {
840  command = new CommandDataSourceBool( bexpr );
841 
842  // try-wrap the asyn or dispatch command, store the result in try_cond.
843  TryCommand* trycommand = new TryCommand( command );
844  // returns true if failure :
845  TryCommandResult* tryresult = new TryCommandResult( trycommand->result(), true );
846  program_builder->setCommand( trycommand );
847  try_cond = tryresult; // save try_cond for catch part (ie true if failure)
848  }
849  if ( program_builder->buildEdges() == 0 )
850  program_builder->proceedToNext( new ConditionTrue(), mpositer.get_position().line - ln_offset );
851  else
852  program_builder->proceedToNext( mpositer.get_position().line - ln_offset ); // we get the data from commandparser
853  }
854 
855  void ProgramGraphParser::seenstatement()
856  {
857  // an expression/method call (former do).
858  DataSourceBase::shared_ptr expr = expressionparser.getResult();
859  ConditionInterface* cnd = expressionparser.getCmdResult();
860  expressionparser.dropResult();
861  DataSource<bool>* bexpr = dynamic_cast<DataSource<bool>*>(expr.get());
862  if (bexpr)
863  program_builder->setCommand( new CommandDataSourceBool( bexpr ) );
864  else
865  program_builder->setCommand( new CommandDataSource( expr ) );
866  if (cnd)
867  program_builder->addConditionEdge( cnd, program_builder->nextNode() );
868  if ( program_builder->buildEdges() == 0 )
869  program_builder->proceedToNext( new ConditionTrue(), mpositer.get_position().line - ln_offset );
870  else
871  program_builder->proceedToNext( mpositer.get_position().line - ln_offset );
872  }
873 
874  void ProgramGraphParser::seenyield()
875  {
876  // a yield branch
877  program_builder->setCommand( new CommandNOP );
878  program_builder->proceedToNext( new ConditionOnce(false), mpositer.get_position().line - ln_offset );
879  }
880 
881  void ProgramGraphParser::seenvaluechange()
882  {
883  // some value changes generate a command, we need to add it to
884  // the program.
885  ActionInterface* ac = 0;
886  ConditionInterface* cond = 0;
887  std::vector<ActionInterface*> acv = valuechangeparser.assignCommands();
888  std::vector<ConditionInterface*> conds = valuechangeparser.assignConditions();
889  // and not forget to reset()..
890  valuechangeparser.clear();
891  if ( acv.size() == 1) {
892  ac = acv.front();
893  }
894  else if (acv.size() > 1) {
895  ac = new CommandComposite(acv);
896  }
897  if ( conds.size() ==1 ) {
898  cond = conds.front();
899  }
900  else if ( conds.size() > 1) {
901  cond = conds.front();
902  unsigned int i = 1;
903  while ( i != conds.size() ) {
904  cond = new ConditionBinaryCompositeAND(cond, conds[i] );
905  ++i;
906  }
907  }
908  if (ac) {
909  program_builder->setCommand( ac );
910  // check if one of the vars caused a condition:
911  if (cond == 0)
912  cond = new ConditionTrue;
913  program_builder->proceedToNext( cond, mpositer.get_position().line - ln_offset );
914  }
915  }
916 
917  void ProgramGraphParser::seencallfunclabel( iter_t begin, iter_t end )
918  {
919  // Used for "call xyz"
920  // lookup mcallfunc
921  seenfuncidentifier( begin, end );
922 
923  assert( mcondition );
924  assert( mcallfunc );
925  program_builder->appendFunction( mcondition, mcallfunc, callfnargs);
926  mcondition = 0;
927 
928  }
929 
930  void ProgramGraphParser::seencontinue( )
931  {
932  // @todo complete the impl for for/while loops.
933  // Used for "continue"
934  assert ( mcondition );
935 
936  // connect to next node under given condition.
937  program_builder->addConditionEdge( mcondition, program_builder->nextNode() );
938 
939  mcondition = 0;
940  }
941 }
std::pair< base::ActionInterface *, ConditionInterface * > getParseResultAsCommand()
Retrieve the result as a command, condition pair.
#define keyword_p(word)
Returns a rule which parses a keyword followed by a non-identifier character, newline or semicolon...
std::vector< base::DataSourceBase::shared_ptr > result()
Get the parsed internal::DataSource&#39;s.
rule_t & parser()
Returns the full parser, as it is used most.
This class builds a program consisting of data contained in a program graph tree, based on the Boost ...
A Command which evaluates a internal::DataSource<bool> and returns the result of get().
rule_t eos
End Of Statement Parser.
boost::shared_ptr< FunctionGraph > FunctionGraphPtr
ConditionInterface * getParseResult()
Call this to get the parsed condition.
base::AttributeBase * buildAttribute(std::string name, base::DataSourceBase::shared_ptr source=0) const
Build an Attribute of this type.
Definition: TypeInfo.hpp:222
Service::shared_ptr provides()
Returns this Service.
void load(Service::shared_ptr source)
Loads all defined names from a service.
virtual AttributeBase * clone() const =0
Returns a clone of this AttributeBase.
rule_t & bareDefinitionParser()
The parser that parses a bare variable definition.
void reset()
After reset, peer() == current context and object() == "this".
Definition: PeerParser.cpp:151
This interface represents the concept of a condition which can be evaluated and return true or false...
bool skipeol
Saves eol skipping state.
A conditional that evaluates true.
void reset()
Completely clear all data and erase all parsed definitions from the taskcontext given in the construc...
The empty command.
Definition: CommandNOP.hpp:52
Compose an &#39;AND&#39; function of two Conditions.
parse_exception class that is used for various semantic errors for which it was not worth defining a ...
virtual DataSourceBase::shared_ptr getDataSource() const =0
Return a internal::DataSource which contains the same contents.
boost::shared_ptr< ProgramInterface > ProgramInterfacePtr
virtual parse_exception * copy() const =0
void initBodyParser(const std::string &name, Service::shared_ptr stck, int offset)
Initialize the bodyParser to parse and store each statement it sees.
This is a parser that you construct to parse a set of arguments.
This is an exception class that keeps a parse_exception pointer along with the location in the file a...
ConditionInterface * getCmdResult()
This class contains some very common parser definitions.
std::vector< scripting::ConditionInterface * > assignConditions()
parse_exception class that is used for various syntactic errors for which it was not worth defining a...
An execution engine serialises (executes one after the other) the execution of all commands...
A command which tries another command and stores the result in a internal::DataSource<bool>.
Definition: TryCommand.hpp:59
A conditional that evaluates the first time true and afterwards always false (or vice versa)...
boost::shared_ptr< Service > shared_ptr
Definition: Service.hpp:101
ProgramInterfacePtr programParserResult()
Returns the last program parsed by programParser()
bool parserUsed() const
Returns true if the parser was already used (even partially).
This class allows storage and retrieval of operations, ports, attributes and properties provided by a...
Definition: Service.hpp:93
rule_t & programParser()
Returns a program foo {} parser.
void store(Service::shared_ptr other)
Store allDefinedNames() in a service.
scanner< iter_t, scanner_pol_t > scanner_t
rule_t & functionParser()
Parses a function foo {} definition.
ProgramGraphParser(iter_t &positer, TaskContext *context, ExecutionEngine *caller, CommonParser &cp)
An attribute is a minimalistic, named placeholder for data.
skip_parser_iteration_policy< skip_parser_t > iter_pol_t
This class represents a program as an Service in the Orocos TaskContext system.
functor_parser< eol_skip_functor > skipper
End Of Statement Parser.
static std::ostream & endl(std::ostream &__os)
Definition: Logger.cpp:383
Based on the software pattern &#39;command&#39;, this interface allows execution of action objects...
base::DataSourceBase::shared_ptr getResult()
This is the uppermost exception class in the parser system.
rule< scanner_t > rule_t
A class for representing a user type, and which can build instances of that type. ...
Definition: TypeInfo.hpp:67
internal::AssignableDataSource< bool >::shared_ptr result()
Definition: TryCommand.cpp:96
rule_t & statementParser()
Parses a single-line statement.
rule_t & bodyParser()
Parses a multi-line program, which you can retrieve with bodyParserResult().
A Command which evaluates a base::DataSourceBase and always returns true.
boost_spirit::alternative< boost_spirit::alternative< boost_spirit::alternative< boost_spirit::alternative< boost_spirit::confix_parser< boost_spirit::impl::string_as_parser::type, boost_spirit::kleene_star< boost_spirit::anychar_parser >, boost_spirit::alternative< boost_spirit::eol_parser, boost_spirit::end_parser >, boost_spirit::unary_parser_category, boost_spirit::non_nested, boost_spirit::is_lexeme >, boost_spirit::confix_parser< boost_spirit::impl::string_as_parser::type, boost_spirit::kleene_star< boost_spirit::anychar_parser >, boost_spirit::alternative< boost_spirit::eol_parser, boost_spirit::end_parser >, boost_spirit::unary_parser_category, boost_spirit::non_nested, boost_spirit::is_lexeme > >, boost_spirit::confix_parser< boost_spirit::impl::string_as_parser::type, boost_spirit::kleene_star< boost_spirit::anychar_parser >, boost_spirit::impl::string_as_parser::type, boost_spirit::unary_parser_category, boost_spirit::non_nested, boost_spirit::is_lexeme > >, boost_spirit::difference< boost_spirit::space_parser, boost_spirit::eol_parser > >, boost_spirit::functor_parser< eol_skip_functor > > skip_parser_t
std::vector< ProgramInterfacePtr > parse(iter_t &begin, iter_t end)
Tries to parse programs, returns the generated programs on success.
A Factory which delivers operations which execute a FunctionGraph in an engine.
void clear()
Clear assignCommands(), definedValues() and definedNames().
std::vector< ProgramInterfacePtr > parseFunction(iter_t &begin, iter_t end)
static Logger & log()
As Instance(), but more userfriendly.
Definition: Logger.cpp:117
base::AttributeBase * lastDefinedValue()
The TaskContext is the C++ representation of an Orocos component.
Definition: TaskContext.hpp:93
Exception thrown when a factory is requested to create an object, but a wrong argument type was given...
std::vector< base::ActionInterface * > assignCommands()
Returns the (accept/reject) status of another command.
Definition: TryCommand.hpp:96
Exception thrown when a factory is requested to create an object but the wrong number of arguments wa...
boost::intrusive_ptr< DataSourceBase > shared_ptr
Use this type to store a pointer to a DataSourceBase.
rule_t notassertingidentifier
identifier with <template> marks in it
Contains TaskContext, Activity, OperationCaller, Operation, Property, InputPort, OutputPort, Attribute.
Definition: Activity.cpp:52
our_pos_iter_t iter_t
boost::shared_ptr< ProgramService > ProgramServicePtr
scanner_policies< iter_pol_t > scanner_pol_t
Based on the software pattern &#39;composite&#39;, this class RTT_SCRIPTING_API allows composing command obje...
A conditional that evaluates false.
const ExecutionEngine * engine() const
Get a const pointer to the ExecutionEngine of this Task.
Definition: TaskCore.hpp:306
static RTT_API Service::shared_ptr Instance()
virtual const std::string & getName() const
Returns the name of this TaskContext.