<commit_before>grammar Ceylon;

options {
    memoize=false;
}

@parser::header { package com.redhat.ceylon.compiler.typechecker.parser; 
                  import static com.redhat.ceylon.compiler.typechecker.tree.CustomTree.*; }
@lexer::header { package com.redhat.ceylon.compiler.typechecker.parser; }

@members {
    private java.util.List<ParseError> errors 
            = new java.util.ArrayList<ParseError>();
    @Override public void displayRecognitionError(String[] tn,
            RecognitionException re) {
        errors.add(new ParseError(this, re, tn));
    }
    public java.util.List<ParseError> getErrors() {
        return errors;
    }
    
}

@lexer::members {
    private java.util.List<LexError> errors 
            = new java.util.ArrayList<LexError>();
    @Override public void displayRecognitionError(String[] tn,
            RecognitionException re) {
        errors.add(new LexError(this, re, tn));
    }
    public java.util.List<LexError> getErrors() {
        return errors;
    }
}

compilationUnit returns [CompilationUnit compilationUnit]
    : { $compilationUnit = new CompilationUnit(null); }
      ( 
        ca1=compilerAnnotations
        SEMICOLON
        { $compilationUnit.getCompilerAnnotations().addAll($ca1.annotations); }
      )?
      importList 
      { $compilationUnit.setImportList($importList.importList); }
      ( 
        ca2=compilerAnnotations declaration
        { if ($declaration.declaration!=null)
              $compilationUnit.addDeclaration($declaration.declaration); 
          if ($declaration.declaration!=null)
              $declaration.declaration.getCompilerAnnotations().addAll($ca2.annotations); } 
      )*
      EOF
    ;

importList returns [ImportList importList]
    : { $importList = new ImportList(null); } 
      ( importDeclaration { $importList.addImport($importDeclaration.importDeclaration); } )*
    ;

importDeclaration returns [Import importDeclaration]
    @init { ImportPath importPath=null; 
            ImportMemberOrTypeList il=null; }
    : IMPORT 
      { $importDeclaration = new Import($IMPORT); } 
      ( 
        pn1=packageName 
        { importPath = new ImportPath(null);
          importPath.addIdentifier($pn1.identifier); 
          $importDeclaration.setImportPath(importPath); } 
        ( 
          MEMBER_OP 
          pn2=packageName 
          { importPath.addIdentifier($pn2.identifier); } 
        )*
      )
    LBRACE
    { il = new ImportMemberOrTypeList($LBRACE);
      $importDeclaration.setImportMemberOrTypeList(il); }
    ( 
      ie1=importElement { il.addImportMemberOrType($ie1.importMemberOrType); } 
      ( COMMA ie2=importElement { il.addImportMemberOrType($ie2.importMemberOrType); } )* 
      ( COMMA iw=importWildcard { il.setImportWildcard($iw.importWildcard); } )?
    | iw=importWildcard { il.setImportWildcard($iw.importWildcard); }
    )
    RBRACE
    { il.setEndToken($RBRACE); }
    ;

importElement returns [ImportMemberOrType importMemberOrType]
    : compilerAnnotations
    (
      memberAlias? memberName
      { $importMemberOrType = new ImportMember(null);
        if ($memberAlias.alias!=null) 
            $importMemberOrType.setAlias($memberAlias.alias);
        $importMemberOrType.setIdentifier($memberName.identifier); }
    | typeAlias? typeName
      { $importMemberOrType = new ImportType(null);
        if ($typeAlias.alias!=null)
            $importMemberOrType.setAlias($typeAlias.alias);
        $importMemberOrType.setIdentifier($typeName.identifier); }
    )
    { if ($importMemberOrType!=null)
        $importMemberOrType.getCompilerAnnotations().addAll($compilerAnnotations.annotations); }
    ;

importWildcard returns [ImportWildcard importWildcard]
    : ELLIPSIS
      { $importWildcard = new ImportWildcard($ELLIPSIS); }
    ;

memberAlias returns [Alias alias]
    : memberName SPECIFY
      { $alias = new Alias($SPECIFY); 
        $alias.setIdentifier($memberName.identifier); }
    ;

typeAlias returns [Alias alias]
    : typeName SPECIFY
      { $alias = new Alias($SPECIFY); 
        $alias.setIdentifier($typeName.identifier); }
    ;

packageName returns [Identifier identifier]
    : LIDENTIFIER
      { $identifier = new Identifier($LIDENTIFIER); }
    ;

typeName returns [Identifier identifier]
    : UIDENTIFIER
      { $identifier = new Identifier($UIDENTIFIER); }
    ;

annotationName returns [Identifier identifier]
    : LIDENTIFIER
      { $identifier = new Identifier($LIDENTIFIER); }
    ;

memberName returns [Identifier identifier]
    : LIDENTIFIER
      { $identifier = new Identifier($LIDENTIFIER); }
    ;
    
objectDeclaration returns [ObjectDefinition declaration]
    : OBJECT_DEFINITION
      { $declaration = new ObjectDefinition($OBJECT_DEFINITION); 
        $declaration.setType(new ValueModifier(null)); }
      memberName 
      { $declaration.setIdentifier($memberName.identifier); }
      ( 
        extendedType
        { $declaration.setExtendedType($extendedType.extendedType); } 
      )?
      ( 
        satisfiedTypes
        { $declaration.setSatisfiedTypes($satisfiedTypes.satisfiedTypes); } 
      )?
      (
        classBody
        { $declaration.setClassBody($classBody.classBody); }
      | { displayRecognitionError(getTokenNames(), 
              new MismatchedTokenException(LBRACE, input)); }
        SEMICOLON
        { $declaration.setEndToken($SEMICOLON); }
      )
    //-> ^(OBJECT_DEFINITION VALUE_MODIFIER memberName extendedType? satisfiedTypes? classBody?) 
    ;

voidOrInferredMethodDeclaration returns [AnyMethod declaration]
    @init { MethodDefinition def=null;
            MethodDeclaration dec=null; }
    : (
        VOID_MODIFIER
        { VoidModifier vm = new VoidModifier($VOID_MODIFIER);
          def = new MethodDefinition($VOID_MODIFIER);
          dec = new MethodDeclaration($VOID_MODIFIER);
          def.setType(vm);
          dec.setType(vm);
          $declaration = def; }
      | FUNCTION_MODIFIER
        { FunctionModifier fm = new FunctionModifier($FUNCTION_MODIFIER);
          def = new MethodDefinition($FUNCTION_MODIFIER);
          dec = new MethodDeclaration($FUNCTION_MODIFIER);
          def.setType(fm);
          dec.setType(fm);
          $declaration = def; }
      )
      memberName
      { dec.setIdentifier($memberName.identifier); 
        def.setIdentifier($memberName.identifier); }
      (
        typeParameters
        { def.setTypeParameterList($typeParameters.typeParameterList); 
          dec.setTypeParameterList($typeParameters.typeParameterList); }        
      )?
      (
        parameters
        { def.addParameterList($parameters.parameterList); 
          dec.addParameterList($parameters.parameterList); }
      )*
      //metatypes? 
      (
        typeConstraints
        { def.setTypeConstraintList($typeConstraints.typeConstraintList); 
          dec.setTypeConstraintList($typeConstraints.typeConstraintList); }
      )?
      ( 
        block 
        { def.setBlock($block.block); }
      //-> ^(METHOD_DEFINITION VOID_MODIFIER memberName methodParameters? block)   
      | 
        (
          specifier
          { dec.setSpecifierExpression($specifier.specifierExpression); }
        )?
        { $declaration = dec; }
        SEMICOLON
        { $declaration.setEndToken($SEMICOLON); }
      //-> ^(METHOD_DECLARATION VOID_MODIFIER memberName methodParameters? specifier?)   
      )
    ;

setterDeclaration returns [AttributeSetterDefinition declaration]
    : ASSIGN 
      { $declaration = new AttributeSetterDefinition($ASSIGN); 
        $declaration.setType( new VoidModifier(null) ); }
      memberName 
      { $declaration.setIdentifier($memberName.identifier); }
      ( 
        block
        { $declaration.setBlock($block.block); }
      | { displayRecognitionError(getTokenNames(), 
              new MismatchedTokenException(LBRACE, input)); }
        SEMICOLON
        { $declaration.setEndToken($SEMICOLON); }
      )
    //-> ^(ATTRIBUTE_SETTER_DEFINITION[$ASSIGN] VOID_MODIFIER memberName block)
    ;

inferredAttributeDeclaration returns [AnyAttribute declaration]
    @init { AttributeGetterDefinition def=null;
            AttributeDeclaration dec=null; }
    : VALUE_MODIFIER 
      { ValueModifier fm = new ValueModifier($VALUE_MODIFIER);
        def = new AttributeGetterDefinition($VALUE_MODIFIER);
        dec = new AttributeDeclaration($VALUE_MODIFIER);
        def.setType(fm);
        dec.setType(fm);
        $declaration = def; }
      memberName
      { dec.setIdentifier($memberName.identifier); 
        def.setIdentifier($memberName.identifier); }
      ( 
        (
          specifier 
          { dec.setSpecifierOrInitializerExpression($specifier.specifierExpression); }
        | 
          initializer
          { dec.setSpecifierOrInitializerExpression($initializer.initializerExpression); }
        )?
        { $declaration = dec; }
        SEMICOLON
        { $declaration.setEndToken($SEMICOLON); }
        //-> ^(ATTRIBUTE_DECLARATION VALUE_MODIFIER memberName specifier? initializer?)
      | block
        { def.setBlock($block.block); }
        //-> ^(ATTRIBUTE_GETTER_DEFINITION VALUE_MODIFIER memberName block)
      )
    ;

typedMethodOrAttributeDeclaration returns [TypedDeclaration declaration]
    @init { AttributeGetterDefinition adef=new AttributeGetterDefinition(null);
            AttributeDeclaration adec=new AttributeDeclaration(null);
            MethodDefinition mdef=new MethodDefinition(null);
            MethodDeclaration mdec=new MethodDeclaration(null); 
            $declaration = adef; }
    : unionType
      { adef.setType($unionType.type);
        adec.setType($unionType.type); 
        mdef.setType($unionType.type);
        mdec.setType($unionType.type); }
      memberName
      { adef.setIdentifier($memberName.identifier);
        adec.setIdentifier($memberName.identifier); 
        mdef.setIdentifier($memberName.identifier);
        mdec.setIdentifier($memberName.identifier); }
      ( 
        { $declaration = mdef; }
        (
          typeParameters
          { mdef.setTypeParameterList($typeParameters.typeParameterList);
            mdec.setTypeParameterList($typeParameters.typeParameterList); }
        )?
        (
          parameters
          { mdef.addParameterList($parameters.parameterList);
            mdec.addParameterList($parameters.parameterList); }
        )+
        //metatypes? 
        ( 
          typeConstraints
          { mdef.setTypeConstraintList($typeConstraints.typeConstraintList);
            mdec.setTypeConstraintList($typeConstraints.typeConstraintList); }
        )?
        ( 
          mb=memberBody[$unionType.type] 
         { mdef.setBlock($mb.block); }
        //-> ^(METHOD_DEFINITION unionType memberName methodParameters memberBody)
        | 
          (
            ms=specifier
           { mdec.setSpecifierExpression($ms.specifierExpression); }
          )?
          { $declaration = mdec; }
          s1=SEMICOLON
          { $declaration.setEndToken($s1); }
        //-> ^(METHOD_DECLARATION unionType memberName methodParameters specifier?)
        )
      | 
        (
          as=specifier 
          { adec.setSpecifierOrInitializerExpression($as.specifierExpression); }
        | 
          initializer
          { adec.setSpecifierOrInitializerExpression($initializer.initializerExpression); }
        )?
        { $declaration = adec; }
        s2=SEMICOLON
        { $declaration.setEndToken($s2); }
      //-> ^(ATTRIBUTE_DECLARATION unionType memberName specifier? initializer?)
      | ab=memberBody[$unionType.type]
        { adef.setBlock($ab.block); }
      //-> ^(ATTRIBUTE_GETTER_DEFINITION unionType memberName memberBody)      
      )
    ;

interfaceDeclaration returns [AnyInterface declaration]
    @init { InterfaceDefinition def=null; 
            InterfaceDeclaration dec=null; }
    : INTERFACE_DEFINITION
      { def = new InterfaceDefinition($INTERFACE_DEFINITION); 
        dec = new InterfaceDeclaration($INTERFACE_DEFINITION);
        $declaration = def; }
      typeName 
      { dec.setIdentifier($typeName.identifier); 
        def.setIdentifier($typeName.identifier); }
      (
        typeParameters 
        { def.setTypeParameterList($typeParameters.typeParameterList); 
          dec.setTypeParameterList($typeParameters.typeParameterList); }
      )?
      (
        caseTypes
        { def.setCaseTypes($caseTypes.caseTypes); 
          dec.setCaseTypes($caseTypes.caseTypes); }
      )?
      /*metatypes?*/ 
      (
        adaptedTypes
        { def.setAdaptedTypes($adaptedTypes.adaptedTypes); }
      )?
      (
        satisfiedTypes
        { def.setSatisfiedTypes($satisfiedTypes.satisfiedTypes); 
          dec.setSatisfiedTypes($satisfiedTypes.satisfiedTypes); }
      )?
      (
        typeConstraints
        { def.setTypeConstraintList($typeConstraints.typeConstraintList); 
          dec.setTypeConstraintList($typeConstraints.typeConstraintList); }
      )?
      (
        interfaceBody
        { def.setInterfaceBody($interfaceBody.interfaceBody); }
      //-> ^(INTERFACE_DEFINITION typeName interfaceParameters? interfaceBody)
      | 
        (
          typeSpecifier
          { dec.setTypeSpecifier($typeSpecifier.typeSpecifier); }
        )? 
        { $declaration = dec; }
        SEMICOLON
        { $declaration.setEndToken($SEMICOLON); }
      //-> ^(INTERFACE_DECLARATION[$INTERFACE_DEFINITION] typeName interfaceParameters? typeSpecifier?)
      )
    ;

classDeclaration returns [AnyClass declaration]
    @init { ClassDefinition def=null; 
            ClassDeclaration dec=null; }
    : CLASS_DEFINITION 
      { def = new ClassDefinition($CLASS_DEFINITION); 
        dec = new ClassDeclaration($CLASS_DEFINITION);
        $declaration = def; }
      typeName
      { dec.setIdentifier($typeName.identifier); 
        def.setIdentifier($typeName.identifier); }
      (
        typeParameters 
        { def.setTypeParameterList($typeParameters.typeParameterList); 
          dec.setTypeParameterList($typeParameters.typeParameterList); }
      )?
      (
        parameters
        { def.setParameterList($parameters.parameterList); 
          dec.setParameterList($parameters.parameterList); }
      )?
      (
        caseTypes
        { def.setCaseTypes($caseTypes.caseTypes); 
          dec.setCaseTypes($caseTypes.caseTypes); }
      )?
      /*metatypes?*/ 
      (
        extendedType
        { def.setExtendedType($extendedType.extendedType); 
          dec.setExtendedType($extendedType.extendedType); }
      )? 
      (
        satisfiedTypes
        { def.setSatisfiedTypes($satisfiedTypes.satisfiedTypes); 
          dec.setSatisfiedTypes($satisfiedTypes.satisfiedTypes); }
      )?
      (
        typeConstraints
        { def.setTypeConstraintList($typeConstraints.typeConstraintList); 
          dec.setTypeConstraintList($typeConstraints.typeConstraintList); }
      )?
      (
        classBody
        { def.setClassBody($classBody.classBody); }
      //-> ^(CLASS_DEFINITION typeName classParameters? classBody)
      | 
        (
          typeSpecifier
          { dec.setTypeSpecifier($typeSpecifier.typeSpecifier); }
        )?
        { $declaration = dec; }
        SEMICOLON
        { $declaration.setEndToken($SEMICOLON); }
      //-> ^(CLASS_DECLARATION[$CLASS_DEFINITION] typeName classParameters? typeSpecifier?)
      )
    ;

    
block returns [Block block]
    : LBRACE 
      { $block = new Block($LBRACE); }
      (
        declarationOrStatement
        { if ($declarationOrStatement.statement!=null)
              $block.addStatement($declarationOrStatement.statement); }
      )*
      RBRACE
      { $block.setEndToken($RBRACE); }
    //-> ^(BLOCK[$LBRACE] annotatedDeclarationOrStatement*)
    ;

//Note: interface bodies can't really contain 
//      statements, but error recovery works
//      much better if we validate that later
//      on, instead of doing it in the parser.
interfaceBody returns [InterfaceBody interfaceBody]
    : LBRACE 
      { $interfaceBody = new InterfaceBody($LBRACE); }
      (
        declarationOrStatement
        { if ($declarationOrStatement.statement!=null)
              $interfaceBody.addStatement($declarationOrStatement.statement); }
      )*
      RBRACE
      { $interfaceBody.setEndToken($RBRACE); }
    //-> ^(INTERFACE_BODY[$LBRACE] annotatedDeclarationOrStatement2*)
    ;

classBody returns [ClassBody classBody]
    : LBRACE
      { $classBody = new ClassBody($LBRACE); }
      (
        declarationOrStatement
        { if ($declarationOrStatement.statement!=null)
              $classBody.addStatement($declarationOrStatement.statement); }
      )*
      RBRACE
      { $classBody.setEndToken($RBRACE); }
    //-> ^(CLASS_BODY[$LBRACE] annotatedDeclarationOrStatement2*)
    ;

//This rule accounts for the problem that we
//can't tell whether a member body is a block
//or a named argument list until after we
//finish parsing it
memberBody[StaticType type] returns [Block block]
      //options { memoize=true; }
    : 
    {$type instanceof BaseType}?=>
    (
      (namedArguments)
      => namedArguments //first try to match with no directives or control structures
      { $block = new Block(null);
        SimpleType t = (SimpleType) $type;
        Return r = new Return(null);
        Expression e = new Expression(null);
        InvocationExpression ie = new InvocationExpression(null);
        BaseTypeExpression bme = new BaseTypeExpression(null);
        bme.setIdentifier(t.getIdentifier());
        bme.setTypeArguments(new InferredTypeArguments(null));
        if (t.getTypeArgumentList()!=null)
            bme.setTypeArguments(t.getTypeArgumentList());
        ie.setPrimary(bme);
        ie.setNamedArgumentList($namedArguments.namedArgumentList);
        e.setTerm(ie);
        r.setExpression(e);
        $block.addStatement(r); }
    //-> ^(BLOCK ^(RETURN ^(EXPRESSION ^(INVOCATION_EXPRESSION ^(BASE_TYPE_EXPRESSION { ((CommonTree)$mt).getChild(0) } { ((CommonTree)$mt).getChild(1) } ) namedArguments))))
      | b1=block //if there is a "return" directive or control structure, it must be a block
      { $block=$b1.block; } 
    )
    | b2=block
      { $block=$b2.block; }
    ;

extendedType returns [ExtendedType extendedType]
    : EXTENDS
      { $extendedType = new ExtendedType($EXTENDS); }
      (
        type
        { $extendedType.setType($type.type); }
        //-> ^(EXTENDED_TYPE[$EXTENDS] type ^(INVOCATION_EXPRESSION ^(EXTENDED_TYPE_EXPRESSION) positionalArguments))
      | SUPER MEMBER_OP 
        typeReference 
        { QualifiedType qt=new QualifiedType(null);
          SuperType st = new SuperType($SUPER);
          qt.setOuterType(st);
          qt.setIdentifier($typeReference.identifier);
          if ($typeReference.typeArgumentList!=null)
              qt.setTypeArgumentList($typeReference.typeArgumentList);
          $extendedType.setType(qt); }
        //-> ^(EXTENDED_TYPE[$EXTENDS] ^(QUALIFIED_TYPE SUPER_TYPE[$SUPER] typeReference) ^(INVOCATION_EXPRESSION ^(EXTENDED_TYPE_EXPRESSION) positionalArguments))
      )
      (
        positionalArguments
        { InvocationExpression ie = new InvocationExpression(null);
          ExtendedTypeExpression ete = new ExtendedTypeExpression(null);
          ie.setPrimary(ete);
          ete.setExtendedType($extendedType.getType());
          ie.setPositionalArgumentList($positionalArguments.positionalArgumentList);
          $extendedType.setInvocationExpression(ie); }
       )
    ;

satisfiedTypes returns [SatisfiedTypes satisfiedTypes]
    : SATISFIES 
      { $satisfiedTypes = new SatisfiedTypes($SATISFIES); }
      t1=type 
      { $satisfiedTypes.addType($t1.type); }
      (
        INTERSECTION_OP 
        t2=type
        { $satisfiedTypes.addType($t2.type); }
      )*
    //-> ^(SATISFIED_TYPES[$SATISFIES] type+)
    ;

abstractedType returns [AbstractedType abstractedType]
    : ABSTRACTED_TYPE
      { $abstractedType = new AbstractedType($ABSTRACTED_TYPE); }
      type
      { $abstractedType.setType($type.type); }
    ;

adaptedTypes returns [AdaptedTypes adaptedTypes]
    : ADAPTED_TYPES 
      { $adaptedTypes = new AdaptedTypes($ADAPTED_TYPES); }
      t1=type 
      { $adaptedTypes.addType($t1.type); }
      (
        INTERSECTION_OP 
        t2=type
        { $adaptedTypes.addType($t2.type); }
      )*
    ;

caseTypes returns [CaseTypes caseTypes]
    : CASE_TYPES
      { $caseTypes = new CaseTypes($CASE_TYPES); }
      ct1=caseType 
      { if ($ct1.type!=null) $caseTypes.addType($ct1.type); 
        if ($ct1.instance!=null) $caseTypes.addBaseMemberExpression($ct1.instance); }
      (
        UNION_OP 
        ct2=caseType
      { if ($ct2.type!=null) $caseTypes.addType($ct2.type); 
        if ($ct2.instance!=null) $caseTypes.addBaseMemberExpression($ct2.instance); }
      )*
    ;

caseType returns [SimpleType type, BaseMemberExpression instance]
    : t=type 
      { $type=$t.type;}
    | memberName //-> ^(BASE_MEMBER_EXPRESSION memberName)
      { $instance = new BaseMemberExpression(null);
        $instance.setIdentifier($memberName.identifier);
        $instance.setTypeArguments( new InferredTypeArguments(null) ); }
    ;

//Support for metatypes
//Note that we don't need this for now
/*metatypes
    : IS_OP type ('&' type)* 
    -> ^(METATYPES[$IS_OP] type*)
    ;*/

parameters returns [ParameterList parameterList]
    : LPAREN
      { $parameterList=new ParameterList($LPAREN); }
      (
        ap1=parameterDeclaration 
        { $parameterList.addParameter($ap1.parameter); }
        (
          COMMA 
          ap2=parameterDeclaration
          { $parameterList.addParameter($ap2.parameter); }
        )*
      )? 
      RPAREN
      { $parameterList.setEndToken($RPAREN); }
      //-> ^(PARAMETER_LIST[$LPAREN] annotatedParameter*)
    ;

//special rule for syntactic predicate
//to distinguish between a formal 
//parameter list and a parenthesized body 
//of an inline callable argument
//be careful with this one, since it 
//matches "()", which can also be an 
//argument list
parametersStart
    : LPAREN ( annotatedDeclarationStart | RPAREN )
    ;
    
// FIXME: This accepts more than the language spec: named arguments
// and varargs arguments can appear in any order.  We'll have to
// enforce the rule that the ... appears at the end of the parapmeter
// list in a later pass of the compiler.
parameter returns [Parameter parameter]
    @init { ValueParameterDeclaration vp = new ValueParameterDeclaration(null); 
            FunctionalParameterDeclaration fp = new FunctionalParameterDeclaration(null); 
            $parameter = vp; }
    : parameterType
      { vp.setType($parameterType.type); }
      { fp.setType($parameterType.type); }
      memberName
      { vp.setIdentifier($memberName.identifier); }
      { fp.setIdentifier($memberName.identifier); }
      (
        (
          valueParameter
        )?
        //-> ^(VALUE_PARAMETER_DECLARATION parameterType memberName specifier?)
      |
        (
          parameters
          { fp.addParameterList($parameters.parameterList); }
        )+
        { $parameter=fp; }
          //for callable parameters
        //-> ^(FUNCTIONAL_PARAMETER_DECLARATION parameterType memberName parameters+ specifier?)
      )
      (
        specifier
        { $parameter.setSpecifierExpression($specifier.specifierExpression); }
      )?
    ;

parameterDeclaration returns [Parameter parameter]
    : compilerAnnotations
      annotations 
      p=parameter
      { $parameter=$p.parameter;
        $parameter.setAnnotationList($annotations.annotationList); }
      { if ($parameter!=null)
        $parameter.getCompilerAnnotations().addAll($compilerAnnotations.annotations); }
    ;

valueParameter
    : ENTRY_OP unionType memberName
    ;

parameterType returns [Type type]
    : unionType 
      { $type = $unionType.type; }
      ( 
        ELLIPSIS
        { SequencedType st = new SequencedType($ELLIPSIS);
          st.setType($type);
          $type = st; }
      )?
    | VOID_MODIFIER
      { $type = new VoidModifier($VOID_MODIFIER); }
    ;

typeParameters returns [TypeParameterList typeParameterList]
    : SMALLER_OP
      { $typeParameterList = new TypeParameterList($SMALLER_OP); }
      tp1=typeParameter
      { $typeParameterList.addTypeParameterDeclaration($tp1.typeParameter); }
      (
        COMMA 
        tp2=typeParameter
        { $typeParameterList.addTypeParameterDeclaration($tp2.typeParameter); }
      )*
      LARGER_OP
      { $typeParameterList.setEndToken($LARGER_OP); }
    //-> ^(TYPE_PARAMETER_LIST[$SMALLER_OP] typeParameter+)
    ;

typeParameter returns [TypeParameterDeclaration typeParameter]
    : { $typeParameter = new TypeParameterDeclaration(null); }
      ( 
        variance 
        { $typeParameter.setTypeVariance($variance.typeVariance); } 
      )? 
      typeName
      { $typeParameter.setIdentifier($typeName.identifier); } 
    //-> ^(TYPE_PARAMETER_DECLARATION variance? typeName)
    | //{ $typeParameter = new SequencedTypeParameter(null); }
      typeName 
      //{ $typeParameter.setIdentifier($typeName.identifier); } 
      ELLIPSIS
    //-> ^(SEQUENCED_TYPE_PARAMETER typeName)
    ;

variance returns [TypeVariance typeVariance]
    : IN_OP //-> ^(TYPE_VARIANCE[$IN_OP])
      { $typeVariance = new TypeVariance($IN_OP); }
    | OUT //-> ^(TYPE_VARIANCE[$OUT])
      { $typeVariance = new TypeVariance($OUT); }
    ;
    
typeConstraint returns [TypeConstraint typeConstraint]
    : compilerAnnotations
      TYPE_CONSTRAINT
      { $typeConstraint = new TypeConstraint($TYPE_CONSTRAINT); 
        $typeConstraint.getCompilerAnnotations().addAll($compilerAnnotations.annotations); }
      typeName 
      { $typeConstraint.setIdentifier($typeName.identifier); }
      //(typeParameters)?
      (
        parameters
        { $typeConstraint.setParameterList($parameters.parameterList); }
      )?
      (
        caseTypes
        { $typeConstraint.setCaseTypes($caseTypes.caseTypes); }
      )?
      //metatypes? 
      (
        satisfiedTypes
        { $typeConstraint.setSatisfiedTypes($satisfiedTypes.satisfiedTypes); }
      )?
      ( 
        abstractedType
        { $typeConstraint.setAbstractedType($abstractedType.abstractedType); }
      )?
    ;

typeConstraints returns [TypeConstraintList typeConstraintList]
    : { $typeConstraintList=new TypeConstraintList(null); }
      (
        typeConstraint
        { $typeConstraintList.addTypeConstraint($typeConstraint.typeConstraint); }
      )+
    //-> ^(TYPE_CONSTRAINT_LIST typeConstraint2+)
    ;

declarationOrStatement returns [Statement statement]
    options {memoize=true;}
    : compilerAnnotations
      ( 
        (annotatedDeclarationStart) => declaration
        { $statement=$declaration.declaration; }
      | s=statement
        { $statement=$s.statement; }
      )
      { if ($statement!=null)
            $statement.getCompilerAnnotations().addAll($compilerAnnotations.annotations); }
    ;

declaration returns [Declaration declaration]
    : annotations
    ( 
      objectDeclaration
      { $declaration=$objectDeclaration.declaration; }
    | setterDeclaration
      { $declaration=$setterDeclaration.declaration; }
    | voidOrInferredMethodDeclaration
      { $declaration=$voidOrInferredMethodDeclaration.declaration; }
    | inferredAttributeDeclaration
      { $declaration=$inferredAttributeDeclaration.declaration; }
    | typedMethodOrAttributeDeclaration
      { $declaration=$typedMethodOrAttributeDeclaration.declaration; }
    | classDeclaration
      { $declaration=$classDeclaration.declaration; }
    | interfaceDeclaration
      { $declaration=$interfaceDeclaration.declaration; }
    /*| { displayRecognitionError(getTokenNames(), 
              new MismatchedTokenException(CLASS_DEFINITION, input)); }
      SEMICOLON
      { $declaration=new BrokenDeclaration($SEMICOLON); }*/
    )
    { if ($declaration!=null)
          $declaration.setAnnotationList($annotations.annotationList);  }
    ;

//special rule for syntactic predicate
//to distinguish between an annotation
//and an expression statement
/*annotatedDeclarationStart
    : declarationStart
    | LIDENTIFIER
      ( 
          declarationStart
        | LIDENTIFIER
        | nonstringLiteral | stringLiteral
        | arguments annotatedDeclarationStart //we need to recurse because it could be an inline callable argument
      )
    ;*/

annotatedDeclarationStart
    : annotation* declarationStart
    ;

//special rule for syntactic predicates
//that distinguish declarations from
//expressions
declarationStart
    : declarationKeyword 
    | unionType (ELLIPSIS | LIDENTIFIER)
    ;

declarationKeyword
    : VALUE_MODIFIER
    | FUNCTION_MODIFIER
    | ASSIGN
    | VOID_MODIFIER
    | INTERFACE_DEFINITION
    | CLASS_DEFINITION
    | OBJECT_DEFINITION
    ;

statement returns [Statement statement]
    : directiveStatement
      { $statement = $directiveStatement.directive; }
    | controlStatement
      { $statement = $controlStatement.controlStatement; }
    | expressionOrSpecificationstatement
      { $statement = $expressionOrSpecificationstatement.statement; }
    ;

expressionOrSpecificationstatement returns [Statement statement]
    @init { SpecifierStatement ss=new SpecifierStatement(null); 
            ExpressionStatement es=new ExpressionStatement(null); }
    : expression 
      { es.setExpression($expression.expression);
        $statement = es; }
      (
        { $expression.expression.getTerm() instanceof BaseMemberExpression }?=>
        specifier
        { ss.setSpecifierExpression($specifier.specifierExpression);
          ss.setBaseMemberExpression((BaseMemberExpression)$expression.expression.getTerm()); 
          $statement = ss; }
      )?
      (
        SEMICOLON
        { $statement.setEndToken($SEMICOLON); }
      | { displayRecognitionError(getTokenNames(), 
              new MismatchedTokenException(SEMICOLON, input)); }
        COMMA
        { $statement.setEndToken($COMMA); }
    )
    ;

directiveStatement returns [Directive directive]
    : d=directive 
      { $directive=$d.directive; } 
      SEMICOLON
      { $directive.setEndToken($SEMICOLON); }
    ;

directive returns [Directive directive]
    : returnDirective
      { $directive = $returnDirective.directive; }
    | throwDirective
      { $directive = $throwDirective.directive; }
    | breakDirective
      { $directive = $breakDirective.directive; }
    | continueDirective
      { $directive = $continueDirective.directive; }
    ;

returnDirective returns [Return directive]
    : RETURN 
      { $directive = new Return($RETURN); }
      (
        expression
        { $directive.setExpression($expression.expression); }
      )?
    ;

throwDirective returns [Throw directive]
    : THROW
      { $directive = new Throw($THROW); }
      ( 
        expression
        { $directive.setExpression($expression.expression); }
      )?
    ;

breakDirective returns [Break directive]
    : BREAK
      { $directive = new Break($BREAK); }
    ;

continueDirective returns [Continue directive]
    : CONTINUE
      { $directive = new Continue($CONTINUE); }
    ;

typeSpecifier returns [TypeSpecifier typeSpecifier]
    : SPECIFY 
      { $typeSpecifier = new TypeSpecifier($SPECIFY); }
      type
      { $typeSpecifier.setType($type.type); }
    //-> ^(TYPE_SPECIFIER[$SPECIFY] type)
    ;

specifier returns [SpecifierExpression specifierExpression]
    : SPECIFY 
      { $specifierExpression = new SpecifierExpression($SPECIFY); }
      expression
      { $specifierExpression.setExpression($expression.expression); }
    ;

initializer returns [InitializerExpression initializerExpression]
    : ASSIGN_OP 
      { $initializerExpression = new InitializerExpression($ASSIGN_OP); }
      expression
      { $initializerExpression.setExpression($expression.expression); }
    //-> ^(INITIALIZER_EXPRESSION[$ASSIGN_OP] expression)
    ;

expression returns [Expression expression]
    : { $expression = new Expression(null); }
      assignmentExpression
      { $expression.setTerm($assignmentExpression.term); }
    ;

base returns [Primary primary]
    : nonstringLiteral
      { $primary=$nonstringLiteral.literal; }
    | stringExpression
      { $primary=$stringExpression.atom; }
    | enumeration
      { $primary=$enumeration.sequenceEnumeration; }
    | selfReference
      { $primary=$selfReference.atom; }
    | typeReference
      { BaseTypeExpression bte = new BaseTypeExpression(null);
        bte.setIdentifier($typeReference.identifier);
        bte.setTypeArguments( new InferredTypeArguments(null) );
        if ($typeReference.typeArgumentList!=null)
            bte.setTypeArguments($typeReference.typeArgumentList);
        $primary=bte; }
    | memberReference
      { BaseMemberExpression bme = new BaseMemberExpression(null);
        bme.setIdentifier($memberReference.identifier);
        bme.setTypeArguments( new InferredTypeArguments(null) );
        if ($memberReference.typeArgumentList!=null)
            bme.setTypeArguments($memberReference.typeArgumentList);
        $primary=bme; }
    | parExpression
      { $primary=$parExpression.expression; }
    ;

primary returns [Primary primary]
    : base
      { $primary=$base.primary; }
    (   
        qualifiedReference
      { QualifiedMemberOrTypeExpression qe;
        if ($qualifiedReference.isMember)
            qe = new QualifiedMemberExpression(null);
        else
            qe = new QualifiedTypeExpression(null);
        qe.setPrimary($primary);
        qe.setMemberOperator($qualifiedReference.operator);
        qe.setIdentifier($qualifiedReference.identifier);
        qe.setTypeArguments( new InferredTypeArguments(null) );
        if ($qualifiedReference.typeArgumentList!=null)
            qe.setTypeArguments($qualifiedReference.typeArgumentList);
        $primary=qe; }
      | indexExpression
        { IndexExpression xe = new IndexExpression(null);
          xe.setPrimary($primary);
          xe.setIndexOperator($indexExpression.operator); 
          xe.setElementOrRange($indexExpression.elementOrRange);
          $primary=xe; }
      | arguments
        { InvocationExpression ie = new InvocationExpression(null);
          ie.setPrimary($primary);
          if ($arguments.argumentList instanceof PositionalArgumentList)
              ie.setPositionalArgumentList((PositionalArgumentList)$arguments.argumentList);
          if ($arguments.argumentList instanceof NamedArgumentList)
              ie.setNamedArgumentList((NamedArgumentList)$arguments.argumentList);
          $primary=ie; }
    )*
    ;
   
qualifiedReference returns [Identifier identifier, MemberOperator operator, 
                            TypeArgumentList typeArgumentList, boolean isMember]
    : memberSelectionOperator 
      { $operator = $memberSelectionOperator.operator;
        $identifier = new Identifier($operator.getToken());
        $identifier.setText("");
        $isMember=true; }
      ( 
        memberReference
        { $identifier = $memberReference.identifier;
          $typeArgumentList = $memberReference.typeArgumentList; }
      | typeReference
        { $identifier = $typeReference.identifier;
          $typeArgumentList = $typeReference.typeArgumentList; 
          $isMember=false; }
      |
        { displayRecognitionError(getTokenNames(), 
              new MismatchedTokenException(LIDENTIFIER, input)); }
      )
    ;

indexExpression returns [IndexOperator operator, ElementOrRange elementOrRange]
    : elementSelectionOperator 
      { $operator=$elementSelectionOperator.operator; }
      indexOrIndexRange 
      { $elementOrRange=$indexOrIndexRange.elementOrRange; }
      RBRACKET
    ;

memberSelectionOperator returns [MemberOperator operator]
    : MEMBER_OP
      { $operator=new MemberOp($MEMBER_OP); }
    | SAFE_MEMBER_OP
      { $operator=new SafeMemberOp($SAFE_MEMBER_OP); }
    | SPREAD_OP
      { $operator=new SpreadOp($SPREAD_OP); }
    ;

elementSelectionOperator returns [IndexOperator operator]
    : SAFE_INDEX_OP
      { $operator=new SafeIndexOp($SAFE_INDEX_OP); }
    | INDEX_OP
      { $operator=new IndexOp($INDEX_OP); }
    ;

enumeration returns [SequenceEnumeration sequenceEnumeration]
    : LBRACE { $sequenceEnumeration = new SequenceEnumeration($LBRACE); } 
      (
        expressions
        { $sequenceEnumeration.setExpressionList($expressions.expressionList); }
      )?
      RBRACE
      { $sequenceEnumeration.setEndToken($RBRACE); }
    ;

expressions returns [ExpressionList expressionList]
    : { $expressionList = new ExpressionList(null); }
      e1=expression 
      { $expressionList.addExpression($e1.expression); }
      ( COMMA 
        e2=expression 
        { $expressionList.addExpression($e2.expression); } 
      )*
    ;

memberReference returns [Identifier identifier, TypeArgumentList typeArgumentList]
    : memberName
      { $identifier = $memberName.identifier; }
      (
        (typeArgumentsStart)
        => typeArguments
        { $typeArgumentList = $typeArguments.typeArgumentList; }
      )?
    ;

typeReference returns [Identifier identifier, TypeArgumentList typeArgumentList]
    : typeName 
      { $identifier = $typeName.identifier; }
      (
        (typeArgumentsStart)
        => typeArguments
        { $typeArgumentList = $typeArguments.typeArgumentList; }
      )?
    ;

//special rule for syntactic predicate to 
//determine if we have a < operator, or a
//type argument list
typeArgumentsStart
    : SMALLER_OP
      UIDENTIFIER ('.' UIDENTIFIER)* /*| 'subtype')*/ (DEFAULT_OP|ARRAY)*
      ((INTERSECTION_OP|UNION_OP) UIDENTIFIER ('.' UIDENTIFIER)* (DEFAULT_OP|ARRAY)*)*
      (LARGER_OP|SMALLER_OP|COMMA|ELLIPSIS)
    ;

indexOrIndexRange returns [ElementOrRange elementOrRange]
    : l=index
    (
      { Element e = new Element(null);
        $elementOrRange = e;
        e.setExpression($l.expression); }
      | '...' 
      { ElementRange er1 = new ElementRange(null);
        $elementOrRange = er1;
        er1.setLowerBound($l.expression); }
      | '..' u=index 
      { ElementRange er2 = new ElementRange(null);
        $elementOrRange = er2;
        er2.setLowerBound($l.expression); 
        er2.setUpperBound($u.expression); }
    )
    ;

index returns [Expression expression]
    : additiveExpression 
      { $expression = new Expression(null);
        $expression.setTerm($additiveExpression.term); }
    ;

arguments returns [ArgumentList argumentList]
    : positionalArguments 
      { $argumentList = $positionalArguments.positionalArgumentList; }
    | namedArguments
      { $argumentList = $namedArguments.namedArgumentList; }
    ;

namedArguments returns [NamedArgumentList namedArgumentList]
    : LBRACE 
      { $namedArgumentList = new NamedArgumentList($LBRACE); }
      (
        (namedArgumentStart) 
        => namedArgument
        { if ($namedArgument.namedArgument!=null) 
              $namedArgumentList.addNamedArgument($namedArgument.namedArgument); }
      )* 
      ( 
        sequencedArgument
        { $namedArgumentList.setSequencedArgument($sequencedArgument.sequencedArgument); }
      )?
      RBRACE
      { $namedArgumentList.setEndToken($RBRACE); }
    ;

sequencedArgument returns [SequencedArgument sequencedArgument]
    : compilerAnnotations
      expressions
      { sequencedArgument = new SequencedArgument(null);
        sequencedArgument.setExpressionList($expressions.expressionList);
        sequencedArgument.getCompilerAnnotations().addAll($compilerAnnotations.annotations); }
    ;

namedArgument returns [NamedArgument namedArgument]
    : compilerAnnotations 
    ( 
      namedSpecifiedArgument
      { $namedArgument = $namedSpecifiedArgument.specifiedArgument; }
    | 
      namedArgumentDeclaration
      { $namedArgument = $namedArgumentDeclaration.declaration; }
    )
    { if ($namedArgument!=null)
          namedArgument.getCompilerAnnotations().addAll($compilerAnnotations.annotations); }
    ;

namedSpecifiedArgument returns [SpecifiedArgument specifiedArgument]
    : memberName 
      { $specifiedArgument = new SpecifiedArgument(null); 
        $specifiedArgument.setIdentifier($memberName.identifier); }
      specifier 
      { $specifiedArgument.setSpecifierExpression($specifier.specifierExpression); }
      SEMICOLON
      { $specifiedArgument.setEndToken($SEMICOLON); }
    ;

objectArgument returns [ObjectArgument declaration]
    : OBJECT_DEFINITION 
      { $declaration = new ObjectArgument($OBJECT_DEFINITION); 
        $declaration.setType(new ValueModifier(null)); }
      memberName
      { $declaration.setIdentifier($memberName.identifier); }
      ( 
        extendedType
        { $declaration.setExtendedType($extendedType.extendedType); } 
      )?
      ( 
        satisfiedTypes
        { $declaration.setSatisfiedTypes($satisfiedTypes.satisfiedTypes); } 
      )?
      (
        classBody
        { $declaration.setClassBody($classBody.classBody); }
      | { displayRecognitionError(getTokenNames(), 
              new MismatchedTokenException(LBRACE, input)); }
        SEMICOLON
        { $declaration.setEndToken($SEMICOLON); }
      )
    //-> ^(OBJECT_ARGUMENT[$OBJECT_DEFINITION] VALUE_MODIFIER memberName extendedType? satisfiedTypes? classBody?)
    ;

voidOrInferredMethodArgument returns [MethodArgument declaration]
    : { $declaration=new MethodArgument(null); }
      (
        VOID_MODIFIER
      { $declaration.setType(new VoidModifier($VOID_MODIFIER)); }
      |
        FUNCTION_MODIFIER
      { $declaration.setType(new FunctionModifier($FUNCTION_MODIFIER)); }
      ) 
      memberName 
      { $declaration.setIdentifier($memberName.identifier); }
      (
        parameters
        { $declaration.addParameterList($parameters.parameterList); }
      )*
      block
      { $declaration.setBlock($block.block); }
    //-> ^(METHOD_ARGUMENT VOID_MODIFIER memberName parameters* block)
    ;

inferredGetterArgument returns [AttributeArgument declaration]
    : { $declaration=new AttributeArgument(null); }
      VALUE_MODIFIER 
      { $declaration.setType(new ValueModifier($VALUE_MODIFIER)); }
      memberName 
      { $declaration.setIdentifier($memberName.identifier); }
      block
      { $declaration.setBlock($block.block); }
      //-> ^(ATTRIBUTE_ARGUMENT VALUE_MODIFIER memberName block)      
    ;

typedMethodOrGetterArgument returns [TypedArgument declaration]
    @init { MethodArgument marg = new MethodArgument(null);
            AttributeArgument aarg = new AttributeArgument(null); 
            $declaration=aarg; }
    : unionType 
      { marg.setType($unionType.type);
        aarg.setType($unionType.type); }
      memberName
      { marg.setIdentifier($memberName.identifier);
        aarg.setIdentifier($memberName.identifier); }
      ( 
        { $declaration = marg; }
        (
          parameters
          { marg.addParameterList($parameters.parameterList); }
        )+
      )?
      memberBody[$unionType.type]
      { marg.setBlock($memberBody.block); 
        aarg.setBlock($memberBody.block); }
      //-> ^(METHOD_ARGUMENT unionType memberName parameters+ memberBody)
      //-> ^(ATTRIBUTE_ARGUMENT unionType memberName memberBody)      
    ;

namedArgumentDeclaration returns [NamedArgument declaration]
    : objectArgument
      { $declaration=$objectArgument.declaration; }
    | typedMethodOrGetterArgument
      { $declaration=$typedMethodOrGetterArgument.declaration; }
    | voidOrInferredMethodArgument
      { $declaration=$voidOrInferredMethodArgument.declaration; }
    | inferredGetterArgument
      { $declaration=$inferredGetterArgument.declaration; }
    ;

//special rule for syntactic predicate
//to distinguish between a named argument
//and a sequenced argument
namedArgumentStart
    : compilerAnnotation* 
      (specificationStart | declarationStart)
    ;

//special rule for syntactic predicates
specificationStart
    : LIDENTIFIER '='
    ;

parExpression returns [Expression expression] 
    : LPAREN 
      { $expression = new Expression($LPAREN); }
      assignmentExpression
      { $expression.setTerm($assignmentExpression.term); }
      RPAREN
      { $expression.setEndToken($RPAREN); }
    ;
    
positionalArguments returns [PositionalArgumentList positionalArgumentList]
    : LPAREN 
      { $positionalArgumentList = new PositionalArgumentList($LPAREN); }
      ( pa1=positionalArgument
        { $positionalArgumentList.addPositionalArgument($pa1.positionalArgument); }
        (
          COMMA 
          pa2=positionalArgument
          { $positionalArgumentList.addPositionalArgument($pa2.positionalArgument); }
        )* 
        (
          ELLIPSIS
          { $positionalArgumentList.setEllipsis( new Ellipsis($ELLIPSIS) ); }
        )?
      )? 
      RPAREN
      { $positionalArgumentList.setEndToken($RPAREN); }
    ;

positionalArgument returns [PositionalArgument positionalArgument]
    : //(declarationStart) => specialArgument | 
      expression
      { $positionalArgument = new PositionalArgument(null);
        $positionalArgument.setExpression($expression.expression); }
    ;
    
assignmentExpression returns [Term term]
    : ee1=disjunctionExpression
      { $term = $ee1.term; }
      (
        assignmentOperator 
        { $assignmentOperator.operator.setLeftTerm($term);
          $term = $assignmentOperator.operator; }
        ee2=assignmentExpression
        { $assignmentOperator.operator.setRightTerm($ee2.term); }<commit_msg>add parser rule for smalltalk-style args<commit_after>grammar Ceylon;

options {
    memoize=false;
}

@parser::header { package com.redhat.ceylon.compiler.typechecker.parser; 
                  import static com.redhat.ceylon.compiler.typechecker.tree.CustomTree.*; }
@lexer::header { package com.redhat.ceylon.compiler.typechecker.parser; }

@members {
    private java.util.List<ParseError> errors 
            = new java.util.ArrayList<ParseError>();
    @Override public void displayRecognitionError(String[] tn,
            RecognitionException re) {
        errors.add(new ParseError(this, re, tn));
    }
    public java.util.List<ParseError> getErrors() {
        return errors;
    }
    
}

@lexer::members {
    private java.util.List<LexError> errors 
            = new java.util.ArrayList<LexError>();
    @Override public void displayRecognitionError(String[] tn,
            RecognitionException re) {
        errors.add(new LexError(this, re, tn));
    }
    public java.util.List<LexError> getErrors() {
        return errors;
    }
}

compilationUnit returns [CompilationUnit compilationUnit]
    : { $compilationUnit = new CompilationUnit(null); }
      ( 
        ca1=compilerAnnotations
        SEMICOLON
        { $compilationUnit.getCompilerAnnotations().addAll($ca1.annotations); }
      )?
      importList 
      { $compilationUnit.setImportList($importList.importList); }
      ( 
        ca2=compilerAnnotations declaration
        { if ($declaration.declaration!=null)
              $compilationUnit.addDeclaration($declaration.declaration); 
          if ($declaration.declaration!=null)
              $declaration.declaration.getCompilerAnnotations().addAll($ca2.annotations); } 
      )*
      EOF
    ;

importList returns [ImportList importList]
    : { $importList = new ImportList(null); } 
      ( importDeclaration { $importList.addImport($importDeclaration.importDeclaration); } )*
    ;

importDeclaration returns [Import importDeclaration]
    @init { ImportPath importPath=null; 
            ImportMemberOrTypeList il=null; }
    : IMPORT 
      { $importDeclaration = new Import($IMPORT); } 
      ( 
        pn1=packageName 
        { importPath = new ImportPath(null);
          importPath.addIdentifier($pn1.identifier); 
          $importDeclaration.setImportPath(importPath); } 
        ( 
          MEMBER_OP 
          pn2=packageName 
          { importPath.addIdentifier($pn2.identifier); } 
        )*
      )
    LBRACE
    { il = new ImportMemberOrTypeList($LBRACE);
      $importDeclaration.setImportMemberOrTypeList(il); }
    ( 
      ie1=importElement { il.addImportMemberOrType($ie1.importMemberOrType); } 
      ( COMMA ie2=importElement { il.addImportMemberOrType($ie2.importMemberOrType); } )* 
      ( COMMA iw=importWildcard { il.setImportWildcard($iw.importWildcard); } )?
    | iw=importWildcard { il.setImportWildcard($iw.importWildcard); }
    )
    RBRACE
    { il.setEndToken($RBRACE); }
    ;

importElement returns [ImportMemberOrType importMemberOrType]
    : compilerAnnotations
    (
      memberAlias? memberName
      { $importMemberOrType = new ImportMember(null);
        if ($memberAlias.alias!=null) 
            $importMemberOrType.setAlias($memberAlias.alias);
        $importMemberOrType.setIdentifier($memberName.identifier); }
    | typeAlias? typeName
      { $importMemberOrType = new ImportType(null);
        if ($typeAlias.alias!=null)
            $importMemberOrType.setAlias($typeAlias.alias);
        $importMemberOrType.setIdentifier($typeName.identifier); }
    )
    { if ($importMemberOrType!=null)
        $importMemberOrType.getCompilerAnnotations().addAll($compilerAnnotations.annotations); }
    ;

importWildcard returns [ImportWildcard importWildcard]
    : ELLIPSIS
      { $importWildcard = new ImportWildcard($ELLIPSIS); }
    ;

memberAlias returns [Alias alias]
    : memberName SPECIFY
      { $alias = new Alias($SPECIFY); 
        $alias.setIdentifier($memberName.identifier); }
    ;

typeAlias returns [Alias alias]
    : typeName SPECIFY
      { $alias = new Alias($SPECIFY); 
        $alias.setIdentifier($typeName.identifier); }
    ;

packageName returns [Identifier identifier]
    : LIDENTIFIER
      { $identifier = new Identifier($LIDENTIFIER); }
    ;

typeName returns [Identifier identifier]
    : UIDENTIFIER
      { $identifier = new Identifier($UIDENTIFIER); }
    ;

annotationName returns [Identifier identifier]
    : LIDENTIFIER
      { $identifier = new Identifier($LIDENTIFIER); }
    ;

memberName returns [Identifier identifier]
    : LIDENTIFIER
      { $identifier = new Identifier($LIDENTIFIER); }
    ;
    
objectDeclaration returns [ObjectDefinition declaration]
    : OBJECT_DEFINITION
      { $declaration = new ObjectDefinition($OBJECT_DEFINITION); 
        $declaration.setType(new ValueModifier(null)); }
      memberName 
      { $declaration.setIdentifier($memberName.identifier); }
      ( 
        extendedType
        { $declaration.setExtendedType($extendedType.extendedType); } 
      )?
      ( 
        satisfiedTypes
        { $declaration.setSatisfiedTypes($satisfiedTypes.satisfiedTypes); } 
      )?
      (
        classBody
        { $declaration.setClassBody($classBody.classBody); }
      | { displayRecognitionError(getTokenNames(), 
              new MismatchedTokenException(LBRACE, input)); }
        SEMICOLON
        { $declaration.setEndToken($SEMICOLON); }
      )
    //-> ^(OBJECT_DEFINITION VALUE_MODIFIER memberName extendedType? satisfiedTypes? classBody?) 
    ;

voidOrInferredMethodDeclaration returns [AnyMethod declaration]
    @init { MethodDefinition def=null;
            MethodDeclaration dec=null; }
    : (
        VOID_MODIFIER
        { VoidModifier vm = new VoidModifier($VOID_MODIFIER);
          def = new MethodDefinition($VOID_MODIFIER);
          dec = new MethodDeclaration($VOID_MODIFIER);
          def.setType(vm);
          dec.setType(vm);
          $declaration = def; }
      | FUNCTION_MODIFIER
        { FunctionModifier fm = new FunctionModifier($FUNCTION_MODIFIER);
          def = new MethodDefinition($FUNCTION_MODIFIER);
          dec = new MethodDeclaration($FUNCTION_MODIFIER);
          def.setType(fm);
          dec.setType(fm);
          $declaration = def; }
      )
      memberName
      { dec.setIdentifier($memberName.identifier); 
        def.setIdentifier($memberName.identifier); }
      (
        typeParameters
        { def.setTypeParameterList($typeParameters.typeParameterList); 
          dec.setTypeParameterList($typeParameters.typeParameterList); }        
      )?
      (
        parameters
        { def.addParameterList($parameters.parameterList); 
          dec.addParameterList($parameters.parameterList); }
      )*
      //metatypes? 
      (
        typeConstraints
        { def.setTypeConstraintList($typeConstraints.typeConstraintList); 
          dec.setTypeConstraintList($typeConstraints.typeConstraintList); }
      )?
      ( 
        block 
        { def.setBlock($block.block); }
      //-> ^(METHOD_DEFINITION VOID_MODIFIER memberName methodParameters? block)   
      | 
        (
          specifier
          { dec.setSpecifierExpression($specifier.specifierExpression); }
        )?
        { $declaration = dec; }
        SEMICOLON
        { $declaration.setEndToken($SEMICOLON); }
      //-> ^(METHOD_DECLARATION VOID_MODIFIER memberName methodParameters? specifier?)   
      )
    ;

setterDeclaration returns [AttributeSetterDefinition declaration]
    : ASSIGN 
      { $declaration = new AttributeSetterDefinition($ASSIGN); 
        $declaration.setType( new VoidModifier(null) ); }
      memberName 
      { $declaration.setIdentifier($memberName.identifier); }
      ( 
        block
        { $declaration.setBlock($block.block); }
      | { displayRecognitionError(getTokenNames(), 
              new MismatchedTokenException(LBRACE, input)); }
        SEMICOLON
        { $declaration.setEndToken($SEMICOLON); }
      )
    //-> ^(ATTRIBUTE_SETTER_DEFINITION[$ASSIGN] VOID_MODIFIER memberName block)
    ;

inferredAttributeDeclaration returns [AnyAttribute declaration]
    @init { AttributeGetterDefinition def=null;
            AttributeDeclaration dec=null; }
    : VALUE_MODIFIER 
      { ValueModifier fm = new ValueModifier($VALUE_MODIFIER);
        def = new AttributeGetterDefinition($VALUE_MODIFIER);
        dec = new AttributeDeclaration($VALUE_MODIFIER);
        def.setType(fm);
        dec.setType(fm);
        $declaration = def; }
      memberName
      { dec.setIdentifier($memberName.identifier); 
        def.setIdentifier($memberName.identifier); }
      ( 
        (
          specifier 
          { dec.setSpecifierOrInitializerExpression($specifier.specifierExpression); }
        | 
          initializer
          { dec.setSpecifierOrInitializerExpression($initializer.initializerExpression); }
        )?
        { $declaration = dec; }
        SEMICOLON
        { $declaration.setEndToken($SEMICOLON); }
        //-> ^(ATTRIBUTE_DECLARATION VALUE_MODIFIER memberName specifier? initializer?)
      | block
        { def.setBlock($block.block); }
        //-> ^(ATTRIBUTE_GETTER_DEFINITION VALUE_MODIFIER memberName block)
      )
    ;

typedMethodOrAttributeDeclaration returns [TypedDeclaration declaration]
    @init { AttributeGetterDefinition adef=new AttributeGetterDefinition(null);
            AttributeDeclaration adec=new AttributeDeclaration(null);
            MethodDefinition mdef=new MethodDefinition(null);
            MethodDeclaration mdec=new MethodDeclaration(null); 
            $declaration = adef; }
    : unionType
      { adef.setType($unionType.type);
        adec.setType($unionType.type); 
        mdef.setType($unionType.type);
        mdec.setType($unionType.type); }
      memberName
      { adef.setIdentifier($memberName.identifier);
        adec.setIdentifier($memberName.identifier); 
        mdef.setIdentifier($memberName.identifier);
        mdec.setIdentifier($memberName.identifier); }
      ( 
        { $declaration = mdef; }
        (
          typeParameters
          { mdef.setTypeParameterList($typeParameters.typeParameterList);
            mdec.setTypeParameterList($typeParameters.typeParameterList); }
        )?
        (
          parameters
          { mdef.addParameterList($parameters.parameterList);
            mdec.addParameterList($parameters.parameterList); }
        )+
        //metatypes? 
        ( 
          typeConstraints
          { mdef.setTypeConstraintList($typeConstraints.typeConstraintList);
            mdec.setTypeConstraintList($typeConstraints.typeConstraintList); }
        )?
        ( 
          mb=memberBody[$unionType.type] 
         { mdef.setBlock($mb.block); }
        //-> ^(METHOD_DEFINITION unionType memberName methodParameters memberBody)
        | 
          (
            ms=specifier
           { mdec.setSpecifierExpression($ms.specifierExpression); }
          )?
          { $declaration = mdec; }
          s1=SEMICOLON
          { $declaration.setEndToken($s1); }
        //-> ^(METHOD_DECLARATION unionType memberName methodParameters specifier?)
        )
      | 
        (
          as=specifier 
          { adec.setSpecifierOrInitializerExpression($as.specifierExpression); }
        | 
          initializer
          { adec.setSpecifierOrInitializerExpression($initializer.initializerExpression); }
        )?
        { $declaration = adec; }
        s2=SEMICOLON
        { $declaration.setEndToken($s2); }
      //-> ^(ATTRIBUTE_DECLARATION unionType memberName specifier? initializer?)
      | ab=memberBody[$unionType.type]
        { adef.setBlock($ab.block); }
      //-> ^(ATTRIBUTE_GETTER_DEFINITION unionType memberName memberBody)      
      )
    ;

interfaceDeclaration returns [AnyInterface declaration]
    @init { InterfaceDefinition def=null; 
            InterfaceDeclaration dec=null; }
    : INTERFACE_DEFINITION
      { def = new InterfaceDefinition($INTERFACE_DEFINITION); 
        dec = new InterfaceDeclaration($INTERFACE_DEFINITION);
        $declaration = def; }
      typeName 
      { dec.setIdentifier($typeName.identifier); 
        def.setIdentifier($typeName.identifier); }
      (
        typeParameters 
        { def.setTypeParameterList($typeParameters.typeParameterList); 
          dec.setTypeParameterList($typeParameters.typeParameterList); }
      )?
      (
        caseTypes
        { def.setCaseTypes($caseTypes.caseTypes); 
          dec.setCaseTypes($caseTypes.caseTypes); }
      )?
      /*metatypes?*/ 
      (
        adaptedTypes
        { def.setAdaptedTypes($adaptedTypes.adaptedTypes); }
      )?
      (
        satisfiedTypes
        { def.setSatisfiedTypes($satisfiedTypes.satisfiedTypes); 
          dec.setSatisfiedTypes($satisfiedTypes.satisfiedTypes); }
      )?
      (
        typeConstraints
        { def.setTypeConstraintList($typeConstraints.typeConstraintList); 
          dec.setTypeConstraintList($typeConstraints.typeConstraintList); }
      )?
      (
        interfaceBody
        { def.setInterfaceBody($interfaceBody.interfaceBody); }
      //-> ^(INTERFACE_DEFINITION typeName interfaceParameters? interfaceBody)
      | 
        (
          typeSpecifier
          { dec.setTypeSpecifier($typeSpecifier.typeSpecifier); }
        )? 
        { $declaration = dec; }
        SEMICOLON
        { $declaration.setEndToken($SEMICOLON); }
      //-> ^(INTERFACE_DECLARATION[$INTERFACE_DEFINITION] typeName interfaceParameters? typeSpecifier?)
      )
    ;

classDeclaration returns [AnyClass declaration]
    @init { ClassDefinition def=null; 
            ClassDeclaration dec=null; }
    : CLASS_DEFINITION 
      { def = new ClassDefinition($CLASS_DEFINITION); 
        dec = new ClassDeclaration($CLASS_DEFINITION);
        $declaration = def; }
      typeName
      { dec.setIdentifier($typeName.identifier); 
        def.setIdentifier($typeName.identifier); }
      (
        typeParameters 
        { def.setTypeParameterList($typeParameters.typeParameterList); 
          dec.setTypeParameterList($typeParameters.typeParameterList); }
      )?
      (
        parameters
        { def.setParameterList($parameters.parameterList); 
          dec.setParameterList($parameters.parameterList); }
      )?
      (
        caseTypes
        { def.setCaseTypes($caseTypes.caseTypes); 
          dec.setCaseTypes($caseTypes.caseTypes); }
      )?
      /*metatypes?*/ 
      (
        extendedType
        { def.setExtendedType($extendedType.extendedType); 
          dec.setExtendedType($extendedType.extendedType); }
      )? 
      (
        satisfiedTypes
        { def.setSatisfiedTypes($satisfiedTypes.satisfiedTypes); 
          dec.setSatisfiedTypes($satisfiedTypes.satisfiedTypes); }
      )?
      (
        typeConstraints
        { def.setTypeConstraintList($typeConstraints.typeConstraintList); 
          dec.setTypeConstraintList($typeConstraints.typeConstraintList); }
      )?
      (
        classBody
        { def.setClassBody($classBody.classBody); }
      //-> ^(CLASS_DEFINITION typeName classParameters? classBody)
      | 
        (
          typeSpecifier
          { dec.setTypeSpecifier($typeSpecifier.typeSpecifier); }
        )?
        { $declaration = dec; }
        SEMICOLON
        { $declaration.setEndToken($SEMICOLON); }
      //-> ^(CLASS_DECLARATION[$CLASS_DEFINITION] typeName classParameters? typeSpecifier?)
      )
    ;

    
block returns [Block block]
    : LBRACE 
      { $block = new Block($LBRACE); }
      (
        declarationOrStatement
        { if ($declarationOrStatement.statement!=null)
              $block.addStatement($declarationOrStatement.statement); }
      )*
      RBRACE
      { $block.setEndToken($RBRACE); }
    //-> ^(BLOCK[$LBRACE] annotatedDeclarationOrStatement*)
    ;

//Note: interface bodies can't really contain 
//      statements, but error recovery works
//      much better if we validate that later
//      on, instead of doing it in the parser.
interfaceBody returns [InterfaceBody interfaceBody]
    : LBRACE 
      { $interfaceBody = new InterfaceBody($LBRACE); }
      (
        declarationOrStatement
        { if ($declarationOrStatement.statement!=null)
              $interfaceBody.addStatement($declarationOrStatement.statement); }
      )*
      RBRACE
      { $interfaceBody.setEndToken($RBRACE); }
    //-> ^(INTERFACE_BODY[$LBRACE] annotatedDeclarationOrStatement2*)
    ;

classBody returns [ClassBody classBody]
    : LBRACE
      { $classBody = new ClassBody($LBRACE); }
      (
        declarationOrStatement
        { if ($declarationOrStatement.statement!=null)
              $classBody.addStatement($declarationOrStatement.statement); }
      )*
      RBRACE
      { $classBody.setEndToken($RBRACE); }
    //-> ^(CLASS_BODY[$LBRACE] annotatedDeclarationOrStatement2*)
    ;

//This rule accounts for the problem that we
//can't tell whether a member body is a block
//or a named argument list until after we
//finish parsing it
memberBody[StaticType type] returns [Block block]
      //options { memoize=true; }
    : 
    {$type instanceof BaseType}?=>
    (
      (namedArguments)
      => namedArguments //first try to match with no directives or control structures
      { $block = new Block(null);
        SimpleType t = (SimpleType) $type;
        Return r = new Return(null);
        Expression e = new Expression(null);
        InvocationExpression ie = new InvocationExpression(null);
        BaseTypeExpression bme = new BaseTypeExpression(null);
        bme.setIdentifier(t.getIdentifier());
        bme.setTypeArguments(new InferredTypeArguments(null));
        if (t.getTypeArgumentList()!=null)
            bme.setTypeArguments(t.getTypeArgumentList());
        ie.setPrimary(bme);
        ie.setNamedArgumentList($namedArguments.namedArgumentList);
        e.setTerm(ie);
        r.setExpression(e);
        $block.addStatement(r); }
    //-> ^(BLOCK ^(RETURN ^(EXPRESSION ^(INVOCATION_EXPRESSION ^(BASE_TYPE_EXPRESSION { ((CommonTree)$mt).getChild(0) } { ((CommonTree)$mt).getChild(1) } ) namedArguments))))
      | b1=block //if there is a "return" directive or control structure, it must be a block
      { $block=$b1.block; } 
    )
    | b2=block
      { $block=$b2.block; }
    ;

extendedType returns [ExtendedType extendedType]
    : EXTENDS
      { $extendedType = new ExtendedType($EXTENDS); }
      (
        type
        { $extendedType.setType($type.type); }
        //-> ^(EXTENDED_TYPE[$EXTENDS] type ^(INVOCATION_EXPRESSION ^(EXTENDED_TYPE_EXPRESSION) positionalArguments))
      | SUPER MEMBER_OP 
        typeReference 
        { QualifiedType qt=new QualifiedType(null);
          SuperType st = new SuperType($SUPER);
          qt.setOuterType(st);
          qt.setIdentifier($typeReference.identifier);
          if ($typeReference.typeArgumentList!=null)
              qt.setTypeArgumentList($typeReference.typeArgumentList);
          $extendedType.setType(qt); }
        //-> ^(EXTENDED_TYPE[$EXTENDS] ^(QUALIFIED_TYPE SUPER_TYPE[$SUPER] typeReference) ^(INVOCATION_EXPRESSION ^(EXTENDED_TYPE_EXPRESSION) positionalArguments))
      )
      (
        positionalArguments
        { InvocationExpression ie = new InvocationExpression(null);
          ExtendedTypeExpression ete = new ExtendedTypeExpression(null);
          ie.setPrimary(ete);
          ete.setExtendedType($extendedType.getType());
          ie.setPositionalArgumentList($positionalArguments.positionalArgumentList);
          $extendedType.setInvocationExpression(ie); }
       )
    ;

satisfiedTypes returns [SatisfiedTypes satisfiedTypes]
    : SATISFIES 
      { $satisfiedTypes = new SatisfiedTypes($SATISFIES); }
      t1=type 
      { $satisfiedTypes.addType($t1.type); }
      (
        INTERSECTION_OP 
        t2=type
        { $satisfiedTypes.addType($t2.type); }
      )*
    //-> ^(SATISFIED_TYPES[$SATISFIES] type+)
    ;

abstractedType returns [AbstractedType abstractedType]
    : ABSTRACTED_TYPE
      { $abstractedType = new AbstractedType($ABSTRACTED_TYPE); }
      type
      { $abstractedType.setType($type.type); }
    ;

adaptedTypes returns [AdaptedTypes adaptedTypes]
    : ADAPTED_TYPES 
      { $adaptedTypes = new AdaptedTypes($ADAPTED_TYPES); }
      t1=type 
      { $adaptedTypes.addType($t1.type); }
      (
        INTERSECTION_OP 
        t2=type
        { $adaptedTypes.addType($t2.type); }
      )*
    ;

caseTypes returns [CaseTypes caseTypes]
    : CASE_TYPES
      { $caseTypes = new CaseTypes($CASE_TYPES); }
      ct1=caseType 
      { if ($ct1.type!=null) $caseTypes.addType($ct1.type); 
        if ($ct1.instance!=null) $caseTypes.addBaseMemberExpression($ct1.instance); }
      (
        UNION_OP 
        ct2=caseType
      { if ($ct2.type!=null) $caseTypes.addType($ct2.type); 
        if ($ct2.instance!=null) $caseTypes.addBaseMemberExpression($ct2.instance); }
      )*
    ;

caseType returns [SimpleType type, BaseMemberExpression instance]
    : t=type 
      { $type=$t.type;}
    | memberName //-> ^(BASE_MEMBER_EXPRESSION memberName)
      { $instance = new BaseMemberExpression(null);
        $instance.setIdentifier($memberName.identifier);
        $instance.setTypeArguments( new InferredTypeArguments(null) ); }
    ;

//Support for metatypes
//Note that we don't need this for now
/*metatypes
    : IS_OP type ('&' type)* 
    -> ^(METATYPES[$IS_OP] type*)
    ;*/

parameters returns [ParameterList parameterList]
    : LPAREN
      { $parameterList=new ParameterList($LPAREN); }
      (
        ap1=parameterDeclaration 
        { $parameterList.addParameter($ap1.parameter); }
        (
          COMMA 
          ap2=parameterDeclaration
          { $parameterList.addParameter($ap2.parameter); }
        )*
      )? 
      RPAREN
      { $parameterList.setEndToken($RPAREN); }
      //-> ^(PARAMETER_LIST[$LPAREN] annotatedParameter*)
    ;

//special rule for syntactic predicate
//to distinguish between a formal 
//parameter list and a parenthesized body 
//of an inline callable argument
//be careful with this one, since it 
//matches "()", which can also be an 
//argument list
parametersStart
    : LPAREN ( annotatedDeclarationStart | RPAREN )
    ;
    
// FIXME: This accepts more than the language spec: named arguments
// and varargs arguments can appear in any order.  We'll have to
// enforce the rule that the ... appears at the end of the parapmeter
// list in a later pass of the compiler.
parameter returns [Parameter parameter]
    @init { ValueParameterDeclaration vp = new ValueParameterDeclaration(null); 
            FunctionalParameterDeclaration fp = new FunctionalParameterDeclaration(null); 
            $parameter = vp; }
    : parameterType
      { vp.setType($parameterType.type); }
      { fp.setType($parameterType.type); }
      memberName
      { vp.setIdentifier($memberName.identifier); }
      { fp.setIdentifier($memberName.identifier); }
      (
        (
          valueParameter
        )?
        //-> ^(VALUE_PARAMETER_DECLARATION parameterType memberName specifier?)
      |
        (
          parameters
          { fp.addParameterList($parameters.parameterList); }
        )+
        { $parameter=fp; }
          //for callable parameters
        //-> ^(FUNCTIONAL_PARAMETER_DECLARATION parameterType memberName parameters+ specifier?)
      )
      (
        specifier
        { $parameter.setSpecifierExpression($specifier.specifierExpression); }
      )?
    ;

parameterDeclaration returns [Parameter parameter]
    : compilerAnnotations
      annotations 
      p=parameter
      { $parameter=$p.parameter;
        $parameter.setAnnotationList($annotations.annotationList); }
      { if ($parameter!=null)
        $parameter.getCompilerAnnotations().addAll($compilerAnnotations.annotations); }
    ;

valueParameter
    : ENTRY_OP unionType memberName
    ;

parameterType returns [Type type]
    : unionType 
      { $type = $unionType.type; }
      ( 
        ELLIPSIS
        { SequencedType st = new SequencedType($ELLIPSIS);
          st.setType($type);
          $type = st; }
      )?
    | VOID_MODIFIER
      { $type = new VoidModifier($VOID_MODIFIER); }
    ;

typeParameters returns [TypeParameterList typeParameterList]
    : SMALLER_OP
      { $typeParameterList = new TypeParameterList($SMALLER_OP); }
      tp1=typeParameter
      { $typeParameterList.addTypeParameterDeclaration($tp1.typeParameter); }
      (
        COMMA 
        tp2=typeParameter
        { $typeParameterList.addTypeParameterDeclaration($tp2.typeParameter); }
      )*
      LARGER_OP
      { $typeParameterList.setEndToken($LARGER_OP); }
    //-> ^(TYPE_PARAMETER_LIST[$SMALLER_OP] typeParameter+)
    ;

typeParameter returns [TypeParameterDeclaration typeParameter]
    : { $typeParameter = new TypeParameterDeclaration(null); }
      ( 
        variance 
        { $typeParameter.setTypeVariance($variance.typeVariance); } 
      )? 
      typeName
      { $typeParameter.setIdentifier($typeName.identifier); } 
    //-> ^(TYPE_PARAMETER_DECLARATION variance? typeName)
    | //{ $typeParameter = new SequencedTypeParameter(null); }
      typeName 
      //{ $typeParameter.setIdentifier($typeName.identifier); } 
      ELLIPSIS
    //-> ^(SEQUENCED_TYPE_PARAMETER typeName)
    ;

variance returns [TypeVariance typeVariance]
    : IN_OP //-> ^(TYPE_VARIANCE[$IN_OP])
      { $typeVariance = new TypeVariance($IN_OP); }
    | OUT //-> ^(TYPE_VARIANCE[$OUT])
      { $typeVariance = new TypeVariance($OUT); }
    ;
    
typeConstraint returns [TypeConstraint typeConstraint]
    : compilerAnnotations
      TYPE_CONSTRAINT
      { $typeConstraint = new TypeConstraint($TYPE_CONSTRAINT); 
        $typeConstraint.getCompilerAnnotations().addAll($compilerAnnotations.annotations); }
      typeName 
      { $typeConstraint.setIdentifier($typeName.identifier); }
      //(typeParameters)?
      (
        parameters
        { $typeConstraint.setParameterList($parameters.parameterList); }
      )?
      (
        caseTypes
        { $typeConstraint.setCaseTypes($caseTypes.caseTypes); }
      )?
      //metatypes? 
      (
        satisfiedTypes
        { $typeConstraint.setSatisfiedTypes($satisfiedTypes.satisfiedTypes); }
      )?
      ( 
        abstractedType
        { $typeConstraint.setAbstractedType($abstractedType.abstractedType); }
      )?
    ;

typeConstraints returns [TypeConstraintList typeConstraintList]
    : { $typeConstraintList=new TypeConstraintList(null); }
      (
        typeConstraint
        { $typeConstraintList.addTypeConstraint($typeConstraint.typeConstraint); }
      )+
    //-> ^(TYPE_CONSTRAINT_LIST typeConstraint2+)
    ;

declarationOrStatement returns [Statement statement]
    options {memoize=true;}
    : compilerAnnotations
      ( 
        (annotatedDeclarationStart) => declaration
        { $statement=$declaration.declaration; }
      | s=statement
        { $statement=$s.statement; }
      )
      { if ($statement!=null)
            $statement.getCompilerAnnotations().addAll($compilerAnnotations.annotations); }
    ;

declaration returns [Declaration declaration]
    : annotations
    ( 
      objectDeclaration
      { $declaration=$objectDeclaration.declaration; }
    | setterDeclaration
      { $declaration=$setterDeclaration.declaration; }
    | voidOrInferredMethodDeclaration
      { $declaration=$voidOrInferredMethodDeclaration.declaration; }
    | inferredAttributeDeclaration
      { $declaration=$inferredAttributeDeclaration.declaration; }
    | typedMethodOrAttributeDeclaration
      { $declaration=$typedMethodOrAttributeDeclaration.declaration; }
    | classDeclaration
      { $declaration=$classDeclaration.declaration; }
    | interfaceDeclaration
      { $declaration=$interfaceDeclaration.declaration; }
    /*| { displayRecognitionError(getTokenNames(), 
              new MismatchedTokenException(CLASS_DEFINITION, input)); }
      SEMICOLON
      { $declaration=new BrokenDeclaration($SEMICOLON); }*/
    )
    { if ($declaration!=null)
          $declaration.setAnnotationList($annotations.annotationList);  }
    ;

//special rule for syntactic predicate
//to distinguish between an annotation
//and an expression statement
/*annotatedDeclarationStart
    : declarationStart
    | LIDENTIFIER
      ( 
          declarationStart
        | LIDENTIFIER
        | nonstringLiteral | stringLiteral
        | arguments annotatedDeclarationStart //we need to recurse because it could be an inline callable argument
      )
    ;*/

annotatedDeclarationStart
    : annotation* declarationStart
    ;

//special rule for syntactic predicates
//that distinguish declarations from
//expressions
declarationStart
    : declarationKeyword 
    | unionType (ELLIPSIS | LIDENTIFIER)
    ;

declarationKeyword
    : VALUE_MODIFIER
    | FUNCTION_MODIFIER
    | ASSIGN
    | VOID_MODIFIER
    | INTERFACE_DEFINITION
    | CLASS_DEFINITION
    | OBJECT_DEFINITION
    ;

statement returns [Statement statement]
    : directiveStatement
      { $statement = $directiveStatement.directive; }
    | controlStatement
      { $statement = $controlStatement.controlStatement; }
    | expressionOrSpecificationStatement
      { $statement = $expressionOrSpecificationStatement.statement; }
    ;

expressionOrSpecificationStatement returns [Statement statement]
    @init { SpecifierStatement ss=new SpecifierStatement(null); 
            ExpressionStatement es=new ExpressionStatement(null); }
    : expression 
      { es.setExpression($expression.expression);
        $statement = es; }
      (
        { $expression.expression.getTerm() instanceof BaseMemberExpression }?=>
        specifier
        { ss.setSpecifierExpression($specifier.specifierExpression);
          ss.setBaseMemberExpression((BaseMemberExpression)$expression.expression.getTerm()); 
          $statement = ss; }
      )?
      (
        SEMICOLON
        { $statement.setEndToken($SEMICOLON); }
      | { displayRecognitionError(getTokenNames(), 
              new MismatchedTokenException(SEMICOLON, input)); }
        COMMA
        { $statement.setEndToken($COMMA); }
    )
    ;

directiveStatement returns [Directive directive]
    : d=directive 
      { $directive=$d.directive; } 
      SEMICOLON
      { $directive.setEndToken($SEMICOLON); }
    ;

directive returns [Directive directive]
    : returnDirective
      { $directive = $returnDirective.directive; }
    | throwDirective
      { $directive = $throwDirective.directive; }
    | breakDirective
      { $directive = $breakDirective.directive; }
    | continueDirective
      { $directive = $continueDirective.directive; }
    ;

returnDirective returns [Return directive]
    : RETURN 
      { $directive = new Return($RETURN); }
      (
        expression
        { $directive.setExpression($expression.expression); }
      )?
    ;

throwDirective returns [Throw directive]
    : THROW
      { $directive = new Throw($THROW); }
      ( 
        expression
        { $directive.setExpression($expression.expression); }
      )?
    ;

breakDirective returns [Break directive]
    : BREAK
      { $directive = new Break($BREAK); }
    ;

continueDirective returns [Continue directive]
    : CONTINUE
      { $directive = new Continue($CONTINUE); }
    ;

typeSpecifier returns [TypeSpecifier typeSpecifier]
    : SPECIFY 
      { $typeSpecifier = new TypeSpecifier($SPECIFY); }
      type
      { $typeSpecifier.setType($type.type); }
    //-> ^(TYPE_SPECIFIER[$SPECIFY] type)
    ;

specifier returns [SpecifierExpression specifierExpression]
    : SPECIFY 
      { $specifierExpression = new SpecifierExpression($SPECIFY); }
      expression
      { $specifierExpression.setExpression($expression.expression); }
    ;

initializer returns [InitializerExpression initializerExpression]
    : ASSIGN_OP 
      { $initializerExpression = new InitializerExpression($ASSIGN_OP); }
      expression
      { $initializerExpression.setExpression($expression.expression); }
    //-> ^(INITIALIZER_EXPRESSION[$ASSIGN_OP] expression)
    ;

expression returns [Expression expression]
    : { $expression = new Expression(null); }
      assignmentExpression
      { $expression.setTerm($assignmentExpression.term); }
    ;

base returns [Primary primary]
    : nonstringLiteral
      { $primary=$nonstringLiteral.literal; }
    | stringExpression
      { $primary=$stringExpression.atom; }
    | enumeration
      { $primary=$enumeration.sequenceEnumeration; }
    | selfReference
      { $primary=$selfReference.atom; }
    | typeReference
      { BaseTypeExpression bte = new BaseTypeExpression(null);
        bte.setIdentifier($typeReference.identifier);
        bte.setTypeArguments( new InferredTypeArguments(null) );
        if ($typeReference.typeArgumentList!=null)
            bte.setTypeArguments($typeReference.typeArgumentList);
        $primary=bte; }
    | memberReference
      { BaseMemberExpression bme = new BaseMemberExpression(null);
        bme.setIdentifier($memberReference.identifier);
        bme.setTypeArguments( new InferredTypeArguments(null) );
        if ($memberReference.typeArgumentList!=null)
            bme.setTypeArguments($memberReference.typeArgumentList);
        $primary=bme; }
    | parExpression
      { $primary=$parExpression.expression; }
    ;

primary returns [Primary primary]
    : base
      { $primary=$base.primary; }
    (   
        inlineFunctionalArgument
      | qualifiedReference
      { QualifiedMemberOrTypeExpression qe;
        if ($qualifiedReference.isMember)
            qe = new QualifiedMemberExpression(null);
        else
            qe = new QualifiedTypeExpression(null);
        qe.setPrimary($primary);
        qe.setMemberOperator($qualifiedReference.operator);
        qe.setIdentifier($qualifiedReference.identifier);
        qe.setTypeArguments( new InferredTypeArguments(null) );
        if ($qualifiedReference.typeArgumentList!=null)
            qe.setTypeArguments($qualifiedReference.typeArgumentList);
        $primary=qe; }
      | indexExpression
        { IndexExpression xe = new IndexExpression(null);
          xe.setPrimary($primary);
          xe.setIndexOperator($indexExpression.operator); 
          xe.setElementOrRange($indexExpression.elementOrRange);
          $primary=xe; }
      | arguments
        { InvocationExpression ie = new InvocationExpression(null);
          ie.setPrimary($primary);
          if ($arguments.argumentList instanceof PositionalArgumentList)
              ie.setPositionalArgumentList((PositionalArgumentList)$arguments.argumentList);
          if ($arguments.argumentList instanceof NamedArgumentList)
              ie.setNamedArgumentList((NamedArgumentList)$arguments.argumentList);
          $primary=ie; }
    )*
    ;
   
qualifiedReference returns [Identifier identifier, MemberOperator operator, 
                            TypeArgumentList typeArgumentList, boolean isMember]
    : memberSelectionOperator 
      { $operator = $memberSelectionOperator.operator;
        $identifier = new Identifier($operator.getToken());
        $identifier.setText("");
        $isMember=true; }
      ( 
        memberReference
        { $identifier = $memberReference.identifier;
          $typeArgumentList = $memberReference.typeArgumentList; }
      | typeReference
        { $identifier = $typeReference.identifier;
          $typeArgumentList = $typeReference.typeArgumentList; 
          $isMember=false; }
      | (~(LIDENTIFIER|UIDENTIFIER))=>
        { displayRecognitionError(getTokenNames(), 
              new MismatchedTokenException(LIDENTIFIER, input)); }
      )
    ;

indexExpression returns [IndexOperator operator, ElementOrRange elementOrRange]
    : elementSelectionOperator 
      { $operator=$elementSelectionOperator.operator; }
      indexOrIndexRange 
      { $elementOrRange=$indexOrIndexRange.elementOrRange; }
      RBRACKET
    ;

memberSelectionOperator returns [MemberOperator operator]
    : MEMBER_OP
      { $operator=new MemberOp($MEMBER_OP); }
    | SAFE_MEMBER_OP
      { $operator=new SafeMemberOp($SAFE_MEMBER_OP); }
    | SPREAD_OP
      { $operator=new SpreadOp($SPREAD_OP); }
    ;

elementSelectionOperator returns [IndexOperator operator]
    : SAFE_INDEX_OP
      { $operator=new SafeIndexOp($SAFE_INDEX_OP); }
    | INDEX_OP
      { $operator=new IndexOp($INDEX_OP); }
    ;

enumeration returns [SequenceEnumeration sequenceEnumeration]
    : LBRACE { $sequenceEnumeration = new SequenceEnumeration($LBRACE); } 
      (
        expressions
        { $sequenceEnumeration.setExpressionList($expressions.expressionList); }
      )?
      RBRACE
      { $sequenceEnumeration.setEndToken($RBRACE); }
    ;

expressions returns [ExpressionList expressionList]
    : { $expressionList = new ExpressionList(null); }
      e1=expression 
      { $expressionList.addExpression($e1.expression); }
      ( COMMA 
        e2=expression 
        { $expressionList.addExpression($e2.expression); } 
      )*
    ;

memberReference returns [Identifier identifier, TypeArgumentList typeArgumentList]
    : memberName
      { $identifier = $memberName.identifier; }
      (
        (typeArgumentsStart)
        => typeArguments
        { $typeArgumentList = $typeArguments.typeArgumentList; }
      )?
    ;

typeReference returns [Identifier identifier, TypeArgumentList typeArgumentList]
    : typeName 
      { $identifier = $typeName.identifier; }
      (
        (typeArgumentsStart)
        => typeArguments
        { $typeArgumentList = $typeArguments.typeArgumentList; }
      )?
    ;

//special rule for syntactic predicate to 
//determine if we have a < operator, or a
//type argument list
typeArgumentsStart
    : SMALLER_OP
      UIDENTIFIER ('.' UIDENTIFIER)* /*| 'subtype')*/ (DEFAULT_OP|ARRAY)*
      ((INTERSECTION_OP|UNION_OP) UIDENTIFIER ('.' UIDENTIFIER)* (DEFAULT_OP|ARRAY)*)*
      (LARGER_OP|SMALLER_OP|COMMA|ELLIPSIS)
    ;

indexOrIndexRange returns [ElementOrRange elementOrRange]
    : l=index
    (
      { Element e = new Element(null);
        $elementOrRange = e;
        e.setExpression($l.expression); }
      | '...' 
      { ElementRange er1 = new ElementRange(null);
        $elementOrRange = er1;
        er1.setLowerBound($l.expression); }
      | '..' u=index 
      { ElementRange er2 = new ElementRange(null);
        $elementOrRange = er2;
        er2.setLowerBound($l.expression); 
        er2.setUpperBound($u.expression); }
    )
    ;

index returns [Expression expression]
    : additiveExpression 
      { $expression = new Expression(null);
        $expression.setTerm($additiveExpression.term); }
    ;

arguments returns [ArgumentList argumentList]
    : positionalArguments 
      { $argumentList = $positionalArguments.positionalArgumentList; }
    | namedArguments
      { $argumentList = $namedArguments.namedArgumentList; }
    ;

namedArguments returns [NamedArgumentList namedArgumentList]
    : LBRACE 
      { $namedArgumentList = new NamedArgumentList($LBRACE); }
      ( //TODO: get rid of the predicate and use the approach
        //      in expressionOrSpecificationStatement
        (namedArgumentStart) 
        => namedArgument
        { if ($namedArgument.namedArgument!=null) 
              $namedArgumentList.addNamedArgument($namedArgument.namedArgument); }
      )* 
      ( 
        sequencedArgument
        { $namedArgumentList.setSequencedArgument($sequencedArgument.sequencedArgument); }
      )?
      RBRACE
      { $namedArgumentList.setEndToken($RBRACE); }
    ;

sequencedArgument returns [SequencedArgument sequencedArgument]
    : compilerAnnotations
      expressions
      { sequencedArgument = new SequencedArgument(null);
        sequencedArgument.setExpressionList($expressions.expressionList);
        sequencedArgument.getCompilerAnnotations().addAll($compilerAnnotations.annotations); }
    ;

namedArgument returns [NamedArgument namedArgument]
    : compilerAnnotations 
    ( 
      namedSpecifiedArgument
      { $namedArgument = $namedSpecifiedArgument.specifiedArgument; }
    | 
      namedArgumentDeclaration
      { $namedArgument = $namedArgumentDeclaration.declaration; }
    )
    { if ($namedArgument!=null)
          namedArgument.getCompilerAnnotations().addAll($compilerAnnotations.annotations); }
    ;

namedSpecifiedArgument returns [SpecifiedArgument specifiedArgument]
    : memberName 
      { $specifiedArgument = new SpecifiedArgument(null); 
        $specifiedArgument.setIdentifier($memberName.identifier); }
      specifier 
      { $specifiedArgument.setSpecifierExpression($specifier.specifierExpression); }
      SEMICOLON
      { $specifiedArgument.setEndToken($SEMICOLON); }
    ;

objectArgument returns [ObjectArgument declaration]
    : OBJECT_DEFINITION 
      { $declaration = new ObjectArgument($OBJECT_DEFINITION); 
        $declaration.setType(new ValueModifier(null)); }
      memberName
      { $declaration.setIdentifier($memberName.identifier); }
      ( 
        extendedType
        { $declaration.setExtendedType($extendedType.extendedType); } 
      )?
      ( 
        satisfiedTypes
        { $declaration.setSatisfiedTypes($satisfiedTypes.satisfiedTypes); } 
      )?
      (
        classBody
        { $declaration.setClassBody($classBody.classBody); }
      | { displayRecognitionError(getTokenNames(), 
              new MismatchedTokenException(LBRACE, input)); }
        SEMICOLON
        { $declaration.setEndToken($SEMICOLON); }
      )
    //-> ^(OBJECT_ARGUMENT[$OBJECT_DEFINITION] VALUE_MODIFIER memberName extendedType? satisfiedTypes? classBody?)
    ;

voidOrInferredMethodArgument returns [MethodArgument declaration]
    : { $declaration=new MethodArgument(null); }
      (
        VOID_MODIFIER
      { $declaration.setType(new VoidModifier($VOID_MODIFIER)); }
      |
        FUNCTION_MODIFIER
      { $declaration.setType(new FunctionModifier($FUNCTION_MODIFIER)); }
      ) 
      memberName 
      { $declaration.setIdentifier($memberName.identifier); }
      (
        parameters
        { $declaration.addParameterList($parameters.parameterList); }
      )*
      block
      { $declaration.setBlock($block.block); }
    //-> ^(METHOD_ARGUMENT VOID_MODIFIER memberName parameters* block)
    ;

inferredGetterArgument returns [AttributeArgument declaration]
    : { $declaration=new AttributeArgument(null); }
      VALUE_MODIFIER 
      { $declaration.setType(new ValueModifier($VALUE_MODIFIER)); }
      memberName 
      { $declaration.setIdentifier($memberName.identifier); }
      block
      { $declaration.setBlock($block.block); }
      //-> ^(ATTRIBUTE_ARGUMENT VALUE_MODIFIER memberName block)      
    ;

typedMethodOrGetterArgument returns [TypedArgument declaration]
    @init { MethodArgument marg = new MethodArgument(null);
            AttributeArgument aarg = new AttributeArgument(null); 
            $declaration=aarg; }
    : unionType 
      { marg.setType($unionType.type);
        aarg.setType($unionType.type); }
      memberName
      { marg.setIdentifier($memberName.identifier);
        aarg.setIdentifier($memberName.identifier); }
      ( 
        { $declaration = marg; }
        (
          parameters
          { marg.addParameterList($parameters.parameterList); }
        )+
      )?
      memberBody[$unionType.type]
      { marg.setBlock($memberBody.block); 
        aarg.setBlock($memberBody.block); }
      //-> ^(METHOD_ARGUMENT unionType memberName parameters+ memberBody)
      //-> ^(ATTRIBUTE_ARGUMENT unionType memberName memberBody)      
    ;

namedArgumentDeclaration returns [NamedArgument declaration]
    : objectArgument
      { $declaration=$objectArgument.declaration; }
    | typedMethodOrGetterArgument
      { $declaration=$typedMethodOrGetterArgument.declaration; }
    | voidOrInferredMethodArgument
      { $declaration=$voidOrInferredMethodArgument.declaration; }
    | inferredGetterArgument
      { $declaration=$inferredGetterArgument.declaration; }
    ;

//special rule for syntactic predicate
//to distinguish between a named argument
//and a sequenced argument
namedArgumentStart
    : compilerAnnotation* 
      (specificationStart | declarationStart)
    ;

//special rule for syntactic predicates
specificationStart
    : LIDENTIFIER '='
    ;

parExpression returns [Expression expression] 
    : LPAREN 
      { $expression = new Expression($LPAREN); }
      assignmentExpression
      { $expression.setTerm($assignmentExpression.term); }
      RPAREN
      { $expression.setEndToken($RPAREN); }
    ;
    
positionalArguments returns [PositionalArgumentList positionalArgumentList]
    : LPAREN 
      { $positionalArgumentList = new PositionalArgumentList($LPAREN); }
      ( pa1=positionalArgument
        { $positionalArgumentList.addPositionalArgument($pa1.positionalArgument); }
        (
          COMMA 
          pa2=positionalArgument
          { $positionalArgumentList.addPositionalArgument($pa2.positionalArgument); }
        )* 
        (
          ELLIPSIS
          { $positionalArgumentList.setEllipsis( new Ellipsis($ELLIPSIS) ); }
        )?
      )? 
      RPAREN
      { $positionalArgumentList.setEndToken($RPAREN); }
    ;

positionalArgument returns [PositionalArgument positionalArgument]
    : //(declarationStart) => specialArgument | 
      expression
      { $positionalArgument = new PositionalArgument(null);
        $positionalArgument.setExpression($expression.expression); }
    ;
    
inlineFunctionalArgument
    : memberName ((parametersStart) => parameters)? 
      (LPAREN expression RPAREN | block)
    ;
    
assignmentExpression returns [Term term]
    : ee1=disjunctionExpression
      { $term = $ee1.term; }
      (
        assignmentOperator 
        { $assignmentOperator.operator.setLeftTerm($term);
          $term = $assignmentOperator.operator; }
        ee2=assignmentExpression
        { $assignmentOperator.operator.setRightTerm($ee2.term); }
{'fraction_non_alphanumeric': 0.10431406856580458, 'fraction_numerical': 0.0015309749029471266, 'mean_word_length': 2.838720510452523, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 111, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5838904', 'n_tokens_mistral': 25975, 'n_tokens_neox': 25086, 'n_words': 3923}