/*************************************************************************** * Copyright (C) 2006-2009 by Fabrizio Montesi * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU Library General Public License as * * published by the Free Software Foundation; either version 2 of the * * License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * * * For details about the authors of this software, see the AUTHORS file. * ***************************************************************************/ package jolie.lang.parse; import jolie.lang.parse.context.ParsingContext; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import jolie.lang.Constants; import jolie.lang.NativeType; import jolie.lang.parse.ast.AddAssignStatement; import jolie.lang.parse.ast.expression.AndConditionNode; import jolie.lang.parse.ast.AssignStatement; import jolie.lang.parse.ast.CompareConditionNode; import jolie.lang.parse.ast.CompensateStatement; import jolie.lang.parse.ast.expression.ConstantIntegerExpression; import jolie.lang.parse.ast.expression.ConstantDoubleExpression; import jolie.lang.parse.ast.expression.ConstantStringExpression; import jolie.lang.parse.ast.CorrelationSetInfo; import jolie.lang.parse.ast.CorrelationSetInfo.CorrelationAliasInfo; import jolie.lang.parse.ast.CorrelationSetInfo.CorrelationVariableInfo; import jolie.lang.parse.ast.courier.CourierChoiceStatement; import jolie.lang.parse.ast.CurrentHandlerStatement; import jolie.lang.parse.ast.DeepCopyStatement; import jolie.lang.parse.ast.DefinitionCallStatement; import jolie.lang.parse.ast.DefinitionNode; import jolie.lang.parse.ast.DivideAssignStatement; import jolie.lang.parse.ast.EmbeddedServiceNode; import jolie.lang.parse.ast.ExecutionInfo; import jolie.lang.parse.ast.ExitStatement; import jolie.lang.parse.ast.ForEachStatement; import jolie.lang.parse.ast.ForStatement; import jolie.lang.parse.ast.expression.FreshValueExpressionNode; import jolie.lang.parse.ast.IfStatement; import jolie.lang.parse.ast.InstallFixedVariableExpressionNode; import jolie.lang.parse.ast.InstallFunctionNode; import jolie.lang.parse.ast.InstallStatement; import jolie.lang.parse.ast.expression.IsTypeExpressionNode; import jolie.lang.parse.ast.LinkInStatement; import jolie.lang.parse.ast.LinkOutStatement; import jolie.lang.parse.ast.NDChoiceStatement; import jolie.lang.parse.ast.expression.NotExpressionNode; import jolie.lang.parse.ast.NotificationOperationStatement; import jolie.lang.parse.ast.NullProcessStatement; import jolie.lang.parse.ast.OLSyntaxNode; import jolie.lang.parse.ast.OneWayOperationDeclaration; import jolie.lang.parse.ast.OneWayOperationStatement; import jolie.lang.parse.ast.expression.OrConditionNode; import jolie.lang.parse.ast.OutputPortInfo; import jolie.lang.parse.ast.ParallelStatement; import jolie.lang.parse.ast.PointerStatement; import jolie.lang.parse.ast.PostDecrementStatement; import jolie.lang.parse.ast.PostIncrementStatement; import jolie.lang.parse.ast.PreDecrementStatement; import jolie.lang.parse.ast.PreIncrementStatement; import jolie.lang.parse.ast.expression.ProductExpressionNode; import jolie.lang.parse.ast.Program; import jolie.lang.parse.ast.RequestResponseOperationDeclaration; import jolie.lang.parse.ast.RequestResponseOperationStatement; import jolie.lang.parse.ast.Scope; import jolie.lang.parse.ast.SequenceStatement; import jolie.lang.parse.ast.InputPortInfo; import jolie.lang.parse.ast.InterfaceDefinition; import jolie.lang.parse.ast.InterfaceExtenderDefinition; import jolie.lang.parse.ast.SubtractAssignStatement; import jolie.lang.parse.ast.MultiplyAssignStatement; import jolie.lang.parse.ast.OperationCollector; import jolie.lang.parse.ast.PortInfo; import jolie.lang.parse.ast.SolicitResponseOperationStatement; import jolie.lang.parse.ast.SpawnStatement; import jolie.lang.parse.ast.expression.SumExpressionNode; import jolie.lang.parse.ast.SynchronizedStatement; import jolie.lang.parse.ast.ThrowStatement; import jolie.lang.parse.ast.TypeCastExpressionNode; import jolie.lang.parse.ast.UndefStatement; import jolie.lang.parse.ast.ValueVectorSizeExpressionNode; import jolie.lang.parse.ast.expression.VariableExpressionNode; import jolie.lang.parse.ast.VariablePathNode; import jolie.lang.parse.ast.VariablePathNode.Type; import jolie.lang.parse.ast.WhileStatement; import jolie.lang.parse.ast.courier.CourierDefinitionNode; import jolie.lang.parse.ast.courier.NotificationForwardStatement; import jolie.lang.parse.ast.courier.SolicitResponseForwardStatement; import jolie.lang.parse.ast.expression.*; import jolie.lang.parse.ast.types.TypeDefinition; import jolie.lang.parse.ast.types.TypeDefinitionLink; import jolie.lang.parse.ast.types.TypeDefinitionUndefined; import jolie.lang.parse.ast.types.TypeInlineDefinition; import jolie.lang.parse.ast.types.UInt16; import jolie.lang.parse.ast.types.UInt32; import jolie.lang.parse.ast.types.UInt64; import jolie.lang.parse.context.URIParsingContext; import jolie.util.Pair; import jolie.util.Range; /** Parser for a .ol file. * @author Fabrizio Montesi * */ public class OLParser extends AbstractParser { private final Program program; private final Map< String, Scanner.Token > constantsMap = new HashMap< String, Scanner.Token >(); private boolean insideInstallFunction = false; private String[] includePaths; private final Map< String, InterfaceDefinition > interfaces = new HashMap< String, InterfaceDefinition >(); private final Map< String, InterfaceExtenderDefinition > interfaceExtenders = new HashMap< String, InterfaceExtenderDefinition >(); private final Map< String, TypeDefinition > definedTypes; private final ClassLoader classLoader; private InterfaceExtenderDefinition currInterfaceExtender = null; public OLParser( Scanner scanner, String[] includePaths, ClassLoader classLoader ) { super( scanner ); ParsingContext context = new URIParsingContext( scanner.source(), 0 ); this.program = new Program( context ); this.includePaths = includePaths; this.classLoader = classLoader; this.definedTypes = createTypeDeclarationMap( context ); } public void putConstants( Map< String, Scanner.Token > constantsToPut ) { constantsMap.putAll( constantsToPut ); } public static Map< String, TypeDefinition > createTypeDeclarationMap( ParsingContext context ) { Map< String, TypeDefinition > definedTypes = new HashMap< String, TypeDefinition >(); // Fill in defineTypes with all the supported native types (string, int, double, ...) for( NativeType type : NativeType.values() ) { definedTypes.put( type.id(), new TypeInlineDefinition( context, type.id(), type, Constants.RANGE_ONE_TO_ONE ) ); } definedTypes.put( TypeDefinitionUndefined.UNDEFINED_KEYWORD, TypeDefinitionUndefined.getInstance() ); return definedTypes; } public Program parse() throws IOException, ParserException { _parse(); if ( initSequence != null ) { program.addChild( new DefinitionNode( getContext(), "init", initSequence ) ); } if ( main != null ) { program.addChild( main ); } return program; } private void _parse() throws IOException, ParserException { getToken(); Scanner.Token t; do { t = token; parseInclude(); parseConstants(); parseInclude(); parseExecution(); parseInclude(); parseCorrelationSets(); parseInclude(); parseTypes(); parseInclude(); parseInterfaceOrPort(); parseInclude(); parseEmbedded(); parseInclude(); parseCode(); } while( t != token ); if ( t.isNot( Scanner.TokenType.EOF ) ) { throwException( "Invalid token encountered" ); } } private void parseTypes() throws IOException, ParserException { String typeName; TypeDefinition currentType; program.addChild( TypeDefinitionUndefined.getInstance() ); while( token.isKeyword( "type" ) ) { getToken(); typeName = token.content(); eat( Scanner.TokenType.ID, "expected type name" ); eat( Scanner.TokenType.COLON, "expected COLON (cardinality not allowed in root type declaration, it is fixed to [1,1])" ); NativeType nativeType = readNativeType(); if ( nativeType == null ) { // It's a user-defined type currentType = new TypeDefinitionLink( getContext(), typeName, Constants.RANGE_ONE_TO_ONE, token.content() ); getToken(); } else { currentType = new TypeInlineDefinition( getContext(), typeName, nativeType, Constants.RANGE_ONE_TO_ONE ); getToken(); if ( token.is( Scanner.TokenType.LCURLY ) ) { // We have sub-types to parse parseSubTypes( (TypeInlineDefinition)currentType ); } } // Keep track of the root types to support them in successive type declarations definedTypes.put( typeName, currentType ); program.addChild( currentType ); } } private NativeType readNativeType() { if ( token.is( Scanner.TokenType.CAST_INT ) ) { return NativeType.INT; } else if ( token.is( Scanner.TokenType.CAST_DOUBLE ) ) { return NativeType.DOUBLE; } else if ( token.is( Scanner.TokenType.CAST_STRING ) ) { return NativeType.STRING; } else if ( token.is( Scanner.TokenType.CAST_LONG ) ) { return NativeType.LONG; } else if ( token.is( Scanner.TokenType.CAST_BOOL ) ) { return NativeType.BOOL; } else { return NativeType.fromString( token.content() ); } } private void parseSubTypes( TypeInlineDefinition type ) throws IOException, ParserException { eat( Scanner.TokenType.LCURLY, "expected {" ); if ( token.is( Scanner.TokenType.QUESTION_MARK ) ) { type.setUntypedSubTypes( true ); getToken(); } else { TypeDefinition currentSubType; while( !token.is( Scanner.TokenType.RCURLY ) ) { currentSubType = parseSubType(); if ( type.hasSubType( currentSubType.id() ) ) { throwException( "sub-type " + currentSubType.id() + " conflicts with another sub-type with the same name" ); } type.putSubType( currentSubType ); } } eat( Scanner.TokenType.RCURLY, "RCURLY expected" ); } private TypeDefinition parseSubType() throws IOException, ParserException { eat( Scanner.TokenType.DOT, "sub-type syntax error (dot not found)" ); // SubType id String id = token.content(); eatIdentifier( "expected type name" ); Range cardinality = parseCardinality(); eat( Scanner.TokenType.COLON, "expected COLON" ); NativeType nativeType = readNativeType(); if ( nativeType == null ) { // It's a user-defined type TypeDefinitionLink linkedSubType = new TypeDefinitionLink( getContext(), id, cardinality, token.content() ); getToken(); return linkedSubType; } else { getToken(); TypeInlineDefinition inlineSubType = new TypeInlineDefinition( getContext(), id, nativeType, cardinality ); if ( token.is( Scanner.TokenType.LCURLY ) ) { // Has ulterior sub-types parseSubTypes( inlineSubType ); } return inlineSubType; } } private Range parseCardinality() throws IOException, ParserException { int min = -1; int max = -1; if ( token.is( Scanner.TokenType.COLON ) ) { // Default (no cardinality specified) min = 1; max = 1; } else if ( token.is( Scanner.TokenType.QUESTION_MARK ) ) { min = 0; max = 1; getToken(); } else if ( token.is( Scanner.TokenType.ASTERISK ) ) { min = 0; max = Integer.MAX_VALUE; getToken(); } else if ( token.is( Scanner.TokenType.LSQUARE ) ) { getToken(); // eat [ // Minimum assertToken( Scanner.TokenType.INT, "expected int value" ); min = Integer.parseInt( token.content() ); if ( min < 0 ) { throwException( "Minimum number of occurences of a sub-type must be positive or zero" ); } getToken(); eat( Scanner.TokenType.COMMA, "expected comma separator" ); // Maximum if ( token.is( Scanner.TokenType.INT ) ) { max = Integer.parseInt( token.content() ); if ( max < 1 ) { throwException( "Maximum number of occurences of a sub-type must be positive" ); } } else if ( token.is( Scanner.TokenType.ASTERISK ) ) { max = Integer.MAX_VALUE; } else { throwException( "Maximum number of sub-type occurences not valid: " + token.content() ); } getToken(); eat( Scanner.TokenType.RSQUARE, "expected ]" ); } else { throwException( "Sub-type cardinality syntax error" ); } return new Range( min, max ); } private void parseEmbedded() throws IOException, ParserException { if ( token.isKeyword( "embedded" ) ) { String servicePath, portId; getToken(); eat( Scanner.TokenType.LCURLY, "expected {" ); boolean keepRun = true; Constants.EmbeddedServiceType type; while ( keepRun ) { type = null; if ( token.isKeyword( "Java" ) ) { type = Constants.EmbeddedServiceType.JAVA; } else if ( token.isKeyword( "Jolie" ) ) { type = Constants.EmbeddedServiceType.JOLIE; } else if ( token.isKeyword( "JavaScript" ) ) { type = Constants.EmbeddedServiceType.JAVASCRIPT; } if ( type == null ) { keepRun = false; } else { getToken(); eat( Scanner.TokenType.COLON, "expected : after embedded service type" ); checkConstant(); while ( token.is( Scanner.TokenType.STRING ) ) { servicePath = token.content(); getToken(); if ( token.isKeyword( "in" ) ) { eatKeyword( "in", "expected in" ); assertToken( Scanner.TokenType.ID, "expected output port name" ); portId = token.content(); getToken(); } else { portId = null; } program.addChild( new EmbeddedServiceNode( getContext(), type, servicePath, portId ) ); if ( token.is( Scanner.TokenType.COMMA ) ) { getToken(); } else { break; } } } } eat( Scanner.TokenType.RCURLY, "expected }" ); } } private void parseCorrelationSets() throws IOException, ParserException { while( token.isKeyword( "cset" ) ) { getToken(); /*assertToken( Scanner.TokenType.ID, "expected correlation set name" ); String csetName = token.content(); getToken();*/ eat( Scanner.TokenType.LCURLY, "expected {" ); List< CorrelationVariableInfo > variables = new ArrayList< CorrelationVariableInfo >(); List< CorrelationAliasInfo > aliases; VariablePathNode correlationVariablePath; String typeName; while ( token.is( Scanner.TokenType.ID ) ) { aliases = new LinkedList< CorrelationAliasInfo >(); correlationVariablePath = parseVariablePath(); eat( Scanner.TokenType.COLON, "expected correlation variable alias list" ); assertToken( Scanner.TokenType.ID, "expected correlation variable alias" ); while ( token.is( Scanner.TokenType.ID ) ) { typeName = token.content(); getToken(); eat( Scanner.TokenType.DOT, "expected . after message type name in correlation alias" ); aliases.add( new CorrelationAliasInfo( typeName, parseVariablePath() ) ); } variables.add( new CorrelationVariableInfo( correlationVariablePath, aliases ) ); if ( token.is( Scanner.TokenType.COMMA ) ) { getToken(); } else { break; } } program.addChild( new CorrelationSetInfo( getContext(), variables ) ); eat( Scanner.TokenType.RCURLY, "expected }" ); } } private void parseExecution() throws IOException, ParserException { if ( token.is( Scanner.TokenType.EXECUTION ) ) { Constants.ExecutionMode mode = Constants.ExecutionMode.SEQUENTIAL; getToken(); eat( Scanner.TokenType.LCURLY, "{ expected" ); assertToken( Scanner.TokenType.ID, "expected execution modality" ); if ( "sequential".equals( token.content() ) ) { mode = Constants.ExecutionMode.SEQUENTIAL; } else if ( "concurrent".equals( token.content() ) ) { mode = Constants.ExecutionMode.CONCURRENT; } else if ( "single".equals( token.content() ) ) { mode = Constants.ExecutionMode.SINGLE; } else { throwException( "Expected execution mode, found " + token.content() ); } program.addChild( new ExecutionInfo( getContext(), mode ) ); getToken(); eat( Scanner.TokenType.RCURLY, "} expected" ); } } private void parseConstants() throws IOException, ParserException { if ( token.is( Scanner.TokenType.CONSTANTS ) ) { getToken(); eat( Scanner.TokenType.LCURLY, "expected {" ); boolean keepRun = true; while ( token.is( Scanner.TokenType.ID ) && keepRun ) { String cId = token.content(); getToken(); eat( Scanner.TokenType.ASSIGN, "expected =" ); if ( token.isValidConstant() == false ) { throwException( "expected string, integer, double or identifier constant" ); } if ( constantsMap.containsKey( cId ) == false ) { constantsMap.put( cId, token ); } getToken(); if ( token.isNot( Scanner.TokenType.COMMA ) ) { keepRun = false; } else { getToken(); } } eat( Scanner.TokenType.RCURLY, "expected }" ); } } private String parseIdWithPossibleDot() throws IOException, ParserException{ String name = token.content(); getToken(); // parsing that allows "." in token names boolean parseMore = true; while (parseMore) { if (token.isNot(Scanner.TokenType.DOT) && !token.isIdentifier()) { parseMore = false; } else { if (token.is(Scanner.TokenType.DOT)) { name = name + "."; } else if (token.isIdentifier()) { name = name + token.content(); } getToken(); } } return name; } private static class IncludeFile { private final InputStream inputStream; private final String parentPath; private IncludeFile( InputStream inputStream, String parentPath ) { this.inputStream = inputStream; this.parentPath = parentPath; } private InputStream getInputStream() { return inputStream; } private String getParentPath() { return parentPath; } } private static IncludeFile retrieveIncludeFile( String path, String filename ) { IncludeFile ret = null; File f = new File( new StringBuilder() .append( path ) .append( Constants.fileSeparator ) .append( filename ) .toString() ); try { ret = new IncludeFile( new BufferedInputStream( new FileInputStream( f ) ), f.getParent() ); } catch( FileNotFoundException e ) { try { String urlStr = new StringBuilder() .append( path ) .append( filename ) .toString(); URL url = null; if ( urlStr.startsWith( "jap:" ) || urlStr.startsWith( "jar:" ) ) { /* * We need the embedded URL path, otherwise URI.normalize * is going to do nothing. */ url = new URL( urlStr.substring( 0,4 ) + new URI( urlStr.substring( 4 ) ).normalize().toString() ); } else { url = new URL( new URI( urlStr ).normalize().toString() ); } ret = new IncludeFile( url.openStream(), path ); } catch( MalformedURLException mue ) { } catch( IOException ioe ) { } catch( URISyntaxException use ) {} } return ret; } private void parseInclude() throws IOException, ParserException { String[] origIncludePaths; IncludeFile includeFile; while ( token.is( Scanner.TokenType.INCLUDE ) ) { getToken(); Scanner oldScanner = scanner(); assertToken( Scanner.TokenType.STRING, "expected filename to include" ); String includeStr = token.content(); includeFile = null; // Try the same directory of the program file first. if ( includePaths.length > 1 ) { includeFile = retrieveIncludeFile( includePaths[0], includeStr ); } if ( includeFile == null ) { URL includeURL = classLoader.getResource( includeStr ); if ( includeURL != null ) { includeFile = new IncludeFile( includeURL.openStream(), null ); } } for ( int i = 1; i < includePaths.length && includeFile == null; i++ ) { includeFile = retrieveIncludeFile( includePaths[i], includeStr ); } if ( includeFile == null ) { throwException( "File not found: " + includeStr ); } origIncludePaths = includePaths; try { setScanner( new Scanner( includeFile.getInputStream(), new URI( "file:" + includeStr ) ) ); } catch( URISyntaxException e ) { throw new IOException( e ); } if ( includeFile.getParentPath() == null ) { includePaths = Arrays.copyOf( origIncludePaths, origIncludePaths.length ); } else { includePaths = Arrays.copyOf( origIncludePaths, origIncludePaths.length + 1 ); includePaths[ origIncludePaths.length ] = includeFile.getParentPath(); } _parse(); includePaths = origIncludePaths; setScanner( oldScanner ); getToken(); } } private boolean checkConstant() { if ( token.is( Scanner.TokenType.ID ) ) { Scanner.Token t = null; Constants.Predefined p = Constants.Predefined.get( token.content() ); if ( p != null ) { t = p.token(); } else { t = constantsMap.get( token.content() ); } if ( t != null ) { token = t; return true; } } return false; } private PortInfo parsePort() throws IOException, ParserException { PortInfo portInfo = null; if ( token.isKeyword( "inputPort" ) ) { portInfo = parseInputPortInfo(); } else if ( token.isKeyword( "outputPort" ) ) { getToken(); assertToken( Scanner.TokenType.ID, "expected output port identifier" ); String name = parseIdWithPossibleDot(); OutputPortInfo p = new OutputPortInfo(getContext(), name); /*OutputPortInfo p = new OutputPortInfo( getContext(), token.content() ); getToken();*/ eat( Scanner.TokenType.LCURLY, "expected {" ); parseOutputPortInfo( p ); program.addChild( p ); eat( Scanner.TokenType.RCURLY, "expected }" ); portInfo = p; } return portInfo; } private void parseInterfaceOrPort() throws IOException, ParserException { String comment = ""; boolean keepRun = true; DocumentedNode node = null; boolean commentsPreset = false; while( keepRun ) { if ( token.is( Scanner.TokenType.DOCUMENTATION_COMMENT ) ) { commentsPreset = true; comment = token.content(); getToken(); } else if ( token.isKeyword( "interface" ) ) { getToken(); if ( token.isKeyword( "extender") ) { getToken(); node = parseInterfaceExtender(); } else { node = parseInterface(); } if ( commentsPreset && node != null ) { node.setDocumentation( comment ); commentsPreset = false; node = null; } } else if ( token.isKeyword( "inputPort" ) ) { node = parsePort(); if ( commentsPreset && node != null ) { node.setDocumentation( comment ); commentsPreset = false; node = null; } } else if ( token.isKeyword( "outputPort" ) ) { node = parsePort(); if ( commentsPreset && node != null ) { node.setDocumentation( comment ); commentsPreset = false; node = null; } } else { keepRun = false; } } } private InputPortInfo parseInputPortInfo() throws IOException, ParserException { String inputPortName; String protocolId; URI inputPortLocation; List< InterfaceDefinition > interfaceList = new ArrayList< InterfaceDefinition >(); OLSyntaxNode protocolConfiguration = new NullProcessStatement( getContext() ); boolean messageBus = false; getToken(); assertToken( Scanner.TokenType.ID, "expected inputPort name" ); /*inputPortName = token.content(); getToken();*/ inputPortName = parseIdWithPossibleDot(); eat( Scanner.TokenType.LCURLY, "{ expected" ); InterfaceDefinition iface = new InterfaceDefinition( getContext(), "Internal interface for: " + inputPortName ); inputPortLocation = null; protocolId = null; Map<String, String> redirectionMap = new HashMap<String, String>(); List< InputPortInfo.AggregationItemInfo > aggregationList = new LinkedList< InputPortInfo.AggregationItemInfo >(); while ( token.isNot( Scanner.TokenType.RCURLY ) ) { if ( token.is( Scanner.TokenType.OP_OW ) ) { parseOneWayOperations( iface ); } else if ( token.is( Scanner.TokenType.OP_RR ) ) { parseRequestResponseOperations( iface ); } else if ( token.isKeyword( "Location" ) ) { if ( inputPortLocation != null ) { throwException( "Location already defined for service " + inputPortName ); } getToken(); eat( Scanner.TokenType.COLON, "expected : after Location" ); checkConstant(); assertToken( Scanner.TokenType.STRING, "expected inputPort location string" ); try { inputPortLocation = new URI( token.content() ); } catch ( URISyntaxException e ) { throwException( e ); } getToken(); } else if ( token.isKeyword( "Interfaces" ) ) { getToken(); eat( Scanner.TokenType.COLON, "expected : after Interfaces" ); boolean keepRun = true; while( keepRun ) { assertToken( Scanner.TokenType.ID, "expected interface name" ); //InterfaceDefinition i = interfaces.get( token.content() ); String interfaceName = parseIdWithPossibleDot(); InterfaceDefinition i = interfaces.get(interfaceName); if ( i == null ) { throwException( "Invalid interface name: " + token.content() ); } i.copyTo( iface ); interfaceList.add( i ); //getToken(); if ( token.is( Scanner.TokenType.COMMA ) ) { getToken(); } else { keepRun = false; } } } else if ( token.isKeyword( "Protocol" ) ) { if ( protocolId != null ) { throwException( "Protocol already defined for inputPort " + inputPortName ); } getToken(); eat( Scanner.TokenType.COLON, "expected :" ); checkConstant(); assertToken( Scanner.TokenType.ID, "expected protocol identifier" ); protocolId = token.content(); getToken(); if ( token.is( Scanner.TokenType.LCURLY ) ) { addTokens( Arrays.asList( new Scanner.Token( Scanner.TokenType.ID, Constants.GLOBAL ), new Scanner.Token( Scanner.TokenType.DOT ), new Scanner.Token( Scanner.TokenType.ID, Constants.INPUT_PORTS_NODE_NAME ), new Scanner.Token( Scanner.TokenType.DOT ), new Scanner.Token( Scanner.TokenType.ID, inputPortName ), new Scanner.Token( Scanner.TokenType.DOT ), new Scanner.Token( Scanner.TokenType.ID, Constants.PROTOCOL_NODE_NAME ), token ) ); // Protocol configuration getToken(); protocolConfiguration = parseInVariablePathProcess( false ); } } else if ( token.isKeyword( "Redirects" ) ) { getToken(); eat( Scanner.TokenType.COLON, "expected :" ); String subLocationName; while ( token.is( Scanner.TokenType.ID ) ) { subLocationName = token.content(); getToken(); eat( Scanner.TokenType.ARROW, "expected =>" ); assertToken( Scanner.TokenType.ID, "expected outputPort identifier" ); redirectionMap.put( subLocationName, token.content() ); getToken(); if ( token.is( Scanner.TokenType.COMMA ) ) { getToken(); } else { break; } } } else if ( token.isKeyword( "Aggregates" ) ) { getToken(); eat( Scanner.TokenType.COLON, "expected :" ); parseAggregationList( aggregationList ); } else if ( token.isKeyword("MessageBus")){ getToken(); eat( Scanner.TokenType.COLON, "expected :" ); if(token.is(Scanner.TokenType.TRUE) || token.is(Scanner.TokenType.FALSE)){ messageBus = Boolean.valueOf(token.content()); if(messageBus){ eat(Scanner.TokenType.TRUE,"expected boolean value true|false"); } else { eat(Scanner.TokenType.FALSE,"expected boolean value true|false"); } } else { throwException("Expected boolean value true|false"); } } else { throwException( "Unrecognized token in inputPort " + inputPortName ); } } eat( Scanner.TokenType.RCURLY, "} expected" ); if ( inputPortLocation == null ) { throwException( "expected location URI for " + inputPortName ); } else if ( iface.operationsMap().isEmpty() && redirectionMap.isEmpty() && aggregationList.isEmpty() ) { throwException( "expected at least one operation, interface, aggregation or redirection for inputPort " + inputPortName ); } else if ( protocolId == null && !inputPortLocation.toString().equals( Constants.LOCAL_LOCATION_KEYWORD ) ) { throwException( "expected protocol for inputPort " + inputPortName ); } InputPortInfo iport = new InputPortInfo( getContext(), inputPortName, inputPortLocation, protocolId, protocolConfiguration, aggregationList.toArray( new InputPortInfo.AggregationItemInfo[ aggregationList.size() ] ), redirectionMap, messageBus ); for( InterfaceDefinition i : interfaceList ) { iport.addInterface( i ); } iface.copyTo( iport ); program.addChild( iport ); return iport; } private void parseAggregationList( List< InputPortInfo.AggregationItemInfo > aggregationList ) throws ParserException, IOException { List< String > outputPortNames; InterfaceExtenderDefinition extender; for( boolean mainKeepRun = true; mainKeepRun; ) { extender = null; outputPortNames = new LinkedList< String >(); if ( token.is( Scanner.TokenType.LCURLY ) ) { getToken(); for( boolean keepRun = true; keepRun; ) { assertToken( Scanner.TokenType.ID, "expected output port name" ); outputPortNames.add( token.content() ); getToken(); if ( token.is( Scanner.TokenType.COMMA ) ) { getToken(); } else if ( token.is( Scanner.TokenType.RCURLY ) ) { keepRun = false; getToken(); } else { throwException( "unexpected token " + token.type() ); } } } else { assertToken( Scanner.TokenType.ID, "expected output port name" ); outputPortNames.add( token.content() ); getToken(); } if ( token.is( Scanner.TokenType.WITH ) ) { getToken(); assertToken( Scanner.TokenType.ID, "expected interface extender name" ); extender = interfaceExtenders.get( token.content() ); if ( extender == null ) { throwException( "undefined interface extender: " + token.content() ); } getToken(); } aggregationList.add( new InputPortInfo.AggregationItemInfo( outputPortNames.toArray( new String[ outputPortNames.size() ] ), extender ) ); if ( token.is( Scanner.TokenType.COMMA ) ) { getToken(); } else { mainKeepRun = false; } } } private InterfaceDefinition parseInterfaceExtender() throws IOException, ParserException { String name; assertToken( Scanner.TokenType.ID, "expected interface extender name" ); name = parseIdWithPossibleDot(); //getToken(); eat( Scanner.TokenType.LCURLY, "expected {" ); InterfaceExtenderDefinition extender = currInterfaceExtender = new InterfaceExtenderDefinition( getContext(), name ); parseOperations( currInterfaceExtender ); interfaceExtenders.put( name, extender ); program.addChild( currInterfaceExtender ); eat( Scanner.TokenType.RCURLY, "expected }" ); currInterfaceExtender = null; return extender; } private InterfaceDefinition parseInterface() throws IOException, ParserException { String name; InterfaceDefinition iface = null; assertToken( Scanner.TokenType.ID, "expected interface name" ); name = parseIdWithPossibleDot(); //getToken(); eat( Scanner.TokenType.LCURLY, "expected {" ); iface = new InterfaceDefinition( getContext(), name ); parseOperations( iface ); interfaces.put( name, iface ); program.addChild( iface ); eat( Scanner.TokenType.RCURLY, "expected }" ); return iface; } private void parseOperations( OperationCollector oc ) throws IOException, ParserException { boolean keepRun = true; while( keepRun ) { if ( token.is( Scanner.TokenType.OP_OW ) ) { parseOneWayOperations( oc ); } else if ( token.is( Scanner.TokenType.OP_RR ) ) { parseRequestResponseOperations( oc ); } else { keepRun = false; } } } private void parseOutputPortInfo( OutputPortInfo p ) throws IOException, ParserException { boolean keepRun = true; boolean messageBus = false; while ( keepRun ) { if ( token.is( Scanner.TokenType.OP_OW ) ) { parseOneWayOperations( p ); } else if ( token.is( Scanner.TokenType.OP_RR ) ) { parseRequestResponseOperations( p ); } else if ( token.isKeyword( "Interfaces" ) ) { getToken(); eat( Scanner.TokenType.COLON, "expected : after Interfaces" ); boolean r = true; String name; while( r ) { name = parseIdWithPossibleDot(); /*assertToken( Scanner.TokenType.ID, "expected interface name" ); InterfaceDefinition i = interfaces.get( token.content() );*/ InterfaceDefinition i = interfaces.get(name); if ( i == null ) { throwException( "Invalid interface name: " + token.content() ); } i.copyTo( p ); p.addInterface( i ); //OutputPortInfo( getContext(), token.content() )getToken(); if ( token.is( Scanner.TokenType.COMMA ) ) { getToken(); } else { r = false; } } } else if ( token.isKeyword( "Location" ) ) { if ( p.location() != null ) { throwException( "Location already defined for output port " + p.id() ); } getToken(); eat( Scanner.TokenType.COLON, "expected :" ); checkConstant(); assertToken( Scanner.TokenType.STRING, "expected location string" ); URI location = null; try { location = new URI( token.content() ); } catch ( URISyntaxException e ) { throwException( e ); } p.setLocation( location ); getToken(); } else if ( token.isKeyword( "Protocol" ) ) { if ( p.protocolId() != null ) { throwException( "Protocol already defined for output port " + p.id() ); } getToken(); eat( Scanner.TokenType.COLON, "expected :" ); checkConstant(); assertToken( Scanner.TokenType.ID, "expected protocol identifier" ); p.setProtocolId( token.content() ); getToken(); if ( token.is( Scanner.TokenType.LCURLY ) ) { addTokens( Arrays.asList( new Scanner.Token( Scanner.TokenType.ID, p.id() ), new Scanner.Token( Scanner.TokenType.DOT ), new Scanner.Token( Scanner.TokenType.ID, "protocol" ), token ) ); // Protocol configuration getToken(); p.setProtocolConfiguration( parseInVariablePathProcess( false ) ); } } else if ( token.isKeyword("MessageBus")){ getToken(); eat( Scanner.TokenType.COLON, "expected :" ); if(token.is(Scanner.TokenType.TRUE) || token.is(Scanner.TokenType.FALSE)){ messageBus = Boolean.valueOf(token.content()); if(messageBus){ eat(Scanner.TokenType.TRUE,"expected boolean value true|false"); p.setMessageBus(true); } else { eat(Scanner.TokenType.FALSE,"expected boolean value true|false"); p.setMessageBus(false); } } } else { keepRun = false; } } } private void parseOneWayOperations( OperationCollector oc ) throws IOException, ParserException { getToken(); eat( Scanner.TokenType.COLON, "expected :" ); boolean keepRun = true; boolean commentsPreset = false; String comment = ""; String opId; while( keepRun ) { checkConstant(); if ( token.is( Scanner.TokenType.DOCUMENTATION_COMMENT ) ) { commentsPreset = true; comment = token.content(); getToken(); } else if ( token.is( Scanner.TokenType.ID ) || ( currInterfaceExtender != null && token.is( Scanner.TokenType.ASTERISK ) ) ) { opId = token.content(); OneWayOperationDeclaration opDecl = new OneWayOperationDeclaration( getContext(), opId ); getToken(); opDecl.setRequestType( TypeDefinitionUndefined.getInstance() ); if ( token.is( Scanner.TokenType.LPAREN ) ) { // Type declaration getToken(); //eat ( if ( definedTypes.containsKey( token.content() ) == false ) { throwException( "invalid type: " + token.content() ); } opDecl.setRequestType( definedTypes.get( token.content() ) ); getToken(); // eat the type name eat( Scanner.TokenType.RPAREN, "expected )" ); } if ( commentsPreset ) { opDecl.setDocumentation( comment ); commentsPreset = false; } if ( currInterfaceExtender != null && opId.equals( "*" ) ) { currInterfaceExtender.setDefaultOneWayOperation( opDecl ); } else { oc.addOperation( opDecl ); } if ( token.is( Scanner.TokenType.COMMA ) ) { getToken(); } else { keepRun = false; } } else { keepRun = false; } } } private void parseRequestResponseOperations( OperationCollector oc ) throws IOException, ParserException { getToken(); eat( Scanner.TokenType.COLON, "expected :" ); boolean keepRun = true; String comment = ""; String opId; boolean commentsPreset = false; while( keepRun ) { checkConstant(); if ( token.is( Scanner.TokenType.DOCUMENTATION_COMMENT ) ) { commentsPreset = true; comment = token.content(); getToken(); } else if ( token.is( Scanner.TokenType.ID ) || ( currInterfaceExtender != null && token.is( Scanner.TokenType.ASTERISK ) ) ) { opId = token.content(); getToken(); String requestTypeName = TypeDefinitionUndefined.UNDEFINED_KEYWORD; String responseTypeName = TypeDefinitionUndefined.UNDEFINED_KEYWORD; if ( token.is( Scanner.TokenType.LPAREN ) ) { getToken(); //eat ( requestTypeName = token.content(); getToken(); eat( Scanner.TokenType.RPAREN, "expected )" ); eat( Scanner.TokenType.LPAREN, "expected (" ); responseTypeName = token.content(); getToken(); eat( Scanner.TokenType.RPAREN, "expected )" ); } Map< String, TypeDefinition > faultTypesMap = new HashMap< String, TypeDefinition >(); if ( token.is( Scanner.TokenType.THROWS ) ) { getToken(); while( token.is( Scanner.TokenType.ID ) ) { String faultName = token.content(); String faultTypeName = TypeDefinitionUndefined.UNDEFINED_KEYWORD; getToken(); if ( token.is( Scanner.TokenType.LPAREN ) ) { getToken(); //eat ( faultTypeName = token.content(); if ( definedTypes.containsKey( faultTypeName ) == false ) { throwException( "invalid type: " + faultTypeName ); } getToken(); eat( Scanner.TokenType.RPAREN, "expected )" ); } faultTypesMap.put( faultName, definedTypes.get( faultTypeName ) ); } } if ( definedTypes.containsKey( requestTypeName ) == false ) { throwException( "invalid type: " + requestTypeName ); } if ( definedTypes.containsKey( responseTypeName ) == false ) { throwException( "invalid type: " + responseTypeName ); } RequestResponseOperationDeclaration opRR = new RequestResponseOperationDeclaration( getContext(), opId, definedTypes.get( requestTypeName ), definedTypes.get( responseTypeName ), faultTypesMap ); // adding documentation if ( commentsPreset ) { opRR.setDocumentation( comment ); commentsPreset = false; } if ( currInterfaceExtender != null && opId.equals( "*" ) ) { currInterfaceExtender.setDefaultRequestResponseOperation( opRR ); } else { oc.addOperation( opRR ); } if ( token.is( Scanner.TokenType.COMMA ) ) { getToken(); } else { keepRun = false; } } else { keepRun = false; } } } private SequenceStatement initSequence = null; private DefinitionNode main = null; private void parseCode() throws IOException, ParserException { boolean keepRun = true; do { if ( token.is( Scanner.TokenType.DEFINE ) ) { program.addChild( parseDefinition() ); } else if ( token.isKeyword( "courier" ) ) { program.addChild( parseCourierDefinition() ); } else if ( token.isKeyword( "main" ) ) { if ( main != null ) { throwException( "you must specify only one main definition" ); } main = parseMain(); } else if ( token.is( Scanner.TokenType.INIT ) ) { if ( initSequence == null ) { initSequence = new SequenceStatement( getContext() ); } initSequence.addChild( parseInit() ); } else { keepRun = false; } } while ( keepRun ); } private DefinitionNode parseMain() throws IOException, ParserException { getToken(); eat( Scanner.TokenType.LCURLY, "expected { after procedure identifier" ); DefinitionNode retVal = new DefinitionNode( getContext(), "main", parseProcess() ); eat( Scanner.TokenType.RCURLY, "expected } after procedure definition" ); return retVal; } private OLSyntaxNode parseInit() throws IOException, ParserException { getToken(); eat( Scanner.TokenType.LCURLY, "expected { after procedure identifier" ); OLSyntaxNode retVal = parseProcess(); eat( Scanner.TokenType.RCURLY, "expected } after procedure definition" ); return retVal; } private DefinitionNode parseDefinition() throws IOException, ParserException { getToken(); assertToken( Scanner.TokenType.ID, "expected definition identifier" ); String definitionId = token.content(); getToken(); eat( Scanner.TokenType.LCURLY, "expected { after definition declaration" ); DefinitionNode retVal = new DefinitionNode( getContext(), definitionId, parseProcess() ); eat( Scanner.TokenType.RCURLY, "expected } after definition declaration" ); return retVal; } private CourierDefinitionNode parseCourierDefinition() throws IOException, ParserException { getToken(); assertToken( Scanner.TokenType.ID, "expected input port identifier" ); String inputPortName = token.content(); getToken(); eat( Scanner.TokenType.LCURLY, "expected { after courier definition" ); CourierDefinitionNode retVal = new CourierDefinitionNode( getContext(), inputPortName, parseCourierChoice() ); eat( Scanner.TokenType.RCURLY, "expected } after courier definition" ); return retVal; } public OLSyntaxNode parseProcess() throws IOException, ParserException { return parseParallelStatement(); } private ParallelStatement parseParallelStatement() throws IOException, ParserException { ParallelStatement stm = new ParallelStatement( getContext() ); stm.addChild( parseSequenceStatement() ); while ( token.is( Scanner.TokenType.PARALLEL ) ) { getToken(); stm.addChild( parseSequenceStatement() ); } return stm; } private SequenceStatement parseSequenceStatement() throws IOException, ParserException { SequenceStatement stm = new SequenceStatement( getContext() ); stm.addChild( parseBasicStatement() ); while ( token.is( Scanner.TokenType.SEQUENCE ) ) { getToken(); stm.addChild( parseBasicStatement() ); } return stm; } private List< List< Scanner.Token > > inVariablePaths = new LinkedList< List< Scanner.Token > >(); private OLSyntaxNode parseInVariablePathProcess( boolean withConstruct ) throws IOException, ParserException { OLSyntaxNode ret = null; List< Scanner.Token> tokens = new LinkedList< Scanner.Token>(); if ( withConstruct ) { eat( Scanner.TokenType.LPAREN, "expected (" ); while( token.isNot( Scanner.TokenType.LCURLY ) ) { tokens.add( token ); getToken(); } //TODO transfer this whole buggy thing to the OOIT tokens.remove( tokens.size() - 1 ); //getToken(); } else { while( token.isNot( Scanner.TokenType.LCURLY ) ) { tokens.add( token ); getToken(); } } inVariablePaths.add( tokens ); eat( Scanner.TokenType.LCURLY, "expected {" ); ret = parseProcess(); eat( Scanner.TokenType.RCURLY, "expected }" ); inVariablePaths.remove( inVariablePaths.size() - 1 ); return ret; } private OLSyntaxNode parseBasicStatement() throws IOException, ParserException { OLSyntaxNode retVal = null; if ( token.is( Scanner.TokenType.LSQUARE ) ) { retVal = parseNDChoiceStatement(); } else if ( token.is( Scanner.TokenType.ID ) ) { checkConstant(); String id = token.content(); getToken(); if ( token.is( Scanner.TokenType.COLON ) || token.is( Scanner.TokenType.LSQUARE ) || token.is( Scanner.TokenType.DOT ) || token.is( Scanner.TokenType.ASSIGN ) || token.is( Scanner.TokenType.ADD_ASSIGN ) || token.is( Scanner.TokenType.MINUS_ASSIGN ) || token.is( Scanner.TokenType.MULTIPLY_ASSIGN ) || token.is( Scanner.TokenType.DIVIDE_ASSIGN ) || token.is( Scanner.TokenType.POINTS_TO ) || token.is( Scanner.TokenType.DEEP_COPY_LEFT ) || token.is( Scanner.TokenType.DECREMENT ) || token.is( Scanner.TokenType.INCREMENT ) ) { retVal = parseAssignOrDeepCopyOrPointerStatement( _parseVariablePath( id ) ); } else if ( id.equals( "forward" ) && ( token.is( Scanner.TokenType.ID ) || token.is( Scanner.TokenType.LPAREN ) ) ) { retVal = parseForwardStatement(); } else if ( token.is( Scanner.TokenType.LPAREN ) ) { retVal = parseInputOperationStatement( id ); } else if ( token.is( Scanner.TokenType.AT ) ) { getToken(); retVal = parseOutputOperationStatement( id ); } else { retVal = new DefinitionCallStatement( getContext(), id ); } } else if ( token.is( Scanner.TokenType.WITH ) ) { getToken(); retVal = parseInVariablePathProcess( true ); } else if ( token.is( Scanner.TokenType.DOT ) && inVariablePaths.size() > 0 ) { retVal = parseAssignOrDeepCopyOrPointerStatement( parsePrefixedVariablePath() ); } else if ( token.is( Scanner.TokenType.INCREMENT ) ) { // Pre increment: ++i getToken(); retVal = new PreIncrementStatement( getContext(), parseVariablePath() ); } else if ( token.is( Scanner.TokenType.DECREMENT ) ) { // Pre decrement getToken(); retVal = new PreDecrementStatement( getContext(), parseVariablePath() ); } else if ( token.is( Scanner.TokenType.SYNCHRONIZED ) ) { getToken(); eat( Scanner.TokenType.LPAREN, "expected (" ); assertToken( Scanner.TokenType.ID, "expected lock id" ); String id = token.content(); getToken(); eat( Scanner.TokenType.RPAREN, "expected )" ); eat( Scanner.TokenType.LCURLY, "expected {" ); retVal = new SynchronizedStatement( getContext(), id, parseProcess() ); eat( Scanner.TokenType.RCURLY, "expected }" ); } else if ( token.is( Scanner.TokenType.UNDEF ) ) { getToken(); eat( Scanner.TokenType.LPAREN, "expected (" ); checkConstant(); retVal = new UndefStatement( getContext(), parseVariablePath() ); eat( Scanner.TokenType.RPAREN, "expected )" ); } else if ( token.is( Scanner.TokenType.FOR ) ) { getToken(); eat( Scanner.TokenType.LPAREN, "expected (" ); OLSyntaxNode init = parseProcess(); eat( Scanner.TokenType.COMMA, "expected ," ); OLSyntaxNode condition = parseExpression(); eat( Scanner.TokenType.COMMA, "expected ," ); OLSyntaxNode post = parseProcess(); eat( Scanner.TokenType.RPAREN, "expected )" ); OLSyntaxNode body = parseBasicStatement(); retVal = new ForStatement( getContext(), init, condition, post, body ); } else if ( token.is( Scanner.TokenType.SPAWN ) ) { getToken(); eat( Scanner.TokenType.LPAREN, "expected (" ); VariablePathNode indexVariablePath = parseVariablePath(); assertToken( Scanner.TokenType.ID, "expected over" ); if ( token.isKeyword( "over" ) == false ) { throwException( "expected over" ); } getToken(); OLSyntaxNode upperBoundExpression = parseBasicExpression(); eat( Scanner.TokenType.RPAREN, "expected )" ); VariablePathNode inVariablePath = null; if ( token.isKeyword( "in" ) ) { getToken(); inVariablePath = parseVariablePath(); } eat( Scanner.TokenType.LCURLY, "expected {" ); OLSyntaxNode process = parseProcess(); eat( Scanner.TokenType.RCURLY, "expected }" ); retVal = new SpawnStatement( getContext(), indexVariablePath, upperBoundExpression, inVariablePath, process ); } else if ( token.is( Scanner.TokenType.FOREACH ) ) { getToken(); eat( Scanner.TokenType.LPAREN, "expected (" ); VariablePathNode keyPath = parseVariablePath(); eat( Scanner.TokenType.COLON, "expected :" ); VariablePathNode targetPath = parseVariablePath(); eat( Scanner.TokenType.RPAREN, "expected )" ); OLSyntaxNode body = parseBasicStatement(); retVal = new ForEachStatement( getContext(), keyPath, targetPath, body ); } else if ( token.is( Scanner.TokenType.LINKIN ) ) { retVal = parseLinkInStatement(); } else if ( token.is( Scanner.TokenType.CURRENT_HANDLER ) ) { getToken(); retVal = new CurrentHandlerStatement( getContext() ); } else if ( token.is( Scanner.TokenType.NULL_PROCESS ) ) { getToken(); retVal = new NullProcessStatement( getContext() ); } else if ( token.is( Scanner.TokenType.EXIT ) ) { getToken(); retVal = new ExitStatement( getContext() ); } else if ( token.is( Scanner.TokenType.WHILE ) ) { retVal = parseWhileStatement(); } else if ( token.is( Scanner.TokenType.LINKOUT ) ) { getToken(); eat( Scanner.TokenType.LPAREN, "expected (" ); assertToken( Scanner.TokenType.ID, "expected link identifier" ); retVal = new LinkOutStatement( getContext(), token.content() ); getToken(); eat( Scanner.TokenType.RPAREN, "expected )" ); } else if ( token.is( Scanner.TokenType.LPAREN ) ) { getToken(); retVal = parseProcess(); eat( Scanner.TokenType.RPAREN, "expected )" ); } else if ( token.is( Scanner.TokenType.LCURLY ) ) { getToken(); retVal = parseProcess(); eat( Scanner.TokenType.RCURLY, "expected }" ); } else if ( token.is( Scanner.TokenType.SCOPE ) ) { getToken(); eat( Scanner.TokenType.LPAREN, "expected (" ); checkConstant(); assertToken( Scanner.TokenType.ID, "expected scope identifier" ); String id = token.content(); getToken(); eat( Scanner.TokenType.RPAREN, "expected )" ); eat( Scanner.TokenType.LCURLY, "expected {" ); retVal = new Scope( getContext(), id, parseProcess() ); eat( Scanner.TokenType.RCURLY, "expected }" ); } else if ( token.is( Scanner.TokenType.COMPENSATE ) ) { getToken(); eat( Scanner.TokenType.LPAREN, "expected (" ); checkConstant(); assertToken( Scanner.TokenType.ID, "expected scope identifier" ); retVal = new CompensateStatement( getContext(), token.content() ); getToken(); eat( Scanner.TokenType.RPAREN, "expected )" ); } else if ( token.is( Scanner.TokenType.THROW ) ) { getToken(); eat( Scanner.TokenType.LPAREN, "expected (" ); checkConstant(); assertToken( Scanner.TokenType.ID, "expected fault identifier" ); String faultName = token.content(); getToken(); if ( token.is( Scanner.TokenType.RPAREN ) ) { retVal = new ThrowStatement( getContext(), faultName ); } else { eat( Scanner.TokenType.COMMA, "expected , or )" ); OLSyntaxNode expression = parseExpression(); /*assertToken( Scanner.TokenType.ID, "expected variable path" ); String varId = token.content(); getToken(); VariablePathNode path = parseVariablePath( varId );*/ retVal = new ThrowStatement( getContext(), faultName, expression ); } eat( Scanner.TokenType.RPAREN, "expected )" ); } else if ( token.is( Scanner.TokenType.INSTALL ) ) { getToken(); eat( Scanner.TokenType.LPAREN, "expected (" ); retVal = new InstallStatement( getContext(), parseInstallFunction() ); eat( Scanner.TokenType.RPAREN, "expected )" ); } else if ( token.is( Scanner.TokenType.IF ) ) { IfStatement stm = new IfStatement( getContext() ); OLSyntaxNode cond; OLSyntaxNode node; getToken(); eat( Scanner.TokenType.LPAREN, "expected (" ); cond = parseExpression(); eat( Scanner.TokenType.RPAREN, "expected )" ); node = parseBasicStatement(); stm.addChild( new Pair<OLSyntaxNode, OLSyntaxNode>( cond, node ) ); boolean keepRun = true; while ( token.is( Scanner.TokenType.ELSE ) && keepRun ) { getToken(); if ( token.is( Scanner.TokenType.IF ) ) { // else if branch getToken(); eat( Scanner.TokenType.LPAREN, "expected (" ); cond = parseExpression(); eat( Scanner.TokenType.RPAREN, "expected )" ); node = parseBasicStatement(); stm.addChild( new Pair<OLSyntaxNode, OLSyntaxNode>( cond, node ) ); } else { // else branch keepRun = false; stm.setElseProcess( parseBasicStatement() ); } } retVal = stm; } if ( retVal == null ) { throwException( "expected basic statement" ); } return retVal; } private OLSyntaxNode parseForwardStatement() throws IOException, ParserException { OLSyntaxNode retVal; String outputPortName = null; if ( token.is( Scanner.TokenType.ID ) ) { outputPortName = token.content(); getToken(); } VariablePathNode outputVariablePath = parseOperationVariablePathParameter(); if ( token.is( Scanner.TokenType.LPAREN ) ) { // Solicit-Response VariablePathNode inputVariablePath = parseOperationVariablePathParameter(); retVal = new SolicitResponseForwardStatement( getContext(), outputPortName, outputVariablePath, inputVariablePath ); } else { // Notification retVal = new NotificationForwardStatement( getContext(), outputPortName, outputVariablePath ); } return retVal; } private InstallFunctionNode parseInstallFunction() throws IOException, ParserException { boolean backup = insideInstallFunction; insideInstallFunction = true; List< Pair< String, OLSyntaxNode > > vec = new LinkedList< Pair< String, OLSyntaxNode > >(); boolean keepRun = true; List< String > names = new LinkedList< String >(); OLSyntaxNode handler; while( keepRun ) { do { if ( token.is( Scanner.TokenType.THIS ) ) { names.add( null ); } else if ( token.is( Scanner.TokenType.ID ) ) { names.add( token.content() ); } else { throwException( "expected fault identifier or this" ); } getToken(); } while( token.isNot( Scanner.TokenType.ARROW ) ); getToken(); // eat the arrow handler = parseProcess(); for( String name : names ) { vec.add( new Pair< String, OLSyntaxNode >( name, handler ) ); } names.clear(); if ( token.is( Scanner.TokenType.COMMA ) ) { getToken(); } else { keepRun = false; } } insideInstallFunction = backup; return new InstallFunctionNode( vec.toArray( new Pair[ vec.size() ] ) ); } private OLSyntaxNode parseAssignOrDeepCopyOrPointerStatement( VariablePathNode path ) throws IOException, ParserException { OLSyntaxNode retVal = null; if ( token.is( Scanner.TokenType.ASSIGN ) ) { getToken(); retVal = new AssignStatement( getContext(), path, parseExpression() ); } else if ( token.is( Scanner.TokenType.ADD_ASSIGN ) ) { getToken(); retVal = new AddAssignStatement( getContext(), path, parseExpression() ); } else if ( token.is( Scanner.TokenType.MINUS_ASSIGN ) ) { getToken(); retVal = new SubtractAssignStatement( getContext(), path, parseExpression() ); } else if ( token.is( Scanner.TokenType.MULTIPLY_ASSIGN ) ) { getToken(); retVal = new MultiplyAssignStatement( getContext(), path, parseExpression() ); } else if ( token.is( Scanner.TokenType.DIVIDE_ASSIGN ) ) { getToken(); retVal = new DivideAssignStatement( getContext(), path, parseExpression() ); } else if ( token.is( Scanner.TokenType.INCREMENT ) ) { getToken(); retVal = new PostIncrementStatement( getContext(), path ); } else if ( token.is( Scanner.TokenType.DECREMENT ) ) { getToken(); retVal = new PostDecrementStatement( getContext(), path ); } else if ( token.is( Scanner.TokenType.POINTS_TO ) ) { getToken(); retVal = new PointerStatement( getContext(), path, parseVariablePath() ); } else if ( token.is( Scanner.TokenType.DEEP_COPY_LEFT ) ) { getToken(); retVal = new DeepCopyStatement( getContext(), path, parseVariablePath() ); } else { throwException( "expected = or -> or << or -- or ++" ); } return retVal; } private VariablePathNode parseVariablePath() throws ParserException, IOException { if ( token.is( Scanner.TokenType.DOT ) ) { return parsePrefixedVariablePath(); } assertToken( Scanner.TokenType.ID, "Expected variable path" ); String varId = token.content(); getToken(); return _parseVariablePath( varId ); } private VariablePathNode _parseVariablePath( String varId ) throws IOException, ParserException { OLSyntaxNode expr; VariablePathNode path; if ( varId.equals( Constants.GLOBAL ) ) { path = new VariablePathNode( getContext(), Type.GLOBAL ); } else if ( varId.equals( Constants.CSETS ) ) { path = new VariablePathNode( getContext(), Type.CSET ); path.append( new Pair<OLSyntaxNode, OLSyntaxNode>( new ConstantStringExpression( getContext(), varId ), new ConstantIntegerExpression( getContext(), 0 ) ) ); } else { path = new VariablePathNode( getContext(), Type.NORMAL ); if ( token.is( Scanner.TokenType.LSQUARE ) ) { getToken(); expr = parseBasicExpression(); eat( Scanner.TokenType.RSQUARE, "expected ]" ); } else { expr = null; } path.append( new Pair<OLSyntaxNode, OLSyntaxNode>( new ConstantStringExpression( getContext(), varId ), expr ) ); } OLSyntaxNode nodeExpr = null; while ( token.is( Scanner.TokenType.DOT ) ) { getToken(); if ( token.isIdentifier() ) { nodeExpr = new ConstantStringExpression( getContext(), token.content() ); } else if ( token.is( Scanner.TokenType.LPAREN ) ) { getToken(); nodeExpr = parseBasicExpression(); assertToken( Scanner.TokenType.RPAREN, "expected )" ); } else { throwException( "expected nested node identifier" ); } getToken(); if ( token.is( Scanner.TokenType.LSQUARE ) ) { getToken(); expr = parseBasicExpression(); eat( Scanner.TokenType.RSQUARE, "expected ]" ); } else { expr = null; } path.append( new Pair<OLSyntaxNode, OLSyntaxNode>( nodeExpr, expr ) ); } return path; } private VariablePathNode parsePrefixedVariablePath() throws IOException, ParserException { int i = inVariablePaths.size() - 1; List< Scanner.Token > tokens = new ArrayList< Scanner.Token >(); try { tokens.addAll( inVariablePaths.get( i ) ); } catch( IndexOutOfBoundsException e ) { throwException( "Prefixed variable paths must be inside a with block" ); } while ( tokens.get( 0 ).is( Scanner.TokenType.DOT ) ) { i--; tokens.addAll( 0, inVariablePaths.get( i ) ); } addTokens( tokens ); addTokens( Arrays.asList( new Scanner.Token( Scanner.TokenType.DOT ) ) ); getToken(); String varId = token.content(); getToken(); return _parseVariablePath( varId ); } private CourierChoiceStatement parseCourierChoice() throws IOException, ParserException { CourierChoiceStatement stm = new CourierChoiceStatement( getContext() ); OLSyntaxNode body; InterfaceDefinition iface; String operationName; VariablePathNode inputVariablePath, outputVariablePath; while ( token.is( Scanner.TokenType.LSQUARE ) ) { iface = null; operationName = null; inputVariablePath = null; outputVariablePath = null; getToken(); if ( token.isKeyword( "interface" ) ) { getToken(); assertToken( Scanner.TokenType.ID, "expected interface name" ); iface = interfaces.get( token.content() ); if ( iface == null ) { throwException( "undefined interface: " + token.content() ); } getToken(); inputVariablePath = parseOperationVariablePathParameter(); if ( inputVariablePath == null ) { throwException( "expected variable path" ); } if ( token.is( Scanner.TokenType.LPAREN ) ) { // Request-Response outputVariablePath = parseOperationVariablePathParameter(); } } else if ( token.is( Scanner.TokenType.ID ) ) { operationName = token.content(); getToken(); inputVariablePath = parseOperationVariablePathParameter(); if ( inputVariablePath == null ) { throwException( "expected variable path" ); } if ( token.is( Scanner.TokenType.LPAREN ) ) { // Request-Response outputVariablePath = parseOperationVariablePathParameter(); } } else { throwException( "expected courier input guard (interface or operation name)" ); } eat( Scanner.TokenType.RSQUARE, "expected ]" ); eat( Scanner.TokenType.LCURLY, "expected {" ); body = parseProcess(); eat( Scanner.TokenType.RCURLY, "expected }" ); if ( iface == null ) { // It's an operation if ( outputVariablePath == null ) { // One-Way stm.operationOneWayBranches().add( new CourierChoiceStatement.OperationOneWayBranch( operationName, inputVariablePath, body ) ); } else { // Request-Response stm.operationRequestResponseBranches().add( new CourierChoiceStatement.OperationRequestResponseBranch( operationName, inputVariablePath, outputVariablePath, body ) ); } } else { // It's an interface if ( outputVariablePath == null ) { // One-Way stm.interfaceOneWayBranches().add( new CourierChoiceStatement.InterfaceOneWayBranch( iface, inputVariablePath, body ) ); } else { // Request-Response stm.interfaceRequestResponseBranches().add( new CourierChoiceStatement.InterfaceRequestResponseBranch( iface, inputVariablePath, outputVariablePath, body ) ); } } } return stm; } private NDChoiceStatement parseNDChoiceStatement() throws IOException, ParserException { NDChoiceStatement stm = new NDChoiceStatement( getContext() ); OLSyntaxNode inputGuard = null; OLSyntaxNode process; while ( token.is( Scanner.TokenType.LSQUARE ) ) { getToken(); // Eat [ /*if ( token.is( Scanner.TokenType.LINKIN ) ) { inputGuard = parseLinkInStatement(); } else */if ( token.is( Scanner.TokenType.ID ) ) { String id = token.content(); getToken(); inputGuard = parseInputOperationStatement( id ); } else { throwException( "expected input guard" ); } eat( Scanner.TokenType.RSQUARE, "] expected" ); eat( Scanner.TokenType.LCURLY, "expected {" ); process = parseProcess(); eat( Scanner.TokenType.RCURLY, "expected }" ); stm.addChild( new Pair<OLSyntaxNode, OLSyntaxNode>( inputGuard, process ) ); } return stm; } private LinkInStatement parseLinkInStatement() throws IOException, ParserException { getToken(); eat( Scanner.TokenType.LPAREN, "expected (" ); assertToken( Scanner.TokenType.ID, "expected link identifier" ); LinkInStatement stm = new LinkInStatement( getContext(), token.content() ); getToken(); eat( Scanner.TokenType.RPAREN, "expected )" ); return stm; } private OLSyntaxNode parseInputOperationStatement( String id ) throws IOException, ParserException { ParsingContext context = getContext(); VariablePathNode inputVarPath = parseOperationVariablePathParameter(); OLSyntaxNode stm; if ( token.is( Scanner.TokenType.LPAREN ) ) { // Request Response operation OLSyntaxNode outputExpression = parseOperationExpressionParameter(); OLSyntaxNode process; eat( Scanner.TokenType.LCURLY, "expected {" ); process = parseProcess(); eat( Scanner.TokenType.RCURLY, "expected }" ); stm = new RequestResponseOperationStatement( context, id, inputVarPath, outputExpression, process ); } else { // One Way operation stm = new OneWayOperationStatement( context, id, inputVarPath ); } return stm; } /** * @return The VariablePath parameter of the statement. May be null. * @throws IOException * @throws ParserException */ private VariablePathNode parseOperationVariablePathParameter() throws IOException, ParserException { VariablePathNode ret = null; eat( Scanner.TokenType.LPAREN, "expected (" ); if ( token.is( Scanner.TokenType.ID ) ) { ret = parseVariablePath(); } else if ( token.is( Scanner.TokenType.DOT ) ) { ret = parsePrefixedVariablePath(); } eat( Scanner.TokenType.RPAREN, "expected )" ); return ret; } private OLSyntaxNode parseOperationExpressionParameter() throws IOException, ParserException { OLSyntaxNode ret = null; eat( Scanner.TokenType.LPAREN, "expected (" ); if ( token.isNot( Scanner.TokenType.RPAREN ) ) { ret = parseExpression(); } eat( Scanner.TokenType.RPAREN, "expected )" ); return ret; } private OLSyntaxNode parseOutputOperationStatement( String id ) throws IOException, ParserException { ParsingContext context = getContext(); /*String outputPortId = token.content(); getToken();*/ String outputPortId = parseIdWithPossibleDot(); OLSyntaxNode outputExpression = parseOperationExpressionParameter(); OLSyntaxNode stm; if ( token.is( Scanner.TokenType.LPAREN ) ) { // Solicit Response operation VariablePathNode inputVarPath = parseOperationVariablePathParameter(); InstallFunctionNode function = null; if ( token.is( Scanner.TokenType.LSQUARE ) ) { eat( Scanner.TokenType.LSQUARE, "expected [" ); function = parseInstallFunction(); eat( Scanner.TokenType.RSQUARE, "expected ]" ); } stm = new SolicitResponseOperationStatement( context, id, outputPortId, outputExpression, inputVarPath, function ); } else { // Notification operation stm = new NotificationOperationStatement( context, id, outputPortId, outputExpression ); } return stm; } private OLSyntaxNode parseWhileStatement() throws IOException, ParserException { ParsingContext context = getContext(); OLSyntaxNode cond, process; getToken(); eat( Scanner.TokenType.LPAREN, "expected (" ); cond = parseExpression(); eat( Scanner.TokenType.RPAREN, "expected )" ); eat( Scanner.TokenType.LCURLY, "expected {" ); process = parseProcess(); eat( Scanner.TokenType.RCURLY, "expected }" ); return new WhileStatement( context, cond, process ); } private OLSyntaxNode parseExpression() throws IOException, ParserException { OrConditionNode orCond = new OrConditionNode( getContext() ); orCond.addChild( parseAndCondition() ); while ( token.is( Scanner.TokenType.OR ) ) { getToken(); orCond.addChild( parseAndCondition() ); } return orCond; } private OLSyntaxNode parseAndCondition() throws IOException, ParserException { AndConditionNode andCond = new AndConditionNode( getContext() ); andCond.addChild( parseBasicCondition() ); while ( token.is( Scanner.TokenType.AND ) ) { getToken(); andCond.addChild( parseBasicCondition() ); } return andCond; } private OLSyntaxNode parseBasicCondition() throws IOException, ParserException { OLSyntaxNode ret; Scanner.TokenType opType; OLSyntaxNode expr1; expr1 = parseBasicExpression(); opType = token.type(); if ( opType == Scanner.TokenType.EQUAL || opType == Scanner.TokenType.LANGLE || opType == Scanner.TokenType.RANGLE || opType == Scanner.TokenType.MAJOR_OR_EQUAL || opType == Scanner.TokenType.MINOR_OR_EQUAL || opType == Scanner.TokenType.NOT_EQUAL ) { OLSyntaxNode expr2; getToken(); expr2 = parseBasicExpression(); ret = new CompareConditionNode( getContext(), expr1, expr2, opType ); } else if ( opType == Scanner.TokenType.INSTANCE_OF ) { getToken(); TypeDefinition type; NativeType nativeType = readNativeType(); if ( nativeType == null ) { // It's a user-defined type assertToken( Scanner.TokenType.ID, "expected type name after instanceof" ); } if ( definedTypes.containsKey( token.content() ) == false ) { throwException( "invalid type: " + token.content() ); } type = definedTypes.get( token.content() ); ret = new InstanceOfExpressionNode( getContext(), expr1, type ); getToken(); } else { ret = expr1; } if ( ret == null ) { throwException( "expected condition" ); } return ret; } /** * @todo Check if negative integer handling is appropriate */ private OLSyntaxNode parseBasicExpression() throws IOException, ParserException { boolean keepRun = true; SumExpressionNode sum = new SumExpressionNode( getContext() ); sum.add( parseProductExpression() ); while ( keepRun ) { if ( token.is( Scanner.TokenType.PLUS ) ) { getToken(); sum.add( parseProductExpression() ); } else if ( token.is( Scanner.TokenType.MINUS ) ) { getToken(); sum.subtract( parseProductExpression() ); } else if ( token.is( Scanner.TokenType.INT ) ) { // e.g. i -1 int value = Integer.parseInt( token.content() ); // We add it, because it's already negative. if ( value < 0 ) { sum.add( parseProductExpression() ); } else { // e.g. i 1 throwException( "expected expression operator" ); } } else if ( token.is( Scanner.TokenType.LONG ) ) { // e.g. i -1L long value = Long.parseLong( token.content() ); // We add it, because it's already negative. if ( value < 0 ) { sum.add( parseProductExpression() ); } else { // e.g. i 1 throwException( "expected expression operator" ); } } else if ( token.is( Scanner.TokenType.DOUBLE ) ) { // e.g. i -1 double value = Double.parseDouble( token.content() ); // We add it, because it's already negative. if ( value < 0 ) { sum.add( parseProductExpression() ); } else { // e.g. i 1 throwException( "expected expression operator" ); } } else { keepRun = false; } } return sum; } private OLSyntaxNode parseFactor() throws IOException, ParserException { OLSyntaxNode retVal = null; VariablePathNode path = null; checkConstant(); if ( token.is( Scanner.TokenType.ID ) ) { path = parseVariablePath(); VariablePathNode freshValuePath = new VariablePathNode( getContext(), Type.NORMAL ); freshValuePath.append( new Pair< OLSyntaxNode, OLSyntaxNode >( new ConstantStringExpression( getContext(), "new" ), new ConstantIntegerExpression( getContext(), 0 ) ) ); if ( path.isEquivalentTo( freshValuePath ) ) { retVal = new FreshValueExpressionNode( path.context() ); return retVal; } } else if ( token.is( Scanner.TokenType.DOT ) ) { path = parseVariablePath(); } else if ( insideInstallFunction && token.is( Scanner.TokenType.CARET ) ) { getToken(); path = parseVariablePath(); retVal = new InstallFixedVariableExpressionNode( getContext(), path ); return retVal; } if ( path != null ) { if ( token.is( Scanner.TokenType.INCREMENT ) ) { // Post increment getToken(); retVal = new PostIncrementStatement( getContext(), path ); } else if ( token.is( Scanner.TokenType.DECREMENT ) ) { getToken(); retVal = new PostDecrementStatement( getContext(), path ); } else if ( token.is( Scanner.TokenType.ASSIGN ) ) { getToken(); retVal = new AssignStatement( getContext(), path, parseExpression() ); } else { retVal = new VariableExpressionNode( getContext(), path ); } } else if ( token.is( Scanner.TokenType.NOT ) ) { getToken(); retVal = new NotExpressionNode( getContext(), parseFactor() ); } else if ( token.is( Scanner.TokenType.STRING ) ) { retVal = new ConstantStringExpression( getContext(), token.content() ); getToken(); } else if ( token.is( Scanner.TokenType.BYTE ) ) { retVal = new ConstantByteExpression( getContext(), Byte.parseByte(token.content() ) ); getToken(); } else if ( token.is( Scanner.TokenType.INT ) ) { retVal = new ConstantIntegerExpression( getContext(), Integer.parseInt( token.content() ) ); getToken(); } else if ( token.is( Scanner.TokenType.INT16 ) ) { retVal = new ConstantInteger16Expression( getContext(), Short.parseShort(token.content() ) ); getToken(); } else if ( token.is( Scanner.TokenType.UINT16 ) ) { retVal = new ConstantUInteger16Expression( getContext(), UInt16.parseUInt16(token.content() ) ); getToken(); } else if ( token.is( Scanner.TokenType.UINT32 ) ) { retVal = new ConstantUInteger32Expression( getContext(), UInt32.parseUInt32(token.content() ) ); getToken(); } else if ( token.is( Scanner.TokenType.LONG ) ) { retVal = new ConstantLongExpression( getContext(), Long.parseLong( token.content() ) ); getToken(); } else if ( token.is( Scanner.TokenType.UINT64 ) ) { retVal = new ConstantUInteger64Expression( getContext(), UInt64.parseUInt64(token.content() ) ); getToken(); } else if ( token.is( Scanner.TokenType.TRUE ) ) { retVal = new ConstantBoolExpression( getContext(), true ); getToken(); } else if ( token.is( Scanner.TokenType.FALSE ) ) { retVal = new ConstantBoolExpression( getContext(), false ); getToken(); } else if ( token.is( Scanner.TokenType.DOUBLE ) ) { retVal = new ConstantDoubleExpression( getContext(), Double.parseDouble( token.content() ) ); getToken(); } else if ( token.is( Scanner.TokenType.LPAREN ) ) { getToken(); retVal = parseExpression(); eat( Scanner.TokenType.RPAREN, "expected )" ); } else if ( token.is( Scanner.TokenType.HASH ) ) { getToken(); retVal = new ValueVectorSizeExpressionNode( getContext(), parseVariablePath() ); } else if ( token.is( Scanner.TokenType.INCREMENT ) ) { // Pre increment: ++i getToken(); retVal = new PreIncrementStatement( getContext(), parseVariablePath() ); } else if ( token.is( Scanner.TokenType.DECREMENT ) ) { // Pre decrement getToken(); retVal = new PreDecrementStatement( getContext(), parseVariablePath() ); } else if ( token.is( Scanner.TokenType.IS_DEFINED ) ) { getToken(); eat( Scanner.TokenType.LPAREN, "expected (" ); retVal = new IsTypeExpressionNode( getContext(), IsTypeExpressionNode.CheckType.DEFINED, parseVariablePath() ); eat( Scanner.TokenType.RPAREN, "expected )" ); } else if ( token.is( Scanner.TokenType.IS_INT ) ) { getToken(); eat( Scanner.TokenType.LPAREN, "expected (" ); retVal = new IsTypeExpressionNode( getContext(), IsTypeExpressionNode.CheckType.INT, parseVariablePath() ); eat( Scanner.TokenType.RPAREN, "expected )" ); } else if ( token.is( Scanner.TokenType.IS_DOUBLE ) ) { getToken(); eat( Scanner.TokenType.LPAREN, "expected (" ); retVal = new IsTypeExpressionNode( getContext(), IsTypeExpressionNode.CheckType.DOUBLE, parseVariablePath() ); eat( Scanner.TokenType.RPAREN, "expected )" ); } else if ( token.is( Scanner.TokenType.IS_BOOL ) ) { getToken(); eat( Scanner.TokenType.LPAREN, "expected (" ); retVal = new IsTypeExpressionNode( getContext(), IsTypeExpressionNode.CheckType.BOOL, parseVariablePath() ); eat( Scanner.TokenType.RPAREN, "expected )" ); } else if ( token.is( Scanner.TokenType.IS_LONG ) ) { getToken(); eat( Scanner.TokenType.LPAREN, "expected (" ); retVal = new IsTypeExpressionNode( getContext(), IsTypeExpressionNode.CheckType.LONG, parseVariablePath() ); eat( Scanner.TokenType.RPAREN, "expected )" ); } else if ( token.is( Scanner.TokenType.IS_STRING ) ) { getToken(); eat( Scanner.TokenType.LPAREN, "expected (" ); retVal = new IsTypeExpressionNode( getContext(), IsTypeExpressionNode.CheckType.STRING, parseVariablePath() ); eat( Scanner.TokenType.RPAREN, "expected )" ); } else if ( token.is( Scanner.TokenType.CAST_INT ) ) { getToken(); eat( Scanner.TokenType.LPAREN, "expected (" ); retVal = new TypeCastExpressionNode( getContext(), NativeType.INT, parseExpression() ); eat( Scanner.TokenType.RPAREN, "expected )" ); } else if ( token.is( Scanner.TokenType.CAST_LONG ) ) { getToken(); eat( Scanner.TokenType.LPAREN, "expected (" ); retVal = new TypeCastExpressionNode( getContext(), NativeType.LONG, parseExpression() ); eat( Scanner.TokenType.RPAREN, "expected )" ); } else if ( token.is( Scanner.TokenType.CAST_BOOL ) ) { getToken(); eat( Scanner.TokenType.LPAREN, "expected (" ); retVal = new TypeCastExpressionNode( getContext(), NativeType.BOOL, parseExpression() ); eat( Scanner.TokenType.RPAREN, "expected )" ); } else if ( token.is( Scanner.TokenType.CAST_DOUBLE ) ) { getToken(); eat( Scanner.TokenType.LPAREN, "expected (" ); retVal = new TypeCastExpressionNode( getContext(), NativeType.DOUBLE, parseExpression() ); eat( Scanner.TokenType.RPAREN, "expected )" ); } else if ( token.is( Scanner.TokenType.CAST_STRING ) ) { getToken(); eat( Scanner.TokenType.LPAREN, "expected (" ); retVal = new TypeCastExpressionNode( getContext(), NativeType.STRING, parseExpression() ); eat( Scanner.TokenType.RPAREN, "expected )" ); } if ( retVal == null ) { throwException( "expected expression" ); } return retVal; } private OLSyntaxNode parseProductExpression() throws IOException, ParserException { ProductExpressionNode product = new ProductExpressionNode( getContext() ); product.multiply( parseFactor() ); boolean keepRun = true; while ( keepRun ) { if ( token.is( Scanner.TokenType.ASTERISK ) ) { getToken(); product.multiply( parseFactor() ); } else if ( token.is( Scanner.TokenType.DIVIDE ) ) { getToken(); product.divide( parseFactor() ); } else if ( token.is( Scanner.TokenType.PERCENT_SIGN ) ) { getToken(); product.modulo( parseFactor() ); } else { keepRun = false; } } return product; } }