/* * Copyright 2013 Guidewire Software, Inc. */ package gw.internal.gosu.parser.expressions; import gw.lang.parser.IExpression; import gw.lang.reflect.IFunctionType; import gw.lang.reflect.IType; import gw.lang.reflect.gs.IGosuClass; import gw.lang.reflect.java.JavaTypes; import gw.lang.parser.IParsedElement; import gw.lang.parser.ISymbolTable; import gw.lang.parser.ISymbol; import gw.lang.parser.IScope; import gw.lang.parser.StandardScope; import gw.lang.parser.ICapturedSymbol; import gw.lang.parser.IBlockClass; import gw.lang.parser.expressions.IBlockExpression; import gw.internal.gosu.parser.*; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; /** * A block expression, representing an anonymous function/closure * * @see gw.lang.parser.IGosuParser */ public final class BlockExpression extends Expression implements IBlockExpression { private List<ISymbol> _args; private IParsedElement _blockBody; private Map<String, ICapturedSymbol> _capturedSymbols; private StandardScope _scope; private IType _blockReturnType; private IBlockClassInternal _blockClass; /** * Constructs an block expression. */ public BlockExpression() { _capturedSymbols = Collections.emptyMap(); } public Object evaluate() { if( !isCompileTimeConstant() ) { return super.evaluate(); } throw new CannotExecuteGosuException(); } @Override public String toString() { String returnString = "\\ "; for( int i = 0; _args != null && i < _args.size(); i++ ) { ISymbol iSymbol = _args.get( i ); returnString += iSymbol.getName() + " : " + iSymbol.getType(); if( i < _args.size() - 2 ) { returnString += ", "; } } returnString += " -> "; if( _blockBody == null ) { returnString += "<empty body>"; } else { returnString += _blockBody.toString(); } return returnString; } public void setArgs( List<ISymbol> args ) { _args = args; } public void setBody( IParsedElement blockBody ) { if( blockBody instanceof Statement || blockBody instanceof Expression ) { _blockBody = blockBody; } else { throw new IllegalArgumentException( "Cannot set block body of type " + blockBody.getClass() ); } } public IFunctionType getType() { return (IFunctionType) super.getType(); } @Override public IFunctionType getTypeImpl() { if( _type == null ) { ArrayList<IType> argTypes = new ArrayList<IType>(); ArrayList<String> argNames = new ArrayList<String>(); ArrayList<IExpression> defValues = new ArrayList<IExpression>(); if( _args != null ) { for( int i = 0; i < _args.size(); i++ ) { ISymbol symbol = _args.get( i ); argTypes.add( symbol.getType() ); argNames.add( symbol.getName() ); defValues.add( symbol.getDefaultValueExpression() ); } } argNames.trimToSize(); argTypes.trimToSize(); IType returnType = _blockBody == null ? JavaTypes.OBJECT() : getBlockReturnType(); setType( new BlockType( returnType, argTypes.toArray( new IType[argTypes.size()] ), argNames, defValues ) ); } return (IFunctionType)super.getTypeImpl(); } public IType getBlockReturnType() { return _blockReturnType; } public void setBlockReturnType( IType blockReturnType ) { _blockReturnType = blockReturnType; } public IParsedElement getBody() { return _blockBody; } public List<ISymbol> getArgs() { return _args; } public ICapturedSymbol getCapturedSymbol( String strName ) { return _capturedSymbols.get( strName ); } public void addCapturedSymbol( ICapturedSymbol sym ) { if( _capturedSymbols.isEmpty() ) { _capturedSymbols = new HashMap<String, ICapturedSymbol>( 2 ); } _capturedSymbols.put( (String)sym.getName(), sym ); } public boolean isWithinScope( ISymbol sym, ISymbolTable symbolTable ) { return getArgs().contains( sym ) || symbolTable.isSymbolWithinScope( sym, _scope ); } @Override public String getFunctionName() { return getType().getName(); } /** * The scope of the block, available only at compile time */ public void setScope( StandardScope blockScope ) { _scope = blockScope; } /** * The scope of the block, available only at compile time */ public IScope getScope() { return _scope; } // Don't clear parse tree information. Ugh, this is bad. @Override public boolean shouldClearParseInfo() { return false; } public void setBlockGosuClass( IBlockClassInternal blockClass ) { _blockClass = blockClass; } public IBlockClass getBlockGosuClass() { return _blockClass; } @Override public IGosuClass getGosuClass() { return getBlockGosuClass(); } public Map<String, ICapturedSymbol> getCapturedSymbols() { return _capturedSymbols; } public void updateGosuClass() { if( _blockClass != null ) { _blockClass.update(); } } }