StateGraphParser.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  tag: Peter Soetens Mon May 10 19:10:37 CEST 2004 StateGraphParser.cxx
3 
4  StateGraphParser.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 General Public *
13  * License as published by the Free Software Foundation; *
14  * version 2 of the License. *
15  * *
16  * As a special exception, you may use this file as part of a free *
17  * software library without restriction. Specifically, if other files *
18  * instantiate templates or use macros or inline functions from this *
19  * file, or you compile this file and link it with other files to *
20  * produce an executable, this file does not by itself cause the *
21  * resulting executable to be covered by the GNU General Public *
22  * License. This exception does not however invalidate any other *
23  * reasons why the executable file might be covered by the GNU General *
24  * Public License. *
25  * *
26  * This library is distributed in the hope that it will be useful, *
27  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
28  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
29  * General Public License for more details. *
30  * *
31  * You should have received a copy of the GNU General Public *
32  * License along with this library; if not, write to the Free Software *
33  * Foundation, Inc., 59 Temple Place, *
34  * Suite 330, Boston, MA 02111-1307 USA *
35  * *
36  ***************************************************************************/
37 
38 #include "parser-debug.hpp"
39 #include "parse_exception.hpp"
40 #include "StateGraphParser.hpp"
41 #include "CommonParser.hpp"
42 #include "ConditionParser.hpp"
43 #include "ConditionCompare.hpp"
44 #include "ConditionComposite.hpp"
45 #include "ConditionCache.hpp"
47 #include "ValueChangeParser.hpp"
48 #include "ProgramGraphParser.hpp"
49 #include "PeerParser.hpp"
50 #include "ArgumentsParser.hpp"
51 #include "StateMachineBuilder.hpp"
52 #include "../TaskContext.hpp"
53 #include "StateMachineService.hpp"
54 
55 #include "CommandComposite.hpp"
56 #include "../internal/Exceptions.hpp"
57 #include "../base/AttributeBase.hpp"
58 #include "ConditionTrue.hpp"
59 #include "ConditionInvert.hpp"
60 #include "StateDescription.hpp"
61 #include "ParsedStateMachine.hpp"
62 
63 #include <iostream>
64 #include <functional>
65 #include <algorithm>
66 #include <boost/bind.hpp>
67 #include <boost/lambda/lambda.hpp>
68 #include <boost/call_traits.hpp>
69 #include <iostream>
70 #include <memory>
71 #include "../internal/mystd.hpp"
72 
73 namespace RTT
74 {
75  using namespace boost;
76  using namespace detail;
77  using boost::bind;
78 
79  using namespace std;
80 
81  namespace {
83  {
84  state_expected,
85  handle_expected,
86  transition_expected,
87  };
88 
89  boost::spirit::classic::assertion<GraphSyntaxErrors> expect_state(state_expected);
90  boost::spirit::classic::assertion<GraphSyntaxErrors> expect_handle(handle_expected);
91  boost::spirit::classic::assertion<GraphSyntaxErrors> expect_transition(transition_expected);
92  boost::spirit::classic::assertion<std::string> expect_end("Ending '}' expected ( or could not find out what this line means ).");
93  boost::spirit::classic::assertion<std::string> expect_end_of_state("Exptected ending '}' at end of state ( or could not find out what this line means ).");
94  boost::spirit::classic::assertion<std::string> expect_if("Wrongly formatted \"if ... then select\" clause.");
95  boost::spirit::classic::assertion<std::string> expect_select("'select' statement required after emty transition program.");
96  boost::spirit::classic::assertion<std::string> expect_select_ident("'select' requires a valid state name.");
97  boost::spirit::classic::assertion<std::string> expect_comma("Expected a comma separator.");
98  boost::spirit::classic::assertion<std::string> expect_ident("Expected a valid identifier.");
99  boost::spirit::classic::assertion<std::string> expect_event_or_if("Expected an event name or an if clause in transition statement.");
100  boost::spirit::classic::assertion<std::string> expect_open("Open brace expected.");
101  boost::spirit::classic::assertion<std::string> expect_eof("Invalid input in file.");
102  boost::spirit::classic::assertion<std::string> expect_eol("Newline expected at end of statement.");
103  boost::spirit::classic::assertion<std::string> expect_semicolon("Semi colon expected after statement.");
104  boost::spirit::classic::assertion<std::string> expect_open_parenth( "Open parenthesis expected." );
105  boost::spirit::classic::assertion<std::string> expect_close_parenth( "Open parenthesis expected." );
106  boost::spirit::classic::assertion<std::string> expect_eventselect("'select' statement required after event or transition program.");
107  boost::spirit::classic::assertion<std::string> expect_eventargs("Could not parse arguments after event.");
108  }
109 
110 
112  TaskContext* tc, ExecutionEngine* tcaller, CommonParser* cp )
113  : context( tc ),
114  caller( tcaller ),
115  mpositer( positer ),
116  ln_offset(0),
117  curtemplate(),
118  curinstantiatedmachine(),
119  curmachinebuilder( 0 ),
120  curinitialstateflag( false ),
121  curfinalstateflag( false ),
122  curstate( 0 ),
123  curnonprecstate( 0 ),
124  progParser( 0 ),
125  elsestate(0),
126  curcondition( 0 ),
127  isroot(false),
128  selectln(0),
129  evname(""),
130  commonparser( cp ),
131  conditionparser( new ConditionParser( context, caller, *commonparser ) ),
132  valuechangeparser( new ValueChangeParser(context, *commonparser, context->provides(), caller) ),
133  expressionparser( new ExpressionParser(context, caller, *commonparser) ),
134  argsparser(0),
135  peerparser( new PeerParser(context, *commonparser, true) ) // full-path peer parser for events.
136  {
137  BOOST_SPIRIT_DEBUG_RULE( production );
138  BOOST_SPIRIT_DEBUG_RULE( body );
139  BOOST_SPIRIT_DEBUG_RULE( rootmachineinstantiation );
140  BOOST_SPIRIT_DEBUG_RULE( statemachine );
141  BOOST_SPIRIT_DEBUG_RULE( machineinstantiation );
142  BOOST_SPIRIT_DEBUG_RULE( statemachinecontent );
143  BOOST_SPIRIT_DEBUG_RULE( varline );
144  BOOST_SPIRIT_DEBUG_RULE( state );
145  BOOST_SPIRIT_DEBUG_RULE( vardec );
146  BOOST_SPIRIT_DEBUG_RULE( subMachinedecl );
147  BOOST_SPIRIT_DEBUG_RULE( statecontent );
148  BOOST_SPIRIT_DEBUG_RULE( statecontentline );
149  BOOST_SPIRIT_DEBUG_RULE( entry );
150  BOOST_SPIRIT_DEBUG_RULE( preconditions );
151  BOOST_SPIRIT_DEBUG_RULE( precondition );
152  BOOST_SPIRIT_DEBUG_RULE( handle );
153  BOOST_SPIRIT_DEBUG_RULE( transitions );
154  BOOST_SPIRIT_DEBUG_RULE( transition );
155  BOOST_SPIRIT_DEBUG_RULE( exit );
156  BOOST_SPIRIT_DEBUG_RULE( transline );
157  BOOST_SPIRIT_DEBUG_RULE( eventline );
158  BOOST_SPIRIT_DEBUG_RULE( ifbranch );
159  BOOST_SPIRIT_DEBUG_RULE( elsebranch );
160  BOOST_SPIRIT_DEBUG_RULE( progselect );
161  BOOST_SPIRIT_DEBUG_RULE( program );
162  BOOST_SPIRIT_DEBUG_RULE( selector );
163  BOOST_SPIRIT_DEBUG_RULE( machineinstarguments );
164  BOOST_SPIRIT_DEBUG_RULE( machineinstargument );
165  BOOST_SPIRIT_DEBUG_RULE( machinememvar );
166  BOOST_SPIRIT_DEBUG_RULE( machinevariable );
167  BOOST_SPIRIT_DEBUG_RULE( machineparam );
168  BOOST_SPIRIT_DEBUG_RULE( machineconstant );
169  BOOST_SPIRIT_DEBUG_RULE( machinealias );
170  BOOST_SPIRIT_DEBUG_RULE( subMachinevarchange );
171 
172  storeOffset();
173 
174  production = *body >> expect_eof(end_p);
175 
176  body = statemachine[ boost::bind( &StateGraphParser::seenstatemachineend, this ) ][boost::bind( &StateGraphParser::saveText, this, _1, _2)]
178 
179 
181  keyword_p("RootMachine")[boost::bind (&StateGraphParser::startrootmachineinstantiation, this) ]
183 
184  statemachine =
185  keyword_p("StateMachine") //[boost::bind( &StateGraphParser::storeOffset, this)]
186  >> expect_ident( commonparser->identifier[ boost::bind( &StateGraphParser::seenstatemachinename, this, _1, _2 )] )
187  >> expect_open( ch_p( '{' ) )
189  >> expect_end( ch_p( '}' ) );
190 
191  // Zero or more declarations and Zero or more states. Once a state is encountered, no more global transitions may be defined.
193 
194  varline = vardec[lambda::var(commonparser->skipeol) = false] >> commonparser->eos[lambda::var(commonparser->skipeol) = true];
195 
197 
202 
204 
205  subMachinedecl = keyword_p("SubMachine")
207 
209  expect_ident( commonparser->identifier[ boost::bind( &StateGraphParser::seenmachinetypename, this, _1, _2 )] )
210  >> expect_ident( commonparser->identifier[ boost::bind( &StateGraphParser::seeninstmachinename, this, _1, _2 )] )
211  >> ( ! ( ch_p( '(' )
213  >> expect_close_parenth( ch_p( ')' ) ) ) )[ boost::bind( &StateGraphParser::seenmachineinstantiation, this )];
214 
217 
220  >> '='
222 
223  state =
224  !( keyword_p( "initial" )[boost::bind( &StateGraphParser::seeninitialstate,this )]
225  | keyword_p( "final" )[boost::bind( &StateGraphParser::seenfinalstate,this )] )
226  >> keyword_p( "state" )
227  >> expect_ident(commonparser->identifier[ boost::bind( &StateGraphParser::statedef, this, _1, _2 ) ])
228  >> expect_open(ch_p( '{' ))
229  >> statecontent
230  >> expect_end_of_state(ch_p( '}' ))[ boost::bind( &StateGraphParser::seenstateend, this ) ];
231 
232  // the content of a program can be any number of lines
233  // a line is not strictly defined in the sense of text-line.
235 
236  // a state can contain various programs and variable definitions
238  entry
239  | preconditions
240  | run
241  | handle
242  | transitions
243  | transition
244  | exit
245  | (machinememvar[lambda::var(commonparser->skipeol) = false] >> commonparser->eos[lambda::var(commonparser->skipeol) = true]);
246 
247  precondition = keyword_p( "precondition")
248  >> conditionparser->parser()[ boost::bind( &StateGraphParser::seenprecondition, this)] ;
249 
250  preconditions = (keyword_p( "preconditions" )[ boost::bind( &StateGraphParser::inpreconditions, this )]
251  >> expect_open( ch_p( '{' ))
252  >> *transline[boost::bind(&StateGraphParser::seenendcondition,this)]
253  >> expect_end( ch_p( '}' ) )[
254  boost::bind( &StateGraphParser::seenpreconditions, this )]) | precondition;
255 
256  entry = keyword_p( "entry" )[ boost::bind( &StateGraphParser::inprogram, this, "entry" )]
257  >> expect_open(ch_p('{'))>> programBody >> expect_end(ch_p('}'))[
258  boost::bind( &StateGraphParser::seenentry, this )];
259 
260  run = keyword_p( "run" )[ boost::bind( &StateGraphParser::inprogram, this, "run" )]
261  >> expect_open(ch_p('{'))>> programBody >> expect_end(ch_p('}'))[
262  boost::bind( &StateGraphParser::seenrun, this )];
263 
264  exit = keyword_p( "exit" )[ boost::bind( &StateGraphParser::inprogram, this, "exit" )]
265  >> expect_open(ch_p('{')) >> programBody >> expect_end(ch_p('}'))[
266  boost::bind( &StateGraphParser::seenexit, this )];
267 
268  handle = keyword_p( "handle" )[ boost::bind( &StateGraphParser::inprogram, this, "handle" )]
269  >> expect_open(ch_p('{'))>> programBody >> expect_end(ch_p('}'))[
270  boost::bind( &StateGraphParser::seenhandle, this )];
271 
272  // formal:
273  // transition [event] [[ {program} ][ select s]] | [ if c then ][ {program} ][select s][ else [ {program} ][select s]]
274  // parsed:
275  // transition [ [if c then ][ {program} ][select s][ else [ {program} ][select s]]]
276  // | [ event [[ {program} ][ select s]] | [ if c then ][ {program} ][select s][ else [ {program} ][select s]] ]
277  // rules:
278  // transition = "transition" >> (transline | eventline)
279  // transline = progselect
280  // | (ifbranch >> !elsebranch)
281  // eventline = eventname >> transline
282  // progselect = (selector | (program >> !selector))
283  // ifbranch = "if" >> c >> "then" >> progselect
284  // elsebranch = "else" >> progselect
285  // selector = "select" >> ...
286  // program = "{" >> ...
287  //
288 
289  // old transition statements
290  // the order of rule "transition" vs "transitions" is important
291  transitions = ( keyword_p( "transitions" )
292  >> expect_open(ch_p('{'))
293  >> *((transline|eventline)[boost::bind(&StateGraphParser::seenendcondition,this)])
294  >> expect_end(ch_p('}')) );
295 
296  // new transition statements
297  transition = keyword_p("transition") >> expect_event_or_if( transline | eventline )[boost::bind(&StateGraphParser::seenendcondition,this)];
299 
300  // @todo: capturing events are only on local ports ?!.
301  eventline =
302  !peerparser->parser() >> commonparser->identifier[ boost::bind( &StateGraphParser::seeneventname, this,_1,_2)]
303  >> expect_eventargs(argslist[ boost::bind( &StateGraphParser::seeneventargs, this)])
304  >> expect_eventselect(transline[ boost::bind( &StateGraphParser::seeneventtrans, this)]);
305 
306  progselect = selector | (program >> (selector | eps_p[boost::bind( &StateGraphParser::noselect, this )] ));
307  // | eps_p[boost::bind( &StateGraphParser::noselect, this )] ); // if eos fails skipeol stays false !, see clear() !
308 
309  ifbranch = keyword_p( "if") >> conditionparser->parser()[ boost::bind( &StateGraphParser::seencondition, this)]
310  >> !keyword_p( "then" )
311  >> progselect;
312  elsebranch = keyword_p("else")[boost::bind( &StateGraphParser::seenelse, this )]
313  >> progselect;
314 
315  program =
316  ch_p('{')[ boost::bind( &StateGraphParser::inprogram, this, "transition" )]
317  >> programBody
318  >> expect_end(ch_p('}'))[boost::bind( &StateGraphParser::seentransprog, this )];
319 
320  selector = keyword_p( "select" ) >> expect_select_ident(( commonparser->identifier[ boost::bind( &StateGraphParser::seenselect, this, _1, _2) ]
321  >> *(keyword_p("or") >> commonparser->identifier[ boost::bind( &StateGraphParser::seenselect, this, _1, _2) ])
322  )[lambda::var(commonparser->skipeol) = false]
323  >> commonparser->eos[lambda::var(commonparser->skipeol) = true]);
324 
325  }
326 
328  return body;
329  }
330 
333  if ( rootmachines.empty() )
334  return ret;
335  std::vector<ParsedStateMachinePtr> vret = values( rootmachines );
336  rootmachines.clear();
337  return vret.front();
338  }
339 
340 
342  {
343  curinitialstateflag = true;
344  }
345 
347  {
348  curfinalstateflag = true;
349  }
350 
352  {
353  assert( !curstate );
354 
355  std::string def(s, f);
356  if ( curtemplate->getState( def ) != 0 )
357  {
358  assert( dynamic_cast<StateDescription*>( curtemplate->getState( def ) ) );
359  StateDescription* existingstate = static_cast<StateDescription*>( curtemplate->getState( def ) );
360  if ( existingstate->isDefined() )
361  ORO_THROW(parse_exception_semantic_error("state " + def + " redefined.") );
362  else
363  curstate = existingstate;
364  curstate->setEntryPoint( mpositer.get_position().line - ln_offset );
365  }
366  else
367  {
368  curstate = new StateDescription(def, mpositer.get_position().line - ln_offset ); // create an empty state
369  curtemplate->addState( curstate );
370  }
371 
372  }
373 
375  {
376  if ( curinitialstateflag )
377  {
378  if ( curtemplate->getInitialState() )
379  ORO_THROW(parse_exception_semantic_error( "Attempt to define more than one initial state." ));
380  else curtemplate->setInitialState( curstate );
381  }
382  if ( curfinalstateflag )
383  {
384  if ( curtemplate->getFinalState() )
385  ORO_THROW(parse_exception_semantic_error( "Attempt to define more than one final state." ));
386  else curtemplate->setFinalState( curstate );
387  }
388 
389  assert( curstate );
390  curstate->setDefined( true );
391  curstate = 0;
392  curinitialstateflag = false;
393  curfinalstateflag = false;
394  // clear all port-triggered transitions for this state.
395  cur_port_events.clear();
396  }
397 
398  void StateGraphParser::inprogram(const std::string& name)
399  {
400  // setup the progParser to parse the program body,
401  // dynamically assign this parser to body.
402  assert( progParser != 0 );
403  // program name, stack, line offset.
404  //cerr << "SGP : Stack is " << curobject->getName() <<endl;
406 
408  }
409 
411  {
412  return progParser->bodyParserResult();
413  }
414 
416  {
417  if ( curstate->getEntryProgram() )
418  ORO_THROW( parse_exception_semantic_error( "Attempt to define entry twice in state "+ curstate->getName() ));
420  }
421 
423  {
424  if ( curstate->getExitProgram() )
425  ORO_THROW( parse_exception_semantic_error( "Attempt to define exit twice in state "+ curstate->getName() ));
427  }
428 
430  {
431  if ( curstate->getHandleProgram() )
432  ORO_THROW( parse_exception_semantic_error( "Attempt to define handle twice in state "+ curstate->getName() ));
434  }
435 
437  {
438  if ( curstate->getRunProgram() )
439  ORO_THROW( parse_exception_semantic_error( "Attempt to define run twice in state "+ curstate->getName() ));
441  }
442 
444  {
446  }
447 
449  {
450  // reuse transProgram to store else progr. See seenselect().
451  //transProgram = finishProgram();
452  //transProgram->setProgramProcessor(curtemplate->getService()->engine()->programs());
453  }
454 
456  {
457  assert( curcondition);
459  }
460 
462  {
463  assert( !curcondition );
465  assert( curcondition );
467  selectln = mpositer.get_position().line - ln_offset;
468  }
469 
471  {
472  evname = string(s,f);
473 
474  // seenselect() will use evname to see if event is causing transition
475  assert(evname.length());
477  peerparser->reset();
478 
479  // check if it's an operation:
480  if (peer->hasOperation(evname) ) {
481  argsparser =
483  evname, "callback" );
484  } else {
485  // check if it's a port.
486  if ( peer->hasService(evname) == false || peer->getService(evname)->hasOperation("read") == false) {
487  if (curstate)
488  ORO_THROW( parse_exception_fatal_semantic_error("In state "+curstate->getName()+": InputPort or Operation "+evname+" not found in Task "+peer->getName() ));
489  else
490  ORO_THROW( parse_exception_fatal_semantic_error("In statemachine: InputPort or Operation "+evname+" not found in Task "+peer->getName() ));
491  }
492  argsparser =
493  new ArgumentsParser( *expressionparser, context, peer->getService(evname),
494  evname, "read" );
495  }
496 
498  }
499 
501  {
502  evargs = argsparser->result();
503  delete argsparser;
504  argsparser = 0;
505  }
506 
508  {
509  // if omitted, implicitly re-enter current state.
510  if (curstate)
511  doselect( curstate->getName() );
512  else
513  doselect(""); // global events/transitions
514  }
515 
517  {
518  std::string state_id(s,f);
519  doselect(state_id);
520  }
521 
522  void StateGraphParser::doselect( const std::string& state_id )
523  {
524  StateInterface* next_state = 0;
525  if ( !state_id.empty() ) {
526  if ( curtemplate->getState( state_id ) != 0 )
527  {
528  next_state = curtemplate->getState( state_id );
529  }
530  else
531  {
532  next_state = new StateDescription(state_id, 1); // create an empty state
533  curtemplate->addState( next_state );
534  }
535  assert( next_state );
536  }
537 
538  // this transition has a lower priority than the previous one
539  if ( selectln == 0)
540  selectln = mpositer.get_position().line - ln_offset;
541 
542  if (evname.empty()) {
543  if (curcondition == 0)
545  } else if ( peer->hasService(evname) && peer->getService(evname)->hasOperation("read") ) { // is a port
546  try {
547  assert(peer->hasService(evname)); // checked in seeneventname()
548  ConditionInterface* evcondition = 0;
549  if ( global_port_events.count(evname) ){
550  // clone the cached condition in order to avoid a second read on the port.
551  evcondition = new ConditionBoolDataSource( global_port_events[evname]->getResult().get() );
552  } else
553  if ( cur_port_events.count(evname) ){
554  // clone the cached condition in order to avoid a second read on the port.
555  evcondition = new ConditionBoolDataSource( cur_port_events[evname]->getResult().get() );
556  } else {
557  // combine the implicit 'read(arg) == NewData' with the guard, if any.
558  DataSourceBase::shared_ptr read_dsb = peer->getService(evname)->produce("read", evargs, context->engine() );
559  DataSource<FlowStatus>* read_ds = dynamic_cast<DataSource<FlowStatus>*>(read_dsb.get());
560  assert(read_ds);
562  if (curstate) {
563  cur_port_events[evname] = new ConditionCache( evcondition ); // caches result until reset().
564  evcondition = cur_port_events[evname]->clone();
565  } else {
566  //global event:
567  global_port_events[evname] = new ConditionCache( evcondition ); // caches result until reset().
568  evcondition = global_port_events[evname]->clone();
569  }
570  }
571  if (curcondition == 0) {
572  curcondition = evcondition;
573  } else {
575  }
576  }
577  catch( const wrong_number_of_args_exception& e )
578  {
580  ( peer->getName(), evname + ".read", e.wanted, e.received );
581  }
582  catch( const wrong_types_of_args_exception& e )
583  {
585  ( peer->getName(), evname + ".read", e.whicharg, e.expected_, e.received_ );
586  }
587  elsestate = 0;
588  elseProgram.reset();
589  } else { // is an operation
590  assert( peer->provides()->hasMember(evname) );
591  bool res;
592  if (curcondition == 0)
594 
595  // if ( elsestate != 0)
596  // res = curtemplate->createEventTransition( &(peer->eventService), evname, evargs, curstate, next_state, curcondition->clone()
597  // else
598  //cerr << "Registering "<<evname<<" handler for SM."<<endl;
599  try {
600  res = curtemplate->createEventTransition( peer->provides(), caller, evname, evargs, curstate, next_state, curcondition->clone(), transProgram );
601  if (!res)
602  throw parse_exception_fatal_semantic_error("StateMachine could not install a Signal Handler for Operation "+evname);
603  }
604  catch( const wrong_number_of_args_exception& e )
605  {
607  ( peer->getName(), evname, e.wanted, e.received );
608  }
609  catch( const wrong_types_of_args_exception& e )
610  {
612  ( peer->getName(), evname, e.whicharg, e.expected_, e.received_ );
613  }
614  catch( const no_asynchronous_operation_exception& e )
615  {
616  throw parse_exception_fatal_semantic_error("StateMachine can't create EventTransition on Operation '" + evname + "' since it was not added with addEventOperation()." );
617  }
618  catch( ... )
619  {
620  assert( false );
621  }
622 
623  assert( res ); // checked in seeneventname()
624  elsestate = 0;
625  elseProgram.reset();
626  return; // we installed the Signal Handler !
627  }
628  // finally, install the handler:
629  curtemplate->transitionSet( curstate, next_state, curcondition->clone(), transProgram, rank--, selectln );
630  }
631 
633  delete curcondition;
634  curcondition = 0;
635  selectln = 0;
636  transProgram.reset();
637  }
638 
640  // cleanup all event related state.
641  evname.clear();
642  evargs.clear();
643  }
644 
646  {
647  assert( !curcondition );
649  assert( curcondition );
651  selectln = mpositer.get_position().line - ln_offset;
652 
653  curtemplate->preconditionSet(curstate, curcondition, selectln );
654  selectln = 0;
655  curcondition = 0;
656  }
657 
658 
660  {
661  assert( curtemplate );
662  assert( ! curstate );
663 
664  // reclaim the defined variables:
665 
666 
667  // Check if the Initial and Final States are ok.
668  if ( curtemplate->getInitialState() == 0 )
669  ORO_THROW( parse_exception_semantic_error("No initial state defined."));
670  if ( curtemplate->getFinalState() == 0 )
671  ORO_THROW( parse_exception_semantic_error("No final state defined."));
672 
673  if ( curtemplate->getStateList().empty() )
674  ORO_THROW( parse_exception_semantic_error("No states defined in this state machine !"));
675 
676  // Check if all States are defined.
677  vector<string> states = curtemplate->getStateList();
678  for( vector<string>::const_iterator it = states.begin(); it != states.end(); ++it)
679  {
680  assert( dynamic_cast<StateDescription*>( curtemplate->getState( *it ) ) );
681  StateDescription* sd = static_cast<StateDescription*>( curtemplate->getState( *it ) );
682  if ( !sd->isDefined() )
683  ORO_THROW( parse_exception_semantic_error("State " + *it + " not defined, but referenced to."));
684  }
685 
686  // retrieve _all_ defined variables and parameters, store them and cleanup the
687  // valuechangeparser.
688  valuechangeparser->store( curtemplate->getService() );
690 
691  // prepend the commands for initialising the subMachine
692  // variables..
693  assert( curtemplate->getInitCommand() == 0);
694  if ( varinitcommands.size() > 1 )
695  {
696  CommandComposite* comcom = new CommandComposite;
697  for ( std::vector<ActionInterface*>::iterator i = varinitcommands.begin();
698  i != varinitcommands.end(); ++i )
699  comcom->add( *i );
700  curtemplate->setInitCommand( comcom );
701  }
702  else if (varinitcommands.size() == 1 )
703  curtemplate->setInitCommand( *varinitcommands.begin() );
704 
705  varinitcommands.clear();
706 
707  // remove temporary subMachine peers from current task.
708  for( StateMachine::ChildList::const_iterator it= curtemplate->getChildren().begin();
709  it != curtemplate->getChildren().end(); ++it ) {
710  ParsedStateMachine* psc = dynamic_cast<ParsedStateMachine*>( it->get() );
711  if (psc) {
712  // remove from parent
713  context->provides()->removeService( psc->getService()->getName() );
714  }
715 
716  }
717 
718  // finally :
719  curtemplate->finish();
720 
721  delete progParser;
722  progParser = 0;
723 
726 
727  // save curmachinename for saveText.
728  curobject.reset();
729  curtemplate.reset();
730  }
731 
732  std::vector<ParsedStateMachinePtr> StateGraphParser::parse( iter_t& begin, iter_t end )
733  {
734  //skip_parser_t skip_parser = SKIP_PARSER;
735  //iter_pol_t iter_policy( skip_parser );
736  //#define SKIP_PARSER
737  skip_parser_t skip_parser = comment_p( "#" ) | comment_p( "//" ) | comment_p( "/*", "*/" ) | (space_p - eol_p) | commonparser->skipper;
738  iter_pol_t iter_policy( skip_parser );
739  scanner_pol_t policies( iter_policy );
740  scanner_t scanner( begin, end, policies );
741 
742  // reset the condition-transition priority.
743  rank = 0;
744 
745  this->storeOffset();
746 
747  try {
748  if ( ! production.parse( scanner ) )
749  {
750  // on error, we clear all remaining data, cause we can't
751  // guarantee consistency...
752  clear();
753  throw file_parse_exception(
754  new parse_exception_syntactic_error( "Syntax error" ),
755  mpositer.get_position().file, mpositer.get_position().line,
756  mpositer.get_position().column );
757  }
758  std::vector<ParsedStateMachinePtr> ret = values( rootmachines );
759  rootmachines.clear();
760  return ret;
761  }
762  catch( const parser_error<std::string, iter_t>& e )
763  {
764  // on error, we clear all remaining data, cause we can't
765  // guarantee consistency...
766  clear();
767  throw file_parse_exception(
768  new parse_exception_syntactic_error( e.descriptor ),
769  mpositer.get_position().file, mpositer.get_position().line,
770  mpositer.get_position().column );
771  }
772  catch( const parser_error<GraphSyntaxErrors, iter_t>& )
773  {
774  // on error, we clear all remaining data, cause we can't
775  // guarantee consistency...
776  clear();
777  throw file_parse_exception(
778  new parse_exception_syntactic_error( "Expected one of: entry, handle, exit, transitions" ),
779  mpositer.get_position().file, mpositer.get_position().line,
780  mpositer.get_position().column );
781  }
782  catch( const parse_exception& e )
783  {
784  // on error, we clear all remaining data, cause we can't
785  // guarantee consistency...
786  clear();
787  throw file_parse_exception(
788  e.copy(), mpositer.get_position().file,
789  mpositer.get_position().line, mpositer.get_position().column );
790  }
791 // catch( ... ) {
792 // assert( false );
793 // }
794  }
795 
797  clear();
798  delete valuechangeparser;
799  delete expressionparser;
800  delete conditionparser;
801  delete peerparser;
802  }
803 
805 
806  // remove tmp vars from TaskContext
808 
809  // in case of corrupt file, skipeol could have remained on false,
810  // so make sure it is set correctly again
811  commonparser->skipeol = true;
812  selectln = 0;
813  transProgram.reset();
814  elseProgram.reset();
815  delete argsparser;
816  argsparser = 0;
817  delete curcondition;
818  curcondition = 0;
819  // we own curstate, but not through this pointer...
820  curstate = 0;
821  delete curnonprecstate;
822  curnonprecstate = 0;
823  // we own curmachinebuilder, but not through this pointer...
824  curmachinebuilder = 0;
825  curinstantiatedmachine.reset();
826  // If non null, there was a parse-error, undo all :
827  if ( curtemplate )
828  {
829 
830  // remove temporary subMachine peers from current task.
831  for( StateMachine::ChildList::const_iterator it= curtemplate->getChildren().begin();
832  it != curtemplate->getChildren().end(); ++it ) {
833  ParsedStateMachine* psc = dynamic_cast<ParsedStateMachine*>( it->get() );
834  if (psc && psc->getService() ) {
835  context->provides()->removeService( psc->getService()->getName() );
836  }
837  }
838  // remove all 'this' data factories
839  curtemplate->getService()->clear();
840 
841  // will also delete all children :
842  curtemplate.reset();
843  }
844  // should be empty in most cases :
845  for ( std::vector<ActionInterface*>::iterator i = varinitcommands.begin();
846  i != varinitcommands.end(); ++ i )
847  delete *i;
848  varinitcommands.clear();
849  for ( std::vector<ActionInterface*>::iterator i = paraminitcommands.begin();
850  i != paraminitcommands.end(); ++ i )
851  delete *i;
852  paraminitcommands.clear();
853  for ( machinebuilders_t::iterator i = machinebuilders.begin();
854  i != machinebuilders.end(); ++i )
855  delete i->second;
856  machinebuilders.clear();
857 
858  }
859 
861  // the 'type' of the SC :
862  curmachinename = std::string ( begin, end );
863 
864  // check if the type exists already :
865  if ( machinebuilders.count( curmachinename ) != 0 )
866  ORO_THROW( parse_exception_semantic_error("StateMachine type " + curmachinename + " redefined."));
867 
868  curtemplate.reset(new ParsedStateMachine());
869  // Connect the new SC to the relevant machines.
870  // 'sc' acts as a stack for storing variables.
872  curobject->setName( curmachinename );
873  curtemplate->setService( curobject ); // store.
874 
875  // we pass the plain file positer such that parse errors are
876  // refering to correct file line numbers.
878 
879  // set the 'type' name :
880  curtemplate->setName( curmachinename, false );
881  }
882 
884  // stores the begining of a (possible) new statemachine definition.
885  // start line number :
886  ln_offset = mpositer.get_position().line - 1;
887  // start of text :
889  }
890 
892  assert( curmachinename.length() != 0 );
893  //cerr << std::string(begin, end)<<endl;
894  if ( machinebuilders.count( curmachinename ) == 0 )
895  return; // yes this might be possible
896  // due to the shared-text implementation, we can set the text afterwards.
897  machinebuilders[curmachinename]->item()->setText( std::string( saveStartPos, end) );
898  this->storeOffset();
899  }
900 
902  // we postpone the current state
903  assert( curnonprecstate == 0 );
905  // add the postponed state in PSM :
906  curtemplate->addState( curnonprecstate );
907  }
908 
910  curtemplate->transitionSet( curstate, curnonprecstate, new ConditionTrue, rank--, mpositer.get_position().line - ln_offset );
911  curstate->setDefined( true );
913  curnonprecstate = 0;
914  }
915 
917  isroot = true;
918  }
919 
921  // first reset the flag.
922  isroot = false;
923  if( rootmachines.find( curinstmachinename ) != rootmachines.end() )
924  ORO_THROW( parse_exception_semantic_error( "Root machine \"" + curinstmachinename + "\" already defined." ));
926 
927  // recursively set the name of this SC and all subs :
928  // furthermore, it adds the TC of each child as peer TC to the parent.
929  curinstantiatedmachine->setName( curinstmachinename, true );
930 
931  // check if the type exists already :
932  if ( context->provides()->hasService( curinstmachinename ) )
933  ORO_THROW( parse_exception_semantic_error("TaskContext '"+context->getName()+"' has already a Service named '" + curinstmachinename + "' ."));
934 
935  // Transfer ownership to the owning task.
936  context->provides()->addService( curinstantiatedmachine->getService() );
937 
938  curinstantiatedmachine.reset();
939  curinstmachinename.clear();
940  }
941 
943  if ( find_if( curtemplate->getChildren().begin(),
944  curtemplate->getChildren().end(),
945  boost::bind( equal_to<string>(), boost::bind(&StateMachine::getName,_1), curinstmachinename )) != curtemplate->getChildren().end() )
946  ORO_THROW( parse_exception_semantic_error( "SubMachine \"" + curinstmachinename + "\" already defined." ));
947 
948  // Since we parse in the task context, we must _temporarily_
949  // make each subMachine a peer of the task so that we can access
950  // its methods.
951 
952  // Warning: use context->unmountService() since curinstantiatedmachine must owns it.
953  if ( !context->provides()->addService( curinstantiatedmachine->getService() ) )
955  "Name clash: name of instantiated machine \"" + curinstmachinename +
956  "\" already used as object name in task '"+context->getName()+"'." ));
957 
958  // SM child relation
959  curtemplate->addChild( curinstantiatedmachine );
960  // sub-Service relation.
961  curtemplate->getService()->addService( curinstantiatedmachine->getService() );
962 
963  curinstantiatedmachine->setName(curinstmachinename, false ); // not recursive !
964 
965  curinstantiatedmachine.reset();
966  curinstmachinename.clear();
967  }
968 
970  assert( curmachinebuilder == 0 );
971  std::string name( begin, end );
972  machinebuilders_t::iterator i = machinebuilders.find( name );
973  if ( i == machinebuilders.end() )
974  ORO_THROW( parse_exception_semantic_error( "StateMachine \"" + name + "\" not defined." ));
975  curmachinebuilder = i->second;
976  }
977 
979  assert( curmachinebuilder != 0 );
980  assert( curinstmachinename.empty() );
981  curinstmachinename = std::string( begin, end );
982  }
983 
985  assert( curmachineinstargumentname.empty() );
986  std::string name( begin, end );
988  }
989 
992  // let's not forget this...
996  "In initialisation of StateMachine \"" + curinstmachinename +
997  "\": Parameter \"" + curmachineinstargumentname +"\" initialised twice..." ));
1000  }
1001 
1003  {
1004  // TODO : move this code to the ParsedStateMachine builder.
1005 
1006  // Create a full depth copy (including subMachines)
1007  // if RootMachine, make special copy which fixes attributes such
1008  // that on subsequent copy() they keep pointing to same var.
1009  // use shared_ptr to release on throw's below.
1011 
1012  // we stored the attributes which are params of nsc
1013  // in the build operation :
1014  machineparams_t params = nsc->getParameters();
1015 
1016  // first run over the given parameters to see if they all exist in
1017  // the context we're instantiating...
1018  for ( machineparamvalues_t::iterator i = curinstmachineparams.begin(); i != curinstmachineparams.end(); ++i )
1019  {
1020  machineparams_t::iterator j = params.find( i->first );
1021  if ( j == params.end() )
1022  ORO_THROW( parse_exception_semantic_error( "No parameter \"" + i->first + "\" in this StateMachine." ) );
1023  }
1024 
1025  for ( machineparams_t::iterator i = params.begin(); i != params.end(); ++i )
1026  {
1027  machineparamvalues_t::iterator j = curinstmachineparams.find( i->first );
1028  if ( j == curinstmachineparams.end() )
1030  "No value given for argument \"" + i->first + "\" in instantiation of this StateMachine." ));
1031 #ifndef ORO_EMBEDDED
1032  try {
1033  paraminitcommands.push_back( i->second->getDataSource()->updateAction( j->second.get() ) );
1034  }
1035  catch( const bad_assignment& )
1036  {
1037  throw parse_exception_semantic_error("Attempt to initialize parameter '"+i->first+"' with a value which is of a different type." );
1038  }
1039 #else
1040  ActionInterface* ret = i->second->getDataSource()->updateAction( j->second.get());
1041  if (ret)
1042  paraminitcommands.push_back( ret );
1043  else
1044  return;
1045 #endif
1046  }
1047 
1048  curinstantiatedmachine = nsc;
1049 
1050  // prepend the commands for initialising the subMachine
1051  // parameters
1052  if ( paraminitcommands.size() > 0 )
1053  {
1054  CommandComposite* comcom = new CommandComposite;
1055  for ( std::vector<ActionInterface*>::iterator i = paraminitcommands.begin();
1056  i != paraminitcommands.end(); ++i )
1057  comcom->add( *i );
1058  // init the vars as last (if any), so that they can be inited by an expression containing the params :
1059  if ( curinstantiatedmachine->getInitCommand() )
1060  comcom->add( curinstantiatedmachine->getInitCommand() );
1061  curinstantiatedmachine->setInitCommand( comcom );
1062  }
1063  paraminitcommands.clear();
1064 
1065  curmachinebuilder = 0;
1066  curinstmachineparams.clear();
1067 
1068  // set the TaskContext name to the instance name :
1069  curinstantiatedmachine->getService()->setName(curinstmachinename );
1070  }
1071 
1073  std::vector<ActionInterface*> acv = valuechangeparser->assignCommands();
1074  for(std::vector<ActionInterface*>::iterator it = acv.begin(); it!=acv.end(); ++it)
1075  varinitcommands.push_back( *it );
1076  // this only clears the last parsed variables, not the 'store' (see reset())
1078  }
1079 
1081  std::vector<std::string> pnames = valuechangeparser->definedNames();
1082  std::vector<AttributeBase*> tbases = valuechangeparser->definedValues();
1083  assert( pnames.size() == tbases.size() );
1084  for (unsigned int i = 0; i < pnames.size(); ++i)
1085  curtemplate->addParameter( pnames[i] , tbases[i] );
1086  // this only clears the last parsed variables, not the 'store' (see reset())
1088  }
1089 
1090 
1091 }
ProgramInterface * getHandleProgram() const
#define keyword_p(word)
std::vector< base::DataSourceBase::shared_ptr > result()
unsigned int selectln
are we instantiating a rootmachine ?
void setEntryProgram(ProgramInterfacePtr entry)
std::vector< base::AttributeBase * > definedValues()
void seenmachineinstargumentname(iter_t begin, iter_t end)
void saveText(iter_t begin, iter_t end)
ConditionInterface * getParseResult()
Service::shared_ptr provides()
#define ORO_THROW(x)
Definition: Exceptions.hpp:52
void setHandleProgram(ProgramInterfacePtr handle)
void setRunProgram(ProgramInterfacePtr run)
This interface represents the concept of a condition which can be evaluated and return true or false...
const std::string & getName() const
const std::string & getName() const
Get the name of this state.
Definition: mystd.hpp:163
bool skipeol
Saves eol skipping state.
This class represents a state with all actions stored in an external program.
StateGraphParser(iter_t &positer, TaskContext *tc, ExecutionEngine *caller, CommonParser *cp)
void statedef(iter_t s, iter_t f)
virtual ConditionInterface * clone() const =0
A Parser for Orocos Program Scripts.
boost::shared_ptr< ProgramInterface > ProgramInterfacePtr
virtual parse_exception * copy() const =0
void initBodyParser(const std::string &name, Service::shared_ptr stck, int offset)
This class contains some very common parser definitions.
machineparamvalues_t curinstmachineparams
std::map< std::string, base::AttributeBase * > machineparams_t
void doselect(const std::string &name)
std::map< std::string, ConditionCache * > global_port_events
ParsedStateMachinePtr curinstantiatedmachine
void store(Service::shared_ptr other)
scanner< iter_t, scanner_pol_t > scanner_t
ProgramInterface * getEntryProgram() const
ParsedStateMachinePtr getParserResult()
std::vector< base::ActionInterface * > varinitcommands
void inprogram(const std::string &name)
void setExitProgram(ProgramInterfacePtr exit)
skip_parser_iteration_policy< skip_parser_t > iter_pol_t
boost::shared_ptr< StateMachineService > getService() const
functor_parser< eol_skip_functor > skipper
Based on the software pattern &#39;command&#39;, this interface allows execution of action objects...
std::vector< std::string > definedNames()
base::DataSourceBase::shared_ptr getResult()
rule< scanner_t > rule_t
StateMachineBuilder * curmachinebuilder
void seenmachinetypename(iter_t begin, iter_t end)
std::vector< base::DataSourceBase::shared_ptr > evargs
store line number of select&#39;s &#39;if&#39; statement.
boost::shared_ptr< StateMachineService > curobject
boost::shared_ptr< ProgramInterface > transProgram
std::map< std::string, ConditionCache * > cur_port_events
ProgramInterface * getRunProgram() const
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
boost::shared_ptr< ProgramInterface > elseProgram
void seeneventname(iter_t s, iter_t f)
virtual void add(base::ActionInterface *com)
void seenstatemachinename(iter_t begin, iter_t end)
std::vector< base::ActionInterface * > assignCommands()
ParsedStateMachinePtr build(bool instantiate)
std::vector< typename MapT::mapped_type > values(const MapT &map)
Definition: mystd.hpp:140
boost::intrusive_ptr< DataSourceBase > shared_ptr
Contains TaskContext, Activity, OperationCaller, Operation, Property, InputPort, OutputPort, Attribute.
Definition: Activity.cpp:53
std::vector< base::ActionInterface * > paraminitcommands
our_pos_iter_t iter_t
void seeninstmachinename(iter_t begin, iter_t end)
boost::shared_ptr< ProgramInterface > finishProgram()
boost::shared_ptr< ParsedStateMachine > ParsedStateMachinePtr
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...
const ExecutionEngine * engine() const
Definition: TaskCore.hpp:306
ProgramInterface * getExitProgram() const
This class represents a stateMachine as a Service in the Orocos TaskContext system.
std::vector< ParsedStateMachinePtr > parse(iter_t &begin, iter_t end)
virtual const std::string & getName() const
void seenselect(iter_t s, iter_t f)


rtt
Author(s): RTT Developers
autogenerated on Tue Jun 25 2019 19:33:36