/*************************************************************************** * Copyright (C) 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.lang.reflect.Array; import jolie.lang.Constants; import jolie.lang.parse.ast.AddAssignStatement; import jolie.lang.parse.ast.expression.AndConditionNode; import jolie.lang.parse.ast.AssignStatement; import jolie.lang.parse.ast.DocumentationComment; 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.CurrentHandlerStatement; import jolie.lang.parse.ast.DeepCopyStatement; 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.IfStatement; import jolie.lang.parse.ast.InstallFixedVariableExpressionNode; 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.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.RunStatement; import jolie.lang.parse.ast.Scope; import jolie.lang.parse.ast.SequenceStatement; import jolie.lang.parse.ast.InputPortInfo; import jolie.lang.parse.ast.SolicitResponseOperationStatement; import jolie.lang.parse.ast.DefinitionCallStatement; import jolie.lang.parse.ast.DefinitionNode; import jolie.lang.parse.ast.DivideAssignStatement; import jolie.lang.parse.ast.expression.FreshValueExpressionNode; import jolie.lang.parse.ast.InstallFunctionNode; 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.OLSyntaxNode; 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.WhileStatement; import jolie.lang.parse.ast.courier.CourierChoiceStatement; 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.ConstantBoolExpression; import jolie.lang.parse.ast.expression.ConstantByteExpression; import jolie.lang.parse.ast.expression.ConstantInteger16Expression; import jolie.lang.parse.ast.expression.ConstantLongExpression; import jolie.lang.parse.ast.expression.ConstantUInteger16Expression; import jolie.lang.parse.ast.expression.ConstantUInteger32Expression; import jolie.lang.parse.ast.expression.ConstantUInteger64Expression; import jolie.lang.parse.ast.expression.InstanceOfExpressionNode; import jolie.lang.parse.ast.types.TypeDefinitionLink; import jolie.lang.parse.ast.types.TypeInlineDefinition; import jolie.util.Pair; /** Builds an optimized version of an OL parse tree. * * @author Fabrizio Montesi */ public class OLParseTreeOptimizer { /** * TODO Optimize expressions and conditions * */ private static class OptimizerVisitor implements OLVisitor { private final Program program; private OLSyntaxNode currNode; public OptimizerVisitor( ParsingContext context ) { program = new Program( context ); } public Program optimize( Program p ) { visit( p ); return program; } public void visit( Program p ) { for( OLSyntaxNode node : p.children() ) { node.accept( this ); } } public void visit( ExecutionInfo p ) { program.addChild( p ); } public void visit( CorrelationSetInfo p ) { program.addChild( p ); } public void visit( OutputPortInfo p ) { if ( p.protocolConfiguration() != null ) { p.protocolConfiguration().accept( this ); p.setProtocolConfiguration( currNode ); } program.addChild( p ); } public void visit( InputPortInfo p ) { if ( p.protocolConfiguration() != null ) { p.protocolConfiguration().accept( this ); InputPortInfo iport = new InputPortInfo( p.context(), p.id(), p.location(), p.protocolId(), currNode, p.aggregationList(), p.redirectionMap(), p.messageBus() ); iport.operationsMap().putAll( p.operationsMap() ); iport.getInterfaceList().addAll( p.getInterfaceList() ); program.addChild( iport ); } else { program.addChild( p ); } } public void visit( OneWayOperationDeclaration decl ) {} public void visit( RequestResponseOperationDeclaration decl ) {} public void visit( EmbeddedServiceNode n ) { program.addChild( n ); } public void visit( DefinitionNode n ) { program.addChild( new DefinitionNode( n.context(), n.id(), optimizeNode( n.body() ) ) ); } public void visit( ParallelStatement stm ) { if ( stm.children().size() > 1 ) { ParallelStatement tmp = new ParallelStatement( stm.context() ); for( OLSyntaxNode node : stm.children() ) { node.accept( this ); if ( currNode instanceof ParallelStatement ) { /* * A || (B || C) === A || B || C */ ParallelStatement curr = (ParallelStatement) currNode; for( OLSyntaxNode subNode : curr.children() ) tmp.addChild( subNode ); } else if ( !( currNode instanceof NullProcessStatement ) ) { /* * The check is for: * A || nullProcess === A */ tmp.addChild( currNode ); } } /* * If we ended up with an empty composition, return nullProcess */ if ( tmp.children().isEmpty() ) { currNode = new NullProcessStatement( stm.context() ); } else { currNode = tmp; } } else if ( stm.children().isEmpty() == false ) { stm.children().get( 0 ).accept( this ); } else { currNode = new NullProcessStatement( stm.context() ); } } public void visit( SequenceStatement stm ) { if ( stm.children().size() > 1 ) { SequenceStatement tmp = new SequenceStatement( stm.context() ); for( OLSyntaxNode node : stm.children() ) { node.accept( this ); if ( currNode instanceof SequenceStatement ) { /* * A ;; (B ;; C) === A ;; B ;; C */ SequenceStatement curr = (SequenceStatement) currNode; for( OLSyntaxNode subNode : curr.children() ) tmp.addChild( subNode ); } else if ( !( currNode instanceof NullProcessStatement ) ) { /* * The check is for: * seq ;; nullProcess ;; seq2 === seq ;; seq2 */ tmp.addChild( currNode ); } } /* * If we ended up with an empty composition, return nullProcess */ if ( tmp.children().isEmpty() ) { currNode = new NullProcessStatement( stm.context() ); } else { currNode = tmp; } } else if ( stm.children().isEmpty() == false ) { stm.children().get( 0 ).accept( this ); } else { currNode = new NullProcessStatement( stm.context() ); } } public void visit( NDChoiceStatement stm ) { if ( stm.children().size() > 0 ) { NDChoiceStatement tmp = new NDChoiceStatement( stm.context() ); for( Pair< OLSyntaxNode, OLSyntaxNode > pair : stm.children() ) { pair.key().accept( this ); OLSyntaxNode n = currNode; pair.value().accept( this ); tmp.addChild( new Pair< OLSyntaxNode, OLSyntaxNode >( n, currNode ) ); } currNode = tmp; } else { currNode = new NullProcessStatement( stm.context() ); } //} else { /* * ( [ I ] A ) === I ;; A * * An NDChoice formed by only one element * is equivalent to a sequence beginning with the * same input. * * This is not true as of 19 Nov 07, * because of InProcess special behaviour inside an NDChoiceProcess */ /*SequenceStatement sequence = new SequenceStatement(); Pair< OLSyntaxNode, OLSyntaxNode > pair = stm.children().get( 0 ); sequence.addChild( pair.key() ); sequence.addChild( pair.value() ); sequence.accept( this ); }*/ } public void visit( IfStatement n ) { IfStatement stm = new IfStatement( n.context() ); OLSyntaxNode condition; for( Pair< OLSyntaxNode, OLSyntaxNode > pair : n.children() ) { pair.key().accept( this ); condition = currNode; pair.value().accept( this ); stm.addChild( new Pair< OLSyntaxNode, OLSyntaxNode >( condition, currNode ) ); } if ( n.elseProcess() != null ) { n.elseProcess().accept( this ); stm.setElseProcess( currNode ); } currNode = stm; } public void visit( SpawnStatement n ) { currNode = new SpawnStatement( n.context(), optimizePath( n.indexVariablePath() ), optimizeNode( n.upperBoundExpression() ), optimizePath( n.inVariablePath() ), optimizeNode( n.body() ) ); } public void visit( WhileStatement n ) { currNode = new WhileStatement( n.context(), optimizeNode( n.condition() ), optimizeNode( n.body() ) ); } public void visit( ForStatement n ) { currNode = new ForStatement( n.context(), optimizeNode( n.init() ), optimizeNode( n.condition() ), optimizeNode( n.post() ), optimizeNode( n.body() ) ); } public void visit( ForEachStatement n ) { currNode = new ForEachStatement( n.context(), optimizePath( n.keyPath() ), optimizePath( n.targetPath() ), optimizeNode( n.body() ) ); } public void visit( VariablePathNode n ) { VariablePathNode varPath = new VariablePathNode( n.context(), n.type() ); for( Pair< OLSyntaxNode, OLSyntaxNode > node : n.path() ) { varPath.append( new Pair< OLSyntaxNode, OLSyntaxNode >( optimizeNode( node.key() ), optimizeNode( node.value() ) ) ); } currNode = varPath; } private VariablePathNode optimizePath( VariablePathNode n ) { if ( n == null ) { return null; } n.accept( this ); return (VariablePathNode)currNode; } private OLSyntaxNode optimizeNode( OLSyntaxNode n ) { if ( n == null ) { return null; } n.accept( this ); return currNode; } public void visit( RequestResponseOperationStatement n ) { OLSyntaxNode outputExpression = null; if ( n.outputExpression() != null ) { n.outputExpression().accept( this ); outputExpression = currNode; } currNode = new RequestResponseOperationStatement( n.context(), n.id(), optimizePath( n.inputVarPath() ), outputExpression, optimizeNode( n.process() ) ); } public void visit( Scope n ) { n.body().accept( this ); currNode = new Scope( n.context(), n.id(), currNode ); } public void visit( InstallStatement n ) { currNode = new InstallStatement( n.context(), optimizeInstallFunctionNode( n.handlersFunction() ) ); } private InstallFunctionNode optimizeInstallFunctionNode( InstallFunctionNode n ) { if ( n == null ) { return null; } Pair< String, OLSyntaxNode >[] pairs = (Pair< String, OLSyntaxNode >[]) Array.newInstance( Pair.class, n.pairs().length ); int i = 0; for( Pair< String, OLSyntaxNode > pair : n.pairs() ) { pair.value().accept( this ); pairs[ i++ ] = new Pair< String, OLSyntaxNode >( pair.key(), currNode ); } return new InstallFunctionNode( pairs ); } public void visit( SynchronizedStatement n ) { n.body().accept( this ); currNode = new SynchronizedStatement( n.context(), n.id(), currNode ); } public void visit( CompensateStatement n ) { currNode = n; } public void visit( ThrowStatement n ) { if ( n.expression() == null ) { currNode = null; } else { n.expression().accept( this ); } currNode = new ThrowStatement( n.context(), n.id(), currNode ); } public void visit( OneWayOperationStatement n ) { currNode = new OneWayOperationStatement( n.context(), n.id(), optimizePath( n.inputVarPath() ) ); } public void visit( NotificationOperationStatement n ) { OLSyntaxNode outputExpression = null; if ( n.outputExpression() != null ) { n.outputExpression().accept( this ); outputExpression = currNode; } currNode = new NotificationOperationStatement( n.context(), n.id(), n.outputPortId(), outputExpression ); } public void visit( SolicitResponseOperationStatement n ) { OLSyntaxNode outputExpression = null; if ( n.outputExpression() != null ) { n.outputExpression().accept( this ); outputExpression = currNode; } currNode = new SolicitResponseOperationStatement( n.context(), n.id(), n.outputPortId(), outputExpression, optimizePath( n.inputVarPath() ), optimizeInstallFunctionNode( n.handlersFunction() ) ); } public void visit( LinkInStatement n ) { currNode = n; } public void visit( LinkOutStatement n ) { currNode = n; } public void visit( AssignStatement n ) { currNode = new AssignStatement( n.context(), optimizePath( n.variablePath() ), optimizeNode( n.expression() ) ); } public void visit( AddAssignStatement n ) { currNode = new AddAssignStatement( n.context(), optimizePath( n.variablePath() ), optimizeNode( n.expression() ) ); } public void visit( SubtractAssignStatement n ) { currNode = new SubtractAssignStatement( n.context(), optimizePath( n.variablePath() ), optimizeNode( n.expression() ) ); } public void visit( MultiplyAssignStatement n ) { currNode = new MultiplyAssignStatement( n.context(), optimizePath( n.variablePath() ), optimizeNode( n.expression() ) ); } public void visit( DivideAssignStatement n ) { currNode = new DivideAssignStatement( n.context(), optimizePath( n.variablePath() ), optimizeNode( n.expression() ) ); } public void visit( DeepCopyStatement n ) { currNode = new DeepCopyStatement( n.context(), optimizePath( n.leftPath() ), optimizePath( n.rightPath() ) ); } public void visit( PointerStatement n ) { currNode = new PointerStatement( n.context(), optimizePath( n.leftPath() ), optimizePath( n.rightPath() ) ); } public void visit( DefinitionCallStatement n ) { currNode = n; } public void visit( OrConditionNode n ) { if ( n.children().size() > 1 ) { OrConditionNode ret = new OrConditionNode( n.context() ); for( OLSyntaxNode child : n.children() ) { child.accept( this ); ret.addChild( currNode ); } currNode = ret; } else { n.children().get( 0 ).accept( this ); } } public void visit( AndConditionNode n ) { if ( n.children().size() > 1 ) { AndConditionNode ret = new AndConditionNode( n.context() ); for( OLSyntaxNode child : n.children() ) { child.accept( this ); ret.addChild( currNode ); } currNode = ret; } else { n.children().get( 0 ).accept( this ); } } public void visit( NotExpressionNode n ) { n.expression().accept( this ); currNode = new NotExpressionNode( n.context(), currNode ); } public void visit( CompareConditionNode n ) { n.leftExpression().accept( this ); OLSyntaxNode leftExpression = currNode; n.rightExpression().accept( this ); currNode = new CompareConditionNode( n.context(), leftExpression, currNode, n.opType() ); } public void visit( ConstantIntegerExpression n ) { currNode = n; } public void visit( ConstantUInteger32Expression n ) { currNode = n; } public void visit( ConstantUInteger16Expression n ) { currNode = n; } public void visit( ConstantInteger16Expression n ) { currNode = n; } public void visit( ConstantUInteger64Expression n ) { currNode = n; } public void visit( ConstantByteExpression n ) { currNode = n; } public void visit( ConstantLongExpression n ) { currNode = n; } public void visit( ConstantBoolExpression n ) { currNode = n; } public void visit( ConstantDoubleExpression n ) { currNode = n; } public void visit( ConstantStringExpression n ) { currNode = new ConstantStringExpression( n.context(), n.value().intern() ); } public void visit( ProductExpressionNode n ) { if ( n.operands().size() > 1 ) { ProductExpressionNode ret = new ProductExpressionNode( n.context() ); for( Pair< Constants.OperandType, OLSyntaxNode > pair : n.operands() ) { pair.value().accept( this ); if ( pair.key() == Constants.OperandType.MULTIPLY ) { ret.multiply( currNode ); } else if ( pair.key() == Constants.OperandType.DIVIDE ) { ret.divide( currNode ); } else if ( pair.key() == Constants.OperandType.MODULUS ) { ret.modulo( currNode ); } } currNode = ret; } else { n.operands().iterator().next().value().accept( this ); } } public void visit( SumExpressionNode n ) { if ( n.operands().size() > 1 ) { SumExpressionNode ret = new SumExpressionNode( n.context() ); for( Pair< Constants.OperandType, OLSyntaxNode > pair : n.operands() ) { pair.value().accept( this ); if ( pair.key() == Constants.OperandType.ADD ) { ret.add( currNode ); } else { ret.subtract( currNode ); } } currNode = ret; } else { n.operands().iterator().next().value().accept( this ); } } public void visit( VariableExpressionNode n ) { currNode = new VariableExpressionNode( n.context(), optimizePath( n.variablePath() ) ); } public void visit( InstallFixedVariableExpressionNode n ) { currNode = new InstallFixedVariableExpressionNode( n.context(), optimizePath( n.variablePath() ) ); } public void visit( NullProcessStatement n ) { currNode = n; } public void visit( ExitStatement n ) { currNode = n; } public void visit( RunStatement n ) { currNode = n; } public void visit( TypeInlineDefinition n ) { program.addChild( n ); } public void visit( TypeDefinitionLink n ) { program.addChild( n ); } public void visit( ValueVectorSizeExpressionNode n ) { currNode = new ValueVectorSizeExpressionNode( n.context(), optimizePath( n.variablePath() ) ); } public void visit( PreIncrementStatement n ) { currNode = new PreIncrementStatement( n.context(), optimizePath( n.variablePath() ) ); } public void visit( PostIncrementStatement n ) { currNode = new PostIncrementStatement( n.context(), optimizePath( n.variablePath() ) ); } public void visit( PreDecrementStatement n ) { currNode = new PreDecrementStatement( n.context(), optimizePath( n.variablePath() ) ); } public void visit( PostDecrementStatement n ) { currNode = new PostDecrementStatement( n.context(), optimizePath( n.variablePath() ) ); } public void visit( UndefStatement n ) { currNode = new UndefStatement( n.context(), optimizePath( n.variablePath() ) ); } public void visit( FreshValueExpressionNode n ) { currNode = n; } public void visit( IsTypeExpressionNode n ) { currNode = new IsTypeExpressionNode( n.context(), n.type(), optimizePath( n.variablePath() ) ); } public void visit( InstanceOfExpressionNode n ) { currNode = new InstanceOfExpressionNode( n.context(), optimizeNode( n.expression() ), n.type() ); } public void visit( TypeCastExpressionNode n ) { currNode = new TypeCastExpressionNode( n.context(), n.type(), optimizeNode( n.expression() ) ); } public void visit( CurrentHandlerStatement n ) { currNode = n; } public void visit( InterfaceDefinition n ) { program.addChild( n ); } public void visit( InterfaceExtenderDefinition n ) { program.addChild( n ); } public void visit( CourierDefinitionNode n ) { program.addChild( new CourierDefinitionNode( n.context(), n.inputPortName(), optimizeNode( n.body() ) ) ); } public void visit( CourierChoiceStatement n ) { CourierChoiceStatement courierChoice = new CourierChoiceStatement( n.context() ); for( CourierChoiceStatement.InterfaceOneWayBranch branch : n.interfaceOneWayBranches() ) { courierChoice.interfaceOneWayBranches().add( new CourierChoiceStatement.InterfaceOneWayBranch( branch.interfaceDefinition, branch.inputVariablePath, optimizeNode( branch.body ) ) ); } for( CourierChoiceStatement.InterfaceRequestResponseBranch branch : n.interfaceRequestResponseBranches() ) { courierChoice.interfaceRequestResponseBranches().add( new CourierChoiceStatement.InterfaceRequestResponseBranch( branch.interfaceDefinition, branch.inputVariablePath, branch.outputVariablePath, optimizeNode( branch.body ) ) ); } for( CourierChoiceStatement.OperationOneWayBranch branch : n.operationOneWayBranches() ) { courierChoice.operationOneWayBranches().add( new CourierChoiceStatement.OperationOneWayBranch( branch.operation, branch.inputVariablePath, optimizeNode( branch.body ) ) ); } for( CourierChoiceStatement.OperationRequestResponseBranch branch : n.operationRequestResponseBranches() ) { courierChoice.operationRequestResponseBranches().add( new CourierChoiceStatement.OperationRequestResponseBranch( branch.operation, branch.inputVariablePath, branch.outputVariablePath, optimizeNode( branch.body ) ) ); } currNode = courierChoice; } public void visit( NotificationForwardStatement n ) { currNode = new NotificationForwardStatement( n.context(), n.outputPortName(), optimizePath( n.outputVariablePath() ) ); } public void visit( SolicitResponseForwardStatement n ) { currNode = new SolicitResponseForwardStatement( n.context(), n.outputPortName(), optimizePath( n.outputVariablePath() ), optimizePath( n.inputVariablePath() ) ); } public void visit( DocumentationComment n ) {} } private Program originalProgram; public OLParseTreeOptimizer( Program originalProgram ) { this.originalProgram = originalProgram; } public Program optimize() { return (new OptimizerVisitor( originalProgram.context() )).optimize( originalProgram ); } }