// -*- C++ -*- (c) 2011 Petr Rockai // (c) 2012, 2013 Jan Kriho #include #include // atoi #include #include #include #include #ifndef DIVINE_DVE_PARSE_H #define DIVINE_DVE_PARSE_H namespace divine { namespace dve { struct SymTab; /* * Avoid dependent scope in the parsers, which would make them quite * * unreadable. XXX use a virtualised StreamBase or such instead... */ struct IOStream { std::istream &i; std::string remove() { char block[1024]; i.read( block, 1023 ); block[i.gcount()] = 0; return block; } bool eof() { return i.eof(); } IOStream( std::istream &i ) : i( i ) {} }; typedef brick::parse::Parser< Token, Lexer< IOStream > > Parser; namespace parse { struct ASTClone {}; struct Constant : Parser { Token token; int value; void sign() { eat( Token::Minus ); value = -1; } Constant( Context &c ) : Parser( c ) { value = 1; maybe( &Constant::sign ); token = eat( Token::Constant ); if ( token.data == "true" ) value = 1; else if ( token.data == "false" ) value = 0; else value = value * atoi( token.data.c_str() ); } Constant( int v, Context &c ) : Parser( c ), token( Token::Constant, "" ), value( v ) {} Constant() {} }; struct Identifier : Parser { Token token; std::string name() const { return token.data; } Identifier( Context &c ) : Parser( c ) { token = eat( Token::Identifier ); } Identifier( std::string name, Context &parent ) { std::stringstream realstream; realstream << name; dve::IOStream stream( realstream ); dve::Lexer< dve::IOStream > lexer( stream ); ASSERT( &parent ); *this = Identifier( parent.createChild( lexer, name ) ); this->token.data = name; } Identifier() {} }; struct Expression; struct MacroNode : Parser { Identifier name; std::vector< Expression* > params; void param(); MacroNode( Context &c ) : Parser( c ) { name = Identifier( context() ); eat( Token::ParenOpen ); do { maybe( &MacroNode::param ); } while ( maybe( Token::Comma ) ); eat( Token::ParenClose ); } MacroNode( const MacroNode &mnode, ASTClone ); MacroNode() {} }; struct RValue : Parser { Identifier ident; Constant value; Expression *idx; MacroNode mNode; void subscript(); void macro() { mNode = MacroNode( context() ); } void reference() { ident = Identifier( context() ); maybe( &RValue::subscript ); } void constant() { value = Constant( context() ); } std::ostream& dump( std::ostream &o ); RValue( Context &c ) : Parser( c ), idx( 0 ) { either( &RValue::macro, &RValue::reference, &RValue::constant ); } RValue( const RValue &rval, ASTClone ); RValue() : idx( 0 ) {} }; struct Expression : Parser { typedef Expression Body; Token op; std::shared_ptr< Expression > lhs, rhs; std::shared_ptr< RValue > rval; void setName( Identifier ) {} void parens() { eat( Token::ParenOpen ); } // TODO: Unary operators: minus and tilde (bitwise negation) void negation() { op = eat( Token::Bool_Not ); Expression _lhs( context(), 0 ); lhs.reset( new Expression( _lhs ) ); } void rvalue() { RValue _rval( context() ); rval.reset( new RValue( _rval ) ); } std::ostream& dump( std::ostream &o ) { if ( lhs && rhs ) { o << "("; lhs->dump( o ); o << ") " << op.data << " ("; rhs->dump( o ); o << ")"; } else if ( lhs ) { o << op.data; lhs->dump( o ); } else { rval->dump( o ); } return o; } // TODO: Use the precedence climbing algorithm here, as it is likely // substantially more efficient Expression( Context &c, int prec = Token::precedences ) : Parser( c ) { lhs = rhs = 0; rval = 0; if ( prec ) { // XXX: associativity Expression _lhs( context(), prec - 1 ); lhs.reset( new Expression( _lhs ) ); op = eat( false ); if ( op.valid() && op.precedence( prec ) ) { Expression _rhs( context(), prec ); rhs.reset( new Expression( _rhs ) ); } else context().rewind( 1 ); } else { if ( next( Token::ParenOpen ) ) { Expression _lhs( context() ); eat( Token::ParenClose ); lhs.reset( new Expression( _lhs ) ); } else { either( &Expression::negation, &Expression::rvalue ); } } if ( !lhs && !rval ) fail( "expression" ); /* simplify expressions */ if ( lhs && !rhs && !op.precedence( prec ) && !op.unary() ) { Expression ex = *lhs; lhs.reset(); *this = ex; } } Expression( std::string name, Context &parent ) { std::stringstream realstream; realstream << name; dve::IOStream stream( realstream ); dve::Lexer< dve::IOStream > lexer( stream ); *this = Expression( parent.createChild( lexer, name ) ); } Expression( const Expression &exp, ASTClone ) : Expression( exp ) { if ( exp.lhs ) lhs.reset( new Expression( *exp.lhs, ASTClone() ) ); if ( exp.rhs ) rhs.reset( new Expression( *exp.rhs, ASTClone() ) ); if ( exp.rval ) rval.reset( new RValue( *exp.rval, ASTClone() ) ); } Expression() : lhs( 0 ), rhs( 0 ), rval( 0 ) {} }; struct ExpressionList : Parser { std::vector< Expression > explist; bool compound; std::ostream& dump( std::ostream &o ) { o << "{"; for( auto it = explist.begin(); it != explist.end(); it++ ) { if ( it != explist.begin() ) o << ","; it->dump( o ); } o << "}"; return o; } ExpressionList( Context &c ) : Parser( c ) { if ( next( Token::BlockOpen ) ) { compound = true; list< Expression >( std::back_inserter( explist ), Token::Comma ); eat( Token::BlockClose ); } else explist.push_back( Expression( context() ) ); } ExpressionList() {}; ExpressionList( const ExpressionList &elist, ASTClone ) : Parser( elist ), compound( elist.compound ) { for ( const Expression &e : elist.explist ) explist.push_back( Expression( e, ASTClone() ) ); } }; inline void RValue::subscript() { eat( Token::IndexOpen ); idx = new Expression( context() ); eat( Token::IndexClose ); } inline std::ostream& RValue::dump( std::ostream &o ) { if ( ident.valid() ) { o << ident.name(); } else { o << value.value; } return o; } inline RValue::RValue( const RValue &rval, ASTClone ) : Parser( rval ), ident( rval.ident ), value( rval.value ), idx( rval.idx ), mNode( rval.mNode, ASTClone() ) { if ( rval.idx ) { idx = new Expression( *rval.idx, ASTClone() ); } } inline void MacroNode::param() { Expression* expr = new Expression( context() ); params.push_back( expr ); } inline MacroNode::MacroNode( const MacroNode &mnode, ASTClone ) : MacroNode( mnode ) { params.clear(); for ( Expression * ex : mnode.params ) params.push_back( new Expression( *ex, ASTClone() ) ); } struct LValue : Parser { Identifier ident; Expression idx; void subscript() { eat( Token::IndexOpen ); idx = Expression( context() ); eat( Token::IndexClose ); } std::ostream& dump( std::ostream &o ) { o << ident.name(); return o; } LValue( Context &c ) : Parser( c ) { ident = Identifier( c ); maybe( &LValue::subscript ); } LValue() {} LValue( const LValue &lval, ASTClone ) : Parser( lval ), ident( lval.ident ), idx( lval.idx, ASTClone() ) {} }; struct LValueList: Parser { std::vector< LValue > lvlist; bool compound; std::ostream& dump( std::ostream &o ) { o << "{"; for( auto it = lvlist.begin(); it != lvlist.end(); it++ ) { if ( it != lvlist.begin() ) o << ","; it->dump( o ); } o << "}"; return o; } LValueList( Context &c ) : Parser( c ) { if ( next( Token::BlockOpen ) ) { compound = true; list< LValue >( std::back_inserter( lvlist ), Token::Comma ); eat( Token::BlockClose ); } else lvlist.push_back( LValue( context() ) ); } LValueList() {} LValueList( const LValueList & lvallist, ASTClone ) : Parser( lvallist ), compound( lvallist.compound ) { for ( const LValue &lval : lvallist.lvlist ) lvlist.push_back( LValue( lval, ASTClone() ) ); } }; struct Assignment : Parser { LValue lhs; Expression rhs; std::ostream& dump( std::ostream &o ) { lhs.dump( o ); o << " = "; rhs.dump( o ); return o; } Assignment( Context &c ) : Parser( c ) { lhs = LValue( c ); eat( Token::Assignment ); rhs = Expression( c ); } Assignment( const Assignment &a, ASTClone ) : Parser( a ), lhs( a.lhs, ASTClone() ), rhs( a.rhs, ASTClone() ) {} }; struct SyncExpr : Parser { bool write; bool compound; RValue proc; Identifier chan; ExpressionList exprlist; LValueList lvallist; void lvalues() { lvallist = LValueList( context() ); } void expressions() { exprlist = ExpressionList( context() ); } std::ostream& dump( std::ostream &o ) { o << chan.name(); if ( write ) { o << "!"; if ( exprlist.valid() ) exprlist.dump( o ); } else { o << "?"; if ( lvallist.valid() ) lvallist.dump( o ); } return o; } SyncExpr( Context &c ) : Parser( c ) { proc = RValue( c ); if ( next( Token::Arrow ) ) { chan = Identifier( c ); } else { chan = proc.ident; proc.ctx = 0; } if ( next( Token::SyncWrite ) ) write = true; else if ( next( Token::SyncRead ) ) write = false; else fail( "sync read/write mark: ! or ?" ); if ( write ) maybe( &SyncExpr::expressions ); else maybe( &SyncExpr::lvalues ); } SyncExpr() {} SyncExpr( const SyncExpr &se, ASTClone ) : Parser( se ), write ( se.write ), compound( se.compound ), proc( se.proc ), chan( se.chan ), exprlist( se.exprlist, ASTClone() ), lvallist( se.lvallist, ASTClone() ) {} }; /* * Never use this parser directly, only as an AST node. See Declarations. */ struct Declaration : Parser { bool is_const, is_compound, is_buffered; int width; // bytes bool is_array; Expression sizeExpr; int size; std::vector< int > components; std::vector< Expression > initialExpr; std::vector< int > initial; std::string name; bool is_input; void setSize( int s ) { size = s; if ( size < 1 ) fail( ( "Invalid array size: " + brick::string::fmt( size ) ).c_str(), FailType::Semantic ); } void subscript() { eat( Token::IndexOpen ); sizeExpr = Expression( context() ); eat( Token::IndexClose ); is_array = true; } void initialiser() { Token t = eat( Token::Assignment ); if ( is_array ) list< Expression >( std::back_inserter( initialExpr ), Token::BlockOpen, Token::Comma, Token::BlockClose ); else initialExpr.push_back( Expression( context() ) ); } Declaration( Context &c ) : Parser( c ), is_buffered( 0 ), size( 1 ) { Token t = eat( Token::Identifier ); name = t.data; is_array = false; maybe( &Declaration::subscript ); maybe( &Declaration::initialiser ); } Declaration( const Declaration &d, ASTClone ) : Declaration( d ) { sizeExpr = Expression( d.sizeExpr, ASTClone() ); initialExpr.clear(); for ( const Expression &e : d.initialExpr ) initialExpr.push_back( Expression( e, ASTClone() ) ); } void fold( SymTab* symtab ); }; struct Type : Parser { enum TypeSize { Byte = 1, Int = 2 }; TypeSize size; Type( Context &c ) : Parser( c ) { if ( next( Token::Byte ) ) { size = TypeSize::Byte; } else if ( next ( Token::Int ) ) { size = TypeSize::Int; } else fail( "type name (int, byte)" ); } }; struct ChannelDeclaration : Parser { bool is_buffered, is_const; Expression sizeExpr; int size, width; std::string name; bool is_compound; std::vector< int > components; bool is_input; void setSize( int s ) { size = s; if ( size < 0 ) fail( ( "Invalid array size: " + brick::string::fmt( size ) ).c_str(), FailType::Semantic ); is_buffered = ( size > 0 ); } void subscript() { eat( Token::IndexOpen ); sizeExpr = Expression( context() ); eat( Token::IndexClose ); } ChannelDeclaration( Context &c ) : Parser( c ), size( 1 ) { Token t = eat( Token::Identifier ); name = t.data; is_buffered = false; maybe( &ChannelDeclaration::subscript ); } ChannelDeclaration( const ChannelDeclaration &cd, ASTClone ) : ChannelDeclaration( cd ) { sizeExpr = Expression( cd.sizeExpr, ASTClone() ); } void fold( SymTab* symtab ); }; /* * This parses a compact (comma-separated) declaration with a common type but * multiple identifiers. Always use this production, and never declaration * above. On the other hand, always use Declaration in the AST. See also * "declarations". */ struct Declarations : Parser { std::vector< Declaration > decls; std::vector< ChannelDeclaration > chandecls; std::vector< Type > types; bool is_const = false; bool is_chan = false; bool is_compound = false; bool is_input = false; int width = 2; // default for untyped channels template< typename T > void init( T &decllist ) { for ( unsigned i = 0; i < decllist.size(); ++i ) { decllist[i].is_const = is_const; //decls[i].is_chan = is_chan; decllist[i].is_compound = is_compound; for( unsigned j = 0; j < types.size(); ++j ) { decllist[i].components.push_back( types[i].size ); width += types[i].size; } decllist[i].width = width; decllist[i].is_input = is_input; } } Declarations( Context &c ) : Parser( c ) { if ( next( Token::Const ) ) is_const = true; else if ( next( Token::Input ) ) is_input = true; if ( next( Token::Int ) ) width = 2; else if ( next( Token::Byte ) ) width = 1; else if ( next( Token::Channel ) ) { is_chan = true; if ( next( Token::BlockOpen ) ) { width = 0; is_compound = true; list< Type >( std::back_inserter( types ), Token::Comma ); eat( Token::BlockClose ); } } else fail( "type name (int, byte, channel)" ); if ( is_chan ) { list< ChannelDeclaration >( std::back_inserter( chandecls ), Token::Comma ); init( chandecls ); } else { list< Declaration >( std::back_inserter( decls ), Token::Comma ); init( decls ); } semicolon(); } }; struct Assertion : Parser { Identifier state; Expression expr; Assertion( Context &c ) : Parser( c ) { state = Identifier( c ); colon(); expr = Expression( c ); } Assertion( const Assertion &a, ASTClone ) : Parser( a ), state( a.state ), expr( a.expr, ASTClone() ) {} }; struct Transition : Parser { Identifier from, to; Identifier proc; std::vector< Expression > guards; std::vector< Assignment > effects; SyncExpr syncexpr; Token end; void guard() { eat( Token::Guard ); list< Expression >( std::back_inserter( guards ), Token::Comma ); semicolon(); } void effect() { eat( Token::Effect ); list< Assignment >( std::back_inserter( effects ), Token::Comma ); semicolon(); } void sync() { eat( Token::Sync ); syncexpr = SyncExpr( context() ); semicolon(); } std::ostream& dump( std::ostream &o ) { o << from.name() << " -> " << to.name() << " { "; o << "guard "; for ( auto grd = guards.begin(); grd != guards.end(); grd++ ) { grd->dump( o ); o << ","; } o << "; sync "; if ( syncexpr.valid() ) syncexpr.dump( o ); o << "; effect "; for ( auto eff = effects.begin(); eff != effects.end(); eff++) { eff->dump( o ); o << ","; } o << ";}"; return o; } Transition( Context &c ) : Parser( c ) { from = Identifier( c ); eat( Token::Arrow ); to = Identifier( c ); eat( Token::BlockOpen ); maybe( &Transition::guard ); maybe( &Transition::sync ); maybe( &Transition::effect ); end = eat( Token::BlockClose ); } Transition() {}; Transition( const Transition &t, ASTClone ) : Parser( t ), from( t.from ), to( t.to ), syncexpr( t.syncexpr, ASTClone() ), end( t.end ) { guards.clear(); for ( const Expression &e : t.guards ) guards.push_back( Expression( e, ASTClone() ) ); effects.clear(); for ( const Assignment &a : t.effects ) effects.push_back( Assignment( a, ASTClone() ) ); } }; inline size_t declarations( Parser &p, std::vector< Declaration > &decls, std::vector< ChannelDeclaration > &chandecls ) { std::vector< Declarations > declss; size_t count = 0; p.many< Declarations >( std::back_inserter( declss ) ); for ( unsigned i = 0; i < declss.size(); ++i ) { std::copy( declss[i].decls.begin(), declss[i].decls.end(), std::back_inserter( decls ) ); count += declss[i].decls.size(); std::copy( declss[i].chandecls.begin(), declss[i].chandecls.end(), std::back_inserter( chandecls ) ); count += declss[i].chandecls.size(); } return count; } struct Automaton : Parser { Identifier name; struct Body : Parser { std::vector< Declaration > decls; std::vector< ChannelDeclaration > chandecls; std::vector< Identifier > states, accepts, commits, inits; std::vector< Assertion > asserts; std::vector< Transition > trans; void accept() { eat( Token::Accept ); list< Identifier >( std::back_inserter( accepts ), Token::Comma ); semicolon(); } void commit() { eat( Token::Commit ); list< Identifier >( std::back_inserter( commits ), Token::Comma ); semicolon(); } void state() { eat( Token::State ); list< Identifier >( std::back_inserter( states ), Token::Comma ); semicolon(); } void init() { eat( Token::Init ); list< Identifier >( std::back_inserter( inits ), Token::Comma ); semicolon(); } void assertion() { if ( next( Token::Assert ) ) { list< Assertion >( std::back_inserter( asserts ), Token::Comma ); semicolon(); } } void optionalComma() { maybe( Token::Comma ); } std::ostream& dump( std::ostream &o ) { o << "state "; for ( Identifier &s : states ) o << s.name() << ", "; o << std::endl; o << "accept "; for ( Identifier &s : accepts ) o << s.name() << ", "; o << std::endl; o << "init "; for ( Identifier &s : inits ) o << s.name() << ", "; o << std::endl; o << "commit "; for ( Identifier &s : commits ) o << s.name() << ", "; o << std::endl; o << "trans" << std::endl; for ( Transition &t : trans ) { t.dump( o ); o << std::endl; } o << "}" << std::endl; return o; } void setName( Identifier newname ) { for( Transition &t : trans ) { t.proc = newname; } } Body( Context &c ) : Parser( c ) { declarations( *this, decls, chandecls ); arbitrary( &Body::accept, &Body::commit, &Body::state, &Body::init, &Body::assertion ); if ( !states.size() ) fail( "states" ); if ( !inits.size() ) fail( "initial" ); if ( next( Token::Trans ) ) { list< Transition >( std::back_inserter( trans ), &Body::optionalComma ); maybe( &Body::semicolon ); } } Body() {} Body( const Body &a, ASTClone ) : Parser( a ), states( a.states ), accepts( a.accepts ), commits( a.commits ), inits( a.inits ) { decls.clear(); for ( const Declaration &d : a.decls ) decls.push_back( Declaration( d, ASTClone() ) ); chandecls.clear(); for ( const ChannelDeclaration &d : a.chandecls ) chandecls.push_back( ChannelDeclaration( d, ASTClone() ) ); asserts.clear(); for ( const Assertion &as : a.asserts ) asserts.push_back( Assertion( as, ASTClone() ) ); trans.clear(); for ( const Transition &t : a.trans ) trans.push_back( Transition( t, ASTClone() ) ); } void fold( SymTab *parent ); }; Body body; std::ostream& dump( std::ostream &o ) { o << "process " << name.name() << " {" << std::endl; body.dump( o ); return o; } void setName( Identifier newname ) { name = newname; body.setName( newname ); } Automaton( Context &c ) : Parser( c ) { eat( Token::Process ); name = Identifier( c ); eat( Token::BlockOpen ); body = Body( c ); body.setName( name ); eat( Token::BlockClose ); } Automaton() : Parser() {} Automaton( const Automaton &a, ASTClone ) : Parser( a ), name( a.name ), body( a.body, ASTClone() ) {} Automaton( const Automaton::Body &b, ASTClone ) : Parser( b ), body( b, ASTClone() ) {} void fold( SymTab *parent ) { body.fold( parent ); } }; typedef Automaton Process; typedef Automaton Property; struct MacroParam : Parser { Identifier param; bool key; MacroParam( Context &c ) : Parser( c ) { key = maybe( Token::Key ); param = Identifier( c ); } }; template< typename T > struct Macro : Parser { typename T::Body content; Identifier name; std::vector< MacroParam > params; unsigned used; void paramlist() { list< MacroParam >( std::back_inserter( params ), Token::Comma ); } Macro( Context &c ) : Parser( c ), used( 0 ) { name = Identifier( c ); eat( Token::ParenOpen ); maybe( &Macro::paramlist ); eat( Token::ParenClose ); eat( Token::BlockOpen ); content = typename T::Body( c ); eat( Token::BlockClose ); } }; struct LTL : Parser { Identifier name; Token property; LTL( Context &c ) : Parser( c ) { name = Identifier( c ); property = eat( Token::String ); } }; template< typename T > struct ForLoop : Parser { Identifier variable; Expression first, last; std::vector< typename T::Instantiable > instantiables; void instantiable() { instantiables.push_back( typename T::Instantiable( context() ) ); } ForLoop( Context &c ) : Parser( c ) { eat( Token::For ); eat( Token::ParenOpen ); variable = Identifier( c ); colon(); first = Expression( c ); eat( Token::Ellipsis ); last = Expression( c ); eat( Token::ParenClose ); eat( Token::BlockOpen ); while( maybe( &ForLoop< T >::instantiable ) ); eat( Token::BlockClose ); } ForLoop( const ForLoop< T > &fl, ASTClone ) : Parser( fl ), variable( fl.variable ), first( fl.first, ASTClone() ), last( fl.last, ASTClone() ) { for ( const typename T::Instantiable &inst : fl.instantiables ) { instantiables.push_back( typename T::Instantiable( inst, ASTClone() ) ); } } }; template< typename T > struct IfBlock : Parser { std::vector< Expression > conditions; std::vector< typename T::Instantiable > instantiables; void instantiable() { instantiables.push_back( typename T::Instantiable( context() ) ); } void elif() { eat( Token::Else ); eat( Token::If ); expr(); body(); } void _else() { eat( Token::Else ); body(); } void expr() { eat( Token::ParenOpen ); conditions.push_back( Expression( context() ) ); eat( Token::ParenClose ); } void body() { eat( Token::BlockOpen ); instantiable(); eat( Token::BlockClose ); } IfBlock( Context &c ) : Parser( c ) { eat( Token::If ); while ( maybe( &IfBlock< T >::elif ) ); maybe( &IfBlock< T >::_else ); } IfBlock( const IfBlock< T > & ib, ASTClone ) : Parser( ib ) { for ( const Expression &cond : ib.conditions ) { conditions.push_back( Expression( cond, ASTClone() ) ); } for ( const typename T::Instantiable &inst : ib.instantiables ) { instantiables.push_back( typename T::Instantiable( inst, ASTClone() ) ); } } }; struct System : Parser { std::vector< Declaration > decls; std::vector< ChannelDeclaration > chandecls; std::vector< Process > processes; std::vector< Property > properties; std::vector< LTL > ltlprops; std::vector< Macro< Expression > > exprs; std::vector< Macro< Automaton > > templates; Identifier property; bool synchronous; struct Instantiable : Parser { std::vector< MacroNode > procInstances; std::vector< ForLoop< System > > loops; std::vector< IfBlock< System > > ifs; void processInstance() { eat( Token::Process ); procInstances.push_back( MacroNode( context() ) ); semicolon(); } void forLoop() { loops.push_back( ForLoop< System >( context() ) ); } void ifBlock() { ifs.push_back( IfBlock< System >( context() ) ); } Instantiable( Context &c ) : Parser( c ) { while ( maybe( &Instantiable::processInstance, &Instantiable::forLoop ) ); if ( !procInstances.size() && !loops.size() ) { fail("System::Instantiable"); } } Instantiable( const Instantiable &inst, ASTClone ) : Parser( inst ) { for ( const MacroNode &mn : inst.procInstances ) { procInstances.push_back( MacroNode( mn, ASTClone() ) ); } for ( const ForLoop< System > &fl : inst.loops ) { loops.push_back( ForLoop< System >( fl, ASTClone() ) ); } } }; std::vector< Instantiable > instantiables; void _property() { eat( Token::Property ); property = Identifier( context() ); } void process() { processes.push_back( Process( context() ) ); } void propDef() { eat( Token::Property ); either( &System::procProperty, &System::LTLProperty ); } void procProperty() { properties.push_back( Property( context() ) ); } void LTLProperty() { eat( Token::LTL ); ltlprops.push_back( LTL( context() ) ); } void declaration() { if ( !declarations( *this, decls, chandecls ) ) fail( "no declarations" ); } void exprMacro() { eat( Token::Expression ); exprs.push_back( Macro< Expression >( context() ) ); } void templateMacro() { eat( Token::Process ); templates.push_back( Macro< Automaton >( context() ) ); templates.back().content.setName( templates.back().name ); } void instantiable() { instantiables.push_back( Instantiable( context() ) ); } System( Context &c ) : Parser( c ) { synchronous = false; while ( maybe( &System::declaration, &System::process, &System::propDef, &System::exprMacro, &System::templateMacro, &System::instantiable ) ); eat( Token::System ); if ( next( Token::Async ) ); // nothing else if ( next( Token::Sync ) ) synchronous = true; else fail( "sync or async" ); maybe( &System::_property ); semicolon(); context().clearErrors(); } void fold(); }; } } } #endif