Orocos Real-Time Toolkit  2.8.3
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(0), 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 = new TaskContext(funcdef, rootc->engine() );
369  context = fcontext->provides();
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 
415  delete fcontext;
416  fcontext = 0;
417  context.reset();
418 
419  // reset
420  exportf = false; globalf = false;
421 
422  valuechangeparser.reset();
423  }
424 
425  void ProgramGraphParser::seenvalidinput() {
426  parserused = true;
427  }
428 
429  void ProgramGraphParser::seencondition()
430  {
431  mcondition = conditionparser.getParseResult();
432  assert( mcondition );
433 
434  // leaves the condition in the parser, if we want to use
435  // getParseResultAsCommand();
436  // mcondition is only used with seen*label statements,
437  // when the command and condition are associated,
438  // not in the branching where the evaluation of the
439  // condition is the command.
440  }
441 
442  void ProgramGraphParser::seenreturnstatement()
443  {
444  // return statement can happen in program and in a function
445  program_builder->returnFunction( new ConditionTrue, mpositer.get_position().line - ln_offset );
446  program_builder->proceedToNext( mpositer.get_position().line - ln_offset );
447  }
448 
449  void ProgramGraphParser::seenreturnvalue()
450  {
451  AttributeBase* ar =program_builder->getFunction()->getResult();
452  if ( ar == 0) {
453  throw parse_exception_syntactic_error("Returning a value in a function returning (void).");
454  }
455  DataSourceBase::shared_ptr expr = expressionparser.getResult().get();
456  expressionparser.dropResult();
457  try {
458  ActionInterface* assigncomm = ar->getDataSource()->updateAction( expr.get() );
459  // assign the return value to the return argument.
460  program_builder->setCommand( assigncomm );
461  program_builder->proceedToNext( new ConditionTrue(), mpositer.get_position().line - ln_offset );
462  }
463  catch(...) {
464  // catch exception from updateAction.
465  throw parse_exception_syntactic_error("Could not convert '" + expr->getType() + "' to '"+ ar->getDataSource()->getType() +"' in return statement.");
466  }
467  }
468 
469  void ProgramGraphParser::seenbreakstatement()
470  {
471  if ( program_builder->inLoop() ) {
472  program_builder->breakLoop();
473  program_builder->proceedToNext( mpositer.get_position().line - ln_offset );
474  } else
475  throw parse_exception_syntactic_error("Illegal use of 'break'. Can only be used within for and while loops.");
476  }
477 
478  void ProgramGraphParser::seenfuncidentifier( iter_t begin, iter_t end )
479  {
480  // store the part after 'call'
481  std::string fname(begin, end);
482  if ( mfuncs.count(fname) == 0 )
483  throw parse_exception_semantic_error("calling function " + fname + " but it is not defined ( remove the 'call' keyword ).");
484  if ( fname == program_builder->getFunction()->getName() )
485  throw parse_exception_semantic_error("calling function " + fname + " recursively is not allowed.");
486 
487  mcallfunc = mfuncs[ fname ];
488 
489  // Parse the function's args in the programs context.
490  argsparser = new ArgumentsParser( expressionparser, rootc, rootc->provides(),
491  "this", fname );
492  arguments = argsparser->parser();
493 
494  }
495 
496  void ProgramGraphParser::seencallfuncargs()
497  {
498  callfnargs = argsparser->result();
499  }
500 
501  void ProgramGraphParser::seencallfuncstatement()
502  {
503  log(Warning) << " 'call' has been deprecated. Please remove this keyword." << endlog();
504  // This function is called if the 'call func' is outside
505  // a termination clause.
506 
507  // add it to the main program line of execution.
508  assert( mcallfunc );
509  try
510  {
511  program_builder->setFunction( mcallfunc, callfnargs );
512  // only delete parser, when the args are used.
513  delete argsparser;
514  argsparser = 0;
515  callfnargs.clear();
516  }
517  catch( const wrong_number_of_args_exception& e )
518  {
520  ( rootc->getName(), mcallfunc->getName(), e.wanted, e.received );
521  }
522  catch( const wrong_types_of_args_exception& e )
523  {
525  ( rootc->getName(), mcallfunc->getName(), e.whicharg, e.expected_, e.received_ );
526  }
527  catch( ... )
528  {
529  assert( false );
530  }
531 
532  // The exit node of the function is already connected
533  // to program->nextNode().
534  program_builder->proceedToNext(mpositer.get_position().line - ln_offset);
535  }
536 
537  void ProgramGraphParser::skip_eol() {
538  commonparser.skipeol = true;
539  }
540 
541  void ProgramGraphParser::noskip_eol() {
542  commonparser.skipeol = false;
543  }
544 
545  void ProgramGraphParser::startcatchpart() {
546  // we saved the try_cond in the previous try statement,
547  // now process like it said if ( try_cond ) then {...}
548  assert( try_cond );
549  program_builder->startIfStatement( try_cond, mpositer.get_position().line - ln_offset );
550  try_cond = 0;
551  }
552 
553  void ProgramGraphParser::seencatchpart() {
554  this->endifblock();
555  this->endifstatement(); // there is no 'else' part, so close the statement
556  }
557 
558  void ProgramGraphParser::seenifstatement() {
559  assert(mcondition);
560  // transform the evaluation in a command, and pass the result
561  // as a condition
562  std::pair<ActionInterface*, ConditionInterface*> comcon;
563  comcon = conditionparser.getParseResultAsCommand();
564  program_builder->setCommand( comcon.first );
565  program_builder->startIfStatement( comcon.second, mpositer.get_position().line - ln_offset );
566 
567  // we did not need this.
568  delete mcondition;
569  mcondition = 0;
570  }
571 
572  void ProgramGraphParser::endifblock() {
573  program_builder->endIfBlock(mpositer.get_position().line - ln_offset);
574  }
575 
576 
577  void ProgramGraphParser::endifstatement() {
578  program_builder->endElseBlock(mpositer.get_position().line - ln_offset);
579  }
580 
581  void ProgramGraphParser::seenwhilestatement() {
582  // analogous to seenifstatement
583  // the evaluation is a command.
584  assert(mcondition);
585  std::pair<ActionInterface*, ConditionInterface*> comcon;
586  comcon = conditionparser.getParseResultAsCommand();
587  program_builder->setCommand( comcon.first );
588  program_builder->startWhileStatement( comcon.second, mpositer.get_position().line - ln_offset );
589 
590  delete mcondition;
591  mcondition = 0;
592  }
593 
594  void ProgramGraphParser::endwhilestatement() {
595  program_builder->endWhileBlock(mpositer.get_position().line - ln_offset);
596  }
597 
598 
599  void ProgramGraphParser::seenforinit()
600  {
601  // the for loop is different from the while and if branch
602  // structures in that it places an init command before the loop.
603  ActionInterface* ac = 0;
604  std::vector<ActionInterface*> acv = valuechangeparser.assignCommands();
605  // and not forget to reset()..
606  valuechangeparser.clear();
607  if ( acv.size() == 1) {
608  ac = acv.front();
609  }
610  else if (acv.size() > 1) {
611  ac = new CommandComposite( acv );
612  }
613  for_init_command = ac;
614  }
615 
616  void ProgramGraphParser::seenforinit_expr()
617  {
618  DataSourceBase::shared_ptr expr = expressionparser.getResult();
619  expressionparser.dropResult();
620  for_init_command = new CommandDataSource( expr );
621  }
622 
623  void ProgramGraphParser::seenforincr()
624  {
625  DataSourceBase::shared_ptr expr = expressionparser.getResult();
626  expressionparser.dropResult();
627  for_incr_command.push( new CommandDataSource( expr ) );
628  }
629 
630  void ProgramGraphParser::seenemptyforincr()
631  {
632  for_incr_command.push( 0 );
633  }
634 
635  void ProgramGraphParser::seenforstatement() {
636  assert( mcondition );
637 
638  // first insert the initialisation command.
639  if ( for_init_command )
640  {
641  program_builder->setCommand( for_init_command );
642  program_builder->proceedToNext( new ConditionTrue, mpositer.get_position().line - ln_offset );
643  }
644  for_init_command = 0;
645 
646  // A for is nothing more than a while loop...
647  std::pair<ActionInterface*, ConditionInterface*> comcon;
648  comcon = conditionparser.getParseResultAsCommand();
649  program_builder->setCommand( comcon.first );
650  program_builder->startWhileStatement( comcon.second, mpositer.get_position().line - ln_offset );
651  delete mcondition;
652  mcondition = 0;
653  }
654 
655  void ProgramGraphParser::endforstatement() {
656  // the last statement is a _conditional_ increment of the 'counter'
657  ActionInterface* incr = for_incr_command.top();
658  for_incr_command.pop();
659  // is null or an action to increment
660  if ( incr )
661  {
662  program_builder->setCommand( incr );
663  // Since a valuechange does not add edges, we use this variant
664  // to create one.
665  program_builder->proceedToNext( new ConditionTrue, mpositer.get_position().line - ln_offset );
666  }
667  program_builder->endWhileBlock(mpositer.get_position().line - ln_offset);
668  }
669 
670  void ProgramGraphParser::seenprogramend()
671  {
672  // Fake a 'return' statement at the last line.
673  program_builder->returnFunction( new ConditionTrue, mpositer.get_position().line - ln_offset );
674  program_builder->proceedToNext( mpositer.get_position().line - ln_offset );
675  program_list.push_back(program_builder->endFunction( mpositer.get_position().line - ln_offset ) );
676 
677  // store the variables in the program's taskcontext object.
678  valuechangeparser.store( context );
679  valuechangeparser.reset();
680  }
681 
682  std::vector< ProgramInterfacePtr > ProgramGraphParser::parse( iter_t& begin, iter_t end )
683  {
684  // end is not used !
685  iter_t begin_copy = begin;
686  skip_parser_t skip_parser = comment_p( "#" ) | comment_p( "//" ) | comment_p( "/*", "*/" ) | (space_p - eol_p) | commonparser.skipper;
687  iter_pol_t iter_policy( skip_parser );
688  scanner_pol_t policies( iter_policy );
689  scanner_t scanner( begin, end, policies );
690  program_list.clear();
691 
692  // todo :Add a universal collect/collectIfDone/ret(sendh, args) operationfactoryparts.
693  //rootc->add("collect",&ProgramGraphParser::collectHandler, this)
694 
695  try {
696  if ( ! production.parse( scanner ) )
697  {
698  // This gets shown if we didn't even get the chance to throw an exception :
699  cleanup(true);
700  throw file_parse_exception(new parse_exception_syntactic_error( " no valid input found." ),
701  mpositer.get_position().file, mpositer.get_position().line,
702  mpositer.get_position().column );
703  }
704  program_text = std::string( begin_copy, begin ); // begin is by reference.
705  // set the program text in each program :
706  for (std::vector<FunctionGraphPtr>::iterator it= program_list.begin();it!=program_list.end();++it)
707  (*it)->setText( program_text );
708  this->cleanup(false);
709  std::vector<ProgramInterfacePtr> result;
710  for (std::vector<FunctionGraphPtr>::iterator it= program_list.begin();it!=program_list.end();++it)
711  result.push_back( *it );
712  program_list.clear();
713  return result;
714  }
715  catch( const parser_error<std::string, iter_t>& e )
716  {
717  cleanup(true);
718  program_list.clear();
719  throw file_parse_exception(
720  new parse_exception_syntactic_error( e.descriptor ),
721  mpositer.get_position().file, mpositer.get_position().line,
722  mpositer.get_position().column );
723 
724  }
725  // Catch our Orocos exceptions
726  catch( const parse_exception& e )
727  {
728  cleanup(true);
729  program_list.clear();
730  throw file_parse_exception(
731  e.copy(), mpositer.get_position().file,
732  mpositer.get_position().line, mpositer.get_position().column );
733  }
734  }
735 
736  std::vector< ProgramInterfacePtr > ProgramGraphParser::parseFunction( iter_t& begin, iter_t end )
737  {
738  // end is not used !
739  iter_t begin_copy = begin;
740  //skip_parser_t skip_parser = SKIP_PARSER;
741  //iter_pol_t iter_policy( skip_parser );
742  iter_pol_t iter_policy( ( comment_p( "#" ) | comment_p( "//" ) | comment_p( "/*", "*/" ) | (space_p - eol_p) | commonparser.skipper ) );
743  scanner_pol_t policies( iter_policy );
744  scanner_t scanner( begin, end, policies );
745 
746  std::vector< ProgramInterfacePtr > function_list;
747 
748  try {
749  if ( ! functions.parse( scanner ) )
750  {
751  // This gets shown if we didn't even get the chance to throw an exception :
752  cleanup(false);
753  throw file_parse_exception(new parse_exception_syntactic_error( " no valid input found." ),
754  mpositer.get_position().file, mpositer.get_position().line,
755  mpositer.get_position().column );
756  }
757  program_text = std::string( begin_copy, begin ); // begin is by reference.
758  // set the program text in each function :
759  for (funcmap::iterator it= mfuncs.begin();it!=mfuncs.end();++it) {
760  it->second->setText( program_text ); // set text.
761  function_list.push_back( it->second );
762  }
763 
764  this->cleanup(false);
765  return function_list;
766  }
767  // Catch Boost::Spirit exceptions
768  catch( const parser_error<std::string, iter_t>& e )
769  {
770  cleanup(false);
771  throw file_parse_exception(
772  new parse_exception_syntactic_error( e.descriptor ),
773  mpositer.get_position().file, mpositer.get_position().line,
774  mpositer.get_position().column );
775 
776  }
777  // Catch our Orocos exceptions
778  catch( const parse_exception& e )
779  {
780  cleanup(false);
781  throw file_parse_exception(
782  e.copy(), mpositer.get_position().file,
783  mpositer.get_position().line, mpositer.get_position().column );
784  }
785  }
786 
787  void ProgramGraphParser::cleanup(bool unload_service)
788  {
789  if (unload_service && rootc && context)
790  rootc->provides()->removeService( context->getName() );
791  // after an exception, we can be in any state, so cleanup
792  // all temp objects.
793  delete argsparser;
794  argsparser = 0;
795  delete implcond;
796  implcond = 0;
797  delete mcondition;
798  mcondition = 0;
799  delete try_cond;
800  try_cond = 0;
801  delete for_init_command;
802  for_init_command = 0;
803  while (!for_incr_command.empty() ) {
804  delete for_incr_command.top();
805  for_incr_command.pop();
806  }
807  // cleanup all functions :
808  delete fcontext;
809  fcontext = 0;
810  exportf = false; globalf = false;
811  rettype.clear();
812  if ( rootc == 0)
813  return;
814 // TaskContext* __f = rootc->getPeer("__functions");
815 // if ( __f != 0 ) {
816 // // first remove rootc from __f itself
817 // rootc->disconnectPeers( __f->getName() );
818 // delete __f;
819 // }
820  while ( ! mfuncs.empty() ) {
821  mfuncs.erase( mfuncs.begin() );
822  }
823  context.reset();
824 
825  valuechangeparser.reset();
826  conditionparser.reset();
827  peerparser.reset();
828  }
829 
830  void ProgramGraphParser::seentrystatement()
831  {
832  // a try expression/method call.
833  ActionInterface* command;
834  DataSourceBase::shared_ptr expr = expressionparser.getResult().get();
835  expressionparser.dropResult();
836  DataSource<bool>* bexpr = dynamic_cast<DataSource<bool>*>(expr.get());
837  if (bexpr == 0) {
838  // if not returning a bool, the try is useless.
839  command = new CommandDataSource( expr );
840  try_cond = new ConditionFalse(); // never execute catch part.
841  program_builder->setCommand(command);
842  } else {
843  command = new CommandDataSourceBool( bexpr );
844 
845  // try-wrap the asyn or dispatch command, store the result in try_cond.
846  TryCommand* trycommand = new TryCommand( command );
847  // returns true if failure :
848  TryCommandResult* tryresult = new TryCommandResult( trycommand->result(), true );
849  program_builder->setCommand( trycommand );
850  try_cond = tryresult; // save try_cond for catch part (ie true if failure)
851  }
852  if ( program_builder->buildEdges() == 0 )
853  program_builder->proceedToNext( new ConditionTrue(), mpositer.get_position().line - ln_offset );
854  else
855  program_builder->proceedToNext( mpositer.get_position().line - ln_offset ); // we get the data from commandparser
856  }
857 
858  void ProgramGraphParser::seenstatement()
859  {
860  // an expression/method call (former do).
861  DataSourceBase::shared_ptr expr = expressionparser.getResult().get();
862  expressionparser.dropResult();
863  DataSource<bool>* bexpr = dynamic_cast<DataSource<bool>*>(expr.get());
864  if (bexpr)
865  program_builder->setCommand( new CommandDataSourceBool( bexpr ) );
866  else
867  program_builder->setCommand( new CommandDataSource( expr ) );
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  std::vector<ActionInterface*> acv = valuechangeparser.assignCommands();
887  // and not forget to reset()..
888  valuechangeparser.clear();
889  if ( acv.size() == 1) {
890  ac = acv.front();
891  }
892  else if (acv.size() > 1) {
893  ac = new CommandComposite(acv);
894  }
895  if (ac) {
896  program_builder->setCommand( ac );
897  // Since a valuechange does not add edges, we use this variant
898  // to create one.
899  program_builder->proceedToNext( new ConditionTrue, mpositer.get_position().line - ln_offset );
900  }
901  }
902 
903  void ProgramGraphParser::seencallfunclabel( iter_t begin, iter_t end )
904  {
905  // Used for "call xyz"
906  // lookup mcallfunc
907  seenfuncidentifier( begin, end );
908 
909  assert( mcondition );
910  assert( mcallfunc );
911  program_builder->appendFunction( mcondition, mcallfunc, callfnargs);
912  mcondition = 0;
913 
914  }
915 
916  void ProgramGraphParser::seencontinue( )
917  {
918  // @todo complete the impl for for/while loops.
919  // Used for "continue"
920  assert ( mcondition );
921 
922  // connect to next node under given condition.
923  program_builder->addConditionEdge( mcondition, program_builder->nextNode() );
924 
925  mcondition = 0;
926  }
927 }
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:221
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
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
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...
This class contains some very common parser definitions.
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).
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:66
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:51
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:327
static RTT_API Service::shared_ptr Instance()
virtual const std::string & getName() const
Returns the name of this TaskContext.