/*
* Copyright 2013 Guidewire Software, Inc.
*/
package gw.internal.gosu.parser;
import gw.config.CommonServices;
import gw.fs.IFile;
import gw.internal.gosu.ir.transform.util.IRTypeResolver;
import gw.internal.gosu.parser.expressions.*;
import gw.internal.gosu.parser.statements.ArrayAssignmentStatement;
import gw.internal.gosu.parser.statements.AssertStatement;
import gw.internal.gosu.parser.statements.AssignmentStatement;
import gw.internal.gosu.parser.statements.BeanMethodCallStatement;
import gw.internal.gosu.parser.statements.BlockInvocationStatement;
import gw.internal.gosu.parser.statements.BreakStatement;
import gw.internal.gosu.parser.statements.CaseClause;
import gw.internal.gosu.parser.statements.CatchClause;
import gw.internal.gosu.parser.statements.ClasspathStatement;
import gw.internal.gosu.parser.statements.ContinueStatement;
import gw.internal.gosu.parser.statements.DelegateStatement;
import gw.internal.gosu.parser.statements.DoWhileStatement;
import gw.internal.gosu.parser.statements.EvalStatement;
import gw.internal.gosu.parser.statements.ForEachStatement;
import gw.internal.gosu.parser.statements.FunctionStatement;
import gw.internal.gosu.parser.statements.HideFieldNoOpStatement;
import gw.internal.gosu.parser.statements.IfStatement;
import gw.internal.gosu.parser.statements.LoopStatement;
import gw.internal.gosu.parser.statements.MapAssignmentStatement;
import gw.internal.gosu.parser.statements.MemberAssignmentStatement;
import gw.internal.gosu.parser.statements.MethodCallStatement;
import gw.internal.gosu.parser.statements.NamespaceStatement;
import gw.internal.gosu.parser.statements.NewStatement;
import gw.internal.gosu.parser.statements.NoOpStatement;
import gw.internal.gosu.parser.statements.NotAStatement;
import gw.internal.gosu.parser.statements.PropertyStatement;
import gw.internal.gosu.parser.statements.ReturnStatement;
import gw.internal.gosu.parser.statements.StatementList;
import gw.internal.gosu.parser.statements.SwitchStatement;
import gw.internal.gosu.parser.statements.SyntheticMemberAccessStatement;
import gw.internal.gosu.parser.statements.ThrowStatement;
import gw.internal.gosu.parser.statements.TryCatchFinallyStatement;
import gw.internal.gosu.parser.statements.TypeLoaderStatement;
import gw.internal.gosu.parser.statements.UsesStatement;
import gw.internal.gosu.parser.statements.UsesStatementList;
import gw.internal.gosu.parser.statements.UsingStatement;
import gw.internal.gosu.parser.statements.VarStatement;
import gw.internal.gosu.parser.statements.WhileStatement;
import gw.internal.gosu.parser.types.ConstructorType;
import gw.internal.gosu.template.TemplateGenerator;
import gw.internal.gosu.util.StringUtil;
import gw.lang.IReentrant;
import gw.lang.annotation.UsageTarget;
import gw.lang.function.IBlock;
import gw.lang.ir.IRType;
import gw.lang.parser.ExternalSymbolMapForMap;
import gw.lang.parser.GlobalScope;
import gw.lang.parser.GosuParserFactory;
import gw.lang.parser.GosuParserTypes;
import gw.lang.parser.IBlockClass;
import gw.lang.parser.ICapturedSymbol;
import gw.lang.parser.ICoercer;
import gw.lang.parser.ICoercionManager;
import gw.lang.parser.IDynamicFunctionSymbol;
import gw.lang.parser.IDynamicPropertySymbol;
import gw.lang.parser.IDynamicSymbol;
import gw.lang.parser.IExpression;
import gw.lang.parser.IFileContext;
import gw.lang.parser.IFunctionSymbol;
import gw.lang.parser.IGosuParser;
import gw.lang.parser.IGosuValidator;
import gw.lang.parser.IHasInnerClass;
import gw.lang.parser.IHasType;
import gw.lang.parser.IInjectedSymbol;
import gw.lang.parser.ILanguageLevel;
import gw.lang.parser.IParseIssue;
import gw.lang.parser.IParseResult;
import gw.lang.parser.IParseTree;
import gw.lang.parser.IParsedElement;
import gw.lang.parser.IParsedElementWithAtLeastOneDeclaration;
import gw.lang.parser.IParserState;
import gw.lang.parser.IResolvingCoercer;
import gw.lang.parser.IScriptPartId;
import gw.lang.parser.ISource;
import gw.lang.parser.ISourceCodeTokenizer;
import gw.lang.parser.ISymbol;
import gw.lang.parser.ISymbolTable;
import gw.lang.parser.IToken;
import gw.lang.parser.ITokenizerInstructor;
import gw.lang.parser.ITypeUsesMap;
import gw.lang.parser.Keyword;
import gw.lang.parser.MemberAccessKind;
import gw.lang.parser.ParserOptions;
import gw.lang.parser.PostCompilationAnalysis;
import gw.lang.parser.ScriptabilityModifiers;
import gw.lang.parser.SourceCodeReader;
import gw.lang.parser.StandardCoercionManager;
import gw.lang.parser.StandardScope;
import gw.lang.parser.SymbolType;
import gw.lang.parser.ThreadSafeSymbolTable;
import gw.lang.parser.TypeVarToTypeMap;
import gw.lang.parser.coercers.FunctionToInterfaceCoercer;
import gw.lang.parser.coercers.IdentityCoercer;
import gw.lang.parser.exceptions.DoesNotOverrideFunctionException;
import gw.lang.parser.exceptions.ErrantGosuClassException;
import gw.lang.parser.exceptions.ImplicitCoercionError;
import gw.lang.parser.exceptions.NoCtorFoundException;
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.ParseWarningForDeprecatedMember;
import gw.lang.parser.exceptions.WrongNumberOfArgsException;
import gw.lang.parser.expressions.IArithmeticExpression;
import gw.lang.parser.expressions.IBlockInvocation;
import gw.lang.parser.expressions.IImplicitTypeAsExpression;
import gw.lang.parser.expressions.IInitializerExpression;
import gw.lang.parser.expressions.ILiteralExpression;
import gw.lang.parser.expressions.IParenthesizedExpression;
import gw.lang.parser.expressions.IProgram;
import gw.lang.parser.expressions.ISynthesizedMemberAccessExpression;
import gw.lang.parser.expressions.ITypeLiteralExpression;
import gw.lang.parser.expressions.ITypeVariableDefinition;
import gw.lang.parser.expressions.ITypeVariableDefinitionExpression;
import gw.lang.parser.expressions.IVarStatement;
import gw.lang.parser.resources.Res;
import gw.lang.parser.resources.ResourceKey;
import gw.lang.parser.statements.IClasspathStatement;
import gw.lang.parser.statements.ITerminalStatement;
import gw.lang.parser.statements.ITypeLoaderStatement;
import gw.lang.parser.statements.IUsesStatement;
import gw.lang.parser.statements.IUsesStatementList;
import gw.lang.parser.statements.TerminalType;
import gw.lang.parser.template.TemplateParseException;
import gw.lang.reflect.FeatureManager;
import gw.lang.reflect.FunctionType;
import gw.lang.reflect.IBlockType;
import gw.lang.reflect.IConstructorHandler;
import gw.lang.reflect.IConstructorInfo;
import gw.lang.reflect.IConstructorType;
import gw.lang.reflect.IEnumType;
import gw.lang.reflect.IErrorType;
import gw.lang.reflect.IFunctionType;
import gw.lang.reflect.IInvocableType;
import gw.lang.reflect.IMetaType;
import gw.lang.reflect.IMethodInfo;
import gw.lang.reflect.INamespaceType;
import gw.lang.reflect.IOptionalParamCapable;
import gw.lang.reflect.IParameterInfo;
import gw.lang.reflect.IPlaceholder;
import gw.lang.reflect.IPropertyInfo;
import gw.lang.reflect.IRelativeTypeInfo;
import gw.lang.reflect.IScriptabilityModifier;
import gw.lang.reflect.IType;
import gw.lang.reflect.ITypeInfo;
import gw.lang.reflect.ITypeVariableType;
import gw.lang.reflect.MethodList;
import gw.lang.reflect.MethodScore;
import gw.lang.reflect.Modifier;
import gw.lang.reflect.TypeInfoUtil;
import gw.lang.reflect.TypeSystem;
import gw.lang.reflect.gs.ClassType;
import gw.lang.reflect.gs.GosuClassTypeLoader;
import gw.lang.reflect.gs.ICompilableType;
import gw.lang.reflect.gs.IGenericTypeVariable;
import gw.lang.reflect.gs.IGosuArrayClass;
import gw.lang.reflect.gs.IGosuClass;
import gw.lang.reflect.gs.IGosuEnhancement;
import gw.lang.reflect.gs.IGosuFragment;
import gw.lang.reflect.gs.IGosuProgram;
import gw.lang.reflect.gs.IGosuVarPropertyInfo;
import gw.lang.reflect.gs.ISourceFileHandle;
import gw.lang.reflect.gs.StringSourceFileHandle;
import gw.lang.reflect.java.GosuTypes;
import gw.lang.reflect.java.IJavaType;
import gw.lang.reflect.java.JavaTypes;
import gw.lang.reflect.module.IModule;
import gw.util.DynamicArray;
import gw.util.GosuCollectionUtil;
import gw.util.GosuExceptionUtil;
import gw.util.GosuObjectUtil;
import gw.util.SpaceEfficientHashMap;
import java.io.Closeable;
import java.io.IOException;
import java.io.StringReader;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
@SuppressWarnings({"ThrowableInstanceNeverThrown"})
public final class GosuParser extends ParserBase implements IGosuParser
{
private static final IType AMBIGUOUS_TYPE = ErrorType.getInstance( "Ambiguous Type" );
public static final IType PENDING_BOUNDING_TYPE = ErrorType.getInstance( "Pending Bounding Type");
public static ErrorType notfound = ErrorType.getInstance( "_notfound_" );
private SourceCodeTokenizer _tokenizer;
private ISymbolTable _symTable;
private boolean _bParsed;
private Stack<ParsedElement> _stack;
private Stack<DynamicFunctionSymbol> _stackDFS;
private List<ParseTree> _locations;
private Program _parsingProgram;
private ArrayList<FunctionType> _parsingFunctions;
private ArrayList<VarStatement> _parsingFieldInitializer;
private Map<String, Set<IFunctionSymbol>> _dfsDeclByName;
private ITypeUsesMap _typeUsesMap;
private String _strNamespace;
private ITokenizerInstructor _tokenizerInstructor;
private IScriptabilityModifier _scriptabilityConstraint;
private int _iBreakOk;
private int _iContinueOk;
int _iReturnOk;
private Stack<IScriptPartId> _scriptPartIdStack;
private Map<String, ITypeVariableDefinition> _typeVarsByName;
private Stack<List<IType>> _inferredContextStack = new Stack<List<IType>>();
private boolean _bThrowForWarnings;
private boolean _bStudioEditorParser;
private boolean _bWarnOnCaseIssue;
private Stack<Boolean> _parsingAbstractConstructor;
private ContextInferenceManager _ctxInferenceMgr = new ContextInferenceManager();
private Stack<IType> _blockReturnTypeStack = new Stack<IType>();
private Stack<Boolean> _parsingStaticFeature;
private boolean _bCaptureSymbolsForEval;
private boolean _parsingAnnotation;
private int _ignoreTypeDeprecation;
private boolean _bGenRootExprAccess;
private boolean _bProgramCallFunction;
private Map<IScriptPartId, Map<String, IType>> _typeCache;
private int _iStmtDepth;
private List<ParseTree> _savedLocations;
private enum JavaAnnotationParsingState {
NOT_PARSING_ENUM,
PARSING_JAVA_NON_VALUE_ENUM,
PARSING_JAVA_VALUE_ENUM
}
GosuParser( ISymbolTable symTable, IScriptabilityModifier scriptabilityConstraint )
{
this(symTable, scriptabilityConstraint, CommonServices.getEntityAccess().getDefaultTypeUses());
}
GosuParser( ISymbolTable symTable, IScriptabilityModifier scriptabilityConstraint, ITypeUsesMap tuMap )
{
super();
setOwner( this );
setWarnOnCaseIssue( Settings.WARN_ON_CASE_DEFAULT.get() == Boolean.TRUE);
setEditorParser( Settings.IDE_EDITOR_PARSER_DEFAULT.get() == Boolean.TRUE);
_symTable = symTable;
//noinspection unchecked
_typeUsesMap = tuMap.copy();
_scriptabilityConstraint = scriptabilityConstraint;
_dfsDeclByName = new HashMap<String, Set<IFunctionSymbol>>();
_stack = new Stack<ParsedElement>();
_stackDFS = new Stack<DynamicFunctionSymbol>();
_locations = new ArrayList<ParseTree>();
_parsingFunctions = new ArrayList<FunctionType>();
_parsingFieldInitializer = new ArrayList<VarStatement>();
_typeVarsByName = new HashMap<String, ITypeVariableDefinition>( 2 );
_parsingStaticFeature = new Stack<Boolean>();
_parsingAbstractConstructor = new Stack<Boolean>();
_typeCache = new HashMap<IScriptPartId, Map<String, IType>>();
_bParsed = false;
_iReturnOk = 1;
}
@Override
public void setValidator( IGosuValidator validator )
{
super.setValidator( validator );
}
//------------------------------------------------------------------------------
// -- IGosuParser implementation --
public IScriptPartId getScriptPart()
{
if( _scriptPartIdStack == null || _scriptPartIdStack.isEmpty() )
{
return null;
}
return _scriptPartIdStack.peek();
}
public Stack<IScriptPartId> getScriptPartIdStack()
{
return _scriptPartIdStack;
}
public void pushScriptPart( IScriptPartId partId )
{
if( _scriptPartIdStack == null )
{
_scriptPartIdStack = new Stack<IScriptPartId>();
}
_scriptPartIdStack.push( partId );
}
void popScriptPart( IScriptPartId partId )
{
IScriptPartId top = _scriptPartIdStack.pop();
if( top != partId )
{
throw new IllegalStateException( "Unbalanced push/pop script id" );
}
}
public void setScript( CharSequence source )
{
if( source == null )
{
source = "";
}
setScript( new SourceCodeReader( source ) );
}
public void setScript( ISource src )
{
if( src == null )
{
setScript( (CharSequence)null );
return;
}
// ISourceCodeTokenizer tokenizer = src.getTokenizer();
// if( tokenizer == null )
{
setScript( src.getSource() );
// src.setTokenizer( _tokenizer );
_tokenizer.setInstructor( _tokenizerInstructor );
if( _tokenizerInstructor != null )
{
_tokenizerInstructor.setTokenizer( _tokenizer );
}
else if( getGosuClass() instanceof IGosuTemplateInternal &&
((IGosuTemplateInternal)getGosuClass()).getTokenizerInstructor() != null )
{
_tokenizerInstructor = ((IGosuTemplateInternal)getGosuClass()).getTokenizerInstructor();
_tokenizerInstructor.setTokenizer( _tokenizer );
if( _tokenizer.getInstructor() == null )
{
_tokenizer.setInstructor( _tokenizerInstructor );
}
}
}
// else
// {
// _tokenizer = (SourceCodeTokenizer)tokenizer;
// _tokenizer.setInstructor( _tokenizerInstructor );
// if( _tokenizerInstructor != null )
// {
// _tokenizerInstructor.setTokenizer( _tokenizer );
// }
// _tokenizer.reset();
// }
reset();
}
public void setScript( SourceCodeReader reader )
{
if( _tokenizer == null )
{
_tokenizer = new SourceCodeTokenizer( reader, _tokenizerInstructor );
// Initialize the tokenizer
_tokenizer.wordChars( '_', '_' );
}
else
{
_tokenizer.reset( reader );
}
reset();
}
public void resetScript()
{
_tokenizer.reset();
reset();
}
private void reset()
{
_stack.clear();
_stackDFS.clear();
_dfsDeclByName.clear();
_typeUsesMap.clearNonDefaultTypeUses();
_strNamespace = null;
_locations.clear();
_parsingFunctions.clear();
_parsingFieldInitializer.clear();
_typeVarsByName.clear();
setParsed( false );
}
@Override
protected String getScript()
{
return _tokenizer.getSource();
}
@Override
public ISymbolTable getSymbolTable()
{
return _symTable;
}
public void setSymbolTable( ISymbolTable symTable )
{
_symTable = symTable;
}
public ITypeUsesMap getTypeUsesMap()
{
return _typeUsesMap;
}
public void setTypeUsesMap( ITypeUsesMap typeUsesMap )
{
_typeUsesMap = typeUsesMap == null ? null : typeUsesMap.copyLocalScope();
}
public String getNamespace()
{
return _strNamespace;
}
void setNamespace( String strNamespace )
{
_strNamespace = strNamespace;
if( _strNamespace != null )
{
getTypeUsesMap().addToTypeUses( strNamespace + ".*" );
}
}
public IScriptabilityModifier getVisibilityConstraint()
{
return _scriptabilityConstraint;
}
public ITokenizerInstructor getTokenizerInstructor()
{
return _tokenizerInstructor;
}
public void setTokenizerInstructor( ITokenizerInstructor tokenizerInstructor )
{
_tokenizerInstructor = tokenizerInstructor;
if( _tokenizer != null )
{
_tokenizer.setInstructor( _tokenizerInstructor );
}
}
public FunctionType peekParsingFunction()
{
return _parsingFunctions.get( 0 );
}
FunctionType popParsingFunction()
{
return _parsingFunctions.remove( 0 );
}
void pushParsingFunction( FunctionType functionType )
{
_parsingFunctions.add( 0, functionType );
}
public boolean isParsingFunction()
{
return _parsingFunctions.size() > 0;
}
public VarStatement peekParsingFieldInitializer()
{
return _parsingFieldInitializer.get( 0 );
}
VarStatement popParsingFieldInitializer()
{
return _parsingFieldInitializer.remove( 0 );
}
void pushParsingFieldInitializer( VarStatement VarStatement )
{
_parsingFieldInitializer.add( 0, VarStatement );
}
public boolean isParsingFieldInitializer()
{
return !isParsingFunction() &&
!isParsingBlock() &&
_parsingFieldInitializer.size() > 0;
}
Program peekParsingProgram()
{
return _parsingProgram;
}
boolean isParsingProgram()
{
return _parsingProgram != null;
}
public Statement parseStatements( IScriptPartId partId ) throws ParseResultsException
{
Statement stmt = parseStatements( partId, true, true );
assignRuntime( stmt, true, null, null, partId);
return stmt;
}
private Statement parseStatements( IScriptPartId partId, boolean verify, boolean isolatedScope ) throws ParseResultsException
{
pushScriptPart( partId );
try
{
int iOffset = _tokenizer.getTokenStart();
int iLineNum = _tokenizer.getLineNumber();
int iColumn = getTokenizer().getTokenColumn();
_parseStatements( isolatedScope );
Statement stmtList = peekStatement();
verify( stmtList, match( null, SourceCodeTokenizer.TT_EOF ), Res.MSG_END_OF_STMT );
_bParsed = true;
if( stmtList instanceof StatementList )
{
setLocation( iOffset, iLineNum, iColumn, true );
}
if( verify )
{
verifyParsedElement( stmtList );
}
return stmtList;
}
finally
{
popScriptPart( partId );
}
}
void _parseStatements( boolean isolatedScope )
{
StatementList stmtList = new StatementList( _symTable );
_tokenizer.nextToken();
if( _tokenizer.getInstructor() == null )
{
parseClasspathStatements();
parseTypeLoaderStatements();
}
if ( isolatedScope )
{
_symTable.pushScope();
}
try
{
UsesStatementList usesStmtList = parseUsesStatementList( true, new Token() );
List<Statement> statements = new ArrayList<Statement>();
parseStatementsAndDetectUnreachable( statements );
if( usesStmtList != null )
{
statements.add( 0, usesStmtList );
}
stmtList.setStatements( statements );
pushStatement( isDontOptimizeStatementLists() ? stmtList : stmtList.getSelfOrSingleStatement() );
}
finally
{
if ( isolatedScope )
{
_symTable.popScope();
}
}
}
void parseStatementsAndDetectUnreachable( List<Statement> statements )
{
for( Statement stmt = null; parseStatement(); )
{
stmt = popStatementAndDetectUnreachable( stmt, statements );
}
}
private Statement popStatementAndDetectUnreachable( Statement previousStatement, List<Statement> statements )
{
Statement currentStatement = popStatement();
if( !(previousStatement instanceof ReturnStatement) || !(currentStatement instanceof NotAStatement) )
{
boolean[] bAbsolute = {false};
verifyOrWarn( currentStatement,
previousStatement == null ||
currentStatement.isNoOp() ||
previousStatement.getLeastSignificantTerminalStatement( bAbsolute ) == null || !bAbsolute[0],
!CommonServices.getEntityAccess().isUnreachableCodeDetectionOn(), Res.MSG_UNREACHABLE_STMT );
}
if( isParsingFunction() && !isParsingBlock() )
{
IType returnType = peekParsingFunction().getReturnType();
if( previousStatement instanceof ReturnStatement && returnType == JavaTypes.pVOID() )
{
if( currentStatement instanceof NotAStatement ||
((currentStatement instanceof BeanMethodCallStatement ||
currentStatement instanceof MethodCallStatement ||
currentStatement instanceof MemberAssignmentStatement) &&
currentStatement.getLineNum() == previousStatement.getLineNum()) ||
(currentStatement instanceof NoOpStatement && isUnexpectedToken( currentStatement )) )
{
if( isUnexpectedToken( currentStatement ) )
{
currentStatement.clearParseExceptions();
}
verify( currentStatement, false, Res.MSG_RETURN_VAL_FROM_VOID_FUNCTION );
}
}
}
statements.add( currentStatement );
return currentStatement.isNoOp() ? previousStatement : currentStatement;
}
private boolean isUnexpectedToken( Statement statement )
{
for( IParseIssue issue : statement.getParseExceptions() )
{
if( issue.getMessageKey() == Res.MSG_UNEXPECTED_TOKEN )
{
return true;
}
}
return false;
}
public IProgram parseProgram( IScriptPartId partId ) throws ParseResultsException
{
return parseProgram( partId, true, null );
}
public IProgram parseProgram( IScriptPartId partId, IType expectedExpressionType ) throws ParseResultsException
{
return parseProgram( partId, true, expectedExpressionType );
}
public IProgram parseProgram( IScriptPartId partId, IType expectedExpressionType, IFileContext ctx, boolean assignRuntime ) throws ParseResultsException
{
return parseProgram( partId, true, false, expectedExpressionType, ctx, assignRuntime );
}
public IProgram parseProgram( IScriptPartId partId, IType expectedExpressionType, IFileContext ctx, boolean assignRuntime, boolean bDoNotThrowParseResultsException ) throws ParseResultsException
{
return parseProgram( partId, true, false, expectedExpressionType, ctx, assignRuntime, bDoNotThrowParseResultsException );
}
public IProgram parseProgram( IScriptPartId partId, boolean isolatedScope, IType expectedExpressionType ) throws ParseResultsException
{
return parseProgram( partId, true, false, expectedExpressionType, null, true );
}
public IProgram parseProgram( IScriptPartId partId, boolean isolatedScope, boolean reallyIsolatedScope, IType expectedExpressionType, IFileContext ctx, boolean assignRuntime ) throws ParseResultsException
{
return parseProgram( partId, isolatedScope, reallyIsolatedScope, expectedExpressionType, ctx, assignRuntime, false );
}
public IProgram parseProgram( IScriptPartId partId, boolean isolatedScope, boolean reallyIsolatedScope, IType expectedExpressionType, IFileContext ctx, boolean assignRuntime, boolean bDoNotThrowParseResultsException ) throws ParseResultsException
{
return parseProgram( partId, isolatedScope, reallyIsolatedScope, expectedExpressionType, ctx, assignRuntime, bDoNotThrowParseResultsException, null );
}
public IProgram parseProgram( IScriptPartId partId, boolean isolatedScope, boolean reallyIsolatedScope, IType expectedExpressionType, IFileContext ctx, boolean assignRuntime, boolean bDoNotThrowParseResultsException, IType superType ) throws ParseResultsException
{
Program program = new Program();
program.setDeclaredReturnType(expectedExpressionType);
_parsingProgram = program;
pushScriptPart( partId );
try
{
try
{
_tokenizer.nextToken();
if( _tokenizer.getInstructor() == null )
{
parseClasspathStatements();
}
if (superType != null)
{
IGosuClassInternal superTypeGosuClass = GosuClass.Util.getGosuClassFrom(superType);
superTypeGosuClass.putClassMembers(this, getSymbolTable(), null, false );
}
if ( isolatedScope )
{
if ( reallyIsolatedScope ) {
_symTable.pushIsolatedScope( new GosuParserTransparentActivationContext( partId ) );
} else {
_symTable.pushScope();
}
}
try
{
//
// First just find and parse the function Declarations
//
for( ISymbol function = parseFunctionOrPropertyDeclaration( program, true, false );
function != null; function = parseFunctionOrPropertyDeclaration( program, true, false ) )
{
_symTable.putSymbol( function );
}
//
// Next we have to reset the tokenizer to the beginning.
//
_tokenizer.reset();
_locations.clear();
//
// Now parse the program as normal....
//
// Note function definitions are parsed as no-op statements, but are
// pushed onto the dynamic function symobl stack.
Statement mainStatement = parseStatements( getScriptPart(), false, false );
// Map the parsed function definitions by name
Map<String, DynamicFunctionSymbol> functionMap = new SpaceEfficientHashMap<String, DynamicFunctionSymbol>();
while( peekDynamicFunctionSymbol() != null )
{
DynamicFunctionSymbol function = popDynamicFunctionSymbol();
functionMap.put( function.getName(), function );
}
mainStatement.addParseIssues( program.getParseIssues() );
program.setMainStatement( mainStatement );
program.setFunctions( functionMap );
program.setLocation( mainStatement.getLocation() );
mainStatement.setParent( program );
verify( program, match( null, SourceCodeTokenizer.TT_EOF ), Res.MSG_END_OF_STMT );
_bParsed = true;
PostCompilationAnalysis.maybeAnalyze( program );
if( !bDoNotThrowParseResultsException )
{
verifyParsedElement( program );
}
CompileTimeAnnotationHandler.postDefinitionVerification( mainStatement );
}
finally
{
if( isolatedScope )
{
_symTable.popScope();
}
}
}
finally
{
_parsingProgram = null;
}
if( assignRuntime )
{
assignRuntime( program, isolatedScope, ctx, superType, partId);
}
}
finally
{
popScriptPart( partId );
}
return program;
}
@Override
public void setGenerateRootExpressionAccessForProgram( boolean bGenRootExprAccess )
{
_bGenRootExprAccess = bGenRootExprAccess;
}
public boolean isGenerateRootExpressionAccessForProgram()
{
return _bGenRootExprAccess;
}
private void assignRuntime(ParsedElement elem, boolean bIsolatedScope, IFileContext context, IType superType, IScriptPartId partId) throws ParseResultsException
{
// if( isForStringLiteralTemplate() )
// {
// return;
// }
if( elem.isCompileTimeConstant() )
{
return;
}
if( bIsolatedScope )
{
_symTable.pushScope();
}
try
{
GosuProgramParser programParser = new GosuProgramParser();
ParserOptions options = new ParserOptions().withTypeUsesMap( getTypeUsesMap() )
.withExpectedType( elem.getReturnType() )
.withTokenizerInstructor( getTokenizerInstructor() )
.withSuperType( superType )
.withFileContext( context )
.withCtxInferenceMgr( _ctxInferenceMgr.copy() )
.withGenRootExprAccess( isGenerateRootExpressionAccessForProgram() )
.asThrowawayProgram()
.withScriptPartId(partId);
IParseResult result = programParser.parseExpressionOrProgram( getScript(), getSymbolTable(), options );
IGosuProgramInternal p = (IGosuProgramInternal)result.getProgram();
if( p == null )
{
throw new IllegalStateException();
}
elem.setGosuProgram( p );
if( partId != null )
{
partId.setRuntimeType( p );
}
}
catch( Exception t )
{
if( t instanceof ParseResultsException )
{
if( isForStringLiteralTemplate() )
{
//## todo: shouldn't be creating a GosuProgram for this case, but the apps don't start for some reason.
// Uncomment code at top of method
return;
}
throw (ParseResultsException)t;
}
throw (RuntimeException)t;
}
finally
{
if( bIsolatedScope )
{
_symTable.popScope();
}
}
}
private boolean isForStringLiteralTemplate()
{
return getScriptPart() != null &&
getScriptPart().getId() != null &&
(getScriptPart().getId().equals( TemplateGenerator.GS_TEMPLATE ) ||
getScriptPart().getId().equals( TemplateGenerator.GS_TEMPLATE_PARSED )) &&
getScriptPart().getContainingType() instanceof IGosuClass;
}
List<IClasspathStatement> parseClasspathStatements()
{
int iOffset = getTokenizer().getTokenStart();
int iLineNum = getTokenizer().getLineNumber();
int iColumn = getTokenizer().getLineOffset();
List<IClasspathStatement> returnList = new ArrayList<IClasspathStatement>();
while( match( null, Keyword.KW_classpath ) )
{
ClasspathStatement cpStatement = new ClasspathStatement();
if( parseStringLiteralSeparately() )
{
StringLiteral expression = (StringLiteral)popExpression();
String strVal = (String)expression.evaluate();
cpStatement.setClasspath( strVal );
if( strVal.contains( ";" ) )
{
verifyOrWarn( cpStatement, false, true, Res.MSG_COMMA_IS_THE_CLASSPATH_SEPARATOR );
}
}
else
{
verify( cpStatement, false, Res.MSG_CLASSPATH_STATEMENT_EXPECTS_A_STRING );
}
returnList.add( cpStatement );
pushStatement(cpStatement);
try
{
setLocation( iOffset, iLineNum, iColumn );
if( getGosuClass() != null &&
((IGosuProgramInternal)getGosuClass()).isParsingExecutableProgramStatements() )
{
// Remove unwanted cp stmt resulting from parsing program on second pass for evaluate() method
getLocationsList().remove( cpStatement.getLocation() );
}
}
finally
{
popStatement();
}
iOffset = getTokenizer().getTokenStart();
iLineNum = getTokenizer().getLineNumber();
iColumn = getTokenizer().getLineOffset();
}
return returnList;
}
List<ITypeLoaderStatement> parseTypeLoaderStatements()
{
int iOffset = getTokenizer().getTokenStart();
int iLineNum = getTokenizer().getLineNumber();
int iColumn = getTokenizer().getLineOffset();
List<ITypeLoaderStatement> returnList = new ArrayList<ITypeLoaderStatement>();
while( match( new Token(), Keyword.KW_typeloader ) )
{
TypeLoaderStatement stmt = new TypeLoaderStatement();
parseTypeLiteral();
TypeLiteral expression = (TypeLiteral)popExpression();
stmt.setTypeLoader( expression.getType().getType() );
returnList.add( stmt );
pushStatement( stmt );
try
{
setLocation( iOffset, iLineNum, iColumn );
}
finally
{
popStatement();
}
iOffset = getTokenizer().getTokenStart();
iLineNum = getTokenizer().getLineNumber();
iColumn = getTokenizer().getLineOffset();
}
return returnList;
}
@Override
public ISymbol[] parseProgramFunctionsOrPropertyDecls(IScriptPartId partId, boolean bParseProperties, boolean bParseVars) throws ParseResultsException
{
Program program = new Program();
pushScriptPart( partId );
_tokenizer.nextToken();
_symTable.pushScope();
List<ISymbol> listFunctions;
try
{
listFunctions = new ArrayList<ISymbol>();
//
// Just find and parse the function Declarations
//
for( ISymbol function = parseFunctionOrPropertyDeclaration( program, bParseProperties, bParseVars);
function != null; function = parseFunctionOrPropertyDeclaration( program, bParseProperties, bParseVars) )
{
if (function instanceof IDynamicPropertySymbol) {
IDynamicPropertySymbol property1 = (IDynamicPropertySymbol)function;
String propertyName = property1.getName();
IDynamicPropertySymbol property2 = null;
for (ISymbol s : listFunctions) {
if (s instanceof IDynamicPropertySymbol && propertyName != null && propertyName.equals(s.getName())) {
property2 = (IDynamicPropertySymbol)s;
}
}
if (property2 == null) {
listFunctions.add( property1 );
} else {
property2.setGetterDfs(property1.getGetterDfs() != null ? property1.getGetterDfs() : property2.getGetterDfs());
property2.setSetterDfs(property1.getSetterDfs() != null ? property1.getSetterDfs() : property2.getSetterDfs());
}
} else {
listFunctions.add( function );
}
}
}
finally
{
_symTable.popScope();
popScriptPart( partId );
}
return listFunctions.toArray( new ISymbol[listFunctions.size()] );
}
public Expression parseExp( IScriptPartId partId ) throws ParseResultsException
{
return parseExp( partId, true, null, true );
}
public Expression parseExp( IScriptPartId partId, IType expectedExpressionType ) throws ParseResultsException
{
return parseExp( partId, true, expectedExpressionType, true );
}
public Expression parseExp( IScriptPartId partId, IType expectedExpressionType, IFileContext context, boolean assignRuntime ) throws ParseResultsException
{
return parseExp( partId, true, expectedExpressionType, context, assignRuntime );
}
private Expression parseExp( IScriptPartId partId, boolean isolatedScope, IType expectedExpressionType, boolean assignRuntime ) throws ParseResultsException {
return parseExp(partId, isolatedScope, expectedExpressionType, null, assignRuntime);
}
private Expression parseExp( IScriptPartId partId, boolean isolatedScope,
IType expectedExpressionType, IFileContext context, boolean assignRuntime ) throws ParseResultsException
{
pushScriptPart( partId );
_tokenizer.nextToken();
Expression expression;
if ( isolatedScope )
{
_symTable.pushScope();
}
try
{
parseExpression(expectedExpressionType);
expression = popExpression();
verify( expression, match( null, SourceCodeTokenizer.TT_EOF ), Res.MSG_END_OF_EXPRESSION );
_bParsed = true;
verifyParsedElement( expression );
}
finally
{
if ( isolatedScope )
{
_symTable.popScope();
}
popScriptPart( partId );
}
if (assignRuntime) {
assignRuntime( expression, isolatedScope, context, null, partId);
}
CompileTimeAnnotationHandler.postDefinitionVerification( expression );
return expression;
}
public IExpression parseExpOrProgram( IScriptPartId partId ) throws ParseResultsException
{
return parseExpOrProgram( partId, true, true );
}
public IExpression parseExpOrProgram( IScriptPartId partId, boolean isolatedScope, boolean assignRuntime ) throws ParseResultsException
{
return parseExpOrProgram( partId, null, isolatedScope, assignRuntime );
}
public IExpression parseExpOrProgram( IScriptPartId partId, IType typeExpected, boolean isolatedScope, boolean assignRuntime ) throws ParseResultsException
{
IExpression exp;
try
{
exp = parseExp( partId, isolatedScope, typeExpected, assignRuntime );
}
catch( ParseResultsException expressionParseResultException )
{
boolean isProbablyProgram = !getTokenizer().isEOF();
//noinspection CaughtExceptionImmediatelyRethrown
try
{
Map<String, Set<IFunctionSymbol>> dfsDeclByName = new HashMap<String, Set<IFunctionSymbol>>(_dfsDeclByName);
resetScript();
_dfsDeclByName = dfsDeclByName;
exp = parseProgram( partId, isolatedScope, typeExpected );
}
catch( ParseResultsException programParseResultsException )
{
if( isProbablyProgram )
{
throw programParseResultsException;
}
else
{
// Note we can't just rethrow the original exception because we need
// the locations etc. in the parser, so we have to reparse and let it throw.
Map<String, Set<IFunctionSymbol>> dfsDeclByName = new HashMap<String, Set<IFunctionSymbol>>(_dfsDeclByName);
resetScript();
_dfsDeclByName = dfsDeclByName;
exp = parseExp( partId, isolatedScope, null, assignRuntime );
}
}
}
return exp;
}
public TypeLiteral parseTypeLiteral( IScriptPartId partId ) throws ParseResultsException
{
pushScriptPart( partId );
try
{
_tokenizer.nextToken();
_symTable.pushScope();
try
{
parseTypeLiteral();
}
finally
{
_symTable.popScope();
}
Expression expression = popExpression();
verify( expression, match( null, SourceCodeTokenizer.TT_EOF ), Res.MSG_END_OF_EXPRESSION );
_bParsed = true;
return (TypeLiteral)expression;
}
finally
{
popScriptPart( partId );
}
}
public boolean isParsed()
{
return _bParsed;
}
protected void setParsed( boolean bParsed )
{
_bParsed = bParsed;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// GosuParser methods
@Override
public SourceCodeTokenizer getTokenizer()
{
return _tokenizer;
}
@Override
List<ParseTree> getLocationsList()
{
return _locations;
}
public List<IParseTree> getLocations()
{
return new ArrayList<IParseTree>( _locations );
}
public ParseTree peekLocation()
{
if( _locations.isEmpty() )
{
return null;
}
return _locations.get( _locations.size()-1 );
}
public boolean hasWarnings()
{
return false;
}
public boolean isThrowParseResultsExceptionForWarnings()
{
return _bThrowForWarnings;
}
public void setThrowParseExceptionForWarnings( boolean bThrowParseExceptionForWarnings )
{
_bThrowForWarnings = bThrowParseExceptionForWarnings;
}
//------------------------------------------------------------------------------
// expression
// <conditional-expression>
//
void parseExpression()
{
parseExpression( (List<IType>)null );
}
void parseExpression( IType contextType )
{
parseExpression( contextType, true );
}
void parseExpression( IType contextType, boolean bVerify )
{
ArrayList<IType> ctxTypes = null;
if( contextType != null )
{
ctxTypes = new ArrayList<IType>(1);
ctxTypes.add( contextType );
}
parseExpression( ctxTypes );
if( bVerify && contextType != null )
{
Expression expr = popExpression();
verifyComparable( contextType, expr, true );
expr = possiblyWrapWithImplicitCoercion( expr, contextType );
pushExpression( expr );
}
}
void parseExpression( List<IType> contextTypes )
{
push_(contextTypes);
try
{
int iOffset = _tokenizer.getTokenStart();
int iLineNum = _tokenizer.getLineNumber();
int iColumn = _tokenizer.getTokenColumn();
parseConditionalExpression();
convertNumberLiteralsToContextType(contextTypes);
convertNullLiterals( contextTypes );
setLocation( iOffset, iLineNum, iColumn );
}
finally
{
_inferredContextStack.pop();
}
}
private void push_(List<IType> contextTypes) {
if (contextTypes != null && contextTypes.size() == 1) {
IType contextType = contextTypes.get(0);
if (contextType instanceof IBlockType) {
IBlockType blockType = (IBlockType) contextType;
IType[] blockParameterTypes = blockType.getParameterTypes();
}
}
_inferredContextStack.push( contextTypes );
}
private void convertNullLiterals( List<IType> contextTypes )
{
if( !_stack.isEmpty() && contextTypes != null && contextTypes.size() == 1)
{
Expression expression = peekExpression();
IType type = contextTypes.get( 0 );
if( expression instanceof NullExpression && !type.isPrimitive() )
{
expression.setType( type );
}
}
}
@SuppressWarnings({"ThrowableResultOfMethodCallIgnored"})
private void convertNumberLiteralsToContextType( List<IType> contextTypes )
{
if( !_stack.isEmpty() )
{
Expression expr = peekExpression();
UnaryExpression unary = null;
if( expr instanceof UnaryExpression )
{
unary = (UnaryExpression)expr;
Expression subexpr = unary.getExpression();
if( subexpr instanceof NumericLiteral )
{
expr = subexpr;
}
}
if( expr instanceof NumericLiteral )
{
// if only a single Number context type exists, consider this number literal to be of that type
NumericLiteral literal = (NumericLiteral)expr;
IType ctxType = getNumberContextType( contextTypes );
if( ctxType != null && !literal.isExplicitlyTyped() )
{
String strValue = literal.getStrValue();
try
{
if( isPrefixNumericLiteral( strValue ) )
{
strValue = parseIntOrLongOrBigInt( strValue ) + "";
}
if( JavaTypes.pBYTE().equals( ctxType ) || JavaTypes.BYTE().equals( ctxType ) )
{
literal.setValue( Byte.parseByte( strValue ) );
}
else if( JavaTypes.pSHORT().equals( ctxType ) || JavaTypes.SHORT().equals( ctxType ) )
{
literal.setValue( Short.parseShort( strValue ) );
}
else if( JavaTypes.pINT().equals( ctxType ) || JavaTypes.INTEGER().equals( ctxType ) )
{
literal.setValue( Integer.parseInt( strValue ) );
}
else if( JavaTypes.pLONG().equals( ctxType ) || JavaTypes.LONG().equals( ctxType ) )
{
literal.setValue( Long.parseLong( strValue ) );
}
else if( JavaTypes.pFLOAT().equals( ctxType ) || JavaTypes.FLOAT().equals( ctxType ) )
{
literal.setValue( Float.parseFloat( strValue ) );
}
else if( JavaTypes.pDOUBLE().equals( ctxType ) || JavaTypes.DOUBLE().equals( ctxType ) )
{
literal.setValue( Double.parseDouble( strValue ) );
}
else if( JavaTypes.BIG_INTEGER().equals( ctxType ) )
{
literal.setValue( new BigInteger( strValue ) );
}
else if( JavaTypes.BIG_DECIMAL().equals( ctxType ) )
{
literal.setValue( new BigDecimal( strValue ) );
}
else if( literal.getType().isPrimitive() &&
(JavaTypes.OBJECT().equals( ctxType ) ||
(JavaTypes.NUMBER().equals( ctxType ) && ctxType.isAssignableFrom( TypeSystem.getBoxType( literal.getType() )))) )
{
ctxType = TypeSystem.getBoxType( literal.getType() );
}
else
{
return;
}
}
catch( NumberFormatException ex )
{
verify( literal, false, Res.MSG_IMPROPER_VALUE_FOR_NUMERIC_TYPE, strValue, ctxType.getName() );
}
literal.setType( ctxType );
if( unary != null )
{
unary.setType( ctxType );
}
}
}
}
}
private IType getNumberContextType( List<IType> contextTypes )
{
if( contextTypes == null )
{
return null;
}
IType onlyNumberContextType = null;
boolean bNumberCtxTypeFound = false;
IType prior = null;
for( IType contextType : contextTypes )
{
if( prior != null && prior != contextType )
{
// Ambiguous context types
return null;
}
prior = contextType;
IType compType = contextType.isPrimitive() ? TypeSystem.getBoxType( contextType ) : contextType;
if( JavaTypes.NUMBER().isAssignableFrom( compType ) &&
!JavaTypes.NUMBER().equals( compType ) )
{
if( onlyNumberContextType == null )
{
onlyNumberContextType = contextType;
bNumberCtxTypeFound = true;
}
else
{
onlyNumberContextType = null;
break;
}
}
}
if( !bNumberCtxTypeFound )
{
for( IType contextType : contextTypes )
{
IType compType = contextType.isPrimitive() ? TypeSystem.getBoxType( contextType ) : contextType;
if( JavaTypes.OBJECT().equals( compType ) || JavaTypes.NUMBER().equals( compType ) )
{
if( onlyNumberContextType == null )
{
onlyNumberContextType = contextType;
}
else
{
onlyNumberContextType = null;
break;
}
}
}
}
return onlyNumberContextType;
}
//------------------------------------------------------------------------------
// conditional-expression
// <conditional-or-expression>
// <conditional-expression> ? <conditional-expression> : <conditional-expression>
//
// Left recursion removed is:
//
// conditional-expression
// <conditional-or-expression> <conditional-expression2>
//
// conditional-expression2
// ? <conditional-expression> : <conditional-expression>
// <null>
//
void parseConditionalExpression()
{
int iOffset = _tokenizer.getTokenStart();
int iLineNum = _tokenizer.getLineNumber();
int iColumn = _tokenizer.getTokenColumn();
_parseConditionalExpression();
setLocation( iOffset, iLineNum, iColumn );
}
void _parseConditionalExpression()
{
// <conditional-or-expression>
_ctxInferenceMgr.pushCtx();
parseConditionalOrExpression();
boolean foundCondExpr = false;
// <conditional-or-expression> ? <conditional-expression> : <conditional-expression>
Token T = new Token();
if( match( T, "?", SourceCodeTokenizer.TT_OPERATOR ) )
{
foundCondExpr = true;
ConditionalTernaryExpression cte = new ConditionalTernaryExpression();
Expression condition = popExpression();
if( !JavaTypes.pBOOLEAN().equals( condition.getType() ) &&
!JavaTypes.BOOLEAN().equals( condition.getType() ) )
{
verifyOrWarn( condition, false, CommonServices.getEntityAccess().getLanguageLevel().allowAllImplicitCoercions(),
Res.MSG_CONDITIONAL_EXPRESSION_EXPECTS_BOOLEAN );
}
condition = possiblyWrapWithImplicitCoercion( condition, JavaTypes.pBOOLEAN() );
verifyComparable( JavaTypes.pBOOLEAN(), condition, true );
parseConditionalExpression();
Expression first = popExpression();
verify( cte, match( T, ":", SourceCodeTokenizer.TT_OPERATOR ), Res.MSG_EXPECTING_COLON_TERNARY );
_ctxInferenceMgr.popCtx( false );
parseConditionalExpression();
Expression second = popExpression();
_ctxInferenceMgr.pushCtx();
List<IType> list = new ArrayList<IType>();
first = wrapExpressionIfNeeded( first, second );
second = wrapExpressionIfNeeded( second, first );
//Do not allow literals that are coercable to the type of the other side to modify the
//type of the expression
if( !(first instanceof ILiteralExpression) ||
GosuParserTypes.NULL_TYPE().equals(second.getType()) ||
second instanceof ILiteralExpression ||
!CommonServices.getCoercionManager().canCoerce( second.getType(), first.getType() ) )
{
list.add( first.getType() );
}
if( !(second instanceof ILiteralExpression) ||
GosuParserTypes.NULL_TYPE().equals(first.getType()) ||
first instanceof ILiteralExpression ||
!CommonServices.getCoercionManager().canCoerce( first.getType(), second.getType() ) )
{
list.add( second.getType() );
}
IType type = TypeLord.findLeastUpperBound( list );
cte.setType( type );
cte.setCondition( condition );
cte.setFirst( possiblyWrapWithImplicitCoercion( first, type ) );
cte.setSecond( possiblyWrapWithImplicitCoercion( second, type ) );
pushExpression( cte );
}
else if( match( T, "?:", SourceCodeTokenizer.TT_OPERATOR ) )
{
foundCondExpr = true;
ConditionalTernaryExpression cte = new ConditionalTernaryExpression();
Expression first = popExpression();
verify( first, !first.getType().isPrimitive(), Res.MSG_EXPECTING_REFERENCE_TYPE );
_ctxInferenceMgr.popCtx( false );
parseConditionalExpression();
Expression second = popExpression();
_ctxInferenceMgr.pushCtx();
List<IType> list = new ArrayList<IType>();
//Do not allow literals that are coercable to the type of the other side to modify the
//type of the expression
if( !(first instanceof ILiteralExpression) ||
GosuParserTypes.NULL_TYPE().equals(second.getType()) ||
second instanceof ILiteralExpression ||
!CommonServices.getCoercionManager().canCoerce( second.getType(), first.getType() ) )
{
list.add( first.getType() );
}
if( !(second instanceof ILiteralExpression) ||
GosuParserTypes.NULL_TYPE().equals(first.getType()) ||
first instanceof ILiteralExpression ||
!CommonServices.getCoercionManager().canCoerce( first.getType(), second.getType() ) )
{
list.add( second.getType() );
}
IType type = TypeLord.findLeastUpperBound( list );
cte.setType( type );
cte.setCondition( null );
cte.setFirst( possiblyWrapWithImplicitCoercion( first, type ) );
cte.setSecond( possiblyWrapWithImplicitCoercion( second, type ) );
pushExpression( cte );
}
_ctxInferenceMgr.popCtx( !foundCondExpr );
}
private Expression wrapExpressionIfNeeded( Expression first, Expression second )
{
if( first.getType() == JavaTypes.pVOID() && second.getType().isPrimitive() )
{
return possiblyWrapWithImplicitCoercion( first, TypeSystem.getBoxType( second.getType() ) );
}
else if( second.getType() == JavaTypes.pVOID() && first.getType().isPrimitive() )
{
return possiblyWrapWithImplicitCoercion( first, TypeSystem.getBoxType( first.getType() ) );
}
return first;
}
//------------------------------------------------------------------------------
// conditional-or-expression
// <conditional-and-expression>
// <conditional-or-expression> || <conditional-and-expression>
//
// Left recursion removed is:
//
// conditional-or-expression
// <conditional-and-expression> <conditional-or-expression2>
//
// conditional-or-expression2
// || <conditional-and-expression>
// <null>
//
void parseConditionalOrExpression()
{
int iOffset = _tokenizer.getTokenStart();
int iLineNum = _tokenizer.getLineNumber();
int iColumn = getTokenizer().getTokenColumn();
_parseConditionalOrExpression();
setLocation( iOffset, iLineNum, iColumn );
}
void _parseConditionalOrExpression()
{
parseConditionalAndExpression();
// <conditional-or-expression2>
boolean foundOr = false;
do
{
Token T = new Token();
if( match( T, "||", SourceCodeTokenizer.TT_OPERATOR ) ||
match( null, Keyword.KW_or ) )
{
_ctxInferenceMgr.clear();
foundOr = true;
parseConditionalAndExpression();
ConditionalOrExpression e = new ConditionalOrExpression();
Expression rhs = popExpression();
verifyComparable( JavaTypes.pBOOLEAN(), rhs, true, true );
rhs = possiblyWrapWithImplicitCoercion( rhs, JavaTypes.pBOOLEAN() );
Expression lhs = popExpression();
verifyComparable( JavaTypes.pBOOLEAN(), lhs, true, true );
lhs = possiblyWrapWithImplicitCoercion( lhs, JavaTypes.pBOOLEAN() );
e.setLHS( lhs );
e.setRHS( rhs );
pushExpression( e );
}
else
{
// The <null> case
break;
}
}
while( true );
if (foundOr) {
_ctxInferenceMgr.clear();
}
}
//------------------------------------------------------------------------------
// conditional-and-expression
// <equality-expression>
// <conditional-and-expression> && <equality-expression>
//
// Left recursion removed is:
//
// conditional-and-expression
// <equality-expression> <conditional-and-expression2>
//
// conditional-and-expression2
// && <equality-expression>
// <null>
//
void parseConditionalAndExpression()
{
int iOffset = _tokenizer.getTokenStart();
int iLineNum = _tokenizer.getLineNumber();
int iColumn = getTokenizer().getTokenColumn();
_parseConditionalAndExpression();
setLocation( iOffset, iLineNum, iColumn );
}
void _parseConditionalAndExpression()
{
parseBitwiseOrExpression();
// <conditional-and-expression2>
do
{
Token T = new Token();
if( match( T, "&&", SourceCodeTokenizer.TT_OPERATOR ) ||
match( null, Keyword.KW_and ) )
{
parseBitwiseOrExpression();
ConditionalAndExpression e = new ConditionalAndExpression();
Expression rhs = popExpression();
verifyComparable( JavaTypes.pBOOLEAN(), rhs, true, true );
rhs = possiblyWrapWithImplicitCoercion( rhs, JavaTypes.pBOOLEAN() );
Expression lhs = popExpression();
verifyComparable( JavaTypes.pBOOLEAN(), lhs, true, true );
lhs = possiblyWrapWithImplicitCoercion( lhs, JavaTypes.pBOOLEAN() );
e.setLHS( lhs );
e.setRHS( rhs );
pushExpression( e );
}
else
{
// The <null> case
break;
}
}
while( true );
}
//------------------------------------------------------------------------------
// bitwise-or-expression
// <bitwise-xor-expression>
// <bitwise-or-expression> | <bitwise-xor-expression>
//
// Left recursion removed is:
//
// bitwise-or-expression
// <bitwise-xor-expression> <bitwise-or-expression2>
//
// bitwise-or-expression2
// | <bitwise-xor-expression>
// <null>
//
void parseBitwiseOrExpression()
{
int iOffset = _tokenizer.getTokenStart();
int iLineNum = _tokenizer.getLineNumber();
int iColumn = getTokenizer().getTokenColumn();
_parseBitwiseOrExpression();
setLocation( iOffset, iLineNum, iColumn );
}
void _parseBitwiseOrExpression()
{
parseBitwiseXorExpression();
// <bitwise-or-expression2>
do
{
Token T = new Token();
if( match( T, "|", SourceCodeTokenizer.TT_OPERATOR ) )
{
BitwiseOrExpression e = new BitwiseOrExpression();
Expression lhs = popExpression();
lhs = ensureOperandIntOrLong( lhs );
parseBitwiseXorExpression();
Expression rhs = popExpression();
verifyComparable( lhs.getType(), rhs, true, true );
rhs = possiblyWrapWithImplicitCoercion( rhs, lhs.getType() );
e.setLHS( lhs );
e.setRHS( rhs );
e.setType( resolveType( e, lhs.getType(), '|', rhs.getType() ) );
pushExpression( e );
}
else
{
// The <null> case
break;
}
}
while( true );
}
private Expression ensureOperandIntOrLong( Expression lhs )
{
IType lhsType = lhs.getType();
if( verify( lhs,
lhsType == JavaTypes.LONG() || lhsType == JavaTypes.pLONG() ||
lhsType == JavaTypes.INTEGER() || lhsType == JavaTypes.pINT() ||
lhsType == JavaTypes.SHORT() || lhsType == JavaTypes.pSHORT() ||
lhsType == JavaTypes.BYTE() || lhsType == JavaTypes.pBYTE(),
Res.MSG_BITWISE_OPERAND_MUST_BE_INT_OR_LONG ) )
{
lhsType = lhsType == JavaTypes.LONG() || lhsType == JavaTypes.pLONG() ? JavaTypes.pLONG() : JavaTypes.pINT();
lhs = possiblyWrapWithImplicitCoercion( lhs, lhsType );
}
return lhs;
}
//------------------------------------------------------------------------------
// bitwise-xor-expression
// <bitwise-and-expression>
// <bitwise-xor-expression> ^ <bitwise-xor-expression>
//
// Left recursion removed is:
//
// bitwise-xor-expression
// <bitwise-and-expression> <bitwise-xor-expression2>
//
// bitwise-xor-expression2
// ^ <bitwise-and-expression>
// <null>
//
void parseBitwiseXorExpression()
{
int iOffset = _tokenizer.getTokenStart();
int iLineNum = _tokenizer.getLineNumber();
int iColumn = getTokenizer().getTokenColumn();
_parseBitwiseXorExpression();
setLocation( iOffset, iLineNum, iColumn );
}
void _parseBitwiseXorExpression()
{
parseBitwiseAndExpression();
// <bitwise-xor-expression2>
do
{
Token T = new Token();
if( match( T, "^", SourceCodeTokenizer.TT_OPERATOR ) )
{
BitwiseXorExpression e = new BitwiseXorExpression();
Expression lhs = popExpression();
lhs = ensureOperandIntOrLong( lhs );
parseBitwiseAndExpression();
Expression rhs = popExpression();
verifyComparable( lhs.getType(), rhs, true, true );
rhs = possiblyWrapWithImplicitCoercion( rhs, lhs.getType() );
e.setLHS( lhs );
e.setRHS( rhs );
e.setType( resolveType( e, lhs.getType(), '^', rhs.getType() ) );
pushExpression( e );
}
else
{
// The <null> case
break;
}
}
while( true );
}
//------------------------------------------------------------------------------
// bitwise-and-expression
// <equality-expression>
// <bitwise-and-expression> & <equality-expression>
//
// Left recursion removed is:
//
// bitwise-and-expression
// <equality-expression> <bitwise-and-expression2>
//
// bitwise-and-expression2
// ^ <equality-expression>
// <null>
//
void parseBitwiseAndExpression()
{
int iOffset = _tokenizer.getTokenStart();
int iLineNum = _tokenizer.getLineNumber();
int iColumn = getTokenizer().getTokenColumn();
_parseBitwiseAndExpression();
setLocation( iOffset, iLineNum, iColumn );
}
void _parseBitwiseAndExpression()
{
parseEqualityExpression();
// <bitwise-and-expression2>
do
{
Token T = new Token();
if( match( T, "&", SourceCodeTokenizer.TT_OPERATOR ) )
{
BitwiseAndExpression e = new BitwiseAndExpression();
Expression lhs = popExpression();
lhs = ensureOperandIntOrLong( lhs );
parseEqualityExpression();
Expression rhs = popExpression();
verifyComparable( lhs.getType(), rhs, true, true );
rhs = possiblyWrapWithImplicitCoercion( rhs, lhs.getType() );
e.setLHS( lhs );
e.setRHS( rhs );
e.setType( resolveType( e, lhs.getType(), '&', rhs.getType() ) );
pushExpression( e );
}
else
{
// The <null> case
break;
}
}
while( true );
}
//------------------------------------------------------------------------------
// equality-expression
// <relational-expression>
// <equality-expression> == <relational-expression>
// <equality-expression> != <relational-expression>
//
// Left recursion removed is:
//
// equality-expression
// <relational-expression> <equality-expression2>
//
// equality-expression2
// == <relational-expression>
// != <relational-expression>
// <null>
//
void parseEqualityExpression()
{
int iOffset = _tokenizer.getTokenStart();
int iLineNum = _tokenizer.getLineNumber();
int iColumn = getTokenizer().getTokenColumn();
_parseEqualityExpression();
setLocation( iOffset, iLineNum, iColumn );
}
void _parseEqualityExpression()
{
parseRelationalExpression();
// <relational-expression2>
do
{
Token T = new Token();
if( match( T, "===", SourceCodeTokenizer.TT_OPERATOR ) ||
match( T, "!==", SourceCodeTokenizer.TT_OPERATOR ) )
{
IdentityExpression e = new IdentityExpression();
Expression lhs = popExpression();
List<IType> hashSet = new ArrayList<IType>(1);
hashSet.add( lhs.getType() );
push_(hashSet);
try
{
parseRelationalExpression();
}
finally
{
_inferredContextStack.pop();
}
Expression rhs = popExpression();
verifyComparable( lhs.getType(), rhs, true, true );
// // Wrap in implicit type-as to handle primitives
// if( (!rhs.hasParseExceptions() && !lhs.hasParseExceptions()) &&
// (rhs.getType() != lhs.getType()) &&
// lhs.getType().isPrimitive() || rhs.getType().isPrimitive() )
// {
// IType type = resolveType( lhs, lhs.getType(), '+', rhs.getType() );
// lhs = possiblyWrapWithImplicitCoercion( lhs, type );
// rhs = possiblyWrapWithImplicitCoercion( rhs, type );
// }
e.setLHS( lhs );
e.setRHS( rhs );
e.setEquals( T._strValue.equals( "===" ) );
pushExpression( e );
}
else if( match( T, "==", SourceCodeTokenizer.TT_OPERATOR ) ||
match( T, "!=", SourceCodeTokenizer.TT_OPERATOR ) ||
match( T, "<>", SourceCodeTokenizer.TT_OPERATOR ) )
{
EqualityExpression e = new EqualityExpression();
Expression lhs = popExpression();
List<IType> hashSet = new ArrayList<IType>(1);
hashSet.add( lhs.getType() );
push_(hashSet);
try
{
parseRelationalExpression();
}
finally
{
_inferredContextStack.pop();
}
Expression rhs = popExpression();
rhs = verifyConditionalTypes( lhs, rhs );
//## see PL-9512
verifyCoercionSymmetry( e, lhs, rhs );
e.setLHS( lhs );
e.setRHS( rhs );
e.setEquals( T._strValue.equals( "==" ) );
warnOnSuspiciousEqualsOperator(e);
pushExpression( e );
}
else
{
// The <null> case
break;
}
}
while( true );
}
private void warnOnSuspiciousEqualsOperator( EqualityExpression e )
{
Expression lhs = e.getLHS();
Expression rhs = e.getRHS();
if( (isThisPointer( lhs ) && isEqualsArgReference( rhs )) ||
(isEqualsArgReference( lhs ) && isThisPointer( rhs )) )
{
warn( e, false, Res.MSG_WARN_ON_SUSPICIOUS_THIS_COMPARISON );
}
}
private boolean isThisPointer( Expression expr )
{
return expr != null &&
expr instanceof Identifier &&
((Identifier)expr).getSymbol() != null &&
Keyword.KW_this.getName().equals( ((Identifier)expr).getSymbol().getName() );
}
private boolean isEqualsArgReference( Expression expr )
{
if( isParsingFunction() && expr != null && expr instanceof Identifier )
{
FunctionType functionType = peekParsingFunction();
if( "equals".equals( functionType.getDisplayName() ) )
{
if( functionType.getParameterTypes().length == 1 &&
functionType.getParameterTypes()[0] == JavaTypes.OBJECT() )
{
ISymbol symbol = ((Identifier)expr).getSymbol();
if( symbol != null && functionType.getParameterNames()[0].equals( symbol.getName() ) )
{
return true;
}
}
}
}
return false;
}
private void verifyCoercionSymmetry( EqualityExpression e, Expression lhs, Expression rhs )
{
if( !lhs.hasParseExceptions() && !rhs.hasParseExceptions() )
{
boolean bHasCoercionWarning = false;
for( IParseIssue p: rhs.getParseIssues() )
{
if( p.getMessageKey() == Res.MSG_IMPLICIT_COERCION_WARNING )
{
bHasCoercionWarning = true;
break;
}
}
ICoercionManager cocerionManager = CommonServices.getCoercionManager();
boolean bDontWarn = bHasCoercionWarning || // rhs has coercion warning, or
((lhs.getType() != JavaTypes.OBJECT() && rhs.getType() != JavaTypes.OBJECT()) || // neither side is Object, or
(lhs.getType() == JavaTypes.pVOID() || rhs.getType() == JavaTypes.pVOID()) || // one side is "null", or
(lhs.getType() != null && BeanAccess.isBeanType( lhs.getType() ) && // both sides are "beans"
rhs.getType() != null && BeanAccess.isBeanType( rhs.getType() )) || // ... , or
cocerionManager.resolveCoercerStatically( lhs.getType(), rhs.getType() ) == // coercer is symmetric
cocerionManager.resolveCoercerStatically( rhs.getType(), lhs.getType() ));
verifyOrWarn( e, bDontWarn, true,
Res.MSG_ASYMMETRICAL_COMPARISON, lhs.getType(), rhs.getType() );
}
}
private void verifyRelationalOperandsComparable( BinaryExpression expr )
{
boolean bComparable = false;
IType lhsType = expr.getLHS().getType();
if( BeanAccess.isNumericType( lhsType ) )
{
bComparable = true;
}
else if( lhsType == GosuParserTypes.DATETIME_TYPE() )
{
bComparable = true;
}
else
{
IType rhsType = expr.getRHS().getType();
if( BeanAccess.isBeanType( lhsType ) )
{
if( BeanAccess.isBeanType( rhsType ) )
{
if( lhsType.isAssignableFrom( rhsType ) )
{
if( JavaTypes.COMPARABLE().isAssignableFrom( lhsType ) )
{
bComparable = true;
}
}
}
}
if( !bComparable &&
(JavaTypes.CHAR_SEQUENCE().isAssignableFrom( lhsType ) ||
JavaTypes.CHAR_SEQUENCE().isAssignableFrom( rhsType )) )
{
bComparable = true;
}
}
verify( expr, bComparable, Res.MSG_RELATIONAL_OPERATOR_CANNOT_BE_APPLIED_TO_TYPE, expr.getOperator(), lhsType );
}
private Expression verifyConditionalTypes( Expression lhs, Expression rhs )
{
IType lhsType = lhs.getType();
IType rhsType = rhs.getType();
IType numberType = ParserBase.resolveType( lhsType, '>', rhsType );
if( numberType instanceof ErrorType ||
JavaTypes.IDIMENSION().isAssignableFrom( lhsType ) ||
JavaTypes.IDIMENSION().isAssignableFrom( rhsType ) )
{
Expression wrappedRhs = verifyWithComparableDimension( rhs, lhsType );
if( wrappedRhs != null )
{
return wrappedRhs;
}
// Not a number, verify types the general way
verifyComparable( lhs.getType(), rhs, true, true );
}
else
{
// Get coercion warnings if any
verifyComparable( numberType, rhs, false, true );
verifyComparable( numberType, lhs, false, true );
}
if( !(rhs instanceof ImplicitTypeAsExpression) &&
rhs.hasImmediateParseWarning( Res.MSG_IMPLICIT_COERCION_WARNING ) )
{
rhs = possiblyWrapWithImplicitCoercion( rhs, lhs.getType() );
}
return rhs;
}
private Expression verifyWithComparableDimension( Expression rhs, IType lhsType )
{
if( JavaTypes.IDIMENSION().isAssignableFrom( lhsType ) &&
JavaTypes.COMPARABLE().isAssignableFrom( lhsType ) )
{
for( IType iface : lhsType.getInterfaces() )
{
if( JavaTypes.COMPARABLE().isAssignableFrom( iface.getGenericType() ) )
{
IType type = iface.getTypeParameters()[0];
verifyComparable( type, rhs, true, true );
if( !rhs.hasParseExceptions() )
{
rhs = possiblyWrapWithImplicitCoercion( rhs, type );
}
return rhs;
}
}
}
return null;
}
//------------------------------------------------------------------------------
// relational-expression
// <bitshift-expression>
// <relational-expression> < <bitshift-expression>
// <relational-expression> > <bitshift-expression>
// <relational-expression> <= <bitshift-expression>
// <relational-expression> >= <bitshift-expression>
// <relational-expression> typeis <type-literal>
// <relational-expression> as <type-literal>
//
// Left recursion removed from this *grammar* is:
//
// relational-expression
// <bitshift-expression> <relational-expression2>
//
// relational-expression2
// < <bitshift-expression>
// > <bitshift-expression>
// <= <bitshift-expression>
// >= <bitshift-expression>
// typeis <type-literal>
// as <type-literal>
// <null>
//
void parseRelationalExpression()
{
int iOffset = _tokenizer.getTokenStart();
int iLineNum = _tokenizer.getLineNumber();
int iColumn = getTokenizer().getTokenColumn();
_parseRelationalExpression();
setLocation( iOffset, iLineNum, iColumn );
}
void _parseRelationalExpression()
{
// <bitshift-expression>
parseIntervalExpression();
// <relational-expression2>
do
{
Token T = new Token();
if( match( T, "<", SourceCodeTokenizer.TT_OPERATOR ) ||
match( T, ">", SourceCodeTokenizer.TT_OPERATOR ) ||
match( T, "<=", SourceCodeTokenizer.TT_OPERATOR ) )
{
Token eq = new Token();
if( T._strValue.equals( ">" ) && match( eq, "=", SourceCodeTokenizer.TT_OPERATOR, true ) )
{
if( T.getTokenEnd() == eq.getTokenStart() )
{
T._strValue = ">=";
match( null, "=", SourceCodeTokenizer.TT_OPERATOR );
}
}
parseIntervalExpression();
RelationalExpression e = new RelationalExpression();
Expression rhs = popExpression();
Expression lhs = popExpression();
rhs = verifyConditionalTypes( lhs, rhs );
e.setLHS( lhs );
e.setRHS( rhs );
e.setOperator( T._strValue );
if( !lhs.hasParseExceptions() && !rhs.hasParseExceptions() )
{
verifyRelationalOperandsComparable( e );
}
pushExpression( e );
}
else if( match( null, Keyword.KW_typeis ) )
{
parseTypeLiteral();
TypeIsExpression e = new TypeIsExpression();
Expression rhs = popExpression();
Expression lhs = popExpression();
if( verify( rhs, rhs instanceof TypeLiteral, Res.MSG_EXPECTING_TYPELITERAL ) )
{
verify( lhs, !lhs.getType().isPrimitive(), Res.MSG_PRIMITIVES_NOT_ALLOWED_HERE );
IType rhsType = ((TypeLiteral)rhs).getType().getType();
verify( rhs, !rhsType.isPrimitive(), Res.MSG_PRIMITIVES_NOT_ALLOWED_HERE );
warn( rhs, TypeLord.isInstanceOfPossible( lhs.getType(), rhsType ), Res.MSG_INCONVERTIBLE_TYPES, lhs.getType().getName(), rhsType.getName() );
e.setRHS( (TypeLiteral)rhs );
_ctxInferenceMgr.updateType( ContextInferenceManager.getUnwrappedExpression( lhs ), e.getRHS().evaluate() );
}
e.setLHS( lhs );
pushExpression( e );
}
else
{
// The <null> case
break;
}
}
while( true );
}
//------------------------------------------------------------------------------
// interval-expression
// <bitshift-expression>
// <interval-expression> .. <bitshift-expression>
// <interval-expression> |.. <bitshift-expression>
// <interval-expression> ..| <bitshift-expression>
// <interval-expression> |..| <bitshift-expression>
//
// Left recursion removed from this *grammar* is:
//
// interval-expression
// <bitshift-expression> <interval-expression2>
//
// interval-expression2
// .. <bitshift-expression> <interval-expression2>
// |.. <bitshift-expression> <interval-expression2>
// ..| <bitshift-expression> <interval-expression2>
// |..| <bitshift-expression> <interval-expression2>
// <null>
//
void parseIntervalExpression()
{
int iOffset = _tokenizer.getTokenStart();
int iLineNum = _tokenizer.getLineNumber();
int iColumn = getTokenizer().getTokenColumn();
_parseIntervalExpression();
setLocation( iOffset, iLineNum, iColumn );
}
void _parseIntervalExpression()
{
// <multiplicative-expression>
parseBitshiftExpression();
// <additive-expression2>
Token t = new Token();
boolean bClosed = match( t, "..", SourceCodeTokenizer.TT_OPERATOR );
boolean bLeftOpen = !bClosed && match( t, "|..", SourceCodeTokenizer.TT_OPERATOR );
boolean bNextTokenIsDotNoWhitespace = false;
if( bClosed | bLeftOpen ) {
int iNextToken = _tokenizer.getCurrentToken().getTokenStart();
bNextTokenIsDotNoWhitespace = t._iDocPosition + t._strValue.length() == iNextToken && match( null, '.' );
}
boolean bRightOpen = !bClosed && !bLeftOpen && match( null, "..|", SourceCodeTokenizer.TT_OPERATOR );
if( !bClosed && !bLeftOpen && !bRightOpen &&
match( null, "|..|", SourceCodeTokenizer.TT_OPERATOR ) )
{
bLeftOpen = bRightOpen = true;
}
if( bClosed || bLeftOpen || bRightOpen )
{
parseBitshiftExpression();
Expression rhs = popExpression();
Expression lhs = popExpression();
rhs = verifyConditionalTypes( lhs, rhs );
IType type = IntervalExpression.getIntervalType( rhs.getType() );
lhs = possiblyWrapWithImplicitCoercion( lhs, rhs.getType() );
IntervalExpression e = new IntervalExpression( bClosed || !bLeftOpen, bClosed || !bRightOpen, lhs, rhs );
verify( e, !bNextTokenIsDotNoWhitespace, Res.MSG_EXTRA_DOT_FOUND_IN_INTERVAL );
//## todo: move to foreach: verify( e, JavaTypes.ITERABLE().isAssignableFrom( type ), Res.MSG_INTERVAL_MUST_BE_ITERABLE_HERE );
e.setType( type );
if( !lhs.hasParseExceptions() && !rhs.hasParseExceptions() )
{
verifyRelationalOperandsComparable( e );
}
pushExpression( e );
}
}
//------------------------------------------------------------------------------
// bitshift-expression
// <additive-expression>
// <bitshift-expression> << <additive-expression>
// <bitshift-expression> >> <additive-expression>
// <bitshift-expression> >>> <additive-expression>
//
// Left recursion removed from this *grammar* is:
//
// bitshift-expression
// <addtive-expression> <bitshift-expression2>
//
// bitshift-expression2
// << <additive-expression> <bitshift-expression2>
// >> <additive-expression> <bitshift-expression2>
// >>> <additive-expression> <bitshift-expression2>
// <null>
//
void parseBitshiftExpression()
{
int iOffset = _tokenizer.getTokenStart();
int iLineNum = _tokenizer.getLineNumber();
int iColumn = getTokenizer().getTokenColumn();
_parseBitshiftExpression();
setLocation( iOffset, iLineNum, iColumn );
}
void _parseBitshiftExpression()
{
// <additive-expression>
parseAdditiveExpression();
// <bitshift-expression2>
do
{
Token T = new Token();
if( match( T, "<<", SourceCodeTokenizer.TT_OPERATOR ) ||
matchRightShift() )
{
if( T._strValue == null || !T._strValue.equals( "<<" ) )
{
T._strValue = ">>";
Token gr = new Token();
int lastEnd = getTokenizer().getPriorToken().getTokenEnd();
if( match( gr, ">", SourceCodeTokenizer.TT_OPERATOR, true ) )
{
if( lastEnd == gr.getTokenStart() )
{
T._strValue = ">>>";
match( null, ">", SourceCodeTokenizer.TT_OPERATOR );
}
}
}
parseAdditiveExpression();
BitshiftExpression e = new BitshiftExpression();
// Rhs must be an int
Expression rhs = popExpression();
verifyTypesComparable( rhs, JavaTypes.pINT(), rhs.getType(), false, true );
rhs = possiblyWrapWithImplicitCoercion( rhs, JavaTypes.pINT() );
// Lhs must be an int or a long
Expression lhs = popExpression();
IType lhsType = lhs.getType();
if( verify( lhs,
lhsType == JavaTypes.LONG() || lhsType == JavaTypes.pLONG() ||
lhsType == JavaTypes.INTEGER() || lhsType == JavaTypes.pINT() ||
lhsType == JavaTypes.SHORT() || lhsType == JavaTypes.pSHORT() ||
lhsType == JavaTypes.BYTE() || lhsType == JavaTypes.pBYTE(),
Res.MSG_BITSHIFT_LHS_MUST_BE_INT_OR_LONG ) )
{
lhsType = lhsType == JavaTypes.LONG() || lhsType == JavaTypes.pLONG() ? JavaTypes.pLONG() : JavaTypes.pINT();
lhs = possiblyWrapWithImplicitCoercion( lhs, lhsType );
}
e.setLHS( lhs );
e.setRHS( rhs );
e.setOperator( T._strValue );
e.setType( lhsType );
pushExpression( e );
}
else
{
// The <null> case
break;
}
}
while( true );
}
private boolean matchRightShift()
{
int iMark = getTokenizer().mark();
Token t0 = new Token();
Token t1 = new Token();
if( match( t0, ">", SourceCodeTokenizer.TT_OPERATOR ) &&
match( t1, ">", SourceCodeTokenizer.TT_OPERATOR ) )
{
if( t0.getTokenEnd() == t1.getTokenStart() )
{
return true;
}
}
getTokenizer().restoreToMark( iMark );
return false;
}
//------------------------------------------------------------------------------
// additive-expression
// <multiplicative-expression>
// <additive-expression> + <multiplicative-expression>
// <additive-expression> - <multiplicative-expression>
//
// Left recursion removed from this *grammar* is:
//
// additive-expression
// <multiplicative-expression> <additive-expression2>
//
// additive-expression2
// + <multiplicative-expression> <additive-expression2>
// - <multiplicative-expression> <additive-expression2>
// <null>
//
void parseAdditiveExpression()
{
int iOffset = _tokenizer.getTokenStart();
int iLineNum = _tokenizer.getLineNumber();
int iColumn = getTokenizer().getTokenColumn();
_parseAdditiveExpression();
setLocation( iOffset, iLineNum, iColumn );
}
void _parseAdditiveExpression()
{
// <multiplicative-expression>
parseMultiplicativeExpression();
// <additive-expression2>
do
{
Token T = new Token();
boolean bPlus = match( T, "+", SourceCodeTokenizer.TT_OPERATOR ) ||
match( T, "?+", SourceCodeTokenizer.TT_OPERATOR );
if( bPlus ||
match( T, "-", SourceCodeTokenizer.TT_OPERATOR ) ||
match( T, "?-", SourceCodeTokenizer.TT_OPERATOR ))
{
parseMultiplicativeExpression();
AdditiveExpression e = new AdditiveExpression();
Expression rhs = popExpression();
Expression lhs = popExpression();
e.setLHS( lhs );
e.setRHS( rhs );
e.setOperator( T._strValue );
e.setType( resolveType( e, lhs.getType(), bPlus ? '+' : '-', rhs.getType() ) );
verify( e, !(e.isNullSafe() && e.getType().isPrimitive()), Res.MSG_EXPECTING_REFERENCE_TYPE );
pushExpression( e );
}
else
{
// The <null> case
break;
}
}
while( true );
}
//------------------------------------------------------------------------------
// multiplicative-expression
// <unary-expression>
// <multiplicative-expression> * <unary-expression>
// <multiplicative-expression> / <unary-expression>
// <multiplicative-expression> % <unary-expression>
//
// NOTE: See parseAdditiveExpression() above for an explanation of left recursion removal.
//
void parseMultiplicativeExpression()
{
int iOffset = _tokenizer.getTokenStart();
int iLineNum = _tokenizer.getLineNumber();
int iColumn = getTokenizer().getTokenColumn();
_parseMultiplicativeExpression();
setLocation( iOffset, iLineNum, iColumn );
}
void _parseMultiplicativeExpression()
{
parseTypeAsExpression();
do
{
Token T = new Token();
if( match( T, "*", SourceCodeTokenizer.TT_OPERATOR ) ||
match( T, "/", SourceCodeTokenizer.TT_OPERATOR ) ||
match( T, "%", SourceCodeTokenizer.TT_OPERATOR ) ||
match( T, "?*", SourceCodeTokenizer.TT_OPERATOR ) ||
match( T, "?/", SourceCodeTokenizer.TT_OPERATOR ) ||
match( T, "?%", SourceCodeTokenizer.TT_OPERATOR ) )
{
parseTypeAsExpression();
MultiplicativeExpression e = new MultiplicativeExpression();
Expression rhs = popExpression();
Expression lhs = popExpression();
e.setLHS( lhs );
e.setRHS( rhs );
e.setOperator( T._strValue );
e.setType( resolveType( e, lhs.getType(), T._strValue.charAt( 0 ), rhs.getType() ) );
verify( e, !(e.isNullSafe() && e.getType().isPrimitive()), Res.MSG_EXPECTING_REFERENCE_TYPE );
pushExpression( e );
}
else
{
// The <null> case
break;
}
}
while( true );
}
void parseTypeAsExpression()
{
int iOffset = _tokenizer.getTokenStart();
int iLineNum = _tokenizer.getLineNumber();
int iColumn = getTokenizer().getTokenColumn();
_parseTypeAsExpression();
setLocation( iOffset, iLineNum, iColumn );
}
void _parseTypeAsExpression()
{
parseUnaryExpression();
do
{
if( match( null, Keyword.KW_typeas ) ||
match( null, Keyword.KW_as ))
{
parseTypeLiteral();
TypeAsExpression e = new TypeAsExpression();
Expression rhs = popExpression();
Expression lhs = popExpression();
if( !(rhs instanceof TypeLiteral) )
{
rhs.addParseException( new ParseException( makeFullParserState(), Res.MSG_EXPECTING_TYPELITERAL ) );
}
else
{
IType rhsType = ((TypeLiteral)rhs).getType().getType();
verify( rhs, rhsType != JavaTypes.pVOID(), Res.MSG_VOID_NOT_ALLOWED );
verifyComparable( TypeLord.replaceTypeVariableTypeParametersWithBoundingTypes( rhsType ), lhs, false, false );
if( rhs.hasParseExceptions() || lhs.hasParseExceptions() )
{
IType lhsType = lhs.getType();
if (TypeSystem.canCast(lhsType, rhsType))
{
//noinspection ThrowableResultOfMethodCallIgnored
lhs.removeParseException( Res.MSG_TYPE_MISMATCH );
}
}
e.setType( rhsType );
e.setCoercer( CommonServices.getCoercionManager().resolveCoercerStatically( rhsType, lhs.getType() ) );
warn( lhs, lhs.getType() instanceof IErrorType ||
rhs.getType() instanceof IErrorType ||
!rhsType.isAssignableFrom( lhs.getType() ) ||
isDynamic( rhsType ),
Res.MSG_UNNECESSARY_COERCION, lhs.getType().getRelativeName(), rhsType.getRelativeName() );
}
e.setLHS( lhs );
pushExpression( e );
}
else
{
// The <null> case
break;
}
} while( true );
}
//------------------------------------------------------------------------------
// unary-expression
// + <unary-expression>
// - <unary-expression>
// <unary-expression-not-plus-minus>
//
void parseUnaryExpression()
{
int iOffset = _tokenizer.getTokenStart();
int iLineNum = _tokenizer.getLineNumber();
int iColumn = getTokenizer().getTokenColumn();
_parseUnaryExpression();
setLocation( iOffset, iLineNum, iColumn );
}
void _parseUnaryExpression()
{
Token T = new Token();
if( match( T, "+", SourceCodeTokenizer.TT_OPERATOR ) ||
match( T, "-", SourceCodeTokenizer.TT_OPERATOR ) )
{
boolean negation = T._strValue.charAt( 0 ) == (int)'-';
if( negation && atNumberLiteralStart() )
{
parseNumberLiteral(true);
}
else
{
parseUnaryExpressionNotPlusMinus();
UnaryExpression ue = new UnaryExpression();
Expression e = popExpression();
verify( e, ue.isSupportedType( e.getType() ), Res.MSG_NUMERIC_TYPE_EXPECTED );
ue.setExpression( e );
ue.setNegated( negation );
ue.setType( e.getType() );
pushExpression( ue );
}
}
else
{
parseUnaryExpressionNotPlusMinus();
}
}
//------------------------------------------------------------------------------
// unary-expression-not-plus-minus
// ~ <unary-expression>
// ! <unary-expression>
// not <unary-expression>
// typeof <unary-expression>
// eval( <expression> )
// <primary-expression>
//
void parseUnaryExpressionNotPlusMinus()
{
int iOffset = _tokenizer.getTokenStart();
int iLineNum = _tokenizer.getLineNumber();
int iColumn = getTokenizer().getTokenColumn();
_parseUnaryExpressionNotPlusMinus();
setLocation( iOffset, iLineNum, iColumn );
checkMemberAccessIsReadable();
}
private void checkMemberAccessIsReadable()
{
Expression expr = peekExpression();
if( expr instanceof MemberAccess )
{
IPropertyInfo pi = ((MemberAccess)expr).getPropertyInfoWithoutThrowing();
if( pi != null )
{
verify( expr, pi.isReadable(),
Res.MSG_CLASS_PROPERTY_NOT_READABLE, pi.getName(), pi.getOwnersType().getName() );
}
}
else if( (expr instanceof Identifier &&
((Identifier)expr).getSymbol() instanceof DynamicPropertySymbol) )
{
DynamicPropertySymbol dps = (DynamicPropertySymbol)((Identifier)expr).getSymbol();
if( dps != null && !dps.isReadable() )
{
verify( expr, dps.isReadable(),
Res.MSG_CLASS_PROPERTY_NOT_READABLE, dps.getName(), dps.getScriptPart() == null ? "" : dps.getScriptPart().getContainingType().getName() );
}
}
}
void _parseUnaryExpressionNotPlusMinus()
{
Token T = new Token();
if( match( T, "!", SourceCodeTokenizer.TT_OPERATOR ) ||
match( T, Keyword.KW_not ) )
{
_ctxInferenceMgr.pushCtx();
try
{
parseUnaryExpression();
}
finally
{
_ctxInferenceMgr.popCtx( false );
}
UnaryNotPlusMinusExpression ue = new UnaryNotPlusMinusExpression();
Expression e = popExpression();
IType type = e.getType();
verifyTypesComparable( e, JavaTypes.pBOOLEAN(), type, false, true );
e = possiblyWrapWithImplicitCoercion( e, JavaTypes.pBOOLEAN() );
ue.setExpression( e );
ue.setNot( true );
ue.setType( JavaTypes.pBOOLEAN() );
pushExpression( ue );
}
else if( match( T, "~", SourceCodeTokenizer.TT_OPERATOR ) )
{
parseUnaryExpression();
UnaryNotPlusMinusExpression ue = new UnaryNotPlusMinusExpression();
Expression e = popExpression();
IType type = e.getType();
if( type == JavaTypes.LONG() || type == JavaTypes.pLONG() )
{
e = possiblyWrapWithImplicitCoercion( e, JavaTypes.pLONG() );
}
else
{
if( verify( e, !isDynamic( type ), Res.MSG_DYNAMIC_TYPE_NOT_ALLOWED_HERE ) )
{
verifyTypesComparable( e, JavaTypes.pINT(), type, false, true );
e = possiblyWrapWithImplicitCoercion( e, JavaTypes.pINT() );
}
}
ue.setExpression( e );
ue.setBitNot( true );
ue.setType( e.getType() );
pushExpression( ue );
}
else if( match( T, Keyword.KW_typeof ) )
{
parseUnaryExpression();
TypeOfExpression toe = new TypeOfExpression();
Expression e = popExpression();
toe.setExpression( e );
pushExpression( toe );
}
else if( match( T, Keyword.KW_statictypeof ) )
{
parseUnaryExpression();
StaticTypeOfExpression toe = new StaticTypeOfExpression();
Expression e = popExpression();
toe.setExpression( e );
pushExpression( toe );
}
else if( match( null, "\\", SourceCodeTokenizer.TT_OPERATOR ) )
{
parseBlockExpression();
}
else if( match( T, Keyword.KW_eval ) )
{
parseEvalExpression();
}
else
{
parsePrimaryExpression();
}
}
private void parseEvalExpression()
{
EvalExpression evalExpr = new EvalExpression( getTypeUsesMap().copy() );
List<ICapturedSymbol> captured = new ArrayList<ICapturedSymbol>();
captureAllSymbols( null, getCurrentEnclosingGosuClass(), captured );
evalExpr.setCapturedSymbolsForBytecode( captured );
evalExpr.setCapturedTypeVars( new HashMap<String, ITypeVariableDefinition>( getTypeVariables() ) );
verify( evalExpr, match( null, '(' ), Res.MSG_EXPECTING_LEFTPAREN_EVAL );
parseExpression();
verify( evalExpr, match( null, ')' ), Res.MSG_EXPECTING_RIGHTPAREN_EVAL );
Expression e = popExpression();
evalExpr.setExpression( e );
pushExpression( evalExpr );
}
public boolean isCaptureSymbolsForEval()
{
return _bCaptureSymbolsForEval;
}
public void setCaptureSymbolsForEval( boolean bCaputreSymbolsForEval )
{
_bCaptureSymbolsForEval = bCaputreSymbolsForEval;
}
//------------------------------------------------------------------------------
// primary-expression
// null
// true
// false
// NaN
// Infinity
// <new-expression>
// <exists-expression>
// <member-access>
// <array-access>
// <name>
// <literal>
// <object-literal>
// ( <expression) )
// <method-call-expression>
// <query-expression>
//
void parsePrimaryExpression()
{
int iOffset = _tokenizer.getTokenStart();
int iLineNum = _tokenizer.getLineNumber();
int iColumn = getTokenizer().getTokenColumn();
boolean bForceRedundancy = _parsePrimaryExpression();
setLocation( iOffset, iLineNum, iColumn, bForceRedundancy );
Expression eas = peekExpression();
if( recoverFromJavaStyleCast( eas ) )
{
setLocation( iOffset, iLineNum, iColumn, bForceRedundancy );
// re-root the parenthesized expression under the implicit typeas we created
Expression implicitTypeAsFromRecovery = peekExpression();
getLocationsList().remove(eas.getLocation());
implicitTypeAsFromRecovery.getLocation().addChild(eas.getLocation());
eas.setParent( implicitTypeAsFromRecovery );
}
parseIndirectMemberAccess( iOffset, iLineNum, iColumn );
}
boolean _parsePrimaryExpression()
{
boolean bRet = false;
if( match( null, Keyword.KW_block ) )
{
_parseBlockLiteral();
}
else if( match( null, Keyword.KW_new ) )
{
parseNewExpression();
}
else if( match( null, Keyword.KW_exists ) )
{
parseExistsExpression();
}
else if( match( null, Keyword.KW_find, true ) )
{
Token T = new Token();
T._strValue = Keyword.KW_find.toString();
parseQueryExpression( T );
}
else if( parseNameOrMethodCall() )
{
}
else if( match( null, '(' ) )
{
parseExpression( _inferredContextStack.size() > 0 ? _inferredContextStack.peek() : Collections.<IType>emptyList() );
_ctxInferenceMgr.restoreLastCtx();
Expression e = popExpression();
ParenthesizedExpression expr = new ParenthesizedExpression( e );
pushExpression( expr );
verify( e, match( null, ')' ), Res.MSG_EXPECTING_EXPRESSION_CLOSE );
}
else if( parseStandAloneDataStructureInitialization() )
{
bRet = true;
}
else
{
parseLiteral();
}
return bRet;
}
private boolean parseBooleanLiteral()
{
if( match( null, Keyword.KW_true ) )
{
BooleanLiteral e = new BooleanLiteral( true );
pushExpression( e );
return true;
}
if( match( null, Keyword.KW_false ) )
{
BooleanLiteral e = new BooleanLiteral( false );
pushExpression( e );
return true;
}
return false;
}
private boolean parseNullLiteral()
{
if( match( null, Keyword.KW_null ) )
{
pushExpression( new NullExpression() );
return true;
}
return false;
}
private boolean parseStandAloneDataStructureInitialization()
{
IToken startToken = getTokenizer().getCurrentToken();
Token token = new Token();
// infered data constructors
if( !match( token, '{' ) )
{
return false;
}
else
{
IType intrinsicType = getInitializableType();
NewExpression e = new InferredNewExpression();
boolean bPlaceholder = isDynamic( intrinsicType );
if( intrinsicType == null || intrinsicType == AMBIGUOUS_TYPE || bPlaceholder )
{
IInitializerExpression initializer;
IType type;
if( match( token, null, '}', true ) )
{
initializer = new CollectionInitializerExpression();
type = JavaTypes.ARRAY_LIST().getParameterizedType( bPlaceholder ? intrinsicType : JavaTypes.OBJECT() );
}
else
{
parseExpression( bPlaceholder ? intrinsicType : JavaTypes.OBJECT() );
Expression initialExpression = popExpression();
Expression actualInitExpr = initialExpression;
if( actualInitExpr instanceof ImplicitTypeAsExpression )
{
actualInitExpr = ((ImplicitTypeAsExpression)actualInitExpr).getLHS();
}
verify( actualInitExpr, actualInitExpr.getType() != JavaTypes.pVOID(), Res.MSG_VOID_NOT_ALLOWED );
if( match( token, ',' ) )
{
_parseInitializerExpression( JavaTypes.ARRAY_LIST().getParameterizedType( bPlaceholder ? intrinsicType : JavaTypes.OBJECT() ) );
CollectionInitializerExpression collectionInit = (CollectionInitializerExpression)popExpression();
collectionInit.addFirst( initialExpression );
IType lub = bPlaceholder ? intrinsicType : TypeLord.findLeastUpperBound( getTypes( collectionInit.getValues() ) );
type = JavaTypes.ARRAY_LIST().getParameterizedType( lub );
initializer = collectionInit;
}
else if( match( token, "->", SourceCodeTokenizer.TT_OPERATOR ) )
{
parseExpression( bPlaceholder ? intrinsicType : JavaTypes.OBJECT() );
Expression initialValueExpression = popExpression();
MapInitializerExpression mapInitializer;
if( match( token, ',' ) )
{
parseMapInitializerList( JavaTypes.HASH_MAP().getParameterizedType( bPlaceholder ? intrinsicType : JavaTypes.OBJECT(),
bPlaceholder ? intrinsicType : JavaTypes.OBJECT() ));
mapInitializer = (MapInitializerExpression)popExpression();
}
else
{
mapInitializer = new MapInitializerExpression();
}
mapInitializer.addFirst( initialExpression, initialValueExpression );
IType keysLub = TypeLord.findLeastUpperBound( getTypes( mapInitializer.getKeys() ) );
IType valuesLub = TypeLord.findLeastUpperBound( getTypes( mapInitializer.getValues() ) );
type = JavaTypes.HASH_MAP().getParameterizedType( keysLub, valuesLub );
initializer = mapInitializer;
}
else
{
CollectionInitializerExpression collectionInit= new CollectionInitializerExpression();
collectionInit.addFirst( initialExpression );
IType lub = bPlaceholder ? intrinsicType : TypeLord.findLeastUpperBound( getTypes( collectionInit.getValues() ) );
type = JavaTypes.ARRAY_LIST().getParameterizedType( lub );
initializer = collectionInit;
}
}
verify( e, match( null, '}' ), Res.MSG_EXPECTING_CLOSE_BRACE_FOR_INITIALIZER );
pushExpression( (Expression)initializer );
setLocation( startToken.getTokenStart(), startToken.getLine(), startToken.getTokenColumn(), true );
popExpression();
e.setType( type );
e.setConstructor( getImplicitConstructor(type) );
((Expression)initializer).setType( type );
e.setInitializer( initializer );
}
else
{
e.setType( intrinsicType );
if( !match( null, '}' ) )
{
if( intrinsicType.isArray() )
{
List<Expression> valueExpressions = parseArrayValueList( intrinsicType.getComponentType() );
e.setValueExpressions( valueExpressions );
ArrayList<IType> types = new ArrayList<IType>();
for (Object valueExpression : valueExpressions) {
types.add(((Expression) valueExpression).getType());
}
IType componentLeastUpperBound = TypeLord.findLeastUpperBound( types );
if( componentLeastUpperBound != GosuParserTypes.NULL_TYPE() &&
!(componentLeastUpperBound instanceof CompoundType) )
{
e.setType( componentLeastUpperBound.getArrayType());
}
verify( e, match( null, '}' ), Res.MSG_EXPECTING_CLOSE_BRACE_FOR_INITIALIZER );
}
else
{
_parseInitializerExpression( e.getType() );
IInitializerExpression initializerExpression = (IInitializerExpression)popExpression();
e.setInitializer( initializerExpression );
e.setConstructor( intrinsicType.getTypeInfo().getCallableConstructor() );
if( getCurrentInitializableContextType().equals( JavaTypes.MAP() ) &&
initializerExpression instanceof MapInitializerExpression )
{
MapInitializerExpression mapInitializer = (MapInitializerExpression)initializerExpression;
IType keysLub = TypeLord.findLeastUpperBound( getTypes( mapInitializer.getKeys() ) );
IType valuesLub = TypeLord.findLeastUpperBound( getTypes( mapInitializer.getValues() ) );
if( keysLub != GosuParserTypes.NULL_TYPE() && valuesLub != GosuParserTypes.NULL_TYPE() )
{
e.setType( JavaTypes.MAP().getParameterizedType( keysLub, valuesLub ) );
}
}
else if( (JavaTypes.COLLECTION().equals(getCurrentInitializableContextType().getGenericType()) ||
JavaTypes.LIST().equals(getCurrentInitializableContextType().getGenericType()) ||
JavaTypes.ITERABLE().equals(getCurrentInitializableContextType().getGenericType()))
&& initializerExpression instanceof CollectionInitializerExpression )
{
CollectionInitializerExpression collectionInitializerExpression = (CollectionInitializerExpression)initializerExpression;
IType valuesLub = TypeLord.findLeastUpperBound( getTypes( collectionInitializerExpression.getValues() ) );
if( valuesLub != GosuParserTypes.NULL_TYPE() )
{
e.setType( e.getType().getGenericType().getParameterizedType( valuesLub ) );
}
}
verify( e, match( null, '}' ), Res.MSG_EXPECTING_CLOSE_BRACE_FOR_INITIALIZER );
pushExpression( (Expression)initializerExpression );
setLocation( startToken.getTokenStart(), startToken.getLine(), startToken.getTokenColumn(), true );
popExpression();
}
}
else
{
e.setConstructor( intrinsicType.getTypeInfo().getCallableConstructor() );
}
}
pushExpression( e );
return true;
}
}
private List<IType> getTypes( List<? extends IHasType> list )
{
if( list == null )
{
return Collections.emptyList();
}
ArrayList<IType> returnList = new ArrayList<IType>( list.size() );
for( IHasType expression : list )
{
returnList.add( expression.getType() );
}
return returnList;
}
private IType getInitializableType()
{
IType typeToInit = getCurrentInitializableContextType();
if (typeToInit == AMBIGUOUS_TYPE) {
return typeToInit;
}
if( typeToInit == null )
{
return null;
}
else
{
if( typeToInit.isInterface() )
{
typeToInit = findImpl( typeToInit );
}
if( typeToInit != null &&
(typeToInit.isArray() ||
typeToInit.getTypeInfo().getCallableConstructor() != null) )
{
return typeToInit; // we have an unambigous, concrete type with a default constructor
}
else
{
return null;
}
}
}
private IType getCurrentInitializableContextType() {
IType typeToInit = null;
List<IType> types = _inferredContextStack.size() > 0 ? _inferredContextStack.peek() : null;
if( types != null )
{
for( IType type : types )
{
if( supportsInitializer( type ) )
{
if( typeToInit != null )
{
typeToInit = AMBIGUOUS_TYPE; //ambigous case, cannot use infered syntax
break;
}
else
{
typeToInit = type;
}
}
}
}
return typeToInit;
}
private IType getCurrentContextType()
{
List<IType> types = _inferredContextStack.size() > 0 ? _inferredContextStack.peek() : null;
if( types != null && types.size() == 1 )
{
return types.get( 0 );
}
else
{
return null;
}
}
public static IType findImpl( IType typeToInit )
{
IType genericType;
if (typeToInit.isParameterizedType()) {
genericType = typeToInit.getGenericType();
} else {
genericType = typeToInit;
}
if( genericType.equals( JavaTypes.LIST() ) ||
genericType.equals( JavaTypes.COLLECTION() ) ||
genericType.equals( JavaTypes.ITERABLE() ))
{
IJavaType arrayListType = JavaTypes.ARRAY_LIST();
if( typeToInit.isParameterizedType() )
{
arrayListType = (IJavaType)arrayListType.getParameterizedType( typeToInit.getTypeParameters() );
}
return arrayListType;
}
else if( genericType.equals( JavaTypes.SET() ) )
{
IJavaType arrayListType = JavaTypes.HASH_SET();
if( typeToInit.isParameterizedType() )
{
arrayListType = (IJavaType)arrayListType.getParameterizedType( typeToInit.getTypeParameters() );
}
return arrayListType;
}
else if( genericType.equals( JavaTypes.MAP() ) )
{
IJavaType hashMapType = JavaTypes.HASH_MAP();
if( typeToInit.isParameterizedType() )
{
hashMapType = (IJavaType)hashMapType.getParameterizedType( typeToInit.getTypeParameters() );
}
return hashMapType;
}
else
{
return null;
}
}
private boolean supportsInitializer( IType type )
{
return type.isArray() ||
JavaTypes.MAP().isAssignableFrom( type ) ||
JavaTypes.SET().isAssignableFrom( type ) ||
JavaTypes.LIST().isAssignableFrom( type ) ||
JavaTypes.COLLECTION().equals( type.getGenericType() ) ||
JavaTypes.ITERABLE().equals( type.getGenericType() ) ||
isDynamic( type );
}
private void parseBlockExpression()
{
ISymbolTable symTable = getSymbolTable();
boolean pushed = false;
int originaliBreakOk = _iBreakOk;
_iBreakOk = 0;
int originaliContinueOk = _iContinueOk;
_iContinueOk = 0;
_iReturnOk++;
try
{
List<IType> contextTypes = _inferredContextStack.peek();
IType expectedBlockReturnType = inferReturnTypeForBlockArgument( contextTypes );
_blockReturnTypeStack.push( expectedBlockReturnType );
pushed = true;
boolean foundArrow;
BlockExpression block = new BlockExpression();
StandardScope blockScope = new StandardScope();
symTable.pushScope( blockScope );
try
{
block.setScope( blockScope );
//If there are arguments, parse them
if( !match( null, "->", SourceCodeTokenizer.TT_OPERATOR ) )
{
//Infer the parameter types of the block
List<IType> inferredContextTypes = getContextTypesForBlockArgument( contextTypes );
ArrayList<ISymbol> args = parseParameterDeclarationList( block, false, inferredContextTypes );
for (ISymbol arg : args) {
_symTable.putSymbol(arg);
}
foundArrow = verify( block, match( null, "->", SourceCodeTokenizer.TT_OPERATOR ), Res.MSG_EXPECTING_ARROW_AFTER_BLOCK_ARGS );
block.setArgs( args );
}
else
{
foundArrow = true;
block.setArgs( Collections.<ISymbol>emptyList() );
}
verify( block, block.getArgs().size() <= IBlock.MAX_ARGS, Res.MSG_BLOCKS_CAN_HAVE_A_MOST_SIXTEEN_ARGS );
pushCurrentBlock( block );
try
{
//parse the expression body
if(foundArrow) {
int iOffset = _tokenizer.getTokenStart();
int iLineNum = _tokenizer.getLineNumber();
int iColumn = getTokenizer().getTokenColumn();
if( match( null, '{' ) )
{
_ctxInferenceMgr.pushLoopCompromised();
try
{
parseStatementBlock();
}
finally
{
_ctxInferenceMgr.popLoopCompromised();
}
if( peekStatement() instanceof StatementList )
{
setLocation( iOffset, iLineNum, iColumn );
}
Statement blockBody = popStatement();
if( expectedBlockReturnType != null && expectedBlockReturnType != GosuParserTypes.NULL_TYPE() )
{
boolean[] bAbsolute = {false};
ITerminalStatement term = blockBody.getLeastSignificantTerminalStatement( bAbsolute );
Statement verifyStmt;
if (blockBody instanceof StatementList &&
((StatementList) blockBody).getStatements() != null &&
((StatementList) blockBody).getStatements().length > 0)
{
StatementList lst = (StatementList)blockBody;
verifyStmt = lst.getStatements()[lst.getStatements().length - 1];
}
else
{
verifyStmt = blockBody;
}
verify( verifyStmt, term != null && (bAbsolute[0] || term.getTerminalType() == TerminalType.ForeverLoop), Res.MSG_MISSING_RETURN );
}
block.setBody( blockBody );
}
else
{
int tokenizerPostion = getTokenizer().getTokenStart();
parseExpression( expectedBlockReturnType );
Expression exprBody = popExpression();
// void functions can work in the body of a block
//noinspection ThrowableResultOfMethodCallIgnored
exprBody.removeParseException( Res.MSG_VOID_EXPRESSION_NOT_ALLOWED );
//If someone is trying to do a naked assignment, parse it and give a good error message
if( matchAssignmentOperator( new Token() ) )
{
parseExpression();
Expression assignmentBody = popExpression();
verify( assignmentBody, false, Res.MSG_ASSIGNMENTS_MUST_BE_ENCLOSED_IN_CURLIES_IN_BLOCKS );
}
else if( tokenizerPostion == getTokenizer().getTokenStart() )
{
//If someone is trying to do a naked return, parse the expression and give a good error message
Token token = new Token();
if( match( token, Keyword.KW_return ) )
{
parseExpression();
Expression returnBody = popExpression();
verify( returnBody, false, Res.MSG_STATEMENTS_MUST_BE_ENCLOSED_IN_CURLIES_IN_BLOCKS, token._strValue );
}
else if( match( token, Keyword.KW_var ) ||
match( token, Keyword.KW_switch ) ||
match( token, Keyword.KW_if ) )
{
verify( exprBody, false, Res.MSG_STATEMENTS_MUST_BE_ENCLOSED_IN_CURLIES_IN_BLOCKS, token._strValue );
}
}
// expressions with return values are acceptable when a block is going to discard the value
if( JavaTypes.pVOID().equals(expectedBlockReturnType) )
{
//noinspection ThrowableResultOfMethodCallIgnored
exprBody.removeParseException( Res.MSG_TYPE_MISMATCH );
}
block.setBody( exprBody );
}
}
block.setBlockReturnType( getBlockReturnType( block.getBody(), expectedBlockReturnType ) );
block.setScope( null );
block.updateGosuClass();
pushExpression( block );
}
finally
{
popCurrentBlock();
}
}
finally
{
symTable.popScope();
}
}
finally
{
_iBreakOk = originaliBreakOk;
_iContinueOk = originaliContinueOk;
_iReturnOk--;
if( pushed ) {
_blockReturnTypeStack.pop();
}
}
}
private IType getBlockReturnType( IParsedElement blockBody, IType ctxType )
{
if(blockBody == null) {
return ErrorType.getInstance();
}
if( blockBody instanceof Expression )
{
return ((Expression)blockBody).getType();
}
else
{
Statement stmt = (Statement) blockBody;
ArrayList<ReturnStatement> returnStatements = new ArrayList<ReturnStatement>();
ArrayList<IType> returnTypes = new ArrayList<IType>();
//noinspection unchecked
stmt.getContainedParsedElementsByTypesWithIgnoreSet( (List) returnStatements,
new HashSet( Arrays.asList( BlockExpression.class ) ),
ReturnStatement.class );
// The statement body must contain only throw statements, so
// the return type should just be whatever the context type is
if( returnStatements.size() == 0 )
{
return ctxType;
}
else
{
for( ReturnStatement returnStmt : returnStatements )
{
returnTypes.add( returnStmt.getValue().getType() );
}
return TypeLord.findLeastUpperBound( returnTypes );
}
}
}
private List<IType> getContextTypesForBlockArgument( List<IType> contextTypes )
{
Set<List<IType>> param = new HashSet<List<IType>>();
if( contextTypes != null )
{
//first look for function types
for( IType inferredArgType : contextTypes )
{
if( inferredArgType instanceof FunctionType )
{
param.add( Arrays.asList( ((FunctionType)inferredArgType).getParameterTypes() ) );
}
}
//then look for java interface types
if( param.size() == 0 )
{
for( IType inferredArgType : contextTypes )
{
IFunctionType functionType = FunctionToInterfaceCoercer.getRepresentativeFunctionType( inferredArgType );
if( functionType != null )
{
ArrayList<IType> paramTypes = new ArrayList<IType>();
for( IType parameterType : functionType.getParameterTypes() )
{
paramTypes.add( TypeLord.getDefaultParameterizedType( parameterType ) );
}
param.add( paramTypes );
}
}
}
}
//Only return an infered argument type array if there is one and only one possible argument list of
// types at this position
if( param.size() == 1 )
{
return param.iterator().next();
}
else if( param.size() > 1 )
{
List<IType> returnList = null;
for( List<IType> iTypes : param )
{
if( returnList == null )
{
returnList = iTypes;
}
else if( GosuCollectionUtil.startsWith( returnList, iTypes ) )
{
//noinspection UnnecessaryContinue
continue;
}
else if( GosuCollectionUtil.startsWith( iTypes, returnList ) )
{
returnList = iTypes;
}
else
{
//We found a disjoint set of parameter types, so we don't do any inference
returnList = null;
break;
}
}
return returnList;
}
else
{
return null;
}
}
private IType inferReturnTypeForBlockArgument( List<IType> contextTypes )
{
IType returnType = null;
if( contextTypes != null )
{
//first look for function types
for( IType inferredArgType : contextTypes )
{
if( inferredArgType instanceof FunctionType )
{
IType iType = ((FunctionType)inferredArgType).getReturnType();
if( returnType == null )
{
returnType = iType;
}
else if( !returnType.equals( iType ) )
{
return null;
}
}
}
for( IType inferredArgType : contextTypes )
{
IFunctionType functionType = FunctionToInterfaceCoercer.getRepresentativeFunctionType( inferredArgType );
if( functionType != null )
{
IType iType = functionType.getReturnType();
if( returnType == null )
{
returnType = iType;
}
else if( !returnType.equals( iType ) )
{
return null;
}
}
}
}
// If we are currently infering the return type, use the bounding type to parse
// on the way in so that we get the actual type on the way out to infer with
returnType = TypeLord.boundTypes( returnType, getCurrentlyInferringTypes() );
return returnType;
}
//------------------------------------------------------------------------------
// <i>exists-expression</i>
// <b>exists</b> <b>(</b> <identifier> <b>in</b> <expression> [ <b>index</b> <identifier> ] <b>where</b> <expression> <b>)</b>
//
void parseExistsExpression()
{
ExistsExpression existsExp = new ExistsExpression();
verify( existsExp, match( null, '(' ), Res.MSG_EXPECTING_LEFTPAREN_EXISTS );
match( null, Keyword.KW_var );
Token T = new Token();
int iNameOffset = getTokenizer().getTokenStart();
if( verify( existsExp, match( T, SourceCodeTokenizer.TT_WORD ), Res.MSG_EXPECTING_IDENTIFIER_EXISTS ) )
{
existsExp.setNameOffset( iNameOffset, T._strValue );
}
String strIdentifier = T._strValue;
verify( existsExp, match( null, Keyword.KW_in ), Res.MSG_EXPECTING_IN_EXISTS );
parseExpression();
Expression ein = popExpression();
IType typeIn = ein.getType();
verify( existsExp, LoopStatement.isIteratorType( typeIn ) || typeIn instanceof ErrorType,
Res.MSG_EXPECTING_ARRAYTYPE_EXISTS, typeIn.getName() );
Symbol symbol;
Symbol symbolIndex = null;
_symTable.pushScope();
try
{
if( match( null, Keyword.KW_index ) )
{
Token Tindex = new Token();
verify( existsExp, match( Tindex, SourceCodeTokenizer.TT_WORD ), Res.MSG_EXPECTING_IDENTIFIER_EXISTS_INDEX );
String strIndexIdentifier = Tindex._strValue;
verify( existsExp, _symTable.getSymbol( strIndexIdentifier ) == null, Res.MSG_VARIABLE_ALREADY_DEFINED, strIndexIdentifier );
// Create a temporary symbol for the identifier part of the exists statement's index
// (so it can be legally referenced in the statement).
symbolIndex = new Symbol( strIndexIdentifier, JavaTypes.pINT(), _symTable, null );
_symTable.putSymbol( symbolIndex );
}
// Create a temporary symbol for the identifier part of the exists expression
IType typeIdentifier = LoopStatement.getArrayComponentType( typeIn );
symbol = new Symbol( strIdentifier, typeIdentifier, _symTable, null );
_symTable.putSymbol( symbol );
verify( existsExp, match( null, Keyword.KW_where ), Res.MSG_EXPECTING_WHERE_EXISTS );
parseExpression( JavaTypes.pBOOLEAN() );
}
finally
{
// Remove the temporary symbols
_symTable.popScope();
}
Expression ewhere = popExpression();
verify( existsExp, match( null, ')' ), Res.MSG_EXPECTING_RIGHTPAREN_EXISTS );
existsExp.setIdentifier( symbol );
existsExp.setIndexIdentifier( symbolIndex );
existsExp.setInExpression( ein );
existsExp.setWhereExpression( ewhere );
pushExpression( existsExp );
}
boolean parseQueryExpression( Token T )
{
QueryExpressionParser queryParser = new QueryExpressionParser( this );
return queryParser.parse( T );
}
/**
* <i>new-expression</i>
* <b>new</b> <type-expression> <b>(</b> [<argument-list>] <b>)</b> <b>{</b> [<initialization-expression>] <b>}</b>
* <b>new</b> <type-expression> <b>[</b> <expression> <b>]</b>
* <b>new</b> <type-expression> <b>[</b><b>]</b> <b>{</b> [<array-value-list>] <b>}</b>
*/
void parseNewExpression()
{
parseNewExpressionOrAnnotation( false );
}
void parseNewExpressionOrAnnotation( boolean bAnnotation )
{
boolean original = isParsingAnnotation();
setParsingAnnotation( bAnnotation );
try
{
int mark = getTokenizer().mark();
TypeLiteral typeLiteral = null;
if( match( null, null, '(', true ) )
{
if( _inferredContextStack.size() > 0 && _inferredContextStack.peek() != null )
{
List<IType> contextTypes = _inferredContextStack.peek();
if( contextTypes.size() == 1 )
{
IType contextType = contextTypes.get( 0 );
typeLiteral = new TypeLiteral( contextType );
pushExpression( typeLiteral );
int iOffset = getTokenizer().getTokenStart();
int iLineNum = getTokenizer().getLineNumber();
int iColumn = getTokenizer().getTokenColumn();
setLocation( iOffset, iLineNum, iColumn, true );
popExpression();
}
}
}
if( typeLiteral == null )
{
parseTypeLiteralIgnoreArrayBrackets();
typeLiteral = (TypeLiteral)popExpression();
}
verify( typeLiteral, !(typeLiteral instanceof BlockLiteral), Res.MSG_BLOCKS_LITERAL_NOT_ALLOWED_IN_NEW_EXPR );
IType declaringClass = typeLiteral.getType().getType();
declaringClass = handleListType( typeLiteral, declaringClass );
if( isParsingStaticFeature() )
{
IType type = typeLiteral.getType().getType();
while( type.getEnclosingType() != null )
{
if( type instanceof IGosuClass && verify( typeLiteral, ((IGosuClass)type).isStatic(), Res.MSG_CANNOT_INSTANTIATE_NON_STATIC_CLASSES_HERE ) )
{
type = type.getEnclosingType();
}
else
{
break;
}
}
}
verify( typeLiteral, !declaringClass.isEnum() || getTokenizer().getCurrentToken().getType() == '[', Res.MSG_ENUM_CONSTRUCTOR_NOT_ACCESSIBLE );
parseNewExpressionOrAnnotation( declaringClass, bAnnotation, false, typeLiteral, mark );
}
finally
{
setParsingAnnotation( original );
}
}
private IType handleListType( TypeLiteral typeLiteral, IType declaringClass )
{
if( !warn( typeLiteral, TypeLord.getPureGenericType( declaringClass ) != GosuParserTypes.LIST_TYPE(), Res.MSG_LIST_TO_ARRAYLIST_WARNING ) )
{
// Implicitly coerce our List type to ArrayList
if( declaringClass.isParameterizedType() )
{
declaringClass = JavaTypes.ARRAY_LIST().getParameterizedType( declaringClass.getTypeParameters() );
}
else
{
declaringClass = JavaTypes.ARRAY_LIST();
}
}
return declaringClass;
}
void parseNewExpressionOrAnnotation( IType declaringClass, boolean bAnnotation, boolean bNoArgNoParenthesis, final TypeLiteral typeLiteral, int mark )
{
int iParenStart = _tokenizer.getTokenStart();
NewExpression e = bAnnotation ? new AnnotationExpression() : new NewExpression();
e.setType( declaringClass );
e.setTypeLiteral(typeLiteral);
verifyCanConstructInnerClassFromCallSite( e, declaringClass );
if( bNoArgNoParenthesis || match( null, '(' ) )
{
boolean bAssumeClosingParenMatch = true;
e.setArgPosition( iParenStart + 1 );
List<IConstructorType> listConstructorTypes = (!declaringClass.isInterface() || isAnnotation( declaringClass ))
? getPreliminaryConstructorTypes( declaringClass, e )
: Collections.<IConstructorType>emptyList();
scrubAnnotationConstructors(declaringClass, listConstructorTypes);
JavaAnnotationParsingState javaAnnotationParsingState = getJavaAnnotationParsingState(declaringClass, listConstructorTypes);
boolean bNoArgsProvided = false;
if( !bNoArgNoParenthesis &&
(!(bNoArgsProvided = match( null, null, ')', true )) ||
listConstructorTypes.size() > 0 && listConstructorTypes.get( 0 ).hasOptionalParams()) )
{
MethodScore bestConst = parseArgumentList( e, listConstructorTypes, null, !(declaringClass instanceof ErrorType), bNoArgsProvided, javaAnnotationParsingState );
IConstructorType constructorType = null;
if( bestConst.isValid() )
{
constructorType = (IConstructorType)bestConst.getRawFunctionType();
List<IExpression> args = bestConst.getArguments();
verifyArgCount( e, args.size(), constructorType );
//noinspection SuspiciousToArrayCall
e.setArgs( args.toArray( new Expression[args.size()] ) );
e.setType( declaringClass );
e.setConstructor( constructorType.getConstructor() );
IType[] argTypes = constructorType.getParameterTypes();
e.setArgTypes( argTypes );
e.setNamedArgOrder( bestConst.getNamedArgOrder() );
}
else
{
verify( e, false, Res.MSG_NO_CONSTRUCTOR_FOUND_FOR_CLASS, declaringClass.getName() );
e.setType( ErrorType.getInstance() );
}
bAssumeClosingParenMatch = verify( e, match( null, ')' ), Res.MSG_EXPECTING_FUNCTION_CLOSE );
boolean bAnonymous = !isInitializableType( e.getType() ) && match( null, null, '{', true );
verifyConstructorIsAccessible( declaringClass, e, constructorType, bAnonymous );
}
else
{
if( bNoArgsProvided )
{
match( null, ')' );
}
try
{
IConstructorType constructorType = getConstructorType( declaringClass, new Expression[0], null, this );
e.setType( declaringClass );
e.setConstructor( constructorType.getConstructor() );
boolean bAnonymous = !isInitializableType( e.getType() ) && match( null, null, '{', true );
verifyConstructorIsAccessible( declaringClass, e, constructorType, bAnonymous );
}
catch( ParseException pe )
{
boolean possibleAnonymousClassDecl = declaringClass.isInterface() && !isInitializableType( declaringClass );
boolean possibleDataStructDecl = isConcreteInitializableType( declaringClass );
if( (possibleAnonymousClassDecl || possibleDataStructDecl) && match( null, null, '{', true ) )
{
// Assume we are constructing an anonymous class on an interface or a data structure
}
else
{
e.setType( declaringClass );
IConstructorInfo firstCtor = getConstructor( declaringClass );
e.setConstructor( firstCtor );
e.addParseException( pe );
}
}
}
if( match( null, null, '{', true ) )
{
if( isInitializableType( e.getType() ) )
{
IToken startToken = getTokenizer().getCurrentToken();
match( null, '{' );
if( !match( null, '}' ) )
{
_parseInitializerExpression( e.getType() );
IInitializerExpression initializerExpression = (IInitializerExpression)peekExpression();
e.setInitializer( initializerExpression );
verify( e, match( null, '}' ), Res.MSG_EXPECTING_CLOSE_BRACE_FOR_INITIALIZER );
setLocation( startToken.getTokenStart(), startToken.getLine(), startToken.getTokenColumn() );
popExpression();
}
}
else if( !(declaringClass instanceof ErrorType) )
{
int state = _tokenizer.mark();
//look ahead 2 to see if this is an object initializer
if( !declaringClass.isAbstract() && !declaringClass.isInterface() &&
match( null, '{' ) && match( null, ":", SourceCodeTokenizer.TT_OPERATOR ) )
{
_tokenizer.restoreToMark( state );
parseObjectInitializer( typeLiteral.evaluate() );
ObjectInitializerExpression expression = (ObjectInitializerExpression)popExpression();
e.setInitializer( expression );
}
else if( bAssumeClosingParenMatch )
{
// Assume we are constructing an anonymous class on a gosu or java type.
declaringClass = parseAnonymousInnerClass( declaringClass, typeLiteral, e, state, mark );
}
}
}
if( e.getConstructor() != null )
{
// java annotations are abstract, technically, but can be instantiated via custom typeinfo constructors
boolean isJavaAnnotation = e.getConstructor().getOwnersType() instanceof IJavaType &&
JavaTypes.ANNOTATION().isAssignableFrom( e.getConstructor().getOwnersType() );
verify( e, isJavaAnnotation || !e.getConstructor().getOwnersType().isAbstract(),
Res.MSG_CANNOT_CONSTRUCT_ABSTRACT_CLASS, declaringClass.getName() );
// Prevent recursive types from being constructed directly
verify( e, !isRecursiveTypeFromBase( declaringClass ),
Res.MSG_CANNOT_CONSTRUCT_RECURSIVE_CLASS, declaringClass.getName() );
}
pushExpression( e );
}
else if( !bAnnotation )
{
if( verify( e, match( null, '[' ), Res.MSG_EXPECTING_NEW_ARRAY_OR_CTOR ) )
{
if( match( null, ']' ) )
{
int numArrays = 1;
while( match( null, '[' ) )
{
verify( e, match( null, ']' ), Res.MSG_EXPECTING_ARRAY_BRACKET );
++numArrays;
}
for( int i = 0; i < numArrays; i++ )
{
declaringClass = declaringClass.getArrayType();
}
verify( e, match( null, '{' ), Res.MSG_EXPECTING_OPEN_BRACE_FOR_NEW_ARRAY );
e.setType( declaringClass );
if( match( null, '}' ) )
{
e.setValueExpressions( null );
}
else
{
List valueExpressions = parseArrayValueList( declaringClass.getComponentType() );
verify( e, match( null, '}' ), Res.MSG_EXPECTING_CLOSE_BRACE_FOR_NEW_ARRAY );
//noinspection unchecked
e.setValueExpressions( valueExpressions );
}
}
else
{
parseExpression( JavaTypes.pINT() );
e.addSizeExpression( popExpression() );
IType arrayType = null;
boolean bSizelessDimension = false;
while( verify( e, match( null, ']' ), Res.MSG_EXPECTING_ARRAY_BRACKET ) )
{
declaringClass = declaringClass.getArrayType();
arrayType = declaringClass;
if( match( null, '[' ) )
{
if( !bSizelessDimension && !match( null, null, ']', true ) )
{
parseExpression( JavaTypes.pINT() );
e.addSizeExpression( popExpression() );
}
else
{
bSizelessDimension = true;
}
}
else
{
break;
}
}
if( arrayType != null )
{
declaringClass = arrayType;
}
else
{
declaringClass = ErrorType.getInstance();
}
e.setType( declaringClass );
}
}
pushExpression( e );
}
else
{
if( !(declaringClass instanceof ErrorType) )
{
ITypeInfo typeInfo = declaringClass.getTypeInfo();
if( typeInfo != null )
{
IConstructorInfo iConstructorInfo = typeInfo.getConstructor();
if (iConstructorInfo == null) {
outer:
for (IConstructorInfo constructorInfo : typeInfo.getConstructors()) {
if (constructorInfo instanceof IOptionalParamCapable) {
IExpression[] defaultVals = ((IOptionalParamCapable) constructorInfo).getDefaultValueExpressions();
for (IExpression defaultVal : defaultVals) {
if (defaultVal == null) {
continue outer;
}
}
iConstructorInfo = constructorInfo;
break;
}
}
}
if( verify( e, iConstructorInfo != null, Res.MSG_NO_DEFAULT_CTOR_IN, declaringClass.getName() ) )
{
e.setType( declaringClass );
e.setConstructor( iConstructorInfo );
e.setArgTypes();
e.setType( declaringClass );
}
else
{
e.setType( ErrorType.getInstance() );
}
}
else
{
e.setType( ErrorType.getInstance() );
}
}
else
{
e.setType( ErrorType.getInstance() );
}
pushExpression( e );
}
IConstructorInfo constructor = e.getConstructor();
if( (constructor != null) && (constructor.isDeprecated()) )
{
IParserState state;
if( typeLiteral == null )
{
state = null;
}
else
{
state = new IParserState()
{
public int getLineNumber()
{
return typeLiteral.getLocation().getLineNum();
}
public int getTokenColumn()
{
return typeLiteral.getLocation().getColumn();
}
public String getSource()
{
return getTokenizer().getSource();
}
public int getTokenStart()
{
return typeLiteral.getLocation().getOffset();
}
public int getTokenEnd()
{
return typeLiteral.getLocation().getExtent();
}
public int getLineOffset()
{
return getTokenizer().getLineOffset();
}
};
}
//noinspection ThrowableInstanceNeverThrown
e.addParseWarning( new ParseWarningForDeprecatedMember( state,
TypeInfoUtil.getConstructorSignature( constructor ),
constructor.getContainer().getName() ) );
}
}
private JavaAnnotationParsingState getJavaAnnotationParsingState(IType declaringClass, List<IConstructorType> listConstructorTypes) {
if (!CommonServices.getEntityAccess().getLanguageLevel().supportHistoricalJavaAnnotationConstructors()) {
if (JavaTypes.ANNOTATION().isAssignableFrom(declaringClass)) {
if (listConstructorTypes.size() == 1) {
IParameterInfo[] parameters = listConstructorTypes.get(0).getConstructor().getParameters();
if (parameters.length == 1 && parameters[0].getName().equals("value")) {
throw new RuntimeException("DEPRECATED 0: GosuParser@getJavaAnnotationParsingState");
//return JavaAnnotationParsingState.PARSING_JAVA_VALUE_ENUM;
}
}
throw new RuntimeException("DEPRECATED 0: GosuParser@getJavaAnnotationParsingState");
//return JavaAnnotationParsingState.PARSING_JAVA_NON_VALUE_ENUM;
}
}
return JavaAnnotationParsingState.NOT_PARSING_ENUM;
}
private void scrubAnnotationConstructors(IType declaringClass, List<IConstructorType> listConstructorTypes) {
//## hack: if this is a java annotation and we are parsing named parameters, we remove the
// legacy constructors. This allows people to port over to the standard java annotation syntax
// while retaining the original legacy constructors
if (JavaTypes.ANNOTATION().isAssignableFrom(declaringClass)) {
if (match(null, ":", ISourceCodeTokenizer.TT_OPERATOR, true)) {
Iterator<IConstructorType> iter = listConstructorTypes.iterator();
while (iter.hasNext()) {
IConstructorType next = iter.next();
IConstructorHandler constructor = next.getConstructor().getConstructor();
if (!(constructor instanceof StandardJavaAnnotationConstructor)) {
iter.remove();
}
}
} else {
Iterator<IConstructorType> iter = listConstructorTypes.iterator();
while (iter.hasNext()) {
IConstructorType next = iter.next();
IConstructorHandler constructor = next.getConstructor().getConstructor();
if (constructor instanceof StandardJavaAnnotationConstructor) {
iter.remove();
}
}
}
}
}
private boolean isRecursiveTypeFromBase( IType declaringClass )
{
if( !declaringClass.isGenericType() && !declaringClass.isParameterizedType() )
{
return false;
}
IType genType = TypeLord.getPureGenericType( declaringClass );
if( genType != TypeLord.getDefaultParameterizedType( genType ) )
{
return false;
}
if( declaringClass.isGenericType() && !declaringClass.isParameterizedType() )
{
if( declaringClass == TypeLord.getDefaultParameterizedType( declaringClass ) )
{
return true;
}
}
else if( declaringClass.isParameterizedType() )
{
for( IType typeParam : declaringClass.getTypeParameters() )
{
if( isRecursiveTypeFromBase( typeParam ) )
{
return true;
}
}
}
return false;
}
private boolean isAnnotation( IType type )
{
return JavaTypes.ANNOTATION().isAssignableFrom( type ) ||
JavaTypes.IANNOTATION().isAssignableFrom( type );
}
private ArrayList<IConstructorType> getPreliminaryConstructorTypes( IType declaringClass, NewExpression e )
{
// Get a preliminary constructorTypes to check arguments. Note we do this to aid in error feedback and value popup completion.
ArrayList<IConstructorType> listConstructorTypes = new ArrayList<IConstructorType>( 2 );
try
{
getConstructorType( declaringClass, null, listConstructorTypes, this );
}
catch( ParseException pe )
{
IConstructorInfo firstCtor = getConstructor( declaringClass );
e.setConstructor( firstCtor );
e.addParseException( pe );
}
IType[][] argTypesArray = new IType[listConstructorTypes.size()][];
IParameterInfo[][] paramTypesPossible = new IParameterInfo[listConstructorTypes.size()][];
for( int i = 0; i < argTypesArray.length; i++ )
{
IConstructorType ctorType = listConstructorTypes.get( i );
paramTypesPossible[i] = ctorType.getConstructor().getParameters();
argTypesArray[i] = ctorType.getParameterTypes();
}
return listConstructorTypes;
}
private IType parseAnonymousInnerClass( IType declaringClass, TypeLiteral typeLiteral, NewExpression newExpr, int state, int mark )
{
_tokenizer.restoreToMark( state );
newExpr.setAnonymousClass( true );
IGosuClassInternal gsDeclaringClass = null;
if( declaringClass instanceof IJavaType )
{
gsDeclaringClass = GosuClassProxyFactory.instance().create( declaringClass );
if( declaringClass.isParameterizedType() )
{
gsDeclaringClass = (IGosuClassInternal)gsDeclaringClass.getParameterizedType( declaringClass.getTypeParameters() );
}
}
else if( declaringClass instanceof IGosuClassInternal )
{
gsDeclaringClass = (IGosuClassInternal)declaringClass;
}
else
{
verify( newExpr, false, Res.MSG_BAD_ANONYMOUS_CLASS_DECLARATION );
}
if( gsDeclaringClass != null && typeLiteral != null )
{
declaringClass = TypeLord.makeDefaultParameterizedType( declaringClass );
ICompilableTypeInternal enclosingType = getCurrentEnclosingGosuClass();
// For now anonymous classes must be enclosed in a gsclass.
if( verify( newExpr, enclosingType instanceof IGosuClassInternal, Res.MSG_ANONYMOUS_CLASS_NOT_ALLOWED_HERE ) )
{
int iNameOffset = typeLiteral.getLocation().getOffset();
int originaliBreakOk = _iBreakOk;
_iBreakOk = 0;
int originaliContinueOk = _iContinueOk;
_iContinueOk = 0;
try
{
_parseAnonymousInnerClass( declaringClass, gsDeclaringClass, enclosingType, iNameOffset, newExpr, mark );
}
finally
{
_iBreakOk = originaliBreakOk;
_iContinueOk = originaliContinueOk;
}
}
}
return declaringClass;
}
private void _parseAnonymousInnerClass( IType declaringClass, IGosuClassInternal gsDeclaringClass, ICompilableTypeInternal enclosingType, int iNameOffset, NewExpression newExpr, int mark )
{
//##todo:
// Note we do NOT case where we are parsing a field initializer since
// the anonymous class should have access to all the class' fields. E.g.,
//
// class Foo {
// static var Field1 = new Whatever() {
// override function bar() : String {
// return Field2
// }
// }
// static var Field2 : String = "hello"
// }
//
String strAnonymousClass = IGosuClassInternal.ANONYMOUS_PREFIX + "_" + enclosingType.getAnonymousInnerClassCount();
boolean bTestClass = enclosingType instanceof IGosuClassInternal && ((IGosuClassInternal)enclosingType).isTestClass();
InnerClassFileSystemSourceFileHandle innerSfh = new InnerClassFileSystemSourceFileHandle(ClassType.Class, enclosingType.getName(), strAnonymousClass, bTestClass );
innerSfh.setOffset( iNameOffset );
innerSfh.setMark( mark );
IGosuClassInternal innerGsClass = (IGosuClassInternal)enclosingType.getTypeLoader().makeNewClass( innerSfh );
((IGosuClassInternal)enclosingType).addInnerClass(innerGsClass);
innerGsClass.setEnclosingType( enclosingType );
innerGsClass.setNamespace( enclosingType.getNamespace() );
innerGsClass.createNewParseInfo();
if( isParsingStaticFeature() )
{
innerGsClass.markStatic();
}
if( declaringClass.isInterface() )
{
innerGsClass.addInterface( declaringClass );
newExpr.setType( innerGsClass );
}
else
{
innerGsClass.setSuperType( declaringClass );
if( declaringClass.isEnum() )
{
innerGsClass.setEnum();
}
newExpr.setType( innerGsClass );
}
if( newExpr.getConstructor() != null )
{
GosuConstructorInfo ci = getGsConstructorInfo( newExpr.getConstructor(), gsDeclaringClass );
verify( newExpr, innerGsClass.getParseInfo().addAnonymousConstructor( _symTable, ci ), Res.MSG_NO_CONSTRUCTOR_FOUND_FOR_CLASS, gsDeclaringClass.getName() );
}
innerGsClass.setCannotCaptureSymbols( isParsingBlock() ||
((IGosuClassInternal)innerGsClass.getEnclosingType()).isCannotCaptureSymbols() );
GosuClassParser.parseAnonymousInnerClass( this, innerGsClass );
if( newExpr.getConstructor() != null && !innerGsClass.getTypeInfo().getConstructors().isEmpty() )
{
IConstructorInfo innerCtor = innerGsClass.getTypeInfo().getConstructors().get( 0 );
newExpr.setConstructor( innerCtor );
}
popStatement();
if( declaringClass.isInterface() )
{
//noinspection unchecked
List<IConstructorInfo> ctors = (List<IConstructorInfo>)innerGsClass.getTypeInfo().getConstructors();
if( ctors.size() > 0 )
{
newExpr.setConstructor( ctors.get( 0 ) );
}
}
}
public boolean isParsingStaticFeature()
{
return !_parsingStaticFeature.isEmpty() && _parsingStaticFeature.peek();
}
public void pushParsingStaticMember( Boolean bParsingStaticFeature )
{
_parsingStaticFeature.push( bParsingStaticFeature );
}
public void popParsingStaticMember()
{
_parsingStaticFeature.pop();
}
public boolean isParsingAbstractConstructor()
{
return !_parsingAbstractConstructor.isEmpty() && _parsingAbstractConstructor.peek();
}
public void pushParsingAbstractConstructor( Boolean bParsingAbstractConstructor )
{
_parsingAbstractConstructor.push( bParsingAbstractConstructor );
}
public void popParsingAbstractConstructor()
{
_parsingAbstractConstructor.pop();
}
private IConstructorInfo getConstructor( IType instanceClass )
{
IConstructorInfo firstCtor = null;
if( instanceClass.getTypeInfo() instanceof IRelativeTypeInfo)
{
if( !instanceClass.isInterface() )
{
List<? extends IConstructorInfo> ctors = ( (IRelativeTypeInfo) instanceClass.getTypeInfo() ).getConstructors( instanceClass );
if ( ! ctors.isEmpty() ) {
firstCtor = ctors.get( 0 );
}
}
}
else
{
if( !instanceClass.isInterface() )
{
ITypeInfo typeInfo = instanceClass.getTypeInfo();
if (typeInfo == null) {
firstCtor = null;
} else {
List<? extends IConstructorInfo> ctors = typeInfo.getConstructors();
if ( ! ctors.isEmpty() ) {
firstCtor = ctors.get( 0 );
}
}
}
}
return firstCtor;
}
private void verifyConstructorIsAccessible( IType instanceClass, NewExpression e, IConstructorType constructorType, boolean bAnonymous )
{
if( e.getType() instanceof ErrorType )
{
return;
}
if( constructorType != null )
{
ITypeInfo typeInfo = instanceClass.getTypeInfo();
List<? extends IConstructorInfo> accessibleCtors = getGosuClass() != null && typeInfo instanceof IRelativeTypeInfo
? ((IRelativeTypeInfo)typeInfo).getConstructors( getGosuClass() )
: typeInfo.getConstructors();
//noinspection SuspiciousMethodCalls
verify( e,
accessibleCtors.contains( constructorType.getConstructor() ) ||
(bAnonymous && constructorType.getConstructor().isProtected()),
Res.MSG_CTOR_HAS_XXX_ACCESS, getModifierString( constructorType ) );
}
}
private String getModifierString( IConstructorType constructorType )
{
return constructorType.getConstructor().isPrivate()
? "private"
: constructorType.getConstructor().isInternal()
? "internal"
: constructorType.getConstructor().isProtected()
? "protected"
: "public";
}
private boolean parseObjectInitializer( IType objectType )
{
int iOffset = _tokenizer.getTokenStart();
int iLineNum = _tokenizer.getLineNumber();
int iColumn = getTokenizer().getTokenColumn();
boolean b = _parseObjectInitializer( objectType );
setLocation( iOffset, iLineNum, iColumn );
return b;
}
private boolean _parseObjectInitializer( IType objectType )
{
if( match( null, '{' ) )
{
_doParseObjectInitializer(objectType);
verify( peekExpression(), match( null, '}' ), Res.MSG_EXPECTING_CLOSE_BRACE_FOR_INITIALIZER );
return true;
}
else
{
return false;
}
}
private void _doParseObjectInitializer(IType objectType) {
ObjectInitializerExpression oie = new ObjectInitializerExpression();
do
{
if( parseInitializerAssignment( objectType ) )
{
InitializerAssignment newAssignment = (InitializerAssignment)popStatement();
for( InitializerAssignment existing : oie.getInitializers() )
{
if( existing.getPropertyName() != null && existing.getPropertyName().equals( newAssignment.getPropertyName() ) )
{
newAssignment.addParseException( Res.MSG_REDUNTANT_INITIALIZERS, existing.getPropertyName() );
}
}
oie.add( newAssignment );
}
else
{
verify( oie, false, Res.MSG_EXPECTING_NAME_VALUE_PAIR, makeLightweightParserState() );
}
} while( match( null, ',' ) );
oie.setType( objectType );
pushExpression( oie );
}
private boolean parseInitializerAssignment( IType objectType )
{
int iOffset = _tokenizer.getTokenStart();
int iLineNum = _tokenizer.getLineNumber();
int iColumn = _tokenizer.getTokenColumn();
if( match( null, ":", SourceCodeTokenizer.TT_OPERATOR ) )
{
//handle two colons in a row with error message to preserve parse tree
//(helps intellisense)
boolean bFoundExtraColon = false;
if( match( null, ":", SourceCodeTokenizer.TT_OPERATOR ) )
{
bFoundExtraColon = true;
}
parseInitializerIdentifier( objectType );
Identifier identifier = (Identifier)popExpression();
InitializerAssignment se = new InitializerAssignment( objectType, identifier.getSymbol().getName() );
IPropertyInfo propertyInfo = null;
try
{
if( se.getPropertyName() != null )
{
propertyInfo = BeanAccess.getPropertyInfo( objectType, se.getPropertyName(), null, this, getVisibilityConstraint() );
}
}
catch( ParseException e )
{
se.addParseException( e );
}
IType type = ErrorType.getInstance();
if( propertyInfo != null )
{
verifyCase( se, se.getPropertyName(), propertyInfo.getName(), Res.MSG_PROPERTY_CASE_MISMATCH, false );
if( !JavaTypes.COLLECTION().isAssignableFrom( propertyInfo.getFeatureType() ) &&
!JavaTypes.MAP().isAssignableFrom( propertyInfo.getFeatureType() ) )
{
try
{
verifyPropertyWritable( objectType, se.getPropertyName(), true );
}
catch( Exception ex )
{
//noinspection ThrowableResultOfMethodCallIgnored
se.addParseException( ParseException.wrap( ex, makeFullParserState() ) );
}
}
type = propertyInfo.getFeatureType();
}
identifier.setType( type );
verify( se, match( null, "=", SourceCodeTokenizer.TT_OPERATOR ), Res.MSG_EQUALS_FOR_INITIALIZER_EXPR );
verify( se, !bFoundExtraColon, Res.MSG_ONLY_ONE_COLON_IN_INITIALIZERS );
parseExpression( type );
Expression value = popExpression();
if( value.hasParseExceptions() )
{
value.getParseExceptions().get( 0 ).setExpectedType( type );
}
se.setRhs( value );
pushStatement( se );
setLocation( iOffset, iLineNum, iColumn );
return true;
}
return false;
}
private void parseInitializerIdentifier( IType objectType )
{
int iOffset = _tokenizer.getTokenStart();
int iLineNum = _tokenizer.getLineNumber();
int iColumn = _tokenizer.getTokenColumn();
Token t = new Token();
Identifier i = new Identifier();
verify( i, match( t, SourceCodeTokenizer.TT_WORD ), Res.MSG_EXPECTING_NAME_PROPERTY );
i.setSymbol( new InitializerSymbol( t._strValue, objectType ), null);
i.setType( objectType );
pushExpression( i );
setLocation( iOffset, iLineNum, iColumn );
}
private GosuConstructorInfo getGsConstructorInfo( IConstructorInfo ci, IGosuClassInternal gsInstanceClass )
{
if( ci instanceof GosuConstructorInfo )
{
return (GosuConstructorInfo)ci;
}
List<IType> argTypes = new ArrayList<IType>( 2 );
if (ci != null) {
for( IParameterInfo pi : ci.getParameters() )
{
argTypes.add( pi.getFeatureType() );
}
}
return (GosuConstructorInfo) gsInstanceClass.getTypeInfo().getConstructor( gsInstanceClass, argTypes.toArray(new IType[argTypes.size()]));
}
private boolean isConcreteInitializableType( IType type )
{
return isInitializableType( type ) && !type.isAbstract() && !type.isInterface();
}
private boolean isInitializableType( IType type )
{
return type != null &&
(JavaTypes.COLLECTION().isAssignableFrom( type ) ||
JavaTypes.MAP().isAssignableFrom( type ));
}
private void verifyCanConstructInnerClassFromCallSite( NewExpression e, IType innerClass )
{
if( !(innerClass instanceof IGosuClassInternal) && !(innerClass instanceof IJavaType) )
{
return;
}
if( Modifier.isStatic( innerClass.getModifiers() ) )
{
return;
}
IType innersEnclosingClass = innerClass.getEnclosingType();
if( innersEnclosingClass == null )
{
// Not an inner class
return;
}
IGosuClassInternal constructingFromClass = getScriptPart() != null && getScriptPart().getContainingType() instanceof IGosuClass
? (IGosuClassInternal)getScriptPart().getContainingType()
: null;
verify( e, constructingFromClass != null &&
(innersEnclosingClass.isAssignableFrom( constructingFromClass ) ||
TypeLord.encloses( innersEnclosingClass, constructingFromClass )),
Res.MSG_MUST_BE_IN_OUTER_TO_CONSTRUCT_INNER,
innersEnclosingClass.getName(), innerClass.getRelativeName() );
}
private void _parseInitializerExpression( IType type )
{
if( type != null && JavaTypes.COLLECTION().isAssignableFrom( type ) )
{
parseCollectionInitializerList( type );
}
else if( type != null && JavaTypes.MAP().isAssignableFrom( type ) )
{
parseMapInitializerList( type );
}
else if( isDynamic( type ) )
{
parseCollectionInitializerList( type );
}
else
{
BadInitializerExpression be = new BadInitializerExpression();
pushExpression(be);
}
}
private IConstructorInfo getImplicitConstructor(IType type) {
if (type instanceof IErrorType ) {
return null;
}
IConstructorInfo constructor = null;
if (!type.isAbstract()) {
ITypeInfo typeInfo = type.getTypeInfo();
if (typeInfo instanceof IRelativeTypeInfo) {
constructor = ((IRelativeTypeInfo) typeInfo).getConstructor(getGosuClass(), IType.EMPTY_ARRAY);
} else {
constructor = typeInfo.getConstructor();
}
}
return constructor;
}
/*
* <i>collection-init-list</i>
* <expression>
* <collection-init-list> , <expression>
*/
private void parseCollectionInitializerList( IType type )
{
CollectionInitializerExpression lie = new CollectionInitializerExpression();
IType componentType;
if( isDynamic( type ) )
{
componentType = type.getComponentType();
}
else
{
IType listType = TypeLord.findParameterizedTypeInHierarchy( type, JavaTypes.COLLECTION() );
if( listType.isParameterizedType() && !listType.isGenericType() )
{
componentType = listType.getTypeParameters()[0];
}
else
{
componentType = JavaTypes.OBJECT();
}
}
do
{
parseExpression( componentType );
Expression e = popExpression();
lie.add( e );
}
while( match( null, ',' ) );
lie.setType( type );
pushExpression( lie );
}
/*
* <i>map-init-list</i>
* <key-expression> <b>-></b> <value-expression> ;
* <map-init-list> , <expression>
*/
private void parseMapInitializerList( IType type )
{
MapInitializerExpression mie = new MapInitializerExpression();
IType listType = TypeLord.findParameterizedTypeInHierarchy(type, JavaTypes.MAP());
IType keyType = JavaTypes.OBJECT();
IType valueType = JavaTypes.OBJECT();
if( listType.isParameterizedType() )
{
keyType = listType.getTypeParameters()[0];
valueType = listType.getTypeParameters()[1];
}
do
{
parseExpression( keyType );
Expression key = popExpression();
Expression value;
if( verify( key, match( null, "->", SourceCodeTokenizer.TT_OPERATOR ), Res.MSG_EXPECTING_ARROW_AFTER_MAP_KEY ) )
{
parseExpression( valueType );
value = popExpression();
}
else
{
value = new NullExpression();
}
mie.add( key, value );
}
while( match( null, ',' ) );
pushExpression( mie );
}
/*
* <i>array-value-list</i>
* <expression>
* <array-value-list> , <expression>
*/
List<Expression> parseArrayValueList( IType componentType )
{
while( componentType instanceof TypeVariableType )
{
componentType = ((TypeVariableType)componentType).getBoundingType();
}
List<Expression> valueExpressions = new ArrayList<Expression>();
do
{
parseExpression( componentType );
Expression e = popExpression();
valueExpressions.add( e );
}
while( match( null, ',' ) );
return valueExpressions;
}
private void parseIndirectMemberAccess( int iOffset, int iLineNum, int iColumn )
{
parseIndirectMemberAccess( iOffset, iLineNum, iColumn, false );
}
private void parseIndirectMemberAccess( int iOffset, int iLineNum, int iColumn, boolean bParsingTypeLiteralOnly )
{
do
{
if( !_parseIndirectMemberAccess( bParsingTypeLiteralOnly ) )
{
Expression expr = peekExpression();
if( !maybeReplaceErrantPackageExprWithEnumConstEpr( iOffset, iLineNum, iColumn, expr ) )
{
maybeReplacePackageExprWithTypeLiteral( iOffset, iLineNum, iColumn, expr );
verify( peekExpression(), !(peekExpression().getType() instanceof NamespaceType), Res.MSG_EXPECTING_TYPE_TO_FOLLOW_PACKAGE_NAME );
}
break;
}
setLocation( iOffset, iLineNum, iColumn );
Expression expr = peekExpression();
if( expr instanceof MemberAccess )
{
IType inferType = _ctxInferenceMgr.infer( expr );
if( inferType != null )
{
popExpression();
expr = possiblyWrapWithImplicitCoercion( expr, inferType );
pushExpression( expr );
}
}
} while( true );
}
private void maybeReplacePackageExprWithTypeLiteral( int iOffset, int iLineNum, int iColumn, Expression expr )
{
if( expr instanceof Identifier && expr.getType() instanceof NamespaceType )
{
Token T = new Token();
String strNamespace = expr.getType().getName();
T._strValue = strNamespace;
T._iDocPosition = iOffset;
T._iDocLength = strNamespace.length();
if( getNamespace() != null )
{
popExpression(); // Pop existing expression; we're going to transform it to a type literal
tryToMakeTypeLiteral( T, iOffset, iLineNum, iColumn, strNamespace, expr );
}
}
}
private boolean maybeReplaceErrantPackageExprWithEnumConstEpr( int iOffset, int iLineNum, int iColumn, Expression expr )
{
if( expr instanceof Identifier && expr.getType() instanceof NamespaceType )
{
MemberAccess enumConstExpr = parseEnumeratedConstant( ((Identifier)expr).getSymbol().getName() );
if( enumConstExpr != null )
{
// Set the errant expression's location so that its subordinate expressions are not
// included in the expression we try next...
setLocation( iOffset, iLineNum, iColumn );
enumConstExpr.setStartOffset( iOffset );
getLocationsList().remove( peekExpression().getLocation() );
popExpression();
pushExpression( enumConstExpr );
return true;
}
}
return false;
}
boolean _parseIndirectMemberAccess( boolean bParseTypeLiteralOnly )
{
Expression peekRootExpression = peekExpression();
int operatorLineNumber = getTokenizer().getLineNumber();
Token T = new Token();
if( match( null, '.' ) ||
match( T, "?.", SourceCodeTokenizer.TT_OPERATOR ) ||
match( T, "*.", SourceCodeTokenizer.TT_OPERATOR ) )
{
MemberAccessKind kind = MemberAccessKind.getForOperator( T._strValue );
Expression expression = popExpression();
verify( expression, kind != MemberAccessKind.EXPANSION ||
TypeLord.getExpandableComponentType( expression.getType() ) != null,
Res.MSG_TYPE_IS_NOT_ITERABLE, expression.getType().getName() );
LightweightParserState state = makeLightweightParserState();
verify( expression, match( T, SourceCodeTokenizer.TT_WORD ) ||
match( T, SourceCodeTokenizer.TT_KEYWORD ),
Res.MSG_EXPECTING_MEMBER_ACCESS_PATH );
parseMemberAccess( expression, kind,
T.getTokenStart(),
T._strValue == null ? "" : T._strValue,
state, bParseTypeLiteralOnly );
verifyNonVoidExpression(peekExpression());
}
else if( parseFeatureLiteral( peekRootExpression ) )
{
// good
}
else if( !bParseTypeLiteralOnly &&
(match( null, '[' ) ||
match( T, "?[", SourceCodeTokenizer.TT_OPERATOR )) )
{
Expression rootExpression = popExpression();
IType rootType = rootExpression.getType();
IType indexType = ArrayAccess.supportsArrayAccess( rootType )
? JavaTypes.pINT()
: MapAccess.supportsMapAccess( rootType )
? MapAccess.getKeyType( rootType )
: null;
parseExpression( indexType );
Expression indexExpression = popExpression();
Expression arrayAccess;
// Assume null-safety for backward compatibility in non-open-source versions, otherwise make it explicit with "?["
boolean bNullSafe = !ILanguageLevel.Util.STANDARD_GOSU() ||
(T._strValue != null && T._strValue.equals( "?[" ));
if( ArrayAccess.supportsArrayAccess( rootType ) )
{
ArrayAccess aa = new ArrayAccess();
aa.setRootExpression( rootExpression );
if( (indexExpression.getType() != JavaTypes.pINT() || indexExpression.getType() != JavaTypes.INTEGER()) &&
!(indexExpression.getType() instanceof IErrorType ||
(indexExpression.getType() instanceof IMetaType) &&
((IMetaType)indexExpression.getType()).getType() instanceof IErrorType) )
{
aa.setMemberExpression( indexExpression );
}
else
{
verify( indexExpression, false, Res.MSG_ARRAY_INDEX_MUST_BE_INT );
}
aa.setNullSafe( bNullSafe );
pushExpression( aa );
arrayAccess = aa;
}
else if( MapAccess.supportsMapAccess( rootType ) )
{
MapAccess ma = new MapAccess();
ma.setRootExpression( rootExpression );
ma.setKeyExpression( indexExpression );
ma.setNullSafe( bNullSafe );
pushExpression( ma );
arrayAccess = ma;
}
else
{
verify( rootExpression, BeanAccess.isBeanType( rootExpression.getType() ) ||
(rootExpression.getType() instanceof MetaType), Res.MSG_EXPECTING_BEAN_TYPE_WITH_REFLECTION_OPERATOR );
verify( indexExpression,
JavaTypes.CHAR_SEQUENCE().isAssignableFrom( indexExpression.getType() ),
Res.MSG_PROPERTY_REFLECTION_ONLY_WITH_STRINGS );
MemberAccess ma = new MemberAccess();
ma.setRootExpression( rootExpression );
ma.setMemberExpression( indexExpression );
ma.setType( JavaTypes.OBJECT() );
ma.setMemberAccessKind( bNullSafe ? MemberAccessKind.NULL_SAFE : MemberAccessKind.NORMAL );
pushExpression( ma );
arrayAccess = ma;
}
verify( arrayAccess, match( null, ']' ), Res.MSG_EXPECTING_BRACKET_TO_CLOSE_DYNAMIC_MEMBER_ACCESS );
}
else if( peekExpression().getType() instanceof IBlockType && match( null, '(' ) )
{
IBlockType iBlockType = (IBlockType)peekExpression().getType();
BlockInvocation bi = new BlockInvocation( popExpression() );
boolean bNoArgsProvided;
if( !(bNoArgsProvided = match( null, ')' )) || iBlockType.hasOptionalParams() )
{
MethodScore score = parseArgumentList( bi, Collections.singletonList( iBlockType ), IType.EMPTY_ARRAY, true, bNoArgsProvided, null);
bi.setNamedArgOrder( score.getNamedArgOrder() );
bi.setArgs( score.getArguments() );
verify( bi, bNoArgsProvided || match( null, ')' ), Res.MSG_EXPECTING_FUNCTION_CLOSE );
}
verify( bi, bi.getArgs().size() == iBlockType.getParameterTypes().length, Res.MSG_WRONG_NUM_OF_ARGS,
iBlockType.getParameterTypes().length );
bi.setType( iBlockType.getReturnType() );
verifyNonVoidExpression( bi );
pushExpression( bi );
}
else
{
return false;
}
// Reroot the root expression under the indirect access
if( peekRootExpression != null && !peekRootExpression.hasParseExceptions() )
{
ParseTree rootLocation = peekRootExpression.getLocation();
if( rootLocation != null )
{
getLocationsList().remove( peekRootExpression.getLocation() );
setLocation( rootLocation.getOffset(), rootLocation.getLineNum(), rootLocation.getColumn() );
peekExpression().getLocation().addChild( peekRootExpression.getLocation() );
}
}
setOperatorLineNumber(peekExpression(), operatorLineNumber);
return true;
}
private boolean parseFeatureLiteral( Expression root )
{
Token T = new Token();
if( match( null, "#", SourceCodeTokenizer.TT_OPERATOR ) )
{
if( root != popExpression() )
{
throw new IllegalStateException();
}
FeatureLiteral fle = new FeatureLiteral( root );
boolean foundWord = verify( fle, match( T, SourceCodeTokenizer.TT_WORD ) ||
match( T, Keyword.KW_construct ),
Res.MSG_FL_EXPECTING_FEATURE_NAME );
if( foundWord )
{
if( match( null, '<' ) )
{
while( verify( fle, parseTypeLiteral(), null ) )
{
popExpression(); // TypeLiteral expr
if( !match( null, ',' ) )
{
break;
}
}
verify( fle, match( null, '>' ), Res.MSG_FL_EXPECTING_RIGHT_CARET );
verify( fle, false, Res.MSG_FL_GENERIC_FUNCTION_REFERENCES_NOT_YET_SUPPORTED );
}
if( match( null, '(' ) )
{
if( !match( null, ')' ) )
{
MethodScore score = parseArgumentList( fle, fle.getFunctionTypes( T._strValue ), IType.EMPTY_ARRAY, true, false, null);
boolean allTypeLiterals = true;
for( IExpression arg : score.getArguments() )
{
if( !(arg instanceof TypeLiteral || (arg instanceof ImplicitTypeAsExpression && ((ImplicitTypeAsExpression)arg).getLHS() instanceof TypeLiteral)) )
{
allTypeLiterals = false;
}
}
if( allTypeLiterals )
{
List<IType> types = new ArrayList<IType>();
for( IExpression expression : score.getArguments() )
{
expression.clearParseExceptions();
expression.clearParseWarnings();
if( expression instanceof ImplicitTypeAsExpression )
{
expression = ((ImplicitTypeAsExpression)expression).getLHS();
}
types.add( ((TypeLiteral)expression).evaluate() );
}
IType[] typesArr = types.toArray( new IType[types.size()] );
if( T._strValue.equals( Keyword.KW_construct.getName() ) )
{
verify( fle, fle.resolveConstructor( typesArr ), Res.MSG_FL_CONSTRUCTOR_NOT_FOUND, StringUtil.join( ",", types ) );
}
else
{
verify( fle, fle.resolveMethod( T._strValue, typesArr ), Res.MSG_FL_METHOD_NOT_FOUND, T._strValue, StringUtil.join(",", types) );
}
}
else
{
IInvocableType funcType = score.getRawFunctionType();
if( funcType == null )
{
fle.setBoundFeature( ErrorType.getInstance().getTypeInfo().getMethod( T._strValue ), score.getArguments() );
}
else if( funcType instanceof FunctionType )
{
fle.setBoundFeature( ((FunctionType)funcType).getMethodInfo(), score.getArguments() );
}
else
{
fle.setBoundFeature( ((ConstructorType)funcType).getConstructor(), score.getArguments() );
}
}
verify( fle, match( null, ')' ), Res.MSG_FL_EXPECTING_RIGHT_PAREN );
}
else
{
if( T._strValue.equals( Keyword.KW_construct.getName() ) )
{
verify( fle, fle.resolveConstructor(), Res.MSG_FL_CONSTRUCTOR_NOT_FOUND, "" );
}
else
{
verify( fle, fle.resolveMethod( T._strValue ), Res.MSG_FL_METHOD_NOT_FOUND, T._strValue, "" );
}
}
}
else
{
boolean propResolved = fle.resolveProperty( T._strValue );
if( !propResolved )
{
verify( fle, propResolved, Res.MSG_FL_PROPERTY_NOT_FOUND, T._strValue );
}
}
}
if( fle.isStaticish() )
{
verify( fle, root instanceof TypeLiteral, Res.MSG_FL_STATIC_FEATURES_MUST_BE_REFERENCED_FROM_THEIR_TYPES );
}
fle.resolveType();
pushExpression( fle );
return true;
}
else
{
return false;
}
}
private void setOperatorLineNumber( Expression expression, int operatorLineNumber )
{
if( expression instanceof IHasOperatorLineNumber )
{
((IHasOperatorLineNumber)expression).setOperatorLineNumber( operatorLineNumber );
}
}
boolean parseNameOrMethodCall()
{
if( match( null, Keyword.KW_true, true ) |
match( null, Keyword.KW_false, true ) |
match( null, Keyword.KW_NaN, true ) |
match( null, Keyword.KW_Infinity, true ) |
match( null, Keyword.KW_null, true ) )
{
return false;
}
int iOffset = _tokenizer.getTokenStart();
int iLineNum = _tokenizer.getLineNumber();
int iColumn = getTokenizer().getTokenColumn();
boolean bRet = _parseNameOrMethodCall();
if( bRet )
{
verifyNonVoidExpression(peekExpression());
setLocation( iOffset, iLineNum, iColumn );
}
Expression expr = peekExpression();
if( expr instanceof Identifier )
{
IType inferType = _ctxInferenceMgr.infer( expr );
if( inferType != null )
{
Identifier ma = (Identifier)popExpression();
expr = possiblyWrapWithImplicitCoercion( ma, inferType );
//ma.setType( inferType );
pushExpression( expr );
}
}
return bRet;
}
boolean _parseNameOrMethodCall()
{
Token T = new Token();
int iOffset = _tokenizer.getTokenStart();
int iLineNum = _tokenizer.getLineNumber();
int iColumn = getTokenizer().getTokenColumn();
IParserState state = makeLightweightParserState(); //capture position of word for error reporting
int markBefore = getTokenizer().mark();
if( match( T, SourceCodeTokenizer.TT_WORD ) || match( T, Keyword.KW_super ) || match( T, Keyword.KW_this ) )
{
MethodCallExpression e = new MethodCallExpression();
IType[] typeParameters = parsePossibleFunctionParameterization( T, e );
String strFunction = T._strValue;
ISymbol functionSymbol = possiblyResolveFunctionSymbol( e, strFunction );
int mark = _tokenizer.mark();
// Only parse the function symbol as a method invocation if it is not a block. Blocks parse as identifiers
// and then indirect BlockInvocation expressions
if( !isBlockSym( functionSymbol ) && !isInSeparateStringTemplateExpression() && match( null, '(' ) )
{
parseMethodCall( T, iOffset, iLineNum, iColumn, state, e, typeParameters, strFunction, functionSymbol, mark );
}
else if( !Keyword.KW_super.equals( T._strValue ) || getTokenizer().getCurrentToken().getType() == '.' )
{
parseIdentifierOrTypeLiteralOrEnumConstant( T, iOffset, iLineNum, iColumn );
}
else
{
getTokenizer().restoreToMark( markBefore );
return false;
}
verify( peekExpression(), !GosuObjectUtil.equals( strFunction, Keyword.KW_this.toString() ) ||
!isParsingStaticFeature() ||
isParsingConstructor(), Res.MSG_CANNOT_REFERENCE_THIS_IN_STATIC_CONTEXT );
return true;
}
return false;
}
private void parseMethodCall( Token t, int iOffset, int iLineNum, int iColumn, IParserState state, MethodCallExpression e, IType[] typeParameters, String strFunction, ISymbol functionSymbol, int mark )
{
int iLocationsCount = _locations.size();
parseMethodCall( t, state, e, typeParameters, strFunction, functionSymbol );
Expression expr = peekExpression();
if( expr.hasParseExceptions() )
{
maybeParseIdentifierAssumingOpenParenIsForParenthesizedExpr( t, iOffset, iLineNum, iColumn, state, e, typeParameters, strFunction, functionSymbol, mark, iLocationsCount );
}
}
private void maybeParseIdentifierAssumingOpenParenIsForParenthesizedExpr( Token t, int iOffset, int iLineNum, int iColumn, IParserState state, MethodCallExpression e, IType[] typeParameters, String strFunction, ISymbol functionSymbol, int mark, int iLocationsCount )
{
if( !isOpenParenOnNextLine( mark ) )
{
return;
}
backtrack( mark, iLocationsCount );
parseIdentifierOrTypeLiteralOrEnumConstant( t, iOffset, iLineNum, iColumn );
Expression expr = peekExpression();
if( expr.hasParseExceptions() )
{
// Failed to parse as Identifier etc., reparse as Method call
_tokenizer.restoreToMark( mark );
expr = popExpression();
removeInnerClasses( expr );
removeLocationsFrom( iLocationsCount );
parseMethodCall( t, state, e, typeParameters, strFunction, functionSymbol );
}
}
private void backtrack( int mark, int iLocationsCount )
{
_tokenizer.restoreToMark( mark );
Expression expr = popExpression();
removeInnerClasses( expr );
removeLocationsFrom( iLocationsCount );
}
private void parseMethodCall( Token t, IParserState state, MethodCallExpression e, IType[] typeParameters, String strFunction, ISymbol functionSymbol )
{
// MethodCall
if( functionSymbol == null && getGosuClass() != null )
{
functionSymbol = getGosuClass().getExternalSymbol( strFunction );
}
if( functionSymbol != null )
{
IType symbolType = functionSymbol.getType();
if( symbolType instanceof IFunctionType )
{
int mark = getTokenizer().mark();
int iLocationsCount = _locations.size();
parsePlainFunction( (IFunctionSymbol)functionSymbol );
verifyCase( peekExpression(), strFunction, functionSymbol.getName(), state, Res.MSG_FUNCTION_CASE_MISMATCH, false );
if( peekExpression().hasParseException( Res.MSG_WRONG_NUMBER_OF_ARGS_TO_FUNCTION ) )
{
// maybe there's a print() method defined locally...
backtrack( mark, iLocationsCount );
}
else
{
return;
}
}
if( isDynamic( symbolType ) )
{
parseDynamicFunction( (Symbol)functionSymbol );
verifyCase( peekExpression(), strFunction, functionSymbol.getName(), state, Res.MSG_FUNCTION_CASE_MISMATCH, false );
return;
}
}
Expression exp = e;
int iParenStart = _tokenizer.getTokenStart();
e.setArgPosition( iParenStart );
List<IFunctionType> listFunctionTypes = null;
boolean bNoArgsProvided;
if( !(bNoArgsProvided = match( null, ')' )) ||
((listFunctionTypes = getFunctionTypesForName( strFunction )).size() == 1 &&
listFunctionTypes.get( 0 ).hasOptionalParams()) )
{
if( listFunctionTypes == null )
{
listFunctionTypes = getFunctionTypesForName( strFunction );
}
if( typeParameters != null )
{
listFunctionTypes = parameterizeFunctionTypes( e, typeParameters, listFunctionTypes );
}
pushInferredFunctionTypeVariableTypes( listFunctionTypes );
MethodScore bestMethod;
try
{
bestMethod = parseArgumentList( e, listFunctionTypes, typeParameters, true, bNoArgsProvided, null);
}
finally
{
popInferredFunctionTypeVariableTypes();
}
//noinspection SuspiciousToArrayCall
Expression[] eArgs = bestMethod.getArguments().toArray( new Expression[bestMethod.getArguments().size()] );
e.setArgs( eArgs );
boolean bMatched = bestMethod.isValid() && bestMethod.getRawFunctionType().getParameterTypes().length == eArgs.length;
for( Expression arg : eArgs )
{
if( arg.hasParseExceptions() )
{
bMatched = false;
break;
}
}
if( !bMatched )
{
IType entityType = resolveTypeName( t._strValue, true );
if( entityType != null && CommonServices.getEntityAccess().isEntityClass( entityType ) )
{
ObjectLiteralExpression objE = new ObjectLiteralExpression( entityType );
IType[] argTypes = new IType[]{GosuParserTypes.STRING_TYPE(), GosuParserTypes.STRING_TYPE()};
if( verify( objE, bestMethod.getArguments().size() > 0, Res.MSG_EXPECTING_ARGS, t._strValue ) )
{
if( verify( objE, bestMethod.getArguments().size() == 1 || bestMethod.getArguments().size() == argTypes.length,
Res.MSG_WRONG_NUM_OF_ARGS, t._strValue + ". Expecting " + argTypes.length + " or 1" ) )
{
warn( objE, false, Res.MSG_OBJECT_LITERALS_DEPRECATED );
}
}
objE.setArgs( eArgs );
exp = objE;
}
else if( listFunctionTypes.isEmpty() )
{
if( staticRefToNonStaticFunc( strFunction, eArgs ) )
{
verify( e, false, state, Res.MSG_CANNOT_CALL_NON_STATIC_METHOD_FROM_STATIC_CONTEXT, strFunction );
}
else
{
verify( e, false, state, Res.MSG_NO_SUCH_FUNCTION, strFunction );
}
}
}
if( exp == e )
{
if( bestMethod.isValid() )
{
// Did not parse as object literal
IFunctionType rawFunctionType = (IFunctionType)bestMethod.getRawFunctionType();
verifyArgCount( e, bestMethod.getArguments().size(), rawFunctionType );
if( !(GosuObjectUtil.equals( strFunction, Keyword.KW_super.toString() ) ||
GosuObjectUtil.equals( strFunction, Keyword.KW_this.toString() )) )
{
verifyCase( e, strFunction, rawFunctionType.getName(), state, Res.MSG_FUNCTION_CASE_MISMATCH, false );
}
e.setFunctionSymbol( getDFSForFunctionType( strFunction, bestMethod ) );
e.setNamedArgOrder( bestMethod.getNamedArgOrder() );
IFunctionType inferredFunctionType = (IFunctionType)bestMethod.getInferredFunctionType();
if( inferredFunctionType instanceof FunctionType )
{
((FunctionType)inferredFunctionType).setScriptPart( rawFunctionType.getScriptPart() );
}
e.setType( inferredFunctionType.getReturnType() );
e.setFunctionType( inferredFunctionType );
}
else
{
e.setType( ErrorType.getInstance() );
}
}
verify( exp, bNoArgsProvided || match( null, ')' ), Res.MSG_EXPECTING_FUNCTION_CLOSE );
}
else
{
IFunctionSymbol function = (IFunctionSymbol)getSymbolTable().getSymbol( strFunction + "()" );
if( function == null )
{
function = (IFunctionSymbol)getSymbolTable().getSymbol( strFunction );
}
// check for property match *before* checking for overloading
if( function == null || !(function.getType() instanceof IFunctionType))
{
String strPropertyName = getPropertyNameFromMethodName( strFunction );
boolean bPossiblePropertyName = getGosuClass() != null && strPropertyName != null;
if( bPossiblePropertyName )
{
t._strValue = strPropertyName;
parseIdentifier( new PropertyAsMethodCallIdentifier( strFunction ), t );
Expression expression = peekExpression();
if( !expression.hasParseExceptions() )
{
return;
}
popExpression();
}
}
if( function == null )
{
listFunctionTypes = getFunctionTypesForName( strFunction );
if( !listFunctionTypes.isEmpty() )
{
verifyArgCount( e, 0, listFunctionTypes.get( 0 ) );
}
List<IFunctionSymbol> possibleMatches = getDfsDeclsForFunction( strFunction );
if( !possibleMatches.isEmpty() )
{
function = possibleMatches.get( 0 );
e.setFunctionSymbol( function );
}
}
if( function == null || !(function.getType() instanceof IFunctionType) )
{
if( staticRefToNonStaticFunc( strFunction, new Expression[0] ) )
{
verify( e, false, state, Res.MSG_CANNOT_CALL_NON_STATIC_METHOD_FROM_STATIC_CONTEXT, strFunction );
}
else
{
verify( e, false, state, Res.MSG_NO_SUCH_FUNCTION, strFunction );
}
e.setType( ErrorType.getInstance() );
}
else
{
e.setFunctionSymbol( function );
verifyCase( e, strFunction, function.getDisplayName(), state, Res.MSG_FUNCTION_CASE_MISMATCH, false );
IFunctionType funcType = (IFunctionType)function.getType();
if( typeParameters != null )
{
if( verifyCanParameterizeType( e, funcType, typeParameters ) )
{
IFunctionType parameterizedFuncType = (IFunctionType)funcType.getParameterizedType( typeParameters );
if( verify( e, parameterizedFuncType != null, Res.MSG_COULD_NOT_PARAMETERIZE ) )
{
funcType = parameterizedFuncType;
}
}
}
assert funcType != null;
funcType = maybeParameterizeOnCtxType( funcType );
IFunctionType boundFuncType = boundFunctionType( funcType );
e.setType( boundFuncType.getReturnType() );
e.setFunctionType( boundFuncType );
}
e.setArgs( null );
}
if( isParsingAbstractConstructor() &&
e.getFunctionSymbol() instanceof DynamicFunctionSymbol &&
e.getFunctionSymbol().isAbstract() )
{
e.addParseException( new ParseException( state, Res.MSG_NO_ABSTRACT_METHOD_CALL_IN_CONSTR, e.getFunctionSymbol().getDisplayName() ) );
}
if( exp instanceof MethodCallExpression )
{
verifyNotCallingOverridableFunctionFromCtor( (MethodCallExpression)exp );
if( getGosuClass() != null && getGosuClass().isAnonymous() && getGosuClass().getEnclosingType() instanceof IGosuEnhancement )
{
verify( e, false, Res.MSG_CANNOT_REFERENCE_ENCLOSING_METHODS_WITHIN_ENHANCEMENTS );
}
}
pushExpression( exp );
}
private boolean isInSeparateStringTemplateExpression()
{
// If parsing a template or string template, look to see if we are parsing tokens within a
// separate template expression. We want to avoid fusing to expressions together
// eg. "${x} ${(x)}" will otherwise try to parse the tokens of the first expr along with the
// tokens of the second as a method bad call instead of just stopping after the first.
// We can determine if we've crossed an expression boundary by examining the prior token and
// checking for the '}' terminal in the whitespace (non-source code content is considered
// whitespace while parsing a template).
IToken priorToken = getTokenizer().getTokenAt( getTokenizer().getState() - 1 );
return priorToken != null &&
priorToken.getType() == ISourceCodeTokenizer.TT_WHITESPACE &&
(priorToken.getStringValue().indexOf( '}' ) >= 0 ||
priorToken.getStringValue().indexOf( '>' ) >= 0);
}
private ISymbol possiblyResolveFunctionSymbol( MethodCallExpression e, String strFunction )
{
ISymbol functionSymbol = _symTable.getSymbol( strFunction );
if( functionSymbol == null )
{
// Try to find a captured closure
functionSymbol = resolveSymbol( e, strFunction, false );
//noinspection ThrowableResultOfMethodCallIgnored
e.removeParseException( Res.MSG_BAD_IDENTIFIER_NAME );
if( !(functionSymbol instanceof CapturedSymbol) )
{
functionSymbol = null;
}
}
return functionSymbol;
}
private boolean isBlockSym( ISymbol functionSymbol )
{
return functionSymbol != null && functionSymbol.getType() instanceof IBlockType;
}
private boolean staticRefToNonStaticFunc( String stFunction, Expression[] eArgs )
{
if( isParsingStaticFeature() && getGosuClass() != null )
{
List<? extends IDynamicFunctionSymbol> matchingDFSs = getGosuClass().getMemberFunctions( stFunction );
for( IDynamicFunctionSymbol dfs : matchingDFSs )
{
if( !dfs.isStatic() && dfs.getArgs().size() == eArgs.length )
{
return true;
}
}
}
return false;
}
private boolean staticRefToNonStaticProp( String name )
{
if( isParsingStaticFeature() && getGosuClass() != null )
{
IDynamicPropertySymbol dps = getGosuClass().getMemberProperty( name );
if( dps != null && !dps.isStatic() )
{
return true;
}
}
return false;
}
private IFunctionType boundFunctionType( IFunctionType funcType )
{
if( funcType.isGenericType() )
{
IGenericTypeVariable[] typeVariables = funcType.getGenericTypeVariables();
List<IType> functionTypeVars = new ArrayList<IType>();
for( IGenericTypeVariable typeVariable : typeVariables )
{
ITypeVariableDefinition typeVariableDefinition = typeVariable.getTypeVariableDefinition();
if( typeVariableDefinition != null && typeVariableDefinition.getType() != null )
{
functionTypeVars.add( typeVariableDefinition.getType() );
}
}
return (IFunctionType)TypeLord.boundTypes( funcType, functionTypeVars );
}
else
{
return funcType;
}
}
private IDynamicFunctionSymbol getDFSForFunctionType( String strFunction, MethodScore bestMethod )
{
List<IFunctionSymbol> list = getDfsDeclsForFunction( strFunction );
IInvocableType rawFunctionType = bestMethod.getRawFunctionType();
for( IFunctionSymbol dfs : list )
{
IType dfsType = dfs.getType();
if( dfsType.equals( rawFunctionType ) || dfsType.equals( rawFunctionType.getGenericType() ) )
{
return (IDynamicFunctionSymbol) dfs;
}
}
throw new IllegalStateException( "Could not find matching DFS in " + list + " for type " + rawFunctionType );
}
private void verifyNotCallingOverridableFunctionFromCtor( MethodCallExpression mce )
{
if( !isParsingConstructor() || isParsingBlock() )
{
return;
}
IFunctionSymbol fs = mce.getFunctionSymbol();
if( fs instanceof DynamicFunctionSymbol &&
!(fs instanceof SuperConstructorFunctionSymbol) &&
!(fs instanceof ThisConstructorFunctionSymbol) )
{
DynamicFunctionSymbol dfs = (DynamicFunctionSymbol)fs;
verifyOrWarn( mce, dfs.isPrivate() || dfs.isFinal() || dfs.isStatic(), true, Res.MSG_CALLING_OVERRIDABLE_FROM_CTOR, dfs.getName() );
}
}
private void verifyNotCallingOverridableFunctionFromCtor( BeanMethodCallExpression mce )
{
if( !isParsingConstructor() || isParsingBlock() )
{
return;
}
if( mce.getRootExpression() instanceof Identifier &&
((Identifier)mce.getRootExpression()).getSymbol() != null &&
Keyword.KW_this.equals( ((Identifier)mce.getRootExpression()).getSymbol().getName() ) )
{
IMethodInfo mi = mce.getMethodDescriptor();
if( mi instanceof AbstractGenericMethodInfo )
{
ReducedDynamicFunctionSymbol dfs = ((AbstractGenericMethodInfo)mi).getDfs();
if (!((AbstractGenericMethodInfo)mi).getDfs().isSuperOrThisConstructor())
{
verifyOrWarn( mce, dfs.isPrivate() || dfs.isFinal() || dfs.isStatic(), true, Res.MSG_CALLING_OVERRIDABLE_FROM_CTOR, dfs.getName() );
}
}
}
}
private boolean isParsingConstructor()
{
if( !isParsingFunction() )
{
return false;
}
FunctionType funcType = peekParsingFunction();
return funcType.getReturnType() == getGosuClass() &&
funcType.getName().equals( funcType.getReturnType().getRelativeName() );
}
private IType[] parsePossibleFunctionParameterization( Token T, MethodCallExpression e )
{
if( match( null, "<", SourceCodeTokenizer.TT_OPERATOR, true ) )
{
List<IFunctionType> listFunctionTypes = getFunctionTypesForName( T._strValue );
for( IFunctionType ftype : listFunctionTypes )
{
if( ftype.isGenericType() )
{
return parseFunctionParameterization( e );
}
}
}
return null;
}
private void parseIdentifier( Token T )
{
parseIdentifier( new Identifier(), T );
}
private void parseIdentifier(Identifier e, Token T)
{
// Identifier
String name = T._strValue;
ISymbol s = resolveSymbol( e, name, true );
if( s instanceof DynamicFunctionSymbol )
{
// No function symbols allowed as identifiers; use a blocks for that
s = resolveNamespaceSymbol( e, name );
}
else if( s instanceof DynamicPropertySymbol )
{
//wrap in a PropertyAccessIdentifier to distinguish between identifiers that
//invoke code versus those that do not
e = new PropertyAccessIdentifier( e );
}
IType type = s.getType();
e.setSymbol( s, _symTable );
e.setType( type );
if( s instanceof DynamicPropertySymbol )
{
if( getGosuClass() != null && !(getGosuClass() instanceof IGosuProgram) && getGosuClass().isAnonymous() && getGosuClass().getEnclosingType() instanceof IGosuEnhancement )
{
if( s.getName().equals( Keyword.KW_outer.toString() ) )
{
verify( e, false, Res.MSG_CANNOT_REFERENCE_OUTER_SYMBOL_WITHIN_ENHANCEMENTS );
}
else
{
verify( e, false, Res.MSG_CANNOT_REFERENCE_ENCLOSING_PROPERTIES_WITHIN_ENHANCEMENTS );
}
}
}
pushExpression( e );
verify( e, !(getCurrentEnclosingGosuClass() instanceof IBlockClass) ||
!Keyword.KW_super.equals( s.getName() ), Res.MSG_SUPER_NOT_ACCESSIBLE_FROM_BLOCK );
if( !(type instanceof ErrorType) && !(type instanceof IFunctionType) )
{
//resolve a symbol, let's make sure its case is correct
verifyCase( e, name, s.getName(), Res.MSG_VAR_CASE_MISMATCH, false );
}
}
private void parseIdentifierOrTypeLiteralOrEnumConstant( Token T, int iOffset, int iLineNum, int iColumn )
{
// Identifier
String name = T._strValue;
parseIdentifier( T );
Expression identifier = peekExpression();
IType type = identifier.getType();
if( type instanceof ErrorType || (type instanceof IFunctionType && !(type instanceof IBlockType)))
{
// Couldn't parse an identifer , lets try parsing static member-access or a type literal...
//
// Note we must parse a type literal here instead of (or in addition to)
// parseLiteral() because Identifiers and TypeLiterals have the same
// start symbol.
// Set the errant expression's location so that its subordinate expressions are not
// included in the expression we try next...
setLocation( iOffset, iLineNum, iColumn );
getLocationsList().remove( peekExpression().getLocation() );
Expression errantExpression = popExpression();
// See if it can be parsed as an inferred enum expression
MemberAccess enumConstExpr = parseEnumeratedConstant( T._strValue );
if( enumConstExpr != null )
{
pushExpression( enumConstExpr );
enumConstExpr.setStartOffset( iOffset );
}
else
{
// Try parsing a type literal
tryToMakeTypeLiteral( T, iOffset, iLineNum, iColumn, name, errantExpression );
}
}
}
private void tryToMakeTypeLiteral( Token T, int iOffset, int iLineNum, int iColumn, String name, Expression errantExpression )
{
TypeLiteral tl = resolveTypeLiteral( T );
boolean bArrayOrParameterzied = resolveArrayOrParameterizationPartOfTypeLiteral( T, false, tl );
if( !bArrayOrParameterzied && ((TypeLiteral)peekExpression()).getType().getType() instanceof ErrorType )
{
popExpression();
// All has failed.
// Try to determine the most suitable errant expression and push that
if( staticRefToNonStaticProp( name ) )
{
errantExpression.clearParseExceptions();
verify( errantExpression, false, Res.MSG_CANNOT_REFERENCE_NON_STATIC_PROPERTY_FROM_STATIC_CONTEXT );
}
pushExpression( errantExpression );
}
else if( match( null, "&", SourceCodeTokenizer.TT_OPERATOR, true ) )
{
setLocation( iOffset, iLineNum, iColumn ); // set location of initial type literal (tl)
parseAggregateTypeLiteral(false);
}
}
private void parseNamespaceStartOrRelativeType( Token T, boolean bInterface )
{
IType type = resolveNamespace( T._strValue );
if( type != null )
{
Identifier e = new Identifier();
ISymbol s = new Symbol( T._strValue, type, null );
e.setSymbol( s, _symTable );
e.setType( type );
pushExpression( e );
}
else
{
TypeLiteral tl = resolveTypeLiteral( T, true, bInterface );
resolveArrayOrParameterizationPartOfTypeLiteral( T, true, tl );
}
}
private MemberAccess parseEnumeratedConstant( String strConstValue )
{
if( _inferredContextStack.size() > 0 && _inferredContextStack.peek() != null )
{
List<IType> contextTypes = _inferredContextStack.peek();
for( IType contextType : contextTypes )
{
if( contextType.isEnum() )
{
IEnumType enumAccess = (IEnumType)contextType;
try
{
IPropertyInfo property = enumAccess.getTypeInfo().getProperty( strConstValue );
if( property != null && property.isStatic() )
{
MemberAccess ma = new MemberAccess();
TypeLiteral e = new TypeLiteral( MetaType.getLiteral( property.getOwnersType() ) );
ma.setRootExpression( e );
ma.setMemberName( property.getName() );
ma.setType( property.getFeatureType() );
ma.setMemberAccessKind( MemberAccessKind.NORMAL );
return ma;
}
}
catch( IllegalArgumentException iae )
{
// skip
}
}
}
}
return null;
}
private void verifyArgCount( ParsedElement element, int iArgs, IFunctionType funcType )
{
int expectedArgs = funcType.getParameterTypes().length;
if( iArgs != expectedArgs )
{
ParseException pe = new WrongNumberOfArgsException( makeFullParserState(), Res.MSG_WRONG_NUMBER_OF_ARGS_TO_FUNCTION, funcType.getParamSignature(), expectedArgs, iArgs );
pe.setParamTypesExpected( funcType.getParameterTypes() );
pe.setCausedByArgumentList( true );
element.addParseException( pe );
}
}
private void verifyOverrideNotOnMethodThatDoesNotExtend( ParsedElement element, DynamicFunctionSymbol dfs )
{
if( !dfs.isOverride() )
{
return;
}
ParseException pe = null;
ICompilableType gsClass = getGosuClass();
if( gsClass != null )
{
String strPotentialProperty = getPropertyNameFromMethodNameIncludingSetter( dfs.getDisplayName() );
if( strPotentialProperty != null )
{
pe = new DoesNotOverrideFunctionException( makeFullParserState(), Res.MSG_FUNCTION_NOT_OVERRIDE_PROPERTY, dfs.getName(), strPotentialProperty );
}
}
if( pe == null )
{
pe = new DoesNotOverrideFunctionException( makeFullParserState(), Res.MSG_FUNCTION_NOT_OVERRIDE, dfs.getName() );
}
element.addParseException( pe );
}
private void verifyArgCount( ParsedElement element, int iArgs, IConstructorType ctorType )
{
int expectedArgs = ctorType.getParameterTypes().length;
if( iArgs != expectedArgs )
{
ParseException pe = new ParseException( makeFullParserState(), Res.MSG_WRONG_NUMBER_OF_ARGS_TO_CONSTRUCTOR, ctorType.getArgSignature(), expectedArgs, iArgs );
pe.setParamTypesExpected( ctorType.getParameterTypes() );
pe.setCausedByArgumentList( true );
element.addParseException( pe );
}
}
private void parsePlainFunction( IFunctionSymbol functionSymbol )
{
MethodCallExpression e = new MethodCallExpression();
e.setFunctionSymbol( functionSymbol );
IFunctionType funcType = (IFunctionType)functionSymbol.getType();
e.setType( funcType.getReturnType() );
IType[] argTypes = funcType.getParameterTypes();
boolean bNoArgsProvided;
if( !(bNoArgsProvided = match( null, ')' )) || funcType.hasOptionalParams() )
{
verify( e, argTypes != null && argTypes.length > 0, Res.MSG_NO_ARGUMENTS, functionSymbol.getName() );
MethodScore score = parseArgumentList( e, Collections.singletonList( funcType ), null, true, bNoArgsProvided, null);
if( score.isValid() )
{
List<IExpression> scoreArgs = score.getArguments();
verifyArgCount( e, scoreArgs.size(), funcType );
//noinspection SuspiciousToArrayCall
e.setArgs( scoreArgs.toArray( new Expression[scoreArgs.size()] ) );
e.setNamedArgOrder( score.getNamedArgOrder() );
}
verify( e, bNoArgsProvided || match( null, ')' ), Res.MSG_EXPECTING_FUNCTION_CLOSE );
}
else
{
verify( e, argTypes == null || argTypes.length == 0, Res.MSG_EXPECTING_ARGS, functionSymbol.getName() );
e.setArgs( null );
}
pushExpression( e );
}
private void parseDynamicFunction( Symbol dynamcSymbol )
{
MethodCallExpression e = new MethodCallExpression();
e.setFunctionSymbol( dynamcSymbol );
e.setType( dynamcSymbol.getType() );
if( !match( null, ')' ) )
{
MethodScore score = parseArgumentList( e, Collections.singletonList(
new FunctionType( dynamcSymbol.getName(), dynamcSymbol.getType(), IType.EMPTY_ARRAY ) ), null, true, false, null );
List<IExpression> scoreArgs = score.getArguments();
//noinspection SuspiciousToArrayCall
e.setArgs( scoreArgs.toArray( new Expression[scoreArgs.size()] ) );
e.setNamedArgOrder( score.getNamedArgOrder() );
verify( e, match( null, ')' ), Res.MSG_EXPECTING_FUNCTION_CLOSE );
}
else
{
e.setArgs( null );
}
pushExpression( e );
}
private void parseMemberAccess( Expression rootExpression, MemberAccessKind kind, int iTokenStart, String strMemberName, LightweightParserState state, boolean bParseTypeLiteralOnly )
{
parseMemberAccess( rootExpression, kind, iTokenStart, strMemberName, state, bParseTypeLiteralOnly, false );
}
private void parseMemberAccess( Expression rootExpression, MemberAccessKind kind, final int iTokenStart, final String strMemberName, LightweightParserState state, boolean bParseTypeLiteralOnly, boolean createSynthesizedProperty )
{
BeanMethodCallExpression e = new BeanMethodCallExpression();
IType rootType = rootExpression.getType();
rootType = IGosuClass.ProxyUtil.isProxy( rootType ) && rootType instanceof IGosuClass ? ((IGosuClass) rootType).getJavaType() : rootType;
if( rootType != null && !rootType.isArray() )
{
boolean bAcceptableType =
BeanAccess.isBeanType( rootType ) ||
rootType == GosuParserTypes.BOOLEAN_TYPE() ||
rootType == GosuParserTypes.STRING_TYPE() ||
rootType == GosuParserTypes.NUMBER_TYPE() ||
rootType instanceof MetaType;
verify( e, bAcceptableType, Res.MSG_EXPECTING_BEANTYPE, rootType.getName() );
}
boolean bExpansion = kind == MemberAccessKind.EXPANSION;
IType[] typeParameters = null;
try
{
if( !(bParseTypeLiteralOnly || rootType instanceof ErrorType) )
{
List<IFunctionType> list = new ArrayList<IFunctionType>();
// if any function with the specified name is generic, parse parameterization
getFunctionType( bExpansion ? TypeLord.getExpandableComponentType( rootType ) : rootType, strMemberName, null, list, this, true );
for( IFunctionType ftype : list )
{
if( ftype.isGenericType() )
{
typeParameters = parseFunctionParameterization( e );
break;
}
}
}
}
catch( ParseException pe )
{
e.addParseException( pe );
}
int iParenStart = _tokenizer.getTokenStart();
int mark = _tokenizer.mark();
if( !bParseTypeLiteralOnly && !isBlockInvoke( rootExpression, strMemberName ) && match( null, '(' ) )
{
// Method call
parseMethodMember( rootExpression, kind, iTokenStart, strMemberName, state, bParseTypeLiteralOnly, createSynthesizedProperty, e, rootType, bExpansion, typeParameters, iParenStart, mark );
}
else
{
// Property access
parsePropertyMember( rootExpression, kind, iTokenStart, strMemberName, state, bParseTypeLiteralOnly, createSynthesizedProperty, rootType, bExpansion );
}
}
private void parseMethodMember( Expression rootExpression, MemberAccessKind kind, int iTokenStart, String strMemberName, LightweightParserState state, boolean bParseTypeLiteralOnly, boolean createSynthesizedProperty, BeanMethodCallExpression e, IType rootType, boolean bExpansion, IType[] typeParameters, int iParenStart, int mark )
{
int iLocationsCount = _locations.size();
parseMethodMember( rootExpression, kind, iTokenStart, strMemberName, state, bParseTypeLiteralOnly, e, rootType, bExpansion, typeParameters, iParenStart );
Expression expr = peekExpression();
if( expr.hasParseExceptions() )
{
maybeOpenParenIsForParenthesizedExpr( rootExpression, kind, iTokenStart, strMemberName, state, bParseTypeLiteralOnly, createSynthesizedProperty, e, rootType, bExpansion, typeParameters, iParenStart, mark, iLocationsCount );
}
}
private void maybeOpenParenIsForParenthesizedExpr( Expression rootExpression, MemberAccessKind kind, int iTokenStart, String strMemberName, LightweightParserState state, boolean bParseTypeLiteralOnly, boolean createSynthesizedProperty, BeanMethodCallExpression e, IType rootType, boolean bExpansion, IType[] typeParameters, int iParenStart, int mark, int iLocationsCount )
{
if( !isOpenParenOnNextLine( mark ) )
{
return;
}
backtrack( mark, iLocationsCount );
parsePropertyMember( rootExpression, kind, iTokenStart, strMemberName, state, bParseTypeLiteralOnly, createSynthesizedProperty, rootType, bExpansion );
Expression expr = peekExpression();
if( expr.hasParseExceptions() )
{
// Failed to parse as Property, reparse as Method call
_tokenizer.restoreToMark( mark );
expr = popExpression();
removeInnerClasses( expr );
removeLocationsFrom( iLocationsCount );
parseMethodMember( rootExpression, kind, iTokenStart, strMemberName, state, bParseTypeLiteralOnly, e, rootType, bExpansion, typeParameters, iParenStart );
}
}
private boolean isOpenParenOnNextLine( int mark )
{
if( mark <= 0 )
{
return false;
}
IToken priorMarkToken = _tokenizer.getTokenAt( mark - 1 );
return priorMarkToken != null && priorMarkToken.getType() == ISourceCodeTokenizer.TT_WHITESPACE && priorMarkToken.getText().indexOf( '\n' ) >= 0;
}
private void parseMethodMember( Expression rootExpression, MemberAccessKind kind, int iTokenStart, String strMemberName, LightweightParserState state, boolean bParseTypeLiteralOnly, BeanMethodCallExpression e, IType rootType, boolean bExpansion, IType[] typeParameters, int iParenStart )
{
e.setArgPosition( iParenStart + 1 );
e.setRootExpression( rootExpression );
IMethodInfo md = null;
List<IFunctionType> listFunctionTypes = getPreliminaryFunctionTypes( strMemberName, e, rootType, bExpansion, typeParameters );
boolean bNoArgsProvided;
if( !(bNoArgsProvided = match( null, ')' )) ||
(listFunctionTypes.size() == 1 && listFunctionTypes.get( 0 ).hasOptionalParams()) )
{
pushInferredFunctionTypeVariableTypes( listFunctionTypes );
MethodScore methodScore;
try
{
methodScore = parseArgumentList( e, listFunctionTypes, typeParameters, !(rootType instanceof ErrorType), bNoArgsProvided, null);
}
finally
{
popInferredFunctionTypeVariableTypes();
}
//noinspection SuspiciousToArrayCall
Expression[] eArgs = methodScore.getArguments().toArray( new Expression[methodScore.getArguments().size()] );
e.setArgs( eArgs );
if( methodScore.isValid() )
{
IFunctionType funcType = (IFunctionType)methodScore.getInferredFunctionType();
verifyArgCount( e, eArgs.length, funcType );
assert funcType != null;
e.setType( (!bExpansion || funcType.getReturnType().isArray() || funcType.getReturnType() == JavaTypes.pVOID())
? funcType.getReturnType()
: funcType.getReturnType().getArrayType() );
e.setFunctionType( funcType );
IType[] argTypes = funcType.getParameterTypes();
e.setArgTypes( argTypes );
IFunctionType rawFunctionType = (IFunctionType)methodScore.getRawFunctionType();
md = rawFunctionType.getMethodInfo();
if( !md.isVisible( getVisibilityConstraint() ) )
{
verify( e, false, Res.MSG_METHOD_NOT_VISIBLE, strMemberName );
}
//verify( md == null || !bMetaType || accessList.size() > 1 || md.isStatic(), strFunction + " cannot call non-static methods here." );
e.setMethodDescriptor( bExpansion ? new ArrayExpansionMethodInfo( md ) : md );
e.setMemberAccessKind( kind );
e.setNamedArgOrder( methodScore.getNamedArgOrder() );
verifyCase( e, strMemberName, funcType.getName(), state, Res.MSG_FUNCTION_CASE_MISMATCH, false );
}
else
{
if( !(rootType instanceof ErrorType) )
{
verify( e, false, state, Res.MSG_NO_SUCH_FUNCTION, strMemberName );
}
e.setType( ErrorType.getInstance() );
}
e.setAccessPath( strMemberName );
verify( e, bNoArgsProvided || match( null, ')' ), Res.MSG_EXPECTING_FUNCTION_CLOSE );
}
else
{
//No parameters were found
if( rootType instanceof ErrorType )
{
e.setType( ErrorType.getInstance() );
e.setArgTypes( IType.EMPTY_ARRAY );
e.setAccessPath( strMemberName );
md = null;
e.setMethodDescriptor( md );
e.setArgs( null );
}
else
{
IFunctionType funcType;
try
{
funcType = getFunctionType( bExpansion ? TypeLord.getExpandableComponentType( rootType ) : rootType, strMemberName, new Expression[0], null, this, true );
}
catch( ParseException pe )
{
// If this method call is a getXXX() call, we can try to convert it to a XXX property reference...
String strPropertyName = getPropertyNameFromMethodName( strMemberName );
if( strPropertyName == null )
{
try
{
funcType = getFunctionType( bExpansion ? TypeLord.getExpandableComponentType( rootType ) : rootType, strMemberName, new Expression[0], null, this, false );
e.addParseException( pe );
}
catch( ParseException e1 )
{
e.addParseException( pe );
e.setType( ErrorType.getInstance() );
e.setStartOffset( iTokenStart );
pushExpression( e );
return;
}
}
else
{
parseMemberAccess( rootExpression, kind, iTokenStart, strPropertyName, state, bParseTypeLiteralOnly, true );
Expression expression = peekExpression();
if( expression instanceof MemberAccess ) {
MemberAccess ma = (MemberAccess) expression;
ma.setMethodNameForSyntheticAccess( strMemberName );
}
if( peekExpression().hasParseExceptions() )
{
e.addParseException( pe );
e.addParseExceptions( peekExpression().getParseExceptions() );
popExpression();
e.setType( ErrorType.getInstance() );
e.setStartOffset( iTokenStart );
pushExpression( e );
}
return;
}
}
if( typeParameters != null )
{
if( verifyCanParameterizeType( e, funcType, typeParameters ) )
{
IFunctionType parameterizedFuncType = (IFunctionType)funcType.getParameterizedType( typeParameters );
if( verify( e, parameterizedFuncType != null, Res.MSG_COULD_NOT_PARAMETERIZE ) )
{
funcType = parameterizedFuncType;
}
}
}
assert funcType != null;
funcType = maybeParameterizeOnCtxType( funcType );
IFunctionType boundType = boundFunctionType( funcType );
e.setType( (!bExpansion || funcType.getReturnType().isArray() || funcType.getReturnType() == JavaTypes.pVOID())
? boundType.getReturnType()
: boundType.getReturnType().getArrayType() );
e.setFunctionType( funcType );
IType[] argTypes = funcType.getParameterTypes();
e.setArgTypes( argTypes );
e.setAccessPath( strMemberName );
md = funcType.getMethodInfo();
verify( e, md.isVisible( getVisibilityConstraint() ), Res.MSG_METHOD_NOT_VISIBLE, strMemberName );
verify( e, !md.isAbstract() ||
!(rootExpression instanceof Identifier) ||
!((Identifier)rootExpression).getSymbol().getName().equals( Keyword.KW_super.toString() ) ||
GosuClass.isObjectMethod( md ),
Res.MSG_ABSTRACT_METHOD_CANNOT_BE_ACCESSED_DIRECTLY, strMemberName );
//verify( md == null || !bMetaType || accessList.size() > 1 || md.isStatic(), strFunction + " cannot call non-static methods here." );
e.setMethodDescriptor( bExpansion ? new ArrayExpansionMethodInfo( md ) : md );
e.setMemberAccessKind( kind );
verifyCase( e, strMemberName, md.getDisplayName(), state, Res.MSG_FUNCTION_CASE_MISMATCH, false );
verifyArgCount( e, 0, funcType );
e.setArgs( null );
}
}
e.setStartOffset( iTokenStart );
if( md != null && md.isDeprecated() )
{
// Add a warning if the method is deprecated
e.addParseWarning(
new ParseWarningForDeprecatedMember( state.cloneWithNewTokenStartAndTokenEnd( iTokenStart, md.getDisplayName().length() ),
TypeInfoUtil.getMethodSignature( md ), e.getRootType().getName() ) );
}
if( isParsingAbstractConstructor() )
{
handleAbstractCtor( iTokenStart, strMemberName, e, state );
}
verifyNotCallingOverridableFunctionFromCtor( e );
pushExpression( e );
}
private void parsePropertyMember( Expression rootExpression, MemberAccessKind kind, int iTokenStart, String strMemberName, LightweightParserState state, boolean bParseTypeLiteralOnly, boolean createSynthesizedProperty, IType rootType, boolean bExpansion )
{
IPropertyInfo pi = null;
MemberAccess ma = bExpansion
? new MemberExpansionAccess()
: createSynthesizedProperty
? new SynthesizedMemberAccess()
: new MemberAccess();
ma.setRootExpression( rootExpression );
ma.setMemberAccessKind( kind );
IType memberType = null;
try
{
if( rootType instanceof IFunctionType && rootExpression instanceof Identifier )
{
// Can't deref functions, convert to namespace if possible (this is to handle a bad
// relative namespaace problem e.g., print.foo.SomeTime where print is a namespace in gw.api.print ..yeeeaaah)
Identifier identifier = (Identifier)rootExpression;
INamespaceType namespaceType = resolveNamespace( identifier.getSymbol().getName() );
if( namespaceType != null )
{
Symbol sym = new Symbol( namespaceType.getRelativeName(), namespaceType, null );
identifier.setSymbol( sym, getSymbolTable() );
identifier.setType( namespaceType );
rootType = namespaceType;
}
}
if( rootType instanceof INamespaceType )
{
if( !strMemberName.equals( "*" ) )
{
String strType = rootType.getName() + '.' + strMemberName;
// First, try a sub-namespace
memberType = TypeSystem.getNamespace( strType );
if( memberType == null )
{
// Now try a fq type name
memberType = resolveTypeName( strType, true );
if( memberType == null )
{
memberType = resolveNamespace( strType );
}
else if( memberType != null )
{
Token T = new Token();
T._strValue = strType;
TypeLiteral tl = resolveTypeLiteral( T );
resolveArrayOrParameterizationPartOfTypeLiteral( T, bParseTypeLiteralOnly, tl );
tl.setPackageExpression( rootExpression );
return;
}
}
}
}
else if( rootExpression instanceof TypeLiteral )
{
// Try an inner class name
IType typeLiteralType = ((MetaType)rootType).getType();
if( typeLiteralType instanceof IHasInnerClass )
{
memberType = getInnerClass( strMemberName, memberType, (IHasInnerClass)typeLiteralType );
if( memberType != null )
{
if( !shouldParseMemberInstead( strMemberName, rootType, bExpansion, memberType ) )
{
Token T = new Token();
T._strValue = memberType.getName();
TypeLiteral tl = resolveTypeLiteral( T );
resolveArrayOrParameterizationPartOfTypeLiteral( T, bParseTypeLiteralOnly, tl );
verifyTypeAccessible( tl, memberType );
tl.setPackageExpression( rootExpression );
return;
}
}
}
else if( typeLiteralType instanceof ErrorType )
{
memberType = ErrorType.getInstance();
}
}
if( memberType == null )
{
if( bParseTypeLiteralOnly && !(rootType instanceof ErrorType) )
{
if( rootType instanceof INamespaceType )
{
verify( ma, false, Res.MSG_NO_TYPE_ON_NAMESPACE, strMemberName, rootType == null ? "<no type specified>" : rootType.getName() );
}
else
{
IType errRootType = rootType instanceof IMetaType ? ((IMetaType)rootType).getType() : rootType;
addError( ma, Res.MSG_INVALID_INNER_TYPE, strMemberName, TypeLord.getPureGenericType( errRootType ).getRelativeName() );
}
memberType = ErrorType.getInstance();
}
else
{
pi = BeanAccess.getPropertyInfo( bExpansion ? TypeLord.getExpandableComponentType( rootType ) : rootType,
strMemberName, null, this, getVisibilityConstraint() );
memberType = bExpansion ? new ArrayExpansionPropertyInfo( pi ).getFeatureType() : pi.getFeatureType();
if( pi != null )
{
verifyCase( ma, strMemberName, pi.getName(), state, Res.MSG_PROPERTY_CASE_MISMATCH, false );
}
if( pi.isStatic() && !JavaTypes.ITYPE().isAssignableFrom( rootType ) )
{
IType intrinsicType = rootExpression.getType();
if( rootExpression instanceof Identifier &&
intrinsicType.getRelativeName().equals( ((Identifier)rootExpression).getSymbol().getName() ) )
{
warn( ma, false, Res.MSG_NON_STATIC_ACCESS_WITH_IDENTIFIER_OF_STATIC_MEMBER, pi.getName(), intrinsicType.getName(), ((Identifier)rootExpression).getSymbol().getName(), intrinsicType.getName() );
}
else
{
warn( ma, false, Res.MSG_NON_STATIC_ACCESS_OF_STATIC_MEMBER, pi.getName(), intrinsicType.getName() );
}
}
}
}
}
catch( ParseException e1 )
{
// memberType = ma.getRootType();
if( rootExpression instanceof Identifier && !(rootType instanceof INamespaceType) )
{
// This is to handle yet another bad relative namespaace problem e.g., new activity.ActivityDetailHelper(Activity)
Identifier identifier = (Identifier)rootExpression;
INamespaceType namespaceType = resolveNamespace( identifier.getSymbol().getName().toLowerCase() );
if( namespaceType != null )
{
ISymbol oldSymbol = identifier.getSymbol();
Symbol sym = new Symbol( namespaceType.getRelativeName(), namespaceType, null );
identifier.setSymbol( sym, getSymbolTable() );
identifier.setType( namespaceType );
parseMemberAccess( rootExpression, kind, iTokenStart, strMemberName, state, bParseTypeLiteralOnly );
Expression namespaceExpr = peekExpression();
if( namespaceExpr.hasParseExceptions() )
{
((Identifier)rootExpression).setSymbol( oldSymbol, getSymbolTable() );
rootExpression.setType( oldSymbol.getType() );
}
else
{
return;
}
}
}
if( rootType instanceof INamespaceType )
{
verify( ma, false, Res.MSG_NO_TYPE_ON_NAMESPACE, strMemberName, rootType == null ? "<no type specified>" : rootType.getName() );
}
else
{
ma.addParseException( e1 );
}
memberType = ErrorType.getInstance();
}
ma.setType( memberType );
ma.setMemberName( strMemberName );
ma.setStartOffset( iTokenStart );
if( pi != null && pi.isDeprecated() )
{
// Add a warning if the property is deprecated
ma.addParseWarning( new ParseWarningForDeprecatedMember( state.cloneWithNewTokenStartAndTokenEnd( iTokenStart, pi.getName().length() ),
pi.getName(), ma.getRootType().getName() ) );
}
if( pi != null )
{
verify( ma, !pi.isAbstract() ||
!(rootExpression instanceof Identifier) ||
!((Identifier)rootExpression).getSymbol().getName().equals( Keyword.KW_super.toString() ),
Res.MSG_ABSTRACT_METHOD_CANNOT_BE_ACCESSED_DIRECTLY, strMemberName );
}
verifyOrWarn( ma, bExpansion || !(pi instanceof ArrayExpansionPropertyInfo), true, Res.MSG_USE_EXPANSION_OPERATOR );
pushExpression( ma );
}
private boolean shouldParseMemberInstead( String strMemberName, IType rootType, boolean bExpansion, IType memberType ) {
List<IType> contextTypes = getOwner().getContextTypes();
boolean bMetaTypeInContext = contextTypes.size() == 1 && contextTypes.get( 0 ) instanceof IMetaType;
if( !bMetaTypeInContext )
{
if( memberType != null )
{
try
{
BeanAccess.getPropertyInfo( bExpansion ? TypeLord.getExpandableComponentType( rootType ) : rootType,
strMemberName, null, this, getVisibilityConstraint() );
// The case exists where both an inner class and a member share the same simple name.
// Favor the member parse when the context type is not a MetaType
return true;
}
catch( Exception e )
{
// eat me
}
}
}
return false;
}
private IType getInnerClass( String strMemberName, IType memberType, IHasInnerClass typeLiteralType )
{
try
{
memberType = typeLiteralType.getInnerClass( strMemberName );
}
catch( IllegalStateException e1 )
{
//Ignore, JavaType.getInnerClass throws IllegalStateException if unable to get type for inner class
}
return memberType;
}
private IFunctionType maybeParameterizeOnCtxType( IFunctionType funcType )
{
if( funcType.isGenericType() )
{
funcType = funcType.inferParameterizedTypeFromArgTypesAndContextType( IType.EMPTY_ARRAY, getCurrentContextType() );
}
return funcType;
}
private List<IFunctionType> getPreliminaryFunctionTypes( String strMemberName, BeanMethodCallExpression e, IType rootType, boolean bExpansion, IType[] typeParameters )
{
// Get a preliminary funcTypes to check arguments. Note we do this to aid in in error feedback and value popup completion.
List<IFunctionType> listFunctionTypes = new ArrayList<IFunctionType>( 8 );
try
{
if( !(rootType instanceof ErrorType) )
{
getFunctionType( bExpansion ? TypeLord.getExpandableComponentType( rootType ) : rootType, strMemberName, null, listFunctionTypes, this, true );
}
}
catch( ParseException pe )
{
e.addParseException( pe );
}
if( typeParameters != null )
{
listFunctionTypes = parameterizeFunctionTypes( e, typeParameters, listFunctionTypes );
}
return listFunctionTypes;
}
private boolean isBlockInvoke( Expression rootExpression, String strMemberName )
{
try
{
IType rootType = rootExpression.getType();
// Don't look up inner classes
if( rootExpression instanceof TypeLiteral )
{
IType typeLiteralType = ((MetaType)rootType).getType();
if( typeLiteralType instanceof IHasInnerClass )
{
if( ((IHasInnerClass)typeLiteralType).getInnerClass( strMemberName ) != null )
{
return false;
}
}
}
// ignore namepspaces and other functions
if( !isErrorType(rootType) && !(rootType instanceof INamespaceType) && !(rootType instanceof IFunctionType) )
{
IPropertyInfo pi = BeanAccess.getPropertyInfo( rootExpression.getType(), strMemberName, null, this, getVisibilityConstraint() );
if( pi != null )
{
return pi.getFeatureType() instanceof IBlockType;
}
}
}
catch( ParseException e )
{
//ignore
}
return false;
}
private boolean isErrorType(IType rootType) {
return
rootType instanceof IErrorType ||
(rootType instanceof IMetaType && ((IMetaType) rootType).getType() instanceof IErrorType);
}
private ArrayList<IFunctionType> parameterizeFunctionTypes( Expression expression, IType[] typeParameters, List<IFunctionType> listFunctionTypes )
{
ArrayList<IFunctionType> parameterizedFunctionTypes = new ArrayList<IFunctionType>( 8 );
for( IFunctionType funcType : listFunctionTypes )
{
if( funcType.isGenericType() && verifyCanParameterizeType( expression, funcType, typeParameters ) )
{
IFunctionType parameterizedFuncType = (IFunctionType)funcType.getParameterizedType( typeParameters );
if( verify( expression, parameterizedFuncType != null, Res.MSG_COULD_NOT_PARAMETERIZE ) )
{
if( parameterizedFuncType != null )
{
parameterizedFunctionTypes.add( parameterizedFuncType );
}
}
}
}
return parameterizedFunctionTypes;
}
private void handleAbstractCtor( final int iTokenStart, final String strMemberName, BeanMethodCallExpression e, final IParserState state )
{
IMethodInfo methodInfo = e.getMethodDescriptor();
if (methodInfo instanceof GosuMethodInfo ) {
// DynamicFunctionSymbol functionSymbol = ((GosuMethodInfo) methodInfo).getDfs();
if (methodInfo.isAbstract()) {
//noinspection ThrowableInstanceNeverThrown
e.addParseException(new ParseException(new IParserState() {
public int getLineNumber() {
return state.getLineNumber();
}
public int getTokenColumn() {
return state.getTokenColumn();
}
public String getSource() {
return state.getSource();
}
public int getTokenStart() {
return iTokenStart;
}
public int getTokenEnd() {
return iTokenStart + strMemberName.length();
}
public int getLineOffset() {
return state.getLineOffset();
}
},
Res.MSG_NO_ABSTRACT_METHOD_CALL_IN_CONSTR,
methodInfo.getDisplayName()));
}
}
}
private boolean verifyCanParameterizeType( ParsedElement elem, IType type, IType[] typeParam )
{
if( !verify( elem, type.isGenericType(), Res.MSG_CANNOT_PARAMETERIZE_NONGENERIC ) )
{
return false;
}
IGenericTypeVariable[] typeVars = type.getGenericTypeVariables();
if( verify( elem, typeParam != null && typeParam.length == typeVars.length, Res.MSG_WRONG_NUM_OF_ARGS, "" ) )
{
assert typeParam != null;
boolean bRet = true;
for( int i = 0; i < typeVars.length; i++ )
{
IType boundingType = typeVars[i].getBoundingType();
boundingType = TypeLord.isParameterizedType( boundingType ) && TypeLord.isRecursiveType( boundingType, boundingType.getTypeParameters() )
? TypeLord.getDefaultParameterizedType( boundingType )
: getActualBoundingType( typeVars, typeParam, boundingType );
bRet = bRet &&
verify( elem,
// Hack to support recursive types
isTypeParamHeaderCompiling( typeParam[i] ) ||
isErrorType( typeParam[i] ) ||
(typeVars[i].getTypeVariableDefinition() != null && typeVars[i].getTypeVariableDefinition().getType().isAssignableFrom( typeParam[i] )) ||
boundingType.isAssignableFrom( typeParam[i] ),
Res.MSG_TYPE_PARAM_NOT_ASSIGNABLE_TO,
typeParam[i], boundingType );
}
return bRet;
}
return false;
}
private IType getActualBoundingType( IGenericTypeVariable[] typeVars, IType[] typeParams, IType boundingType ) {
TypeVarToTypeMap typeVarToTypeMap = new TypeVarToTypeMap();
for( int i = 0; i < typeVars.length; i++ ) {
IGenericTypeVariable gv = typeVars[i];
if( gv.getTypeVariableDefinition() != null ) {
typeVarToTypeMap.put( gv.getTypeVariableDefinition().getType(), typeParams.length > i ? typeParams[i] : gv.getTypeVariableDefinition().getType() );
}
}
return TypeLord.getActualType( boundingType, typeVarToTypeMap, true );
}
private boolean isTypeParamHeaderCompiling( IType typeParam )
{
return (typeParam instanceof IGosuClass && ((IGosuClass)typeParam).isCompilingHeader()) ||
(typeParam instanceof TypeVariableType && isTypeParamHeaderCompiling( ((TypeVariableType)typeParam).getBoundingType() )) ||
(typeParam instanceof TypeVariableType && ((TypeVariableType)typeParam).getBoundingType() == PENDING_BOUNDING_TYPE);
}
private IType[] parseFunctionParameterization( Expression e )
{
int iOffset = _tokenizer.getTokenStart();
int iLineNum = _tokenizer.getLineNumber();
int iColumn = getTokenizer().getTokenColumn();
if( match( null, "<", SourceCodeTokenizer.TT_OPERATOR ) )
{
List<TypeLiteral> paramTypes = parseTypeParameters( null );
verify( e, match( null, ">", SourceCodeTokenizer.TT_OPERATOR ), Res.MSG_EXPECTING_CLOSING_ANGLE_BRACKET_FOR_TYPE );
if( paramTypes.isEmpty() )
{
return null;
}
makeTypeParameterListClause( iOffset, iLineNum, iColumn, paramTypes );
IType[] types = new IType[paramTypes.size()];
for( int i = 0; i < paramTypes.size(); i++ )
{
TypeLiteral typeLiteral = paramTypes.get( i );
types[i] = (typeLiteral.getType()).getType();
}
return types;
}
return null;
}
private MethodScore parseArgumentList(ParsedElement element, List<? extends IInvocableType> listFunctionTypes,
IType[] typeParams, boolean bVerifyArgs, boolean bNoArgsProvided,
JavaAnnotationParsingState valueAnnotationParsing)
{
int iArgs = 0;
List<Expression> argExpressions = new ArrayList<Expression>( 4 );
List<LightweightParserState> parserStates = new ArrayList<LightweightParserState>( 4 );
List<List<IType>> contextTypesList = extractContextTypes( listFunctionTypes );
int iOffset = _tokenizer.getTokenStart();
int iLineNum = _tokenizer.getLineNumber();
int iColumn = _tokenizer.getTokenColumn();
TypeVarToTypeMap inferenceMap = new TypeVarToTypeMap();
List<Integer> namedArgOrder = null;
Set<String> namedArgs = new HashSet<String>();
if( !bNoArgsProvided )
{
do
{
parserStates.add( makeLightweightParserState() );
int iArgPos = parseArgExpression( listFunctionTypes, iArgs, argExpressions, contextTypesList, inferenceMap, parserStates, valueAnnotationParsing, namedArgs );
namedArgOrder = assignArgExprPosition( listFunctionTypes, iArgs, namedArgOrder, iArgPos );
iArgs++;
}
while( match( null, ',' ) );
}
// Extend the args list with default (or empty) values
addMisingArgsWithDefaultValues( element, listFunctionTypes, argExpressions, parserStates );
if( iArgs > 0 )
{
// Maybe reassign offset for ArgumentListClause...
for( Expression argExpr: argExpressions ) {
if( argExpr.getLocation() == null )
{
continue;
}
int iExpressionOffset = argExpr.getLocation().getOffset();
if( iExpressionOffset < iOffset )
{
// Can happen if first arg is a NotAWordExpression in which case the expr's length is 0 and its offset is the
// previous token's ending offset, which will likely be less than the first token in the arg position's offset.
iOffset = iExpressionOffset;
iLineNum = argExpr.getLocation().getLineNum();
iColumn = argExpr.getLocation().getColumn();
}
}
ArgumentListClause e = new ArgumentListClause();
pushExpression( e );
setLocation( iOffset, iLineNum, iColumn, true );
popExpression();
}
if( !bVerifyArgs )
{
MethodScore score = new MethodScore();
score.setValid( false );
//noinspection unchecked
score.setArguments( (List)argExpressions );
score.setNamedArgOrder( namedArgOrder );
return score;
}
else if( isDynamicMethod( listFunctionTypes ) )
{
return makeDynamicMethodScore( listFunctionTypes, argExpressions );
}
// if there were type parameters, remove any non-parameterized functions
if( typeParams != null && typeParams.length > 0 )
{
ArrayList<IInvocableType> genericFunctions = new ArrayList<IInvocableType>();
for( IInvocableType type : listFunctionTypes )
{
if( type.isGenericType() )
{
genericFunctions.add( type );
}
}
if( !genericFunctions.isEmpty() )
{
listFunctionTypes = genericFunctions;
}
}
// score 'em
List<MethodScore> scoredMethods = scoreMethods( listFunctionTypes, argExpressions );
boolean isAmbiguousMethodCall = false;
// warn on ambiguity
if( scoredMethods.size() > 1 &&
scoredMethods.get( 0 ).getScore() == scoredMethods.get( 1 ).getScore() &&
scoredMethods.get( 0 ).matchesArgSize( argExpressions ) &&
!FeatureManager.areInvocableTypesAssignable( scoredMethods.get(0).getRawFunctionType(), scoredMethods.get(1).getRawFunctionType() ) )
{
isAmbiguousMethodCall = true;
verify( element, false, Res.MSG_AMBIGUOUS_METHOD_INVOCATION);
}
if( scoredMethods.size() > 0 )
{
MethodScore score = scoredMethods.get( 0 );
// infer the function type
IInvocableType rawFunctionType = score.getRawFunctionType();
IInvocableType inferredFunctionType = inferFunctionType( rawFunctionType, argExpressions );
// reverify args
verifyArgTypes( inferredFunctionType.getParameterTypes(), argExpressions, parserStates, isAmbiguousMethodCall );
if( score.isValid() )
{
// if the score is valid, bound infered variables to avoid them getting out as raw type variables
inferredFunctionType = maybeBoundFunctionTypeVars( inferredFunctionType );
// wrap up any arguments that need coercions
handleImplicitCoercionsInArgs( inferredFunctionType.getParameterTypes(),
rawFunctionType.getParameterTypes(),
argExpressions );
}
//noinspection unchecked
score.setArguments( (List)argExpressions );
score.setInferredFunctionType( inferredFunctionType );
score.setNamedArgOrder( namedArgOrder );
return score;
}
else
{
MethodScore score = new MethodScore();
score.setValid( false );
//noinspection unchecked
score.setArguments( (List)argExpressions );
return score;
}
}
private IInvocableType maybeBoundFunctionTypeVars( IInvocableType inferredFunctionType )
{
List<IType> types = new ArrayList<IType>();
for( IType typeVarType : getCurrentlyInferringTypes() )
{
IType encType = TypeLord.getPureGenericType( typeVarType.getEnclosingType() );
if( encType != null && TypeLord.getPureGenericType( inferredFunctionType ).isAssignableFrom( typeVarType.getEnclosingType() ) )
{
types.add( typeVarType );
}
}
return (IInvocableType) TypeLord.boundTypes( inferredFunctionType, types );
}
private List<Integer> assignArgExprPosition( List<? extends IInvocableType> listFunctionTypes, int iArgs, List<Integer> namedArgOrder, int iArgPos )
{
if( (namedArgOrder != null || iArgPos != iArgs) && listFunctionTypes.size() > 0 )
{
if( namedArgOrder == null )
{
int iSize = listFunctionTypes.get( 0 ).getParameterTypes().length;
namedArgOrder = new ArrayList<Integer>( iSize );
for( int i = 0; i < iSize; i++ )
{
namedArgOrder.add( i );
}
}
namedArgOrder.remove( (Integer)iArgPos );
if( namedArgOrder.size() >= iArgs ) {
namedArgOrder.add( iArgs, iArgPos );
}
else {
namedArgOrder.add( iArgPos );
}
}
return namedArgOrder;
}
private void addMisingArgsWithDefaultValues( ParsedElement element, List<? extends IInvocableType> listFunctionTypes,
List<Expression> argExpressions, List<LightweightParserState> parserStates )
{
if( listFunctionTypes.size() == 1 )
{
IInvocableType funcType = listFunctionTypes.get( 0 );
if( funcType.hasOptionalParams() )
{
for( int i = argExpressions.size(); i < funcType.getParameterTypes().length; i++ )
{
parserStates.add( makeLightweightParserState() );
argExpressions.add( i, getDefaultValueOrPlaceHolderForParam( i, funcType ) );
}
for( Expression argExpr : argExpressions )
{
if( !verify( element, argExpr != DefaultParamValueLiteral.instance(), Res.MSG_MISSING_REQUIRED_ARGUMENTS ) )
{
break;
}
}
}
}
}
private int parseArgExpression(List<? extends IInvocableType> listFunctionTypes,
int iArgs,
List<Expression> argExpressions,
List<List<IType>> contextTypesList,
TypeVarToTypeMap inferenceMap,
List<LightweightParserState> parserStates,
JavaAnnotationParsingState javaAnnotationParsingState,
Set<String> namedArgs )
{
List<IType> allCtxTypes;
List<IType> rawContextTypes;
List<IType> boundContextTypes;
int iArgPos = iArgs;
boolean isAlreadyDef = false;
if( match( null, ":", ISourceCodeTokenizer.TT_OPERATOR, true ) )
{
// Since named params can only be applied to non-overloaded methods, we can assume only one function
IInvocableType funcType = listFunctionTypes.size() > 0 ? listFunctionTypes.get( 0 ) : null;
iArgPos = parseNamedParamExpression( listFunctionTypes.size() > 0 ? funcType : null );
if( iArgPos == -1)
{
namedArgs.add( "err" );
}
else if ( funcType != null )
{
String[] parameterNames = funcType.getParameterNames();
isAlreadyDef = namedArgs.add( parameterNames[iArgPos] );
}
allCtxTypes = iArgPos < contextTypesList.size() && iArgPos >= 0 ? contextTypesList.get( iArgPos ) : null;
rawContextTypes = inferContextTypes( inferenceMap, allCtxTypes );
boundContextTypes = boundCtxTypes( rawContextTypes );
if( argExpressions.size() < iArgPos+1 )
{
// Extend the args list with default (or empty) values up to, but not including, the newly parsed arg
for( int i = argExpressions.size(); i < iArgPos; i++ )
{
argExpressions.add( i, getDefaultValueOrPlaceHolderForParam( i, funcType ) );
parserStates.add( i, makeLightweightParserState() );
}
assert argExpressions.size() == iArgPos;
}
}
else
{
allCtxTypes = iArgPos < contextTypesList.size() ? contextTypesList.get( iArgPos ) : null;
rawContextTypes = inferContextTypes( inferenceMap, allCtxTypes );
boundContextTypes = boundCtxTypes( rawContextTypes );
// Add the component type as a context type, to help w/ type inference for enums
if( javaAnnotationParsingState == JavaAnnotationParsingState.PARSING_JAVA_VALUE_ENUM ) {
if (boundContextTypes.size() == 1) {
IType iType = boundContextTypes.get(0);
if (iType.isArray()) {
boundContextTypes.add(iType.getComponentType());
}
}
}
parseExpression( boundContextTypes );
// Do not support non java value annotation w/o named args
if( javaAnnotationParsingState == JavaAnnotationParsingState.PARSING_JAVA_NON_VALUE_ENUM )
{
verify(peekExpression(), false, Res.MSG_ANY, "Named arguments are required for this style of java annotation");
}
if( !namedArgs.isEmpty() )
{
addError( peekExpression(), Res.MSG_EXPECTING_NAMED_ARG );
}
}
Expression expression = popExpression();
// Crazy java value enum array wrapping
if( javaAnnotationParsingState == JavaAnnotationParsingState.PARSING_JAVA_VALUE_ENUM )
{
if (boundContextTypes.size() == 2) {
IType iType = boundContextTypes.get(0);
if( iType.isArray() )
{
if( expression.getType().equals( iType.getComponentType() ) )
{
NewExpression newExpression = new NewExpression();
newExpression.setType(iType);
newExpression.setValueExpressions(Arrays.asList(expression));
findAndWrapLocation(expression, newExpression);
expression = newExpression;
}
}
}
}
inferFunctionTypeVariables( rawContextTypes, boundContextTypes, expression.getType(), inferenceMap );
iArgPos = iArgPos < 0 ? iArgs : iArgPos;
if( iArgPos >= 0 )
{
if( iArgPos == argExpressions.size() )
{
argExpressions.add( iArgPos, expression );
}
else if (iArgPos >=0 && iArgPos < argExpressions.size() && isAlreadyDef )
{
Expression existingExpr = argExpressions.set( iArgPos, expression );
verify( expression,
existingExpr == DefaultParamValueLiteral.instance() ||
existingExpr instanceof DefaultArgLiteral ||
existingExpr instanceof NullExpression,
Res.MSG_ARGUMENT_ALREADY_DEFINED );
}
}
return iArgPos;
}
private Expression getDefaultValueOrPlaceHolderForParam( int iParam, IInvocableType invType )
{
if( invType == null )
{
return DefaultParamValueLiteral.instance();
}
IExpression[] defValues = invType.getDefaultValueExpressions();
if( defValues == null || defValues.length == 0 )
{
return DefaultParamValueLiteral.instance();
}
IExpression defValue = defValues[iParam];
if( defValue != null )
{
return new DefaultArgLiteral( invType.getParameterTypes()[iParam], defValue );
}
return DefaultParamValueLiteral.instance();
}
private int parseNamedParamExpression( IInvocableType invType )
{
match( null, ":", SourceCodeTokenizer.TT_OPERATOR );
parseNamedParamIdentifier();
Identifier identifier = (Identifier)popExpression();
int[] iPos = {-1};
IType type = getParamTypeFromParamName( invType, identifier.getSymbol().getName(), iPos );
identifier.setType( type );
verify( identifier, !(type instanceof ErrorType), Res.MSG_PARAM_NOT_FOUND );
verify( identifier, match( null, "=", SourceCodeTokenizer.TT_OPERATOR ), Res.MSG_EXPECTING_EQUALS_ASSIGN );
parseExpression( type );
Expression value = popExpression();
if( value.hasParseExceptions() )
{
value.getParseExceptions().get( 0 ).setExpectedType( type );
}
pushExpression( value );
return iPos[0];
}
private IType getParamTypeFromParamName( IInvocableType invType, String strParam, int[] iPos )
{
if( invType == null )
{
return ErrorType.getInstance();
}
String[] names = invType.getParameterNames();
for( int i = 0; i < names.length; i++ )
{
if( names[i].equals( strParam ) )
{
iPos[0] = i;
return invType.getParameterTypes()[i];
}
}
return ErrorType.getInstance();
}
private void parseNamedParamIdentifier()
{
int iOffset = _tokenizer.getTokenStart();
int iLineNum = _tokenizer.getLineNumber();
int iColumn = _tokenizer.getTokenColumn();
Token t = new Token();
Identifier expr = new Identifier();
verify( expr, match( t, SourceCodeTokenizer.TT_WORD ) ||
match( t, SourceCodeTokenizer.TT_KEYWORD ), Res.MSG_EXPECTING_NAME_PARAM );
expr.setSymbol( new TypedSymbol( t._strValue, null, null, null, SymbolType.NAMED_PARAMETER ), null );
pushExpression( expr );
setLocation( iOffset, iLineNum, iColumn );
}
private boolean isDynamicMethod( List<? extends IInvocableType> listFunctionTypes )
{
if( listFunctionTypes == null || listFunctionTypes.isEmpty() )
{
return false;
}
IInvocableType invoType = listFunctionTypes.get( 0 );
if( invoType instanceof FunctionType )
{
IMethodInfo mi = ((FunctionType)invoType).getMethodInfo();
if( mi != null && mi.getOwnersType() instanceof IPlaceholder )
{
return true;
}
}
return false;
}
private MethodScore makeDynamicMethodScore( List<? extends IInvocableType> listFunctionTypes, List<Expression> argExpressions )
{
MethodScore score = new MethodScore();
score.setValid( true );
//noinspection unchecked
score.setArguments( (List)argExpressions );
IMethodInfo mi = ((FunctionType)listFunctionTypes.get( 0 )).getMethodInfo();
mi = ((ITypeInfo)mi.getContainer()).getMethod( mi.getName(), getTypes( argExpressions ).toArray( new IType[argExpressions.size()] ) );
score.setInferredFunctionType( new FunctionType( mi ) );
score.setRawFunctionType( score.getInferredFunctionType() );
score.setScore( 1 );
return score;
}
private List<MethodScore> scoreMethods( List<? extends IInvocableType> listFunctionTypes, List<Expression> argExpressions ) {
List<IType> argTypes = new ArrayList<IType>(argExpressions.size());
for(Expression argExpression : argExpressions) {
argTypes.add(argExpression.getType());
}
return ITypeInfo.FIND.scoreMethods( listFunctionTypes, argTypes, getCurrentlyInferringTypes() );
}
private List<IType> inferContextTypes( TypeVarToTypeMap inferenceMap, List<IType> ctxArgs )
{
if( ctxArgs != null )
{
ArrayList<IType> returnTypes = new ArrayList<IType>( );
for( IType ctxArg : ctxArgs )
{
returnTypes.add( inferArgTypes( ctxArg, inferenceMap ) );
}
return returnTypes;
}
else
{
return Collections.emptyList();
}
}
private List<IType> boundCtxTypes( List<IType> allCtxTypes )
{
ArrayList<IType> returnList = new ArrayList<IType>();
List<IType> inferringTypes = getCurrentlyInferringTypes();
for( IType allCtxType : allCtxTypes )
{
returnList.add( TypeLord.boundTypes( allCtxType, inferringTypes ) );
}
return returnList;
}
private String makeTypesStringForAlternativeMethods( List<MethodScore> scores )
{
String str = "";
long bestScore = scores.get( 0 ).getScore();
for( int i = 1; i < scores.size(); i++ )
{
MethodScore score = scores.get( i );
if( score.getScore() != bestScore )
{
break;
}
if( i != 1 )
{
str += "\n ";
}
str += makeTypesStringForMethodScore( score );
}
return str;
}
private String makeTypesStringForMethodScore( MethodScore bestScore )
{
IType[] types = bestScore.getRawFunctionType().getParameterTypes();
String str = "(";
for( int j = 0; j < types.length; j++ )
{
if( j != 0 )
{
str += ", ";
}
str += types[j].getRelativeName();
}
return str + ")";
}
private void inferFunctionTypeVariables( List<IType> rawContextTypes, List<IType> boundContextTypes, IType expressionType, TypeVarToTypeMap inferenceMap )
{
// we only infer if there is a single unambiguous type
if( rawContextTypes.size() == 1 )
{
IType rawContextType = rawContextTypes.get( 0 );
IType boundContextType = boundContextTypes.get( 0 );
if( rawContextType != null && boundContextType != null )
{
ICoercer iCoercer = CommonServices.getCoercionManager().resolveCoercerStatically( boundContextType, expressionType );
if( iCoercer instanceof IResolvingCoercer )
{
IType resolvedType = ((IResolvingCoercer)iCoercer).resolveType( rawContextType, expressionType );
TypeLord.inferTypeVariableTypesFromGenParamTypeAndConcreteType( rawContextType, resolvedType, inferenceMap );
}
else
{
TypeLord.inferTypeVariableTypesFromGenParamTypeAndConcreteType( rawContextType, expressionType, inferenceMap );
}
}
}
}
private IType inferArgTypes( IType contextType, TypeVarToTypeMap inferenceMap )
{
TypeLord.addReferencedTypeVarsThatAreNotInMap( contextType, inferenceMap, true );
return TypeLord.getActualType( contextType, inferenceMap, true );
}
private void handleImplicitCoercionsInArgs( IType[] argTypes, IType[] rawArgTypes, List<Expression> args )
{
for( int i = 0; i < argTypes.length && i < args.size(); i++ )
{
IType argType = argTypes[i];
Expression expr = args.get( i );
if( argType != rawArgTypes[i] &&
(argType instanceof IGosuArrayClass || argType instanceof TypeVariableArrayType) &&
!(rawArgTypes[i] instanceof IGosuArrayClass) )
{
// Special case for GosuArray -> JavaArray since a generic function can be inferred via structural equivalence
// e.g., component types are assignable, but different array types on a component type may not be.
argType = rawArgTypes[i];
}
if( !(expr instanceof DefaultParamValueLiteral) )
{
args.set( i, possiblyWrapWithImplicitCoercion( expr, argType ) );
}
}
}
// returns a list of lists of unique types at each argument position,
private List<List<IType>> extractContextTypes( List<? extends IInvocableType> funcTypes )
{
if( funcTypes != null )
{
ArrayList<List<IType>> returnList = new ArrayList<List<IType>>();
for( IInvocableType funcType : funcTypes )
{
for( int i = 0; i < funcType.getParameterTypes().length; i++ )
{
IType paramType = funcType.getParameterTypes()[i];
if( i >= returnList.size() )
{
returnList.add( new ArrayList<IType>() );
}
List<IType> paramTypeList = returnList.get( i );
if( !paramTypeList.contains( paramType ) )
{
paramTypeList.add( paramType );
}
}
}
return returnList;
}
else
{
return Collections.emptyList();
}
}
private void verifyArgTypes( IType[] argTypes, List<Expression> argExpressions, List<LightweightParserState> parserStates, boolean isAmbiguousMethodCall )
{
if( argTypes == null || argTypes.length == 0 || argExpressions == null || argExpressions.size() == 0 )
{
return;
}
for( int i = 0; i < argTypes.length && i < argExpressions.size(); i++ )
{
Expression e = argExpressions.get( i );
LightweightParserState state = parserStates.get( i );
if( isAmbiguousMethodCall && argTypes[i] != e.getType() )
{
e.addParseException( new ImplicitCoercionError( state, Res.MSG_IMPLICIT_COERCION_ERROR, argTypes[i],
e.getType().getDisplayName(), argTypes[i].getDisplayName() ) );
}
else
{
// Adds any parse exceptions that may have been cleared during method scoring
verifyComparable( argTypes[i], e, false, true, state );
}
//Add a warning if a closure with a void return type is passed to a method expecting
//a non-void return value
if( argTypes[i] instanceof FunctionType && e.getType() instanceof FunctionType )
{
FunctionType expectedFunType = (FunctionType)argTypes[i];
FunctionType foundFunType = (FunctionType)e.getType();
if( expectedFunType.getReturnType() != GosuParserTypes.NULL_TYPE() && foundFunType.getReturnType() == GosuParserTypes.NULL_TYPE() )
{
warn( e, false, Res.MSG_VOID_RETURN_IN_CTX_EXPECTING_VALUE );
}
}
if( e.hasParseExceptions() )
{
IParseIssue pe = e.getParseExceptions().get( 0 );
if( pe.getExpectedType() == null )
{
pe.setExpectedType( argTypes[i] );
}
}
}
}
//------------------------------------------------------------------------------
// literal
// number
// string
// <type-literal>
//
void parseLiteral()
{
int iOffset = _tokenizer.getTokenStart();
int iLineNum = _tokenizer.getLineNumber();
int iColumn = getTokenizer().getTokenColumn();
_parseLiteral();
setLocation( iOffset, iLineNum, iColumn );
}
void _parseLiteral()
{
if( !parseNumberLiteral() &&
!parseRelativeFeatureLiteral() &&
!parseStringLiteral() &&
!parseCharLiteral() &&
!parseBooleanLiteral() &&
!parseNullLiteral() &&
!parseTypeLiteral() )
{
Expression expr = popExpression();
getLocationsList().remove( expr.getLocation() );
NotAWordExpression notAWord = new NotAWordExpression();
pushExpression( notAWord );
verify( notAWord, false, Res.MSG_SYNTAX_ERROR );
IToken T = getTokenizer().getPriorToken();
setLocation( T.getTokenEnd(), T.getLine(), T.getTokenColumn(), true );
}
}
private boolean parseRelativeFeatureLiteral()
{
if( getGosuClass() != null &&
match( null, "#", SourceCodeTokenizer.TT_OPERATOR, true ) )
{
Expression root = new TypeLiteral( getGosuClass() );
pushExpression( root );
if( parseFeatureLiteral( root ) )
{
return true;
}
popExpression();
}
return false;
}
private boolean parseNumberLiteral()
{
return parseNumberLiteral(false);
}
private boolean atNumberLiteralStart()
{
return match( null, null, SourceCodeTokenizer.TT_INTEGER, true ) || match( null, null, '.', true );
}
private boolean parseNumberLiteral(boolean negated)
{
if( match( null, Keyword.KW_NaN ) )
{
pushExpression( NumericLiteral.NaN.get().copy() );
return true;
}
else if( match( null, Keyword.KW_Infinity ) )
{
pushExpression( NumericLiteral.INFINITY.get().copy() );
return true;
}
int mark = getTokenizer().mark();
Token T = new Token();
if( match( T, SourceCodeTokenizer.TT_INTEGER ) )
{
String strValue = (negated ? "-" : "") + T._strValue;
if( getNumericTypeFrom( strValue ) == null && match( null, '.' ) )
{
strValue = maybeStripTypeModifier( strValue, null );
Token tmp = new Token();
if( match( tmp, null, SourceCodeTokenizer.TT_INTEGER, true ) )
{
if( !isPrefixNumericLiteral(tmp._strValue) )
{
match( T, SourceCodeTokenizer.TT_INTEGER );
strValue += '.';
strValue += T._strValue;
}
else
{
strValue += ".0";
}
}
else
{
match( T, SourceCodeTokenizer.TT_INTEGER );
strValue += ".0";
}
}
int lastPos = T.getTokenEnd();
if( match( T, null, SourceCodeTokenizer.TT_WORD, true ) )
{
if( (lastPos >= T.getTokenStart()) && (T._strValue.charAt(0) == 'e' ||
T._strValue.charAt(0) == 'E' ) )
{
match( T, SourceCodeTokenizer.TT_WORD );
if( T._strValue.length() == 1 )
{
strValue += "e";
if( match( T, "+", SourceCodeTokenizer.TT_OPERATOR ) ||
match( T, "-", SourceCodeTokenizer.TT_OPERATOR ) )
{
strValue += T._strValue;
}
if( match( T, SourceCodeTokenizer.TT_INTEGER ) )
{
strValue += T._strValue;
}
else
{
getTokenizer().restoreToMark( mark );
return false;
}
}
else
{
strValue += T._strValue;
}
int end = strValue.length() - 1;
char suffix = ' ';
if ( !Character.isDigit(strValue.charAt(end)) )
{
suffix = strValue.charAt(end);
strValue = strValue.substring(0, end);
}
try
{
BigDecimal bigNum = new BigDecimal( strValue );
strValue = bigNum.toPlainString();
if (bigNum.scale() <= 0)
{
strValue += ".0";
}
}
catch( NumberFormatException e )
{
getTokenizer().restoreToMark( mark );
return false;
}
if( suffix != ' ')
{
strValue += suffix;
}
}
}
parseNumericValue( strValue );
return true;
}
else if( match( null, null, '.' ) )
{
String strValue = (negated ? "-" : "") + ".";
if( match( T, SourceCodeTokenizer.TT_INTEGER ) )
{
strValue += T._strValue;
parseNumericValue( strValue );
}
else
{
pushErrorNumberLiteral( Res.MSG_EXPECTING_NUMBER_TO_FOLLOW_DECIMAL );
}
return true;
}
return false;
}
private void parseNumericValue( String strValue )
{
if( isPrefixNumericLiteral( strValue ) && strValue.indexOf( '.' ) != -1 )
{
pushErrorNumberLiteral( Res.MSG_IMPROPER_VALUE_FOR_NUMERIC_TYPE, strValue, JavaTypes.pINT() );
}
else
{
IType numericTypeFrom = getNumericTypeFrom( strValue );
if( numericTypeFrom != null )
{
parseExplicitlyTypedNumericLiteral( strValue, numericTypeFrom );
}
else
{
IType ctxType = getNumberContextType( _inferredContextStack.isEmpty() ? null : _inferredContextStack.peek() );
NumericLiteral e;
if( strValue.indexOf( '.' ) != -1 )
{
if( ctxType == JavaTypes.BIG_DECIMAL() )
{
e = new NumericLiteral( strValue, new BigDecimal( strValue ), JavaTypes.BIG_DECIMAL());
}
else
{
e = parseDoubleOrBigDec( strValue );
}
}
else
{
if( ctxType == JavaTypes.BIG_INTEGER() )
{
if( isPrefixNumericLiteral(strValue))
{
strValue = stripPrefix( strValue );
}
e = new NumericLiteral( strValue, new BigInteger( strValue ), JavaTypes.BIG_INTEGER());
}
else
{
try
{
e = parseIntOrLongOrBigInt( strValue );
}
catch( NumberFormatException ex )
{
pushErrorNumberLiteral(Res.MSG_IMPROPER_VALUE_FOR_NUMERIC_TYPE, strValue, JavaTypes.pINT());
return;
}
}
}
pushExpression( e );
}
}
}
private NumericLiteral parseIntOrLongOrBigInt( String strValue )
{
NumericLiteral e;
int radix = 10;
String strippedValue = strValue;
if( isPrefixNumericLiteral( strValue ) )
{
strippedValue = stripPrefix( strValue );
if( isHexLiteral( strValue ) )
{
radix = 16;
}
else if ( isBinLiteral( strValue ) )
{
radix = 2;
}
}
try
{
e = new NumericLiteral( strValue, Integer.parseInt( strippedValue, radix), JavaTypes.pINT() );
}
catch( NumberFormatException nfe )
{
try
{
e = new NumericLiteral( strValue, Long.parseLong( strippedValue, radix ), JavaTypes.pLONG() );
}
catch( NumberFormatException nfe2 )
{
e = new NumericLiteral( strValue, new BigInteger( strippedValue, radix ), JavaTypes.BIG_INTEGER() );
}
}
return e;
}
private String stripPrefix( String strValue ) {
String strippedValue;
if( strValue.startsWith( "-" ) )
{
strippedValue = "-" + strValue.substring( 3 );
}
else
{
strippedValue = strValue.substring( 2 );
}
return strippedValue;
}
private NumericLiteral parseDoubleOrBigDec( String strValue )
{
double dValue = Double.parseDouble( strValue );
if( dValue == Double.POSITIVE_INFINITY || dValue == Double.NEGATIVE_INFINITY )
{
return new NumericLiteral( strValue, new BigDecimal( strValue ), JavaTypes.BIG_DECIMAL() );
}
else
{
return new NumericLiteral( strValue, dValue, JavaTypes.pDOUBLE() );
}
}
private void parseExplicitlyTypedNumericLiteral( String strValue, IType numericTypeFrom )
{
if( isPrefixNumericLiteral( strValue ) )
{
parsePrefixNumericLiteral( strValue, numericTypeFrom );
}
else
{
parsePostfixNumericLiteral( strValue, numericTypeFrom, 10 );
}
}
private boolean isPrefixNumericLiteral( String strValue )
{
return !(strValue.equalsIgnoreCase( "0b" ) || strValue.equalsIgnoreCase( "0bi" )|| strValue.equalsIgnoreCase( "0bd" ) ||
strValue.equalsIgnoreCase( "-0b" ) || strValue.equalsIgnoreCase( "-0bi" )|| strValue.equalsIgnoreCase( "-0bd" )) &&
(strValue.startsWith( "0x" ) || strValue.startsWith( "0X" ) ||
strValue.startsWith( "0b" ) || strValue.startsWith( "0B" ) ||
strValue.startsWith( "-0x" ) || strValue.startsWith( "-0X" ) ||
strValue.startsWith( "-0b" ) || strValue.startsWith( "-0B" ));
}
private void parsePrefixNumericLiteral( String strValue, IType numericTypeFrom )
{
int radix = 10;
String strippedValue = stripPrefix( strValue );
if( isHexLiteral( strValue ) )
{
radix = 16;
}
else if ( isBinLiteral( strValue ) )
{
radix = 2;
}
parsePostfixNumericLiteral( strippedValue, numericTypeFrom, radix );
}
private boolean isHexLiteral(String num) {
num = num.toLowerCase();
return num.startsWith( "0x" ) ||
num.startsWith( "-0x" );
}
private boolean isBinLiteral(String num) {
num = num.toLowerCase();
boolean hasPrefix = num.startsWith("0b") ||
num.startsWith("-0b");
int b = num.indexOf("b") + 1;
boolean hasDigit = ( b < num.length() ) && Character.isDigit( num.charAt( b ) );
return hasPrefix && hasDigit;
}
private void parsePostfixNumericLiteral( String num, IType numericTypeFrom, int radix )
{
String strValue = maybeStripTypeModifier( num, numericTypeFrom );
try
{
NumericLiteral e;
if( JavaTypes.pBYTE().equals( numericTypeFrom ) )
{
e = new NumericLiteral( strValue, Byte.parseByte( strValue, radix ), JavaTypes.pBYTE() );
}
else if( JavaTypes.pSHORT().equals( numericTypeFrom ) )
{
e = new NumericLiteral( strValue, Short.parseShort( strValue, radix ), JavaTypes.pSHORT() );
}
else if( JavaTypes.pINT().equals( numericTypeFrom ) )
{
e = new NumericLiteral( strValue, Integer.parseInt( strValue, radix ), JavaTypes.pINT() );
}
else if( JavaTypes.pLONG().equals( numericTypeFrom ) )
{
e = new NumericLiteral( strValue, Long.parseLong( strValue, radix ), JavaTypes.pLONG() );
}
else if( JavaTypes.pFLOAT().equals( numericTypeFrom ) )
{
e = new NumericLiteral( strValue, Float.parseFloat( strValue ), JavaTypes.pFLOAT() );
}
else if( JavaTypes.pDOUBLE().equals( numericTypeFrom ) )
{
e = new NumericLiteral( strValue, Double.parseDouble( strValue ), JavaTypes.pDOUBLE() );
}
else if( JavaTypes.BIG_INTEGER().equals( numericTypeFrom ) )
{
e = new NumericLiteral( strValue, new BigInteger( strValue , radix), JavaTypes.BIG_INTEGER() );
}
else if( JavaTypes.BIG_DECIMAL().equals( numericTypeFrom ) )
{
e = new NumericLiteral( strValue, new BigDecimal( strValue ), JavaTypes.BIG_DECIMAL() );
}
else
{
throw new IllegalStateException( "Do not know how to parse a numeric type of value " + numericTypeFrom );
}
if( hasTypeModifier( num ) )
{
e.setExplicitlyTyped( true );
}
pushExpression( e );
}
catch( NumberFormatException ex )
{
pushErrorNumberLiteral( Res.MSG_IMPROPER_VALUE_FOR_NUMERIC_TYPE, strValue, numericTypeFrom.getName() );
}
}
private String maybeStripTypeModifier( String strValue, IType numericTypeFrom )
{
if( hasTypeModifier( strValue ) )
{
int modifierLen = JavaTypes.BIG_DECIMAL().equals( numericTypeFrom ) || JavaTypes.BIG_INTEGER().equals( numericTypeFrom ) ? 2 : 1;
strValue = strValue.substring( 0, strValue.length() - modifierLen );
}
return strValue;
}
private boolean hasTypeModifier( String strValue )
{
boolean hex = isHexLiteral( strValue );
char ch = strValue.toLowerCase().charAt(strValue.length() - 1);
if( hex && ( ch == 's' ) || ( ch == 'l' ) )
{
return true;
}
else if ( !hex && !Character.isDigit(ch) )
{
return true;
}
return false;
}
private void pushErrorNumberLiteral( ResourceKey key, Object... args )
{
NumericLiteral error = new NumericLiteral( "0", 0, JavaTypes.pINT() );
addError( error, key, args );
pushExpression( error );
}
private IType getNumericTypeFrom( String strValue )
{
boolean hex = isHexLiteral( strValue );
boolean bin = isBinLiteral( strValue );
if( !hex && (strValue.endsWith( "b" ) || strValue.endsWith( "B" )) )
{
return JavaTypes.pBYTE();
}
else if( strValue.endsWith( "s" ) || strValue.endsWith( "S" ) )
{
return JavaTypes.pSHORT();
}
else if( strValue.endsWith( "l" ) || strValue.endsWith( "L" ) )
{
return JavaTypes.pLONG();
}
else if( !hex && !bin && (strValue.endsWith( "f" ) || strValue.endsWith( "F" )) )
{
return JavaTypes.pFLOAT();
}
else if( !hex && (strValue.endsWith( "bi" ) || strValue.endsWith( "BI" )) )
{
return JavaTypes.BIG_INTEGER();
}
else if( !hex && !bin && (strValue.endsWith( "bd" ) || strValue.endsWith( "BD" )) )
{
return JavaTypes.BIG_DECIMAL();
}
else if( !hex && !bin && (strValue.endsWith( "d" ) || strValue.endsWith( "D" )) )
{
return JavaTypes.pDOUBLE();
}
else
{
return null;
}
}
private boolean parseCharLiteral()
{
Token T = new Token();
if( match( T, (int)'\'' ) )
{
if( T._strValue.length() != 1 )
{
_parseStringLiteral( T._bUnterminated, T );
}
else
{
char c = T._strValue.charAt( 0 );
Expression e = new CharLiteral( c );
verify( e, T.getInvalidCharPos() < 0, Res.MSG_INVALID_CHAR_AT, T.getInvalidCharPos() );
verify( e, !T._bUnterminated, Res.MSG_UNTERMINATED_STRING_LITERAL );
pushExpression( e );
}
return true;
}
return false;
}
private boolean parseStringLiteralSeparately()
{
int iOffset = _tokenizer.getTokenStart();
int iLineNum = _tokenizer.getLineNumber();
int iColumn = getTokenizer().getTokenColumn();
if( parseStringLiteral() )
{
setLocation( iOffset, iLineNum, iColumn );
return true;
}
return false;
}
private boolean parseStringLiteral()
{
Token T = new Token();
if( match( T, (int)'"' ) )
{
_parseStringLiteral( T._bUnterminated, T );
return true;
}
return false;
}
private void _parseStringLiteral( boolean bUnterminatedLiteral, Token t )
{
Expression e;
if( t._strValue.contains( TemplateGenerator.SCRIPTLET_BEGIN ) || t._strValue.contains( TemplateGenerator.ALTERNATE_EXPRESSION_BEGIN ) )
{
e = parseTemplatizedStringLiteral( t );
}
else
{
String strValue = t._strValue;
if( strValue.length() > 0 && strValue.charAt( 0 ) == TemplateGenerator.ESCAPED_SCRIPTLET_MARKER )
{
strValue = strValue.substring( 1 )
.replace( TemplateGenerator.ESCAPED_SCRIPTLET_BEGIN_CHAR, '<' )
.replace( TemplateGenerator.ESCAPED_ALTERNATE_EXPRESSION_BEGIN_CHAR, '$' );
}
e = new StringLiteral( strValue );
}
if( bUnterminatedLiteral )
{
e.addParseException(new ParseException(makeFullParserState(), Res.MSG_UNTERMINATED_STRING_LITERAL));
}
verify( e, t.getInvalidCharPos() < 0, Res.MSG_INVALID_CHAR_AT, t.getInvalidCharPos() );
pushExpression( e );
}
private TemplateStringLiteral parseTemplatizedStringLiteral( Token t )
{
TemplateGenerator template = TemplateGenerator.getTemplate( new StringReader( t._strValue ) );
template.setContextInferenceManager(_ctxInferenceMgr);
template.setForStringLiteral( true );
TemplateStringLiteral e = new TemplateStringLiteral( template );
GosuParser parser = (GosuParser)GosuParserFactory.createParser( _symTable, ScriptabilityModifiers.SCRIPTABLE );
IScriptPartId scriptPart = getScriptPart();
parser.pushScriptPart( scriptPart );
try
{
parser.setEditorParser( isEditorParser() );
parser._ctxInferenceMgr = _ctxInferenceMgr;
template.setUseStudioEditorParser( isEditorParser() );
copyBlockStackTo( parser );
try
{
template.verify( parser, _dfsDeclByName, _typeUsesMap );
}
catch( ParseResultsException exc )
{
// errors are already where they occurred
}
boolean hasIssues = false;
// adjust for open quote
for( IParseTree location : parser.getLocations() )
{
((ParseTree)location).adjustOffset( 1, 0, 0 );
for( IParseIssue parseIssue : location.getParsedElement().getParseIssues() )
{
((ParseIssue)parseIssue).setStateSource( _tokenizer.getSource() );
hasIssues = true;
}
}
setSubTree( parser.getLocations() );
try
{
template.compile( _scriptPartIdStack, _symTable instanceof ThreadSafeSymbolTable ? _symTable : _symTable.copy(), _dfsDeclByName, _typeUsesMap, _blocks, _ctxInferenceMgr );
}
catch( TemplateParseException exc )
{
if( !hasIssues &&
exc.getParseException() != null &&
!(getScriptPart().getContainingType() instanceof IGosuFragment) )
{
List<IParseIssue> parseExceptions = exc.getParseException().getParseExceptions();
for( IParseIssue p : parseExceptions )
{
e.addParseException( p );
}
}
}
return e;
}
finally
{
parser.popScriptPart( scriptPart );
}
}
//------------------------------------------------------------------------------
// type-literal
// name[\<<type-parameter-list>\>][\[\]]
//
public boolean parseTypeLiteral()
{
return parseTypeLiteral(false);
}
boolean parseTypeLiteral( boolean bInterface )
{
boolean bContextType = getContextTypes().isEmpty();
if( bContextType )
{
// Hint that we are trying to parse a type literal (during indirect member parsing)
pushContextType( MetaType.DEFAULT_TYPE_TYPE.get() );
}
try
{
int iOffset = _tokenizer.getTokenStart();
int iLineNum = _tokenizer.getLineNumber();
int iColumn = getTokenizer().getTokenColumn();
boolean bSuccess = _parseTypeLiteralWithAggregateSyntax( false, bInterface );
if( bSuccess )
{
Expression e = peekExpression();
if( e instanceof TypeLiteral )
{
IType monitorLockType = GosuTypes.IMONITORLOCK();
verify( e,
monitorLockType == null ||
!monitorLockType.equals( ((TypeLiteral)e).getType().getType() ),
Res.MSG_IMONITOR_LOCK_SHOULD_ONLY_BE_USED_WITHIN_USING_STMTS );
}
setLocation( iOffset, iLineNum, iColumn, true );
}
return bSuccess;
}
finally
{
if( bContextType )
{
popContextType();
}
}
}
void parseTypeLiteralIgnoreArrayBrackets()
{
int iOffset = _tokenizer.getTokenStart();
int iLineNum = _tokenizer.getLineNumber();
int iColumn = getTokenizer().getTokenColumn();
if( _parseTypeLiteralWithAggregateSyntax(true, false) )
{
setLocation( iOffset, iLineNum, iColumn );
}
}
boolean _parseTypeLiteralWithAggregateSyntax( boolean bIgnoreArrayBrackets, boolean bInterface )
{
boolean bRet = _parseTypeLiteral( bIgnoreArrayBrackets, bInterface );
if( match( null, "&", SourceCodeTokenizer.TT_OPERATOR, true ) )
{
parseAggregateTypeLiteral( bInterface );
}
return bRet;
}
boolean _parseTypeLiteral( boolean bIgnoreArrayBrackets, boolean bInterface )
{
Token T = new Token();
int iOffset = _tokenizer.getTokenStart();
int iLineNum = _tokenizer.getLineNumber();
int iColumn = getTokenizer().getTokenColumn();
if( match( null, Keyword.KW_block ) )
{
_parseBlockLiteral();
setLocation( iOffset, iLineNum, iColumn, true );
}
else
{
boolean bNotAWord = !match( T, SourceCodeTokenizer.TT_WORD );
if( bNotAWord )
{
TypeLiteral typeLiteral = bInterface ? new InterfaceTypeLiteral( ErrorType.getInstance() ) : new TypeLiteral( ErrorType.getInstance() );
verify( typeLiteral, false, Res.MSG_EXPECTING_TYPE_NAME );
pushExpression( typeLiteral );
IToken priorT = getTokenizer().getPriorToken();
setLocation( priorT.getTokenEnd(), priorT.getLine(), priorT.getTokenColumn(), true, true );
return false;
}
parseTypeLiteral( T, bIgnoreArrayBrackets, bInterface, iOffset, iLineNum, iColumn );
}
return true;
}
private void parseAggregateTypeLiteral(boolean bInterface)
{
CompoundTypeLiteral typeLiteral = new CompoundTypeLiteral();
List<IType> types = new ArrayList<IType>();
while( true )
{
addToCompoundType( types );
if( !match( null, "&", SourceCodeTokenizer.TT_OPERATOR ) )
{
break;
}
_parseTypeLiteral( false, bInterface );
}
verify( typeLiteral, types.size() > 1, Res.MSG_AGGREGATES_MUST_CONTAIN_MORE );
typeLiteral.setType( CompoundType.get( new HashSet<IType>( types ) ) );
pushExpression(typeLiteral);
}
private void addToCompoundType( List<IType> types )
{
TypeLiteral typeLiteralComponent = (TypeLiteral)popExpression();
IType t = typeLiteralComponent.getType().getType();
verify( typeLiteralComponent, t != JavaTypes.pVOID(), Res.MSG_VOID_NOT_ALLOWED );
Set<IType> componentTypes = t.isCompoundType() ? t.getCompoundTypeComponents() : Collections.singleton(t);
for( IType componentType : componentTypes )
{
boolean bFoundClassAlready = false;
for( IType csr : types )
{
if( verify( typeLiteralComponent, csr instanceof ErrorType || csr != componentType,
Res.MSG_ALREADY_CONTAINS_TYPE, componentType ) )
{
verify( typeLiteralComponent,
csr instanceof ErrorType || !csr.isAssignableFrom( componentType ),
Res.MSG_INTERFACE_REDUNDANT, csr, componentType );
verify( typeLiteralComponent,
csr instanceof ErrorType || !componentType.isAssignableFrom( csr ),
Res.MSG_INTERFACE_REDUNDANT, componentType, csr );
}
if( !csr.isInterface() )
{
bFoundClassAlready = true;
}
verify( typeLiteralComponent,
componentType.isInterface() || !bFoundClassAlready,
Res.MSG_ONLY_ONE_CLASS_IN_COMPONENT_TYPE );
}
types.add( componentType );
}
}
void parseBlockLiteral() {
int iOffset = getTokenizer().getTokenStart();
int iLineNum = getTokenizer().getLineNumber();
int iColumn = getTokenizer().getTokenColumn();
_parseBlockLiteral();
setLocation( iOffset, iLineNum, iColumn );
}
void _parseBlockLiteral()
{
BlockLiteral literal = new BlockLiteral();
verify( literal, match( null, '(' ), Res.MSG_EXPECTING_LEFTPAREN_BLOCK );
ArrayList<IType> argTypes = new ArrayList<IType>();
ArrayList<String> argNames = new ArrayList<String>();
ArrayList<IExpression> defValues = new ArrayList<IExpression>();
if( !match( null, ')' ) )
{
do
{
String result;
int state = _tokenizer.mark();
Token t = new Token();
boolean bEquals = false;
Expression defExpr = null;
TypeLiteral blockLiteral = null;
if( match( t, SourceCodeTokenizer.TT_WORD ) )
{
if( match( null, ":", SourceCodeTokenizer.TT_OPERATOR ) )
{
result = t._strValue;
}
else if( bEquals = match( null, "=", SourceCodeTokenizer.TT_OPERATOR ) )
{
result = t._strValue;
parseExpression();
defExpr = popExpression();
}
else if( match( null, null, '(', true ) )
{
result = t._strValue;
parseBlockLiteral();
blockLiteral = (TypeLiteral) popExpression();
}
else
{
_tokenizer.restoreToMark( state );
result = null;
}
}
else
{
_tokenizer.restoreToMark( state );
result = null;
}
String name = result;
TypeLiteral typeLiteral = null;
if( !bEquals )
{
if ( blockLiteral == null )
{
parseTypeLiteral();
typeLiteral = (TypeLiteral)popExpression();
if( match( null, "=", SourceCodeTokenizer.TT_OPERATOR ) )
{
parseExpression( typeLiteral.getType().getType() );
defExpr = popExpression();
}
}
else
{
typeLiteral = blockLiteral;
}
argTypes.add( typeLiteral.getType().getType() );
verifyOrWarn( typeLiteral, name != null, true, Res.MSG_BLOCK_TYPES_SHOULD_HAVE_ARG_NAMES );
verify( typeLiteral, typeLiteral.getType().getType() != JavaTypes.pVOID(), Res.MSG_VOID_NOT_ALLOWED );
}
else
{
argTypes.add( defExpr.getType() );
verifyOrWarn( literal, name != null, true, Res.MSG_BLOCK_TYPES_SHOULD_HAVE_ARG_NAMES );
verify( literal, defExpr.getType() != JavaTypes.pVOID(), Res.MSG_VOID_NOT_ALLOWED );
}
if( defExpr != null )
{
if( verify( defExpr, defExpr.isCompileTimeConstant(), Res.MSG_COMPILE_TIME_CONSTANT_REQUIRED ) )
{
defValues.add( defExpr );
}
}
else
{
defValues.add( null );
}
if( name != null && argNames.contains( name ) )
{
verify( typeLiteral == null ? literal : typeLiteral, false, Res.MSG_VARIABLE_ALREADY_DEFINED, name );
}
argNames.add( name == null ? "" : name );
} while( match( null, ',' ) );
verify( literal, match( null, ')' ), Res.MSG_EXPECTING_RIGHTPAREN_BLOCK );
}
argNames.trimToSize();
argTypes.trimToSize();
literal.setArgTypes( argTypes );
literal.setArgNames( argNames );
literal.setDefValueExpressions( defValues );
TypeLiteral returnType;
if( match( null, ":", SourceCodeTokenizer.TT_OPERATOR ) )
{
parseTypeLiteral();
returnType = (TypeLiteral)popExpression();
}
else
{
returnType = new TypeLiteral( JavaTypes.pVOID() );
returnType.setType( JavaTypes.pVOID() );
}
verify( literal, argNames.size() <= IBlock.MAX_ARGS, Res.MSG_BLOCKS_CAN_HAVE_A_MOST_SIXTEEN_ARGS );
literal.setReturnType( returnType );
pushExpression( literal );
}
void parseTypeLiteral( Token T, boolean bIgnoreArrayBrackets, boolean bInterface, int iOffset, int iLineNum, int iColumn )
{
parseCompoundTypeLiteralExpression( T, bInterface, iOffset, iLineNum, iColumn );
Expression expr = popExpression();
if( expr instanceof Identifier && !(expr.getType() instanceof ErrorType) )
{
//!! todo: this is a hack so we can resolve types with the same name as identifiers (case insensitively)
TypeLiteral e = bInterface ? new InterfaceTypeLiteral( MetaType.getLiteral( expr.getType() ) ) : new TypeLiteral( MetaType.getLiteral( expr.getType() ) );
e.setPackageExpression( expr );
expr = e;
}
else if( !(expr instanceof TypeLiteral) )
{
TypeLiteral e = bInterface ? new InterfaceTypeLiteral( ErrorType.getInstance() ) : new TypeLiteral( ErrorType.getInstance() );
e.setPackageExpression( expr );
e.addParseException( new ParseException( null, Res.MSG_EXPECTING_TYPE_NAME ) );
expr = e;
}
IType type = ((TypeLiteral)expr).getType().getType();
verifyTypeAccessible( (TypeLiteral)expr, type );
T._strValue = type.getName();
resolveArrayOrParameterizationPartOfTypeLiteral( T, bIgnoreArrayBrackets, (TypeLiteral)expr );
}
private void verifyTypeAccessible( TypeLiteral expr, IType type )
{
ICompilableType gsClass = getGosuClass();
if( gsClass == null || Modifier.isPublic( type.getModifiers() ) )
{
return;
}
IRelativeTypeInfo.Accessibility acc = FeatureManager.getAccessibilityForClass( type, gsClass );
if( Modifier.isPrivate( type.getModifiers() ) )
{
verify( expr,
acc == IRelativeTypeInfo.Accessibility.PRIVATE,
Res.MSG_TYPE_HAS_XXX_ACCESS, type.getName(), Keyword.KW_private.toString() );
}
else if( Modifier.isProtected( type.getModifiers() ) )
{
verify( expr,
acc == IRelativeTypeInfo.Accessibility.PROTECTED ||
acc == IRelativeTypeInfo.Accessibility.INTERNAL ||
acc == IRelativeTypeInfo.Accessibility.PRIVATE,
Res.MSG_TYPE_HAS_XXX_ACCESS, type.getName(), Keyword.KW_protected.toString() );
}
else if( /* package-protected (or internal) */ !Modifier.isPublic( type.getModifiers() ) &&
!Modifier.isProtected( type.getModifiers() ))
{
verify( expr,
acc == IRelativeTypeInfo.Accessibility.INTERNAL ||
acc == IRelativeTypeInfo.Accessibility.PRIVATE,
Res.MSG_TYPE_HAS_XXX_ACCESS, type.getName(), Keyword.KW_internal.toString() );
}
}
private void parseCompoundTypeLiteralExpression( Token T, boolean bInterface, int iOffset, int iLineNum, int iColumn )
{
parseNamespaceStartOrRelativeType( T, bInterface );
Expression expr = peekExpression();
if( expr.hasParseExceptions() )
{
List<IParseIssue> exceptions = expr.getParseExceptions();
if( exceptions.size() != 1 ||
(exceptions.get( 0 ).getMessageKey() != Res.MSG_CANNOT_REFERENCE_CLASS_TYPE_VAR_IN_STATIC_CONTEXT &&
exceptions.get( 0 ).getMessageKey() != Res.MSG_TYPE_PARAM_NOT_ASSIGNABLE_TO &&
exceptions.get( 0 ).getMessageKey() != Res.MSG_EXPECTING_CLOSING_ANGLE_BRACKET_FOR_TYPE) )
{
ParseException pe = expr.removeParseException( null );// Res.MSG_BAD_IDENTIFIER_NAME );
pe.setMessage( Res.MSG_INVALID_TYPE, T._strValue );
expr.addParseException( pe );
}
}
setLocation( iOffset, iLineNum, iColumn );
parseIndirectMemberAccess( iOffset, iLineNum, iColumn, true );
}
private boolean isTypeParameterErrorMsg( Expression expr, List<IParseIssue> exceptions )
{
return exceptions.get( 0 ).getMessageKey() == Res.MSG_TYPE_PARAM_NOT_ASSIGNABLE_TO && expr instanceof TypeLiteral && !isErrorType( ((TypeLiteral)expr).getType().getType() );
}
/**
* @return True if parsed parameterized type.
*/
private boolean resolveArrayOrParameterizationPartOfTypeLiteral( Token T, boolean bIgnoreArrayBrackets, TypeLiteral e )
{
boolean bArrayOrParameterization = false;
if( !bIgnoreArrayBrackets )
{
bArrayOrParameterization = parseArrayType( e );
}
pushExpression(e);
if( !T._strValue.endsWith( "[]" ) )
{
int iOffset = _tokenizer.getTokenStart();
int iLineNum = _tokenizer.getLineNumber();
int iColumn = getTokenizer().getTokenColumn();
if( match( null, "<", SourceCodeTokenizer.TT_OPERATOR ) )
{
TypeLiteral typeLiteral = (TypeLiteral)peekExpression();
IType type = typeLiteral.getType().getType();
List<TypeLiteral> paramTypes = Collections.emptyList();
boolean bRecursiveGosuClass = type instanceof IGosuClassInternal &&
((IGosuClassInternal)type).isCompilingHeader();
if( (type instanceof IJavaTypeInternal && ((IJavaTypeInternal)type).isDefiningGenericTypes()) ||
bRecursiveGosuClass )
{
// If defining generic types, we assume a recursive type and, for now, use the raw generic type
eatPossibleParametarization( false );
}
else
{
verify( e, type.isGenericType(), Res.MSG_PARAMETERIZATION_NOT_SUPPORTED_FOR_TYPE, type.getName() );
paramTypes = parseTypeParameters( type );
verify( e, match( null, ">", SourceCodeTokenizer.TT_OPERATOR ), Res.MSG_EXPECTING_CLOSING_ANGLE_BRACKET_FOR_TYPE );
makeTypeParameterListClause( iOffset, iLineNum, iColumn, paramTypes );
}
int numArrays = 0;
while( !bIgnoreArrayBrackets && match( null, '[' ) )
{
verify( e, match( null, ']' ), Res.MSG_EXPECTING_ARRAY_BRACKET );
++numArrays;
}
if( !(type instanceof ErrorType) )
{
IType[] types = new IType[paramTypes.size()];
for( int i = 0; i < paramTypes.size(); i++ )
{
TypeLiteral tl = (TypeLiteral)paramTypes.get( i );
types[i] = (tl.getType()).getType();
}
if( !bRecursiveGosuClass && verifyCanParameterizeType( e, type, types ) )
{
typeLiteral.setParameterTypes( types );
type = typeLiteral.getType().getType();
}
if( numArrays > 0 )
{
for( int i = 0; i < numArrays; i++ )
{
type = type.getArrayType();
}
typeLiteral.setType( MetaType.getLiteral( type ) );
}
}
bArrayOrParameterization = true;
}
else
{
TypeLiteral typeLiteral = (TypeLiteral)peekExpression();
IType type = typeLiteral.getType().getType();
try
{
if( type.isGenericType() )
{
// If a generic type, assume the default parameterized version e.g., List => List<Object>
type = TypeLord.makeDefaultParameterizedType( type, !type.isParameterizedType() && (!(getGosuClass() instanceof IGosuClass) || !((IGosuClass)getGosuClass()).isCompilingHeader()) );
typeLiteral.setType( MetaType.getLiteral( type ) );
}
}
catch( Exception ex )
{
throw GosuExceptionUtil.forceThrow( ex, type.getName() );
}
}
}
return bArrayOrParameterization;
}
private boolean parseArrayType( TypeLiteral tl )
{
boolean bBalancedBrackets = true;
IType baseType = tl.getType().getType();
while( match( null, '[' ) )
{
bBalancedBrackets = match( null, ']' );
if( !bBalancedBrackets )
{
advanceToNextTokenSilently();
}
try
{
baseType = baseType.getArrayType();
}
catch( IllegalArgumentException iae )
{
tl.addParseException( Res.MSG_ARRAY_NOT_SUPPORTED, baseType.getName() );
baseType = ErrorType.getInstance();
}
}
if( baseType != tl.getType().getType() )
{
warn( tl, !tl.getType().getType().isParameterizedType() ||
TypeLord.getDefaultParameterizedType( tl.getType().getType() ) == tl.getType().getType(),
Res.MSG_PARAMETERIZED_ARRAY_COMPONENT );
tl.setType( MetaType.getLiteral( baseType ) );
verify( tl, bBalancedBrackets, Res.MSG_EXPECTING_ARRAY_BRACKET );
return true;
}
return false;
}
//------------------------------------------------------------------------------
// type-parameter-list
// <type-parameter>
// <type-parameter-list> , <type-parameter>
//
List<TypeLiteral> parseTypeParameters( IType enclosingType )
{
List<TypeLiteral> paramTypes = new ArrayList<TypeLiteral>();
int i = 0;
do
{
IType boundingType = JavaTypes.OBJECT();
if( enclosingType != null && enclosingType.isGenericType() )
{
IGenericTypeVariable[] typeVars = enclosingType.getGenericTypeVariables();
if( typeVars != null && typeVars.length > i )
{
boundingType = typeVars[i].getBoundingType();
}
}
parseParameterType( boundingType );
paramTypes.add( (TypeLiteral)popExpression() );
}
while( match( null, ',' ) && ++i > 0 );
return paramTypes;
}
private void makeTypeParameterListClause( int iOffset, int iLineNum, int iColumn, List<TypeLiteral> paramTypes )
{
if( paramTypes.size() > 0 )
{
TypeParameterListClause e = new TypeParameterListClause( paramTypes.toArray( new ITypeLiteralExpression[paramTypes.size()] ) );
pushExpression( e );
boolean bZeroLength = _tokenizer.getTokenStart() == iOffset;
if( bZeroLength )
{
IToken priorToken = getTokenizer().getPriorToken();
iOffset = priorToken.getTokenEnd();
iLineNum = priorToken.getLine();
iColumn = priorToken.getTokenColumn();
}
setLocation( iOffset, iLineNum, iColumn, true );
popExpression();
}
}
//------------------------------------------------------------------------------
// type-parameter
// ? [extends <type-literal>]
// ? [super <type-literal>]
// <type-literal>
//
boolean parseParameterType( IType boundingType )
{
if( match( null, "?", SourceCodeTokenizer.TT_OPERATOR ) )
{
if( match( null, Keyword.KW_extends ) )
{
if( match( null, "?", SourceCodeTokenizer.TT_OPERATOR, true ) )
{
// A strange, but necessary case. E.g.,
// List<E> has method: addAll( Collection<? extends E> c )
// when we have type List<?> we'll have addAll( Collection<? extends ?> c )
// which essentially needs to be just that - a Collection of something undefined
if( !parseParameterType( boundingType ) )
{
return false;
}
}
else
{
parseTypeLiteral();
}
}
else if( match( null, Keyword.KW_super ) )
{
if( match( null, "?", SourceCodeTokenizer.TT_OPERATOR, true ) )
{
// A strange, but necessary case. E.g.,
// List<E> has method: addAll( Collection<? super E> c )
// when we have type List<?> we'll have addAll( Collection<? super ?> c )
// which essentially needs to be just that - a Collection of something undefined
if( !parseParameterType( boundingType ) )
{
return false;
}
}
else
{
parseTypeLiteral();
popExpression(); // Eat whatever type literal is here (we punt on contravariance)
TypeLiteral typeLiteral = new TypeLiteral( MetaType.getLiteral( boundingType ) );
pushExpression( typeLiteral );
}
}
else
{
pushExpression( new TypeLiteral( JavaTypes.OBJECT() ) );
}
}
else
{
parseTypeLiteral();
}
TypeLiteral tl = (TypeLiteral)peekExpression();
boxTypeLiteralsType( tl );
return true;
}
private void boxTypeLiteralsType( TypeLiteral tl )
{
IType tlType = tl.getType().getType();
if( !warn( tl, !tlType.isPrimitive(), Res.MSG_PRIMITIVE_TYPE_PARAM, tlType.getName(), TypeSystem.getBoxType( tlType ) ) )
{
tl.setType(TypeSystem.getBoxType(tlType));
}
}
//------------------------------------------------------------------------------
// statement
// namespace-statement
// package namespace-name
//
// uses-statement
// uses type-literal
// uses namespace-ref
//
// block
// { [statement-list] }
// statement-list
// <statement>
// <statement-list> <statement>
// * Left recursion removed is: *
// statement-list
// <statement> <statement-list2>
// statement-list2
// <statement>
// null
//
// assignment-statement
// <identifier> = <expression>
// <member-access> = <expression>
// <array-access> = <expression>
//
// method-call-statement
// <method-call-expression>
//
// argument-list
// <expression>
// <argument-list> , <expression>
//
// if-statement
// if ( <expression> ) <statement> [ else <statement> ] [ unless ( <expression> ) ]
//
// for...in-statement
// for ( <identifier> in <expression> [ index <identifier> ] ) <statement>
//
// while-statement
// while ( <expression> ) <statement>
//
// do...while-statetment
// do <statement> while ( <expression> )
//
// var-statement
// var <identifier> [ : <type-literal> ] = <expression>
// var <identifier> : <type-literal> [ = <expression> ]
//
// switch-statement
// switch ( <expression> ) { [switch-cases] [switch-default] }
//
// switch-cases
// <switch-case>
// <switch-cases> <switch-case>
//
// switch-case
// case <expression> : [statement-list]
//
// switch-default
// default : [statement-list]
//
// continue-statement
// continue
//
// break-statement
// break
//
// return-statement
// return <expression>
// return ;
//
// assert-statement
// assert <expression>
// assert <expression> : <expression>
//
// class-definition
// [modifiers] class <identifier> [extends <base-class>] [implements <interfaces-list>] { <class-members> }
//
// function-definition
// [modifiers] function <identifier> ( [ <argument-declaration-list> ] ) [ : <type-literal> ] <statement-block>
//
// argument-declaration-list
// <argument-declaration>
// <argument-declarationlist> , <argument-declaration>
//
// argument-declaration
// <identifier> : <type-literal>
//
// try-catch-finally-statement
// try <statement> [ catch ( <identifier> ) <statement> ] [ finally <statement> ]
//
boolean parseStatement()
{
return parseStatement( false );
}
boolean parseStatement( boolean bAsStmtBlock )
{
incStatementDepth();
try
{
int iOffset = _tokenizer.getTokenStart();
int iLineNum = _tokenizer.getLineNumber();
int iColumn = getTokenizer().getTokenColumn();
checkInstruction( true );
ParseTree prevLocation = peekLocation();
IParsedElement prevStmt =
prevLocation != null && prevLocation.getParsedElement() != null
? prevLocation.getParsedElement() instanceof Statement
? prevLocation.getParsedElement()
: null
: null;
boolean bRet;
boolean bSetLocation = true;
boolean bMatchedBrace = !bAsStmtBlock && match( null, '{' );
if( bMatchedBrace || bAsStmtBlock )
{
parseStatementBlock( bMatchedBrace || !bAsStmtBlock );
bRet = true;
bSetLocation = peekStatement() instanceof StatementList;
}
else
{
bRet = _parseStatement();
}
if( bRet && bSetLocation )
{
// Consume optional trailing semi as part of the statement
match( null, ';' );
setLocation( iOffset, iLineNum, iColumn, bAsStmtBlock );
ParsedElement currentStmt = peekStatement();
if( !(currentStmt instanceof NoOpStatement) && !(prevStmt instanceof NoOpStatement) )
{
warn( currentStmt, prevStmt == null || prevStmt.getLineNum() != currentStmt.getLineNum() ||
prevStmt.hasParseExceptions() || currentStmt.hasParseExceptions(),
Res.MSG_STATEMENT_ON_SAME_LINE );
}
}
return bRet;
}
finally
{
decStatementDepth();
}
}
boolean parseLoopStatement()
{
_iBreakOk++;
_iContinueOk++;
try
{
return parseStatement();
// if( parseStatement() ) {
// Statement stmt = peekStatement();
// warn( stmt, doesLoopCycle( stmt ), Res.MSG_LOOP_DOESNT_LOOP );
// return true;
// }
// return false;
}
finally
{
_iBreakOk--;
_iContinueOk--;
}
}
//## todo: doesn't handle this case (break is absolute with help of continue, but info about continue does not exist past the break)
//## propbably best to change getLeastSignificantTerminal() to return something that provides more information, yet retains the same
//## terminal statement for normal unreachable code detection.
// do {
// if( cond ) {
// break
// }
// else {
// continue
// }
// } while( true )
//
// private boolean doesLoopCycle( Statement stmt )
// {
// boolean[] bAbsolute = {false};
// ITerminalStatement termStmt = stmt.getLeastSignificantTerminalStatement( bAbsolute );
// if( termStmt == null || !bAbsolute[0] ) {
// return true;
// }
// return termStmt.getTerminalType() == TerminalType.Continue;
// }
boolean _parseStatement()
{
Token T = new Token();
if( areUsingStatementsAllowedInStatementLists() &&
match( null, Keyword.KW_uses ) )
{
parseUsesStatement( T );
}
else if( match( null, Keyword.KW_if ) )
{
parseIfStatement();
}
else if( match( null, Keyword.KW_try ) )
{
parseTryCatchFinallyStatement( T );
}
else if( match( null, Keyword.KW_throw ) )
{
parseThrowStatement();
}
else if( match( null, Keyword.KW_continue ) )
{
ContinueStatement stmt = new ContinueStatement();
verify( stmt, _iContinueOk > 0, Res.MSG_CONTINUE_OUTSIDE_LOOP );
pushStatement( stmt );
}
else if( match( null, Keyword.KW_break ) )
{
BreakStatement stmt = new BreakStatement();
verify( stmt, _iBreakOk > 0, Res.MSG_BREAK_OUTSIDE_SWITCH_OR_LOOP );
pushStatement( stmt );
}
else if( match( null, Keyword.KW_return ) )
{
parseReturnStatement();
}
else if( match( null, Keyword.KW_foreach ) || match( null, Keyword.KW_for ) )
{
parseForEachStatement( T );
}
else if( match( null, Keyword.KW_while ) )
{
parseWhileStatement();
}
else if( match( null, Keyword.KW_do ) )
{
parseDoWhileStatement();
}
else if( match( null, Keyword.KW_switch ) )
{
parseSwitchStatement();
}
else if( match( null, Keyword.KW_using ) )
{
parseUsingStatement();
}
else if( match( null, Keyword.KW_assert ) )
{
parseAssertStatement();
}
else if( match( null, Keyword.KW_final ) )
{
VarStatement varStmt = new VarStatement();
varStmt.setModifierInfo( new ModifierInfo(0) );
varStmt.setFinal( true );
parseLocalVarStatement( varStmt, T );
}
else if( match( null, Keyword.KW_var, true ) )
{
VarStatement varStmt = new VarStatement();
parseLocalVarStatement( varStmt, T );
}
else if( match( null, ';' ) )
{
pushStatement( new NoOpStatement() );
}
else if( getGosuClass() instanceof IGosuProgram &&
getStatementDepth() == 1 &&
!isParsingBlock() &&
!((IGosuProgramInternal)getGosuClass()).isStatementsOnly() &&
(maybeAdvanceTokenizerToEndOfSavedLocation()) )
{
pushStatement( new NoOpStatement() );
}
else if( !isParsingFunction() && parseFunctionDefinition() )
{
return true;
}
else if( !isParsingFunction() && parsePropertyDefinition() )
{
return true;
}
else if( match( null, Keyword.KW_eval, true ) )
{
int iOffset = _tokenizer.getTokenStart();
int iLineNum = _tokenizer.getLineNumber();
int iColumn = getTokenizer().getTokenColumn();
match( null, Keyword.KW_eval );
parseEvalExpression();
setLocation( iOffset, iLineNum, iColumn, true );
pushStatement( new EvalStatement( (EvalExpression)popExpression() ) );
}
else if( !parseAssignmentOrMethodCall() )
{
if( !match( null, null, SourceCodeTokenizer.TT_EOF, true ) )
{
int iOffset = _tokenizer.getTokenStart();
int iLineNum = _tokenizer.getLineNumber();
int iColumn = getTokenizer().getTokenColumn();
if( !match( null, null, '}', true ) &&
!match( null, null, ';', true ) )
{
if( isParsingFunction() )
{
Statement noop = new NoOpStatement();
if( maybeAdvanceTokenizerToEndOfSavedLocation() )
{
pushStatement( noop );
setLocation( iOffset, iLineNum, iColumn, true, true );
popStatement();
return false;
}
else if( match( null, Keyword.KW_construct ) ||
match( null, Keyword.KW_function ) ||
match( null, Keyword.KW_property ) )
{
eatStatementBlock( noop, Res.MSG_SYNTAX_ERROR );
pushStatement( noop );
setLocation( iOffset, iLineNum, iColumn, true, true );
popStatement();
return false;
}
}
String str = _tokenizer.getTokenAsString();
TypeLiteral type = resolveTypeLiteral( str );
_tokenizer.nextToken();
Statement noop = new NoOpStatement();
if( !(type.evaluate() instanceof ErrorType) )
{
int state = _tokenizer.mark();
int iLocationsCount = _locations.size();
parsePrimaryExpression();
Expression expression = popExpression();
if( expression instanceof Identifier && match( null, "=", SourceCodeTokenizer.TT_OPERATOR ) )
{
expression.clearParseExceptions();
parseExpression(); //bad rhs
popExpression();
verify( noop, false, Res.MSG_JAVA_STYLE_VARIABLE_DECLARATION, str );
}
else
{
_tokenizer.restoreToMark( state );
removeLocationsFrom( iLocationsCount );
verify( noop, false, Res.MSG_UNEXPECTED_TOKEN, str );
}
}
else
{
verify( noop, false, Res.MSG_UNEXPECTED_TOKEN, str );
}
pushStatement( noop );
}
else if( getStatementDepth() == 1 &&
!isParsingBlock() &&
getGosuClass() instanceof IGosuProgram &&
((IGosuProgramInternal)getGosuClass()).isParsingExecutableProgramStatements() )
{
Statement noop = new NoOpStatement();
verify( noop, false, Res.MSG_UNEXPECTED_TOKEN, _tokenizer.getTokenAsString() );
pushStatement( noop );
_tokenizer.nextToken();
}
else
{
return false;
}
}
else
{
return false;
}
}
return true;
}
private void parseAssertStatement()
{
AssertStatement assertStmt = new AssertStatement();
if( verify( assertStmt, getGosuClass() instanceof IGosuClassInternal, Res.MSG_ASSERTIONS_NOT_ALLOWED_HERE ) )
{
((IGosuClassInternal)getGosuClass()).setHasAssertions( true );
}
parseExpression( JavaTypes.pBOOLEAN() );
Expression condition = popExpression();
if( !verify( condition, !(condition instanceof NotAWordExpression), Res.MSG_EXPECTING_CONDITION_FOR_ASSERT ) )
{
//noinspection ThrowableResultOfMethodCallIgnored
condition.removeParseException( Res.MSG_SYNTAX_ERROR );
}
assertStmt.setCondition( condition );
if( match( null, ":", SourceCodeTokenizer.TT_OPERATOR ) )
{
parseExpression( JavaTypes.OBJECT() );
Expression detail = popExpression();
if( !verify( detail, !(detail instanceof NotAWordExpression), Res.MSG_EXPECTING_MESSAGE_FOR_ASSERT ) )
{
//noinspection ThrowableResultOfMethodCallIgnored
detail.removeParseException( Res.MSG_SYNTAX_ERROR );
}
assertStmt.setDetail( detail );
}
pushStatement( assertStmt );
}
private boolean areUsingStatementsAllowedInStatementLists()
{
return getGosuClass() == null ||
(getGosuClass() instanceof IGosuProgramInternal && ((IGosuProgramInternal) getGosuClass()).allowsUses()) ||
CommonServices.getEntityAccess().areUsesStatementsAllowedInStatementLists(getGosuClass());
}
private int getStatementDepth()
{
return _iStmtDepth;
}
private void incStatementDepth()
{
_iStmtDepth++;
}
private void decStatementDepth()
{
_iStmtDepth--;
}
void parseLocalVarStatement( VarStatement varStmt, Token t )
{
verify( varStmt, match( t, Keyword.KW_var ), Res.MSG_EXPECTING_VAR_STMT );
int iNameOffset = getTokenizer().getTokenStart();
if( verify( varStmt, match( t, SourceCodeTokenizer.TT_WORD ), Res.MSG_EXPECTING_IDENTIFIER_VAR ) )
{
varStmt.setNameOffset( iNameOffset, t._strValue );
warn( varStmt, !Keyword.isKeyword( t._strValue ), Res.MSG_IMPROPER_USE_OF_KEYWORD, t._strValue );
}
else
{
t._strValue = null;
}
parseVarStatement( varStmt, t, false );
}
void parseVarStatement( VarStatement varStmt, Token idToken, boolean bClassMember )
{
String strIdentifier = idToken._strValue == null ? "" : idToken._strValue;
warn( varStmt, !Keyword.isKeyword( strIdentifier ), Res.MSG_IMPROPER_USE_OF_KEYWORD, strIdentifier );
IToken priorToken = getTokenizer().getPriorToken();
boolean bZeroLength = strIdentifier.length() <= 0;
addNameInDeclaration( strIdentifier,
bZeroLength ? priorToken.getTokenEnd() : idToken._iDocPosition,
priorToken.getLine(), priorToken.getTokenColumn(), !bZeroLength );
ISymbol existingSymbol = _symTable.getSymbol( strIdentifier );
if( (isParsingBlock() || getParsingAnonymousClass() != null) && !isParsingAnnotation() )
{
existingSymbol = captureSymbol( getCurrentEnclosingGosuClass(), strIdentifier, null );
}
boolean bFieldSymbolFromProgram = false;
if( !bClassMember && existingSymbol != null )
{
bFieldSymbolFromProgram = getGosuClass() instanceof IGosuProgram &&
(bClassMember || isLocalVarTopLevelFunctionBodyStmt()) &&
((IGosuProgramInternal)getGosuClass()).isParsingExecutableProgramStatements() &&
(existingSymbol instanceof DynamicSymbol || existingSymbol instanceof ScopedDynamicSymbol);
if( verify( varStmt, bFieldSymbolFromProgram, Res.MSG_VARIABLE_ALREADY_DEFINED, strIdentifier ) )
{
// Overwrite program var's symbol with class field symbol
varStmt.setSymbol( existingSymbol );
}
}
// NOTE: For class member parsing we don't care about these results. We just want to eat the keywords.
// The scope is setup when we decl parse in GosuClassParser
boolean bRequest = false;
boolean bSession = false;
boolean bApplication = false;
if( (match( null, Keyword.KW_execution )) ||
(bRequest = match( null, Keyword.KW_request )) ||
(bSession = match( null, Keyword.KW_session )) ||
(bApplication = match( null, Keyword.KW_application )) )
{
}
if( varStmt.getModifierInfo() == null )
{
varStmt.setModifierInfo( new ModifierInfo(0) );
}
TypeLiteral typeLiteral = null;
if( match( null, ":", SourceCodeTokenizer.TT_OPERATOR ) )
{
parseTypeLiteral();
typeLiteral = (TypeLiteral)popExpression();
}
else if( !match( null, "=", SourceCodeTokenizer.TT_OPERATOR, true ) )
{
if( match( null, null, '(', true ) )
{
parseBlockLiteral();
typeLiteral = (TypeLiteral)popExpression();
}
}
if( !bClassMember && getGosuClass() instanceof IGosuProgram &&
getGosuClass().getMemberField( strIdentifier ) != null )
{
// Eat the property alias if this is a program field (the class parser already made the property)
if( match( null, Keyword.KW_as ) )
{
match( null, Keyword.KW_readonly );
match( null, SourceCodeTokenizer.TT_WORD );
}
}
Expression eas = null;
if( match( null, "=", SourceCodeTokenizer.TT_OPERATOR ) )
{
pushParsingFieldInitializer( varStmt );
putThisAndSuperSymbols( varStmt.getModifierInfo() ); // assume caller must pushes scope
try
{
parseExpression( typeLiteral == null ? null : typeLiteral.getType().getType() );
}
finally
{
popParsingFieldInitializer();
}
eas = popExpression();
if( eas.hasParseExceptions() )
{
if( typeLiteral != null )
{
IType typeCast = typeLiteral.getType().getType();
eas.getParseExceptions().get( 0 ).setExpectedType( typeCast );
}
}
detectLikelyJavaCast(eas);
}
verify( varStmt, eas != null || typeLiteral != null, Res.MSG_VARIABLE_TYPE_OR_VALUE_REQUIRED );
if( typeLiteral != null )
{
verify( typeLiteral, typeLiteral.getType().getType() != JavaTypes.pVOID(), Res.MSG_VOID_NOT_ALLOWED );
}
// Create a local symbol for the identifier part of the 'var' statement
// (so it can be legally referenced in the containing scope).
IType type = null;
if( eas != null )
{
type = eas.getType();
if( type == null )
{
type = ErrorType.getInstance();
}
}
if( typeLiteral != null )
{
type = typeLiteral.getType().getType();
}
else if( bClassMember && eas != null )
{
// Assign inferred type to var property corresponding with this field
IGosuVarPropertyInfo vpi = (IGosuVarPropertyInfo) getGosuClass().getTypeInfo().getProperty( getGosuClass(), strIdentifier );
vpi.assignActualType( eas.getType() );
vpi.assignSymbolType( eas.getType() );
}
verify( varStmt, !JavaTypes.pVOID().equals(type), Res.MSG_VARIABLE_MUST_HAVE_NON_NULL_TYPE );
//if no type was found, we have added an error so give the symbol the error type
if( type == null )
{
type = ErrorType.getInstance();
}
ISymbol symbol;
if( bClassMember || bFieldSymbolFromProgram )
{
symbol = varStmt.getSymbol();
symbol.setType( type );
varStmt.setType( type );
}
else
{
GlobalScope scope = bApplication
? GlobalScope.APPLICATION
: bSession
? GlobalScope.SESSION
: bRequest
? GlobalScope.REQUEST
: GlobalScope.EXECUTION;
Symbol newSym;
if( scope == GlobalScope.EXECUTION )
{
newSym = new Symbol( strIdentifier, type, _symTable, null );
}
else
{
newSym = new ScopedDynamicSymbol( _symTable, strIdentifier, "", type, scope );
}
newSym.setModifierInfo( varStmt.getModifierInfo() );
symbol = newSym;
varStmt.setSymbol( newSym );
varStmt.setScope( scope );
}
if( existingSymbol == null )
{
_symTable.putSymbol( symbol );
}
eas = possiblyWrapWithImplicitCoercion( eas, type );
varStmt.setAsExpression( eas );
varStmt.setTypeLiteral( typeLiteral );
varStmt.setScriptPart( getScriptPart() );
varStmt.setDefinitionParsed( true );
boolean bHideFieldForEditorParsing = getGosuClass() != null && getGosuClass().isCreateEditorParser() && bFieldSymbolFromProgram;
if( bHideFieldForEditorParsing )
{
pushStatement( new HideFieldNoOpStatement( varStmt ) );
}
else
{
pushStatement( varStmt );
}
}
private boolean isLocalVarTopLevelFunctionBodyStmt() {
if( _symTable.getScopeCount() > 1 ) {
return _symTable.peekScope( 1 ).getActivationCtx() != null;
}
return false;
}
private void detectLikelyJavaCast(Expression eas) {
if( eas instanceof IParenthesizedExpression || (eas instanceof IImplicitTypeAsExpression && ((IImplicitTypeAsExpression)eas).getLHS() instanceof IParenthesizedExpression) ) {
IParenthesizedExpression parenExpr;
if(eas instanceof IParenthesizedExpression) {
parenExpr = (IParenthesizedExpression)eas;
} else {
parenExpr = (IParenthesizedExpression)((IImplicitTypeAsExpression)eas).getLHS();
}
if (parenExpr.getExpression() instanceof TypeLiteral) {
IType castType = eas.getType();
if(castType instanceof IMetaType) {
castType = ((IMetaType)castType).getType();
}
eas.addParseWarning( new ParseWarning( makeFullParserState(), Res.MSG_LIKELY_JAVA_CAST, castType.getName() ) );
}
}
}
private boolean recoverFromJavaStyleCast( Expression eas )
{
if( eas instanceof IParenthesizedExpression &&
((IParenthesizedExpression)eas).getExpression() instanceof TypeLiteral )
{
if( getTokenizer().getLineNumber() == eas.getLocation().getLineNum() )
{
// Something follows a java-style-cast-looking expression on same line, so assume it's a java-style cast...
IType castType = eas.getType();
if( castType instanceof IMetaType ){
castType = ((IMetaType) castType).getType();
}
int mark = getTokenizer().mark();
parseUnaryExpression();
Expression maybeRealEas = popExpression();
if( maybeRealEas.hasParseExceptions() )
{
_locations.remove( maybeRealEas.getLocation() );
getTokenizer().restoreToMark( mark );
}
else
{
popExpression();
eas.addParseWarning( new ParseWarning( makeFullParserState(), Res.MSG_LIKELY_JAVA_CAST, castType.getName() ) );
eas = possiblyWrapWithImplicitCoercion( maybeRealEas, castType );
pushExpression( eas );
return true;
}
}
}
return false;
}
DynamicPropertySymbol parseVarPropertyClause( VarStatement varStmt, String strVarIdentifier, IType varType, boolean parseInitializer )
{
if( !match( null, Keyword.KW_as ) )
{
return null;
}
boolean bReadonly = match( null, Keyword.KW_readonly ) || varStmt.isFinal();
final int iNameStart = getTokenizer().getTokenStart();
Token T = new Token();
varStmt.setHasProperty( true );
String strPropertyName = null;
if( verify( varStmt, match( T, SourceCodeTokenizer.TT_WORD ), Res.MSG_EXPECTING_NAME_PROPERTY ) )
{
strPropertyName = T._strValue;
warn( varStmt, !Keyword.isKeyword( strPropertyName ), Res.MSG_IMPROPER_USE_OF_KEYWORD, strPropertyName );
}
String strIdentifier = strPropertyName == null ? "" : strPropertyName;
IToken restoreState = getTokenizer().getPriorToken();
addNameInDeclaration( strIdentifier,
T._iDocPosition,
restoreState.getLine(), restoreState.getTokenColumn(), strIdentifier.length() > 0 );
varStmt.setPropertyName( strPropertyName );
varStmt.setNameOffset(iNameStart, strPropertyName );
ISymbol symbol = getSymbolTable().getSymbol( strPropertyName );
if( symbol != null && !symbol.getDisplayName().equals( strPropertyName ) )
{
// Force case sensitivity, mainly to make overrides consistent
symbol = null;
}
if( symbol instanceof DynamicPropertySymbol &&
(((DynamicPropertySymbol)symbol).getVarIdentifier() != null &&
!((DynamicPropertySymbol)symbol).getVarIdentifier().equals( strVarIdentifier) ) )
{
varStmt.addParseException( new ParseException( makeFullParserState(), Res.MSG_PROPERTY_ALREADY_DEFINED, strPropertyName) );
}
ICompilableType gsClass = getGosuClass();
DynamicPropertySymbol dps;
if( symbol instanceof DynamicPropertySymbol )
{
dps = new DynamicPropertySymbol( (DynamicPropertySymbol)symbol );
if( dps.getGetterDfs() == null || dps.getGetterDfs().getScriptPart().getContainingType() != gsClass )
{
VarPropertyGetFunctionSymbol getFunctionSymbol = new VarPropertyGetFunctionSymbol( gsClass, getSymbolTable(), strPropertyName, strVarIdentifier, varType );
getFunctionSymbol.getModifierInfo().setAnnotations( varStmt.getAnnotations() );
getFunctionSymbol.getModifierInfo().setDescription( varStmt.getModifierInfo().getDescription() );
getFunctionSymbol.setClassMember( true );
if (dps.getGetterDfs() != null) {
getFunctionSymbol.setOverride(true);
getFunctionSymbol.setSuperDfs(dps.getGetterDfs());
}
getFunctionSymbol.setStatic( varStmt.isStatic() );
dps.setGetterDfs( getFunctionSymbol );
verifyFunction( getFunctionSymbol, varStmt );
}
}
else
{
VarPropertyGetFunctionSymbol getFunctionSymbol = new VarPropertyGetFunctionSymbol( gsClass, getSymbolTable(), strPropertyName, strVarIdentifier, varType );
getFunctionSymbol.getModifierInfo().setAnnotations( varStmt.getAnnotations() );
getFunctionSymbol.getModifierInfo().setDescription( varStmt.getModifierInfo().getDescription() );
getFunctionSymbol.setClassMember( true );
getFunctionSymbol.setStatic( varStmt.isStatic() );
verifyFunction( getFunctionSymbol, varStmt );
dps = new DynamicPropertySymbol( getFunctionSymbol, true );
}
if( !bReadonly && (dps.getSetterDfs() == null || dps.getSetterDfs().getScriptPart().getContainingType() != gsClass) )
{
VarPropertySetFunctionSymbol setFunctionSymbol = new VarPropertySetFunctionSymbol( gsClass, getSymbolTable(), strPropertyName, strVarIdentifier, varType );
setFunctionSymbol.getModifierInfo().setAnnotations( varStmt.getAnnotations() );
setFunctionSymbol.getModifierInfo().setDescription( varStmt.getModifierInfo().getDescription() );
setFunctionSymbol.setClassMember( true );
setFunctionSymbol.setStatic( varStmt.isStatic() );
if (dps.getSetterDfs() != null) {
setFunctionSymbol.setOverride(true);
setFunctionSymbol.setSuperDfs(dps.getSetterDfs());
}
dps.setSetterDfs( setFunctionSymbol );
verifyFunction( setFunctionSymbol, varStmt );
}
dps.setScriptPart( getOwner().getScriptPart() );
dps.setVarIdentifier( strVarIdentifier );
if( parseInitializer && match( null, "=", SourceCodeTokenizer.TT_OPERATOR, false ) )
{
pushParsingFieldInitializer( varStmt );
try
{
parseExpression( varType );
}
finally
{
popParsingFieldInitializer();
}
Expression expression = popExpression();
verifyComparable( varType, expression, true );
expression = possiblyWrapWithImplicitCoercion( expression, varType );
varStmt.setAsExpression( expression );
}
return dps;
}
void parseDelegateStatement( DelegateStatement delegateStmt, String strIdentifier )
{
if( delegateStmt.getModifierInfo() == null )
{
delegateStmt.setModifierInfo( new ModifierInfo(0) );
}
TypeLiteral typeLiteral = null;
if( match( null, ":", SourceCodeTokenizer.TT_OPERATOR ) )
{
parseTypeLiteral();
typeLiteral = (TypeLiteral)popExpression();
}
ICompilableType gsClass = getGosuClass();
List<IType> constituents = new ArrayList<IType>();
if( verify( delegateStmt, match( null, Keyword.KW_represents ), Res.MSG_EXPECTING_REPRESENTS ) )
{
do
{
getOwner().parseTypeLiteral();
TypeLiteral ifaceLiteral = (TypeLiteral)popExpression();
IType iface = ifaceLiteral.getType().getType();
if( !(iface instanceof ErrorType) )
{
verify( ifaceLiteral, iface.isInterface(), Res.MSG_DELEGATES_REPRESENT_INTERFACES_ONLY );
verify( ifaceLiteral, iface.isAssignableFrom( gsClass ), Res.MSG_CLASS_DOES_NOT_IMPL, iface );
verify( typeLiteral, typeLiteral == null || iface.isAssignableFrom( typeLiteral.getType().getType() ), Res.MSG_CLASS_DOES_NOT_IMPL, iface );
}
constituents.add( iface );
} while( match( null, ',' ) );
}
delegateStmt.setConstituents( constituents );
Expression eas = null;
if( match( null, "=", SourceCodeTokenizer.TT_OPERATOR ) )
{
pushParsingFieldInitializer( delegateStmt );
try
{
parseExpression( typeLiteral == null ? null : typeLiteral.getType().getType() );
}
finally
{
popParsingFieldInitializer();
}
eas = popExpression();
if( eas.hasParseExceptions() )
{
if( typeLiteral != null )
{
IType typeCast = typeLiteral.getType().getType();
eas.getParseExceptions().get( 0 ).setExpectedType( typeCast );
}
}
if( eas instanceof IParenthesizedExpression &&
((IParenthesizedExpression)eas).getExpression() instanceof TypeLiteral )
{
IType castType = eas.getType();
if(castType instanceof IMetaType) {
castType = ((IMetaType)castType).getType();
}
eas.addParseWarning( new ParseWarning( makeFullParserState(), Res.MSG_LIKELY_JAVA_CAST, castType.getName() ) );
}
}
// Create a local symbol for the identifier part of the 'var' statement
// (so it can be legally referenced in the containing scope).
IType type = null;
if( eas != null )
{
type = eas.getType();
if( type == null )
{
type = ErrorType.getInstance();
}
}
if( typeLiteral != null )
{
IType typeCast = typeLiteral.getType().getType();
if( eas != null && type != null )
{
verifyComparable( typeCast, eas, false, true );
}
type = typeCast;
}
else if( !(type instanceof ErrorType) )
{
if( constituents.isEmpty() )
{
type = ErrorType.getInstance();
}
else if( constituents.size() == 1 )
{
type = constituents.get( 0 );
}
else
{
type = CompoundType.get( new HashSet<IType>( constituents ) );
}
}
//if no type was found, we have added an error so give the symbol the error type
if( type == null )
{
type = ErrorType.getInstance();
}
ISymbol symbol = delegateStmt.getSymbol();
if( symbol == null )
{
symbol = new Symbol( strIdentifier, type, _symTable );
delegateStmt.setSymbol( symbol );
}
else
{
symbol.setType( type );
}
_symTable.putSymbol( symbol );
delegateStmt.setType( type );
eas = possiblyWrapWithImplicitCoercion( eas, type );
delegateStmt.setAsExpression( eas );
delegateStmt.setTypeLiteral( typeLiteral );
delegateStmt.setScriptPart( getScriptPart() );
pushStatement( delegateStmt );
}
private void parseSwitchStatement()
{
SwitchStatement switchStmt = new SwitchStatement();
verify( switchStmt, match( null, '(' ), Res.MSG_EXPECTING_LEFTPAREN_SWITCH );
parseExpression();
verify( switchStmt, match( null, ')' ), Res.MSG_EXPECTING_RIGHTPAREN_SWITCH );
verify( switchStmt, match( null, '{' ), Res.MSG_EXPECTING_OPEN_BRACE_FOR_SWITCH );
Expression e = popExpression();
switchStmt.setSwitchExpression( e );
_iBreakOk++;
try
{
parseCaseClauses( switchStmt );
parseDefaultClause( switchStmt, Arrays.asList( switchStmt.getCases() ) );
}
finally
{
_iBreakOk--;
}
verify( switchStmt, match( null, '}' ), Res.MSG_EXPECTING_CLOSE_BRACE_FOR_SWITCH );
pushStatement( switchStmt );
}
private void parseDoWhileStatement()
{
DoWhileStatement whileStmt = new DoWhileStatement();
_ctxInferenceMgr.pushLoopCompromised();
boolean loopStmtParsed;
try
{
loopStmtParsed = parseLoopStatement();
}
finally
{
_ctxInferenceMgr.popLoopCompromised();
}
if( verify( whileStmt, loopStmtParsed, Res.MSG_EXPECTING_STATEMENT ) )
{
verify( whileStmt, match( null, Keyword.KW_while ), Res.MSG_EXPECTING_WHILE_DO );
Statement stmt = popStatement();
verify( whileStmt, match( null, '(' ), Res.MSG_EXPECTING_LEFTPAREN_IF );
parseExpression( JavaTypes.pBOOLEAN() );
verify( whileStmt, match( null, ')' ), Res.MSG_EXPECTING_RIGHTPAREN_IF );
Expression e = popExpression();
whileStmt.setExpression( e );
whileStmt.setStatement( stmt );
}
pushStatement( whileStmt );
}
private void parseWhileStatement()
{
WhileStatement whileStmt = new WhileStatement();
_ctxInferenceMgr.pushLoopCompromised();
verify( whileStmt, match( null, '(' ), Res.MSG_EXPECTING_LEFTPAREN_WHILE );
parseExpression( JavaTypes.pBOOLEAN() );
verify( whileStmt, match( null, ')' ), Res.MSG_EXPECTING_RIGHTPAREN_WHILE );
Expression e = popExpression();
_ctxInferenceMgr.pushLastCtx();
try
{
whileStmt.setExpression( e );
if( verify( whileStmt, parseLoopStatement(), Res.MSG_EXPECTING_STATEMENT ) )
{
Statement stmt = popStatement();
whileStmt.setStatement( stmt );
}
pushStatement( whileStmt );
}
finally
{
_ctxInferenceMgr.popCtx( false );
_ctxInferenceMgr.popLoopCompromised();
}
}
private void parseForEachStatement( Token t )
{
ForEachStatement forEachStmt = new ForEachStatement( _symTable );
verify( forEachStmt, match( null, '(' ), Res.MSG_EXPECTING_LEFTPAREN_FE );
boolean bLoneInterval = false;
Expression ein = null;
if( !match( null, Keyword.KW_var ) )
{
// Handle case where no loop var is provided e.g., for( 1..3 ) ... similar to ruby's 3.times( ... )
int state = _tokenizer.mark();
int iLocationsCount = _locations.size();
parseExpression();
ein = popExpression();
bLoneInterval = JavaTypes.NUMBER_INTERVAL().isAssignableFrom( ein.getType() );
if( !bLoneInterval )
{
_tokenizer.restoreToMark( state );
removeLocationsFrom( iLocationsCount );
}
}
int iOffset = getTokenizer().getTokenStart();
int iLineNum = getTokenizer().getLineNumber();
int iColumn = getTokenizer().getTokenColumn();
if( !bLoneInterval && verify( forEachStmt, match( t, SourceCodeTokenizer.TT_WORD ), Res.MSG_EXPECTING_IDENTIFIER_FOREACH ) )
{
forEachStmt.setNameOffset( iOffset, t._strValue );
warn( forEachStmt, !Keyword.isKeyword( t._strValue ), Res.MSG_IMPROPER_USE_OF_KEYWORD, t._strValue );
}
else
{
t._strValue = null;
}
_symTable.pushScope();
try
{
String strIdentifier = t._strValue == null ? "" : t._strValue;
// var decl *expression* is only for editors
LocalVarDeclaration varDecl = null;
if( strIdentifier.length() > 0 )
{
varDecl = new LocalVarDeclaration( strIdentifier );
verify( forEachStmt, _symTable.getSymbol( strIdentifier ) == null, Res.MSG_VARIABLE_ALREADY_DEFINED, strIdentifier );
varDecl.setType( ErrorType.getInstance() ); // this is updated below
pushExpression( varDecl );
setLocation( iOffset, iLineNum, iColumn, strIdentifier == null, true );
popExpression();
}
if( !bLoneInterval )
{
verify( forEachStmt, match( null, Keyword.KW_in ), Res.MSG_EXPECTING_IN_FOREACH );
parseExpression();
ein = popExpression();
}
IType typeIn = ein.getType();
verify( ein, LoopStatement.isIteratorType( typeIn ) || typeIn instanceof ErrorType,
Res.MSG_EXPECTING_ARRAYTYPE_FOREACH, typeIn.getName() );
forEachStmt.setInExpression( ein );
forEachStmt.setStructuralIterable( StandardCoercionManager.isStructurallyAssignable_Laxed( JavaTypes.ITERABLE(), typeIn ) );
if( strIdentifier != null )
{
// Create a temporary symbol for the identifier part of the foreach statement
// (so it can be legally referenced in the statement).
IType typeIdentifier = LoopStatement.getArrayComponentType( typeIn );
if( strIdentifier.length() > 0 )
{
varDecl.setType( typeIdentifier );
}
Symbol symbol = new Symbol( bLoneInterval ? ("_unused_loop_var_" + iOffset) : strIdentifier, typeIdentifier, _symTable, null );
_symTable.putSymbol( symbol );
forEachStmt.setIdentifier( symbol );
}
boolean foundIterator = false;
if( match( null, Keyword.KW_iterator ) )
{
foundIterator = true;
parseIteratorVar( forEachStmt, typeIn );
}
if( match( null, Keyword.KW_index ) )
{
parseIndexVar( forEachStmt );
}
if( !foundIterator && match( null, Keyword.KW_iterator ) )
{
foundIterator = true;
parseIteratorVar( forEachStmt, typeIn );
}
if( bLoneInterval && foundIterator )
{
addError( forEachStmt, Res.MSG_FOREACH_ITERATOR_NOT_ALLOWED );
}
verify( forEachStmt, match( null, ')' ), Res.MSG_EXPECTING_RIGHTPAREN_FE );
if( strIdentifier != null )
{
_ctxInferenceMgr.pushLoopCompromised();
boolean bHasBody;
try
{
bHasBody = parseLoopStatement();
}
finally
{
_ctxInferenceMgr.popLoopCompromised();
}
verify( forEachStmt, bHasBody, Res.MSG_EXPECTING_STATEMENT );
if( bHasBody )
{
Statement stmt = popStatement();
forEachStmt.setStatement( stmt );
}
}
pushStatement( forEachStmt );
}
finally
{
_symTable.popScope();
}
}
private void parseIndexVar( ForEachStatement forEachStmt ) {
int iOffset;
int iLineNum;
int iColumn;
iOffset = getTokenizer().getTokenStart();
iLineNum = getTokenizer().getLineNumber();
iColumn = getTokenizer().getTokenColumn();
Token Tindex = new Token();
if( verify( forEachStmt, match( Tindex, SourceCodeTokenizer.TT_WORD ), Res.MSG_EXPECTING_IDENTIFIER_FOREACH_INDEX ) )
{
warn( forEachStmt, !Keyword.isKeyword( Tindex._strValue ), Res.MSG_IMPROPER_USE_OF_KEYWORD, Tindex._strValue );
}
String strIndexIdentifier = Tindex._strValue;
// index decl *expression* is only for editors
LocalVarDeclaration varIndexDecl = new LocalVarDeclaration( strIndexIdentifier == null ? "#err" : strIndexIdentifier );
varIndexDecl.setType(JavaTypes.pINT());
verify( forEachStmt, _symTable.getSymbol( strIndexIdentifier ) == null, Res.MSG_VARIABLE_ALREADY_DEFINED, strIndexIdentifier );
forEachStmt.setIndexNameOffset( Tindex.getTokenStart() );
// Create a temporary symbol for the identifier part of the foreach statement's index
// (so it can be legally referenced in the statement).
Symbol indexIdentifier = new TypedSymbol( strIndexIdentifier, JavaTypes.pINT(), _symTable, null, SymbolType.FOREACH_VARIABLE );
indexIdentifier.setFinal( true );
_symTable.putSymbol( indexIdentifier );
forEachStmt.setIndexIdentifier( indexIdentifier );
pushExpression( varIndexDecl );
setLocation( iOffset, iLineNum, iColumn, strIndexIdentifier == null, false );
popExpression();
}
private void parseIteratorVar( ForEachStatement forEachStmt, IType type )
{
int iOffset;
int iLineNum;
int iColumn;
iOffset = getTokenizer().getTokenStart();
iLineNum = getTokenizer().getLineNumber();
iColumn = getTokenizer().getTokenColumn();
Token Titer = new Token();
if( verify( forEachStmt, match( Titer, SourceCodeTokenizer.TT_WORD ), Res.MSG_EXPECTING_IDENTIFIER_FOREACH_ITERATOR ) )
{
warn( forEachStmt, !Keyword.isKeyword( Titer._strValue ), Res.MSG_IMPROPER_USE_OF_KEYWORD, Titer._strValue );
}
String strIterIdentifier = Titer._strValue;
boolean bIterable = JavaTypes.ITERABLE().isAssignableFrom( type ) || forEachStmt.isStructuralIterable();
verify( forEachStmt, bIterable, Res.MSG_ITERATOR_SYMBOL_ONLY_SUPPORTED_ON_ITERABLE_OBJECTS );
IType iterType;
if( bIterable )
{
IType iterParam = LoopStatement.getArrayComponentType( type );
iterType = JavaTypes.ITERATOR().getParameterizedType( iterParam );
}
else
{
iterType = ErrorType.getInstance();
}
// index decl *expression* is only for editors
LocalVarDeclaration varIteratorDecl = new LocalVarDeclaration( strIterIdentifier == null ? "#err" : strIterIdentifier );
varIteratorDecl.setType( iterType );
verify( forEachStmt, _symTable.getSymbol( strIterIdentifier ) == null, Res.MSG_VARIABLE_ALREADY_DEFINED, strIterIdentifier );
forEachStmt.setIndexNameOffset( Titer.getTokenStart() );
Symbol iteratorIdentifier = new TypedSymbol( strIterIdentifier, iterType, _symTable, null, SymbolType.FOREACH_VARIABLE );
iteratorIdentifier.setFinal( true );
_symTable.putSymbol( iteratorIdentifier );
forEachStmt.setIteratorIdentifier( iteratorIdentifier );
pushExpression(varIteratorDecl);
setLocation(iOffset, iLineNum, iColumn, strIterIdentifier == null, false);
popExpression();
}
private void parseReturnStatement()
{
ReturnStatement returnStmt = new ReturnStatement();
IType returnType = null;
if( isParsingBlock() )
{
returnType = _blockReturnTypeStack.peek();
}
else if( isParsingFunction() )
{
if( _bProgramCallFunction && getGosuClass() instanceof IGosuProgram )
{
returnType = ((IGosuProgram)getGosuClass()).getExpectedReturnType();
}
if( returnType == null )
{
returnType = peekParsingFunction().getReturnType();
}
}
else if ( isParsingProgram() )
{
returnType = peekParsingProgram().getDeclaredReturnType();
}
verify( returnStmt, _iReturnOk > 0, Res.MSG_RETURN_NOT_ALLOWED_HERRE );
if( match( null, ';' ) || match( null, null, '}', true ) )
{
boolean bShouldNotHaveReturnValue = _bProgramCallFunction || returnType == null || returnType == GosuParserTypes.NULL_TYPE();
FunctionType functionType = isParsingFunction() ? peekParsingFunction() : null;
boolean bConstructor = !bShouldNotHaveReturnValue && functionType != null && functionType.getMethodOrConstructorInfo() instanceof IConstructorInfo;
verify( returnStmt, bShouldNotHaveReturnValue || bConstructor, Res.MSG_MISSING_RETURN_VALUE );
setReturnNullExpr( returnStmt, _bProgramCallFunction );
}
else if( returnType != GosuParserTypes.NULL_TYPE() && !inConstructorCtx() )
{
parseExpression( returnType );
Expression retValue = popExpression();
if( returnType != null && !isParsingBlock() )
{
Expression actualReturnExpr = retValue;
if( retValue instanceof ImplicitTypeAsExpression )
{
actualReturnExpr = ((ImplicitTypeAsExpression)retValue).getLHS();
}
boolean bVoidReturnType = actualReturnExpr.getType() == GosuParserTypes.NULL_TYPE() &&
!(actualReturnExpr instanceof NullExpression);
if( bVoidReturnType )
{
//noinspection ThrowableResultOfMethodCallIgnored
actualReturnExpr.removeParseWarning( Res.MSG_USING_VOID_RETURN_TYPE_FROM_NON_NULL_EXPR );
verify( actualReturnExpr, !bVoidReturnType, Res.MSG_USING_VOID_RETURN_TYPE_FROM_NON_NULL_EXPR );
}
}
returnStmt.setValue( retValue );
if( (returnType == null || _bProgramCallFunction) && retValue.getType() == JavaTypes.pVOID() )
{
retValue.setType( JavaTypes.OBJECT() );
}
}
else
{
setReturnNullExpr( returnStmt, _bProgramCallFunction );
}
pushStatement( returnStmt );
}
private boolean inConstructorCtx()
{
if( isParsingConstructor() )
{
return _blocks == null || _blocks.isEmpty();
}
return false;
}
private void setReturnNullExpr( ReturnStatement returnStmt, boolean bProgramCallFunction )
{
if( bProgramCallFunction )
{
NullExpression nullExpr = new NullExpression();
nullExpr.setType( JavaTypes.OBJECT() );
returnStmt.setValue( nullExpr );
}
else
{
returnStmt.setValue( NullExpression.instance() );
}
}
private void parseThrowStatement()
{
parseExpression();
Expression e = popExpression();
if( !JavaTypes.THROWABLE().isAssignableFrom( e.getType() ) )
{
verifyComparable( JavaTypes.STRING(), e, true );
e = possiblyWrapWithImplicitCoercion( e, JavaTypes.STRING() );
}
ThrowStatement throwStmt = new ThrowStatement();
throwStmt.setExpression( e );
pushStatement( throwStmt );
}
private void parseTryCatchFinallyStatement( Token t )
{
TryCatchFinallyStatement tryCatchFinallyStmt = new TryCatchFinallyStatement();
if( verify( tryCatchFinallyStmt, match( null, null, '{', true ), Res.MSG_EXPECTING_LEFTBRACE_STMTBLOCK ) )
{
parseStatement();
Statement tryStmt = popStatement();
tryCatchFinallyStmt.setTryStatement( tryStmt );
}
for( int iCatchOffset = _tokenizer.getTokenStart(),
iCatchLineNum = _tokenizer.getLineNumber(),
iCatchColumn = getTokenizer().getTokenColumn();
match( null, Keyword.KW_catch );
iCatchOffset = _tokenizer.getTokenStart(),
iCatchLineNum = _tokenizer.getLineNumber(),
iCatchColumn = getTokenizer().getTokenColumn())
{
CatchClause catchClause = new CatchClause();
_symTable.pushScope();
try
{
verify( tryCatchFinallyStmt, match( null, '(' ), Res.MSG_EXPECTING_LEFTPAREN_CATCH );
match( null, Keyword.KW_var );
int iOffset = _tokenizer.getTokenStart();
int iLineNum = _tokenizer.getLineNumber();
int iColumn = getTokenizer().getTokenColumn();
if( verify( tryCatchFinallyStmt, match( t, SourceCodeTokenizer.TT_WORD ), Res.MSG_EXPECTING_IDENTIFIER_CATCH ) )
{
catchClause.setNameOffset( iOffset, t._strValue );
}
String strIdentifier = t._strValue;
LocalVarDeclaration localVarDecl = new LocalVarDeclaration( strIdentifier == null ? "#err" : strIdentifier );
IType iIntrinsicType;
if( match( null, ":", SourceCodeTokenizer.TT_OPERATOR ) )
{
parseTypeLiteral();
TypeLiteral typeLiteral = (TypeLiteral)popExpression();
iIntrinsicType = typeLiteral.getType().getType();
verify( typeLiteral, (JavaTypes.THROWABLE().isAssignableFrom( iIntrinsicType ) && !(iIntrinsicType instanceof ITypeVariableType)) || iIntrinsicType instanceof IErrorType,
Res.MSG_NOT_A_VALID_EXCEPTION_TYPE, iIntrinsicType.getName() );
}
else
{
if( !CommonServices.getEntityAccess().getLanguageLevel().supportsNakedCatchStatements() )
{
warn( localVarDecl, false, Res.MSG_EXPLICIT_TYPE_RECOMMENDED_FOR_CATCH_STMTS );
}
iIntrinsicType = null;
}
pushExpression( localVarDecl );
localVarDecl.setType( iIntrinsicType == null ? ErrorType.getInstance() : iIntrinsicType );
setLocation( iOffset, iLineNum, iColumn, strIdentifier == null, false );
popExpression();
verify( tryCatchFinallyStmt, match( null, ')' ), Res.MSG_EXPECTING_RIGHTPAREN_CATCH );
verify( tryCatchFinallyStmt, _symTable.getSymbol( strIdentifier ) == null, Res.MSG_VARIABLE_ALREADY_DEFINED, strIdentifier );
Symbol symbol = new TypedSymbol( strIdentifier, iIntrinsicType != null ? iIntrinsicType : JavaTypes.THROWABLE(), _symTable, null, SymbolType.CATCH_VARIABLE);
_symTable.putSymbol( symbol );
if( tryCatchFinallyStmt.getCatchStatements() != null )
{
for( CatchClause c : tryCatchFinallyStmt.getCatchStatements() )
{
IType earlierCatchType = c.getCatchType() != null ? c.getCatchType() : CatchClause.getNakedCatchExceptionType();
IType currentCatchType = iIntrinsicType != null ? iIntrinsicType : CatchClause.getNakedCatchExceptionType();
if( earlierCatchType.isAssignableFrom( currentCatchType ) )
{
verify( catchClause, false, Res.MSG_CATCH_STMT_CANNOT_EXECUTE );
}
}
}
if( verify( tryCatchFinallyStmt, match( null, null, '{', true ), Res.MSG_EXPECTING_LEFTBRACE_STMTBLOCK ) )
{
parseStatement();
Statement catchStmt = popStatement();
catchClause.init( iIntrinsicType, catchStmt, symbol );
pushStatement( catchClause );
setLocation( iCatchOffset, iCatchLineNum, iCatchColumn );
popStatement();
tryCatchFinallyStmt.addCatchClause( catchClause );
}
}
finally
{
_symTable.popScope();
}
}
if( match( null, Keyword.KW_finally ) )
{
int originaliBreakOk = _iBreakOk;
_iBreakOk = 0;
int originaliContinueOk = _iContinueOk;
_iContinueOk = 0;
int originalReturnOk = _iReturnOk;
_iReturnOk = 0;
try
{
if( verify( tryCatchFinallyStmt, match( null, null, '{', true ), Res.MSG_EXPECTING_LEFTBRACE_STMTBLOCK ) )
{
parseStatement();
Statement finallyStmt = popStatement();
tryCatchFinallyStmt.setFinallyStatement( finallyStmt );
}
}
finally
{
_iBreakOk = originaliBreakOk;
_iContinueOk = originaliContinueOk;
_iReturnOk = originalReturnOk;
}
}
if( tryCatchFinallyStmt.getCatchStatements() == null &&
tryCatchFinallyStmt.getFinallyStatement() == null )
{
tryCatchFinallyStmt.addParseException( new ParseException( makeFullParserState(), Res.MSG_CATCH_OR_FINALLY_REQUIRED ) );
}
pushStatement( tryCatchFinallyStmt );
}
private void parseIfStatement()
{
IfStatement ifStmt = new IfStatement();
verify( ifStmt, match( null, '(' ), Res.MSG_EXPECTING_LEFTPAREN_IF );
parseExpression( JavaTypes.pBOOLEAN() );
Expression e = popExpression();
// Bad assignment statement in if clause (mistaken for equality "==")
if( match( null, "=", SourceCodeTokenizer.TT_OPERATOR ) )
{
parseExpression();
popExpression();
verify( ifStmt, false, Res.MSG_ASSIGNMENT_IN_IF_STATEMENT);
}
verify( ifStmt, match( null, ')' ), Res.MSG_EXPECTING_RIGHTPAREN_IF );
getSymbolTable().pushScope();
boolean statementParsed = false;
try
{
_ctxInferenceMgr.pushLastCtx();
statementParsed = parseStatement();
}
finally
{
getSymbolTable().popScope();
_ctxInferenceMgr.popCtx( false );
}
verify( ifStmt, statementParsed, Res.MSG_EXPECTING_STATEMENT );
if( statementParsed )
{
Statement stmt = popStatement();
ifStmt.setExpression( e );
ifStmt.setStatement( stmt );
// Swallow a semicolon if necessary
match( null, ';' );
if( match( null, Keyword.KW_else ) )
{
getSymbolTable().pushScope();
boolean elseStmtParsed;
try
{
elseStmtParsed = parseStatement();
}
finally
{
getSymbolTable().popScope();
}
verify( ifStmt, elseStmtParsed, Res.MSG_EXPECTING_STATEMENT );
if( elseStmtParsed )
{
ifStmt.setElseStatement( popStatement() );
}
}
//## todo: in Diamond remove usage of 'unless' clause in if-statement
if( match( null, Keyword.KW_except ) || match( null, Keyword.KW_unless ) )
{
verify( ifStmt, match( null, '(' ), Res.MSG_EXPECTING_LEFTPAREN_EXCEPT );
parseExpression();
verify( ifStmt, match( null, ')' ), Res.MSG_EXPECTING_RIGHTPAREN_EXCEPT );
Expression ee = popExpression();
ifStmt.setExcept( ee );
}
}
pushStatement( ifStmt );
}
private void parseUsingStatement()
{
UsingStatement usingStmt = new UsingStatement();
verify( usingStmt, match( null, '(' ), Res.MSG_EXPECTING_LEFTPAREN_USING );
_symTable.pushScope();
try
{
parseVarStatementsInUsingStatement( usingStmt );
if( usingStmt.getVarStatements().isEmpty() )
{
parseExpression();
Expression expr = popExpression();
usingStmt.setExpression( expr );
verifyTypeForUsingStatementPredicate( expr, expr.getType() );
}
verify( usingStmt, match( null, ')' ), Res.MSG_EXPECTING_RIGHTPAREN_USING );
if( verify( usingStmt, match( null, null, '{', true ), Res.MSG_EXPECTING_LEFTBRACE_STMTBLOCK ) )
{
parseStatement();
usingStmt.setStatement( popStatement() );
}
if( match( null, Keyword.KW_finally ))
{
if( verify( usingStmt, match( null, null, '{', true ), Res.MSG_EXPECTING_LEFTBRACE_STMTBLOCK ) )
{
parseStatement();
Statement finallyStmt = popStatement();
usingStmt.setFinallyStatement( finallyStmt );
}
}
pushStatement( usingStmt );
}
finally
{
_symTable.popScope();
}
}
private void parseVarStatementsInUsingStatement( UsingStatement usingStmt )
{
Token T = new Token();
List<IVarStatement> varStmts = new ArrayList<IVarStatement>();
int iOffset;
int iLineNum;
int iColumn;
do
{
iOffset = getTokenizer().getTokenStart();
iLineNum = getTokenizer().getLineNumber();
iColumn = getTokenizer().getTokenColumn();
if( match( null, Keyword.KW_var ) )
{
VarStatement varStmt = new VarStatement();
int iNameOffset = getTokenizer().getTokenStart();
if( verify( varStmt, match( T, SourceCodeTokenizer.TT_WORD ), Res.MSG_EXPECTING_IDENTIFIER_VAR ) )
{
varStmt.setNameOffset( iNameOffset, null );
}
else
{
T._strValue = null;
}
parseVarStatement( varStmt, T, false );
setLocation( iOffset, iLineNum, iColumn );
varStmt = (VarStatement)popStatement();
verifyTypeForUsingStatementPredicate( varStmt, varStmt.getType() );
varStmts.add( varStmt );
}
else
{
break;
}
}while( match( null, ',' ) );
if( varStmts.size() > 0 )
{
usingStmt.setVarStatements( varStmts );
// Add a synthetic noop-stmt to avoid "multiple stmts on same line" warning wrt
// the using stmt's var stmts and its body e.g., if the stmt body stmt list puts
// an opening brace on same line as vars
pushStatement( new NoOpStatement() );
setLocation( getTokenizer().getTokenStart(), iLineNum, getTokenizer().getTokenColumn(), true );
popStatement();
}
}
private void verifyTypeForUsingStatementPredicate( ParsedElement pe, IType type )
{
maybeRemoveIMonitorLockError( pe );
boolean bAssignableFromUsingType =
isAssignableFrom(JavaTypes.LOCK(), type) ||
isAssignableFrom(JavaTypes.getJreType(Closeable.class), type ) ||
isAssignableFrom(JavaTypes.getGosuType( IReentrant.class ), type ) ||
isAssignableFrom(GosuTypes.IDISPOSABLE(), type ) ||
type == GosuTypes.IMONITORLOCK() ||
type.getTypeInfo().getMethod( "dispose" ) != null ||
type.getTypeInfo().getMethod( "close" ) != null ||
(type.getTypeInfo().getMethod( "lock" ) != null && type.getTypeInfo().getMethod( "unlock" ) != null);
verify( pe, bAssignableFromUsingType, Res.MSG_BAD_TYPE_FOR_USING_STMT );
}
private boolean isAssignableFrom(IType type1, IType type2) {
return type1 != null && type1.isAssignableFrom( type2 );
}
private void maybeRemoveIMonitorLockError( ParsedElement pe )
{
if( pe instanceof TypeAsExpression )
{
ParseTree after = pe.getLocation().getChildAfter(((TypeAsExpression) pe).getLHS().getLocation());
if( after.getParsedElement() instanceof TypeLiteral )
{
//noinspection ThrowableResultOfMethodCallIgnored
after.getParsedElement().removeParseException(Res.MSG_IMONITOR_LOCK_SHOULD_ONLY_BE_USED_WITHIN_USING_STMTS);
}
}
else if( pe instanceof VarStatement )
{
maybeRemoveIMonitorLockError( ((VarStatement)pe).getAsExpression() );
}
else if( pe instanceof ParenthesizedExpression )
{
maybeRemoveIMonitorLockError( ((ParenthesizedExpression)pe).getExpression() );
}
}
private void parseStatementBlock()
{
parseStatementBlock( true );
}
private void parseStatementBlock( boolean bMatchClosingBrace )
{
_symTable.pushScope();
if( !bMatchClosingBrace )
{
decStatementDepth();
}
try
{
ArrayList<Statement> statements = new ArrayList<Statement>();
parseStatementsAndDetectUnreachable( statements );
StatementList stmtList = new StatementList( _symTable );
verify( stmtList, !bMatchClosingBrace || match( null, '}' ), Res.MSG_EXPECTING_RIGHTBRACE_STMTBLOCK );
stmtList.setStatements( statements );
pushStatement( isDontOptimizeStatementLists() ? stmtList : stmtList.getSelfOrSingleStatement() );
}
finally
{
if( !bMatchClosingBrace )
{
incStatementDepth();
}
_symTable.popScope();
}
}
void parseNamespaceStatement( Token t )
{
if( isEditorParser() )
{
parseNamespaceStatement_editor( t );
}
else
{
parseNamespaceStatement_normal(t);
}
}
void parseNamespaceStatement_editor( Token t )
{
NamespaceStatement namespaceStmt = new NamespaceStatement();
parseExpression();
Expression namespace = popExpression();
//noinspection ThrowableResultOfMethodCallIgnored
namespace.clearParseExceptions(); //Res.MSG_EXPECTING_TYPE_TO_FOLLOW_PACKAGE_NAME );
IGosuClassInternal gsClass = (IGosuClassInternal)getScriptPart().getContainingType();
String strNamespace = namespace.toString();
verify( namespaceStmt, strNamespace.equals( gsClass.getNamespace() ), Res.MSG_WRONG_NAMESPACE, strNamespace, gsClass.getNamespace() );
setNamespace( strNamespace );
namespaceStmt.setNamespace( strNamespace );
pushStatement( namespaceStmt );
while( match( null, ';' ) )
{
//pushStatement( new NoOpStatement() );
}
}
void parseNamespaceStatement_normal( Token t )
{
NamespaceStatement namespaceStmt = new NamespaceStatement();
verify( namespaceStmt, match( t, SourceCodeTokenizer.TT_WORD ), Res.MSG_EXPECTING_TYPELITERAL_OR_NAMESPACE );
parseDotPathWord( t );
String strNamespace = t._strValue;
IGosuClassInternal gsClass = (IGosuClassInternal)getScriptPart().getContainingType();
verify( namespaceStmt, strNamespace.equals( gsClass.getNamespace() ), Res.MSG_WRONG_NAMESPACE, strNamespace, gsClass.getNamespace() );
setNamespace( strNamespace );
namespaceStmt.setNamespace( strNamespace );
pushStatement( namespaceStmt );
while( match( null, ';' ) )
{
//pushStatement( new NoOpStatement() );
}
}
@Override
public IUsesStatementList parseUsesStatementList( boolean resolveTypes )
{
return parseUsesStatementList( resolveTypes, new Token() );
}
public UsesStatementList parseUsesStatementList( boolean bResolveUsesTypes, Token t )
{
int iOffset = getTokenizer().getTokenStart();
int iLineNum = getTokenizer().getLineNumber();
int iColumn = getTokenizer().getTokenColumn();
int iUsesListOffset = iOffset;
int iUsesListLineNum = iLineNum;
if( match( null, Keyword.KW_uses, true ) )
{
List<IUsesStatement> usesList = new ArrayList<IUsesStatement>();
UsesStatementList stmtList = new UsesStatementList();
stmtList.setUsesStatements( usesList );
while( match( null, Keyword.KW_uses ) )
{
getOwner().parseUsesStatement( t, bResolveUsesTypes );
setLocation( iOffset, iLineNum, iColumn );
UsesStatement stmt = (UsesStatement)popStatement();
//noinspection ThrowableResultOfMethodCallIgnored
stmt.removeParseWarningRecursively( Res.MSG_DEPRECATED_MEMBER ); // don't show these in uses statements
IUsesStatement duplicate = stmtList.conflicts( stmt );
if( duplicate != null )
{
if( warn( stmt, !duplicate.getTypeName().equals( stmt.getTypeName() ), Res.MSG_USES_STMT_DUPLICATE ) )
{
verify( stmt, false, Res.MSG_USES_STMT_CONFLICT, duplicate.getTypeName() );
}
}
usesList.add( stmt );
getOwner().checkInstruction( true );
iOffset = getTokenizer().getTokenStart();
iLineNum = getTokenizer().getLineNumber();
}
pushStatement( stmtList );
setLocation( iUsesListOffset, iUsesListLineNum, iColumn, true );
popStatement();
return stmtList;
}
return null;
}
void parseUsesStatement( Token t )
{
parseUsesStatement(t, true);
}
void parseUsesStatement( Token t, boolean bResolveTypes )
{
if( isEditorParser() )
{
parseUsesStatement_editor(t, bResolveTypes);
}
else
{
parseUsesStatement_normal(t, bResolveTypes);
}
}
void parseUsesStatement_editor( Token t, boolean bResolveTypes )
{
UsesStatement usesStmt = new UsesStatement();
parseTypeLiteral();
TypeLiteral typeLiteral = (TypeLiteral)popExpression();
t._strValue = typeLiteral.getType().getType() instanceof ErrorType && typeLiteral.getPackageExpression() != null
? typeLiteral.getPackageExpression().toString()
: TypeLord.getPureGenericType( typeLiteral.getType().getType() ).getName();
boolean bForwardRefToInnerClass =
getGosuClass() instanceof IGosuClassInternal &&
t._strValue != null && t._strValue.startsWith( getGosuClass().getName() );
verify( usesStmt, t._strValue == null || !t._strValue.endsWith( "]" ), Res.MSG_BAD_NAMESPACE, t._strValue );
if( !bForwardRefToInnerClass || ((IGosuClassInternal)getGosuClass()).isHeaderCompiled() )
{
if( t._strValue.endsWith( "*" ) && match( null, "*", SourceCodeTokenizer.TT_OPERATOR ) )
{
typeLiteral.clearParseExceptions();
usesStmt.setTypeName( t._strValue );
if( verify( usesStmt, t._strValue.endsWith( ".*" ), Res.MSG_BAD_NAMESPACE, t._strValue ) )
{
String namespace = t._strValue.substring( 0, t._strValue.length() - 2 );
IType type = TypeSystem.getNamespace( namespace );
if( type == null )
{
type = TypeSystem.getByFullNameIfValid( namespace );
}
verify( usesStmt, type != null, Res.MSG_BAD_NAMESPACE, namespace );
}
getTypeUsesMap().addToTypeUses( usesStmt );
}
else
{
if( bResolveTypes )
{
String strTypeName = TypeLord.getPureGenericType( typeLiteral.getType().getType() ).getName();
usesStmt.setTypeName( strTypeName );
if( typeLiteral.hasParseExceptions() )
{
IParseIssue first = typeLiteral.getParseExceptions().get( 0 );
usesStmt.addParseException( first );
//noinspection ThrowableResultOfMethodCallIgnored
typeLiteral.removeParseException( first.getMessageKey() );
}
else
{
getTypeUsesMap().addToTypeUses( usesStmt );
}
}
else
{
usesStmt.setTypeName( t._strValue );
getTypeUsesMap().addToTypeUses( usesStmt );
}
}
}
pushStatement( usesStmt );
while( match( null, ';' ) )
{
//pushStatement( new NoOpStatement() );
}
}
void parseUsesStatement_normal( Token t, boolean bResolveTypes )
{
UsesStatement usesStmt = new UsesStatement();
int iOffset = _tokenizer.getTokenStart();
int iLineNum = _tokenizer.getLineNumber();
int iColumn = getTokenizer().getTokenColumn();
verify( usesStmt, match( t, SourceCodeTokenizer.TT_WORD ), Res.MSG_EXPECTING_TYPELITERAL_OR_NAMESPACE );
parseDotPathWord( t );
boolean bForwardRefToInnerClass =
getGosuClass() instanceof IGosuClassInternal &&
t._strValue != null && t._strValue.startsWith( getGosuClass().getName() );
if( !bForwardRefToInnerClass || ((IGosuClassInternal)getGosuClass()).isHeaderCompiled() )
{
if( match( null, "*", SourceCodeTokenizer.TT_OPERATOR ) )
{
usesStmt.setTypeName( t._strValue + "*" );
if( verify( usesStmt, t._strValue.endsWith( "." ), Res.MSG_BAD_NAMESPACE, t._strValue ) )
{
String namespace = t._strValue.substring( 0, t._strValue.length() - 1 );
IType type = TypeSystem.getNamespace( namespace );
if( type == null )
{
type = TypeSystem.getByFullNameIfValid( namespace );
}
verify( usesStmt, type != null, Res.MSG_BAD_NAMESPACE, namespace );
}
getTypeUsesMap().addToTypeUses( usesStmt );
}
else
{
if( bResolveTypes )
{
TypeLiteral typeLiteral = resolveTypeLiteral( t, false, false );
pushExpression( typeLiteral );
setLocation( iOffset, iLineNum, iColumn );
popExpression();
IType type = TypeLord.getPureGenericType( (typeLiteral.getType()).getType() );
String strTypeName = type.getName();
usesStmt.setTypeName( strTypeName );
if( typeLiteral.hasParseExceptions() )
{
IParseIssue first = typeLiteral.getParseExceptions().get( 0 );
usesStmt.addParseException( first );
//noinspection ThrowableResultOfMethodCallIgnored
typeLiteral.removeParseException(first.getMessageKey());
}
else
{
getTypeUsesMap().addToTypeUses( usesStmt );
}
}
else
{
usesStmt.setTypeName( t._strValue );
getTypeUsesMap().addToTypeUses( usesStmt );
}
}
}
pushStatement( usesStmt );
while( match( null, ';' ) )
{
//pushStatement( new NoOpStatement() );
}
}
void parseCaseClauses( SwitchStatement switchStmt )
{
List<CaseClause> cases = new ArrayList<CaseClause>();
while( parseCaseClause( switchStmt, cases ) )
{ /* do nothing */ }
switchStmt.setCases( cases.toArray( new CaseClause[cases.size()] ) );
}
boolean parseCaseClause( SwitchStatement switchStmt, List<CaseClause> cases )
{
int iOffset = getTokenizer().getTokenStart();
int iLine = getTokenizer().getLineNumber();
int iColumn = getTokenizer().getTokenColumn();
if( !match( null, Keyword.KW_case ) )
{
return false;
}
verify( switchStmt, switchStmt.getDefaultStatements() == null, Res.MSG_CASE_CLAUSE_MAY_NOT_FOLLOW_DEFAULT_CLAUSE );
if( !cases.isEmpty() )
{
warnIfCaseNotTerminated( cases.get( cases.size()-1 ).getStatements() );
}
Expression switchExpr = switchStmt.getSwitchExpression();
parseExpression( switchExpr.getType() );
verify( switchStmt, match( null, ":", SourceCodeTokenizer.TT_OPERATOR ), Res.MSG_EXPECTING_CASE_COLON );
Expression e = popExpression();
verifyCaseIsUnique( e, cases );
boolean typeInfered = switchExpr instanceof TypeOfExpression && e instanceof TypeLiteral && isIsolatedCase( cases );
List<Statement> statements = new ArrayList<Statement>();
CaseClause caseClause = new CaseClause( e, statements );
cases.add( caseClause );
if( typeInfered )
{
TypeOfExpression toe = (TypeOfExpression)switchExpr;
_ctxInferenceMgr.pushCtx();
_ctxInferenceMgr.updateType( toe.getExpression(), (IType)e.evaluate() );
}
_symTable.pushScope();
boolean typeInferenceCancelled = false;
try
{
for( Statement stmt = null; true; )
{
// must cancel inference before the next case or default clause is parsed
if( match( null, Keyword.KW_case, true ) ||
match( null, Keyword.KW_default, true ) )
{
if( typeInfered )
{
_ctxInferenceMgr.popCtx( false );
typeInferenceCancelled = true;
}
break;
}
if( !parseStatement() )
{
break;
}
stmt = popStatementAndDetectUnreachable( stmt, statements );
}
}
finally
{
_symTable.popScope();
if( typeInfered && !typeInferenceCancelled )
{
_ctxInferenceMgr.popCtx( false );
}
}
pushExpression( caseClause );
setLocation( iOffset, iLine, iColumn, true );
popExpression();
return true;
}
private void verifyCaseIsUnique( Expression e, List<CaseClause> cases )
{
if( e.getType() instanceof IErrorType || !e.isCompileTimeConstant() )
{
return; // Can't verify this
}
Object value;
try {
value = e.evaluate();
if( value == null && e instanceof TypeAsExpression ) {
// sometimes a coercer isn't available in a compile-time environment, so bail if null
return;
}
}
catch( Exception err ) {
return;
}
for( CaseClause cc: cases )
{
Expression expr = cc.getExpression();
if( expr != null && expr.isCompileTimeConstant() ) {
Object csr;
try {
csr = expr.evaluate();
}
catch( Exception err ) {
// skip over evaluation errors
continue;
}
verify( e,
!GosuObjectUtil.equals( csr, value ),
Res.MSG_DUPLICATE_CASE_EXPRESSION );
}
}
}
private boolean isIsolatedCase( List<CaseClause> cases )
{
if( cases.isEmpty() )
{
return true;
}
else
{
CaseClause caseClause = cases.get( cases.size() - 1 );
List<Statement> statements = caseClause.getStatements();
int i;
for( i = statements.size()-1; i >= 0 && statements.get( i ) instanceof NoOpStatement; i-- )
{ /* Loop until we find the index of the last non-noop statement */ }
if( i >= 0 )
{
boolean[] bAbsolute = {false};
return statements.get( i ).getLeastSignificantTerminalStatement( bAbsolute ) != null && bAbsolute[0];
}
}
return false;
}
private void warnIfCaseNotTerminated( List<Statement> statements )
{
int i;
for( i = statements.size()-1; i >= 0 && statements.get( i ) instanceof NoOpStatement; i-- )
{ /* Loop until we find the index of the last non-noop statement */ }
if( i >= 0 )
{
Statement lastStmt = statements.get( i );
boolean[] bAbsolute = {false};
verifyOrWarn( lastStmt, lastStmt.hasParseExceptions() || lastStmt.getLeastSignificantTerminalStatement( bAbsolute ) != null && bAbsolute[0],
true, Res.MSG_NONTERMINAL_CASE_CLAUSE );
}
}
boolean parseDefaultClause( SwitchStatement switchStmt, List<CaseClause> cases )
{
if( !match( null, Keyword.KW_default ) )
{
return false;
}
if( cases.size() > 0 )
{
warnIfCaseNotTerminated( cases.get( cases.size()-1 ).getStatements() );
}
verify( switchStmt, match( null, ":", SourceCodeTokenizer.TT_OPERATOR ), Res.MSG_EXPECTING_CASE_COLON );
verify( switchStmt, switchStmt.getDefaultStatements() == null, Res.MSG_MULTIPLE_DEFAULT_CLAUSES_NOT_PERMITTED );
List<Statement> defaultStatements = new ArrayList<Statement>();
parseStatementsAndDetectUnreachable( defaultStatements );
switchStmt.setDefaultStatements( defaultStatements );
return true;
}
void checkInstruction( boolean bProcessDirectives )
{
// If the tokenizer has an 'instructor', it may be in a state where
// it needs to analyze tokens in a separate context e.g., in a template
// an expressoin of the form <%=foo.bar%>. Thus "analyze separately"
// means to parse these as expressions and throw them away. Note we can
// throw them away because instructors are indicative of pure analysis
// of the source; the object code is of no interest e.g., a template
// verifier may use an instructor.
while( !_tokenizer.isEOF() && (_tokenizer.isAnalyzingSeparately() || _tokenizer.isAnalyzingDirective()) )
{
if( _tokenizer.isAnalyzingDirective() )
{
parseDirective( bProcessDirectives );
}
else if( !(getGosuClass() instanceof IGosuClass) ||
!((CompilationState)((IGosuClass)getGosuClass()).getCompilationState()).isReparsingHeader() )
{
// Only eat <%= xx %> expressions *after* the header is parsed during definition phase.
// They need to be parsed within the program evaluate() body parsing phase, which includes class members etc.
int iOffset = _tokenizer.getTokenStart();
parseExpression();
Expression e = popExpression();
clearExpressionInTemplateUnlessParsingEvaluateFunctionBody( e );
if( iOffset == _tokenizer.getTokenStart() )
{
break;
}
}
else
{
break;
}
}
}
private void clearExpressionInTemplateUnlessParsingEvaluateFunctionBody( Expression e )
{
// Retain the parse tree (location) for template expressions when we are parsing the
// template as a class and *only* during the phase where we are parsing the program
// entry point aka the evaluate() function. Otherwise throw out the parse tree.
if( !isParsingProgram() &&
getGosuClass() instanceof IGosuClass &&
(((IGosuClass)getGosuClass()).getCompilationState().isDeclarationsCompiled() &&
!((CompilationState)((IGosuClass)getGosuClass()).getCompilationState()).isReparsingHeader()) )
{
if( !isParsingFunction() || (getProgramEntryPointDfs() != null && !peekParsingFunction().equals( getProgramEntryPointDfs().getType() )) )
{
e.clearParseExceptions();
removeLocation( e.getLocation() );
removeInnerClasses( e );
}
}
}
private void removeInnerClasses( IParsedElement e ) {
if( e == null ) {
return;
}
if( e instanceof BlockExpression ) {
IBlockClass blockGosuClass = ((BlockExpression)e).getBlockGosuClass();
if( blockGosuClass != null ) {
((IGosuClassInternal)blockGosuClass.getEnclosingType()).removeBlock( blockGosuClass );
}
}
if( e instanceof NewExpression && ((NewExpression)e).isAnonymousClass() ) {
IGosuClassInternal anon = (IGosuClassInternal)((Expression)e).getType();
if( anon != null ) {
((IGosuClassInternal)anon.getEnclosingType()).removeInnerClass( anon );
}
}
IParseTree location = e.getLocation();
if( location != null ) {
for( IParseTree pt: location.getChildren() ) {
removeInnerClasses( pt.getParsedElement() );
}
}
}
private void removeLocation( ParseTree location )
{
if( location.getParent() != null )
{
location.getParent().removeChild( location );
}
getLocationsList().remove( location );
}
private void parseDirective( boolean processDirectives ) {
int iOffset = _tokenizer.getTokenStart();
int iLineNum = _tokenizer.getLineNumber();
int iColumn = _tokenizer.getTokenColumn();
DirectiveExpression e = new DirectiveExpression();
if( match( null, Keyword.KW_extends ) ) {
parseTypeLiteral();
Expression typeLiteral = peekExpression();
if( typeLiteral instanceof TypeLiteral ) {
IType extendsType = ((TypeLiteral)typeLiteral).getType().getType();
if( extendsType instanceof IGosuClassInternal ) {
IGosuClassInternal supertype = (IGosuClassInternal)extendsType;
supertype.putClassMembers( this, _symTable, supertype, true );
List<? extends GosuClassTypeLoader> typeLoaders = TypeSystem.getCurrentModule().getTypeLoaders( GosuClassTypeLoader.class );
for( GosuClassTypeLoader typeLoader : typeLoaders ) {
List<? extends IGosuEnhancement> enhancementsForType = typeLoader.getEnhancementIndex().getEnhancementsForType( supertype );
for( IGosuEnhancement enhancement : enhancementsForType ) {
if( enhancement instanceof IGosuEnhancementInternal ) {
((IGosuEnhancementInternal)enhancement).putClassMembers( this, _symTable, supertype, true );
}
}
}
for( Object entryObj : _symTable.getSymbols().entrySet() ) {
//noinspection unchecked
Map.Entry<CharSequence, ISymbol> entry = (Map.Entry<CharSequence, ISymbol>)entryObj;
if( entry.getValue().isPrivate() ) {
_symTable.removeSymbol( entry.getKey() );
}
}
}
}
}
else if( match( null, "params" ) ) {
verify(e, match(null, '('), Res.MSG_EXPECTING_LEFTPAREN_FUNCTION_DEF);
ArrayList<ISymbol> params = parseParameterDeclarationList( e, false, null );
if( processDirectives ) {
for( ISymbol param : params ) {
getSymbolTable().putSymbol( param );
}
}
verify( e, match( null, ')' ), Res.MSG_EXPECTING_RIGHTPAREN_FUNCTION_DEF );
}
else {
advanceToNextTokenSilently();
e.addParseException( new ParseException( makeFullParserState(), Res.MSG_BAD_TEMPLATE_DIRECTIVE ) );
}
pushExpression( e );
setLocation( iOffset, iLineNum, iColumn );
popExpression();
}
boolean parseAssignmentOrMethodCall()
{
boolean bRet = true;
if( !match( null, null, SourceCodeTokenizer.TT_KEYWORD, true ) &&
!match( null, null, SourceCodeTokenizer.TT_WORD, true ) &&
!match( null, null, '(', true ) &&
!match( null, null, '"', true ) )
{
return false;
}
int initialMark = _tokenizer.mark();
parsePrimaryExpression();
Expression e = popExpression();
if( e instanceof NewExpression )
{
// New Statement
NewStatement stmt = new NewStatement();
stmt.setNewExpression( (NewExpression)e );
pushStatement( stmt );
}
else if( e instanceof MethodCallExpression )
{
// Method Call Statement
MethodCallStatement stmt = new MethodCallStatement();
stmt.setMethodCall( (MethodCallExpression)e );
pushStatement( stmt );
//noinspection ThrowableResultOfMethodCallIgnored
e.removeParseException( Res.MSG_VOID_EXPRESSION_NOT_ALLOWED );
}
else if( e instanceof BeanMethodCallExpression )
{
// Bean Method Call Statement
BeanMethodCallStatement stmt = new BeanMethodCallStatement();
stmt.setBeanMethodCall( (BeanMethodCallExpression)e );
pushStatement( stmt );
//noinspection ThrowableResultOfMethodCallIgnored
e.removeParseException(Res.MSG_VOID_EXPRESSION_NOT_ALLOWED);
}
else if( e instanceof IBlockInvocation )
{
pushStatement( new BlockInvocationStatement( (IBlockInvocation)e ) );
//noinspection ThrowableResultOfMethodCallIgnored
e.removeParseException(Res.MSG_VOID_EXPRESSION_NOT_ALLOWED);
}
else if( e instanceof Identifier ||
(e instanceof ImplicitTypeAsExpression && ((ImplicitTypeAsExpression)e).getLHS() instanceof Identifier) )
{
Identifier id;
Statement statement;
// Assigment Statement
Token T = new Token();
if( matchAssignmentOperator( T ) )
{
if( e instanceof ImplicitTypeAsExpression )
{
id = (Identifier)((ImplicitTypeAsExpression)e).getLHS();
IParsedElement parent = ContextInferenceManager.unwrapImplicitTypeAs( ((ImplicitTypeAsExpression)e).getLHS() );
_locations.remove( e.getLocation() );
if( parent == null )
{
_locations.add( id.getLocation() );
}
}
else
{
id = (Identifier)e;
}
AssignmentStatement as = new AssignmentStatement();
statement = as;
as.setIdentifier( id );
Expression rhs = parseAssignmentRhs( T, e.getType(), e );
rhs = buildRhsOfCompoundOperator( e, T, rhs );
if( rhs.hasParseExceptions() )
{
rhs.getParseExceptions().get( 0 ).setExpectedType( id.getType() );
}
ISymbol idSym = id.getSymbol();
verify( as, idSym.isWritable() ||
(idSym.isFinal() && !idSym.isStatic() && !(idSym instanceof CapturedSymbol) &&
((idSym.isLocal() && !((Symbol)idSym).isImplicitlyInitialized()) ||
(isParsingConstructor() || isParsingProgramEvaluateMethod()) && isSymbolInScopeDirectly( idSym ))), // initializing final var is ok
Res.MSG_PROPERTY_NOT_WRITABLE, idSym.getDisplayName() );
if( id instanceof PropertyAccessIdentifier )
{
DynamicPropertySymbol symbol = (DynamicPropertySymbol)idSym;
DynamicFunctionSymbol setter = symbol.getSetterDfs();
if(setter != null && setter.getScriptPart() != null) {
IType settersOwningType = setter.getScriptPart().getContainingType();
if( settersOwningType instanceof IGosuClassInternal )
{
IGosuClassInternal settersOwningClass = (IGosuClassInternal) settersOwningType;
ICompilableTypeInternal ctxType = getGosuClass();
if( ctxType instanceof IGosuClassInternal )
{
IGosuClassInternal ctxClass = (IGosuClassInternal)ctxType;
if( !settersOwningClass.isAccessible( ctxClass, setter ) )
{
id.addParseException( Res.MSG_PROPERTY_NOT_VISIBLE, idSym.getDisplayName() );
}
}
}
}
}
if( rhs instanceof Identifier )
{
Identifier identifier = (Identifier)rhs;
if( idSym != null && idSym.equals( identifier.getSymbol() ) )
{
as.addParseWarning( new ParseWarning( makeFullParserState(), Res.MSG_SILLY_ASSIGNMENT, idSym.getName() ) );
}
if( identifier.getSymbol() instanceof DynamicPropertySymbol )
{
DynamicPropertySymbol dps = (DynamicPropertySymbol) identifier.getSymbol();
if( dps.getVarIdentifier() != null && dps.getVarIdentifier().equals( idSym.getName() ) )
{
as.addParseWarning( new ParseWarning( makeFullParserState(), Res.MSG_SILLY_ASSIGNMENT, idSym.getName() ) );
}
}
}
//noinspection ThrowableResultOfMethodCallIgnored
e.removeParseException( Res.MSG_CANNOT_READ_A_WRITE_ONLY_PROPERTY );
_ctxInferenceMgr.cancelInferences( id, rhs );
verifyComparable( id.getType(), rhs, true );
rhs = possiblyWrapWithImplicitCoercion( rhs, id.getType() );
as.setExpression( rhs );
}
else
{
NotAStatement nas = new NotAStatement();
statement = nas;
nas.setExpression( e );
verify( nas, false, Res.MSG_NOT_A_STATEMENT );
}
pushStatement( statement );
}
else if( e instanceof ParenthesizedExpression )
{
NotAStatement nas = new NotAStatement();
nas.setExpression( e );
verify( nas, false, Res.MSG_EXPECTING_OPERATOR_TO_FOLLOW_EXPRESSION );
pushStatement( nas );
}
else if( e instanceof SynthesizedMemberAccess )
{
SyntheticMemberAccessStatement stmt = new SyntheticMemberAccessStatement((ISynthesizedMemberAccessExpression)e);
pushStatement( stmt );
}
else if( e instanceof MemberAccess ||
(e instanceof ImplicitTypeAsExpression && ((ImplicitTypeAsExpression)e).getLHS() instanceof MemberAccess))
{
// Member Assignment Statement
MemberAssignmentStatement as = new MemberAssignmentStatement();
MemberAccess ma;
if( e instanceof ImplicitTypeAsExpression )
{
ma = (MemberAccess)((ImplicitTypeAsExpression)e).getLHS();
IParsedElement parent = ContextInferenceManager.unwrapImplicitTypeAs( ((ImplicitTypeAsExpression)e).getLHS() );
_locations.remove( e.getLocation() );
if( parent == null )
{
_locations.add( ma.getLocation() );
}
}
else
{
ma = (MemberAccess)e;
}
// This tests the validity of the access list
IType typeExpected = ma.getType();
Token T = new Token();
if( verify( as, matchAssignmentOperator(T), Res.MSG_EXPECTING_EQUALS_ASSIGN ) )
{
try
{
// The only case this can be null is for associative array access.
if (ma.getMemberName() != null) {
verifyPropertyWritable( ma.getRootType(), ma.getMemberName(), false );
}
}
catch( Exception ex )
{
//noinspection ThrowableResultOfMethodCallIgnored
ma.addParseException( ParseException.wrap( ex, makeFullParserState() ) );
}
Expression rhs = parseAssignmentRhs( T, typeExpected, e );
_ctxInferenceMgr.cancelInferences( ma, rhs );
typeExpected = ma.getType(); //update type in case an inference was cancelled
if( !(typeExpected instanceof ErrorType) )
{
IPropertyInfo lhsPi = ma.getPropertyInfo();
if( lhsPi instanceof JavaPropertyInfo &&
((JavaPropertyInfo)lhsPi).getWriteMethodInfo() == null &&
((JavaPropertyInfo)lhsPi).getPublicField() != null )
{
typeExpected = TypeSystem.get(((JavaPropertyInfo) lhsPi).getPublicField().getType());
}
}
verifyComparable( typeExpected, rhs, true );
rhs = buildRhsOfCompoundOperator( e, T, rhs );
if( rhs.hasParseExceptions() )
{
rhs.getParseExceptions().get( 0 ).setExpectedType( typeExpected );
}
rhs = possiblyWrapWithImplicitCoercion(rhs, typeExpected);
as.setExpression( rhs );
}
//noinspection ThrowableResultOfMethodCallIgnored
ma.removeParseException( Res.MSG_CANNOT_READ_A_WRITE_ONLY_PROPERTY );
as.setRootExpression( ma.getRootExpression() );
as.setMemberName( ma.getMemberName() );
as.setMemberExpression( ma.getMemberExpression() );
as.setMemberAccess( ma );
pushStatement( as );
}
else if( e instanceof ArrayAccess )
{
// Array Assignment Statement
Statement statement;
ArrayAccess aa = (ArrayAccess)e;
IType typeExpected = aa.getComponentType();
Token T = new Token();
ArrayAssignmentStatement as = new ArrayAssignmentStatement();
if( verify( as, matchAssignmentOperator(T), Res.MSG_EXPECTING_EQUALS_ASSIGN ) )
{
verify( as, aa.getRootExpression().getType() != JavaTypes.STRING(), Res.MSG_STR_IMMUTABLE );
Expression rhs = parseAssignmentRhs( T, typeExpected, aa );
verifyComparable( typeExpected, rhs, true );
rhs = buildRhsOfCompoundOperator( e, T, rhs );
rhs = possiblyWrapWithImplicitCoercion( rhs, typeExpected );
as.setExpression( rhs );
as.setArrayAccessExpression( aa );
statement = as;
}
else
{
NotAStatement nas = new NotAStatement();
statement = nas;
nas.setExpression( aa );
verify( nas, false, Res.MSG_NOT_A_STATEMENT );
}
pushStatement( statement );
}
else if( e instanceof MapAccess )
{
// Array Assignment Statement
Statement statement;
MapAccess ma = (MapAccess)e;
IType typeExpected = ma.getComponentType();
Token T = new Token();
MapAssignmentStatement as = new MapAssignmentStatement();
if( verify( as, matchAssignmentOperator(T), Res.MSG_EXPECTING_EQUALS_ASSIGN ) )
{
Expression rhs = parseAssignmentRhs( T, typeExpected, e );
verifyComparable( typeExpected, rhs, true );
rhs = buildRhsOfCompoundOperator(e, T, rhs);
rhs = possiblyWrapWithImplicitCoercion( rhs, ma.getComponentType() );
as.setExpression( rhs );
as.setMapAccessExpression( ma );
statement = as;
}
else
{
NotAStatement nas = new NotAStatement();
statement = nas;
nas.setExpression( ma );
verify( nas, false, Res.MSG_NOT_A_STATEMENT );
}
pushStatement( statement );
}
else if (e instanceof FeatureLiteral) {
NotAStatement nas = new NotAStatement();
nas.setExpression(e);
verify(nas, false, Res.MSG_NOT_A_STATEMENT);
pushStatement( nas );
} else {
_tokenizer.restoreToMark(initialMark);
_locations.remove(e.getLocation());
bRet = false;
}
return bRet;
}
private boolean isParsingProgramEvaluateMethod() {
return isParsingFunction() && getProgramEntryPointDfs() != null &&
peekParsingFunction().equals( getProgramEntryPointDfs().getType() );
}
private boolean isSymbolInScopeDirectly( ISymbol idSym ) {
if( !(idSym instanceof DynamicSymbol) ) {
return true;
}
DynamicSymbol fieldSym = (DynamicSymbol)idSym;
IGosuClassInternal declaringClass = fieldSym.getGosuClass();
return getGosuClass() == declaringClass && !isParsingBlock();
}
private Expression parseAssignmentRhs( Token operation, IType typeExpected, Expression lhs )
{
Expression rhs;
if( "++".equals(operation._strValue) ||
"--".equals( operation._strValue ) )
{
AdditiveExpression add = new AdditiveExpression();
add.setLHS( lhs );
NumericLiteral one = new NumericLiteral( "1", 1, JavaTypes.pINT() );
pushExpression( one );
setLocation( lhs.getLocation().getExtent() + 1, lhs.getLineNum(), lhs.getLocation().getColumn() + 1 );
popExpression();
add.setRHS( one );
one.setType( typeExpected );
add.setOperator( "++".equals( operation._strValue ) ? "+" : "-" );
add.setType( resolveType( lhs, lhs.getType(), operation._strValue.charAt( 0 ), lhs.getType() ) );
pushExpression( add );
setLocation( lhs.getLocation().getOffset(), lhs.getLineNum(), lhs.getLocation().getColumn()+1 );
popExpression();
rhs = add;
}
else
{
parseExpression( typeExpected, false );
rhs = popExpression();
detectLikelyJavaCast(rhs);
}
return rhs;
}
private Expression buildRhsOfCompoundOperator( Expression lhs, Token t, Expression rhs )
{
Expression synthetic = null;
if( "+=".equals( t._strValue ) || "-=".equals( t._strValue ) )
{
AdditiveExpression add = new AdditiveExpression();
add.setLHS( lhs );
add.setRHS( rhs );
add.setOperator( t._strValue.charAt( 0 ) == '+' ? "+" : "-" );
add.setType( resolveType( lhs, lhs.getType(), t._strValue.charAt( 0 ), rhs.getType() ) );
synthetic = add;
}
else if( "*=".equals( t._strValue ) || "/=".equals( t._strValue ) || "%=".equals( t._strValue ) )
{
MultiplicativeExpression mult = new MultiplicativeExpression();
mult.setLHS( lhs );
mult.setRHS( rhs );
mult.setOperator( String.valueOf( t._strValue.charAt( 0 ) ) );
mult.setType( resolveType( lhs, lhs.getType(), t._strValue.charAt( 0 ), rhs.getType() ) );
synthetic = mult;
}
else if( "&=".equals( t._strValue ) )
{
BitwiseAndExpression and = new BitwiseAndExpression();
lhs = ensureOperandIntOrLong( lhs );
rhs = possiblyWrapWithImplicitCoercion( rhs, lhs.getType() );
and.setLHS( lhs );
and.setRHS( rhs );
and.setType( resolveType( lhs, lhs.getType(), t._strValue.charAt( 0 ), rhs.getType() ) );
synthetic = and;
}
else if( "&&=".equals( t._strValue ) )
{
ConditionalAndExpression and = new ConditionalAndExpression();
verifyComparable( JavaTypes.pBOOLEAN(), rhs, true, true );
rhs = possiblyWrapWithImplicitCoercion( rhs, JavaTypes.pBOOLEAN() );
verifyComparable( JavaTypes.pBOOLEAN(), lhs, true, true );
lhs = possiblyWrapWithImplicitCoercion( lhs, JavaTypes.pBOOLEAN() );
and.setLHS( lhs );
and.setRHS( rhs );
synthetic = and;
}
else if( "^=".equals( t._strValue ) )
{
BitwiseXorExpression xor = new BitwiseXorExpression();
lhs = ensureOperandIntOrLong( lhs );
rhs = possiblyWrapWithImplicitCoercion( rhs, lhs.getType() );
xor.setLHS( lhs );
xor.setRHS( rhs );
xor.setType( resolveType( lhs, lhs.getType(), t._strValue.charAt( 0 ), rhs.getType() ) );
synthetic = xor;
}
else if( "|=".equals( t._strValue ) )
{
BitwiseOrExpression or = new BitwiseOrExpression();
lhs = ensureOperandIntOrLong( lhs );
rhs = possiblyWrapWithImplicitCoercion( rhs, lhs.getType() );
or.setLHS( lhs );
or.setRHS( rhs );
or.setType( resolveType( lhs, lhs.getType(), t._strValue.charAt( 0 ), rhs.getType() ) );
synthetic = or;
}
else if( "||=".equals( t._strValue ) )
{
ConditionalOrExpression or = new ConditionalOrExpression();
verifyComparable( JavaTypes.pBOOLEAN(), rhs, true, true );
rhs = possiblyWrapWithImplicitCoercion( rhs, JavaTypes.pBOOLEAN() );
verifyComparable( JavaTypes.pBOOLEAN(), lhs, true, true );
lhs = possiblyWrapWithImplicitCoercion( lhs, JavaTypes.pBOOLEAN() );
or.setLHS( lhs );
or.setRHS( rhs );
synthetic = or;
}
else if( "<<=".equals( t._strValue ) || ">>=".equals( t._strValue ) || ">>>=".equals( t._strValue ) )
{
BitshiftExpression shift = new BitshiftExpression();
verifyTypesComparable( rhs, JavaTypes.pINT(), rhs.getType(), false, true );
rhs = possiblyWrapWithImplicitCoercion( rhs, JavaTypes.pINT() );
// Lhs must be an int or a long
IType lhsType = lhs.getType();
if( verify( lhs,
lhsType == JavaTypes.LONG() || lhsType == JavaTypes.pLONG() ||
lhsType == JavaTypes.INTEGER() || lhsType == JavaTypes.pINT() ||
lhsType == JavaTypes.SHORT() || lhsType == JavaTypes.pSHORT() ||
lhsType == JavaTypes.BYTE() || lhsType == JavaTypes.pBYTE(),
Res.MSG_BITSHIFT_LHS_MUST_BE_INT_OR_LONG ) )
{
lhsType = lhsType == JavaTypes.LONG() || lhsType == JavaTypes.pLONG() ? JavaTypes.pLONG() : JavaTypes.pINT();
lhs = possiblyWrapWithImplicitCoercion( lhs, lhsType );
}
shift.setLHS( lhs );
shift.setRHS( rhs );
shift.setOperator( t._strValue );
shift.setType( resolveType( lhs, lhs.getType(), t._strValue.charAt( 0 ), rhs.getType() ) );
synthetic = shift;
}
if( synthetic != null )
{
pushExpression( synthetic );
ParseTree rhsLoc = rhs.getLocation();
setLocation( rhsLoc.getOffset(), rhs.getLineNum(), rhsLoc.getColumn(), true );
popExpression();
rhs = synthetic;
}
return rhs;
}
private boolean matchAssignmentOperator( Token t )
{
return match( t, "=", SourceCodeTokenizer.TT_OPERATOR ) ||
match( t, "+=", SourceCodeTokenizer.TT_OPERATOR ) ||
match( t, "-=", SourceCodeTokenizer.TT_OPERATOR ) ||
match( t, "++", SourceCodeTokenizer.TT_OPERATOR ) ||
match( t, "--", SourceCodeTokenizer.TT_OPERATOR ) ||
match( t, "*=", SourceCodeTokenizer.TT_OPERATOR ) ||
match( t, "%=", SourceCodeTokenizer.TT_OPERATOR ) ||
match( t, "/=", SourceCodeTokenizer.TT_OPERATOR ) ||
match( t, "&=", SourceCodeTokenizer.TT_OPERATOR ) ||
match( t, "&&=", SourceCodeTokenizer.TT_OPERATOR ) ||
match( t, "^=", SourceCodeTokenizer.TT_OPERATOR ) ||
match( t, "|=", SourceCodeTokenizer.TT_OPERATOR ) ||
match( t, "||=", SourceCodeTokenizer.TT_OPERATOR ) ||
match( t, "<<=", SourceCodeTokenizer.TT_OPERATOR ) ||
matchRightShiftAssign( t );
}
private boolean matchRightShiftAssign( Token t )
{
int iMark = getTokenizer().mark();
if( match( null, ">", SourceCodeTokenizer.TT_OPERATOR ) )
{
if( match( null, ">", SourceCodeTokenizer.TT_OPERATOR ) )
{
if( match( null, ">", SourceCodeTokenizer.TT_OPERATOR ) )
{
if( match( null, "=", SourceCodeTokenizer.TT_OPERATOR ) )
{
t._strValue = ">>>=";
return true;
}
}
else if( match( null, "=", SourceCodeTokenizer.TT_OPERATOR ) )
{
t._strValue = ">>=";
return true;
}
}
}
getTokenizer().restoreToMark( iMark );
return false;
}
//------------------------------------------------------------------------------
//
// function-declaration
// [modifiers] function <identifier> ( [ <argument-declaration-list> ] ) : <type-literal>
//
ISymbol parseFunctionOrPropertyDeclaration( ParsedElement element, boolean bParseProperties, boolean bParseVars )
{
ModifierInfo modifiers;
do
{
while( match( null, Keyword.KW_uses ) )
{
Token T = new Token();
parseUsesStatement( T );
popStatement();
}
modifiers = parseModifiers();
if( match( null, Keyword.KW_function ) )
{
DynamicFunctionSymbol symbol = parseFunctionDecl(element, modifiers);
eatStatementBlock( symbol != null && symbol.getDeclFunctionStmt() != null ? symbol.getDeclFunctionStmt() : element, Res.MSG_EXPECTING_OPEN_BRACE_FOR_FUNCTION_DEF );
return symbol;
}
if( bParseProperties && match( null, Keyword.KW_property ) )
{
boolean bGetter = match( null, Keyword.KW_get );
boolean bSetter = match( null, Keyword.KW_set );
// We only care about legal properties
if (bGetter || bSetter) {
FunctionStatement fs = new FunctionStatement();
DynamicFunctionSymbol dfs = getOwner().parseFunctionDecl( fs, true, bGetter, modifiers );
if( dfs == null )
{
element.addParseException( new ParseException( makeFullParserState(), Res.MSG_EXPECTING_DECL ) );
return null;
}
fs.setDynamicFunctionSymbol( dfs );
pushStatement( fs );
dfs.setClassMember( true );
eatStatementBlock( fs, Res.MSG_EXPECTING_OPEN_BRACE_FOR_FUNCTION_DEF );
DynamicPropertySymbol dps = getOrCreateDynamicPropertySymbol( element, null, dfs, bGetter );
dps.setClassMember(false);
return dps;
}
}
if( bParseVars && match( null, Keyword.KW_var ) )
{
VarStatement var = new VarStatement();
Token T = new Token();
if( !verify( var, match( T, SourceCodeTokenizer.TT_WORD ), Res.MSG_EXPECTING_IDENTIFIER_VAR ) )
{
T._strValue = null;
}
parseVarStatement( var, T, false );
return var.getSymbol();
}
if( match( null, SourceCodeTokenizer.TT_EOF ) )
{
return null;
}
_tokenizer.nextToken();
} while( true );
}
boolean parsePropertyDefinition()
{
if( isParsingBlock() )
{
return false;
}
ModifierInfo modifiers = parseModifiers();
if( match( null, Keyword.KW_property ) )
{
boolean bGetter = match( null, Keyword.KW_get );
boolean bSetter = !bGetter && match( null, Keyword.KW_set );
int iOffset = getTokenizer().getTokenStart();
int iLineNum = getTokenizer().getLineNumber();
int iColumn = getTokenizer().getTokenColumn();
FunctionStatement functionStmt = parseBaseFunctionDefinition( null, true, bGetter, modifiers );
verify( functionStmt, bGetter || bSetter, Res.MSG_EXPECTING_PROPERTY_GET_OR_SET_MODIFIER );
setLocation( iOffset, iLineNum, iColumn );
getOwner().popStatement();
DynamicFunctionSymbol dfs = functionStmt.getDynamicFunctionSymbol();
if( dfs == null )
{
// Note, dfs is null only if the function stmt is errant
dfs = new DynamicFunctionSymbol( _symTable, "@", new FunctionType( "@", JavaTypes.pVOID(), null, GenericTypeVariable.EMPTY_TYPEVARS ), Collections.<ISymbol>emptyList(), (Statement)null );
functionStmt.setDynamicFunctionSymbol( dfs );
}
IType returnType = functionStmt.getDynamicFunctionSymbol().getReturnType();
verify( functionStmt, bGetter || returnType == JavaTypes.pVOID(), Res.MSG_PROPERTY_SET_MUST_RETURN_VOID );
dfs.setClassMember( false );
if( bGetter && dfs.getArgTypes() != null && dfs.getArgTypes().length > 0 )
{
List<IParseTree> children = functionStmt.getLocation().getChildren();
for( IParseTree child : children )
{
if( child.getParsedElement() instanceof ParameterDeclaration )
{
child.getParsedElement().addParseException(Res.MSG_GETTER_CANNOT_HAVE_PARAMETERS);
}
}
}
DynamicPropertySymbol dps = getOrCreateDynamicPropertySymbol( functionStmt, null, dfs, bGetter );
dps.setClassMember( false );
getOwner().pushStatement( new PropertyStatement( functionStmt, dps ) );
return true;
}
return false;
}
DynamicPropertySymbol getOrCreateDynamicPropertySymbol(
ParsedElement parsedElement, IGosuClassInternal gsClass, DynamicFunctionSymbol dfs, boolean bGetter )
{
String strPropertyName = dfs.getDisplayName().substring( 1 );
ISymbol symbol = getSymbolTable().getSymbol( strPropertyName );
DynamicPropertySymbol dps;
if( !verify( parsedElement, symbol == null || symbol instanceof DynamicPropertySymbol, Res.MSG_VARIABLE_ALREADY_DEFINED, strPropertyName ) )
{
return new DynamicPropertySymbol( dfs, bGetter );
}
if( symbol == null ||
(gsClass != null &&
gsClass.getMemberProperty( strPropertyName ) == null &&
gsClass.getStaticProperty( strPropertyName ) == null) )
{
dps = new DynamicPropertySymbol( dfs, bGetter );
if( symbol != null )
{
assert symbol instanceof DynamicPropertySymbol;
dps.setParent( (DynamicPropertySymbol)symbol );
}
return dps;
}
assert symbol instanceof DynamicPropertySymbol;
dps = (DynamicPropertySymbol)symbol;
if( bGetter )
{
verify( parsedElement,
dps.getImmediateGetterDfs() == null ||
dps.getImmediateGetterDfs() instanceof VarPropertyGetFunctionSymbol ||
dps.getImmediateGetterDfs().getValueDirectly() != null ||
dps.getImmediateGetterDfs().isAbstract() ||
(gsClass != null && gsClass.isInterface()),
Res.MSG_GETTER_FOR_PROPERTY_ALREADY_DEFINED,
strPropertyName );
dps.setGetterDfs( dfs );
}
else
{
verify( parsedElement,
dps.getImmediateSetterDfs() == null ||
dps.getImmediateSetterDfs() instanceof VarPropertySetFunctionSymbol ||
dps.getImmediateSetterDfs().getValueDirectly() != null ||
dps.getImmediateSetterDfs().isAbstract() ||
(gsClass != null && gsClass.isInterface()),
Res.MSG_SETTER_FOR_PROPERTY_ALREADY_DEFINED,
strPropertyName );
dps.setSetterDfs(dfs);
}
return dps;
}
//------------------------------------------------------------------------------
//
// function-definition
// [modifiers] function <identifier> ( [ <argument-declaration-list> ] ) : <type-literal> statement-block
//
boolean parseFunctionDefinition()
{
ModifierInfo modifiers = parseModifiers();
if( !isParsingBlock() && match(null, Keyword.KW_function) )
{
parseBaseFunctionDefinition( new FunctionStatement(), false, false, modifiers );
return true;
}
return false;
}
FunctionStatement parseBaseFunctionDefinition(FunctionStatement functionStmt, boolean bProperty, boolean bGetter, ModifierInfo modifiers)
{
boolean bNullFunctionStmt = functionStmt == null;
functionStmt = bNullFunctionStmt ? new FunctionStatement() : functionStmt;
Token T = new Token();
int iNamedOffset = getTokenizer().getTokenStart();
int iOffsetName = getTokenizer().getTokenStart();
int iLineNumName = getTokenizer().getLineNumber();
int iColumnName = getTokenizer().getTokenColumn();
boolean bHasName;
if( bHasName = verify( functionStmt, match( T, SourceCodeTokenizer.TT_WORD ), Res.MSG_EXPECTING_NAME_FUNCTION_DEF ) )
{
functionStmt.setNameOffset( iNamedOffset, T._strValue );
}
String strFunctionName = T._strValue;
addNameInDeclaration( strFunctionName, iOffsetName, iLineNumName, iColumnName, bHasName );
maybeEatNonDeclKeyword( bHasName, strFunctionName );
strFunctionName = strFunctionName == null
? ""
: bProperty
? "@" + strFunctionName
: strFunctionName;
DynamicFunctionSymbol dfsDecl = findCorrespondingDeclDfs( iOffsetName, modifiers.getModifiers() );
List<TypeVariableDefinitionImpl> defsFromDecl = getTypeVarDefsFromDecl( dfsDecl );
List<ITypeVariableDefinitionExpression> typeVarDefs = parseTypeVariableDefs( functionStmt, true, defsFromDecl );
// Must create function type and assign it as the type var's enclosing
// type *before* we parse the return type (in case it refs function's type vars)
IGenericTypeVariable[] typeVars = TypeVariableDefinition.getTypeVars( typeVarDefs );
if( bProperty && !typeVarDefs.isEmpty() )
{
verify( functionStmt, false, Res.MSG_GENERIC_PROPERTIES_NOT_SUPPORTED );
}
try
{
verify( functionStmt, match( null, '(' ), Res.MSG_EXPECTING_LEFTPAREN_FUNCTION_DEF );
ArrayList<ISymbol> args = null;
Statement statement = null;
boolean functionBodyParsed = true;
// Pushing an isolated scope to that the compile-time stack indexes are
// aligned with runtime e.g., args must be indexed relative to the
// activation record.
// We push a scope to parse the function decl, then we pop it to find the dfs, then we push it again to parse the body.
_symTable.pushIsolatedScope( new GosuParserTransparentActivationContext( getScriptPart() ) );
try
{
IType[] argTypes = null;
if( !match( null, null, ')', true ) )
{
args = parseParameterDeclarationList( functionStmt, Modifier.isStatic( modifiers.getModifiers() ), null, bProperty, bGetter, false );
argTypes = new IType[args.size()];
for( int i = 0; i < args.size(); i++ )
{
_symTable.putSymbol( args.get( i ) );
argTypes[i] = args.get( i ).getType();
}
}
else
{
parseParameterDeclarationList( functionStmt, Modifier.isStatic( modifiers.getModifiers() ), null, bProperty, bGetter, true );
verify ( functionStmt, ! bProperty || bGetter, Res.MSG_PROPERTY_SET_MUST_HAVE_ONE_PARAMETER );
}
verify( functionStmt, match( null, ')' ), Res.MSG_EXPECTING_RIGHTPAREN_FUNCTION_DEF );
TypeLiteral typeLiteral;
if( match( null, ":", SourceCodeTokenizer.TT_OPERATOR ) )
{
parseTypeLiteral();
typeLiteral = (TypeLiteral)popExpression();
if( bGetter )
{
verify( typeLiteral, typeLiteral.getType().getType() != JavaTypes.pVOID(), Res.MSG_VOID_NOT_ALLOWED );
}
}
else
{
Token fabricatedT = new Token();
fabricatedT._strValue = Keyword.KW_void.toString();
typeLiteral = resolveTypeLiteral( fabricatedT );
verify( functionStmt, !bGetter, Res.MSG_MISSING_PROPERTY_RETURN );
}
// Note dfsDecl *should have* been assigned prior to parsing args etc. However it can be the case where
// e.g., crappy pcf types, the symbol can only be resolved from the _symTable. Sigh.
if( !bProperty && dfsDecl == null )
{
String functionNameWithArgs = DynamicFunctionSymbol.getSignatureName( strFunctionName == null ? "" : strFunctionName, args );
DynamicFunctionSymbol dfsInSymTable = (DynamicFunctionSymbol)_symTable.getSymbol( functionNameWithArgs );
if( dfsInSymTable == null )
{
dfsDecl = dfsInSymTable;
}
else if( dfsInSymTable.getGosuClass() == getGosuClass() )
{
dfsDecl = dfsInSymTable;
dfsDecl = assignPossibleDuplicateDfs( dfsDecl, _symTable.getSymbols().values() );
}
}
FunctionType type = dfsDecl == null
? new FunctionType( strFunctionName, JavaTypes.pVOID(), null, typeVars )
: (FunctionType)dfsDecl.getType();
if( !bProperty && dfsDecl != null )
{
type = (FunctionType)dfsDecl.getType();
if( typeVarDefs != null )
{
for( ITypeVariableDefinitionExpression tvd : typeVarDefs )
{
((ITypeVariableDefinition)tvd).setEnclosingType( type );
}
}
}
else
{
type.setRetType( typeLiteral.getType().getType() );
type.setArgumentTypes( argTypes );
}
type.setScriptPart( getScriptPart() );
type.setModifiers( modifiers.getModifiers() );
putThisAndSuperSymbols( modifiers );
if( !Modifier.isAbstract( modifiers.getModifiers() ) && !Modifier.isNative( modifiers.getModifiers() ) )
{
if( getScriptPart() == null ||
!(getScriptPart().getContainingType() instanceof IGosuClassInternal) ||
!getScriptPart().getContainingType().isInterface() )
{
if( parseFunctionBody( functionStmt, type ) )
{
statement = popStatement();
}
else
{
functionBodyParsed = false;
}
}
}
}
finally
{
_symTable.popScope();
}
ISymbol functionSymbol = _symTable.getSymbol( strFunctionName );
if( functionSymbol instanceof CommonSymbolsScope.LockedDownSymbol )
{
functionSymbol = null;
}
if( bProperty )
{
if( functionSymbol instanceof DynamicPropertySymbol )
{
DynamicPropertySymbol dps = (DynamicPropertySymbol)functionSymbol;
String functionNameWithArgs = DynamicFunctionSymbol.getSignatureName( strFunctionName, args );
dfsDecl = (dps == null || !dps.getName().equals( strFunctionName ) )
? (DynamicFunctionSymbol)_symTable.getSymbol( functionNameWithArgs )
: dps.getFunction( functionNameWithArgs );
}
else if( getGosuClass() == null )
{
dfsDecl = findProgramPropertyDfs( strFunctionName, args );
}
}
if( dfsDecl != null &&
dfsDecl.getDeclFunctionStmt() != null &&
bNullFunctionStmt )
{
FunctionStatement funcStmtFromDecl = dfsDecl.getDeclFunctionStmt();
if( funcStmtFromDecl.getParent() != null &&
funcStmtFromDecl.getGosuClass() == getGosuClass() )
{
functionStmt = dfsDecl.getDeclFunctionStmt();
}
}
verify( functionStmt, functionBodyParsed, Res.MSG_EXPECTING_RETURN_TYPE_OR_FUN_BODY );
verify( functionStmt, (getGosuClass() != null && getGosuClass().isAbstract()) || !Modifier.isAbstract( modifiers.getModifiers() ), Res.MSG_ABSTRACT_MEMBER_NOT_IN_ABSTRACT_CLASS );
verify( functionStmt, dfsDecl != null, Res.MSG_EXPECTING_NAME_FUNCTION_DEF );
verify( functionStmt, parsingFunctionsEncloseMyClass(), Res.MSG_EXPECTING_CLOSE_BRACE_FOR_FUNCTION_DEF );
if( dfsDecl != null )
{
if( args != null )
{
//replace the decl time arg symbols with the impl time arg symbols
dfsDecl.getArgs().clear();
dfsDecl.getArgs().addAll( args );
}
// Overwrite annotations to use the new-expressions created in
dfsDecl.getModifierInfo().setAnnotations( modifiers.getAnnotations() );
dfsDecl.setValueDirectly( statement );
pushDynamicFunctionSymbol( dfsDecl );
functionStmt.setDynamicFunctionSymbol( dfsDecl );
verifyOrWarn( functionStmt, isTerminal( statement, dfsDecl.getReturnType() ),
!CommonServices.getEntityAccess().isUnreachableCodeDetectionOn(),
Res.MSG_MISSING_RETURN );
}
pushStatement( functionStmt );
DynamicFunctionSymbol dynamicFunctionSymbol = functionStmt.getDynamicFunctionSymbol();
if(dynamicFunctionSymbol != null && dynamicFunctionSymbol.getDeclFunctionStmt() == null )
{
dynamicFunctionSymbol.setDeclFunctionStmt(functionStmt);
}
return functionStmt;
}
finally
{
for( ITypeVariableDefinitionExpression typeVarDef : typeVarDefs )
{
Map<String, ITypeVariableDefinition> typeVarMap = getTypeVariables();
if( typeVarMap.containsValue( typeVarDef ) )
{
typeVarMap.remove( ((ITypeVariableDefinition)typeVarDef).getName() );
}
}
}
}
private void putThisAndSuperSymbols( ModifierInfo modifiers )
{
if( !Modifier.isStatic( modifiers.getModifiers() ) &&
getScriptPart() != null &&
(getScriptPart().getContainingType() instanceof IGosuClassInternal) )
{
IGosuClassInternal gsClass = (IGosuClassInternal)getScriptPart().getContainingType();
IType thisType = gsClass;
if( gsClass instanceof IGosuEnhancementInternal )
{
thisType = ((IGosuEnhancementInternal)gsClass).getEnhancedType();
}
if( thisType != null )
{
thisType = TypeLord.getConcreteType( thisType );
getSymbolTable().putSymbol( new ThisSymbol( thisType, _symTable ) );
if( !(gsClass instanceof IGosuEnhancementInternal) )
{
IGosuClassInternal superClass = gsClass.getSuperClass();
if( superClass == null )
{
superClass = IGosuClassInternal.Util.getGosuClassFrom( JavaTypes.OBJECT() );
}
getSymbolTable().putSymbol( new Symbol( Keyword.KW_super.getName(), superClass, _symTable, null ) );
}
}
}
}
private DynamicFunctionSymbol findProgramPropertyDfs( String strFunctionName, ArrayList<ISymbol> args )
{
List<IFunctionSymbol> list = getDfsDeclsForFunction( (String)strFunctionName );
String propertyNameWithArgs = DynamicFunctionSymbol.getSignatureName( strFunctionName == null ? "" : strFunctionName, args );
for( IFunctionSymbol func : list )
{
if( func instanceof DynamicFunctionSymbol )
{
DynamicFunctionSymbol dfs = (DynamicFunctionSymbol)func;
String sig = DynamicFunctionSymbol.getSignatureName( dfs.getDisplayName(), dfs.getArgs() );
if( propertyNameWithArgs.equals( sig ) )
{
return dfs;
}
}
}
return null;
}
private List<TypeVariableDefinitionImpl> getTypeVarDefsFromDecl( DynamicFunctionSymbol dfsDecl )
{
if( dfsDecl == null )
{
return Collections.emptyList();
}
IGenericTypeVariable[] typeVars = dfsDecl.getType().getGenericTypeVariables();
if( typeVars == null )
{
return Collections.emptyList();
}
List<TypeVariableDefinitionImpl> result = new ArrayList<TypeVariableDefinitionImpl>( typeVars.length );
for( IGenericTypeVariable typeVar : typeVars )
{
result.add( (TypeVariableDefinitionImpl)typeVar.getTypeVariableDefinition() );
}
return result;
}
private DynamicFunctionSymbol findCorrespondingDeclDfs( int iOffsetName, int iModifiers )
{
ICompilableTypeInternal gsClass = getGosuClass();
if( gsClass == null )
{
return null;
}
Collection<DynamicFunctionSymbol> functions = Modifier.isStatic( iModifiers )
? gsClass.getParseInfo().getStaticFunctions()
: gsClass.getParseInfo().getMemberFunctions().values();
for( DynamicFunctionSymbol dfs : functions )
{
FunctionStatement funcStmt = dfs.getDeclFunctionStmt();
if( funcStmt != null && funcStmt.getNameOffset( null ) == iOffsetName )
{
return dfs;
}
}
return null;
}
boolean isDeclarationKeyword( String strKeyword )
{
return strKeyword != null &&
(Keyword.KW_function.equals( strKeyword ) ||
Keyword.KW_construct.equals( strKeyword ) ||
Keyword.KW_property.equals( strKeyword ) ||
//Keyword.KW_var.equals( strFunctionName ) ||
Keyword.KW_delegate.equals( strKeyword ) ||
Keyword.KW_class.equals( strKeyword ) ||
Keyword.KW_interface.equals( strKeyword ) ||
Keyword.KW_structure.equals( strKeyword ) ||
Keyword.KW_enum.equals( strKeyword ));
}
static DynamicFunctionSymbol assignPossibleDuplicateDfs( DynamicFunctionSymbol dfsDecl, Iterable symbols )
{
DynamicFunctionSymbol result = dfsDecl;
if( dfsDecl != null && dfsDecl.getValueDirectly() != null )
{
int iMin = Integer.MAX_VALUE;
for( Object csr : symbols )
{
if( csr instanceof DynamicFunctionSymbol )
{
DynamicFunctionSymbol dfsCsr = (DynamicFunctionSymbol)csr;
if( dfsCsr.getName().toLowerCase().contains( "_duplicate_" + dfsDecl.getName().toLowerCase() ) && dfsCsr.getGosuClass() == dfsDecl.getGosuClass() )
{
String strName = dfsCsr.getName();
if( dfsCsr.getValueDirectly() == null )
{
int iIndex = Integer.parseInt( strName.substring( 0, strName.indexOf( '_' ) ) );
if( iIndex < iMin )
{
iMin = iIndex;
result = (DynamicFunctionSymbol)csr;
}
}
}
}
}
}
return result;
}
void addNameInDeclaration( String strName, int iOffsetName, int iLineNumName, int iColumnName, boolean bHasName )
{
NameInDeclaration name = new NameInDeclaration( strName );
pushExpression( name );
setLocation( bHasName ? iOffsetName : getTokenizer().getPriorToken().getTokenEnd(), iLineNumName, iColumnName, !bHasName, true );
popExpression();
}
private boolean parsingFunctionsEncloseMyClass()
{
if( _parsingFunctions.isEmpty() )
{
return true;
}
for( FunctionType ft : _parsingFunctions )
{
if( ft.getScriptPart().getContainingType() == getGosuClass() )
{
return false;
}
}
return true;
}
FunctionStatement parseProgramEntryPointBody()
{
DynamicFunctionSymbol dfsDecl = getProgramEntryPointDfs();
// if( _tokenizer.getInstructor() == null )
// {
// parseClasspathStatements( true );
// }
_symTable.pushIsolatedScope( new GosuParserTransparentActivationContext( getScriptPart() ) );
boolean bFunctionBodyParsed = true;
StatementList stmtList = null;
_bProgramCallFunction = true;
try
{
if( dfsDecl != null && parseProgramFunctionBody( (FunctionType)dfsDecl.getType() ) )
{
stmtList = (StatementList)popStatement();
}
else
{
bFunctionBodyParsed = false;
}
}
finally
{
_bProgramCallFunction = false;
_symTable.popScope();
}
FunctionStatement fs = new FunctionStatement();
fs.setDynamicFunctionSymbol( dfsDecl );
verify( fs, bFunctionBodyParsed, Res.MSG_EXPECTING_RETURN_TYPE_OR_FUN_BODY );
if (dfsDecl != null) {
dfsDecl.setValueDirectly( stmtList );
}
pushDynamicFunctionSymbol( dfsDecl );
fs.setDynamicFunctionSymbol( dfsDecl );
// if( bFunctionBodyParsed )
// {
addDefaultReturnStmt( dfsDecl, stmtList );
// }
pushStatement( fs );
return fs;
}
private DynamicFunctionSymbol getProgramEntryPointDfs()
{
String functionNameWithArgs = DynamicFunctionSymbol.getSignatureName( "evaluate", Collections.<ISymbol>singletonList( new Symbol( "symbols", JavaTypes.IEXTERNAL_SYMBOL_MAP(), null ) ) );
return (DynamicFunctionSymbol)_symTable.getSymbol( functionNameWithArgs );
}
private StatementList handleExpressionStatementList( Expression expr )
{
Statement stmt;
if( expr.hasParseExceptions() || expr.getType() != JavaTypes.pVOID() || expr instanceof NullExpression )
{
stmt = wrapProgramExpressionInReturnStmt( expr );
}
else if( expr instanceof MethodCallExpression )
{
stmt = new MethodCallStatement();
((MethodCallStatement)stmt).setMethodCall( (MethodCallExpression)expr );
}
else if( expr instanceof BeanMethodCallExpression )
{
stmt = new BeanMethodCallStatement();
((BeanMethodCallStatement)stmt).setBeanMethodCall( (BeanMethodCallExpression)expr );
}
else if( expr instanceof BlockInvocation )
{
stmt = new BlockInvocationStatement((BlockInvocation)expr);
}
else
{
throw new UnsupportedOperationException( "Did not expect expression of type: " + expr.getClass().getName() );
}
pushStatement( stmt );
ParseTree exprLoc = expr.getLocation();
setLocation( exprLoc.getOffset(), exprLoc.getLineNum(), exprLoc.getColumn(), true );
popStatement();
StatementList stmtList = new StatementList( _symTable );
List<Statement> stmts = Collections.singletonList( stmt );
stmtList.setStatements( stmts );
pushStatement( stmtList );
setLocation( exprLoc.getOffset(), exprLoc.getLineNum(), exprLoc.getColumn(), true );
return stmtList;
}
private ReturnStatement wrapProgramExpressionInReturnStmt( Expression e )
{
ReturnStatement retStmt = new ReturnStatement();
retStmt.setSynthetic( true );
e = possiblyWrapWithImplicitCoercion( e, JavaTypes.OBJECT() );
if( e.getType() == JavaTypes.pVOID() )
{
e.setType( JavaTypes.OBJECT() );
}
retStmt.setValue( e );
return retStmt;
}
private void addDefaultReturnStmt( DynamicFunctionSymbol dfsDecl, StatementList stmtList )
{
if( dfsDecl != null && !isTerminal( stmtList, dfsDecl.getReturnType() ) )
{
ReturnStatement defaultReturnStmt = new ReturnStatement();
ImplicitTypeAsExpression ta = new ImplicitTypeAsExpression();
ta.setLHS( new NullExpression() );
ta.setType( JavaTypes.OBJECT() );
ta.setCoercer( IdentityCoercer.instance() );
defaultReturnStmt.setValue( ta );
List<Statement> stmts;
if( stmtList.getStatements() == null )
{
//## todo: Probably short-circuit the condition when a program is an empty expression i.e., don't generate a class for it
stmts = new ArrayList<Statement>( 2 );
}
else
{
stmts = new ArrayList<Statement>( Arrays.asList( stmtList.getStatements() ) );
}
stmts.add( defaultReturnStmt );
stmtList.setStatements( stmts );
}
}
private boolean parseProgramFunctionBody( FunctionType type )
{
maybeSetExternalSymbols();
pushParsingFunction( type );
try
{
setDontOptimizeStatementLists( true );
int state = _tokenizer.mark();
int iLocationsCount = _locations.size();
try
{
parseExpression();
Expression expr = popExpression();
verify( expr, match( null, SourceCodeTokenizer.TT_EOF ), Res.MSG_END_OF_EXPRESSION );
((IGosuProgramInternal)getGosuClass()).setExpression( expr );
verifyParsedElement( expr, false );
handleExpressionStatementList( expr );
}
catch( ParseResultsException exprErr )
{
boolean bProbablyProgram = !getTokenizer().isEOF();
_tokenizer.restoreToMark( state );
_stack.clear();
removeLocationsFrom( iLocationsCount );
try
{
if( !parseStatement( true ) )
{
// Couldn't parse a function body at all
return false;
}
verifyParsedElement( peekStatement(), false );
((IGosuProgramInternal)getGosuClass()).setStatement( peekStatement() );
}
catch( ParseResultsException stmtErr )
{
if( !bProbablyProgram )
{
// Note we can't just rethrow the original exception because we need
// the locations etc. in the parser, so we have to reparse and let it throw.
_tokenizer.restoreToMark( state );
_stack.clear();
removeLocationsFrom( iLocationsCount );
parseExpression();
Expression expr = popExpression();
verify( expr, match( null, SourceCodeTokenizer.TT_EOF ), Res.MSG_END_OF_EXPRESSION );
final IGosuValidator validator = getValidator();
if( validator != null )
{
validator.validate( expr, getScript() );
}
handleExpressionStatementList( expr );
}
// else
// {
// int iProgSourceLen = _tokenizer.getReader().getLength();
// int iProgStmtExtent = peekStatement().getLocation().getExtent() + 1;
// if( iProgStmtExtent < iProgSourceLen )
// {
// ParseTree loc = peekStatement().getLocation();
// loc.setLength( iProgSourceLen - loc.getOffset() );
// }
// }
}
}
Statement body = peekStatement();
if( body instanceof StatementList )
{
((StatementList)body).setNoScope();
}
return true;
}
finally
{
popParsingFunction();
}
}
private void maybeSetExternalSymbols() {
if( getGosuClass() instanceof IGosuProgram )
{
ISourceFileHandle sfh = getGosuClass().getSourceFileHandle();
if( sfh instanceof StringSourceFileHandle )
{
ISymbolTable extSyms = ((StringSourceFileHandle)sfh).getExternalSymbols();
if( extSyms != null )
{
// If extSyms is non-null, it usually means this program is for context-sensitive evaluation e.g., in a debugger
HashMap<String, ISymbol> map = new HashMap<String, ISymbol>();
//noinspection unchecked
for( Symbol s: (Collection<Symbol>)extSyms.getSymbols().values() )
{
map.put( s.getName(), s );
}
ExternalSymbolMapForMap extMap = new ExternalSymbolMapForMap( map );
((GosuProgramParseInfo)getGosuClass().getParseInfo()).setExternalSymbols( extMap );
}
}
}
}
private void removeLocationsFrom( int iLocationsCount )
{
for( int i = _locations.size(); i > iLocationsCount; i-- )
{
_locations.remove( i-1 );
}
}
private boolean isTerminal( Statement statement, IType returnType )
{
boolean[] bAbsolute = {false};
return returnType == JavaTypes.pVOID()
|| statement == null
|| statement.getLeastSignificantTerminalStatement( bAbsolute ) != null && bAbsolute[0];
}
private boolean parseFunctionBody( FunctionStatement functionStmt, FunctionType type )
{
pushParsingFunction( type );
try
{
if( (!(getGosuClass() instanceof IGosuProgram) || !((IGosuProgramInternal)getGosuClass()).isParsingExecutableProgramStatements()) &&
!match( null, null, '{', true ) )
{
IToken T = getTokenizer().getCurrentToken();
eatStatementBlock( functionStmt, Res.MSG_EXPECTING_OPEN_BRACE_FOR_FUNCTION_DEF );
NotAStatement nas = new NotAStatement();
pushStatement( nas );
setLocation( T.getTokenStart(), T.getLine(), T.getTokenColumn() );
}
else if( !parseStatement() )
{
return false;
}
Statement body = peekStatement();
if( body instanceof StatementList )
{
((StatementList)body).setNoScope();
}
return true;
}
finally
{
popParsingFunction();
}
}
DynamicFunctionSymbol parseFunctionDecl( ParsedElement element, ModifierInfo modifiers )
{
return parseFunctionDecl( element, false, false, modifiers );
}
DynamicFunctionSymbol parseFunctionDecl(ParsedElement element, boolean bProperty, boolean bGetter, ModifierInfo modifiers)
{
return parseFunctionDecl( element, null, bProperty, bGetter, modifiers );
}
DynamicFunctionSymbol parseFunctionDecl(ParsedElement element, Token T, boolean bProperty, boolean bGetter, ModifierInfo modifiers)
{
_symTable.pushIsolatedScope(new FunctionDeclTransparentActivationContext(getScriptPart()));
try
{
boolean bHasName = true;
if( T == null )
{
T = new Token();
bHasName = verify( element, match( T, SourceCodeTokenizer.TT_WORD ), Res.MSG_EXPECTING_NAME_FUNCTION_DEF );
}
if( element instanceof IParsedElementWithAtLeastOneDeclaration )
{
((IParsedElementWithAtLeastOneDeclaration)element).setNameOffset( T.getTokenStart(), T._strValue );
}
String strFunctionName = T._strValue;
// if( strFunctionName == null )
// {
// return null;
// }
maybeEatNonDeclKeyword( bHasName, strFunctionName );
strFunctionName = strFunctionName == null ? "" : strFunctionName;
warn( element, !Keyword.isReservedKeyword( strFunctionName ), Res.MSG_IMPROPER_USE_OF_KEYWORD, strFunctionName );
ICompilableType gsClass = getGosuClass();
if( gsClass != null && strFunctionName.equals( gsClass.getRelativeName() ) && gsClass.isEnum() )
{
verify( element, Modifier.isPrivate( modifiers.getModifiers() ), Res.MSG_ENUM_CONSTRUCTOR_MUST_BE_PRIVATE );
}
List<ITypeVariableDefinitionExpression> typeVarDefs = parseTypeVariableDefs( element, true, null );
// Must create function type and assign it as the type var's enclosing
// type *before* we parse the return type (in case it refs function's type vars)
IGenericTypeVariable[] typeVars = TypeVariableDefinition.getTypeVars( typeVarDefs );
strFunctionName = !bProperty
? strFunctionName
: ('@' + strFunctionName);
FunctionType type = new FunctionType( strFunctionName, JavaTypes.pVOID(), null, typeVars );
if( bProperty && !typeVarDefs.isEmpty() )
{
verify( element, false, Res.MSG_GENERIC_PROPERTIES_NOT_SUPPORTED );
}
try
{
verify( element, match( null, '(' ), Res.MSG_EXPECTING_LEFTPAREN_FUNCTION_DEF );
ArrayList<ISymbol> params = null;
IType[] paramTypes = null;
if( !match( null, ')' ) )
{
params = parseParameterDeclarationList( element, Modifier.isStatic( modifiers.getModifiers() ), null, bProperty, bGetter, false );
paramTypes = new IType[params.size()];
for( int i = 0; i < params.size(); i++ )
{
_symTable.putSymbol( params.get( i ) );
paramTypes[i] = params.get( i ).getType();
}
verify( element, match( null, ')' ), Res.MSG_EXPECTING_RIGHTPAREN_FUNCTION_DEF );
}
else
{
verify ( element, ! bProperty || bGetter, Res.MSG_PROPERTY_SET_MUST_HAVE_ONE_PARAMETER );
}
TypeLiteral typeLiteral;
if( match( null, ":", SourceCodeTokenizer.TT_OPERATOR ) )
{
parseTypeLiteral();
typeLiteral = (TypeLiteral)popExpression();
if( bGetter )
{
verify( typeLiteral, typeLiteral.getType().getType() != JavaTypes.pVOID(), Res.MSG_VOID_NOT_ALLOWED );
}
}
else
{
Token fakeT = new Token();
fakeT._strValue = Keyword.KW_void.toString();
typeLiteral = resolveTypeLiteral( fakeT );
verify( element, !bGetter, Res.MSG_MISSING_PROPERTY_RETURN );
}
type.setArgumentTypes( paramTypes );
type.setRetType( typeLiteral.getType().getType() );
type.setScriptPart( getScriptPart() );
DynamicFunctionSymbol dfs = new DynamicFunctionSymbol( _symTable, strFunctionName, type, params, (Statement)null );
dfs.setScriptPart( getScriptPart() );
dfs.setModifierInfo( modifiers );
if( element instanceof FunctionStatement )
{
dfs.setDeclFunctionStmt( (FunctionStatement)element );
}
verifyFunction( dfs, element );
int iDupIndex = nextIndexOfErrantDuplicateDynamicSymbol( dfs, _dfsDeclByName.get( dfs.getDisplayName() ), true );
if( iDupIndex >= 0 )
{
dfs.renameAsErrantDuplicate( iDupIndex );
}
putDfsDeclInSetByName( dfs );
return dfs;
}
finally
{
for( ITypeVariableDefinitionExpression typeVarDef : typeVarDefs )
{
Map<String, ITypeVariableDefinition> typeVarMap = getTypeVariables();
if( typeVarMap.containsValue( typeVarDef ) )
{
typeVarMap.remove( ((ITypeVariableDefinition)typeVarDef).getName() );
}
}
}
}
finally
{
_symTable.popScope();
}
}
boolean maybeEatNonDeclKeyword( boolean bHasName, String strFunctionName )
{
return !bHasName && strFunctionName != null && strFunctionName.length() > 0 &&
!isDeclarationKeyword( strFunctionName ) &&
match( null, SourceCodeTokenizer.TT_KEYWORD );
}
private void verifyFunction( DynamicFunctionSymbol dfs, ParsedElement element )
{
boolean bValidOverrideFound = false;
for( IFunctionSymbol existing : getDfsDeclsForFunction( dfs.getDisplayName() ) )
{
if( existing instanceof DynamicFunctionSymbol )
{
DynamicFunctionSymbol dfsExisting = (DynamicFunctionSymbol)existing;
// proxies are ignored and names must match exactly (error if they do not?)
if( existing.getDisplayName().equals( dfs.getDisplayName() ) )
{
// if the parameters match exactly,
if( areParametersEquivalent( dfs, dfsExisting ) )
{
if( areDFSsInSameNameSpace( dfs, dfsExisting ) )
{
IGosuClass owningTypeForDfs = getOwningTypeForDfs( dfsExisting );
if( owningTypeForDfs instanceof IGosuEnhancement )
{
if( dfs.isOverride() || owningTypeForDfs == getGosuClass() )
{
verify( element, false, Res.MSG_CANNOT_OVERRIDE_FUNCTION_FROM_ENHANCEMENT );
}
else
{
warn( element, false, Res.MSG_MASKING_ENHANCEMENT_METHODS_MAY_BE_CONFUSING );
}
}
else
{
boolean bSameButNotInSameClass = !GosuObjectUtil.equals( dfsExisting.getScriptPart(), dfs.getScriptPart() );
if( !verify( element, bSameButNotInSameClass,
Res.MSG_FUNCTION_ALREADY_DEFINED, dfs.getMethodSignature(), getScriptPart() ) )
{
return;
}
boolean bClassAndReturnTypesCompatible = !GosuObjectUtil.equals( dfsExisting.getScriptPart(), dfs.getScriptPart() ) &&
returnTypesCompatible( dfsExisting, dfs );
if( verify( element, bClassAndReturnTypesCompatible, Res.MSG_FUNCTION_CLASH,
dfs.getName(), dfs.getScriptPart(), dfsExisting.getName(), dfsExisting.getScriptPart() ) )
{
boolean b = !dfsExisting.isFinal() && (getGosuClass() == null ||
getGosuClass().getSupertype() == null ||
!getGosuClass().getSupertype().isFinal());
verify( element, b, Res.MSG_CANNOT_OVERRIDE_FINAL, dfsExisting.getName(), dfsExisting.getScriptPart() );
if( verify( element, !dfs.isStatic() || dfsExisting.isStatic(), Res.MSG_STATIC_METHOD_CANNOT_OVERRIDE, dfs.getName(), dfsExisting.getDeclaringTypeInfo().getName() ) )
{
if( !dfs.isStatic() && !dfsExisting.isStatic() )
{
if( !dfs.isOverride() )
{
boolean bIsConstructorName = getGosuClass() != null && getGosuClass().getRelativeName().equals( dfs.getDisplayName() );
warn( element, bIsConstructorName, Res.MSG_MISSING_OVERRIDE_MODIFIER, dfsExisting.getName(), dfsExisting.getScriptPart().getContainingTypeName() );
if( verify( element, !bIsConstructorName, Res.MSG_SUPER_CLASS_METHOD_NAME_SAME_AS_SUBCLASS, dfsExisting.getName() ) )
{
// Set the override modifier when the modifier is missing
dfs.setOverride( true );
}
}
verifyNotWeakerAccess( element, dfs, dfsExisting );
verifySameNumberOfFunctionTypeVars( element, dfs, dfsExisting );
dfs.setSuperDfs( dfsExisting );
bValidOverrideFound = true;
}
}
}
}
}
}
else
{
// if the parameters do not match, but reify to the same IR types, it is an error
verify( element, !doParametersReifyToSameBytecodeType( dfs, dfsExisting ), Res.MSG_METHOD_REIFIES_TO_SAME_SIGNATURE_AS_ANOTHER_METHOD );
verify( element, !dfs.hasOptionalParameters() && !dfsExisting.hasOptionalParameters(), Res.MSG_OVERLOADING_NOT_ALLOWED_WITH_OPTIONAL_PARAMS );
}
}
}
}
if( !bValidOverrideFound )
{
verifyOverrideNotOnMethodThatDoesNotExtend( element, dfs );
}
verifyNoImplicitPropertyMethodConflicts( element, dfs );
}
private void verifySameNumberOfFunctionTypeVars( ParsedElement element, DynamicFunctionSymbol dfs, DynamicFunctionSymbol dfsExisting )
{
FunctionType dfsType = (FunctionType)dfs.getType();
FunctionType existingDfsType = (FunctionType)dfsExisting.getType();
IGenericTypeVariable[] typeVars = dfsType.getTypeVariables();
IGenericTypeVariable[] existingTypeVars = existingDfsType.getTypeVariables();
verify( element, existingTypeVars.length == typeVars.length, Res.MSG_OVERRIDING_FUNCTION_MUST_HAVE_SAME_NUMBER_OF_TYPE_VARS, existingTypeVars.length );
}
private boolean areDFSsInSameNameSpace( IDynamicSymbol newDfs, IDynamicSymbol existingDfs )
{
IGosuClass newType = getOwningTypeForDfs( newDfs );
IType existingType = getOwningTypeForDfs( existingDfs );
if( newType == null )
{
return existingType == null;
}
else if( newType.isAnonymous() || newType.getEnclosingType() != null )
{
if( existingType instanceof IGosuEnhancement )
{
existingType = ((IGosuEnhancement)existingType).getEnhancedType();
}
if( IGosuClass.ProxyUtil.isProxy( existingType ) )
{
IType type = IGosuClass.ProxyUtil.getProxiedType( existingType );
return type.isAssignableFrom( newType ) || JavaTypes.IGOSU_OBJECT().equals( type );
}
else
{
return existingType.isAssignableFrom( newType );
}
}
else
{
return true;
}
}
private void verifyNoImplicitPropertyMethodConflicts( ParsedElement element, DynamicFunctionSymbol dfs )
{
String name = dfs.getDisplayName();
if( name.startsWith( "@" ) )
{
String propName = name.substring( 1 );
if( dfs.getArgs().size() == 0 )
{
for( IFunctionSymbol func : getDfsDeclsForFunction( "get" + propName ) )
{
if( func instanceof DynamicFunctionSymbol )
{
DynamicFunctionSymbol existingDfs = (DynamicFunctionSymbol)func;
if( areDFSsInSameNameSpace( dfs, existingDfs ) )
{
verify( element, existingDfs.getArgs().size() != 0, Res.MSG_PROPERTY_AND_FUNCTION_CONFLICT, existingDfs.getName(), propName );
}
}
}
}
else
{
for( IFunctionSymbol func : getDfsDeclsForFunction( "set" + propName ) )
{
if( func instanceof DynamicFunctionSymbol )
{
DynamicFunctionSymbol existingDfs = (DynamicFunctionSymbol)func;
if( areDFSsInSameNameSpace( dfs, existingDfs ) )
{
if( existingDfs.getArgs().size() == 1 )
{
IType argType = dfs.getArgs().get( 0 ).getType();
IType existingArgType = existingDfs.getArgs().get( 0 ).getType();
if( argType.equals( existingArgType ) )
{
verify( element, false, Res.MSG_PROPERTY_AND_FUNCTION_CONFLICT, existingDfs.getName(), propName );
}
if( doTypesReifyToTheSameBytecodeType( argType, existingArgType ) )
{
verify( element, false, Res.MSG_PROPERTY_AND_FUNCTION_CONFLICT_UPON_REIFICATION, existingDfs.getName(), propName );
}
}
}
}
}
}
}
else
{
if( name.startsWith( "set" ) && dfs.getArgs().size() == 1 )
{
ISymbol symbol = getSymbolTable().getSymbol( name.substring( 3, name.length() ) );
if( symbol instanceof DynamicPropertySymbol )
{
DynamicPropertySymbol dps = (DynamicPropertySymbol)symbol;
if( areDFSsInSameNameSpace( dfs, dps ) )
{
if( dps.getSetterDfs() != null )
{
IType argType = dfs.getArgs().get( 0 ).getType();
if( argType.equals( dps.getType() ) )
{
verify( element, false, Res.MSG_PROPERTY_AND_FUNCTION_CONFLICT, dfs.getName(), dps.getName() );
}
else if( doTypesReifyToTheSameBytecodeType( argType, dps.getType() ) )
{
verify( element, false, Res.MSG_PROPERTY_AND_FUNCTION_CONFLICT_UPON_REIFICATION, dfs.getName(), dps.getName() );
}
}
}
}
}
else if( name.startsWith( "get" ) && dfs.getArgs().size() == 0 )
{
ISymbol symbol = getSymbolTable().getSymbol( name.substring( 3, name.length() ) );
if( symbol instanceof DynamicPropertySymbol )
{
DynamicPropertySymbol dps = (DynamicPropertySymbol)symbol;
if( areDFSsInSameNameSpace( dfs, dps ) )
{
verify( element, dps.getGetterDfs() == null, Res.MSG_PROPERTY_AND_FUNCTION_CONFLICT, dfs.getName(), dps.getName() );
}
}
}
}
}
private boolean returnTypesCompatible( DynamicFunctionSymbol dfsExisting, DynamicFunctionSymbol dfs )
{
IType existingReturnType = maybeResolveFunctionTypeVars( dfsExisting, dfsExisting.getReturnType() );
IType overrideReturnType = maybeResolveFunctionTypeVars( dfs, dfs.getReturnType() );
if( existingReturnType.isAssignableFrom( overrideReturnType ) )
{
return true;
}
else {
return dfs.getReturnType() instanceof ErrorType;
}
}
public boolean areParametersEquivalent(IDynamicFunctionSymbol dfs1, IDynamicFunctionSymbol dfs2) {
IType[] args = ((FunctionType)dfs1.getType()).getParameterTypes();
IType[] toArgs = ((FunctionType)dfs2.getType()).getParameterTypes();
if( args == null || args.length == 0 )
{
return toArgs == null || toArgs.length == 0;
}
if( toArgs == null )
{
return false;
}
if( args.length != toArgs.length )
{
return false;
}
for( int i = 0; i < args.length; i++ )
{
IType argType = maybeResolveFunctionTypeVars( dfs1, args[i] );
IType toArgType = maybeResolveFunctionTypeVars( dfs2, toArgs[i] );
if( !argType.equals( toArgType ) )
{
// Note we use equals() check instead of == to handle non-loadable types e.g., TypeVariableTypes
return false;
}
}
return true;
}
private IType maybeResolveFunctionTypeVars( IDynamicFunctionSymbol dfs, IType type )
{
if (TypeSystem.isDeleted(type)) {
return TypeSystem.getErrorType();
}
FunctionType iType = (FunctionType)dfs.getType();
ArrayList<IType> functionTypeVars = new ArrayList<IType>();
for( IGenericTypeVariable typeVariable : iType.getTypeVariables() )
{
functionTypeVars.add( typeVariable.getTypeVariableDefinition().getType() );
}
return TypeLord.boundTypes( type, functionTypeVars );
}
public boolean doParametersReifyToSameBytecodeType( IDynamicFunctionSymbol dfs, IDynamicFunctionSymbol dfsExisting )
{
IType[] toArgs = ((FunctionType) dfsExisting.getType()).getParameterTypes();
IType[] args = ((FunctionType)dfs.getType()).getParameterTypes();
if( args == null || args.length == 0 )
{
return toArgs == null || toArgs.length == 0;
}
if( toArgs == null )
{
return false;
}
if( args.length != toArgs.length )
{
return false;
}
for( int i = 0; i < args.length; i++ )
{
if( !doTypesReifyToTheSameBytecodeType( toArgs[i], args[i] ) )
{
return false;
}
}
return true;
}
private boolean doTypesReifyToTheSameBytecodeType( IType toArg, IType arg )
{
IRType argType = IRTypeResolver.getDescriptor( arg );
argType = eraseIfStructuralType( argType );
IRType toArgType = IRTypeResolver.getDescriptor( toArg );
toArgType = eraseIfStructuralType( toArgType );
return argType.equals( toArgType );
}
private IRType eraseIfStructuralType( IRType argType ) {
return argType.isStructural()
? IRTypeResolver.getDescriptor( Object.class )
: argType;
}
private IGosuClass getOwningTypeForDfs( IDynamicSymbol dfs )
{
if( dfs.getScriptPart() != null && dfs.getScriptPart().getContainingType() instanceof IGosuClass )
{
return (IGosuClass)dfs.getScriptPart().getContainingType();
}
else
{
return null;
}
}
private void verifyNotWeakerAccess( ParsedElement element, DynamicFunctionSymbol dfs, DynamicFunctionSymbol dfsExisting )
{
if( dfsExisting.isPublic() )
{
verify( element, dfs.isPublic(),
Res.MSG_ATTEMPTING_TO_ASSIGN_WEAKER_ACCESS_PRIVILEGES,
dfs.getName(), dfs.getScriptPart(),
dfsExisting.getName(), dfsExisting.getScriptPart() );
}
else if( dfsExisting.isProtected() )
{
verify( element, dfs.isPublic() || dfs.isProtected(),
Res.MSG_ATTEMPTING_TO_ASSIGN_WEAKER_ACCESS_PRIVILEGES,
dfs.getName(), dfs.getScriptPart(),
dfsExisting.getName(), dfsExisting.getScriptPart() );
}
else if( dfsExisting.isInternal() )
{
verify( element, dfs.isPublic() || dfs.isProtected() || dfs.isInternal(),
Res.MSG_ATTEMPTING_TO_ASSIGN_WEAKER_ACCESS_PRIVILEGES,
dfs.getName(), dfs.getScriptPart(),
dfsExisting.getName(), dfsExisting.getScriptPart() );
}
}
//------------------------------------------------------------------------------
public ArrayList<ISymbol> parseParameterDeclarationList( IParsedElement element, boolean bStatic, List<IType> inferredArgumentTypes )
{
return parseParameterDeclarationList( element, bStatic, inferredArgumentTypes, false, false, false );
}
public ArrayList<ISymbol> parseParameterDeclarationList( IParsedElement element, boolean bStatic, List<IType> inferredArgumentTypes, boolean bProperty, boolean bGetter, boolean bEmpty )
{
ArrayList<ISymbol> params = new ArrayList<ISymbol>();
Token T = new Token();
int iParamPos = -1;
boolean bOptionalParamsStarted = false;
int iOffsetList = getTokenizer().getTokenStart();
int iLineNumList = getTokenizer().getLineNumber();
int iColumnList = getTokenizer().getTokenColumn();
do
{
iParamPos++;
int iOffsetParam = getTokenizer().getTokenStart();
int iLineNumParam = getTokenizer().getLineNumber();
int iColumnParam = getTokenizer().getTokenColumn();
List<IGosuAnnotation> annotations = parseLocalAnnotations( Collections.<IGosuAnnotation>emptyList() );
boolean bFinal = match( T, Keyword.KW_final );
int iOffsetArgIdentifier = getTokenizer().getTokenStart();
int iColumnArgIdentifier = getTokenizer().getTokenColumn();
int iLineArgIdentifier = getTokenizer().getLineNumber();
IToken tokenBeforeParam = getTokenizer().getCurrentToken();
boolean bMatchColonWithoutName = false;
if( bEmpty || !verify( (ParsedElement)element, match( T, SourceCodeTokenizer.TT_WORD ), Res.MSG_EXPECTING_ARGS, "" ) )
{
bMatchColonWithoutName = match( null, ":", SourceCodeTokenizer.TT_OPERATOR, true );
if( !bMatchColonWithoutName )
{
break;
}
else
{
T._strValue = "";
}
}
ModifierListClause e = new ModifierListClause();
pushExpression( e );
boolean bZeroLength = tokenBeforeParam.getTokenStart() <= iOffsetParam;
setLocation( iOffsetParam, iLineNumParam, iColumnParam, bZeroLength, true );
popExpression();
String strArgIdentifier = T._strValue;
ParameterDeclaration parameterIdentifier = new ParameterDeclaration( strArgIdentifier );
addNameInDeclaration( strArgIdentifier, iOffsetArgIdentifier, iLineArgIdentifier, iColumnArgIdentifier, strArgIdentifier.length() > 0 || bMatchColonWithoutName );
ISymbol existingSymbol = _symTable.getSymbol( strArgIdentifier );
if( existingSymbol == null )
{
for( ISymbol symbol : params )
{
if( symbol.getName().equals( strArgIdentifier ) )
{
existingSymbol = symbol;
}
}
}
boolean bSymbolConflict = existingSymbol != null;
if( bStatic && existingSymbol instanceof DynamicSymbol )
{
bSymbolConflict = existingSymbol.isStatic();
}
verify( parameterIdentifier, !bSymbolConflict, Res.MSG_VARIABLE_ALREADY_DEFINED, strArgIdentifier );
verify( parameterIdentifier, ! bProperty || bGetter || iParamPos == 0, Res.MSG_PROPERTY_SET_MUST_HAVE_ONE_PARAMETER );
IType argType;
Expression defExpr = null;
boolean bColonFound = match( null, ":", SourceCodeTokenizer.TT_OPERATOR );
boolean bEqualsFound = match( null, "=", SourceCodeTokenizer.TT_OPERATOR );
boolean bParenFound = match( null, null, '(', true );
if( inferredArgumentTypes != null && !bColonFound && !bEqualsFound && !bParenFound )
{
if( inferredArgumentTypes.size() > iParamPos )
{
argType = inferredArgumentTypes.get(iParamPos);
}
else
{
argType = ErrorType.getInstance();
}
}
else
{
if( bParenFound )
{
parseBlockLiteral();
}
else
{
verify( parameterIdentifier, bColonFound || bEqualsFound, Res.MSG_EXPECTING_TYPE_FUNCTION_DEF );
if( bEqualsFound )
{
parseExpression();
}
else
{
parseTypeLiteral();
}
}
Expression expr = popExpression();
if( bEqualsFound )
{
defExpr = expr;
argType = defExpr.hasParseExceptions()
? JavaTypes.pVOID()
: expr instanceof NullExpression
? JavaTypes.OBJECT()
: expr.getType();
}
else
{
argType = ((TypeLiteral)expr).getType().getType();
}
verify( expr, bEqualsFound || argType != JavaTypes.pVOID(), Res.MSG_VOID_NOT_ALLOWED );
if( bColonFound && match( null, "=", SourceCodeTokenizer.TT_OPERATOR ) )
{
int iOffsetDef = getTokenizer().getTokenStart();
int iLineNumDef = getTokenizer().getLineNumber();
int iColumnDef = getTokenizer().getTokenColumn();
parseExpression( argType );
defExpr = popExpression();
defExpr = possiblyWrapWithImplicitCoercion( defExpr, argType );
pushExpression( defExpr );
setLocation( iOffsetDef, iLineNumDef, iColumnDef );
popExpression();
}
}
if( strArgIdentifier != null )
{
Symbol symbol = new TypedSymbol( strArgIdentifier, argType, _symTable, null, SymbolType.PARAMETER_DECLARATION );
if( defExpr != null )
{
verifyComparable( argType, defExpr, true );
verify( defExpr, defExpr.isCompileTimeConstant(), Res.MSG_COMPILE_TIME_CONSTANT_REQUIRED );
verify( (ParsedElement)element, argType != JavaTypes.pVOID() || !defExpr.hasParseExceptions(),
Res.MSG_PARAM_TYPE_CANT_BE_INFERRED_FROM_LATE_BOUND_EXPRESSION );
symbol.setDefaultValueExpression( defExpr );
bOptionalParamsStarted = true;
}
else
{
verify( parameterIdentifier, !bOptionalParamsStarted, Res.MSG_EXPECTING_DEFAULT_VALUE );
}
symbol.setFinal( bFinal );
symbol.getModifierInfo().setAnnotations( annotations );
params.add( symbol );
parameterIdentifier.setType( argType );
pushExpression( parameterIdentifier );
setLocation( iOffsetParam, iLineNumParam, iColumnParam, true );
popExpression();
if( getGosuClass() instanceof IGosuClassInternal && ((IGosuClassInternal)getGosuClass()).isCompilingDefinitions() )
{
verifyModifiers( parameterIdentifier, symbol.getModifierInfo(), UsageTarget.ParameterTarget );
}
}
}
while( match( null, ',' ) );
ParameterListClause e = new ParameterListClause();
pushExpression( e );
boolean bZeroLength = getTokenizer().getTokenStart() <= iOffsetList;
setLocation( bZeroLength ? getTokenizer().getPriorToken().getTokenEnd() : iOffsetList, iLineNumList, iColumnList, bZeroLength, true );
popExpression();
params.trimToSize();
return params;
}
private List<IGosuAnnotation> parseLocalAnnotations( List<IGosuAnnotation> annotations ) {
while( 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 );
}
return annotations;
}
//------------------------------------------------------------------------------
// type-variables
// < <type-variable-list> >
//
List<ITypeVariableDefinitionExpression> parseTypeVariableDefs( ParsedElement parsedElem, boolean bFunction, List<TypeVariableDefinitionImpl> typeVarDefListFromDecl )
{
int iOffset = getTokenizer().getTokenStart();
int iLineNum = getTokenizer().getLineNumber();
int iColumn = getTokenizer().getTokenColumn();
if( !match( null, "<", SourceCodeTokenizer.TT_OPERATOR ) )
{
return Collections.emptyList();
}
List<ITypeVariableDefinitionExpression> typeVarDefList = parseTypeVariableDefList( parsedElem, bFunction, typeVarDefListFromDecl );
verify( parsedElem, match( null, ">", SourceCodeTokenizer.TT_OPERATOR ),
Res.MSG_EXPECTING_CLOSING_ANGLE_BRACKET_FOR_TYPE_VAR_LIST );
// For editors only
TypeVariableListClause e = new TypeVariableListClause( typeVarDefList );
pushExpression( e );
setLocation( iOffset, iLineNum, iColumn, true );
popExpression();
return typeVarDefList;
}
//------------------------------------------------------------------------------
// type-variable-list
// <type-variable>
// <type-variable-list> , <type-variable>
//
List<ITypeVariableDefinitionExpression> parseTypeVariableDefList( ParsedElement parsedElem, boolean bForFunction, List<TypeVariableDefinitionImpl> typeVarDefListFromDecl )
{
List<ITypeVariableDefinitionExpression> typeVarDefList = new ArrayList<ITypeVariableDefinitionExpression>();
int i = 0;
do
{
TypeVariableDefinition typeVarDef = new TypeVariableDefinition( getEnclosingType(), bForFunction );
if( typeVarDefListFromDecl != null && !typeVarDefListFromDecl.isEmpty() )
{
typeVarDef.setTypeVarDef( typeVarDefListFromDecl.get( i++ ) );
}
parseTypeVariableDefinition( parsedElem, typeVarDef );
typeVarDef = (TypeVariableDefinition)popExpression();
for( ITypeVariableDefinition csr : _typeVarsByName.values() )
{
if( !verify( typeVarDef, !csr.getName().equals( typeVarDef.getName() ) ||
((TypeVariableDefinition)csr).getLocation().getExtent() == typeVarDef.getLocation().getExtent(),
Res.MSG_VARIABLE_ALREADY_DEFINED, typeVarDef.getName() ) )
{
break;
}
}
typeVarDefList.add( typeVarDef );
}
while( match( null, ',' ) );
return typeVarDefList;
}
//------------------------------------------------------------------------------
// type-variable
// <identifier> [extends <type-literal>]
//
void parseTypeVariableDefinition( ParsedElement parsedElem, TypeVariableDefinition typeVarDef )
{
int iOffset = getTokenizer().getTokenStart();
int iLineNum = getTokenizer().getLineNumber();
int iColumn = getTokenizer().getTokenColumn();
if( _parseTypeVariableDefinition( parsedElem, typeVarDef ) )
{
setLocation( iOffset, iLineNum, iColumn );
}
}
boolean _parseTypeVariableDefinition( ParsedElement parsedElem, TypeVariableDefinition typeVarDef )
{
Token T = new Token();
if( verify( parsedElem, match( T, SourceCodeTokenizer.TT_WORD ), Res.MSG_EXPECTING_IDENTIFIER_EXISTS ) )
{
typeVarDef.setName( T._strValue );
Map<String, ITypeVariableDefinition> typeVarMap = getTypeVariables();
if( !typeVarMap.containsKey( typeVarDef.getName() ) )
{
getTypeVariables().put( typeVarDef.getName(), typeVarDef );
}
int iOffset = getTokenizer().getTokenStart();
int iLineNum = getTokenizer().getLineNumber();
int iColumn = getTokenizer().getTokenColumn();
IType boundingType = null;
boolean bExtends;
if( bExtends = match( null, Keyword.KW_extends ) )
{
typeVarDef.setBoundingType( PENDING_BOUNDING_TYPE );
parseTypeLiteral();
boxTypeLiteralsType( (TypeLiteral)peekExpression() );
TypeLiteral typeLiteral = (TypeLiteral)popExpression();
boundingType = typeLiteral.getType().getType();
if( verify( typeLiteral, boundingType != typeVarDef.getType(), Res.MSG_CYCLIC_INHERITANCE, boundingType.getRelativeName() ) )
{
typeVarDef.setBoundingType( boundingType );
}
}
// For editors only
TypeVariableExtendsListClause e = new TypeVariableExtendsListClause( boundingType );
pushExpression( e );
// Note for a zero-length extends clause we must shift it to the left one char to avoid having it follow the enclosing TypeVariableListClause
setLocation( iOffset - (bExtends ? 0 : 1), iLineNum, iColumn, !bExtends, true );
popExpression();
}
else
{
typeVarDef.setName( "" );
verify( typeVarDef, false, Res.MSG_ERRANT_TYPE_VAR );
Map<String, ITypeVariableDefinition> typeVarMap = getTypeVariables();
if( !typeVarMap.containsKey( typeVarDef.getName() ) )
{
getTypeVariables().put( typeVarDef.getName(), typeVarDef );
}
pushExpression( typeVarDef );
// Set the location to zero length at the end of the last token
IToken priorT = getTokenizer().getPriorToken();
setLocation( priorT.getTokenEnd(), priorT.getLine(), priorT.getTokenColumn() );
return false;
}
pushExpression( typeVarDef );
return true;
}
private IType getEnclosingType()
{
if( isParsingFunction() )
{
return peekParsingFunction();
}
return getScriptPart() == null ? null : getScriptPart().getContainingType();
}
//------------------------------------------------------------------------------
@Override
protected void pushExpression( Expression e )
{
assert e != null;
maybeVerifyDoubleLiterals( e );
_stack.push( e );
}
private void maybeVerifyDoubleLiterals( Expression e )
{
if( e instanceof AdditiveExpression || e instanceof MultiplicativeExpression )
{
IArithmeticExpression ae = (IArithmeticExpression) e;
maybeVerifyDoubleLiteral( ae.getLHS(), ae.getRHS() );
maybeVerifyDoubleLiteral( ae.getRHS(), ae.getLHS() );
}
}
private void maybeVerifyDoubleLiteral( IExpression oneSide, IExpression otherSide )
{
if( (JavaTypes.BIG_DECIMAL().equals( oneSide.getType() ) || JavaTypes.BIG_INTEGER().equals( oneSide.getType() )) && JavaTypes.pDOUBLE().equals( otherSide.getType() ))
{
if( otherSide instanceof UnaryExpression )
{
otherSide = ((UnaryExpression)otherSide).getExpression();
}
if( otherSide instanceof NumericLiteral )
{
NumericLiteral nl = (NumericLiteral)otherSide;
boolean repsAreIdentical = new BigDecimal( nl.getStrValue() ).equals( CommonServices.getCoercionManager().makeBigDecimalFrom( nl.getValue() ) );
verify( (ParsedElement)otherSide, repsAreIdentical, Res.MSG_LOSS_OF_PRECISION_IN_NUMERIC_LITERAL, nl.getStrValue() + "bd" );
}
}
}
//------------------------------------------------------------------------------
@Override
public Expression popExpression()
{
return (Expression)_stack.pop();
}
@Override
public String parseDotPathWord()
{
Token token = new Token();
if( match(token, ISourceCodeTokenizer.TT_WORD) )
{
parseDotPathWord( token );
return token.getStringValue();
}
else
{
return null;
}
}
@Override
public void setTokenizer( ISourceCodeTokenizer tokenizer )
{
_tokenizer = (SourceCodeTokenizer)tokenizer;
}
//------------------------------------------------------------------------------
@Override
protected Expression peekExpression()
{
IParsedElement elem = peekParsedElement();
return elem instanceof Expression ? (Expression)elem : null;
}
//------------------------------------------------------------------------------
protected ParsedElement peekParsedElement()
{
if( _stack.isEmpty() )
{
return null;
}
return _stack.peek();
}
//------------------------------------------------------------------------------
@Override
protected void pushStatement( Statement stmt )
{
_stack.push( stmt );
}
//------------------------------------------------------------------------------
@Override
protected Statement popStatement()
{
ParsedElement stmt = _stack.pop();
return (Statement)stmt;
}
@Override
protected Statement peekStatement()
{
IParsedElement elem = peekParsedElement();
return elem instanceof Statement ? (Statement)elem : null;
}
protected void pushDynamicFunctionSymbol( DynamicFunctionSymbol stmt )
{
_stackDFS.push( stmt );
}
protected DynamicFunctionSymbol popDynamicFunctionSymbol()
{
return _stackDFS.pop();
}
protected DynamicFunctionSymbol peekDynamicFunctionSymbol()
{
if( _stackDFS.isEmpty() )
{
return null;
}
return _stackDFS.peek();
}
protected void clearDfsStack()
{
_stackDFS.clear();
}
public void putDfsDeclsInTable( ISymbolTable table )
{
if( table == null )
{
return;
}
for( Iterator iterator = table.getSymbols().values().iterator(); iterator.hasNext(); )
{
ISymbol symbol = (ISymbol)iterator.next();
if( symbol instanceof DynamicFunctionSymbol )
{
putDfsDeclInSetByName( (DynamicFunctionSymbol)symbol );
}
}
}
public void putDfsDeclInSetByName( IDynamicFunctionSymbol dfs )
{
String displayName = dfs.getDisplayName();
Set<IFunctionSymbol> dfsDecls = _dfsDeclByName.get( displayName );
if( dfsDecls == null )
{
dfsDecls = new HashSet<IFunctionSymbol>();
try
{
_dfsDeclByName.put( displayName, dfsDecls );
}
catch( Exception e )
{
throw new RuntimeException( "Map type: " + _dfsDeclByName.getClass().getName(), e );
}
}
if( !dfsDecls.add(dfs) )
{
// Replace old
dfsDecls.remove( dfs );
boolean bAdd = dfsDecls.add(dfs);
assert bAdd;
}
}
public int nextIndexOfErrantDuplicateDynamicSymbol( IDynamicSymbol ds, Collection<? extends ISymbol> symbols, boolean bCheckContains )
{
if( symbols == null )
{
return -1;
}
int iMax = -1;
if( !bCheckContains || symbolIn( ds, symbols ) )
{
for( ISymbol csr : symbols )
{
boolean bInjected = csr instanceof IInjectedSymbol;
if( !(csr instanceof IDynamicSymbol) && !bInjected )
{
continue;
}
String strName = csr.getName();
if( csr.getGosuClass() == ds.getGosuClass() || bInjected )
{
if( iMax < 0 && strName.equals( ds.getName() ) )
{
iMax = 0;
}
else if( strName.toLowerCase().contains( "_duplicate_" + ds.getName().toLowerCase() ) )
{
int iIndex = Integer.parseInt( strName.substring( 0, strName.indexOf( '_' ) ) );
if( iIndex > iMax )
{
iMax = iIndex;
}
}
}
}
}
return iMax < 0 ? iMax : iMax+1;
}
private boolean symbolIn( IDynamicSymbol ds, Collection<? extends ISymbol> symbols )
{
for( ISymbol s : symbols )
{
if( s.getName().equals( ds.getName() ) )
{
return true;
}
}
return false;
}
public void setDfsDeclInSetByName( Map<String, Set<IFunctionSymbol>> dfsDecl )
{
_dfsDeclByName = dfsDecl;
}
protected void newDfsDeclInSetByName()
{
_dfsDeclByName = new HashMap<String, Set<IFunctionSymbol>>();
}
public Map<String, Set<IFunctionSymbol>> getDfsDecls()
{
return _dfsDeclByName;
}
protected List<IFunctionType> getFunctionTypesForName( String strFunctionName )
{
List<IFunctionSymbol> list = getDfsDeclsForFunction( strFunctionName );
List<IFunctionType> listOfTypes = new ArrayList<IFunctionType>( list.size() );
for (IFunctionSymbol dfs : list)
{
listOfTypes.add((FunctionType) dfs.getType());
}
return listOfTypes;
}
protected TypeLiteral resolveTypeLiteral( Token T )
{
return resolveTypeLiteral( T, true, false );
}
protected TypeLiteral resolveTypeLiteral( Token T, boolean bRelative, boolean bInterface )
{
String strTypeName = T._strValue == null ? "" : T._strValue;
return resolveTypeLiteral( strTypeName, bRelative, bInterface );
}
protected List<IFunctionSymbol> getDfsDeclsForFunction( String strFunctionName )
{
Set<IFunctionSymbol> setOfDfsDecls = _dfsDeclByName.get( strFunctionName );
return setOfDfsDecls == null ? Collections.<IFunctionSymbol>emptyList() : new ArrayList<IFunctionSymbol>(setOfDfsDecls);
}
/**
* Resolves the type literal given by strTypeName. If parentType is non null then strTypeName is assumed relative to
* the given parent.
* @param strTypeName
*/
public TypeLiteral resolveTypeLiteral( String strTypeName )
{
return resolveTypeLiteral( strTypeName, true, false );
}
public TypeLiteral resolveTypeLiteral( String strTypeName, boolean bRelative, boolean bInterface )
{
int iArrayDims = 0;
if( strTypeName.length() > 0 && strTypeName.charAt( strTypeName.length()-1 ) == ']' )
{
int iFirstBracketIndex = strTypeName.indexOf( '[' );
strTypeName = strTypeName.substring( 0, iFirstBracketIndex );
iArrayDims = (strTypeName.length() - iFirstBracketIndex)/2;
}
IType intrType;
boolean bClassTypeVar = false;
ITypeVariableDefinition typeVarDef = getTypeVarDef( strTypeName );
if( typeVarDef != null )
{
bClassTypeVar = !typeVarDef.getType().isFunctionStatement();
intrType = typeVarDef.getType();
if( intrType == null )
{
intrType = resolveInnerClassByRelativeName( strTypeName );
}
}
else
{
intrType = resolveInnerClassByRelativeName( strTypeName );
if( intrType == null )
{
intrType = resolveTypeName( strTypeName, bRelative );
}
}
if( intrType == null )
{
intrType = ErrorType.getInstance( strTypeName );
}
IType finalType = intrType;
for( int i = 0; i < iArrayDims; i++ )
{
finalType = finalType.getArrayType();
}
if (TypeSystem.isDeleted(finalType)) {
finalType = TypeSystem.getErrorType();
}
TypeLiteral typeLiteral = bInterface
? new InterfaceTypeLiteral( MetaType.getLiteral( finalType ), _ignoreTypeDeprecation > 0 )
: new TypeLiteral( MetaType.getLiteral( finalType ), _ignoreTypeDeprecation > 0 );
verify( typeLiteral, !bClassTypeVar || !isParsingStaticFeature() || isParsingConstructor(), Res.MSG_CANNOT_REFERENCE_CLASS_TYPE_VAR_IN_STATIC_CONTEXT );
if( verify( typeLiteral, !(intrType instanceof ErrorType) || IErrorType.NAME.equals( strTypeName ), Res.MSG_INVALID_TYPE, strTypeName ) )
{
verifyCase( typeLiteral, strTypeName, intrType.getName(), Res.MSG_TYPE_CASE_MISMATCH, true );
}
return typeLiteral;
}
private IType resolveTypeName( String strTypeName, boolean bRelative )
{
return resolveTypeName_Cache( strTypeName, bRelative );
// return resolveTypeName_NoCache( strTypeName, bRelative );
}
private IType resolveTypeName_NoCache( String strTypeName, boolean bRelative )
{
IType type;
if( bRelative )
{
type = TypeLoaderAccess.instance().getTypeByRelativeNameIfValid_NoGenerics(strTypeName, getTypeUsesMap());
}
else
{
type = TypeLoaderAccess.instance().getByFullNameIfValid( strTypeName );
}
return type;
}
private IType resolveTypeName_Cache( String strTypeName, boolean bRelative )
{
IType type;
Map<String, IType> cache = _typeCache.get( null );
if( cache == null )
{
cache = new HashMap<String, IType>();
_typeCache.put( getScriptPart(), cache );
type = null;
}
else
{
type = cache.get( strTypeName );
}
if( type == null )
{
type = resolveTypeName_NoCache( strTypeName, bRelative );
if( type == null )
{
type = notfound;
}
cache.put( strTypeName, type );
}
return type == null || type == notfound
? null
: type;
}
private ITypeVariableDefinition getTypeVarDef( String strTypeName )
{
ITypeVariableDefinition typeVarDef = getTypeVariables().get( strTypeName );
if( typeVarDef != null && getGosuClass() != null && getGosuClass().isStatic() && TypeLord.encloses( typeVarDef.getEnclosingType(), getGosuClass() ) )
{
// Static inner class cannot access enclosing type vars
typeVarDef = null;
}
if( typeVarDef == null )
{
typeVarDef = getEnclosingTypeVars().get( strTypeName );
}
return typeVarDef;
}
//## do we still need this method since inner class parsing re-uses the enclosing class' owner/parser
private Map<String, TypeVariableDefinition> getEnclosingTypeVars()
{
ICompilableType gsClass = getGosuClass();
if( gsClass == null )
{
return Collections.emptyMap();
}
Map<String, TypeVariableDefinition> mapTypeVarDefByName = new HashMap<String, TypeVariableDefinition>( 2 );
while( !gsClass.isStatic() && gsClass.getEnclosingType() != null )
{
// Note we don't resolve type vars defined in outer for static inner classes.
// This is because we do not maintain separate inner classes for each
// parameterization of a generic outer class
ICompilableType enclosingType = gsClass.getEnclosingType();
for( IGenericTypeVariable gtv : enclosingType.getGenericTypeVariables() )
{
Map<String, ITypeVariableDefinition> typeVarMap = getTypeVariables();
if( !typeVarMap.containsKey( gtv.getTypeVariableDefinition().getName() ) )
{
getTypeVariables().put( gtv.getName(), gtv.getTypeVariableDefinition() );
}
}
gsClass = enclosingType;
}
return mapTypeVarDefByName;
}
private IType resolveInnerClassByRelativeName( String strTypeName )
{
ICompilableType gsClass = getGosuClass();
if( gsClass == null )
{
return null;
}
return gsClass.resolveRelativeInnerClass( strTypeName, false );
}
public Map<String, ITypeVariableDefinition> getTypeVariables()
{
return _typeVarsByName;
}
public IGosuClassInternal parseClass( String strQualifiedClassName, ISourceFileHandle sourceFile, boolean bThrowOnWarnings, boolean bFullyCompile ) throws ParseResultsException
{
GosuClassTypeLoader classLoader;
if (!CommonServices.getPlatformHelper().isInIDE()) {
classLoader = GosuClassTypeLoader.getDefaultClassLoader(TypeSystem.getGlobalModule());
} else {
IFile file = sourceFile.getFile();
IModule module = TypeSystem.getExecutionEnvironment().getModule(file);
if (module == null) {
// these are files outside of the typesystem (i.e. not in any source root)
classLoader = GosuClassTypeLoader.getDefaultClassLoader(TypeSystem.getGlobalModule());
} else {
classLoader = module.getModuleTypeLoader().getTypeLoader(GosuClassTypeLoader.class);
}
}
IGosuClassInternal gsClass = (IGosuClassInternal)classLoader.makeNewClass(sourceFile, _symTable);
gsClass.setEditorParser(this);
gsClass.setCreateEditorParser(isEditorParser());
try
{
if( bFullyCompile )
{
gsClass.compileDefinitionsIfNeeded( true );
}
else
{
gsClass.compileDeclarationsIfNeeded();
}
}
catch( ErrantGosuClassException e )
{
//ignore
}
//noinspection ThrowableResultOfMethodCallIgnored
if( gsClass.getParseResultsException() != null )
{
//noinspection ThrowableResultOfMethodCallIgnored
if( gsClass.getParseResultsException().hasParseExceptions() || bThrowOnWarnings )
{
throw gsClass.getParseResultsException();
}
}
return gsClass;
}
public IFunctionType getFunctionType( IType classBean, String functionName, Expression[] eArgs, List<IFunctionType> listAllMatchingMethods, GosuParser parser, boolean bMatchParamTypes ) throws ParseException
{
if( classBean == null )
{
throw new ParseException( parser == null ? null : parser.makeFullParserState(), Res.MSG_BEAN_CLASS_IS_NULL );
}
if( functionName == null )
{
throw new ParseException( parser == null ? null : parser.makeFullParserState(), Res.MSG_BEAN_MEMBER_PATH_IS_NULL );
}
ITypeInfo beanInfo = classBean.getTypeInfo();
if( beanInfo == null )
{
throw new ParseException( parser == null ? null : parser.makeFullParserState(), Res.MSG_NO_EXPLICIT_TYPE_INFO_FOUND, classBean.getName() );
}
if( ErrorType.shouldHandleAsErrorType( classBean ) )
{
return ErrorType.getInstance().getErrorTypeFunctionType( eArgs, functionName, listAllMatchingMethods );
}
boolean bFoundMethodWithName = false;
MethodList methods = BeanAccess.getMethods( beanInfo, getGosuClass() == null ? JavaTypes.OBJECT() : getGosuClass() );
if( methods != null )
{
DynamicArray<? extends IMethodInfo> theMethods = methods.getMethods(functionName);
for (int i = 0; i < theMethods.size; i++) {
IMethodInfo method = (IMethodInfo) theMethods.data[i];
if( BeanAccess.isDescriptorHidden( method ) )
{
continue;
}
bFoundMethodWithName = true;
if( !bMatchParamTypes )
{
return new FunctionType(method);
}
IParameterInfo[] paramTypes = method.getParameters();
if( eArgs == null || paramTypes.length == eArgs.length )
{
if( listAllMatchingMethods == null )
{
return new FunctionType(method);
}
listAllMatchingMethods.add(new FunctionType(method));
}
}
}
if( listAllMatchingMethods != null && listAllMatchingMethods.size() > 0 )
{
return listAllMatchingMethods.get( 0 );
}
if( bFoundMethodWithName )
{
throw new ParseException( parser == null ? null : parser.makeFullParserState(), Res.MSG_WRONG_NUMBER_OF_ARGS_FOR_METHOD_ON_CLASS, functionName, TypeSystem.getUnqualifiedClassName( classBean ) );
}
else
{
checkForStaticMethod( classBean, eArgs, functionName, parser );
if( isDynamic( classBean ) )
{
IType[] params = null;
if( eArgs != null )
{
params = new IType[eArgs.length];
for( int i = 0; i < eArgs.length; i++ )
{
params[i] = eArgs[i].getType();
}
}
IMethodInfo mi = classBean.getTypeInfo().getMethod( functionName, params );
if( mi != null )
{
FunctionType funcType = new FunctionType( mi );
if( listAllMatchingMethods != null )
{
listAllMatchingMethods.add( funcType );
}
return funcType;
}
}
throw new ParseException( parser == null ? null : parser.makeFullParserState(), Res.MSG_NO_METHOD_DESCRIPTOR_FOUND_FOR_METHOD, functionName, TypeSystem.getUnqualifiedClassName( classBean ) );
}
}
private void checkForStaticMethod( IType classBean, Expression[] eArgs, String strMethod, GosuParser parserState )
throws ParseException
{
if( classBean instanceof MetaType)
{
IType referredType = ((MetaType)classBean).getType();
IType[] paramTypes = new IType[eArgs == null ? 0 : eArgs.length];
for( int i = 0; i < paramTypes.length; i++ )
{
paramTypes[i] = eArgs[i].getType();
}
IMethodInfo mi = referredType.getTypeInfo().getCallableMethod( strMethod, paramTypes );
if( mi != null && !mi.isStatic() )
{
throw new ParseException( parserState == null ? null : parserState.makeFullParserState(), Res.MSG_METHOD_IS_NOT_STATIC, strMethod, TypeSystem.getUnqualifiedClassName( classBean ) );
}
}
else
{
IType referredType = MetaType.get( classBean );
IType[] paramTypes = new IType[eArgs == null ? 0 : eArgs.length];
for( int i = 0; i < paramTypes.length; i++ )
{
paramTypes[i] = eArgs[i].getType();
}
IMethodInfo mi = referredType.getTypeInfo().getCallableMethod( strMethod, paramTypes );
if( mi != null && !mi.isStatic() )
{
throw new ParseException( parserState == null ? null : parserState.makeFullParserState(), Res.MSG_METHOD_IS_STATIC, strMethod, TypeSystem.getUnqualifiedClassName( classBean ) );
}
}
}
private IInvocableType inferFunctionType( IInvocableType funcType, List<Expression> eArgs )
{
if( funcType instanceof IFunctionType && funcType.isGenericType() )
{
IType[] argTypes = new IType[eArgs.size()];
for( int i = 0; i < eArgs.size(); i++ )
{
argTypes[i] = eArgs.get( i ).getType();
}
for( int i = 0; i < funcType.getParameterTypes().length; i++ )
{
IType paramType = funcType.getParameterTypes()[i];
if( i < argTypes.length )
{
IType argType = argTypes[i];
IType boundArgType = TypeLord.boundTypes( paramType, getCurrentlyInferringTypes() );
ICoercer coercer = CommonServices.getCoercionManager().resolveCoercerStatically( boundArgType, argType );
if( coercer instanceof IResolvingCoercer )
{
argTypes[i] = ((IResolvingCoercer)coercer).resolveType( paramType, argType );
}
}
}
return ((IFunctionType) funcType).inferParameterizedTypeFromArgTypesAndContextType( argTypes, getCurrentContextType());
}
else
{
return funcType;
}
}
/**
* Using some simple pattern matching, get a potential property name from a
* method name at the end of an access list.
* <p/>
* Patterns:<br>
* <code>get</code>>mixed-case-name<
* <code>is</code>>mixed-case-name<
*/
private static final String[] METHOD_PREFIX_LIST = {"get", "is"};
private String getPropertyNameFromMethodName( String strMethod )
{
if( strMethod == null || strMethod.length() == 0 )
{
return null;
}
for( String strPrefix : METHOD_PREFIX_LIST )
{
String strProperty = getPropertyNameFromMethodName( strPrefix, strMethod );
if( strProperty != null )
{
return strProperty;
}
}
return null;
}
private static final String[] METHOD_PREFIX_LIST_WITH_SETTER = {"get", "is", "set" };
private String getPropertyNameFromMethodNameIncludingSetter( String strMethod )
{
if( strMethod == null || strMethod.length() == 0 )
{
return null;
}
for( String strPrefix : METHOD_PREFIX_LIST_WITH_SETTER )
{
String strProperty = getPropertyNameFromMethodName( strPrefix, strMethod );
if( strProperty != null )
{
return strProperty;
}
}
return null;
}
private String getPropertyNameFromMethodName( String strPrefix, String strMethod )
{
int iPropertyOffset = strPrefix.length();
if( strMethod.startsWith( strPrefix ) &&
strMethod.length() > iPropertyOffset &&
Character.isUpperCase( strMethod.charAt( iPropertyOffset ) ) )
{
return strMethod.substring( iPropertyOffset );
}
return null;
}
private void verifyPropertyWritable( IType classRoot, String strProperty, boolean bFromObjInitializer ) throws ParseException
{
if( classRoot == null )
{
throw new IllegalArgumentException( "Root class is null|" );
}
if( strProperty == null )
{
throw new IllegalArgumentException( "Bean member path is null!" );
}
IPropertyInfo pi = BeanAccess.getPropertyInfo( classRoot, strProperty, null, null, null );
if( pi != null )
{
if( !BeanAccess.isDescriptorHidden( pi ) )
{
if( !pi.isWritable( getGosuClass() ) )
{
if( bFromObjInitializer || !(isParsingConstructor() && pi instanceof IGosuVarPropertyInfo && pi.isFinal() && !pi.isStatic()) ) {
throw new ParseException( makeFullParserState(), Res.MSG_CLASS_PROPERTY_NOT_WRITABLE, strProperty, TypeSystem.getUnqualifiedClassName( classRoot ));
}
}
return;
}
}
throw new IllegalArgumentException( "No property descriptor found for property, " + strProperty + ", on class, " + TypeSystem.getUnqualifiedClassName( classRoot ) );
}
/**
* Get the type of the method specified in the member path.
*
*@param classBean The declaring class of the constructor.
* @param parserState The parserState that may be involved in the process of parsing a constructor. Can be null.
* @return A Gosu type for the constructor.
*/
public IConstructorType getConstructorType( IType classBean, Expression[] eArgs, List<IConstructorType> listAllMatchingMethods, ParserBase parserState ) throws ParseException
{
if( classBean == null )
{
throw new ParseException( parserState == null ? null : parserState.makeFullParserState(), Res.MSG_BEAN_CLASS_IS_NULL );
}
if( ErrorType.shouldHandleAsErrorType( classBean ) )
{
return ErrorType.getInstance().getErrorTypeConstructorType( eArgs, listAllMatchingMethods );
}
ITypeInfo typeInfo = classBean.getTypeInfo();
if( typeInfo != null )
{
List<? extends IConstructorInfo> constructors;
if( typeInfo instanceof IRelativeTypeInfo )
{
while( classBean instanceof ITypeVariableType )
{
classBean = ((ITypeVariableType)classBean).getBoundingType();
}
constructors = ((IRelativeTypeInfo)typeInfo).getConstructors( classBean );
}
else
{
constructors = typeInfo.getConstructors();
}
for( IConstructorInfo constructor : constructors )
{
if( typeInfo instanceof JavaTypeInfo )
{
if( constructor.isPrivate() )
{
continue;
}
}
IParameterInfo[] paramTypes = constructor.getParameters();
if( eArgs == null || paramTypes.length == eArgs.length )
{
if( listAllMatchingMethods == null )
{
return new ConstructorType( constructor );
}
listAllMatchingMethods.add( new ConstructorType( constructor ) );
}
}
if( listAllMatchingMethods != null && listAllMatchingMethods.size() > 0 )
{
return listAllMatchingMethods.get( 0 );
}
}
throw new NoCtorFoundException( parserState == null ? null : parserState.makeFullParserState(), TypeSystem.getUnqualifiedClassName( classBean ), eArgs == null ? 0 : eArgs.length );
}
private void verifyCase( ParsedElement element, String foundName, String actualName, ResourceKey errorKey,
boolean isEndsWithMatchOK )
{
verifyCase(element, foundName, actualName, null, errorKey, isEndsWithMatchOK);
}
private void verifyCase( ParsedElement element, String foundName, String actualName, IParserState state, ResourceKey errorKey, boolean isEndsWithMatchOK )
{
if( _bWarnOnCaseIssue )
{
if( isEndsWithMatchOK )
{
if( !actualName.endsWith( foundName ) )
{
if( actualName.toUpperCase().endsWith( foundName.toUpperCase() ) )
{
CharSequence correctedName = actualName.subSequence( actualName.length() - foundName.length(), actualName.length() );
if(state == null) {
warn( element, false, errorKey, foundName, correctedName );
} else {
warn( element, false, state, errorKey, foundName, correctedName );
}
}
}
}
else if( !GosuObjectUtil.equals( foundName, actualName ) )
{
if( actualName.toUpperCase().equals( foundName.toUpperCase() ) )
{
if(state == null) {
warn( element, false, errorKey, foundName, actualName );
} else {
warn( element, false, state, errorKey, foundName, actualName );
}
}
}
}
}
public void setWarnOnCaseIssue( boolean warnOnCaseIssue )
{
_bWarnOnCaseIssue = warnOnCaseIssue;
}
public void setEditorParser(boolean bStudioEditorParser) {
_bStudioEditorParser = bStudioEditorParser;
}
public boolean isEditorParser() {
if (getOwner() != this) {
return getOwner().isEditorParser();
}
return _bStudioEditorParser;
}
public IParserState getState() {
return makeFullParserState();
}
public void setContextInferenceManager(ContextInferenceManager ctxInferenceMgr) {
_ctxInferenceMgr = ctxInferenceMgr;
}
public void pushContextType( IType type )
{
ArrayList<IType> typeList = new ArrayList<IType>(1);
typeList.add( type );
push_(typeList);
}
public void popContextType()
{
_inferredContextStack.pop();
}
public List<IType> getContextTypes()
{
if( _inferredContextStack == null || _inferredContextStack.empty() || _inferredContextStack.peek() == null )
{
return Collections.emptyList();
}
else
{
return _inferredContextStack.peek();
}
}
public boolean isParsingAnnotation()
{
return _parsingAnnotation;
}
public void setParsingAnnotation( boolean parsingAnnotation )
{
_parsingAnnotation = parsingAnnotation;
}
public boolean isIgnoreTypeDeprecation()
{
return _ignoreTypeDeprecation > 0;
}
public void pushIgnoreTypeDeprecation()
{
_ignoreTypeDeprecation++;
}
public void popIgnoreTypeDeprecation()
{
if( _ignoreTypeDeprecation == 0 )
{
throw new IllegalStateException( "Unbalanced calls to push/popIgnoreTypeDeprecation()" );
}
_ignoreTypeDeprecation--;
}
public void setLocationsFromProgramClassParser( List<ParseTree> savedLocations )
{
_savedLocations = savedLocations;
}
boolean maybeAdvanceTokenizerToEndOfSavedLocation()
{
if( _savedLocations == null )
{
return false;
}
for( ParseTree pt : _savedLocations )
{
IToken T = getTokenizer().getCurrentToken();
if( T.getTokenStart() >= pt.getOffset() && T.getTokenEnd() <= pt.getExtent() )
{
try
{
getTokenizer().goToPosition( pt.getOffset() + pt.getLength() );
return true;
}
catch( IOException e )
{
// Eof ok
return true;
}
}
}
return false;
}
protected void pushInferredFunctionTypeVariableTypes( List<IFunctionType> listFunctionTypes )
{
List<IType> typeVariableTypes = new ArrayList<IType>();
for( IFunctionType functionType : listFunctionTypes )
{
if( functionType.isGenericType() && (getEnclosingType() == null || !getEnclosingType().equals( functionType )) )
{
IGenericTypeVariable[] typeVariables = functionType.getGenericTypeVariables();
for( IGenericTypeVariable typeVariable : typeVariables )
{
ITypeVariableDefinition typeVariableDefinition = typeVariable.getTypeVariableDefinition();
if( typeVariableDefinition != null
&& typeVariableDefinition.getType() != null )
{
typeVariableTypes.add( typeVariableDefinition.getType() );
}
}
}
}
pushInferredTypeVars( typeVariableTypes );
}
@Override
public String toString()
{
return "Parsing: " + getScriptPart();
}
private static class GosuParserTransparentActivationContext extends TransparentActivationContext
{
public GosuParserTransparentActivationContext( IScriptPartId scriptPart )
{
super( scriptPart );
}
public String getLabel()
{
return getContext().toString();
}
}
private static class FunctionDeclTransparentActivationContext extends TransparentActivationContext
{
public FunctionDeclTransparentActivationContext( IScriptPartId scriptPart )
{
super( scriptPart );
}
public String getLabel()
{
return "parseFunctionDecl";
}
}
}