/*
* Copyright 2013 Guidewire Software, Inc.
*/
package gw.internal.gosu.parser;
import gw.config.CommonServices;
import gw.internal.gosu.parser.expressions.AnnotationExpression;
import gw.internal.gosu.parser.expressions.ArithmeticExpression;
import gw.internal.gosu.parser.expressions.BlockExpression;
import gw.internal.gosu.parser.expressions.DefaultArgLiteral;
import gw.internal.gosu.parser.expressions.Identifier;
import gw.internal.gosu.parser.expressions.ImplicitTypeAsExpression;
import gw.internal.gosu.parser.expressions.Literal;
import gw.internal.gosu.parser.expressions.ModifierListClause;
import gw.internal.gosu.parser.expressions.NotAWordExpression;
import gw.internal.gosu.parser.expressions.NullExpression;
import gw.internal.gosu.parser.expressions.StringLiteral;
import gw.internal.gosu.parser.expressions.TypeAsExpression;
import gw.internal.gosu.parser.expressions.TypeLiteral;
import gw.internal.gosu.parser.statements.ClassFileStatement;
import gw.internal.gosu.parser.statements.ClassStatement;
import gw.internal.gosu.parser.statements.HideFieldNoOpStatement;
import gw.internal.gosu.parser.statements.VarStatement;
import gw.lang.annotation.UsageModifier;
import gw.lang.annotation.UsageTarget;
import gw.lang.parser.GosuParserTypes;
import gw.lang.parser.ICapturedSymbol;
import gw.lang.parser.ICoercer;
import gw.lang.parser.ICoercionManager;
import gw.lang.parser.IFullParserState;
import gw.lang.parser.IGosuValidator;
import gw.lang.parser.INonCapturableSymbol;
import gw.lang.parser.IParseIssue;
import gw.lang.parser.IParseTree;
import gw.lang.parser.IParsedElement;
import gw.lang.parser.IParserPart;
import gw.lang.parser.IParserState;
import gw.lang.parser.IResolvingCoercer;
import gw.lang.parser.IScope;
import gw.lang.parser.IScriptPartId;
import gw.lang.parser.ISymbol;
import gw.lang.parser.ISymbolTable;
import gw.lang.parser.IToken;
import gw.lang.parser.ITypeUsesMap;
import gw.lang.parser.Keyword;
import gw.lang.parser.ScriptPartId;
import gw.lang.parser.StandardCoercionManager;
import gw.lang.parser.StandardScope;
import gw.lang.parser.exceptions.ImplicitCoercionError;
import gw.lang.parser.exceptions.ImplicitCoercionWarning;
import gw.lang.parser.exceptions.IncompatibleTypeException;
import gw.lang.parser.exceptions.ParseException;
import gw.lang.parser.exceptions.ParseIssue;
import gw.lang.parser.exceptions.ParseResultsException;
import gw.lang.parser.exceptions.ParseWarning;
import gw.lang.parser.exceptions.SymbolNotFoundException;
import gw.lang.parser.expressions.IBlockExpression;
import gw.lang.parser.expressions.IOverridableOperation;
import gw.lang.parser.resources.Res;
import gw.lang.parser.resources.ResourceKey;
import gw.lang.reflect.IErrorType;
import gw.lang.reflect.IMethodInfo;
import gw.lang.reflect.INamespaceType;
import gw.lang.reflect.IPlaceholder;
import gw.lang.reflect.IType;
import gw.lang.reflect.Modifier;
import gw.lang.reflect.TypeSystem;
import gw.lang.reflect.gs.ICompilableType;
import gw.lang.reflect.gs.IGosuProgram;
import gw.lang.reflect.java.JavaTypes;
import gw.lang.reflect.module.IModule;
import gw.util.Stack;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
*/
@SuppressWarnings({"ThrowableInstanceNeverThrown"})
public abstract class ParserBase implements IParserPart
{
private static final Object[] EMPTY_ARRAY = new Object[0];
// Constant used as a placholder value to facilitate lazy parser state initialization for error reporting
private static final IParserState PLACEHOLDER_PARSER_STATE = new PlaceholderParserState();
private static final INamespaceType PROGRAM_NAMESPACE = new NamespaceType( IGosuProgram.PACKAGE, null);
private boolean _snapshotSymbols;
private GosuParser _owner;
Stack<BlockExpression> _blocks;
private IGosuValidator _validator;
protected int _offsetShift; // For class fragments
private int _lineNumShift; // For class fragments
protected boolean _bDontOptimizeStatementLists;
private List<IParseTree> _subTree;
private java.util.Stack<List<IType>> _inferringFunctionTypes;
private static final NotAWordExpression NOT_SET_EXPRESSION = new NotAWordExpression();
private Set<ResourceKey> _ignoreWarnings;
public ParserBase()
{
this( null );
}
public ParserBase( GosuParser owner )
{
_inferringFunctionTypes = new java.util.Stack<List<IType>>();
_owner = owner;
}
public GosuParser getOwner()
{
return _owner;
}
protected void setOwner( GosuParser owner )
{
_owner = owner;
}
public void setIgnoreWarnings(Set<ResourceKey> msgKeys) {
_ignoreWarnings = msgKeys;
}
SourceCodeTokenizer getTokenizer()
{
return _owner.getTokenizer();
}
ISymbolTable getSymbolTable()
{
return _owner.getSymbolTable();
}
void setLocation( int iOffset, int iLineNum, int iColumn )
{
setLocation( iOffset, iLineNum, iColumn, false );
}
void setLocation( int iOffset, int iLineNum, int iColumn, boolean bForceRedundancy )
{
setLocation( iOffset, iLineNum, iColumn, false, bForceRedundancy );
}
void setLocation( int iOffset, int iLineNum, int iColumn, boolean bZeroLength, boolean bForceRedundancy )
{
if( iOffset == -1 )
{
iOffset = 0;
}
if( iOffset < 0 )
{
throw new IllegalArgumentException( iOffset + " is out of bounds" );
}
ParsedElement e = getOwner().peekParsedElement();
IToken priorToken = (e instanceof ClassFileStatement) && getTokenizer().isEOF()
? getTokenizer().getCurrentToken() // consume trailing whitespace near EOF
: getTokenizer().getPriorToken();
int iLength = bZeroLength ? 0 : priorToken.getTokenEnd() - iOffset;
if( iLength < 0 )
{
iLength = 0;
//throw new IllegalStateException( "Parsed element has negative length" );
}
//## hack alert: We check for positive length locations because we sometime recycle expressions after back tracking
//## It would be nice if we could detect that an expression was reused somehow.
if( e != null && (e.getLocation() == null || (e.getLocation().getLength() > 0 || e instanceof ClassStatement)) )
{
ParseTree node = e.initLocation( iOffset, iLength, iLineNum, iColumn, getOwner().getScriptPart() );
ParseTree location = addLocation( node, bForceRedundancy );
e.setLocation( location );
if( _subTree != null )
{
try
{
for( IParseTree childLocation : _subTree )
{
childLocation.addUnder( location );
childLocation.getParsedElement().setParent( location.getParsedElement() );
}
}
finally
{
_subTree = null;
}
}
}
}
private ParseTree addLocation( ParseTree location, boolean bForceRedundancy )
{
List<ParseTree> locationsList = getLocationsList();
int iLast = location.getChildren().size();
for( int i = locationsList.size() - 1; i >= 0; i-- )
{
ParseTree l = locationsList.get( i );
if( l.getParsedElement() == location.getParsedElement() )
{
return l;
}
if( location.getLength() != 0 &&
(location.contains( l ) ||
// zero-length elements that follow the element we're adding are intended to belong
// to it, but since the elements are of zero-length they did not advance the tokenizer
// so they may have an offset that is out of the bounds of the intended enclosing element.
// This here condition allows for zero-length elements to be swallowed up by the intended
// enclosing element (let's hope).
(l.getLength() == 0 && l.getOffset() > location.getOffset())) )
{
if( !bForceRedundancy && l.contains( location ) )
{
if( !(location.getParsedElement() instanceof Statement) ||
l.getParsedElement() instanceof Statement )
{
// Already has an element covering the exact same area.
// Probably an abstract element. Don't allow redundancy.
return l;
}
}
ParseTree tree = locationsList.remove(i);
location.addChild( iLast, tree);
}
else
{
break;
}
}
if( !(location.getParsedElement() instanceof HideFieldNoOpStatement) )
{
locationsList.add( location );
}
return location;
}
List<ParseTree> getLocationsList()
{
return getOwner().getLocationsList();
}
protected void pushExpression( Expression e )
{
getOwner().pushExpression( e );
}
protected void verifyParsedElement( IParsedElement element ) throws ParseResultsException
{
verifyParsedElement( element, getOwner().isThrowParseResultsExceptionForWarnings() );
}
protected void verifyParsedElement( IParsedElement element, boolean bThrowOnWarnings ) throws ParseResultsException
{
final IGosuValidator validator = getValidator();
if( validator != null )
{
validator.validate( element, getScript() );
}
// Walk through all parse issues and convert local stuff to pointers into the type (i.e. kill the source, fix the locations, etc...)
List<IParseIssue> issues = element.getParseIssues();
boolean hasParseException = false;
boolean hasParseWarning = false;
for( IParseIssue issue : issues )
{
if( issue instanceof ParseException )
{
hasParseException = true;
}
else
{
hasParseWarning = true;
}
issue.resolve( this );
}
if( hasParseException || (bThrowOnWarnings && hasParseWarning) )
{
ParseResultsException resultsException = new ParseResultsException( element );
resultsException.setContextType( getOwner().getGosuClass() );
throw resultsException;
}
}
protected abstract String getScript();
protected Expression popExpression()
{
return getOwner().popExpression();
}
protected Expression peekExpression()
{
return getOwner().peekExpression();
}
protected void pushStatement( Statement stmt )
{
getOwner().pushStatement( stmt );
}
protected Statement popStatement()
{
return getOwner().popStatement();
}
protected Statement peekStatement()
{
return getOwner().peekStatement();
}
boolean eatStatementBlock( ParsedElement parsedElement, ResourceKey errorKey )
{
verify( parsedElement, match( null, '{' ), errorKey );
return eatBlock( '{', '}', false ) != null;
}
boolean eatPossibleStatementBlock()
{
if( match( null, '{' ) )
{
eatBlock( '{', '}', false );
return true;
}
return false;
}
boolean eatPossibleEnclosedVarInStmt()
{
if( (match( null, Keyword.KW_for ) ||
match( null, Keyword.KW_foreach ) ||
match( null, Keyword.KW_exists ) ||
match( null, Keyword.KW_find ) ||
match( null, Keyword.KW_using )) &&
match( null, '(' ) )
{
eatBlock( '(', ')', false );
return true;
}
return false;
}
void eatParenthesized( ParsedElement parsedElement, ResourceKey errorKey )
{
verify( parsedElement, match( null, '(' ), errorKey );
eatBlock( '(', ')', false );
}
void eatPossibleParametarization()
{
eatPossibleParametarization( true );
}
void eatPossibleParametarization( boolean bMatchStart )
{
if( !bMatchStart || match( null, "<", SourceCodeTokenizer.TT_OPERATOR ) )
{
eatBlock( '<', '>', true );
}
}
public Token eatBlock( char cBegin, char cEnd, boolean bOperator )
{
return eatBlock( cBegin, cEnd, bOperator, getTokenizer() );
}
public Token eatBlock( char cBegin, char cEnd, boolean bOperator, boolean bStopAtDeclarationKeyword )
{
return eatBlock( cBegin, cEnd, bOperator, bStopAtDeclarationKeyword, getTokenizer() );
}
public static Token eatBlock( char cBegin, char cEnd, boolean bOperator, SourceCodeTokenizer tokenizer )
{
return eatBlock( cBegin, cEnd, bOperator, false, tokenizer );
}
public static Token eatBlock( char cBegin, char cEnd, boolean bOperator, boolean bStopAtDeclarationKeyword, SourceCodeTokenizer tokenizer )
{
int iBraceDepth = 1;
Token endToken = new Token();
boolean bNewMatched = false;
do
{
if( match( null, null, SourceCodeTokenizer.TT_EOF, false, tokenizer ) )
{
return null;
}
// Total hack to handle an annotation with an anonymous new expression and blocks (they has declarations that need to be eaten)
// We're hacking this because soon, real soon now, we'll be dropkicking support for an anonymous new expression or block
// as an argument to a gosu annotation -- gosu annotations need to behave like java annotation, no more no less.
bNewMatched = bStopAtDeclarationKeyword && (bNewMatched ||
match( null, Keyword.KW_new.toString(), SourceCodeTokenizer.TT_KEYWORD, true, tokenizer ) ||
match( null, "->", SourceCodeTokenizer.TT_OPERATOR, true, tokenizer ) ||
match( null, "#", SourceCodeTokenizer.TT_OPERATOR, true, tokenizer ));
if( bStopAtDeclarationKeyword && !bNewMatched && matchDeclarationKeyword( null, true, tokenizer ) )
{
return null;
}
if( (bOperator && match( endToken, String.valueOf( cEnd ), SourceCodeTokenizer.TT_OPERATOR, false, tokenizer )) ||
match( endToken, null, (int)cEnd, false, tokenizer ) )
{
if( --iBraceDepth == 0 )
{
return endToken;
}
continue;
}
if( (bOperator && match( null, String.valueOf( cBegin ), SourceCodeTokenizer.TT_OPERATOR, false, tokenizer )) ||
match( null, null, (int)cBegin, false, tokenizer ) )
{
iBraceDepth++;
continue;
}
tokenizer.nextToken();
} while( true );
}
boolean eatMemberBlockFromProgramClass( Keyword start )
{
int iMark = getTokenizer().mark();
ModifierInfo modifierInfo = parseModifiers( true );
if( match( null, start ) )
{
if( getOwner().maybeAdvanceTokenizerToEndOfSavedLocation() )
{
return true;
}
}
removeAnnotationsFromLocationsList( modifierInfo );
getTokenizer().restoreToMark( iMark );
return false;
}
private void removeAnnotationsFromLocationsList( ModifierInfo modifierInfo ) {
List<ParseTree> locs = getLocationsList();
for( int i = locs.size()-1; i >= 0; i-- )
{
ParseTree csr = locs.get( i );
for( IGosuAnnotation ann: modifierInfo.getAnnotations() )
{
if( csr == ann.getExpression().getLocation() )
{
locs.remove( i );
}
}
}
}
/**
* Parse a dot separated path as a single logical token
*/
public void parseDotPathWord( Token t )
{
StringBuilder sb = t == null ? null : new StringBuilder( t._strValue == null ? "" : t._strValue );
while( match( null, '.' ) )
{
if( sb != null )
{
sb.append( '.' );
}
Token T = new Token();
boolean consume = match( T, null, SourceCodeTokenizer.TT_WORD, true );
if( consume )
{
match( T, SourceCodeTokenizer.TT_WORD ); //eat it
}
else
{
consume = maybeConsumeKeywordInDotPath( T, consume );
}
if( consume )
{
if( sb != null )
{
sb.append( T._strValue );
}
}
}
if( sb != null )
{
t._strValue = sb.toString();
}
}
// Uh-oh, we've done got ourselves a keyword in our dot path. Is it real:
//
// import java.lang.[Class]
//
// class MyClass { ...
//
// or
//
// import my.public.package.*
//
// class MyClass { ...
//
// Or is the code in a bad state, like so:
//
// import java.
//
// [class] MyClass { ...
//
// To differentiate, we do two tests:
//
// * If the keyword in the dot path is a different case than the canonical keyword, we consume it;
// * If it is the same case then we look ahead to see if the keyword is followed by a '.'. If it is we consume it
// * Otherwise we do not consume the keyword and it is not considered part of the dot path
private boolean maybeConsumeKeywordInDotPath( Token t, boolean consume )
{
boolean keywordMatch = match( t, null, SourceCodeTokenizer.TT_KEYWORD, true );
if( keywordMatch )
{
if( !Keyword.isExactKeywordMatch( t._strValue ) )
{
match( t, null, SourceCodeTokenizer.TT_KEYWORD ); //eat it
consume = true;
}
else
{
int state = getTokenizer().mark();
match( t, null, SourceCodeTokenizer.TT_KEYWORD ); //eat it
if( match( null, null, '.', true ) )
{
consume = true;
}
else
{
getTokenizer().restoreToMark( state );
}
}
}
return consume;
}
/**
* Possibly matches the specified string token value. If a match occurs the token will be eaten and its
* information put into T (if T is not null).
*
* @param T the Token object to populate iff a match is found
* @param token the string object to match
*
* @return true if a match occurred, and false otherwise
*/
protected boolean match( Token T, String token )
{
return match( T, token, 0, false );
}
/**
* Possibly matches the specified token type. If a match occurs then the token will be eaten
* and its information put into T (if T is not null).
*
* @param T the Token object to populate iff a match is found
* @param iType the token "type" to match (e.g. {@link SourceCodeTokenizer#TT_WORD})
*
* @return true if a match occurred, and false otherwise
*/
protected boolean match( Token T, int iType )
{
return match( T, null, iType, false );
}
/**
* Possibly matches the specified token or name (in token). If a match occurs then
* the token will be eaten and its information put into T (if T is not null).
*
* @param T the Token object to populate iff a match is found
* @param token the string object to match
* @param iType the token "type" to match (e.g. {@link SourceCodeTokenizer#TT_WORD})
*
* @return true if a match occurred, and false otherwise
*/
public boolean match( Token T, String token, int iType )
{
return match( T, token, iType, false );
}
/**
* Possibly matches the specified token or name (in token). If a match occurs and bPeek is false then
* the token will be eaten and its information put into T (if T is not null).
*
* @param T the Token object to populate iff a match is found
* @param token the string object to match
* @param iType the token "type" to match (e.g. {@link SourceCodeTokenizer#TT_WORD})
* @param bPeek if true, a matching token will <b>not</b> be consumed (i.e. the stream will not advance to the next token.)
* if false, a matching token will be removed from the front of the stream.
*
* @return true if a match occurred, and false otherwise
*/
public boolean match( Token T, String token, int iType, boolean bPeek )
{
SourceCodeTokenizer tokenizer = getTokenizer();
return match( T, token, iType, bPeek, tokenizer );
}
private static boolean match( Token T, String token, int iType, boolean bPeek, SourceCodeTokenizer tokenizer )
{
boolean bMatch = false;
if( T != null )
{
tokenizer.copyInto( T );
}
int iCurrentType = tokenizer.getType();
if( token != null )
{
if( (iType == iCurrentType) || ((iType == 0) && (iCurrentType == SourceCodeTokenizer.TT_WORD)) )
{
bMatch = token.equals( tokenizer.getStringValue() );
}
}
else
{
bMatch = (iCurrentType == iType) || isValueKeyword( iType, tokenizer );
}
if( bMatch && !bPeek )
{
tokenizer.nextToken();
}
return bMatch;
}
private static boolean isValueKeyword( int iType, SourceCodeTokenizer tokenizer )
{
return iType == SourceCodeTokenizer.TT_WORD &&
tokenizer.getType() == SourceCodeTokenizer.TT_KEYWORD &&
Keyword.isValueKeyword( tokenizer.getStringValue() );
}
protected boolean match( Token T, Keyword token )
{
return match( T, token, false );
}
boolean match( Token T, Keyword token, boolean bPeek )
{
boolean bMatch = false;
if( T != null )
{
getTokenizer().copyInto( T );
}
SourceCodeTokenizer tokenizer = getTokenizer();
if( SourceCodeTokenizer.TT_KEYWORD == tokenizer.getType() )
{
bMatch = token.toString().equals( tokenizer.getStringValue() );
}
if( bMatch && !bPeek )
{
tokenizer.nextToken();
}
return bMatch;
}
void addError( ParsedElement parsedElement, ResourceKey errorMsg, Object... args )
{
verify( parsedElement, false, errorMsg, args );
}
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// Optimizations to avoid creating vararg array
void addError( ParsedElement parsedElement, ResourceKey errorMsg )
{
verify( parsedElement, false, errorMsg, EMPTY_ARRAY );
}
boolean verify( ParsedElement parsedElement, boolean bExpression, ResourceKey errorMesg, String arg0 )
{
if( !bExpression )
{
return verify( parsedElement, bExpression, false, errorMesg, (Object)arg0 );
}
return bExpression;
}
boolean verify( ParsedElement parsedElement, boolean bExpression, ResourceKey errorMesg, String... args)
{
if( !bExpression )
{
return verify( parsedElement, bExpression, false, errorMesg, args);
}
return bExpression;
}
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
boolean verify( ParsedElement parsedElement, boolean bExpression, ResourceKey errorMesg, Object... args )
{
return verify( parsedElement, bExpression, false, errorMesg, args );
}
boolean verify( ParsedElement parsedElement, boolean bExpression, IParserState parserState, ResourceKey errorMesg, Object... args )
{
return verify( parsedElement, bExpression, false, false, parserState, errorMesg, args );
}
boolean verify( ParsedElement parsedElement, boolean bExpression, boolean bNextTokenIfException, ResourceKey errorMesg, Object... args )
{
return verify( parsedElement, bExpression, bNextTokenIfException, false, PLACEHOLDER_PARSER_STATE, errorMesg, args );
}
boolean verify( ParsedElement parsedElement, boolean bExpression, boolean bNextTokenIfException, IParserState parserState, ResourceKey errorMesg, Object... args )
{
return verify( parsedElement, bExpression, bNextTokenIfException, false, parserState, errorMesg, args );
}
boolean warn( ParsedElement target, boolean bExpression, ResourceKey err, Object... args )
{
return warn( target, bExpression, makeFullParserState(), err, args );
}
boolean warn( ParsedElement target, boolean bExpression, IParserState state, ResourceKey err, Object... args )
{
return verify( target, bExpression, false, true, state, err, args );
}
boolean verifyOrWarn( ParsedElement target, boolean bExpression, boolean bWarning, ResourceKey err, Object... args )
{
return verify( target, bExpression, false, bWarning, PLACEHOLDER_PARSER_STATE, err, args );
}
private boolean verify( ParsedElement parsedElement, boolean bExpression, boolean bNextTokenIfException, boolean bWarning,
IParserState parserState, ResourceKey errorMesg, Object... args )
{
if( !bExpression )
{
if( bNextTokenIfException )
{
advanceToNextTokenSilently();
}
if( parserState == PLACEHOLDER_PARSER_STATE )
{
parserState = makeFullParserState();
}
if( bWarning )
{
if (_ignoreWarnings == null || !_ignoreWarnings.contains(errorMesg)) {
parsedElement.addParseWarning( new ParseWarning( parserState, errorMesg, args ) );
}
}
else
{
parsedElement.addParseException( new ParseException( parserState, errorMesg, args ) );
}
}
return bExpression;
}
//
// Advance to the next token so as to include the token being tested in the tokenizer's stored state
//
void advanceToNextTokenSilently()
{
try
{
getTokenizer().nextToken();
}
catch( Exception e )
{
// ignore
}
}
/**
* @return a full parser state, which includes symbol table information, a clone of the tokenizer and everything else
*/
IFullParserState makeFullParserState()
{
return new StandardParserState(null, getTokenizer(), getOffsetShift(), getLineNumShift(), getOwner().isEditorParser() );
}
/**
* @return a full parser state, which includes symbol table information, a clone of the tokenizer and everything else
*/
IFullParserState makeFullParserStateWithSymbols()
{
ISymbolTable symTable = getOwner().isEditorParser() && getOwner().shouldSnapshotSymbols() ? getSymbolTable().copy() : null;
return new StandardParserState(symTable, getTokenizer(), getOffsetShift(), getLineNumShift(), getOwner().isEditorParser() );
}
/**
* @return a lightweight parser state, which includes *only* the offset information of the parser, and no symbol information or
* a tokenizer state.
*/
LightweightParserState makeLightweightParserState()
{
return new LightweightParserState( getTokenizer(), getOffsetShift(), getLineNumShift() );
}
protected IType resolveType( ParsedElement parsedElement, IType lhsType, int op, IType rhsType )
{
if( isDynamic( lhsType ) )
{
return lhsType;
}
if( isDynamic( rhsType ) )
{
return rhsType;
}
if( op == '+' &&
!(lhsType instanceof IErrorType) &&
!(rhsType instanceof IErrorType) &&
(JavaTypes.CHAR_SEQUENCE().isAssignableFrom( lhsType ) ||
JavaTypes.CHAR_SEQUENCE().isAssignableFrom( rhsType )) )
{
return GosuParserTypes.STRING_TYPE();
}
IType retType = resolveIfDimensionOperand( this, parsedElement, lhsType, op, rhsType );
if( retType != null )
{
verify( parsedElement,
retType != ErrorType.getInstance() &&
(!isFinalDimension( this, lhsType, parsedElement ) || BeanAccess.isNumericType( retType )),
Res.MSG_TYPE_MISMATCH,
rhsType.getDisplayName(),
lhsType.getDisplayName(),
rhsType.getTypeLoader().getModule().getName(),
lhsType.getTypeLoader().getModule().getName());
return retType;
}
// If we get here and have a non-final dimension on either the LHS or RHS, we want to just short-circuit
// and return ErrorType, without generating the type mismatch parse error. There should always already
// be a parse error indicating that one of the dimensions is non-final.
if( (isNonFinalDimension( lhsType ) || isNonFinalDimension( rhsType )) )
{
assert parsedElement.getParseExceptions().size() > 0;
return ErrorType.getInstance();
}
IType type = resolveType( lhsType, op, rhsType );
verify( parsedElement,
type != ErrorType.getInstance() &&
(!isFinalDimension( this, lhsType, parsedElement ) || BeanAccess.isNumericType( type )),
Res.MSG_TYPE_MISMATCH, rhsType.getDisplayName(), lhsType.getDisplayName() );
return type;
}
private static boolean isNonFinalDimension( IType type )
{
return JavaTypes.IDIMENSION().isAssignableFrom( type ) && !type.isFinal();
}
public static IType resolveRuntimeType( IType lhsType, int op, IType rhsType )
{
if( op == '+' &&
(JavaTypes.CHAR_SEQUENCE().isAssignableFrom( lhsType ) ||
JavaTypes.CHAR_SEQUENCE().isAssignableFrom( rhsType )) )
{
return GosuParserTypes.STRING_TYPE();
}
//## todo: support dimensional arithmetic with Dynamic types
// IType retType = resolveIfDimensionOperand( null, null, lhsType, op, rhsType );
// if( retType != null )
// {
// return retType;
// }
return resolveType( lhsType, op, rhsType );
}
public static IType resolveType( IType lhsType, int op, IType rhsType )
{
if( isDynamic( lhsType ) )
{
return lhsType;
}
if( isDynamic( rhsType ) )
{
return rhsType;
}
if( op == '+' &&
!(lhsType instanceof IErrorType) &&
!(rhsType instanceof IErrorType) &&
(JavaTypes.CHAR_SEQUENCE().isAssignableFrom( lhsType ) ||
JavaTypes.CHAR_SEQUENCE().isAssignableFrom( rhsType )) )
{
return GosuParserTypes.STRING_TYPE();
}
if( !BeanAccess.isNumericType( lhsType ) ||
!BeanAccess.isNumericType( rhsType ) )
{
return ErrorType.getInstance();
}
if( op == 0x226A /* left shift */ ||
op == 0x226B /* right shift */ ||
op == 0x22D9 /* logical right shift */ )
{
if( rhsType == JavaTypes.INTEGER() || rhsType == JavaTypes.pINT() )
{
return lhsType;
}
else
{
return ErrorType.getInstance();
}
}
if( JavaTypes.BIG_DECIMAL() == lhsType || JavaTypes.BIG_DECIMAL() == rhsType )
{
return JavaTypes.BIG_DECIMAL();
}
if( lhsType == JavaTypes.BIG_INTEGER() )
{
if( rhsType == JavaTypes.DOUBLE() || rhsType == JavaTypes.pDOUBLE() || rhsType == JavaTypes.FLOAT() || rhsType == JavaTypes.pFLOAT() )
{
return JavaTypes.BIG_DECIMAL();
}
else
{
return JavaTypes.BIG_INTEGER();
}
}
if( rhsType == JavaTypes.BIG_INTEGER() )
{
if( lhsType == JavaTypes.DOUBLE() || lhsType == JavaTypes.pDOUBLE() || lhsType == JavaTypes.FLOAT() || lhsType == JavaTypes.pFLOAT() )
{
return JavaTypes.BIG_DECIMAL();
}
else
{
return JavaTypes.BIG_INTEGER();
}
}
return handleBoxedAndPrimitiveTypes( lhsType, rhsType );
}
private static IType handleBoxedAndPrimitiveTypes( IType lhsType, IType rhsType )
{
IType retType;
if( lhsType == JavaTypes.DOUBLE() || lhsType == JavaTypes.pDOUBLE() )
{
retType = lhsType;
}
else if( rhsType == JavaTypes.DOUBLE() || rhsType == JavaTypes.pDOUBLE() )
{
retType = rhsType;
}
else if( lhsType == JavaTypes.FLOAT() || lhsType == JavaTypes.pFLOAT() )
{
retType = lhsType;
}
else if( rhsType == JavaTypes.FLOAT() || rhsType == JavaTypes.pFLOAT() )
{
retType = rhsType;
}
else if( lhsType == JavaTypes.LONG() || lhsType == JavaTypes.pLONG() )
{
retType = lhsType;
}
else if( rhsType == JavaTypes.LONG() || rhsType == JavaTypes.pLONG() )
{
retType = rhsType;
}
else if( lhsType == JavaTypes.INTEGER() || lhsType == JavaTypes.pINT() )
{
retType = lhsType;
}
else if( rhsType == JavaTypes.INTEGER() || rhsType == JavaTypes.pINT() )
{
retType = rhsType;
}
else if( lhsType == JavaTypes.SHORT() || lhsType == JavaTypes.pSHORT() ||
rhsType == JavaTypes.SHORT() || rhsType == JavaTypes.pSHORT() ||
lhsType == JavaTypes.CHARACTER() || lhsType == JavaTypes.pCHAR() ||
rhsType == JavaTypes.CHARACTER() || rhsType == JavaTypes.pCHAR() ||
lhsType == JavaTypes.BYTE() || lhsType == JavaTypes.pBYTE() ||
rhsType == JavaTypes.BYTE() || rhsType == JavaTypes.pBYTE() )
{
// Always widen short/byte operations up to int in conformance with Java and to avoid unnecessary overflow
retType = JavaTypes.pINT();
}
else
{
retType = ErrorType.getInstance();
}
retType = makeBoxedTypeIfEitherOperandIsBoxed( lhsType, rhsType, retType );
return retType;
}
private static IType makeBoxedTypeIfEitherOperandIsBoxed( IType lhsType, IType rhsType, IType retType )
{
if( retType.isPrimitive() &&
(StandardCoercionManager.isBoxed( lhsType ) ||
StandardCoercionManager.isBoxed( rhsType ) ) )
{
retType = TypeLord.getBoxedTypeFromPrimitiveType( retType );
}
return retType;
}
private static IType resolveIfDimensionOperand( ParserBase parser, ParsedElement parsedElement, IType lhsType, int op, IType rhsType )
{
if( isFinalDimension( parser, lhsType, parsedElement ) )
{
IType retType = getAndAssignOperatorOverloader( lhsType, rhsType, op, parsedElement );
if( retType != null )
{
return retType;
}
if( isFinalDimension( parser, rhsType, parsedElement ) )
{
if( op == '*' || op == '/' || op == '%' )
{
// Unless the lhs overrides default behavior (via method impl e.g., divide(... )),
// multiplication is undefined between to dimensions.
if( parser != null )
{
parser.addError( parsedElement, Res.MSG_DIMENSION_MULTIPLICATION_UNDEFINED );
}
return ErrorType.getInstance();
}
if( lhsType != rhsType )
{
// If both addition operands derive from IDimension, they must be the same *exact* type.
if( parser != null )
{
parser.addError( parsedElement, Res.MSG_DIMENSION_ADDITION_MUST_BE_SAME_TYPE );
}
return ErrorType.getInstance();
}
}
else if( op == '+' || op == '-' )
{
// Operands must both be Dimensions for addition or subtraction
if( parser != null )
{
parser.addError( parsedElement, Res.MSG_DIMENSION_ADDITION_MUST_BE_SAME_TYPE );
}
return ErrorType.getInstance();
}
return lhsType;
}
else if( isFinalDimension( parser, rhsType, parsedElement ) )
{
if( op == '*' || op == '+' )
{
// Multiplication and addition are commutative, so we can use the override on the rhs if one exists...
IType retType = getAndAssignOperatorOverloader( rhsType, lhsType, op, parsedElement );
if( retType != null )
{
Expression temp = ((ArithmeticExpression)parsedElement).getLHS();
((ArithmeticExpression)parsedElement).setLHS( ((ArithmeticExpression)parsedElement).getRHS() );
((ArithmeticExpression)parsedElement).setRHS( temp );
return retType;
}
}
if( op == '+' || op == '-' )
{
// Operands must both be Dimensions for addition or subtraction
if( parser != null )
{
parser.addError( parsedElement, Res.MSG_DIMENSION_ADDITION_MUST_BE_SAME_TYPE );
}
return ErrorType.getInstance();
}
if( op == '/' || op == '%' )
{
// Can't divide a scalar value by a dimension; it's nonsense
if( parser != null )
{
parser.addError( parsedElement, Res.MSG_DIMENSION_DIVIDE_SCALAR_BY_DIMENSION );
}
return ErrorType.getInstance();
}
return rhsType;
}
return null;
}
private static boolean isFinalDimension( ParserBase parser, IType lhsType, ParsedElement pe )
{
if( JavaTypes.IDIMENSION().isAssignableFrom( lhsType ) )
{
if( !lhsType.isFinal() )
{
if( parser != null )
{
parser.addError( pe, Res.MSG_DIMENSION_MUST_BE_FINAL, lhsType.getName() );
}
return false;
}
return true;
}
return false;
}
private static IType getAndAssignOperatorOverloader( IType lhsType, IType rhsType, int op, ParsedElement parsedElement )
{
IMethodInfo mi = findMathOpMethod( lhsType, op, rhsType );
if( mi != null )
{
if( parsedElement instanceof IOverridableOperation )
{
((IOverridableOperation)parsedElement).setOverride( mi );
}
return mi.getReturnType();
}
return null;
}
public static IMethodInfo findMathOpMethod( IType lhsType, int op, IType rhsType )
{
String strMethod;
switch( op )
{
case '+':
strMethod = "add";
break;
case '-':
strMethod = "subtract";
break;
case '*':
strMethod = "multiply";
break;
case '/':
strMethod = "divide";
break;
case '%':
strMethod = "modulo";
break;
default:
return null;
}
return lhsType.getTypeInfo().getMethod( strMethod, rhsType );
}
protected ISymbol resolveSymbol( ParsedElement e, String strName, boolean ignoreFunctionSymbols )
{
assert getSymbolTable() != null : CommonServices.getGosuLocalizationService().localize( Res.MSG_NULL_SYMBOL_TABLE );
ISymbol sym;
// Uberhack alert: query path root symbols can mask existing symbols, so we need to see if one of these
// is around prior to checking for captured symbol values
ISymbol symbol = findSymbol( strName, ignoreFunctionSymbols );
if( symbol instanceof QueryPathRootSymbol )
{
return symbol;
}
if( isParsingBlock() || isOrIsEnclosedByAnonymousClass( getGosuClass() ) && !getOwner().isParsingAnnotation() )
{
sym = captureSymbol( getCurrentEnclosingGosuClass(), strName, e );
}
else
{
sym = findSymbol( strName, ignoreFunctionSymbols );
}
if( sym == null && getGosuClass() != null )
{
sym = getGosuClass().getExternalSymbol( strName );
}
if( sym == null )
{
// Produces symbol w/ errant type if not found
sym = resolveNamespaceSymbol( e, strName );
}
else if( sym.getType() instanceof IErrorType && !e.hasParseExceptions() )
{
// Ensure a symbol defined with a null type is marked with a parse exception
SymbolNotFoundException pe = new SymbolNotFoundException(makeFullParserState(), strName);
List<IType> contextTypes = getOwner().getContextTypes();
if( contextTypes.size() == 1 )
{
pe.setExpectedType( contextTypes.get( 0 ) );
}
e.addParseException(pe);
}
if( sym == null )
{
throw new IllegalStateException( "Should never return null symbol: " + sym.getName() );
}
else if( e != null )
{
sym = handleForwardReference( e, sym );
}
return sym;
}
private ISymbol handleForwardReference( ParsedElement e, ISymbol sym )
{
if( !getOwner().isParsingFieldInitializer() ||
(!(sym instanceof DynamicSymbol) && (!(sym instanceof DynamicPropertySymbol) || ((DynamicPropertySymbol)sym).getVarIdentifier() == null)) ||
sym.getScriptPart() == null ||
sym.getScriptPart().getContainingType() != getGosuClass() )
{
return sym;
}
String varName = sym instanceof DynamicSymbol
? sym.getName()
: ((DynamicPropertySymbol)sym).getVarIdentifier();
VarStatement varStmt = (VarStatement)getGosuClass().getMemberField( varName );
if( varStmt == null )
{
varStmt = ((IGosuClassInternal)getGosuClass()).getStaticField( varName );
}
if( varStmt == getOwner().peekParsingFieldInitializer() )
{
// Reference to field we are currently definition parsing e.g., var _x = _x
// Allow stuff like: var Logger = Logger.make( "fubar" )
// Logger in the lhs is a relative type ref, not a ref to the l-value
sym = resolveNamespaceSymbol( e, sym.getName() );
}
else
{
verify( e, varStmt == null || varStmt.isDefinitionParsed(), Res.MSG_ILLEGAL_FORWARD_REFERENCE );
}
return sym;
}
private boolean isOrIsEnclosedByAnonymousClass( ICompilableType type )
{
return type instanceof IGosuClassInternal &&
(type.isAnonymous() || isOrIsEnclosedByAnonymousClass( type.getEnclosingType() ));
}
protected ISymbol resolveNamespaceSymbol( ParsedElement e, String strName )
{
ISymbol sym;
INamespaceType namespaceType = resolveNamespace( strName );
if( namespaceType != null )
{
sym = new Symbol( strName, namespaceType, null );
}
else
{
maybeAddLocalsOfEnclosingType();
List<IType> contextTypes = getOwner().getContextTypes();
SymbolNotFoundException pe = new SymbolNotFoundException(makeFullParserStateWithSymbols(), strName);
if( contextTypes.size() == 1 )
{
pe.setExpectedType( contextTypes.get( 0 ) );
}
e.addParseException(pe);
sym = new Symbol( strName, ErrorType.getInstance(), null );
}
return sym;
}
private void maybeAddLocalsOfEnclosingType()
{
if( shouldSnapshotSymbols() )
{
// add in outer class symbols if this is an editor parser
ISymbolTable completionSymbolTable = getSymbolTable();
ICompilableTypeInternal gosuClass = getGosuClass();
if( gosuClass != null )
{
ICompilableTypeInternal enclosingType = gosuClass.getEnclosingType();
while( enclosingType != null)
{
ISymbolTable symbolTableForClass = getSymbolTableForClass( enclosingType );
if( symbolTableForClass != null )
{
for( Object key : symbolTableForClass.getSymbols().keySet() )
{
String name = key.toString();
if( completionSymbolTable.getSymbol( name ) == null )
{
completionSymbolTable.putSymbol( symbolTableForClass.getSymbol( name ) );
}
}
}
enclosingType = enclosingType.getEnclosingType();
}
}
}
}
protected INamespaceType resolveNamespace( String strName )
{
INamespaceType namespaceType = TypeSystem.getNamespace( strName );
if( namespaceType == null )
{
ITypeUsesMap typeUsesMap = getOwner().getTypeUsesMap();
if( typeUsesMap != null )
{
namespaceType = typeUsesMap.resolveRelativeNamespaceInAllNamespaces( strName );
}
if( namespaceType == null && strName.equals( IGosuProgram.PACKAGE ) )
{
return PROGRAM_NAMESPACE;
}
}
return namespaceType;
}
protected void captureAllSymbols( ICompilableTypeInternal anonClass, ICompilableTypeInternal enclosingClass, List<ICapturedSymbol> capturedSymbols )
{
ISymbolTable tableForClass = getSymbolTableForClass( enclosingClass );
if( tableForClass == null )
{
if( anonClass instanceof IGosuProgram )
{
// Presumably this is a recursive call where the caller is an eval program, so give it its enclosing class' captured symbols
Map<String, ICapturedSymbol> enclCaptured = anonClass.getCapturedSymbols();
capturedSymbols.addAll( enclCaptured.values() );
}
return;
}
@SuppressWarnings({"unchecked"})
Map<CharSequence, ISymbol> symbols = (Map<CharSequence, ISymbol>)tableForClass.getSymbols();
for( ISymbol sym : symbols.values() )
{
if( sym != null && sym.canBeCaptured() && (enclosingClass == null || enclosingClass.getExternalSymbol( sym.getName() ) == null) )
{
ICapturedSymbol capturedSymbol = sym.makeCapturedSymbol( sym.getName(),
getSymbolTable(),
anonClass == null ? new StandardScope() : getScope( anonClass ) );
if( anonClass != null )
{
if( anonClass instanceof IBlockClassInternal )
{
IBlockExpression expression = ((IBlockClassInternal)anonClass).getBlock();
if( !expression.isWithinScope( sym, tableForClass ) )
{
anonClass.addCapturedSymbol( capturedSymbol );
}
}
else
{
anonClass.addCapturedSymbol( capturedSymbol );
}
}
else
{
capturedSymbols.add( capturedSymbol );
}
}
}
if( enclosingClass != null && enclosingClass.isAnonymous() )
{
captureAllSymbols( enclosingClass, enclosingClass.getEnclosingType(), capturedSymbols );
}
}
protected ISymbol captureSymbol( ICompilableTypeInternal anonClass, String strName,ParsedElement e )
{
//never capture this, outer or super
if( Keyword.KW_this.equals( strName ) ||
Keyword.KW_super.equals( strName ) ||
Keyword.KW_outer.equals( strName ) )
{
return findSymbol( strName, true );
}
try
{
// check if we've already captured this symbol
ISymbol sym = anonClass.getCapturedSymbol( strName );
if( sym == null )
{
// see if we have access to the uncaptured version of this symbol
sym = getUncapturedSymbol( anonClass, strName );
// if we don't have access to the uncaptured version of this symbol, resolve it
// through the enclosing class
if( sym == null && isOrIsEnclosedByAnonymousClass( anonClass ) )
{
ICompilableTypeInternal enclosingType = anonClass.getEnclosingType();
if( enclosingType == null )
{
sym = resolveForNullEnclosingClass( strName );
}
else
{
sym = captureSymbol( enclosingType, strName, e );
}
warnOnPcfVariablesHack( e, sym );
if( sym != null && sym.canBeCaptured() && (enclosingType == null || enclosingType.getExternalSymbol( strName ) == null) )
{
// and wrap it up as a captured symbol
ICapturedSymbol capturedSymbol = sym.makeCapturedSymbol( strName,
getSymbolTable(),
getScope( anonClass ) );
anonClass.addCapturedSymbol( capturedSymbol );
sym = capturedSymbol;
}
}
}
return sym;
}
catch( IllegalStateException ise )
{
if( e != null )
{
e.addParseException( new ParseException( makeFullParserState(), Res.MSG_BAD_CAPTURE_TYPE ) );
}
return null;
}
}
private void warnOnPcfVariablesHack( ParsedElement e, ISymbol sym )
{
if( sym instanceof INonCapturableSymbol )
{
// Ugh... have to do this for PCF variables
e.addParseWarning( new ParseWarning( makeFullParserState(), Res.MSG_POTENTIALLY_BAD_CAPTURE ) );
}
}
private IScope getScope( ICompilableType anonClass )
{
if( anonClass instanceof IBlockClassInternal )
{
return ((IBlockClassInternal)anonClass).getBlock().getScope();
}
else
{
return getSymbolTableForClass( anonClass ).peekIsolatedScope();
}
}
private ISymbol resolveForNullEnclosingClass( String strName )
{
ISymbol symbol = getSymbolTable().getSymbol( strName );
if( symbol == null )
{
symbol = CompiledGosuClassSymbolTable.instance().getSymbol( strName );
}
return symbol;
}
protected ISymbol getUncapturedSymbol( ICompilableType gsClass, String strName )
{
ISymbolTable symTable = getSymbolTableForClass( gsClass );
if( symTable == null )
{
return null;
}
ISymbol symbol = symTable.getSymbol( strName );
if( symbol == null )
{
symbol = getSymbolTable().getSymbol( strName );
if( symbol != null )
{
symTable = getSymbolTable();
}
}
if( gsClass instanceof IBlockClassInternal )
{
IBlockExpression block = ((IBlockClassInternal)gsClass).getBlock();
return block.isWithinScope( symbol, symTable ) ? symbol : null;
}
else
{
return symbol;
}
}
private ISymbolTable getSymbolTableForClass( ICompilableType gsClass )
{
if( gsClass == null )
{
return getSymbolTable();
}
else if( gsClass instanceof IBlockClassInternal )
{
return getSymbolTableForClass( gsClass.getEnclosingType() );
}
else
{
return CompiledGosuClassSymbolTable.instance().getSymbolTableForCompilingClass( gsClass );
}
}
private ISymbol findSymbol( String strName, boolean ignoreFunctionSymbols )
{
return findSymbol( strName, getSymbolTable(), ignoreFunctionSymbols );
}
private ISymbol findSymbol( String strName, ISymbolTable symTable, boolean ignoreFunctionSymbols )
{
if( Keyword.KW_this.getName().equals( strName ) ) {
if( isEvalClass() ) {
// In eval expressions 'this' = 'outer' since the eval expression is wrapped in an anonymous inner class
strName = Keyword.KW_outer.getName();
}
else if( getGosuClass() instanceof IGosuProgram ) {
// 'this' must be an external symbol for non-eval programs e.g., debugger expressions
return null;
}
}
ISymbol sym = symTable.getSymbol( strName );
if( sym == null && !ignoreFunctionSymbols )
{
List dfsDecls = getOwner().getDfsDeclsForFunction( strName );
sym = dfsDecls.isEmpty() ? null : (ISymbol)dfsDecls.get( dfsDecls.size() - 1 );
}
return sym;
}
protected boolean isEvalClass()
{
return getGosuClass() instanceof IGosuProgram &&
getGosuClass().isAnonymous();
}
protected void verifyComparable( IType lhsType, Expression rhs, boolean bWarnOnCoercion )
{
verifyComparable( lhsType, rhs, false, bWarnOnCoercion );
}
protected void verifyComparable( IType lhsType, Expression rhs, boolean bBiDirectional, boolean bWarnOnCoercion )
{
verifyComparable( lhsType, rhs, bBiDirectional, bWarnOnCoercion, makeFullParserState() );
}
protected void verifyComparable( IType lhsType, Expression rhs, boolean bBiDirectional, boolean bWarnOnCoercion, IParserState state )
{
IType rhsType = rhs.getType();
if (TypeSystem.isDeleted(lhsType) || TypeSystem.isDeleted(rhsType)) {
return;
}
if( lhsType != JavaTypes.pVOID() &&
(rhsType == GosuParserTypes.NULL_TYPE() && !(rhs instanceof NullExpression)) )
{
if( rhs instanceof Identifier && ((Identifier)rhs).getSymbol() instanceof AbstractDynamicSymbol )
{
rhs.addParseException( new ParseException( state, Res.MSG_FIELD_TYPE_HAS_NOT_BEEN_INFERRED ) );
}
}
if( rhsType != null )
{
verifyTypesComparable( rhs, lhsType, rhsType, bBiDirectional, bWarnOnCoercion, state );
if( lhsType == GosuParserTypes.DATETIME_TYPE() )
{
if( rhs instanceof StringLiteral )
{
String str = ((StringLiteral)rhs).getValue();
if( str != null )
{
try
{
CommonServices.getCoercionManager().isDateTime( str );
}
catch( java.text.ParseException e )
{
ParseException pe = ParseException.wrap( e, state );
pe.setExpectedType( lhsType );
rhs.addParseException( pe );
}
}
}
}
else if( BeanAccess.isBeanType( lhsType ) &&
rhs instanceof Literal && !(rhs instanceof DefaultParamValueLiteral) && !(rhs instanceof TypeLiteral) && !(rhs instanceof NullExpression) && !rhs.hasParseExceptions() )
{
try
{
Object valueLiteral = rhs.evaluate();
List<IType> inferringTypes = getCurrentlyInferringTypes();
IType verifyType;
if( !inferringTypes.isEmpty() )
{
verifyType = TypeLord.boundTypes( lhsType, inferringTypes );
}
else
{
verifyType = lhsType;
}
if( !CommonServices.getEntityAccess().verifyValueForType( verifyType, valueLiteral ) )
{
rhs.addParseException( new ParseException(
state, lhsType, Res.MSG_VALUE_MISMATCH, valueLiteral, TypeSystem.getUnqualifiedClassName( lhsType ) ) );
}
}
catch( IncompatibleTypeException ite )
{
Object valueLiteral = rhs.evaluate();
rhs.addParseException( new ParseException(
state, lhsType, Res.MSG_VALUE_MISMATCH, valueLiteral, TypeSystem.getUnqualifiedClassName( lhsType ) ) );
}
catch( RuntimeException re )
{
//noinspection ThrowableResultOfMethodCallIgnored
rhs.addParseException( ParseException.wrap( re, state ) );
}
}
}
}
protected IType verifyTypesComparable( ParsedElement element, IType lhsType, IType rhsType, boolean bBiDirectional,
boolean bWarnOnCoercion )
{
return verifyTypesComparable( element, lhsType, rhsType, bBiDirectional, bWarnOnCoercion, makeFullParserState() );
}
protected IType verifyTypesComparable( ParsedElement element, IType lhsType, IType rhsType, boolean bBiDirectional,
boolean bWarnOnCoercion, IParserState state )
{
try
{
CommonServices.getCoercionManager().verifyTypesComparable( lhsType, rhsType, bBiDirectional );
if( bWarnOnCoercion &&
CommonServices.getEntityAccess().isWarnOnImplicitCoercionsOn() &&
CommonServices.getCoercionManager().coercionRequiresWarningIfImplicit( lhsType, rhsType ) )
{
if( CommonServices.getEntityAccess().getLanguageLevel().allowAllImplicitCoercions() )
{
element.addParseWarning( new ImplicitCoercionWarning( state,
Res.MSG_IMPLICIT_COERCION_WARNING,
lhsType,
rhsType.getDisplayName(),
lhsType.getDisplayName() ) );
}
else
{
element.addParseException( new ImplicitCoercionError( state,
Res.MSG_IMPLICIT_COERCION_ERROR,
lhsType,
rhsType.getDisplayName(),
lhsType.getDisplayName() ) );
}
}
if( rhsType instanceof ErrorType )
{
List<IParseIssue> pes = element.getParseExceptions();
if( pes.size() > 0 )
{
ParseException pe = (ParseException)pes.get( pes.size() - 1 );
pe.setExpectedType( lhsType );
}
}
}
catch( ParseIssue issue )
{
ParseException wrappedPe = ParseException.wrap( issue, state );
wrappedPe.setExpectedType( lhsType );
element.addParseException( wrappedPe );
}
return lhsType;
}
public void verifyNonVoidExpression(Expression eas) {
if( eas != null )
{
verify( eas, eas instanceof NullExpression || eas.getType() != JavaTypes.pVOID(), Res.MSG_VOID_EXPRESSION_NOT_ALLOWED );
}
}
ICompilableTypeInternal getGosuClass()
{
return getOwner() == null || getOwner().getScriptPart() == null
? null
: getOwner().getScriptPart().getContainingType() instanceof ICompilableTypeInternal
? (ICompilableTypeInternal)getOwner().getScriptPart().getContainingType()
: null;
}
ClassStatement getClassStatement()
{
return null;
}
ModifierInfo parseModifiers()
{
return parseModifiers( false );
}
ModifierInfo parseModifiers( boolean bIgnoreErrors )
{
int iOffsetList = getTokenizer().getTokenStart();
int iLineNumList = getTokenizer().getLineNumber();
int iColumnList = getTokenizer().getTokenColumn();
ICompilableType gsClass = getGosuClass();
boolean bNotInterface = gsClass == null || (gsClass instanceof IGosuEnhancementInternal) || !gsClass.isInterface();
ParsedElement elem = getClassStatement();
bIgnoreErrors = bIgnoreErrors || elem == null;
List<IGosuAnnotation> annotations = Collections.emptyList();
ModifierInfo modifiers = new ModifierInfo(0);
int iModifiers = 0;
DocCommentBlock block = null;
boolean matchedStatic = false;
boolean matchedAbstract = false;
while( true )
{
if( match( null, SourceCodeTokenizer.TT_EOF ) )
{
modifiers.setModifiers( -1 );
return modifiers;
}
if( block == null )
{
block = popLastComment();
if( block != null )
{
modifiers.setDescription( block.getDescription() );
if( annotations.isEmpty() )
{
annotations = new ArrayList<IGosuAnnotation>( 2 );
}
annotations.addAll( block.getAnnotations() );
}
}
if( match( null, null, '@', true ) )
{
if( getOwner() == null )
{
match( null, '@' );
throw new IllegalStateException( "Found null owning parser" );
}
if( annotations.isEmpty() )
{
annotations = new ArrayList<IGosuAnnotation>( 2 );
}
parseAnnotation( annotations );
}
else if( match( null, Keyword.KW_private ) )
{
verify( elem, bIgnoreErrors || bNotInterface || gsClass.getEnclosingType() != null, Res.MSG_NOT_ALLOWED_IN_INTERFACE );
verifyNoAccessibilityModifierDefined( elem, bIgnoreErrors, iModifiers, Keyword.KW_private );
verifyNoHideOverrideModifierDefined( elem, bIgnoreErrors, iModifiers, Keyword.KW_private );
iModifiers = Modifier.setPrivate( iModifiers, true );
}
else if( match( null, Keyword.KW_internal ) )
{
verify( elem, bIgnoreErrors || bNotInterface || gsClass.getEnclosingType() != null, Res.MSG_NOT_ALLOWED_IN_INTERFACE );
verifyNoAccessibilityModifierDefined( elem, bIgnoreErrors, iModifiers, Keyword.KW_internal );
iModifiers = Modifier.setInternal( iModifiers, true );
}
else if( match( null, Keyword.KW_protected ) )
{
verify( elem, bIgnoreErrors || bNotInterface || gsClass.getEnclosingType() != null, Res.MSG_NOT_ALLOWED_IN_INTERFACE );
verifyNoAccessibilityModifierDefined( elem, bIgnoreErrors, iModifiers, Keyword.KW_protected );
iModifiers = Modifier.setProtected( iModifiers, true );
}
else if( match( null, Keyword.KW_public ) )
{
verifyNoAccessibilityModifierDefined( elem, bIgnoreErrors, iModifiers, Keyword.KW_public );
iModifiers = Modifier.setPublic( iModifiers, true );
}
else if( match( null, Keyword.KW_static ) )
{
verifyNoAbstractHideOverrideStaticModifierDefined( elem, bIgnoreErrors, iModifiers, Keyword.KW_static, matchedStatic );
iModifiers = Modifier.setStatic( iModifiers, true );
matchedStatic = true;
}
else if( match( null, Keyword.KW_abstract ) )
{
verifyNoAbstractHideOverrideStaticModifierDefined( elem, bIgnoreErrors, iModifiers, Keyword.KW_abstract, matchedAbstract );
iModifiers = Modifier.setAbstract( iModifiers, true );
matchedAbstract = true;
}
else if( match( null, Keyword.KW_override ) )
{
verifyNoAbstractHideOverrideStaticModifierDefined( elem, bIgnoreErrors, iModifiers, Keyword.KW_override );
verify( elem, bIgnoreErrors || !Modifier.isPrivate( iModifiers ), Res.MSG_ILLEGAL_USE_OF_MODIFIER, Keyword.KW_private, Keyword.KW_override );
iModifiers = Modifier.setOverride( iModifiers, true );
}
else if( match( null, Keyword.KW_hide ) )
{
verifyNoAbstractHideOverrideStaticModifierDefined( elem, bIgnoreErrors, iModifiers, Keyword.KW_hide );
verify( elem, bIgnoreErrors || !Modifier.isPrivate( iModifiers ), Res.MSG_ILLEGAL_USE_OF_MODIFIER, Keyword.KW_private, Keyword.KW_hide );
iModifiers = Modifier.setHide( iModifiers, true );
}
else if( match( null, Keyword.KW_final ) )
{
verify( elem, bIgnoreErrors || !Modifier.isAbstract( iModifiers ), Res.MSG_ILLEGAL_USE_OF_MODIFIER, Keyword.KW_abstract, Keyword.KW_final );
verify( elem, bIgnoreErrors || !Modifier.isFinal( iModifiers ), Res.MSG_ILLEGAL_USE_OF_MODIFIER, Keyword.KW_final, Keyword.KW_final );
iModifiers = Modifier.setFinal( iModifiers, true );
}
else if( match( null, Keyword.KW_transient ) )
{
verify( elem, bIgnoreErrors || !Modifier.isTransient( iModifiers ), Res.MSG_ILLEGAL_USE_OF_MODIFIER, Keyword.KW_transient, Keyword.KW_transient );
iModifiers = Modifier.setTransient( iModifiers, true );
}
else
{
break;
}
}
if( match( null, Keyword.KW_function, true ) ||
match( null, Keyword.KW_property, true ) ||
match( null, Keyword.KW_var, true ) ||
match( null, Keyword.KW_delegate, true ) )
{
verifyNoCombinedPrivateAbstract( elem, bIgnoreErrors, iModifiers );
}
modifiers.setModifiers( iModifiers );
modifiers.setAnnotations( annotations );
if( !bIgnoreErrors )
{
pushModifierList( iOffsetList, iLineNumList, iColumnList );
}
return modifiers;
}
private void pushModifierList( int iOffsetList, int iLineNumList, int iColumnList )
{
ModifierListClause e = new ModifierListClause();
pushExpression( e );
boolean bZeroLength = getTokenizer().getTokenStart() <= iOffsetList;
setLocation( iOffsetList, iLineNumList, iColumnList, bZeroLength, true );
popExpression();
}
protected void eatOptionalSemiColon( boolean bEat )
{
if( bEat && match( null, ';' ) )
{
// Eat optional semi-colon on interface method decls.
}
}
protected static boolean isDynamic( IType type )
{
return type instanceof IPlaceholder && ((IPlaceholder)type).isPlaceholder();
}
protected void parseAnnotation( List<IGosuAnnotation> annotations )
{
int iOffset = getTokenizer().getTokenStart();
int iLineNum = getTokenizer().getLineNumber();
int iColumn = getTokenizer().getTokenColumn();
match( null, '@' );
getOwner().pushParsingStaticMember( true );
IType type = ErrorType.getInstance();
Expression e = NOT_SET_EXPRESSION;
int end;
try
{
if( getGosuClass() == null || getGosuClass().shouldFullyCompileAnnotations() )
{
getOwner().parseNewExpressionOrAnnotation( true );
setLocation( iOffset, iLineNum, iColumn, true );
e = popExpression();
end = e.getLocation().getExtent() + 1;
type = e.getType();
maybeVerifyAnnotationArgs( e );
}
else
{
getOwner().parseTypeLiteral();
Expression typeLiteral = popExpression();
if( match( null, '(' ) )
{
end = getTokenizer().getTokenStart();
Token token = getOwner().eatBlock( '(', ')', false, true );
if( token != null )
{
end = token.getTokenEnd();
}
}
else
{
end = typeLiteral.getLocation().getExtent() + 1;
}
if( typeLiteral instanceof TypeLiteral && !typeLiteral.hasParseExceptions())
{
type = (IType)typeLiteral.evaluate();
}
}
}
finally
{
getOwner().popParsingStaticMember();
}
if( type == null )
{
type = ErrorType.getInstance();
}
getOwner().checkInstruction( true );
if( !ErrorType.getInstance().equals( type ) && type.getTypeLoader() != null )
{
IModule module = type.getTypeLoader().getModule();
TypeSystem.pushModule( module );
try
{
boolean bAnnotation = JavaTypes.IANNOTATION().isAssignableFrom( type ) ||
JavaTypes.ANNOTATION().isAssignableFrom( type );
verify( e, bAnnotation || type instanceof IErrorType, Res.MSG_TYPE_NOT_ANNOTATION, type.getName() );
}
finally
{
TypeSystem.popModule( module );
}
}
GosuAnnotation annotationInfo = new GosuAnnotation( getGosuClass(), type, e, iOffset, end );
if( e instanceof AnnotationExpression )
{
((AnnotationExpression)e).setAnnotation( annotationInfo );
}
annotations.add( annotationInfo );
}
private void maybeVerifyAnnotationArgs( Expression e )
{
if( e instanceof AnnotationExpression )
{
AnnotationExpression ae = (AnnotationExpression)e;
if( JavaTypes.ANNOTATION().isAssignableFrom( ae.getType() ) && ae.getArgs() != null)
{
for( Expression arg : ae.getArgs() )
{
verifyOrWarn( arg, arg.isCompileTimeConstant(),
JavaTypes.IANNOTATION().isAssignableFrom( e.getType() ) && CommonServices.getEntityAccess().getLanguageLevel().allowNonLiteralArgsForJavaAnnotations(),
Res.MSG_NON_LITERAL_ARG_TO_JAVA_ANNOTATION );
}
}
}
}
void verifyModifiers( IParsedElement pe, ModifierInfo modInfo, UsageTarget targetType )
{
verifyModifiersForFeature( pe, modInfo );
verifyAnnotations( modInfo, targetType );
}
protected void verifyModifiersForFeature( IParsedElement pe, ModifierInfo modInfo )
{
if( getGosuClass().getEnclosingType() != null &&
!getGosuClass().isStatic())
{
verify( (ParsedElement) pe, !Modifier.isStatic( modInfo.getModifiers() ), Res.MSG_STATIC_MODIFIER_NOT_ALLOWED_HERE );
}
}
void verifyAnnotations( ModifierInfo modInfo, UsageTarget targetType )
{
List<IType> annotationTypes = new ArrayList<IType>();
for( IGosuAnnotation annotation : modInfo.getAnnotations() )
{
if( annotation instanceof GosuAnnotation )
{
IType annotationType = annotation.getType();
if( !(annotationType instanceof ErrorType) )
{
UsageModifier modifer = UsageModifier.getUsageModifier( targetType, annotationType );
if( modifer.equals( UsageModifier.None ) )
{
annotation.getExpression().addParseException( Res.MSG_ANNOTATION_WHEN_NONE_ALLOWED, annotation.getName(), targetType.toString().toLowerCase() );
}
else if( modifer.equals( UsageModifier.One ) && annotationTypes.contains( annotationType ) )
{
annotation.getExpression().addParseException( Res.MSG_TOO_MANY_ANNOTATIONS, annotation.getName(), targetType.toString().toLowerCase() );
}
annotationTypes.add( annotationType );
}
}
}
}
void verifyNoAbstractHideOverrideStaticModifierDefined( ParsedElement elem, boolean bIgnoreErrors, int modifier, Keyword kw ) {
verifyNoAbstractHideOverrideStaticModifierDefined( elem, bIgnoreErrors, modifier, kw, false );
}
void verifyNoAbstractHideOverrideStaticModifierDefined( ParsedElement elem, boolean bIgnoreErrors, int modifier, Keyword kw, boolean alreadyMatched )
{
if( bIgnoreErrors )
{
return;
}
verify( elem, !Modifier.isOverride( modifier ), Res.MSG_ILLEGAL_USE_OF_MODIFIER, Keyword.KW_override, kw );
verify( elem, !Modifier.isHide( modifier ), Res.MSG_ILLEGAL_USE_OF_MODIFIER, Keyword.KW_hide, kw );
if( !(elem instanceof ClassStatement) || alreadyMatched )
{
verify( elem, !Modifier.isAbstract( modifier ), Res.MSG_ILLEGAL_USE_OF_MODIFIER, Keyword.KW_abstract, kw );
verify( elem, !Modifier.isStatic( modifier ), Res.MSG_ILLEGAL_USE_OF_MODIFIER, Keyword.KW_static, kw );
}
}
void verifyNoAccessibilityModifierDefined( ParsedElement elem, boolean bIgnoreErrors, int modifier, Keyword kw )
{
if( bIgnoreErrors )
{
return;
}
verify( elem, !Modifier.isPrivate( modifier ), Res.MSG_ILLEGAL_USE_OF_MODIFIER, Keyword.KW_private, kw );
verify( elem, !Modifier.isInternal( modifier ), Res.MSG_ILLEGAL_USE_OF_MODIFIER, Keyword.KW_internal, kw );
verify( elem, !Modifier.isProtected( modifier ), Res.MSG_ILLEGAL_USE_OF_MODIFIER, Keyword.KW_protected, kw );
verify( elem, !Modifier.isPublic( modifier ), Res.MSG_ILLEGAL_USE_OF_MODIFIER, Keyword.KW_public, kw );
}
void verifyNoAbstractHideOverrideModifierDefined( ParsedElement elem, boolean bIgnoreErrors, int modifier, Keyword kw )
{
if( bIgnoreErrors )
{
return;
}
verify( elem, !Modifier.isAbstract( modifier ), Res.MSG_ILLEGAL_USE_OF_MODIFIER, Keyword.KW_abstract, kw );
verify( elem, !Modifier.isOverride( modifier ), Res.MSG_ILLEGAL_USE_OF_MODIFIER, Keyword.KW_override, kw );
verify( elem, !Modifier.isHide( modifier ), Res.MSG_ILLEGAL_USE_OF_MODIFIER, Keyword.KW_hide, kw );
}
void verifyNoHideOverrideModifierDefined( ParsedElement elem, boolean bIgnoreErrors, int modifier, Keyword kw )
{
if( bIgnoreErrors )
{
return;
}
verify( elem, !Modifier.isOverride( modifier ), Res.MSG_ILLEGAL_USE_OF_MODIFIER, Keyword.KW_override, kw );
verify( elem, !Modifier.isHide( modifier ), Res.MSG_ILLEGAL_USE_OF_MODIFIER, Keyword.KW_hide, kw );
}
void verifyNoCombinedPrivateAbstract( ParsedElement elem, boolean bIgnoreErrors, int modifier )
{
if( bIgnoreErrors )
{
return;
}
verify( elem, !(Modifier.isPrivate( modifier ) && Modifier.isAbstract( modifier )),
Res.MSG_ILLEGAL_USE_OF_MODIFIER, Keyword.KW_private, Keyword.KW_abstract );
}
void verifyNoCombinedFinalPrivateModifierDefined( ParsedElement elem, boolean bIgnoreErrors, int modifier )
{
if( bIgnoreErrors )
{
return;
}
verify( elem, !(Modifier.isPrivate( modifier ) && Modifier.isFinal( modifier )),
Res.MSG_ILLEGAL_USE_OF_MODIFIER, Keyword.KW_private, Keyword.KW_final );
}
void verifyNoCombinedFinalStaticModifierDefined( ParsedElement elem, boolean bIgnoreErrors, int modifier )
{
if( bIgnoreErrors )
{
return;
}
verify( elem, !(Modifier.isStatic( modifier ) && Modifier.isFinal( modifier )),
Res.MSG_ILLEGAL_USE_OF_MODIFIER, Keyword.KW_static, Keyword.KW_final );
}
public void setDontOptimizeStatementLists( boolean dontOptimizeStatementLists )
{
if( getOwner() != this )
{
getOwner().setDontOptimizeStatementLists( dontOptimizeStatementLists );
}
_bDontOptimizeStatementLists = dontOptimizeStatementLists;
}
public boolean isDontOptimizeStatementLists()
{
if( getOwner() != this )
{
return getOwner().isDontOptimizeStatementLists();
}
return _bDontOptimizeStatementLists;
}
public void setSubTree( List<IParseTree> subTree )
{
_subTree = subTree;
}
public void setBlocks( Stack<BlockExpression> blocks )
{
_blocks = blocks;
}
public void pushCurrentBlock( BlockExpression block )
{
if( _blocks == null )
{
_blocks = new Stack<BlockExpression>();
}
// Note: This has to come BEFORE we push the block on the stack, because it depends on
// whether or not the block stack is empty
ICompilableTypeInternal enclosingClass = getCurrentEnclosingGosuClass();
_blocks.push( block );
IBlockClassInternal blockClass = BlockClass.create( enclosingClass, block, _blocks.size() == 1 && getOwner().isParsingStaticFeature() );
block.setBlockGosuClass( blockClass );
if( enclosingClass != null )
{
enclosingClass.addBlock( blockClass );
}
}
public void addBlockToBlockStack( BlockExpression block ) {
if( _blocks == null )
{
_blocks = new Stack<BlockExpression>();
}
_blocks.push( block );
}
protected ICompilableTypeInternal getCurrentEnclosingGosuClass()
{
ICompilableTypeInternal enclosingClass;
if( _blocks == null || _blocks.isEmpty() )
{
enclosingClass = getGosuClass();
}
else
{
enclosingClass = (ICompilableTypeInternal)_blocks.peek().getBlockGosuClass();
}
if( enclosingClass == null )
{
// enclosingClass can be null, for example, if the block is inside a string template
enclosingClass = getOuterFromScriptPartStack();
}
return enclosingClass;
}
private ICompilableTypeInternal getOuterFromScriptPartStack() {
java.util.Stack<IScriptPartId> scriptPartIdStack = getOwner().getScriptPartIdStack();
for( int i = scriptPartIdStack.size() - 1; i >= 0; i-- )
{
IScriptPartId id = scriptPartIdStack.get( i );
if( id instanceof ScriptPartId )
{
IType type = id.getContainingType();
if( type instanceof ICompilableTypeInternal )
{
return (ICompilableTypeInternal)type;
}
}
}
return null;
}
void popCurrentBlock()
{
_blocks.pop();
}
public boolean isParsingBlock()
{
return _blocks != null && !_blocks.isEmpty();
}
protected void copyBlockStackTo( ParserBase otherParser )
{
if( _blocks != null )
{
for( BlockExpression block : _blocks )
{
otherParser.addBlockToBlockStack( block );
}
}
}
protected IGosuClassInternal getParsingAnonymousClass()
{
IType type = getOwner().getScriptPart() != null
? getOwner().getScriptPart().getContainingType()
: null;
return type instanceof IGosuClassInternal && ((IGosuClassInternal)type).isAnonymous()
? (IGosuClassInternal)type
: null;
}
protected Expression possiblyWrapWithImplicitCoercion( Expression expressionToCoerce, IType typeToCoerceTo )
{
return possiblyWrapWithCoercion( expressionToCoerce, typeToCoerceTo, true );
}
protected Expression possiblyWrapWithCoercion( Expression expressionToCoerce, IType typeToCoerceTo, boolean bImplicit )
{
if( expressionToCoerce == null )
{
return null;
}
if( typeToCoerceTo == null || typeToCoerceTo instanceof ErrorType )
{
return expressionToCoerce;
}
IType resolvedTypeToCoerceTo;
List<IType> inferringTypes = getCurrentlyInferringTypes();
if( inferringTypes.size() > 0 )
{
resolvedTypeToCoerceTo = TypeLord.boundTypes( typeToCoerceTo, inferringTypes );
}
else
{
resolvedTypeToCoerceTo = typeToCoerceTo;
}
IType typeToCoerceFrom = expressionToCoerce.getType();
ICoercionManager cocerionManager = CommonServices.getCoercionManager();
ICoercer coercer = cocerionManager.resolveCoercerStatically( resolvedTypeToCoerceTo, typeToCoerceFrom );
if(coercer == null)
{
return expressionToCoerce;
}
else if((JavaTypes.pVOID().equals(typeToCoerceFrom) && !(expressionToCoerce instanceof NullExpression)))
{
return expressionToCoerce;
}
else
{
TypeAsExpression tas = bImplicit ? new ImplicitTypeAsExpression() : new TypeAsExpression();
tas.setLHS( expressionToCoerce );
if( coercer instanceof IResolvingCoercer)
{
IResolvingCoercer resolvingCoercer = (IResolvingCoercer) coercer;
typeToCoerceTo = resolvingCoercer.resolveType( typeToCoerceTo, typeToCoerceFrom );
}
tas.setType( typeToCoerceTo );
tas.setCoercer( coercer );
setLocationForImplicitTypeAs( expressionToCoerce, tas );
return tas;
}
}
protected void setLocationForImplicitTypeAs( Expression expressionToCoerce, TypeAsExpression tas )
{
if( expressionToCoerce instanceof DefaultArgLiteral )
{
// DefaultArgLiterals do not exist in the parse tree
return;
}
ParseTree wrappedLoc = findAndWrapLocation( expressionToCoerce, tas );
if( wrappedLoc == null )
{
throw new IllegalStateException( "The expression wrapped with an implicit type-as did not have its location set." );
}
}
public ParseTree findAndWrapLocation( Expression oldExpr, ParsedElement newExpr )
{
ParseTree oldLoc = oldExpr.getLocation();
if( oldLoc == null )
{
return null;
}
ParseTree newLoc = newExpr.initLocation(oldLoc.getOffset(), oldLoc.getLength(), oldExpr.getLineNum(), oldExpr.getColumn(), oldLoc.getScriptPartId());
IParseTree parent = oldLoc.getParent();
newExpr.setLocation( newLoc );
newLoc.addChild( oldLoc );
if( parent != null )
{
parent.removeChild( oldLoc );
newLoc.addChild( oldLoc );
parent.addChild( newLoc );
}
List<ParseTree> locations = getOwner().getLocationsList();
for( int i = locations.size()-1; i > 0; i-- )
{
ParseTree loc = locations.get( i );
if( loc.getParsedElement() == oldExpr )
{
locations.set( i, newLoc );
break;
}
}
return newLoc;
}
private DocCommentBlock popLastComment()
{
DocCommentBlock lastComment= getOwner().getTokenizer().popLastComment();
if( lastComment != null )
{
lastComment.setOwnersTypes( getGosuClass() );
}
return lastComment;
}
public void setValidator( IGosuValidator validator )
{
_validator = validator;
}
public IGosuValidator getValidator() {
return _validator != null ? _validator : getOwner() != null && !this.equals(getOwner()) ? getOwner().getValidator() : null;
}
protected void setOffsetShift( int offsetShift )
{
_offsetShift = offsetShift;
}
public void setLineNumShift( int lineNumShift )
{
_lineNumShift = lineNumShift;
}
public int getLineNumShift()
{
return _lineNumShift;
}
public int getOffsetShift()
{
return _offsetShift;
}
protected void pushInferredTypeVars( List<IType> typeVariableTypes )
{
_inferringFunctionTypes.push( typeVariableTypes );
}
protected void popInferredFunctionTypeVariableTypes()
{
_inferringFunctionTypes.pop();
}
public List<IType> getCurrentlyInferringTypes()
{
List<IType> types = Collections.emptyList();
if( _inferringFunctionTypes.size() != 0 )
{
for( List<IType> inferringFunctionType : _inferringFunctionTypes )
{
for( IType type : inferringFunctionType )
{
if( types == Collections.EMPTY_LIST )
{
types = new ArrayList<IType>();
}
types.add( type );
}
}
}
return types;
}
public static boolean matchDeclarationKeyword( Token T, boolean bPeek, SourceCodeTokenizer tokenizer )
{
return
match( T, Keyword.KW_construct.toString(), SourceCodeTokenizer.TT_KEYWORD, bPeek, tokenizer ) ||
match( T, Keyword.KW_function.toString(), SourceCodeTokenizer.TT_KEYWORD, bPeek, tokenizer ) ||
match( T, Keyword.KW_property.toString(), SourceCodeTokenizer.TT_KEYWORD, bPeek, tokenizer ) ||
match( T, Keyword.KW_var.toString(), SourceCodeTokenizer.TT_KEYWORD, bPeek, tokenizer ) ||
match( T, Keyword.KW_delegate.toString(), SourceCodeTokenizer.TT_KEYWORD, bPeek, tokenizer ) ||
match( T, Keyword.KW_class.toString(), SourceCodeTokenizer.TT_KEYWORD, bPeek, tokenizer ) ||
match( T, Keyword.KW_interface.toString(), SourceCodeTokenizer.TT_KEYWORD, bPeek, tokenizer ) ||
match( T, Keyword.KW_structure.toString(), SourceCodeTokenizer.TT_KEYWORD, bPeek, tokenizer ) ||
match( T, Keyword.KW_enum.toString(), SourceCodeTokenizer.TT_KEYWORD, bPeek, tokenizer );
}
private static final class PlaceholderParserState implements IParserState
{
@Override
public int getLineNumber()
{
return 0;
}
@Override
public int getTokenColumn()
{
return 0;
}
@Override
public String getSource()
{
return null;
}
@Override
public int getTokenStart()
{
return 0;
}
@Override
public int getTokenEnd()
{
return 0;
}
@Override
public int getLineOffset()
{
return 0;
}
}
public boolean shouldSnapshotSymbols() {
return _snapshotSymbols;
}
public void setSnapshotSymbols() {
_snapshotSymbols = true;
}
}