Fixes #939

Fixes #939

//
// Copyright (c) XSharp B.V.  All Rights Reserved.
// Licensed under the Apache License, Version 2.0.
// See License.txt in the project root for license information.
//

// Note that the comment blocks from the various rules have been copied from XSharp.g4 inside the compiler

USING System.Collections.Generic
USING System.Collections
USING System.Text
USING System.Text.RegularExpressions
USING System.IO
USING System.Diagnostics
USING System.Linq
USING LanguageService.CodeAnalysis.XSharp
USING LanguageService.SyntaxTree
USING LanguageService.CodeAnalysis.XSharp.SyntaxParser
USING XSharp.Parser
USING LanguageService.CodeAnalysis.Text

BEGIN NAMESPACE XSharpModel

   CLASS XsParser IMPLEMENTS VsParser.IErrorListener
      PRIVATE  _stream       AS BufferedTokenStream
      PRIVATE  _list         AS XSharpTokenList
      PRIVATE  _file         AS XFile
      PRIVATE  _usings       AS IList<STRING>
      PRIVATE  _staticusings AS IList<STRING>
      PRIVATE  _EntityList    AS IList<XSourceEntity>
      PRIVATE  _EntityStack   AS Stack<XSourceEntity>
      PRIVATE  _BlockList     AS IList<XSourceBlock>
      PRIVATE  _BlockStack    AS Stack<XSourceBlock>
      PRIVATE  _PPBlockStack  AS Stack<XSourceBlock>
      PRIVATE  _locals        AS IList<XSourceVariableSymbol>
      PRIVATE  _collectLocals AS LOGIC
      PRIVATE  _collectBlocks AS LOGIC
      PRIVATE  _errors        AS IList<XError>
      PRIVATE  _globalType    AS XSourceTypeSymbol
      PRIVATE  _dialect       AS XSharpDialect
      PRIVATE  _xppVisibility AS Modifiers
      PRIVATE  _commentTasks  AS IList<XCommentTask>

      PRIVATE  _attributes   AS Modifiers      // for the current entity
      PRIVATE  _start        AS IToken
      PRIVATE  _hasXmlDoc    AS LOGIC
      PRIVATE  _tokens       AS IList<IToken>
      PRIVATE  _firstTokenOnLine as IToken
      PRIVATE  _missingType  AS STRING

      PRIVATE PROPERTY CurrentEntity      AS XSourceEntity GET IIF(_EntityStack:Count > 0, _EntityStack:Peek(), NULL_OBJECT)
      PRIVATE PROPERTY CurrentType        AS XSourceTypeSymbol
         GET
            VAR aStack := _EntityStack:ToArray()
            FOREACH VAR item IN aStack
               IF item IS XSourceTypeSymbol VAR type
                  RETURN type
               ENDIF
            NEXT
            RETURN NULL
         END GET
      END PROPERTY
      PRIVATE PROPERTY CurrentBlock       AS XSourceBlock   GET IIF(_BlockStack:Count > 0, _BlockStack:Peek(), NULL_OBJECT)
      PRIVATE PROPERTY CurrentEntityKind  AS Kind     GET IIF(_EntityStack:Count > 0, CurrentEntity:Kind, Kind.Unknown)
      PRIVATE PROPERTY InFoxClass AS LOGIC GET CurrentType != NULL .AND. CurrentType:ClassType == XSharpDialect.FoxPro
      PRIVATE PROPERTY InXppClass AS LOGIC GET CurrentType != NULL .AND. CurrentType:ClassType == XSharpDialect.XPP
      PROPERTY EntityList AS IList<XSourceEntity>  GET _EntityList
      PROPERTY BlockList  AS IList<XSourceBlock>    GET _BlockList
      PROPERTY Locals     AS IList<XSourceVariableSymbol> GET _locals
      PROPERTY SaveToDisk AS LOGIC AUTO

      CONSTRUCTOR(oFile AS XFile, dialect AS XSharpDialect)
          SELF:SaveToDisk := TRUE
         _errors        := List<XError>{}
         _usings        := List<STRING>{}
         _commentTasks  := List<XCommentTask>{}
         _staticusings  := List<STRING>{}
         _EntityList    := List<XSourceEntity>{}
         _EntityStack   := Stack<XSourceEntity>{}
         _BlockList     := List<XSourceBlock>{}
         _BlockStack    := Stack<XSourceBlock>{}
         _PPBlockStack  := Stack<XSourceBlock>{}
         _file          := oFile
         _dialect       := dialect
         _locals        := List<XSourceVariableSymbol>{}
         _file:Clear()
         _globalType    := _file:GlobalType
         _globalType:ClearMembers()
         _EntityStack:Push(_globalType)
         _missingType := XLiterals.ObjectType
          IF SELF:_file:Project != NULL .and. SELF:_file:Project:ParseOptions:Dialect != XSharpDialect.Core
             _missingType := XLiterals.UsualType
          ENDIF

      #region IErrorListener
      METHOD ReportError(fileName AS STRING, span AS LinePositionSpan, errorCode AS STRING, message AS STRING, args AS OBJECT[]) AS VOID
         SELF:_errors:Add(XError{fileName, span, errorCode, message, args})

      METHOD ReportWarning(fileName AS STRING, span AS LinePositionSpan, errorCode AS STRING, message AS STRING, args AS OBJECT[]) AS VOID
         SELF:_errors:Add(XWarning{fileName, span, errorCode, message, args})
      #endregion

      METHOD Parse(lBlocks AS LOGIC, lLocals AS LOGIC) AS VOID
         VAR cSource  := System.IO.File.ReadAllText(_file:SourcePath)
         VAR options  := XSharpParseOptions.Default
         XSharp.Parser.VsParser.Lex(cSource, SELF:_file:SourcePath, options, SELF, OUT VAR stream, OUT VAR includeFiles)
         SELF:Parse(stream, lBlocks, lLocals)
         RETURN

      METHOD AddCommentLine(comment AS STRING, token AS XSharpToken, cmtToken AS XCommentToken) AS VOID
        comment := comment:Trim()
        IF comment:IndexOf(cmtToken:Text, StringComparison.OrdinalIgnoreCase) == 0 .AND. ;
            comment:Length > cmtToken:Text:Length
            VAR nextchar := comment[cmtToken:Text:Length]
            IF ! Char.IsLetterOrDigit(nextchar)
                VAR item := XCommentTask{}{ File:=_file, Line := token:Line, Column := token:Column, Priority := cmtToken:Priority, Comment := comment}
                _commentTasks:Add(item)
            ENDIF
        ENDIF

      METHOD Parse( tokenStream AS ITokenStream, lBlocks AS LOGIC, lLocals AS LOGIC) AS VOID
         LOCAL aAttribs        AS IList<XSharpToken>
         LOCAL cXmlDoc   := "" AS STRING
         VAR cmtTokens  := XSolution.CommentTokens
         Log(i"Start")

         _collectLocals := lLocals
         _collectBlocks := lBlocks
         _stream        := (BufferedTokenStream) tokenStream
         _tokens        := _stream:GetTokens()
         VAR _input     := List<XSharpToken>{}
         FOREACH token AS XSharpToken IN _tokens
            SWITCH token:Channel
            CASE TokenConstants.HiddenChannel
            CASE XSharpLexer.DEFOUTCHANNEL // Inactive code
               IF XSharpLexer.IsComment(token:Type)
                  FOREACH VAR cmtToken IN cmtTokens
                     VAR pos := token:Text:IndexOf(cmtToken:Text, StringComparison.OrdinalIgnoreCase)
                     VAR include := FALSE
                     IF pos >= 0
                        IF token:Type == XSharpLexer.SL_COMMENT
                            VAR comment := token:Text:Substring(2):Trim()
                            AddCommentLine(comment, token, cmtToken)
                         ELSEIF token:Type == XSharpLexer.ML_COMMENT
                            VAR comment := token:Text:Substring(2, token:Text:Length-4)
                            VAR lines :=comment:Split(<CHAR>{'\r','\n'}, StringSplitOptions.RemoveEmptyEntries)
                            FOREACH VAR line IN lines
                                AddCommentLine(line, token, cmtToken)
                            NEXT
                        ENDIF
                     ENDIF
                  NEXT
               ENDIF
             CASE XSharpLexer.DefaultTokenChannel
             CASE XSharpLexer.PREPROCESSORCHANNEL
               _input:Add(token)
             CASE XSharpLexer.XMLDOCCHANNEL
               _hasXmlDoc := TRUE
             OTHERWISE
               NOP
             END SWITCH
         NEXT
         _file:CommentTasks := _commentTasks
         _list :=XSharpTokenList{_input}

         DO WHILE ! SELF:Eoi()
            VAR tokenBefore := LastToken
            _firstTokenOnLine := SELF:Lt1
            LOCAL first := (XSharpToken) SELF:Lt1  AS XSharpToken
            IF ParsePPLine()
               LOOP
            ENDIF
            IF SELF:ParseUsing()
               LOOP
            ENDIF
            LOCAL startOfTrivia := -1 as LONG
            SELF:ParseUdcTokens()
            aAttribs := SELF:ParseAttributes()
            VAR mods := SELF:ParseVisibilityAndModifiers()
            VAR vis  := _AND(mods, Modifiers.VisibilityMask)
            IF IsStartOfEntity(OUT VAR entityKind, mods)
               IF _hasXmlDoc
                  LOCAL cDoc := first:XmlComments AS STRING
                  if first:HasTrivia
                     foreach Var triv in first:Trivia
                        if triv:Type == XSharpLexer.DOC_COMMENT
                            startOfTrivia := triv:Line -1
                            EXIT
                        ENDIF
                     NEXT
                  ENDIF

                  cDoc := cDoc:Replace("///","")
                  IF ! String.IsNullOrEmpty(cDoc)
                     cXmlDoc := "<doc>"+cDoc+"</doc>"
                  ELSE
                     cXmlDoc := NULL
                  ENDIF
               ENDIF
               // note: do not set this before the IsStartOfEntity check to make sure that
               // single identifiers on a line are not matched with the ClassVar rule
               IF vis == Modifiers.None
                  mods |= Modifiers.Public
               ENDIF
               SELF:_attributes  := mods
               SELF:_start := first
               VAR entities := SELF:ParseEntity(entityKind)
               IF entities != NULL

                  FOREACH VAR entity IN entities
                     IF entity == NULL
                        LOOP
                     ENDIF
                     entity:File := _file
                     IF _hasXmlDoc
                        entity:XmlComments   := cXmlDoc
                        entity:StartOfXmlComments := startOfTrivia
                         if entity:Kind == Kind.Delegate .and. entity is XSourceTypeSymbol var xtype
                            var invoke := xtype:XMembers:First()
                            invoke:XmlComments := cXmlDoc
                            invoke:StartOfXmlComments :=startOfTrivia
                         endif
                     ENDIF
                     IF aAttribs?:Count > 0
                        entity:CustomAttributes := TokensAsString(aAttribs)
                     ENDIF
                     VAR lastEntity := _EntityList:LastOrDefault()
                     IF lastEntity != NULL
                        lastEntity:Range       := lastEntity:Range:WithEnd(tokenBefore)
                        lastEntity:Interval    := lastEntity:Interval:WithEnd(tokenBefore)
                     ENDIF
                     _EntityList:Add(entity)
                     VAR isMember := entity IS XSourceMemberSymbol
                     VAR isType   := entity IS XSourceTypeSymbol
                     var canAddMembers  := CurrentEntityKind:HasMembers()
                     var canAddChildren := CurrentEntityKind:HasChildren()
                     LOCAL mustPop as LOGIC
                     IF _EntityStack:Count == 0
                        mustPop := FALSE
                     ELSEIF isType
                         IF canAddChildren
                            mustPop := FALSE
                         ELSE
                            mustPop := TRUE
                         ENDIF
                     ELSEIF isMember
                        IF canAddMembers
                            mustPop := FALSE
                        ELSE
                            mustPop := TRUE
                        ENDIF
                     ELSEIF CurrentEntityKind:HasBody() .and. entity:Kind:IsLocal()
                        mustPop := FALSE
                     ELSE
                        mustPop := TRUE
                     ENDIF
                     IF mustPop
                        _EntityStack:Pop()
                     ENDIF
                     IF entity:Kind:IsGlobalTypeMember() .AND. entity IS XSourceMemberSymbol VAR xGlobalMember
                        // GLOBAL, DEFINE, FUNCTION, PROCEDURE
                        // also #define, #command etc
                        SELF:_globalType:AddMember(xGlobalMember)
                     ELSEIF entity:Kind:IsLocal()
                        entity:Parent := CurrentEntity
                     ELSEIF canAddMembers .AND. CurrentEntity IS XSourceTypeSymbol VAR xEnt
                        // CurrentEntity should be a type: Class, Structure, Interface, Enum, VoStruct, Union
                        IF entity IS XSourceMemberSymbol VAR xMember .AND. xMember:Parent == NULL
                            xEnt:AddMember( xMember )
                         ELSEIF canAddChildren .and. xEnt != NULL .and. entity IS XSourceTypeSymbol VAR xChild .AND.  ;
                            ! XSourceTypeSymbol.IsGlobalType(xEnt) .and. xEnt:Kind:HasChildren()
                            // Namespace, class, structure, interface can have children (nested types)
                            xEnt:AddChild( xChild )
                            xChild:Namespace := xEnt:FullName
                         ENDIF
                     ENDIF
                     IF ! entity:SingleLine
                        _EntityStack:Push(entity)
                     ENDIF
                     _BlockStack:Clear()
                  NEXT
               ELSE
                  NOP
               ENDIF
            ELSEIF IsEndOfEntity( OUT VAR endKind)
               VAR type := SELF:La2
               // match la2 with current entity
               DO WHILE _EntityStack:Count > 0
                  VAR top := _EntityStack:Pop()
                  // END PROCEDURE and END FUNCTION may indicate normal procedure and local procedure
                  IF top:Kind == Kind.LocalFunc .and. endKind == Kind.Function
                     NOP // Ok
                  ELSEIF top:Kind == Kind.LocalProc .and. endKind == Kind.Procedure
                     NOP // Ok
                  ELSEIF top:Kind != endKind
                     top:Range       := top:Range:WithEnd(tokenBefore)
                     top:Interval    := top:Interval:WithEnd(tokenBefore)
                     LOOP
                  ENDIF
                  top:Range       := top:Range:WithEnd(SELF:Lt2)
                  top:Interval    := top:Interval:WithEnd(SELF:Lt2)
                  EXIT
               ENDDO
               SELF:ReadLine()
            ELSEIF aAttribs:Count > 0 .AND. La1 == XSharpLexer.EOS
                // Add Attribute
                VAR attribute := ParseAttribute(aAttribs)
                _EntityList:Add(attribute)
            ELSEIF ParseBlock()
               NOP
            ELSE
               SELF:ParseStatement()
            ENDIF
         ENDDO
         VAR types := SELF:_EntityList:Where( {x => x IS XSourceTypeSymbol})
         VAR typelist := XDictionary<STRING, XSourceTypeSymbol>{System.StringComparer.InvariantCultureIgnoreCase}
         typelist:Add(_globalType:Name, _globalType)
         LOCAL last  := NULL AS XSourceTypeSymbol
         FOREACH type AS XSourceTypeSymbol IN types
            IF last != NULL .AND. last:Range:StartLine == last:Range:EndLine
               // adjust the end of the type with the start of the current line
               VAR newEndLine := type:Range:StartLine-1
               VAR newEndPos  := type:Interval:Start -1

               last:Range     := TextRange{last:Range:StartLine, last:Range:StartColumn, newEndLine, 0}
               last:Interval  := TextInterval{last:Interval:Start, newEndPos}
            ENDIF
            IF type:Parent != NULL .AND. (type:Parent:Kind:IsType() .OR. type:Parent:Kind == Kind.Namespace)
               IF type:Parent == _globalType
                  type:Namespace := ""
               ELSE
                  type:Namespace := type:Parent:FullName
               ENDIF
            ENDIF
            IF type:Kind == Kind.Namespace
               // Add to usings when needed. Not strictly necessary but makes lookup easier later
               SELF:AddNameSpaceToUsing(type:Name)
            ELSEIF type:Name:Contains(".")
               VAR pos := type:Name:LastIndexOf(".")
               VAR ns  := type:Name:Substring(0, pos)
               SELF:AddNameSpaceToUsing(ns)
               type:Name   := type:Name:Substring(pos+1)
               IF String.IsNullOrEmpty(type:Namespace)
                  type:Namespace := ns
               ELSE
                  type:Namespace += "."+ns
               ENDIF
            ENDIF

            IF ! typelist:ContainsKey(type:FullName)
               type:File := _file

              typelist:Add(type:FullName, type)
            ENDIF
            last := type
         NEXT
         VAR lasttoken := _tokens[_tokens.Count -1]
         IF last != NULL .AND. last:Range:StartLine == last:Range:EndLine .and. ! last:SingleLine
            // adjust the end of the type with the start of the current line
            // find the last token in the stream

            last:Range     := last:Range:WithEnd(lasttoken)
            last:Interval  := last:Interval:WithEnd(lasttoken)
         ENDIF
         Log(i"Completed, found {_EntityList.Count} entities and {typelist.Count} types")
         IF SELF:_EntityList:Count > 0
            LOCAL lastEntity          := SELF:_EntityList:Last() as XSourceEntity
            if lastEntity:Kind:IsClassMember(_dialect)
                // if type has no end clause then also set the end
                if lastEntity:Range:StartLine == lastEntity:Range:EndLine
                    lastEntity:Range        := lastEntity:Range:WithEnd(lasttoken)
                    lastEntity:Interval     := lastEntity:Interval:WithEnd(lasttoken)
                endif

            elseif ! lastEntity:Kind:HasEndKeyword()
                lastEntity:Range        := lastEntity:Range:WithEnd(lasttoken)
                lastEntity:Interval     := lastEntity:Interval:WithEnd(lasttoken)

            ENDIF
         ELSE
             // Add at least one entity that represents the global namespace
             SELF:GetSourceInfo(_tokens[0], _tokens[_tokens:Count-1], OUT VAR range, OUT VAR interval, OUT VAR source)

             VAR xmember := XSourceMemberSymbol{_globalType:Name,Kind.Namespace, ;
                Modifiers.Export, range, interval, "", FALSE}
             xmember.File := _file
             xmember.SourceCode := source
             SELF:_EntityList:Add(xmember)
             SELF:_globalType:AddMember(xmember)
         ENDIF
         IF ! lLocals
	         _file:SetTypes(typelist, _usings, _staticusings, SELF:_EntityList)
             IF SELF:SaveToDisk
                _file:SaveToDatabase()
             ENDIF
         ENDIF
      PRIVATE METHOD AddNameSpaceToUsing(name as STRING) AS VOID
         var pos  := name:LastIndexOf(".")
         SELF:AddUniqueUsing(name)
         DO WHILE pos > 0
            name := name:Substring(0, pos)
            SELF:AddUniqueUsing(name)
            pos  := name:LastIndexOf(".")
         ENDDO
         RETURN

      PRIVATE METHOD AddUniqueUsing(strName as STRING) AS VOID
         FOREACH var u in SELF:_usings
            if String.Compare(u, strName, TRUE) == 0
               RETURN
            ENDIF
         NEXT
         _usings:Add(strName)
         RETURN

      PRIVATE METHOD ParsePPLine() AS LOGIC
         LOCAL entity as XSourceMemberSymbol
         LOCAL kind AS Kind
         VAR token := SELF:La1
         VAR start := SELF:Lt1

         SWITCH token
         CASE XSharpLexer.PP_REGION
         CASE XSharpLexer.PP_IFDEF
         CASE XSharpLexer.PP_IFNDEF
            VAR block := XSourceBlock{ SELF:Lt1, SELF:Lt2}
            _BlockList:Add(block)
            _PPBlockStack:Push(block)
         CASE XSharpLexer.PP_ENDREGION
         CASE XSharpLexer.PP_ENDIF
               // end
            IF _PPBlockStack:Count > 0
               _PPBlockStack:Peek():Children:Add( XSourceBlock{SELF:Lt1,SELF:Lt2})
               _PPBlockStack:Pop()
            ENDIF
         CASE XSharpLexer.PP_ELSE
               // middle
            IF _PPBlockStack:Count > 0
               _PPBlockStack:Peek():Children:Add( XSourceBlock{SELF:Lt1,SELF:Lt2})
            ENDIF
         CASE XSharpLexer.PP_INCLUDE
             var sb := StringBuilder{}
             kind   := Kind.Include
             SELF:Consume()
             VAR eol   := SELF:Lt1
             DO WHILE SELF:La1 != XSharpLexer.EOS
                eol   := SELF:Lt1
                sb:Append(eol:Text)
                SELF:Consume()
             ENDDO
             SELF:GetSourceInfo(start, eol, OUT VAR range, OUT VAR interval, OUT VAR source)
             VAR name := sb:ToString():Trim()
             if name:StartsWith("""") .and. name.EndsWith("""")
                name := name:Substring(1, name:Length-2)
             endif
             entity := XSourceMemberSymbol{name, kind, Modifiers.None, range,interval,"",FALSE}
             entity:SourceCode := source
         CASE XSharpLexer.PP_DEFINE
         CASE XSharpLexer.PP_UNDEF
         CASE XSharpLexer.PP_COMMAND
         CASE XSharpLexer.PP_TRANSLATE

             VAR name  := SELF:Lt2:Text
             VAR eol   := SELF:Lt2
             var hasId := IsId(SELF:La2)
             if SELF:La2 == XSharpLexer.BACKSLASH
                eol   := SELF:Lt3
                if ! hasId .and. IsId(eol:Type)
                    name  := eol:Text
                    hasId := TRUE
                endif
             ENDIF
             DO WHILE SELF:La1 != XSharpLexer.EOS
                eol := SELF:Lt1
                if ! hasId .and. IsId(eol:Type)
                    name  := eol:Text
                    hasId := TRUE
                endif
                SELF:Consume()
             ENDDO
             SELF:GetSourceInfo(start, eol, OUT VAR range, OUT VAR interval, OUT VAR source)
             SWITCH token
             CASE XSharpLexer.PP_DEFINE
                 kind := Kind.Define
             CASE XSharpLexer.PP_UNDEF
                 kind := Kind.Undefine
             CASE XSharpLexer.PP_COMMAND
                 kind := Kind.Command
                 VAR cType := start:Text[1]
                 IF cType == c'x' .OR. cType == c'X'
                    kind := Kind.XCommand
                 ENDIF
             CASE XSharpLexer.PP_TRANSLATE
                 kind := Kind.Translate
                 VAR cType := start:Text[1]
                 IF cType == c'x' .OR. cType == c'X'
                    kind := Kind.XTranslate
                 ENDIF
             END SWITCH
             entity := XSourceMemberSymbol{name, kind, Modifiers.None, range,interval,"",FALSE}
             entity:SourceCode := source
         OTHERWISE
            RETURN FALSE
         END SWITCH
         SELF:ReadLine()
         if entity != NULL
             entity:ReturnType := ""
             entity:File := _file
             entity:SingleLine := TRUE
             _EntityList.Add(entity)
             _globalType:AddMember(entity)
         ENDIF
         RETURN TRUE

        PRIVATE METHOD ParseUsing() AS LOGIC
/*
using_              : USING (Static=STATIC)? (Alias=identifierName Op=assignoperator)? Name=name EOS
                    ;

*/

         IF SELF:La1 != XSharpLexer.USING
            RETURN FALSE
         ENDIF
         IF SELF:La2 == XSharpLexer.VAR             // USING VAR
            RETURN FALSE
         ENDIF
         VAR startToken := SELF:ConsumeAndGet()
         VAR isStatic := FALSE
         VAR alias := ""
         IF Expect(XSharpLexer.STATIC)
            isStatic := TRUE
         ENDIF
         IF IsId(SELF:La1) .AND. SELF:IsAssignOp(SELF:La2)
            // name :=
            alias := SELF:ConsumeAndGetText()
            SELF:Consume()   // :=
         ENDIF
         VAR name := SELF:ParseQualifiedName()
         IF isStatic
            SELF:_staticusings:Add(name)
         ELSE
            SELF:_usings:Add(name)
         ENDIF
         SELF:ReadLine()
         RETURN TRUE
      PRIVATE METHOD ParseUdcTokens() AS VOID
        DO WHILE SELF:La1 == XSharpLexer.UDC_KEYWORD
            SELF:Consume()
        ENDDO
        RETURN

      PRIVATE METHOD ParseAttributes() AS IList<XSharpToken>
/*
attributes          : ( AttrBlk+=attributeBlock )+
                    ;

attributeBlock      : LBRKT Target=attributeTarget? Attributes+=attribute (COMMA Attributes+=attribute)* RBRKT
                    | String=BRACKETED_STRING_CONST
                    ;

attributeTarget     : Token=(ID | CLASS | CONSTRUCTOR | DELEGATE | ENUM | EVENT | FIELD | INTERFACE | METHOD | PROPERTY  | RETURN | STRUCTURE ) COLON
                    ;

attribute           : Name=name (LPAREN (Params+=attributeParam (COMMA Params+=attributeParam)* )? RPAREN )?
                    ;

attributeParam      : Name=identifierName Op=assignoperator Expr=expression     #propertyAttributeParam
                    | Expr=expression                                   #exprAttributeParam
                    ;

*/
         // Please note that in the editor we do not check the contents of the attributes.
         // we simply parse LBRKT ... RBRKT groups until we find no more LBRKT

         VAR tokens := List<XSharpToken>{}
         DO WHILE SELF:La1 == XSharpLexer.LBRKT .AND. ! SELF:Eos()
            tokens:Add(SELF:ConsumeAndGet())
            DO WHILE SELF:La1 != XSharpLexer.RBRKT .AND. ! SELF:Eos()
               tokens:Add(SELF:ConsumeAndGet())
            ENDDO
            IF SELF:La1 == XSharpLexer.RBRKT
               tokens:Add(SELF:ConsumeAndGet())
            ENDIF
         ENDDO
         RETURN tokens

      PRIVATE METHOD IsModifier(num AS LONG) AS LOGIC
            SWITCH num
               // Visibility Alphabetical
            CASE XSharpLexer.EXPORT
            CASE XSharpLexer.HIDDEN
            CASE XSharpLexer.INTERNAL
            CASE XSharpLexer.PRIVATE
            CASE XSharpLexer.PROTECTED
            CASE XSharpLexer.PUBLIC

            // Real modifiers Alphabetical
            CASE XSharpLexer.ABSTRACT
            CASE XSharpLexer.ASYNC
            CASE XSharpLexer.CONST
            CASE XSharpLexer.EXTERN
            CASE XSharpLexer.INITONLY
            CASE XSharpLexer.INSTANCE
            CASE XSharpLexer.NEW
            CASE XSharpLexer.OVERRIDE
            CASE XSharpLexer.PARTIAL
            CASE XSharpLexer.SEALED
            CASE XSharpLexer.STATIC
            CASE XSharpLexer.UNSAFE
            CASE XSharpLexer.VIRTUAL
            CASE XSharpLexer.VOLATILE

            // XPP modifiers
            CASE XSharpLexer.DEFERRED
            CASE XSharpLexer.FINAL
            CASE XSharpLexer.FREEZE
            CASE XSharpLexer.INTRODUCE
            CASE XSharpLexer.SYNC
               RETURN TRUE
            END SWITCH
         RETURN FALSE
      PRIVATE METHOD ParseVisibilityAndModifiers() AS   Modifiers
         VAR result := Modifiers.None
         DO WHILE ! SELF:Eos()
            VAR done := FALSE
            SWITCH SELF:La1
               // Visibility Alphabetical
            CASE XSharpLexer.EXPORT
               result |= Modifiers.Public
            CASE XSharpLexer.HIDDEN
               result |= Modifiers.Private
            CASE XSharpLexer.INTERNAL
               result |= Modifiers.Internal
            CASE XSharpLexer.PRIVATE
               result |= Modifiers.Private
            CASE XSharpLexer.PROTECTED
               result |= Modifiers.Protected
            CASE XSharpLexer.PUBLIC
               result |= Modifiers.Public

            // Real modifiers Alphabetical
            CASE XSharpLexer.ABSTRACT
               result |= Modifiers.Abstract
            CASE XSharpLexer.ASYNC
               result |= Modifiers.Async
            CASE XSharpLexer.CONST
               result |= Modifiers.Const
               result |= Modifiers.Static
            CASE XSharpLexer.EXTERN
               result |= Modifiers.External
            CASE XSharpLexer.INITONLY
               result |= Modifiers.InitOnly
            CASE XSharpLexer.INSTANCE
               result |= Modifiers.Instance
               result |= Modifiers.Protected
            CASE XSharpLexer.NEW
               result |= Modifiers.New
            CASE XSharpLexer.OVERRIDE
               result |= Modifiers.Override
            CASE XSharpLexer.PARTIAL
               result |= Modifiers.Partial
            CASE XSharpLexer.SEALED
               result |= Modifiers.Sealed
            CASE XSharpLexer.STATIC
               result |= Modifiers.Static
            CASE XSharpLexer.UNSAFE
               result |= Modifiers.Unsafe
            CASE XSharpLexer.VIRTUAL
               result |= Modifiers.Virtual
            CASE XSharpLexer.VOLATILE
               result |= Modifiers.Volatile

            // XPP modifiers
            CASE XSharpLexer.DEFERRED
               result |= Modifiers.Deferred
            CASE XSharpLexer.FINAL
               result |= Modifiers.Final
            CASE XSharpLexer.FREEZE
               result |= Modifiers.Freeze
            CASE XSharpLexer.INTRODUCE
               result |= Modifiers.Introduce
            CASE XSharpLexer.SYNC
               result |= Modifiers.Sync
            OTHERWISE
               done := TRUE
            END SWITCH
            IF ! done
               SELF:Consume()
            ELSE
               EXIT
            ENDIF
         ENDDO
         RETURN result

      PRIVATE METHOD IsStartOfEntity(entityKind OUT Kind, mods AS Modifiers) AS LOGIC
         entityKind := Kind.Unknown
         SWITCH SELF:La1
         CASE XSharpLexer.BEGIN
               // namespace ?
            IF SELF:La2 == XSharpLexer.NAMESPACE
                IF SELF:IsId(SELF:La3)
                    entityKind := Kind.Namespace
                ENDIF
            ENDIF
         CASE XSharpLexer.CLASS
            IF SELF:InXppClass
               IF SELF:La2 == XSharpLexer.METHOD  .OR. SELF:IsModifier(SELF:La2)   // XPP has CLASS METHOD = a static method
                  entityKind := Kind.Method
               ELSEIF SELF:La2 == XSharpLexer.VAR                                  // CLASS VAR for XPP
                  entityKind := Kind.Field
               ENDIF
            ELSEIF SELF:IsId(SELF:La2)
               entityKind := Kind.Class
            ENDIF
         CASE XSharpLexer.STRUCTURE
            IF SELF:IsId(SELF:La2)
                entityKind := Kind.Structure
            ENDIF
         CASE XSharpLexer.DELEGATE
            IF SELF:IsId(SELF:La2)
               entityKind := Kind.Delegate
            ENDIF
         CASE XSharpLexer.INTERFACE
            IF SELF:IsId(SELF:La2)
                entityKind := Kind.Interface
            ENDIF
         CASE XSharpLexer.ENUM
            IF SELF:IsId(SELF:La2)
               entityKind := Kind.Enum
            ENDIF
         CASE XSharpLexer.EVENT
            IF SELF:IsId(SELF:La2)
                entityKind := Kind.Event
            ENDIF
         CASE XSharpLexer.METHOD
            IF SELF:IsId(SELF:La2)
                entityKind := Kind.Method
            ENDIF
         CASE XSharpLexer.ACCESS
            IF SELF:IsId(SELF:La2)
                entityKind := Kind.Access
            ENDIF
         CASE XSharpLexer.ASSIGN
            IF SELF:IsId(SELF:La2)
                entityKind := Kind.Assign
            ENDIF
         CASE XSharpLexer.PROPERTY
            IF SELF:IsId(SELF:La2) .or. SELF:La2 == XSharpLexer.SELF
                entityKind := Kind.Property
            ENDIF
         CASE XSharpLexer.OPERATOR
            entityKind := Kind.Operator
         CASE XSharpLexer.CONSTRUCTOR
            entityKind := Kind.Constructor
         CASE XSharpLexer.DESTRUCTOR
            entityKind := Kind.Destructor
         CASE XSharpLexer.DECLARE
               // access, assign, method
            IF SELF:La2 == XSharpLexer.ASSIGN .OR. SELF:La2 == XSharpLexer.ACCESS .OR. SELF:La2 == XSharpLexer.METHOD
               entityKind := Kind.Ignore
            ENDIF
         CASE XSharpLexer.DEFINE
               // define class ?
            IF SELF:IsId(SELF:La2)
               entityKind := Kind.VODefine
            ELSEIF _dialect == XSharpDialect.FoxPro
               entityKind := Kind.Class
            ENDIF
         CASE XSharpLexer.VOSTRUCT
            IF SELF:IsId(SELF:La2)
                entityKind := Kind.VOStruct
            ENDIF
         CASE XSharpLexer.UNION
            IF SELF:IsId(SELF:La2)
                entityKind := Kind.Union
            ENDIF
         CASE XSharpLexer.MEMBER
            IF SELF:IsId(SELF:La2)
                entityKind := Kind.EnumMember
            ENDIF
         CASE XSharpLexer.ADD
            // Todo handle Add Object clause inside Class.
            IF SELF:La2 == XSharpLexer.OBJECT
               entityKind := Kind.Field
            ENDIF
         CASE XSharpLexer.FUNCTION
            IF SELF:IsId(SELF:La2)
                entityKind := Kind.Function
            ENDIF
         CASE XSharpLexer.PROCEDURE
            IF SELF:IsId(SELF:La2)
                entityKind := Kind.Procedure
            ENDIF
         CASE XSharpLexer.GLOBAL
            IF SELF:IsId(SELF:La2)
                entityKind := Kind.VOGlobal
            ENDIF
         CASE XSharpLexer.DLL
            IF SELF:La2 == XSharpLexer.FUNCTION .OR. SELF:La2 == XSharpLexer.PROCEDURE
               entityKind := Kind.VODLL
            ENDIF
         CASE XSharpLexer.FIELD
            // field declaration only inside FoxPro class definition
            IF InFoxClass .AND. (CurrentEntity:Kind == Kind.Class .OR. CurrentEntity:Kind == Kind.Field)
               entityKind := Kind.Field
            ENDIF
         CASE XSharpLexer.IMPLEMENTS
            IF InFoxClass
               entityKind := Kind.Field // Not really a field but handled later
            ENDIF
         CASE XSharpLexer.DIMENSION
            // make sure that DIMENSION inside a method is not matched
            IF InFoxClass
               entityKind := Kind.Field // Not really a field but handled later
            ENDIF
         // XPP code between CLASS .. ENDCLASS
         // fail when partially parseing for locals lookup
         CASE XSharpLexer.VAR WHEN  SELF:InXppClass
            entityKind := Kind.Field // Not really a field but handled later

         CASE XSharpLexer.COLON WHEN SELF:InXppClass
            IF _AND(mods, Modifiers.VisibilityMask) != Modifiers.None
               entityKind := Kind.Field // Not really a field but handled later
            ENDIF
         CASE XSharpLexer.INLINE WHEN SELF:InXppClass
            entityKind := Kind.Method // Not really a field but handled later
         CASE XSharpLexer.LOCAL
            IF SELF:La2 == XSharpLexer.FUNCTION
               entityKind := Kind.LocalFunc
            ELSEIF SELF:La2 == XSharpLexer.PROCEDURE
               entityKind := Kind.LocalProc
            ENDIF
         CASE XSharpLexer.GET
         CASE XSharpLexer.SET
         //CASE XSharpLexer.ADD
         CASE XSharpLexer.REMOVE
            entityKind := Kind.Unknown
         OTHERWISE
            IF IsId(SELF:La1)
               IF mods != Modifiers.None
                  LOCAL parent AS XSourceTypeSymbol
                  IF CurrentEntity IS XSourceTypeSymbol
                     parent := (XSourceTypeSymbol) CurrentEntity
                  ELSEIF CurrentEntity != NULL .AND. CurrentEntity:Parent IS XSourceTypeSymbol
                     parent := (XSourceTypeSymbol) CurrentEntity:Parent
                  ENDIF
                  IF ! XSourceTypeSymbol.IsGlobalType(parent)
                     entityKind := Kind.Field
                  ENDIF
                  // PRIVATE and PUBLIC as memvar declarator
                  // when inside a method or function
                  IF mods == Modifiers.Public .OR. mods == Modifiers.Private
                     var txt := LastToken.Text.ToUpper()
                     if (txt == "PUBLIC" .or. txt == "PRIVATE")
                         IF CurrentEntity IS XSourceMemberSymbol VAR xDef .AND. ! xDef:SingleLine .AND. CurrentEntity.Kind:HasBody()
                            if _file:Project:ParseOptions:SupportsMemvars
                                entityKind := Kind.Unknown
                            endif
                        ENDIF
                     ENDIF
                  ENDIF
               ELSEIF InFoxClass .AND. (CurrentEntity:Kind == Kind.Class .OR. CurrentEntity:Kind == Kind.Field)
                  IF SELF:La1 == XSharpLexer.ID .AND. IsAssignOp(SELF:La2)
                     entityKind := Kind.Field
                  ELSEIF SELF:La1 == XSharpLexer.ID .AND. SELF:Lt1:Text:EndsWith("COMATTRIB", StringComparison.OrdinalIgnoreCase)
                     entityKind := Kind.Field
                  ENDIF
               ELSEIF CurrentType?:Kind == Kind.Enum
                  entityKind := Kind.EnumMember
               ENDIF
            ENDIF

         END SWITCH

         RETURN entityKind != Kind.Unknown

      PRIVATE METHOD ParseBlock() AS LOGIC
         // Adds, updates or removes block token on the block tokens stack
         // Start of block is also added to the _BlockList
         LOCAL nStart  := 0 AS LONG
         LOCAL nMiddle := 0 AS LONG
         LOCAL nEnd    := 0 AS LONG
         SELF:ParseUdcTokens()  // Read UDC tokens on the current line
         SWITCH SELF:La1
         CASE XSharpLexer.BEGIN
            SWITCH SELF:La2
            // tokens in the same order as the rules inside xsharp.g4
            CASE XSharpLexer.SEQUENCE
            CASE XSharpLexer.LOCK
            CASE XSharpLexer.SCOPE
            CASE XSharpLexer.SWITCH
            CASE XSharpLexer.USING
            CASE XSharpLexer.UNSAFE
            CASE XSharpLexer.CHECKED
            CASE XSharpLexer.UNCHECKED
            CASE XSharpLexer.FIXED
               nStart := 2
            END SWITCH

         CASE XSharpLexer.DO
            SWITCH SELF:La2
               CASE XSharpLexer.WHILE
               CASE XSharpLexer.CASE
               CASE XSharpLexer.SWITCH
               nStart := 2
            END SWITCH

            CASE XSharpLexer.IF
            CASE XSharpLexer.FOR
            CASE XSharpLexer.FOREACH
            CASE XSharpLexer.REPEAT
            CASE XSharpLexer.TRY
            CASE XSharpLexer.WITH
            CASE XSharpLexer.TEXT
            CASE XSharpLexer.SWITCH
            CASE XSharpLexer.GET
            CASE XSharpLexer.SET
            CASE XSharpLexer.ADD
            CASE XSharpLexer.REMOVE
               nStart := 1
            // Middle of a block
         CASE XSharpLexer.CASE
         CASE XSharpLexer.OTHERWISE
         CASE XSharpLexer.ELSEIF
         CASE XSharpLexer.ELSE
         CASE XSharpLexer.CATCH
         CASE XSharpLexer.FINALLY
            nMiddle := 1
         CASE XSharpLexer.RECOVER
            IF SELF:La2 == XSharpLexer.USING
               nMiddle := 2
            ENDIF
            // End of a block
         CASE XSharpLexer.ENDCASE
         CASE XSharpLexer.ENDDO
         CASE XSharpLexer.ENDIF
         CASE XSharpLexer.NEXT      // Also covers ENDFOR
         CASE XSharpLexer.UNTIL
            nEnd := 1
         CASE XSharpLexer.END
               SWITCH SELF:La2
               // tokens in the same order as the rules inside xsharp.g4
               CASE XSharpLexer.SEQUENCE
               CASE XSharpLexer.LOCK
               CASE XSharpLexer.SCOPE
               CASE XSharpLexer.SWITCH
               CASE XSharpLexer.USING
               CASE XSharpLexer.UNSAFE
               CASE XSharpLexer.CHECKED
               CASE XSharpLexer.UNCHECKED
               CASE XSharpLexer.FIXED
               CASE XSharpLexer.TRY
               CASE XSharpLexer.WITH

               CASE XSharpLexer.GET        // Accessors
               CASE XSharpLexer.SET
               CASE XSharpLexer.ADD
               CASE XSharpLexer.REMOVE
                     nEnd := 2

               CASE XSharpLexer.EOS        // End without following keyword is usually also allowed
                  nEnd := 1
                END SWITCH
         END SWITCH
         IF nStart > 0
            IF SELF:_collectBlocks
               VAR block := XSourceBlock{ SELF:Lt1, IIF(nStart == 1, SELF:Lt1, SELF:Lt2)}
               _BlockList:Add(block)
               _BlockStack:Push(block)
            ENDIF
            IF SELF:_collectLocals
              IF SELF:La1 == XSharpLexer.SET .OR. SELF:La1 == XSharpLexer.REMOVE .OR. SELF:La1 == XSharpLexer.ADD
                  // Add value token inside accessors
                  VAR id := "Value"
                  VAR strType := SELF:CurrentEntity:TypeName
                  VAR start := SELF:Lt1
                  VAR stop  := SELF:Lt2
                  SELF:GetSourceInfo(start, stop, OUT VAR range, OUT VAR interval, OUT VAR _)
                  VAR xVar := XSourceVariableSymbol{SELF:CurrentEntity, id, range, interval, strType}

                  SELF:_locals:Add(xVar)
               ENDIF
               IF SELF:La1 == XSharpLexer.IF
                  SELF:ParseForLocals()
               ELSE
                  SELF:ParseForLocalDeclaration()
               ENDIF
            ELSE
               SELF:ReadLine()
            ENDIF
            RETURN TRUE
         ELSEIF nMiddle > 0
            // Do something
            IF SELF:_collectBlocks .AND. _BlockStack:Count > 0
               CurrentBlock:Children:Add( XSourceBlock{SELF:Lt1,IIF(nMiddle == 1, SELF:Lt1, SELF:Lt2)})
            ENDIF
            SWITCH SELF:La1
            CASE XSharpLexer.CATCH
               IF SELF:_collectLocals
                  IF SELF:IsId(SELF:La2)
                     SELF:Consume()
                     VAR start   := SELF:Lt1
                     VAR id      := SELF:ParseIdentifier()
                     VAR strType := "System.Exception"
                     IF SELF:La1 == XSharpLexer.AS
                         strType := SELF:ParseDataType(FALSE)
                     ENDIF

                     SELF:GetSourceInfo(start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR _)
                     VAR xVar := XSourceVariableSymbol{SELF:CurrentEntity, id, range, interval, strType}
                     SELF:_locals:Add(xVar)
                  ENDIF
               ENDIF
            CASE XSharpLexer.ELSEIF
            CASE XSharpLexer.CASE
                IF SELF:_collectLocals
                     SELF:ParseForLocals()
                ENDIF
            END SWITCH
            SELF:ReadLine()
            RETURN TRUE
         ELSEIF nEnd > 0
            IF SELF:_collectBlocks .AND. _BlockStack:Count > 0
               CurrentBlock:Children:Add( XSourceBlock{SELF:Lt1,IIF(nEnd == 1, SELF:Lt1, SELF:Lt2)})
               _BlockStack:Pop()
            ENDIF
            SELF:ReadLine()
            RETURN TRUE
         ENDIF
         RETURN FALSE

      PRIVATE METHOD IsEndOfEntity(EntityKind OUT Kind) AS LOGIC
         SWITCH SELF:La1
         CASE XSharpLexer.END
            SWITCH SELF:La2
               // mandatory END <keyword> pairs
            CASE XSharpLexer.NAMESPACE
                  EntityKind := Kind.Namespace
               RETURN TRUE
            CASE XSharpLexer.CLASS
            CASE XSharpLexer.DEFINE // FoxPro
                  EntityKind := Kind.Class
               RETURN TRUE
            CASE XSharpLexer.STRUCTURE
                  EntityKind := Kind.Structure
               RETURN TRUE
            CASE XSharpLexer.ENUM
                  EntityKind := Kind.Enum
                  RETURN TRUE

            CASE XSharpLexer.INTERFACE
                  EntityKind := Kind.Interface
                  RETURN TRUE

            CASE XSharpLexer.PROPERTY
                  EntityKind := Kind.Property
                  RETURN TRUE

            CASE XSharpLexer.EVENT
                  EntityKind := Kind.Event
                  RETURN TRUE

                  // Optional

            CASE XSharpLexer.METHOD
                  EntityKind := Kind.Method
                  RETURN TRUE

            CASE XSharpLexer.ACCESS
                  EntityKind := Kind.Access
                  RETURN TRUE

            CASE XSharpLexer.ASSIGN
                  EntityKind := Kind.Assign
                  RETURN TRUE

            CASE XSharpLexer.OPERATOR
                  EntityKind := Kind.Operator
                  RETURN TRUE

            CASE XSharpLexer.CONSTRUCTOR
                  EntityKind := Kind.Constructor
                  RETURN TRUE

            CASE XSharpLexer.DESTRUCTOR
                  EntityKind := Kind.Destructor
                  RETURN TRUE

            CASE XSharpLexer.FUNCTION
                  IF InFoxClass
                     EntityKind := Kind.Method
                  ELSE
                     EntityKind := Kind.Function
                  ENDIF
                  RETURN TRUE

            CASE XSharpLexer.PROCEDURE
                  IF InFoxClass
                     EntityKind := Kind.Method
                  ELSE
                     EntityKind := Kind.Procedure
                  ENDIF
                  RETURN TRUE

            CASE XSharpLexer.VOSTRUCT
                  EntityKind := Kind.VOStruct
                  RETURN TRUE

            CASE XSharpLexer.UNION
                  EntityKind := Kind.Union
                  RETURN TRUE

               // END <nothing> is NOT allowed for entities since that conflicts with the END for blocks
            END SWITCH
         CASE XSharpLexer.ENDDEFINE
            EntityKind := Kind.Class
            RETURN TRUE

         CASE XSharpLexer.ENDCLASS
            EntityKind := Kind.Class
            RETURN TRUE

         CASE XSharpLexer.UDC_KEYWORD
            IF String.Equals(SELF:Lt1.Text, "ENDFUNC", StringComparison.OrdinalIgnoreCase)
               IF InFoxClass
                  EntityKind := Kind.Method
               ELSE
                  EntityKind := Kind.Function
               ENDIF
               RETURN TRUE
            ELSEIF String.Equals(SELF:Lt1.Text, "ENDPROC",StringComparison.OrdinalIgnoreCase)
               IF InFoxClass
                  EntityKind := Kind.Method
               ELSE
                  EntityKind := Kind.Procedure
               ENDIF
               RETURN TRUE
            ENDIF
         END SWITCH
         EntityKind := Kind.Unknown
         RETURN FALSE

         #region Properties and Methods that are delegated to the XSharpTokenList type
      PRIVATE PROPERTY La1 AS INT => _list:La1
      PRIVATE PROPERTY La2 AS INT => _list:La2
      PRIVATE PROPERTY La3 AS INT => _list:La3
      PRIVATE PROPERTY Lt1 AS XSharpToken => _list:Lt1
      PRIVATE PROPERTY Lt2 AS XSharpToken => _list:Lt2
      PRIVATE PROPERTY Lt3 AS XSharpToken => _list:Lt3
      PRIVATE PROPERTY LastToken AS XSharpToken => _list:LastReadToken
      PRIVATE METHOD La(nToken AS LONG) AS LONG => _list:La(nToken)
      PRIVATE METHOD Lt(nToken AS LONG) AS XSharpToken => _list:Lt(nToken)
      PRIVATE METHOD Eoi() AS LOGIC => _list:Eoi()
      PRIVATE METHOD Eos() AS LOGIC => _list:Eos()
      PRIVATE METHOD Consume() AS VOID =>_list:Consume()
      PRIVATE METHOD ConsumeAndGet() AS XSharpToken => _list:ConsumeAndGet()
      PRIVATE METHOD ConsumeAndGetAny(nTypes PARAMS LONG[]) AS XSharpToken => _list:ConsumeAndGetAny(nTypes)
      PRIVATE METHOD ConsumeAndGetText() AS STRING => _list:ConsumeAndGetText()
      PRIVATE METHOD Expect(nType AS LONG) AS LOGIC => _list:Expect(nType)
      PRIVATE METHOD ExpectAny(nTypes PARAMS LONG[]) AS LOGIC => _list:ExpectAny(nTypes)
      PRIVATE METHOD ExpectAndGet(nType AS LONG, t OUT IToken) AS LOGIC => _list:ExpectAndGet(nType, OUT t)
      PRIVATE METHOD Matches(nTypes PARAMS LONG[]) AS LOGIC => _list:Matches(nTypes)
      PRIVATE METHOD PushBack() AS VOID => _list:PushBack()
      PRIVATE METHOD ReadLine() AS VOID => _list:ReadLine()
         #endregion

      PRIVATE METHOD ExpectAssignOp() AS LOGIC
         RETURN SELF:ExpectAny(XSharpLexer.ASSIGN_OP, XSharpLexer.EQ)

      PRIVATE METHOD IsId(token AS LONG) AS LOGIC
         IF token == XSharpLexer.ID .OR. token == XSharpLexer.KWID
            RETURN TRUE
         ENDIF
         // Soft keywords need to be
         RETURN SELF:IsKeywordXs(token) .OR. SELF:IsKeywordFox(token) .OR. SELF:IsKeywordXpp(token)

      PRIVATE METHOD ParseIdentifier() AS STRING
         IF SELF:IsId(SELF:La1)
            VAR id := SELF:ConsumeAndGetText()
            IF id:StartsWith("@@")
               id := id:Substring(2)
            ENDIF
            RETURN id
         ENDIF
         RETURN ""

      PRIVATE METHOD ParseAttribute(aAttribs AS IList<XSharpToken>) AS XSourceEntity
            VAR name := SELF:TokensAsString(aAttribs, FALSE)
            SELF:GetSourceInfo(aAttribs[0], aAttribs[aAttribs:Count-1], OUT VAR range, OUT VAR interval, OUT VAR source)
            VAR entity := XSourceMemberSymbol{name, Kind.Attribute, Modifiers.None,;
                            range,interval,"",FALSE}
            entity:SourceCode := source
            entity:File := _file
            entity:SingleLine := TRUE
            _globalType:AddMember(entity)
            RETURN entity

      PRIVATE METHOD ParseOptionalClassClause() AS STRING
         // parse the clause after a METHOD, ACCESS, ASSIGN, CONSTRUCTOR, DESTRUCTOR CLASS <Identifier>
         IF SELF:La1 == XSharpLexer.CLASS
            SELF:Consume()
            RETURN SELF:ParseIdentifier()
         ENDIF
         RETURN ""

      PRIVATE METHOD ParseQualifiedName() AS STRING
         LOCAL result := "" AS STRING
         VAR Tokens := List<XSharpToken>{}
         IF SELF:La1 == XSharpLexer.ID .OR. SELF:La1 == XSharpLexer.KWID
            Tokens:Add(SELF:ConsumeAndGet())
            DO WHILE SELF:La1 == XSharpLexer.DOT .AND.  SELF:IsId(SELF:La2) .AND. ! SELF:Eos()
               Tokens:Add(SELF:ConsumeAndGet())
               Tokens:Add(SELF:ConsumeAndGet())
            ENDDO
         ENDIF
         RETURN SELF:TokensAsString(Tokens,FALSE)

      PRIVATE METHOD TokensAsString(tokens AS IList<XSharpToken>, lAddTrivia := TRUE AS LOGIC) AS STRING
         LOCAL sb AS StringBuilder
         IF (tokens == NULL .or. tokens:Count == 0)
            RETURN ""
         ENDIF
         sb := StringBuilder{}

         FOREACH t AS XSharpToken IN tokens
            IF t:HasTrivia .AND. lAddTrivia
                sb:Append(t:TriviaAsText)
            ENDIF
            sb:Append(t:CleanText())
         NEXT
         RETURN sb:ToString():Trim()

      PRIVATE METHOD ParseEntity(entityKind AS Kind) AS IList<XSourceEntity>
         LOCAL result AS IList<XSourceEntity>
         SWITCH entityKind
         CASE Kind.Ignore
            SELF:ReadLine()
         CASE Kind.Namespace
            result := SELF:ParseNamespace()
         CASE Kind.Class
            IF SELF:La1 == XSharpLexer.DEFINE
               result := SELF:ParseFoxClass()
            ELSE
               IF _dialect == XSharpDialect.XPP .AND. SELF:HasXppEndClass()
                  result := SELF:ParseXppClass()
               ELSE
                  result := SELF:ParseTypeDef()
               ENDIF
            ENDIF
         CASE Kind.Structure
            result := SELF:ParseTypeDef()
         CASE Kind.Interface
            result := SELF:ParseTypeDef()
         CASE Kind.Delegate
            result := SELF:ParseDelegate()
         CASE Kind.Access
            IF InXppClass .AND. _dialect == XSharpDialect.XPP
               result := SELF:ParseXppProperty()
            ELSE
               result := SELF:ParseMethod()
            ENDIF
         CASE Kind.Assign
            IF InXppClass .AND. _dialect == XSharpDialect.XPP
               result := SELF:ParseXppProperty()
            ELSE
               result := SELF:ParseMethod()
            ENDIF
         CASE Kind.Method
            IF SELF:_dialect == XSharpDialect.XPP
               result := SELF:ParseXppMethod()
            ELSE
               result := SELF:ParseMethod()
            ENDIF
         CASE Kind.Function
         CASE Kind.Procedure
            IF InFoxClass .AND. _dialect == XSharpDialect.FoxPro
               result := SELF:ParseFoxMethod()
            ELSE
               result := SELF:ParseFuncProc()
            ENDIF
         CASE Kind.VODLL
            result := SELF:ParseVODLL()
         CASE Kind.VOStruct
            result := SELF:ParseVoStruct()
         CASE Kind.Union
            result := SELF:ParseVOUnion()
         CASE Kind.Enum
            result := SELF:ParseEnum()
         CASE Kind.EnumMember
            IF SELF:CurrentEntity:Kind == Kind.Enum
               result := SELF:ParseEnumMember()
            ELSE
               result := SELF:ParseVoStructMember()
            ENDIF
         CASE Kind.VODefine
            result := SELF:ParseVoDefine()
         CASE Kind.VOGlobal
            result := SELF:ParseVOGlobals()
         CASE Kind.Property
            result := SELF:ParseProperty()

         CASE Kind.Event
            result := SELF:ParseEvent()

         CASE Kind.Operator
            result := SELF:ParseOperator()

         CASE Kind.Constructor
            result := SELF:ParseConstructor()

         CASE Kind.Destructor
            result := SELF:ParseDestructor()

         CASE Kind.Field
            IF InXppClass .AND. _dialect == XSharpDialect.XPP
               IF SELF:La1 == XSharpLexer.COLON
                  result := SELF:ParseXppVisibility()
               ELSE
                  result := SELF:ParseXppClassVars()
               ENDIF
            ELSEIF InFoxClass .AND. _dialect == XSharpDialect.FoxPro
               result := SELF:ParseFoxFields()
            ELSE
               result := SELF:ParseClassVars()
            ENDIF
         CASE Kind.LocalFunc
         CASE Kind.LocalProc
               result := SELF:ParseLocalFuncProc()
         END SWITCH
         RETURN result

         PRIVATE METHOD HasXppEndClass() AS LOGIC
            LOCAL iCurrent := 1 AS LONG
            LOCAL iLa AS LONG
            iLa := SELF:La(iCurrent)
            DO WHILE iLa != -1
               IF iLa == XSharpLexer.ENDCLASS
                  RETURN TRUE
               ELSEIF iLa == XSharpLexer.INLINE // Does not occurs in normal class definition
                  RETURN TRUE
               ELSEIF SELF:IsModifier(iLa) .AND. SELF:La(iCurrent+1) == XSharpLexer.COLON
                  // visibility clause
                  RETURN TRUE
               ELSEIF iLa == XSharpLexer.END .AND. SELF:La(iCurrent+1) == XSharpLexer.CLASS
                  RETURN FALSE
               ENDIF
               iCurrent++
               iLa := SELF:La(iCurrent)
            ENDDO
            RETURN FALSE

         #region Main entities

         PRIVATE METHOD ParseFuncProc() AS IList<XSourceEntity>
/*
funcproc      : (Attributes=attributes)? (Modifiers=funcprocModifiers)?
              T=funcproctype Sig=signature
              InitExit=(INIT1|INIT2|INIT3|EXIT)?
              vodummyclauses
              end=eos
              StmtBlk=statementBlock
              (END T2=funcproctype   EOS )?
              ;
funcproctype        : Token=(FUNCTION | PROCEDURE)
                ;

*/
            LOCAL kind AS Kind
            IF ! ParseFuncProcType (OUT kind)
               RETURN NULL
            ENDIF
            SELF:Consume()
            VAR sig := SELF:ParseSignature()

            VAR initexit := ""
            IF SELF:Matches(XSharpLexer.INIT1,XSharpLexer.INIT2,XSharpLexer.INIT3,XSharpLexer.EXIT)
               initexit := SELF:ConsumeAndGetText()
            ENDIF
            SELF:GetSourceInfo(_start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR source)
            SELF:ReadLine()

            VAR xMember := XSourceMemberSymbol{sig, kind, _attributes, range, interval, _attributes:HasFlag(Modifiers.Static)}
            xMember:SourceCode := source
            xMember:File := SELF:_file
            RETURN <XSourceEntity>{xMember}

         PRIVATE METHOD ParseNamespace() AS IList<XSourceEntity>
/*
namespace_          : BEGIN NAMESPACE Name=name e=eos
                      (Entities+=entity)*
                      END NAMESPACE EOS
                    ;
*/
            IF SELF:La1 != XSharpLexer.BEGIN .AND. SELF:La2 != XSharpLexer.NAMESPACE
               RETURN NULL
            ENDIF
            SELF:Consume()   // BEGIN
            SELF:Consume()   // NAMESPACE
            VAR id := SELF:ParseQualifiedName()
            SELF:GetSourceInfo(_start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR source)
            SELF:ReadLine()
            VAR xType := XSourceTypeSymbol{id, Kind.Namespace, _attributes, range, interval,_file}
            xType:SourceCode := source
            RETURN <XSourceEntity>{xType}

         PRIVATE METHOD ParseTypeDef() AS IList<XSourceEntity>
/*
interface_          : (Attributes=attributes)? (Modifiers=classModifiers)?
                      I=INTERFACE (Namespace=nameDot)? Id=identifier
                      TypeParameters=typeparameters?                                      // TypeParameters indicate Generic Interface
                      ((INHERIT|COLON) Parents+=datatype)? (COMMA Parents+=datatype)*
                      (ConstraintsClauses+=typeparameterconstraintsclause)*              // Optional typeparameterconstraints for Generic Interface
                      e=eos
                      (Members+=classmember)*
                      END INTERFACE EOS
                    ;
class_              : (Attributes=attributes)? (Modifiers=classModifiers)?
                      C=CLASS (Namespace=nameDot)? Id=identifier
                      TypeParameters=typeparameters?                                    // TypeParameters indicate Generic Class
                      (INHERIT BaseType=datatype)?
                      (IMPLEMENTS Implements+=datatype (COMMA Implements+=datatype)*)?
                      (ConstraintsClauses+=typeparameterconstraintsclause)*             // Optional typeparameterconstraints for Generic Class
                      e=eos
                      (Members+=classmember)*
                      END CLASS EOS
                    ;
structure_          : (Attributes=attributes)? (Modifiers=classModifiers)?
                      S=STRUCTURE (Namespace=nameDot)? Id=identifier
                      TypeParameters=typeparameters?
                      (IMPLEMENTS Implements+=datatype (COMMA Implements+=datatype)*)?
                      (ConstraintsClauses+=typeparameterconstraintsclause)* e=eos
                      (Members+=classmember)*
                      END STRUCTURE EOS
                    ;

*/
            LOCAL kind AS Kind
            SWITCH SELF:La1
            CASE XSharpLexer.CLASS
               kind := Kind.Class
            CASE XSharpLexer.INTERFACE
               kind := Kind.Interface
            CASE XSharpLexer.STRUCTURE
               kind := Kind.Structure
            OTHERWISE
               RETURN NULL
            END SWITCH
            SELF:Consume()

            LOCAL constraints   AS List<STRING>
            LOCAL parentType    AS STRING
            LOCAL interfaces    AS List<STRING>
            // read Id with optional namespace prefix
            VAR id := SELF:ParseQualifiedName()
            VAR typePars := SELF:ParseTypeParameters()
            // get inherit clause
            IF ExpectAny(XSharpLexer.INHERIT, XSharpLexer.COLON)
               parentType := SELF:ParseTypeName()
            ENDIF
            // get implements clause + list of interfaces
            IF SELF:La1 == XSharpLexer.IMPLEMENTS
               interfaces := List<STRING>{}
               DO WHILE ExpectAny(XSharpLexer.IMPLEMENTS, XSharpLexer.COMMA)
                  interfaces:Add(ParseTypeName())
               ENDDO
            ENDIF
            DO WHILE SELF:La1 == XSharpLexer.WHERE .AND. ! SELF:Eos()
               IF constraints == NULL
                  constraints := List<STRING>{}
               ENDIF
               constraints:Add(SELF:ParseTypeParameterConstraints())
            ENDDO
            // read to EndOfLine
            SELF:GetSourceInfo(_start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR source)
            SELF:ReadLine()
            if typePars != null && typePars:Count > 0
                id += "`"+typePars:Count:ToString()
            endif
            VAR xType := XSourceTypeSymbol{id, kind, _attributes, range, interval, _file}
            xType:SourceCode := source

            IF interfaces?:Count > 0
               FOREACH VAR sInterface IN interfaces
                  xType:AddInterface(sInterface)
               NEXT
            ENDIF
            IF ! String.IsNullOrEmpty(parentType)
               xType:BaseTypeName := parentType
            ENDIF
            xType:IsPartial := _attributes:HasFlag(Modifiers.Partial)
            IF constraints?:Count > 0
               FOREACH VAR constraint IN constraints
                  xType:AddConstraints(constraint)
               NEXT
            ENDIF
            IF typePars?:Count > 0
               FOREACH VAR typepar IN typePars
                  xType:AddTypeParameter(typepar)
               NEXT
            ENDIF
            IF CurrentEntity != _globalType .AND. CurrentEntityKind:HasChildren()
               xType:Parent := SELF:CurrentEntity
            ENDIF

            RETURN <XSourceEntity>{xType}

         PRIVATE METHOD ParseDelegate() AS IList<XSourceEntity>
/*
delegate_           : (Attributes=attributes)? (Modifiers=classModifiers)?
                      D=DELEGATE (Namespace=nameDot)? Id=identifier
                      TypeParameters=typeparameters?
                      ParamList=parameterList?
                      (AS Type=datatype)?
                      (ConstraintsClauses+=typeparameterconstraintsclause)*
                      e=EOS
                    ;

*/
            IF ! Expect(XSharpLexer.DELEGATE)
               RETURN NULL
            ENDIF

            VAR sig      := SELF:ParseSignature()
            SELF:GetSourceInfo(_start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR source)
            VAR xType    := XSourceTypeSymbol{sig:Id, Kind.Delegate,_attributes, range, interval,_file}{SingleLine := TRUE}
            xType:SourceCode := source
            xType:TypeName := sig:DataType
            xType:File     := _file
            VAR xMember  := XSourceMemberSymbol{sig, Kind.Delegate, _attributes, ;
                              range, interval,_attributes:HasFlag(Modifiers.Static)} {SingleLine := TRUE}
            xMember:SourceCode := source
            xMember:Name := "Invoke"
            xType:AddMember(xMember)
            xMember:File       := _file
            RETURN <XSourceEntity>{xType}
            #endregion

         #region ClassMembers
      PRIVATE METHOD ParseMethod(  ) AS IList<XSourceEntity>
/*
// method rule used inside and outside class members rule
method              : (Attributes=attributes)? (Modifiers=memberModifiers)?
                      T=methodtype (ExplicitIface=nameDot)? Sig=signature
                      (CLASS ClassId=identifier)?      // Class Clause needed when entity and allowed when class member
                      vodummyclauses
                      end=eos
                      StmtBlk=statementBlock
                      (END T2=methodtype EOS)?
                    ;

methodtype          : Token=(METHOD | ACCESS | ASSIGN )
                    ;

*/
         LOCAL kind AS Kind
         IF SELF:La1 == XSharpLexer.ACCESS
            kind := Kind.Access
         ELSEIF SELF:La1 == XSharpLexer.ASSIGN
            kind := Kind.Assign
         ELSEIF SELF:La1 == XSharpLexer.METHOD
            kind := Kind.Method
         ELSE
            RETURN NULL
         ENDIF
         SELF:Consume()

         VAR sig := SELF:ParseSignature()
         VAR classClause := SELF:ParseOptionalClassClause()
         // read to EndOfLine
         SELF:GetSourceInfo(_start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR source)
         SELF:ReadLine()

         VAR xMember := XSourceMemberSymbol{sig, kind, _attributes, range, interval, _attributes:HasFlag(Modifiers.Static)}
         IF SELF:CurrentType != NULL
            SELF:CurrentType:AddMember(xMember)
         ENDIF
         xMember:SourceCode := source
         RETURN <XSourceEntity>{xMember}

      PRIVATE METHOD ParseProperty() AS IList<XSourceEntity>
            IF ! Expect(XSharpLexer.PROPERTY)
               RETURN NULL
            ENDIF
         // Note that inside the editor the SET and GET accessors are parsed as blocks and not as entities
/*
property            : (Attributes=attributes)? (Modifiers=memberModifiers)?
                      P=PROPERTY (SELF ParamList=propertyParameterList | (ExplicitIface=nameDot)? Id=identifier)
                      (ParamList=propertyParameterList)?
                      (AS Type=datatype)?
                      ( Auto=AUTO (AutoAccessors+=propertyAutoAccessor)* (Op=assignoperator Initializer=expression)? end=EOS	// Auto
                        | (LineAccessors+=propertyLineAccessor)+ end=EOS													// Single Line
                        | Multi=eos (Accessors+=propertyAccessor)+  END PROPERTY? EOS				// Multi Line
                      )
                    ;

propertyParameterList
                    : LBRKT  (Params+=parameter (COMMA Params+=parameter)*)? RBRKT
                    | LPAREN (Params+=parameter (COMMA Params+=parameter)*)? RPAREN		// Allow Parentheses as well
                    ;

propertyAutoAccessor: Attributes=attributes? Modifiers=accessorModifiers? Key=(GET|SET)
                    ;

propertyLineAccessor: Attributes=attributes? Modifiers=accessorModifiers?
                      ( {InputStream.La(2) != SET}? Key=GET Expr=expression?
                      | {InputStream.La(2) != SET}? Key=UDCSEP Expr=expression?           // New: UDCSep instead of GET
                      | {InputStream.La(2) != GET}? Key=SET ExprList=expressionList?
                      | Key=(GET|SET) )
                    ;
expressionList	    : Exprs+=expression (COMMA Exprs+=expression)*
                    ;

propertyAccessor    : Attributes=attributes? Modifiers=accessorModifiers?
                      ( Key=GET end=eos StmtBlk=statementBlock END GET?
                      | Key=GET UDCSEP ExpressionBody=expression              // New: Expression Body
                      | Key=SET end=eos StmtBlk=statementBlock END SET? )
                      | Key=SET UDCSEP ExpressionBody=expression              // New: Expression Body
                      end=eos
                    ;

*/
         VAR id := ""
         IF SELF:Matches(XSharpLexer.SELF)   // Self property
            id := ConsumeAndGetText()
         ELSE
            id := SELF:ParseQualifiedName()
         ENDIF
         VAR aParams := List<XSourceParameterSymbol>{}
         IF Matches(XSharpLexer.LPAREN)
            aParams := SELF:ParseParameterList( FALSE, OUT VAR _)
         ELSEIF Matches(XSharpLexer.LBRKT)
            aParams := SELF:ParseParameterList(TRUE, OUT VAR _)
         ENDIF
         VAR sType := SELF:ParseDataType(FALSE)
         LOCAL lSingleLine AS LOGIC
         DO WHILE ! SELF:Eos()
            IF Matches(XSharpLexer.AUTO, XSharpLexer.GET,XSharpLexer.SET, XSharpLexer.UDCSEP)
               lSingleLine := TRUE
            ENDIF
            Consume()
         ENDDO
         SELF:GetSourceInfo(_start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR source)
         SELF:ReadLine()
         VAR xMember := XSourceMemberSymbol{id, Kind.Property, _attributes, range, interval,sType} {SingleLine := lSingleLine}
         xMember:SourceCode := source
         IF SELF:CurrentType != NULL
            CurrentType:AddMember(xMember)
         ENDIF
         xMember:AddParameters(aParams)
         RETURN <XSourceEntity>{xMember}

      PRIVATE METHOD ParseEvent() AS IList<XSourceEntity>
            IF ! Expect(XSharpLexer.EVENT)
               RETURN NULL
            ENDIF

         // Inside the editor the ADD and REMOVE accessors are parsed as blocks and not as entities
/*
event_              : (Attributes=attributes)? (Modifiers=memberModifiers)?
                       E=EVENT (ExplicitIface=nameDot)? Id=identifier (AS Type=datatype)?
                       ( end=EOS
                        | (LineAccessors += eventLineAccessor)+ end=EOS
                        | Multi=eos (Accessors+=eventAccessor)+ END EVENT? EOS
                       )
                    ;

eventLineAccessor   : Attributes=attributes? Modifiers=accessorModifiers?
                      ( {InputStream.La(2) != REMOVE}? Key=ADD ExprList=expressionList?
                      | {InputStream.La(2) != ADD}?    Key=REMOVE ExprList=expressionList?
                      | Key=(ADD|REMOVE) )
                    ;
eventAccessor       : Attributes=attributes? Modifiers=accessorModifiers?
                      ( Key=ADD     END=eos StmtBlk=statementBlock END ADD?
                      | Key=ADD     UDCSEP ExpressionBody=expression              // New: Expression Body
                      | Key=REMOVE  END=eos StmtBlk=statementBlock END REMOVE? )
                      | Key=REMOVE  UDCSEP ExpressionBody=expression              // New: Expression Body
                      end=eos
                    ;
*/
         VAR id := SELF:ParseQualifiedName()
         VAR strType  := SELF:ParseDataType(FALSE)
         LOCAL lSingleLine AS LOGIC
         DO WHILE ! SELF:Eos()
            IF Matches( XSharpLexer.ADD,XSharpLexer.REMOVE)
               lSingleLine := TRUE
            ENDIF
            Consume()
         ENDDO
         SELF:GetSourceInfo(_start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR source)
         SELF:ReadLine()
         VAR xMember := XSourceMemberSymbol{id, Kind.Event, _attributes, range, interval,strType}  {SingleLine := lSingleLine}
         xMember:SourceCode := source
         IF SELF:CurrentType != NULL
            CurrentType:AddMember(xMember)
         ENDIF
         RETURN <XSourceEntity>{xMember}

      PRIVATE METHOD IsOperatorToken(token AS INT) AS LOGIC
/*
overloadedOps       : Token= (PLUS | MINUS | NOT | TILDE | INC | DEC | TRUE_CONST | FALSE_CONST |
                              MULT | DIV | MOD | AMP | PIPE | LSHIFT | RSHIFT | EEQ | NEQ | NEQ2 |
                              GT | LT | GTE | LTE |
                              AND | OR )  // these two do not exist in C# and are mapped to & and |
                    ;

conversionOps		: Token=( IMPLICIT | EXPLICIT )
                    ;
*/
            SWITCH token
            CASE XSharpLexer.PLUS
            CASE XSharpLexer.MINUS
            CASE XSharpLexer.NOT
            CASE XSharpLexer.TILDE
            CASE XSharpLexer.INC
            CASE XSharpLexer.DEC
            CASE XSharpLexer.TRUE_CONST
            CASE XSharpLexer.FALSE_CONST
            CASE XSharpLexer.MULT
            CASE XSharpLexer.DIV
            CASE XSharpLexer.MOD
            CASE XSharpLexer.AMP
            CASE XSharpLexer.PIPE
            CASE XSharpLexer.LSHIFT
            CASE XSharpLexer.RSHIFT
            CASE XSharpLexer.EEQ
            CASE XSharpLexer.NEQ
            CASE XSharpLexer.NEQ2
            CASE XSharpLexer.GT
            CASE XSharpLexer.LT
            CASE XSharpLexer.GTE
            CASE XSharpLexer.LTE
            CASE XSharpLexer.AND
            CASE XSharpLexer.OR
            CASE XSharpLexer.IMPLICIT
            CASE XSharpLexer.EXPLICIT
            RETURN TRUE
            END SWITCH
            RETURN FALSE

      PRIVATE METHOD ParseOperator() AS IList<XSourceEntity>
            IF ! Expect(XSharpLexer.OPERATOR)
               RETURN NULL
            ENDIF

/*
operator_           : Attributes=attributes? Modifiers=operatorModifiers?
                      o1=OPERATOR (Operation=overloadedOps | Conversion=conversionOps) Gt=GT?
                      ParamList=parameterList
                      (AS Type=datatype)?
                      (UDCSEP ExpressionBody=expression)?             // New: Expression Body
                      end=eos
                      StmtBlk=statementBlock
                     (END o1=OPERATOR EOS)?
                    ;

*/
         LOCAL t1 AS IToken
         LOCAL t2 AS IToken

         IF SELF:IsOperatorToken(SELF:La1)
            t1 := SELF:ConsumeAndGet()
         ENDIF
         IF SELF:La1 == XSharpLexer.GT
            t2 := SELF:ConsumeAndGet()
         ENDIF
         VAR aParams     := SELF:ParseParameterList(FALSE, OUT VAR _)
         VAR sType       := SELF:ParseDataType(FALSE)
         SELF:ParseExpressionBody()
         SELF:GetSourceInfo(_start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR source)

         VAR id := t1:GetText()+ IIF(t2 != NULL, t2:GetText(),"")
         SELF:ReadLine()
         VAR xMember := XSourceMemberSymbol{id, Kind.Operator, _attributes, range, interval,sType}
         xMember:SourceCode := source
         IF SELF:CurrentType != NULL
            CurrentType:AddMember(xMember)
         ENDIF
         xMember:AddParameters(aParams)
         RETURN <XSourceEntity>{xMember}

      PRIVATE METHOD ParseConstructor() AS IList<XSourceEntity>

/*constructor         :  (Attributes=attributes)? (Modifiers=constructorModifiers)?
                      c1=CONSTRUCTOR (ParamList=parameterList)? (AS VOID)? // As Void is allowed but ignored
                        (CallingConvention=callingconvention)?
                        (CLASS ClassId=identifier)?
                        (UDCSEP ExpressionBody=expression)?               // New: Expression Body
                        end=eos
                      (Chain=constructorchain)?
                      StmtBlk=statementBlock
                      (END c2=CONSTRUCTOR EOS)?
                    ;
*/
         IF ! Expect(XSharpLexer.CONSTRUCTOR)
            RETURN NULL
         ENDIF
         VAR id  := XLiterals.ConstructorName
         VAR aParams     := SELF:ParseParameterList(FALSE, OUT VAR _)
         VAR asType      := SELF:ParseDataType(FALSE)
         VAR callconv    := SELF:ParseCallingConvention()
         VAR classClause := SELF:ParseOptionalClassClause()
         SELF:ParseExpressionBody()
         SELF:GetSourceInfo(_start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR source)
         SELF:ReadLine()
         VAR xMember := XSourceMemberSymbol{id, Kind.Constructor, _attributes, range, interval,""}
         xMember:SourceCode := source
         xMember:AddParameters(aParams)
         IF SELF:CurrentType != NULL
            CurrentType:AddMember(xMember)
         ENDIF

         RETURN <XSourceEntity>{xMember}

      PRIVATE METHOD ParseDestructor() AS IList<XSourceEntity>
/*
destructor          : (Attributes=attributes)? (Modifiers=destructorModifiers)?
                      d1=DESTRUCTOR (LPAREN RPAREN)?
                      (CLASS ClassId=identifier)?
                      (UDCSEP ExpressionBody=expression)?               // New: Expression Body
                      end=eos
                      StmtBlk=statementBlock
                      (END d2=DESTRUCTOR EOS)?
                    ;

*/
         IF ! Expect(XSharpLexer.DESTRUCTOR)
            RETURN NULL
         ENDIF

         VAR id  := XLiterals.DestructorName
         IF SELF:La1 == XSharpLexer.LPAREN .AND. SELF:La2 == XSharpLexer.RPAREN
            Consume()
            Consume()
         ENDIF
         VAR classClause := SELF:ParseOptionalClassClause()
         SELF:ParseExpressionBody()
         SELF:GetSourceInfo(_start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR source)
         SELF:ReadLine()
         VAR xMember := XSourceMemberSymbol{id, Kind.Destructor, _attributes, range, interval,"VOID"}
         xMember:SourceCode := source
         IF SELF:CurrentType != NULL
            SELF:CurrentType:AddMember(xMember)
         ENDIF
         RETURN <XSourceEntity>{xMember}

      PRIVATE METHOD ParseClassVars() AS IList<XSourceEntity>
/*
classvars           : (Attributes=attributes)? Modifiers=classvarModifiers
                      Vars+=classvar (COMMA Vars+=classvar)*
                      eos
                    ;
*/

         VAR result := List<XSourceEntity>{}
         VAR classVars := List<XSourceMemberSymbol>{}
            VAR classVar := ParseClassVar(Kind.Field)
            classVar:SourceCode := classVar:ModVis +" "+classVar:SourceCode
            classVars:Add(classVar)
            DO WHILE Expect(XSharpLexer.COMMA)
                classVar := ParseClassVar(Kind.Field)
                classVar:SourceCode := classVar:ModVis +" "+classVar:SourceCode
                classVars:Add(classVar)
            ENDDO
            SELF:ReadLine()
            CopyClassVarTypes(classVars)
            result:AddRange(classVars)
         RETURN result
         #endregion

     PRIVATE METHOD CopyClassVarTypes(classVars AS List<XSourceMemberSymbol>)  AS VOID
         IF classVars:Count > 1
            VAR type := ""
                classVars:Reverse()
                FOREACH VAR classVar IN classVars
                    IF !classVar:IsTyped
                        classVar:ReturnType := type
                        classVar:SourceCode += " AS "+type
                    ELSE
                        type := classVar:ReturnType
                    ENDIF
                NEXT
                classVars:Reverse()
            ENDIF
         RETURN

      #region Enum
      PRIVATE METHOD ParseEnum() AS IList<XSourceEntity>
/*
enum_               : (Attributes=attributes)? (Modifiers=classModifiers)?
                      E=ENUM (Namespace=nameDot)? Id=identifier ((AS|INHERIT) Type=datatype)? e=eos
                      (Members+=enummember)+
                      END ENUM? EOS
                    ;
*/
         IF ! Expect(XSharpLexer.ENUM)
            RETURN NULL
         ENDIF

         VAR id := SELF:ParseQualifiedName()
         VAR type := ""
         IF Matches(XSharpLexer.AS)
            type := SELF:ParseDataType(FALSE)
         ELSEIF Expect(XSharpLexer.INHERIT)
            type := SELF:ParseTypeName()
         ENDIF
         SELF:GetSourceInfo(_start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR source)
         SELF:ReadLine()
         VAR xType := XSourceTypeSymbol{id, Kind.Enum, _attributes, range, interval, _file} {BaseTypeName := type}
         xType:SourceCode := source
         RETURN <XSourceEntity>{xType}

      PRIVATE METHOD ParseEnumMember() AS IList<XSourceEntity>
/*
enummember          : (Attributes=attributes)? MEMBER? Id=identifier (Op=assignoperator Expr=expression)? eos
                    ;
*/

         VAR att := SELF:ParseAttributes()
         Expect(XSharpLexer.MEMBER)    // Optional !
         VAR strValue := ""
         VAR id := SELF:ParseQualifiedName()
         IF ExpectAssignOp()
            strValue := SELF:ParseExpression()
         ENDIF
         SELF:GetSourceInfo(_start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR source)
         SELF:ReadLine()
         VAR type := CurrentType?:Name
         VAR xMember := XSourceMemberSymbol{id, Kind.EnumMember, _attributes, range, interval, type} {SingleLine := TRUE, @@Value := strValue}
         xMember:File := SELF:_file
         xMember:SourceCode := source
         xMember:IsStatic := TRUE
         RETURN <XSourceEntity>{xMember}

         #endregion

      #region VO Global, Define, Struct, Union
      PRIVATE METHOD ParseVOGlobals() AS IList<XSourceEntity>
/*
                    // [STATIC] GLOBAL [CONST] Identifier [:= Expression] [, Identifier2 [:= Expression2] ] [AS Type]
                    // STATIC          [CONST] Identifier [:= Expression] [, Identifier2 [:= Expression2] ] [AS Type]
voglobal            : (Attributes=attributes)? (Modifiers=funcprocModifiers)? Global=GLOBAL (Const=CONST)? Vars=classVarList end=EOS
                    | (Attributes=attributes)? Static=STATIC (Const=CONST)? Vars=classVarList end=EOS
                    ;
*/
         IF ! Expect(XSharpLexer.GLOBAL)
            RETURN NULL
         ENDIF
         Expect(XSharpLexer.CONST)
         VAR result := List<XSourceEntity>{}
         VAR classVars := List<XSourceMemberSymbol>{}
         VAR classVar := ParseClassVar(Kind.VOGlobal)
         classVar:SourceCode := classVar:ModVis+" GLOBAL "+ classVar:SourceCode
         classVars:Add(classVar)
         DO WHILE Expect(XSharpLexer.COMMA)
            classVar := ParseClassVar(Kind.VOGlobal)
            classVar:SourceCode := classVar:ModVis+" GLOBAL "+ classVar:SourceCode
            classVars:Add(classVar)
        ENDDO
         SELF:ReadLine()
         CopyClassVarTypes(classVars)
         result:AddRange(classVars)
         RETURN result

      PRIVATE METHOD ParseVoDefine() AS IList<XSourceEntity>
/*
vodefine            : (Modifiers=funcprocModifiers)?
                      D=DEFINE Id=identifier Op=assignoperator Expr=expression (AS DataType=typeName)? end=EOS
                    ;

*/
         IF ! Expect(XSharpLexer.DEFINE)
            RETURN NULL
         ENDIF

         VAR id := SELF:ParseQualifiedName()
         LOCAL strValue AS STRING
         IF ExpectAssignOp()
            strValue := SELF:ParseExpression()
         ENDIF
         VAR type := SELF:ParseDataType(FALSE)
         SELF:GetSourceInfo(_start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR source)
         SELF:ReadLine()
         VAR xMember := XSourceMemberSymbol{id, Kind.VODefine, _attributes, range,interval, type} {SingleLine := TRUE, @@Value := strValue}
         xMember:SourceCode := source
         xMember:File := SELF:_file
         RETURN <XSourceEntity>{xMember}

      PRIVATE METHOD ParseVoStruct() AS IList<XSourceEntity>
/*
vostruct            : (Modifiers=votypeModifiers)?
                      V=VOSTRUCT (Namespace=nameDot)? Id=identifier (ALIGN Alignment=INT_CONST)? e=eos
                      (Members+=vostructmember)+
                      (END VOSTRUCT EOS)?
                    ;
*/
         IF ! Expect(XSharpLexer.VOSTRUCT)
            RETURN NULL
         ENDIF

         VAR id := SELF:ParseQualifiedName()
         VAR sAlign := ""
         IF Expect(XSharpLexer.ALIGN)
            IF ExpectAndGet(XSharpLexer.INT_CONST, OUT VAR t2)
               sAlign := t2:GetText() // Align number
            ENDIF
         ENDIF
         // read to EndOfLine
         SELF:GetSourceInfo(_start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR source)
         SELF:ReadLine()
         VAR xType := XSourceTypeSymbol{id, Kind.VOStruct, _attributes, range, interval,_file}
         xType:SourceCode := source
         IF String.IsNullOrEmpty(sAlign)
            xType:AddInterface(sAlign)
         ENDIF
         RETURN <XSourceEntity>{xType}

      PRIVATE METHOD ParseVOUnion() AS IList<XSourceEntity>
/*
vounion             : (Modifiers=votypeModifiers)?
                      U=UNION (Namespace=nameDot)? Id=identifier e=eos
                      (Members+=vostructmember)+
                      (END UNION EOS)?
                    ;
*/
         IF ! Expect(XSharpLexer.UNION)
            RETURN NULL
         ENDIF

         VAR id := SELF:ParseQualifiedName()
         SELF:GetSourceInfo(_start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR source)
         SELF:ReadLine()
         VAR xType := XSourceTypeSymbol{id, Kind.Union, _attributes, range, interval,_file}
         xType:SourceCode := source
         RETURN <XSourceEntity>{xType}

      PRIVATE METHOD ParseVODLL() AS IList<XSourceEntity>
/*

vodll               : (Attributes=attributes)? (Modifiers=funcprocModifiers)? // Optional
                      D=DLL T=funcproctype Id=identifier ParamList=parameterList (AS Type=datatype)?
                      (CallingConvention=dllcallconv)? COLON
                      Dll=identifierString (DOT Extension=identifierString)?
                      (	Ordinal=REAL_CONST
                       |  DOT Entrypoint=identifierString Address=ADDROF? Number=INT_CONST? (NEQ2 INT_CONST)?
                      )

                      ( CharSet=(AUTO | ID) )?  // ID must be either ANSI or UNICODE
                      EOS
                    ;

*/
         IF ! Expect(XSharpLexer.DLL)
            RETURN NULL
         ENDIF
         IF ! ParseFuncProcType (OUT VAR kind)
            RETURN NULL
         ENDIF
         SELF:Consume()
         VAR sig     := SELF:ParseSignature()
         VAR colon   := SELF:ConsumeAndGetText()
         VAR dllName := SELF:ConsumeAndGetText()
         VAR dotName := ""
         IF Expect(XSharpLexer.DOT)
            dotName := SELF:ConsumeAndGetText()
         ENDIF
         SELF:GetSourceInfo(_start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR source)
         SELF:ReadLine()
         VAR xMember := XSourceMemberSymbol{sig, Kind.VODLL, _attributes, range, interval, _attributes:HasFlag(Modifiers.Static)} {SubType := kind }
         xMember:File := SELF:_file
         xMember:SourceCode := source
         RETURN <XSourceEntity>{xMember}

      PRIVATE METHOD ParseVoStructMember() AS IList<XSourceEntity>
/*
vostructmember      : MEMBER Dim=DIM Id=identifier LBRKT ArraySub=arraysub RBRKT (As=(AS | IS) DataType=datatype)? eos
                    | MEMBER Id=identifier (As=(AS | IS) DataType=datatype)? eos
                    ;
*/
         IF ! Expect(XSharpLexer.MEMBER)
            RETURN NULL
         ENDIF

         LOCAL sBracket AS STRING
         VAR isArray := Expect(XSharpLexer.DIM)
         VAR id := SELF:ParseQualifiedName()

         IF isArray
            sBracket := SELF:ParseArraySub()
         ENDIF
         VAR sType := SELF:ParseDataType(TRUE)
         SELF:GetSourceInfo(_start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR source)
         SELF:ReadLine()
         IF isArray
            sType += "[]"
         ENDIF
         VAR xMember := XSourceMemberSymbol{id, Kind.Field, _attributes, range, interval, sType} {SingleLine := TRUE, IsArray := isArray}
         xMember:File := SELF:_file
         xMember:SourceCode := source
         RETURN <XSourceEntity>{xMember}

         #endregion

      PRIVATE METHOD ParseClassVar(eKind AS Kind) AS XSourceMemberSymbol
/*

classvar            : (DIM=DIM)? Id=identifier (LBRKT ArraySub=arraysub RBRKT)?
                      (Op=assignoperator Initializer=expression)?
                      (As=(AS | IS) DataType=datatype)?
                    ;
*/
         LOCAL isDim  := FALSE  AS LOGIC
         LOCAL sId        AS STRING
         LOCAL sDefault   AS STRING
         LOCAL startToken AS IToken
         LOCAL endToken   AS IToken
         startToken := SELF:Lt1
         isDim := Expect(XSharpLexer.DIM)
         sId  := SELF:ParseIdentifier()
         VAR sBracket  := SELF:ParseArraySub()
         IF ExpectAssignOp()
            sDefault := SELF:ParseExpression()
         ENDIF
         VAR sType := SELF:ParseDataType(TRUE)
         endToken := SELF:LastToken
         SELF:GetSourceInfo(startToken, endToken, OUT VAR range, OUT VAR interval, OUT VAR source)
         VAR xMember   := XSourceMemberSymbol{sId,eKind, SELF:_attributes, range,interval,sType} {SingleLine := TRUE}
         xMember:ReturnType := sType
         xMember:SourceCode := source
         xMember:Value := sDefault
         IF isDim .OR. !String.IsNullOrEmpty(sBracket)
            xMember:IsArray := TRUE
         ENDIF
         RETURN xMember

      PRIVATE METHOD ParseArraySub AS STRING
/*
arraysub            : ArrayIndex+=expression (RBRKT LBRKT ArrayIndex+=expression)+		// x][y
                    | ArrayIndex+=expression (COMMA ArrayIndex+=expression)+			// x,y
                    | ArrayIndex+=expression
                    ;
*/
         LOCAL sBracket:= "" AS STRING
         DO WHILE SELF:La1 == XSharpLexer.LBRKT           // allow SomeId[1][2]
            sBracket += SELF:ConsumeAndGetText()
            DO WHILE SELF:La1 != XSharpLexer.RBRKT .AND. ! SELF:Eos()
               sBracket += SELF:ConsumeAndGetText()
            ENDDO
            IF SELF:La1 == XSharpLexer.RBRKT
               sBracket += SELF:ConsumeAndGetText()
            ENDIF
         ENDDO
         RETURN sBracket

      PRIVATE METHOD ParseArraySubFox AS STRING
/*
arraysub            : ArrayIndex+=expression (RPAREN LPAREN ArrayIndex+=expression)+		// x)(y
                    | ArrayIndex+=expression (COMMA ArrayIndex+=expression)+			// x,y
                    | ArrayIndex+=expression
                    ;
*/
         LOCAL sBracket:= "" AS STRING
         DO WHILE SELF:La1 == XSharpLexer.LPAREN           // allow SomeId(1)(2)
            sBracket += SELF:ConsumeAndGetText()
            DO WHILE SELF:La1 != XSharpLexer.RPAREN .AND. ! SELF:Eos()
               sBracket += SELF:ConsumeAndGetText()
            ENDDO
            IF SELF:La1 == XSharpLexer.RPAREN
               sBracket += SELF:ConsumeAndGetText()
            ENDIF
         ENDDO
         RETURN sBracket

      PRIVATE METHOD ParseSignature() AS XMemberSignature
/*
signature             : Id=identifier
                      TypeParameters=typeparameters?
                      (ParamList=parameterList)?
                      (AS Type=datatype)?
                      (ConstraintsClauses+=typeparameterconstraintsclause)*
                      (CallingConvention=callingconvention)?
                      (UDCSEP ExpressionBody=expression)?
                      ;

*/
         LOCAL oSig          AS XMemberSignature
         oSig                := XMemberSignature{}
         oSig:Id             := SELF:ParseQualifiedName()
         IF SELF:La1 == XSharpLexer.LT
            oSig:TypeParameters := SELF:ParseTypeParameters()
         ENDIF
         IF SELF:La1 == XSharpLexer.LPAREN
            var params        := SELF:ParseParameterList(FALSE,  OUT VAR isSelf)
            oSig:Parameters:AddRange(params)
            oSig:IsExtension  := isSelf
         ENDIF
         oSig:DataType       := SELF:ParseDataType(FALSE)
         DO WHILE SELF:La1 == XSharpLexer.WHERE .AND. ! SELF:Eos()
            oSig:TypeParameterContraints:Add(SELF:ParseTypeParameterConstraints())
         ENDDO
         oSig:CallingConvention  := SELF:ParseCallingConvention()
         IF oSig:CallingConvention == CallingConvention.Clipper
             FOREACH var parameter in oSig:Parameters
                 parameter:TypeName := XLiterals.UsualType
                 parameter:ParamType := ParamType.As
             NEXT
         ENDIF
         SELF:ParseExpressionBody()
         RETURN oSig

      PRIVATE METHOD ParseTypeParameters AS List<STRING>
         IF SELF:La1 != XSharpLexer.LT
            RETURN NULL
         ENDIF
         Consume()
         VAR aTypeParams := List<STRING>{}
         DO WHILE SELF:La1 != XSharpLexer.GT .AND. !SELF:Eos()
            VAR sParam := SELF:TokensAsString(ParseAttributes())

            IF SELF:La1 == XSharpLexer.IN .OR. SELF:La1 == XSharpLexer.OUT
               sParam += ConsumeAndGetText()+" "
            ENDIF
            IF SELF:IsId(SELF:La1)
               sParam += ConsumeAndGetText()
            ELSE
               EXIT
            ENDIF
            aTypeParams:Add(sParam)
            SELF:Expect(XSharpLexer.COMMA)
         ENDDO
         SELF:Expect(XSharpLexer.GT)
         RETURN aTypeParams

      PRIVATE METHOD ParseParameterList(lBracketed AS LOGIC, isSelf OUT LOGIC) AS List<XSourceParameterSymbol>
         isSelf := FALSE
         IF SELF:La1 != XSharpLexer.LPAREN .AND. ! lBracketed
            RETURN NULL
         ENDIF
         IF SELF:La1 != XSharpLexer.LBRKT .AND. lBracketed
            RETURN NULL
         ENDIF
         Consume()   // LParen
         VAR aResult  := List<XSourceParameterSymbol>{}
         LOCAL cond AS DelEndToken
         cond := { token => IIF (lBracketed, token == XSharpLexer.RBRKT, token == XSharpLexer.RPAREN ) }
         DO WHILE !cond(SELF:La1) .AND. ! SELF:Eos()
            LOCAL defaultExpr := NULL AS IList<XSharpToken>

            VAR start := SELF:Lt1
            VAR atts := SELF:TokensAsString(ParseAttributes())
            VAR sId   := ""
            VAR sTypeName := ""
            IF SELF:La1 == XSharpLexer.SELF
               isSelf := TRUE
               Consume()
            ENDIF
            IF SELF:IsId(SELF:La1)
               sId  += SELF:ConsumeAndGetText()
            ENDIF
            IF ExpectAssignOp()
               defaultExpr := SELF:ParseExpressionAsTokens()
            ENDIF

            VAR token := SELF:ConsumeAndGetAny(XSharpLexer.AS, XSharpLexer.IN, XSharpLexer.OUT, XSharpLexer.REF,XSharpLexer.PARAMS)
            IF token != NULL
               IF SELF:La1 == XSharpLexer.CONST
                  Consume()
               ENDIF
               sTypeName := SELF:ParseTypeName()
            ELSE
                IF _file:Project:ParseOptions:VOUntypedAllowed
                    sTypeName := "USUAL"
                ENDIF
            ENDIF
            LOCAL variable AS XSourceParameterSymbol
            VAR endToken := SELF:LastToken
            SELF:GetSourceInfo(start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR _)
            variable := XSourceParameterSymbol{CurrentEntity, sId, range,interval, sTypeName}
            IF token != NULL
               variable:ParamType := (ParamType) token.Type
            ENDIF
            IF defaultExpr != NULL
               variable:Value := SELF:TokensAsString(defaultExpr)
            ENDIF
            aResult:Add(variable)
            IF SELF:La1 == XSharpLexer.COMMA
               Consume()
            ELSE
               EXIT
            ENDIF
         ENDDO
         IF cond(SELF:La1)
            Consume()
         ENDIF
         RETURN aResult

      PRIVATE METHOD ParseTypeParameterConstraints() AS STRING

         IF SELF:La1 != XSharpLexer.WHERE
            RETURN ""
         ENDIF
         VAR result := SELF:ConsumeAndGetText() +" "
         IF SELF:IsId(SELF:La1) .AND. SELF:La2 == XSharpLexer.IS
            result     += SELF:ParseIdentifier() +" "
            result     += SELF:ConsumeAndGetText()+" "
            result     += SELF:ParseTypeParameterConstraint()+" "
            DO WHILE SELF:La1 == XSharpLexer.COMMA .AND. ! SELF:Eos()
               result += SELF:ConsumeAndGetText()+" "
               result     += SELF:ParseTypeParameterConstraint()+" "
            ENDDO
         ENDIF
         RETURN result

      PRIVATE METHOD ParseTypeParameterConstraint() AS STRING
         IF SELF:La1 == XSharpLexer.CLASS .OR. SELF:La1 == XSharpLexer.STRUCTURE
            RETURN ConsumeAndGetText()
         ELSEIF SELF:La1 == XSharpLexer.NEW .AND. SELF:La2 == XSharpLexer.LPAREN .AND. La3 == XSharpLexer.RPAREN
            RETURN "NEW()"
         ENDIF
         RETURN ParseTypeName()

      PRIVATE METHOD ParseDataType(lIsAllowed AS LOGIC) AS STRING
         IF SELF:La1 == XSharpLexer.AS .OR. (SELF:La1 == XSharpLexer.IS .AND. lIsAllowed)
            SELF:Consume()
            RETURN SELF:ParseTypeName()
         ENDIF
         RETURN ""

      PRIVATE METHOD ParseExpressionBody() AS STRING
         IF SELF:La1 != XSharpLexer.UDCSEP
            RETURN ""
         ENDIF
         SELF:Consume()
         RETURN SELF:ParseExpression()

      PRIVATE METHOD ParseCallingConvention() AS CallingConvention
/*
callingconvention	: Convention=(CLIPPER | STRICT | PASCAL | ASPEN | WINCALL | CALLBACK | FASTCALL | THISCALL)
                    ;
*/
         SWITCH SELF:La1
         CASE XSharpLexer.CLIPPER
         CASE XSharpLexer.STRICT
         CASE XSharpLexer.PASCAL
         CASE XSharpLexer.ASPEN
         CASE XSharpLexer.WINCALL
         CASE XSharpLexer.CALLBACK
         CASE XSharpLexer.FASTCALL
         CASE XSharpLexer.THISCALL
            VAR result := (CallingConvention) SELF:La1
            Consume()
            RETURN result
         END SWITCH
         RETURN CallingConvention.None

      PRIVATE METHOD ParseFuncProcType( kind OUT Kind) AS LOGIC

            IF SELF:La1 == XSharpLexer.FUNCTION
                kind := Kind.Function
            ELSEIF SELF:La1 == XSharpLexer.PROCEDURE
                kind := Kind.Procedure
            ELSE
                kind := Kind.Unknown
                RETURN FALSE
            ENDIF
            RETURN TRUE

      PRIVATE METHOD ParseLocalFuncProc() AS IList<XSourceEntity>
         /*

         localfuncproc       :  (Modifiers=localfuncprocModifiers)?
                                 LOCAL T=funcproctype Sig=signature
                                 end=eos
                                 StmtBlk=statementBlock
                                 END T2=funcproctype   EOS
                     ;

         */
            LOCAL kind AS Kind
            IF SELF:La1 != XSharpLexer.LOCAL
                RETURN NULL
            ENDIF
            SELF:Consume()      // Local
            IF ! ParseFuncProcType (OUT kind)
               RETURN NULL
            ENDIF
            SELF:Consume()      // Function or Procedure
            IF kind == Kind.Function
                kind := Kind.LocalFunc
            ELSEIF kind == Kind.Procedure
                kind := Kind.LocalProc
            ENDIF
            VAR sig := SELF:ParseSignature()
            SELF:GetSourceInfo(_start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR source)
            SELF:ReadLine()
            _attributes |= Modifiers.Private
            _attributes &= (~Modifiers.Export)
            VAR xMember := XSourceMemberSymbol{sig, kind, _attributes, range, interval, false}
            xMember:SourceCode := source
            xMember:File := SELF:_file
            RETURN <XSourceEntity>{xMember}

      PRIVATE METHOD IsAssignOp (nToken AS LONG) AS LOGIC
         RETURN nToken == XSharpLexer.ASSIGN_OP .OR. nToken == XSharpLexer.EQ

      PRIVATE METHOD ParseTypeName() AS STRING
         LOCAL result := "" AS STRING
         SWITCH SELF:La1

         CASE XSharpLexer.ID
            result := SELF:ParseQualifiedName()

         CASE XSharpLexer.ARRAY
            result :=  ConsumeAndGetText()
            IF SELF:La1 == XSharpLexer.OF
               SELF:Consume()
               var subtype := SELF:ParseTypeName()
               result := "ARRAY<"+subtype+">"
            ENDIF
        CASE XSharpLexer.CODEBLOCK
        CASE XSharpLexer.DATE
        CASE XSharpLexer.FLOAT
        CASE XSharpLexer.PSZ
        CASE XSharpLexer.SYMBOL
        CASE XSharpLexer.USUAL
            result :=  ConsumeAndGetText()
        CASE XSharpLexer.BYTE
        CASE XSharpLexer.CHAR
        CASE XSharpLexer.DATETIME
        CASE XSharpLexer.DECIMAL
        CASE XSharpLexer.DWORD
        CASE XSharpLexer.DYNAMIC
        CASE XSharpLexer.INT
        CASE XSharpLexer.INT64
        CASE XSharpLexer.LOGIC
        CASE XSharpLexer.LONGINT
        CASE XSharpLexer.OBJECT
        CASE XSharpLexer.PTR
        CASE XSharpLexer.REAL4
        CASE XSharpLexer.REAL8
        CASE XSharpLexer.SHORTINT
        CASE XSharpLexer.STRING
        CASE XSharpLexer.UINT64
        CASE XSharpLexer.VOID
         CASE XSharpLexer.WORD
            result :=  SELF:ConsumeAndGetText()
         OTHERWISE
            IF XSharpLexer.IsKeyword(SELF:La1)
               result :=  SELF:ConsumeAndGetText()
            ENDIF
         END SWITCH
         IF result:Length > 0
            VAR suffix := ParseTypeSuffix()
            IF suffix:Trim() == "?"
                result := "Nullable<"+result+">"
            ELSE
                result += suffix
            ENDIF
         ENDIF
         RETURN result:Trim()

      PRIVATE METHOD ParseTypeSuffix AS STRING
         IF SELF:La1 == XSharpLexer.PTR
            RETURN " "+SELF:ConsumeAndGetText()
         ELSEIF SELF:La1 = XSharpLexer.QMARK
            RETURN SELF:ConsumeAndGetText()
         ELSEIF SELF:La1 == XSharpLexer.LBRKT .and. (SELF:La2 != XSharpLexer.ID .and. SELF:La2 != XSharpLexer.UDC_KEYWORD .and. ! XSharpLexer.IsKeyword(SELF:La2))
            VAR tokens := List<XSharpToken>{}
            tokens:Add(SELF:ConsumeAndGet())
            LOCAL openCount := 1 as LONG
            LOCAL closed := FALSE AS LOGIC
            DO WHILE ! SELF:Eos() .AND. ! closed
               SWITCH SELF:La1
               CASE XSharpLexer.RBRKT
                  tokens:Add(SELF:ConsumeAndGet())
                  openCount -= 1
                  IF openCount == 0 .and. SELF:La1 != XSharpLexer.LBRKT
                     closed := TRUE
                  ENDIF
               CASE XSharpLexer.LBRKT
                  openCount += 1
                  tokens:Add(SELF:ConsumeAndGet())
               CASE XSharpLexer.COMMA
                  tokens:Add(SELF:ConsumeAndGet())
               OTHERWISE
                  closed := TRUE
               END SWITCH
            ENDDO
            RETURN SELF:TokensAsString(tokens)
         ELSEIF SELF:La1 == XSharpLexer.LT
            VAR result := ""
            DO WHILE SELF:La1 != XSharpLexer.GT .AND. ! SELF:Eos()
               result += SELF:ConsumeAndGetText()
            ENDDO
            IF SELF:La1 == XSharpLexer.GT
               result += SELF:ConsumeAndGetText()
            ENDIF
            RETURN result

         ENDIF
         RETURN ""

      PRIVATE METHOD ParseExpressionAsTokens AS IList<XSharpToken>
         // parse until SELF:Eos() or tokens such as AS, IS,
         LOCAL nested := 0 AS LONG
         LOCAL done  := FALSE AS LOGIC
         VAR tokens := List<XSharpToken>{}

         DO WHILE ! SELF:Eos() .AND. ! done
            SWITCH SELF:La1
            CASE XSharpLexer.LT
            CASE XSharpLexer.LPAREN
            CASE XSharpLexer.LBRKT
            CASE XSharpLexer.LCURLY
               nested++
            CASE XSharpLexer.GT
            CASE XSharpLexer.RPAREN
            CASE XSharpLexer.RBRKT
            CASE XSharpLexer.RCURLY
                  nested--

            CASE XSharpLexer.AS
            CASE XSharpLexer.IS
            CASE XSharpLexer.COMMA
                  // The comma is used for a comma separated list of expression.
                  // however inside a method call or constructor call we should have nested > 0
                  IF (nested == 0)
                     done := TRUE
                  ENDIF

            OTHERWISE
                  // other keywords, operators etc.
                  //
               NOP
            END SWITCH
            IF !done
               tokens:Add(ConsumeAndGet())
            ENDIF
         ENDDO
         RETURN tokens

      PRIVATE METHOD ParseExpression() AS STRING
         RETURN TokensAsString(SELF:ParseExpressionAsTokens())

      PRIVATE METHOD ParseForLocalDeclaration() AS LOGIC
         VAR tokens := List<IToken>{}
         VAR firstToken := SELF:Lt1
         SWITCH firstToken.Type
         CASE XSharpLexer.LOCAL
         CASE XSharpLexer.VAR
         CASE XSharpLexer.STATIC
            RETURN SELF:ParseDeclarationStatement()
         CASE XSharpLexer.PUBLIC
         CASE XSharpLexer.PRIVATE
         CASE XSharpLexer.MEMVAR
         CASE XSharpLexer.PARAMETERS
         CASE XSharpLexer.LPARAMETERS
         CASE XSharpLexer.DIMENSION
         CASE XSharpLexer.DECLARE
            RETURN SELF:ParseXBaseDeclarationStatement()

         CASE XSharpLexer.ID WHEN SELF:_firstTokenOnLine != NULL
            // The PRIVATE and PUBLIC keyword may have been parsed as Modifier
            SWITCH SELF:_firstTokenOnLine:Type
            CASE XSharpLexer.PRIVATE
            CASE XSharpLexer.PUBLIC
                SELF:ParseMemvarAllocationStatement(SELF:_firstTokenOnLine:Type)
                RETURN TRUE
            END SWITCH

         END SWITCH

         DO WHILE ! SELF:Eos()
            IF SELF:Matches(XSharpLexer.IMPLIED, XSharpLexer.VAR)
               IF SELF:IsId(SELF:La2)
                  LOCAL kind := ImpliedKind.Assignment as ImpliedKind
                  VAR start := SELF:Lt2
                  Consume()      // IMPLIED or VAR
                  VAR id    := SELF:ParseIdentifier()
                  LOCAL expr AS IList<XSharpToken>
                  IF SELF:IsAssignOp(SELF:La1) .OR. SELF:La1 == XSharpLexer.IN
                     IF SELF:La1 == XSharpLexer.IN
                        kind := ImpliedKind.InCollection
                     ELSE
                        kind := ImpliedKind.Assignment
                     ENDIF
                     SWITCH firstToken.Type
                     CASE XSharpLexer.FOR
                        kind := ImpliedKind.LoopCounter
                     CASE XSharpLexer.FOREACH
                        kind := ImpliedKind.InCollection
                     CASE XSharpLexer.BEGIN
                     CASE XSharpLexer.USING
                        kind := ImpliedKind.Using
                     END SWITCH
                     Consume()
                     // get the text after IN or the expression after the assignment operator
                     expr := SELF:ParseExpressionAsTokens()
                  ENDIF
                  SELF:GetSourceInfo(start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR _)
                  VAR xVar := XSourceImpliedVariableSymbol{SELF:CurrentEntity, id, range, interval }
                  xVar:Expression  := expr
                  xVar:ImpliedKind := kind
                  xVar:TypeName    := TokensAsString(expr):Trim()
                  SELF:_locals:Add(xVar)
                  SELF:ReadLine()
               ENDIF
            ELSEIF SELF:IsId(SELF:La1) .AND. SELF:La2 == XSharpLexer.AS
               VAR start := SELF:Lt1
               VAR id    := SELF:ParseIdentifier()
               VAR type  := SELF:ParseDataType(FALSE)
               SELF:GetSourceInfo(start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR _)
               VAR xVar := XSourceVariableSymbol{SELF:CurrentEntity, id, range, interval, IIF(type == "", _missingType, type) }
               SELF:_locals:Add(xVar)

            ELSEIF SELF:La1 == XSharpLexer.LOCAL .AND. SELF:IsId(SELF:La2)
               VAR start := SELF:Lt1
               LOCAL expression := List<XSharpToken>{} AS IList<XSharpToken>
               Consume()
               VAR id    := SELF:ParseIdentifier()
               IF SELF:ExpectAssignOp()
                   expression := SELF:ParseExpressionAsTokens()
               ENDIF
               LOCAL type := "" AS STRING
               LOCAL lIsIs := SELF:La1 == XSharpLexer.IS AS LOGIC
               IF SELF:ExpectAny(XSharpLexer.AS, XSharpLexer.IS)
                    type  := SELF:ParseTypeName()
               ELSE
                    type := SELF:_missingType
               ENDIF
               SELF:GetSourceInfo(start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR _)
               VAR xVar := XSourceVariableSymbol{SELF:CurrentEntity, id, range, interval, type }
               IF lIsIs
                    xVar:LocalType := LocalType.Is
               ENDIF
               xVar:Expression := expression
               SELF:_locals:Add(xVar)

            ENDIF
            Consume()
         ENDDO
         RETURN TRUE

      PRIVATE METHOD ParseForLocals() AS VOID
         // Check for method call with OUT VAR or OUT id AS TYPE
         // Check for <expr> IS <type> VAR <id>
         // also parses the EOS characters following the line
         DO WHILE ! SELF:Eos()
            IF SELF:La1 == XSharpLexer.IS
               VAR start := SELF:Lt1
               Consume() // IS token
               VAR type := SELF:ParseTypeName()
               IF SELF:La1 == XSharpLexer.VAR
                  Consume()   // VAR
                  VAR id            := SELF:ParseIdentifier()
                  SELF:GetSourceInfo(start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR _)
                  VAR xVar          := XSourceImpliedVariableSymbol{SELF:CurrentEntity, id, range, interval}
                  xVar:TypeName     := type:Trim()
                  xVar:ImpliedKind  := ImpliedKind.TypeCheck
                  SELF:_locals:Add(xVar)
               ENDIF
            ELSEIF SELF:La1 == XSharpLexer.OUT
               // OUT Id AS Type
               VAR start := SELF:Lt1
               Consume() // OUT
               IF SELF:IsId(SELF:La2) .AND. La3 == XSharpLexer.AS
                  VAR id   := SELF:ParseIdentifier()
                  VAR type := SELF:ParseDataType(FALSE)
                  SELF:GetSourceInfo(start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR _)
                  VAR xVar     := XSourceVariableSymbol{SELF:CurrentEntity, id, range, interval, iif(type == "", _missingType, type)}
                  SELF:_locals:Add(xVar)
               ELSEIF SELF:La2 == XSharpLexer.VAR
                  // OUT VAR Id, when Id = '_' then discard and do not create a local
                  Consume()   // Var
                  VAR id         := SELF:ParseIdentifier()
                  IF id          == "_"
                     SELF:GetSourceInfo(start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR _)
                     VAR xVar           := XSourceImpliedVariableSymbol{SELF:CurrentEntity, id, range, interval}
                     xVar:ImpliedKind := ImpliedKind.OutParam
                     // Todo: Get Expression for OUT variable
                     SELF:_locals:Add(xVar)
                  ENDIF
               ELSEIF SELF:La2 == XSharpLexer.NULL
                  // OUT NULL, also discard
                  Consume()
               ENDIF
            ELSEIF LastToken:Type == XSharpLexer.CASE .and. SELF:IsId(SELF:La1) .and. SELF:La2 == XSharpLexer.AS       // CASE x as SomeType
               VAR start := SELF:Lt1
               VAR id    := SELF:ParseIdentifier()
               VAR type  := SELF:ParseDataType(FALSE)
               SELF:GetSourceInfo(start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR _)
               VAR xVar     := XSourceVariableSymbol{SELF:CurrentEntity, id, range, interval, iif(type == "", _missingType, type)}
               SELF:_locals:Add(xVar)
            ELSE
               Consume()
            ENDIF
         ENDDO
         DO WHILE SELF:La1 == XSharpLexer.EOS
            Consume() // Consume the EOS
         ENDDO

      PRIVATE METHOD ParseStatement() AS VOID
         IF ! SELF:_collectLocals
            SELF:ReadLine()
            RETURN
         ENDIF
         DO WHILE SELF:Eos() .AND. !SELF:Eoi()
             Consume()
         ENDDO
         SELF:ParseUdcTokens()  // Read UDC tokens on the current line
         SWITCH SELF:La1
         CASE XSharpLexer.FIELD
            IF !SELF:ParseFieldStatement()
               SELF:ReadLine()
            ENDIF

         CASE XSharpLexer.FOREACH
         CASE XSharpLexer.FOR
            SELF:Consume()
            IF ! SELF:ParseForLocalDeclaration()
               SELF:ReadLine()
            ENDIF
         CASE XSharpLexer.LOCAL
         CASE XSharpLexer.STATIC
         CASE XSharpLexer.VAR
            IF ! SELF:ParseDeclarationStatement()
               SELF:ReadLine()
            ENDIF
         CASE XSharpLexer.MEMVAR
         CASE XSharpLexer.PARAMETERS
         CASE XSharpLexer.LPARAMETERS
         CASE XSharpLexer.DIMENSION
         CASE XSharpLexer.DECLARE
            IF ! SELF:ParseXBaseDeclarationStatement()
               SELF:ReadLine()
            ENDIF
         CASE XSharpLexer.ID
            IF SELF:LastToken != NULL .AND. (SELF:LastToken:Type == XSharpLexer.PUBLIC .OR. SELF:LastToken:Type == XSharpLexer.PRIVATE)
               SELF:PushBack()
               IF ! SELF:ParseXBaseDeclarationStatement()
                  SELF:ReadLine()
               ENDIF
            ELSE
               SELF:ParseForLocals()
            ENDIF

         OTHERWISE
            SELF:ParseForLocals()
         END SWITCH
         RETURN

      PRIVATE METHOD ParseDeclarationStatement() AS LOGIC
//
//localdecl          : LOCAL (STATIC=STATIC)? LocalVars+=localvar (COMMA LocalVars+=localvar)*			END=eos #commonLocalDecl
//                   | STATIC=STATIC LOCAL    LocalVars+=localvar (COMMA LocalVars+=localvar)*			END=eos #commonLocalDecl
//                   | {!XSharpLexer.IsKeyword(InputStream.La(2))}?   // STATIC Identifier , but not STATIC <Keyword>
//                     STATIC=STATIC          LocalVars+=localvar (COMMA LocalVars+=localvar)*			END=eos #commonLocalDecl
//                   // The following rules allow STATIC in the parser,
//                   // but the treetransformation will produce an error 9044 for STATIC implied
//                   | STATIC=STATIC? VAR           ImpliedVars+=impliedvar (COMMA ImpliedVars+=impliedvar)*  END=eos #varLocalDecl
//                   | STATIC=STATIC LOCAL? IMPLIED ImpliedVars+=impliedvar (COMMA ImpliedVars+=impliedvar)*  END=eos #varLocalDecl

         LOCAL lStatic AS LOGIC
         LOCAL lImplied := FALSE AS LOGIC
         IF SELF:La1 == XSharpLexer.LOCAL
            Consume()
            IF SELF:La1 == XSharpLexer.STATIC
               lStatic := TRUE
               Consume()
            ENDIF
            IF SELF:La1 == XSharpLexer.IMPLIED
               lImplied := TRUE
               Consume()
            ENDIF
            IF SELF:La1 == XSharpLexer.STATIC
               lStatic := TRUE
               Consume()
            ENDIF
            IF SELF:La1 == XSharpLexer.ARRAY .and. _dialect == XSharpDialect.FoxPro
               Consume()
            ENDIF
         ELSEIF SELF:La1 == XSharpLexer.STATIC
            lStatic := TRUE
            Consume()
            IF SELF:La1 == XSharpLexer.LOCAL
               Consume()
            ELSEIF SELF:La1 == XSharpLexer.VAR
               Consume()
               lImplied  := TRUE
            ELSEIF XSharpLexer.IsKeyword(SELF:La1)      // STATIC Keyword should exit
               RETURN FALSE
            ENDIF
            IF SELF:La1 == XSharpLexer.IMPLIED
               lImplied := TRUE
               Consume()
            ENDIF
         ELSEIF SELF:La1 == XSharpLexer.VAR
            Consume()
            lImplied  := TRUE
         ELSE
            RETURN FALSE
         ENDIF
         VAR result := List<XSourceVariableSymbol>{}
         IF lImplied
            VAR xVar := SELF:ParseImpliedVar()
            result:Add(xVar)
            DO WHILE SELF:La1 == XSharpLexer.COMMA
               Consume()
               xVar := SELF:ParseImpliedVar()
               result:Add(xVar)
            ENDDO
         ELSE
            VAR xVar := SELF:ParseLocalVar()
            result:Add(xVar)
            DO WHILE SELF:La1 == XSharpLexer.COMMA
               Consume()
               xVar := SELF:ParseLocalVar()
               result:Add(xVar)
            ENDDO
         ENDIF
         // copy type forward
         VAR missingTypes := List<XSourceVariableSymbol>{}
         FOREACH VAR xVar IN result
            IF xVar:TypeName == XLiterals.NoType
               missingTypes:Add(xVar)
            ELSE
               IF missingTypes:Count > 0
                  FOREACH VAR xMissing IN missingTypes
                     xMissing:TypeName := xVar:TypeName
                  NEXT
                  missingTypes:Clear()
               ENDIF
            ENDIF
            SELF:_locals:Add(xVar)
         NEXT
         FOREACH VAR xMissing IN missingTypes
            xMissing:TypeName := _missingType
         NEXT
         SELF:ReadLine()
         RETURN TRUE

      PRIVATE METHOD ParseLocalVar AS XSourceVariableSymbol
/*
//localvar           : (CONST=CONST)? ( DIM=DIM )? Id=identifier (LBRKT ArraySub=arraysub RBRKT)?
//                     (Op=assignoperator Expression=expression)? (AS=(AS | IS) DataType=datatype)?
//                   ;
*/
         LOCAL lConst   := FALSE AS LOGIC
         LOCAL lDim     := FALSE AS LOGIC
         LOCAL expr     AS IList<XSharpToken>
         LOCAL start    := SELF:Lt1 AS IToken
         IF SELF:La1 == XSharpLexer.CONST
            lConst := TRUE
            Consume()
         ENDIF
         IF SELF:La1 == XSharpLexer.DIM
            lDim := TRUE
            Consume()
         ENDIF
         VAR id := SELF:ParseIdentifier()
         VAR arraysub := SELF:ParseArraySub()
         IF ExpectAssignOp()
            expr       := SELF:ParseExpressionAsTokens()
         ENDIF
         VAR type     := SELF:ParseDataType(TRUE)
         SELF:GetSourceInfo(start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR _)
         IF String.IsNullOrEmpty(type)
            type := XLiterals.NoType
         ENDIF
         VAR xVar     := XSourceVariableSymbol{SELF:CurrentEntity, id, range, interval, type} {IsArray := lDim .OR. !String.IsNullOrEmpty(arraysub)}
         IF type:EndsWith("[]")
            xVar:IsArray := TRUE
         ENDIF
         xVar:Expression := expr
         IF expr != NULL
            var sb := StringBuilder{}
            foreach var token in expr
                sb:Append(token:TextWithTrivia)
            next
            xVar:Value := sb:ToString():Trim()
         endif
         RETURN xVar

      PRIVATE METHOD ParseImpliedVar() AS XSourceVariableSymbol
         // impliedvar: (Const=CONST)? Id=identifier Op=assignoperator Expression=expression
         LOCAL lConst   := FALSE AS LOGIC
         LOCAL lDim     := FALSE AS LOGIC
         LOCAL expr     AS IList<XSharpToken>
         LOCAL start    := SELF:Lt1 AS IToken
         IF SELF:La1 == XSharpLexer.CONST
            lConst := TRUE
            Consume()
         ENDIF
         VAR id := SELF:ParseIdentifier()
         IF ExpectAssignOp()
            expr := SELF:ParseExpressionAsTokens()
         ENDIF
         SELF:GetSourceInfo(start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR _)
         VAR xVar     := XSourceImpliedVariableSymbol{SELF:CurrentEntity, id,  range, interval}
         xVar:Expression := expr
         xVar:ImpliedKind := ImpliedKind.Assignment
         xVar:TypeName    := Self:TokensAsString(expr):Trim()
         RETURN xVar

      PRIVATE METHOD ParseXBaseDeclarationStatement() AS LOGIC
         /*
            xbasedecl           : T=(MEMVAR|PARAMETERS|LPARAMETERS)      // MEMVAR  Foo, Bar or PARAMETERS Foo, Bar
                                  Vars+=identifierName (COMMA Vars+=identifierName)*
                                  end=eos
                                | T=(PRIVATE | PUBLIC)
                                  XVars+=xbasevar (COMMA XVars+=xbasevar)*   // PRIVATE Foo := 123,  PUBLIC Bar, PUBLIC MyArray[5,2]
                                  end=eos
                                // FoxPro dimension statement
                                | T=(DIMENSION|DECLARE) DimVars += dimensionVar (COMMA DimVars+=dimensionVar)*    end=eos
                                ;

            xbasevar            : (Amp=AMP)?  Id=identifierName (LBRKT ArraySub=arraysub RBRKT)? (Op=assignoperator Expression=expression)?
                                ;

            dimensionVar        : Id=identifierName  ( LBRKT ArraySub=arraysub RBRKT | LPAREN ArraySub=arraysub RPAREN ) (AS DataType=datatype)?
                                ;

         */
         SWITCH SELF:La1
         CASE XSharpLexer.MEMVAR
         CASE XSharpLexer.PARAMETERS
         CASE XSharpLexer.LPARAMETERS
               ParseMemvarDeclarationStatement()
         CASE XSharpLexer.PUBLIC
         CASE XSharpLexer.PRIVATE
            ParseMemvarAllocationStatement(SELF:La1)
         CASE XSharpLexer.DIMENSION
         CASE XSharpLexer.DECLARE
            ParseFoxProDim()
         END SWITCH
         RETURN TRUE

      PRIVATE METHOD ParseMemvarDeclarationStatement() AS VOID
            /*
            xbasedecl           : T=(MEMVAR|PARAMETERS|LPARAMETERS)      // MEMVAR  Foo, Bar or PARAMETERS Foo, Bar
                                  Vars+=identifierName (COMMA Vars+=identifierName)*
                                  end=eos
            */
            VAR start := SELF:Lt1
            IF ! ExpectAny(XSharpLexer.MEMVAR, XSharpLexer.PARAMETERS, XSharpLexer.LPARAMETERS)
               RETURN
            ENDIF
            VAR Ids := List<STRING>{}
            IF ! SELF:IsId(SELF:La1)
               RETURN
            ENDIF
            Ids:Add(SELF:ParseIdentifier())
            DO WHILE Expect(XSharpLexer.COMMA)
               Ids:Add(SELF:ParseIdentifier())
            ENDDO
            SELF:GetSourceInfo(start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR _)
            SELF:ReadLine()
            FOREACH VAR id IN Ids
               VAR xVar := XSourceVariableSymbol{SELF:CurrentEntity, id, range, interval,XLiterals.UsualType}
               IF start.Type != XSharpLexer.MEMVAR
                  xVar.Kind := Kind.Parameter
               ELSE
                  xVar.Kind := Kind.MemVar
               ENDIF
               SELF:_locals:Add(xVar)
            NEXT
            RETURN

          PRIVATE METHOD ParseMemvarAllocationStatement(nType as LONG) AS VOID
            /*
                                | T=(PRIVATE | PUBLIC)
                                  XVars+=xbasevar (COMMA XVars+=xbasevar)*   // PRIVATE Foo := 123,  PUBLIC Bar, PUBLIC MyArray[5,2]
                                  end=eos
            */
            VAR start := SELF:_firstTokenOnLine
            IF nType != XSharpLexer.PRIVATE && nType != XSharpLexer.PUBLIC
               RETURN
            ENDIF
            IF SELF:La1 == nType
                Consume()
            ENDIF
            VAR result := List<XSourceVariableSymbol>{}
            VAR xVar := SELF:ParseXBaseVar()
            result:Add(xVar)
            DO WHILE SELF:La1 == XSharpLexer.COMMA
               Consume()
               xVar := SELF:ParseXBaseVar()
               result:Add(xVar)
            ENDDO
            SELF:ReadLine()
            FOREACH VAR mv IN result
               SELF:_locals:Add(mv)
            NEXT
            RETURN

      PRIVATE METHOD ParseXBaseVar AS XSourceVariableSymbol
         /*
         xbasevar            : (Amp=AMP)?  Id=identifierName (LBRKT ArraySub=arraysub RBRKT)? (Op=assignoperator Expression=expression)?
                              ;
         */
         LOCAL lAmp     := FALSE AS LOGIC
         LOCAL expr     AS IList<XSharpToken>
         LOCAL start    := SELF:Lt1 AS IToken
         IF SELF:La1 == XSharpLexer.AMP
            lAmp := TRUE
            Consume()
         ENDIF
         VAR id         := SELF:ParseIdentifier()
         VAR arraysub   := SELF:ParseArraySub()
         IF ExpectAssignOp()
            expr        := SELF:ParseExpressionAsTokens()
         ENDIF
         VAR type       := XLiterals.UsualType
         SELF:GetSourceInfo(start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR _)
         VAR xVar       := XSourceVariableSymbol{SELF:CurrentEntity, id, range, interval, type} {IsArray := !String.IsNullOrEmpty(arraysub)}
         xVar.Kind      := Kind.MemVar
         xVar:Expression := expr
         if expr != NULL
                var sb := StringBuilder{}
                foreach var token in expr
                    sb:Append(token:TextWithTrivia)
                next
                xVar:Value := sb:ToString()
         endif

         RETURN xVar

      PRIVATE METHOD ParseFoxProDim AS VOID
         /*
                                | T=(DIMENSION|DECLARE) DimVars += dimensionVar (COMMA DimVars+=dimensionVar)*    END=eos

            dimensionVar        : Id=identifierName  ( LBRKT ArraySub=arraysub RBRKT | LPAREN ArraySub=arraysub RPAREN ) (AS DataType=datatype)?
                                ;
         */
         IF _dialect == XSharpDialect.FoxPro
            IF ! ExpectAny(XSharpLexer.DIMENSION, XSharpLexer.DECLARE)
               RETURN
            ENDIF
            ParseDimensionVar()
            DO WHILE Expect(XSharpLexer.COMMA)
               ParseDimensionVar()
            ENDDO
         ENDIF
         SELF:ReadLine()
         RETURN

      PRIVATE METHOD ParseDimensionVar() AS VOID
         LOCAL start    := SELF:Lt1 AS IToken
         LOCAL arraysub AS STRING
         VAR id       := SELF:ParseIdentifier()
         IF SELF:La1 == XSharpLexer.LBRKT
            arraysub := SELF:ParseArraySub()
         ELSEIF SELF:La1 == XSharpLexer.LPAREN
            arraysub := SELF:ParseArraySubFox()
         ENDIF
         VAR type := SELF:ParseDataType(FALSE)
         SELF:GetSourceInfo(start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR _)
         IF String.IsNullOrEmpty(type)
            type := "ARRAY"
         ENDIF
         VAR xVar     := XSourceVariableSymbol{SELF:CurrentEntity, id, range, interval, type}
         xVar:Kind    := Kind.MemVar
         IF type != "ARRAY"
            xVar:IsArray := !String.IsNullOrEmpty(arraysub)
         ENDIF
         SELF:_locals:Add(xVar)

      PRIVATE METHOD ParseFieldStatement() AS LOGIC
            /*
               fielddecl          : FIELD Fields+=identifierName (COMMA Fields+=identifierName)* (IN Alias=identifierName)? end=eos
                                  ;

            */
            VAR start := SELF:Lt1
            IF ! Expect(XSharpLexer.FIELD)
               RETURN FALSE
            ENDIF
            VAR Ids := List<STRING>{}
            IF ! SELF:IsId(SELF:La1)
               RETURN FALSE
            ENDIF
            Ids:Add(SELF:ParseIdentifier())
            DO WHILE Expect(XSharpLexer.COMMA)
               Ids:Add(SELF:ParseIdentifier())
            ENDDO
            VAR area := ""
            IF Expect(XSharpLexer.IN)
               area := SELF:ParseIdentifier()
            ENDIF
            SELF:GetSourceInfo(start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR _)
            SELF:ReadLine()
            FOREACH VAR id IN Ids
               VAR xVar := XSourceVariableSymbol{SELF:CurrentEntity, id, range, interval,XLiterals.UsualType}
               xVar.Kind := Kind.DbField
               IF ! String.IsNullOrEmpty(area)
                  xVar:Value := area
               ENDIF
               SELF:_locals:Add(xVar)
            NEXT
            RETURN TRUE

#region FoxPro class definitions
         PRIVATE METHOD ParseFoxClass() AS IList<XSourceEntity>
         /*
         foxclass            : (Attributes=attributes)?
                               D=DEFINE (Modifiers=classModifiers)?
                               CLASS (Namespace=nameDot)? Id=identifier
                               TypeParameters=typeparameters?
                               (AS BaseType=datatype)?
                               (OF Classlib=identifier) ?
                               (ConstraintsClauses+=typeparameterconstraintsclause)*             // Optional typeparameterconstraints for Generic Class
                               (OLEPUBLIC) ?
                               e=eos
                               (Members+=foxclassmember)*
                               (ENDDEFINE | END DEFINE) eos
                             ;

            */
            IF ! Expect(XSharpLexer.DEFINE)
               RETURN NULL
            ENDIF
            VAR mods := SELF:ParseVisibilityAndModifiers()
            IF ! Expect(XSharpLexer.CLASS)
               RETURN NULL
            ENDIF
            VAR id := SELF:ParseQualifiedName()
            VAR typePars := SELF:ParseTypeParameters()
            VAR baseType := ""
            VAR classlib := ""
            IF Expect(XSharpLexer.AS)
               baseType := SELF:ParseTypeName()
            ENDIF
            IF Expect(XSharpLexer.OF)
               classlib := SELF:ParseIdentifier()
            ENDIF
            LOCAL constraints   AS List<STRING>
            DO WHILE SELF:La1 == XSharpLexer.WHERE
               IF constraints == NULL
                  constraints := List<STRING>{}
               ENDIF
               constraints:Add(SELF:ParseTypeParameterConstraints())
            ENDDO
            SELF:GetSourceInfo(_start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR source)
            SELF:ReadLine()
            VAR xType := XSourceTypeSymbol{id, Kind.Class, mods, range, interval, _file}
            xType:SourceCode := source
            IF constraints?:Count > 0
               FOREACH VAR constraint IN constraints
                  xType:AddConstraints(constraint)
               NEXT
            ENDIF
            IF typePars?:Count > 0
               FOREACH VAR typepar IN typePars
                  xType:AddTypeParameter(typepar)
               NEXT
            ENDIF
            xType:ClassType := XSharpDialect.FoxPro
            RETURN <XSourceEntity>{xType}

         PRIVATE METHOD ParseFoxMethod() AS IList<XSourceEntity>
           /*
         foxmethod           : (Attributes=attributes)? (Modifiers=memberModifiers)?
                               T=foxmethodtype  Sig=signature
                               (HelpString=HELPSTRING HelpText=expression)?
                               (ThisAccess=THISACCESS LPAREN MemberId=identifier RPAREN)?
                               end=eos
                               StmtBlk=statementBlock
                               (END T2=foxmethodtype  EOS)?
                               ;
         funcproctype        : Token=(FUNCTION | PROCEDURE)
                              ;

            */
            LOCAL hs := "" AS STRING
            LOCAL thisId := "" AS STRING
            IF ! ParseFuncProcType (OUT VAR kind)
               RETURN NULL
            ENDIF
            VAR sig := SELF:ParseSignature()
            IF Expect(XSharpLexer.HELPSTRING)
               hs  := SELF:ParseExpression()
            ENDIF
            IF Expect(XSharpLexer.THISACCESS) .AND. Expect(XSharpLexer.LPAREN)
               thisId := SELF:ParseIdentifier()
               Expect(XSharpLexer.RPAREN)
            ENDIF
            SELF:GetSourceInfo(_start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR source)
            SELF:ReadLine()
            kind := Kind.Method
            // Check for _ACCESS or _ASSIGN
            VAR id := sig:Id
            IF id:EndsWith("_access", StringComparison.OrdinalIgnoreCase)
               sig:Id   := sig:Id:Substring(0, sig:Id:Length - "_access":Length)
               kind := Kind.Access
            ELSEIF id:EndsWith("_assign", StringComparison.OrdinalIgnoreCase)
               sig:Id   := sig:Id:Substring(0, sig:Id:Length - "_assign":Length)
               kind := Kind.Assign
            ENDIF

            VAR xMember := XSourceMemberSymbol{sig, kind, _attributes, range, interval, _attributes:HasFlag(Modifiers.Static)}
            xMember:SourceCode := source
            xMember:File := SELF:_file
            RETURN <XSourceEntity>{xMember}

         PRIVATE METHOD ParseFoxFields() AS IList<XSourceEntity>
         /*
            foxclassvars        : (Attributes=attributes)? (Modifiers=classvarModifiers)?
                                  (Fld=FIELD)? Vars += identifier (COMMA Vars += identifier )*  (AS DataType=datatype)?
                                  end=eos
                                ;

            foxfield            : (Modifiers=classvarModifiers)? (Fld=FIELD)? F=foxfieldinitializer end=eos
                                ;
            foxfieldinitializer : Name=name assignoperator Expr=expression
                                ;

            foximplementsclause : IMPLEMENTS Type=datatype (Excl=EXCLUDE)? (IN Library=expression)?
                                  end=eos
                                ;

            foxaddobjectclause  : (Attributes=attributes)? ADD OBJECT (Modifiers=classvarModifiers)?
                                  Id=identifier AS Type=datatype (NoInit=NOINIT)?
                                  (WITH FieldsInits += foxfieldinitializer (COMMA FieldsInits += foxfieldinitializer)* )?
                                  end=eos
                                ;

            foxpemcomattrib     : DIMENSION Id=identifier LBRKT expression RBRKT end=eos
                                | Id=identifier LBRKT expression RBRKT assignoperator expression end=eos
                                ;
*/

            IF CurrentType == NULL .OR. CurrentType:ClassType != XSharpDialect.FoxPro
               RETURN NULL
            ENDIF
            VAR typedef := CurrentType
            VAR result := List<XSourceEntity>{}
            // Detect which variant we need
            IF Expect(XSharpLexer.IMPLEMENTS)
               //  foximplementsclause : IMPLEMENTS Type=datatype (Excl=EXCLUDE)? (IN Library=expression)? END=eos
               VAR interf    := SELF:ParseTypeName()
               VAR exclude   := Expect(XSharpLexer.EXCLUDE)
               IF Expect(XSharpLexer.IN)
                  VAR Lib := SELF:ParseExpression()
               ENDIF
               typedef:AddInterface(interf)
               SELF:ReadLine()
               RETURN NULL
            ELSEIF Expect(XSharpLexer.DIMENSION)
               // foxpemcomattrib     : DIMENSION Id=identifier LBRKT expression RBRKT end=eos
               VAR id := SELF:ParseIdentifier()
               VAR expr := ""
               IF ! Expect(XSharpLexer.LBRKT)
                  RETURN NULL
               ENDIF
               expr := SELF:ParseExpression()
               IF !Expect(XSharpLexer.RBRKT)
                  RETURN NULL
               ENDIF
               SELF:GetSourceInfo(_start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR source)
               SELF:ReadLine()
               VAR xMember := XSourceMemberSymbol{id, Kind.Field, _attributes, range, interval,"ARRAY"} {SingleLine := TRUE}
               xMember:SourceCode := source
               xMember:File := SELF:_file
               RETURN <XSourceEntity>{xMember}
            ELSEIF Matches(XSharpLexer.ID, XSharpLexer.FIELD)
               VAR hasField := Expect(XSharpLexer.FIELD)
               IF ! IsId(SELF:La1)
                  RETURN NULL
               ENDIF
               VAR ids      := List<STRING>{}
               ids:Add(ParseIdentifier())
               IF ids:First():EndsWith("COMATTRIB", StringComparison.OrdinalIgnoreCase)
                  RETURN NULL
               ENDIF
               VAR source := ""

               IF ExpectAssignOp()
                     // foxfield            : (Modifiers=classvarModifiers)? (Fld=FIELD)? F=foxfieldinitializer END=eos
                     //                     ;
                     // foxfieldinitializer : Name=name assignoperator Expr=expression
                     //                     ;
                     VAR expr := SELF:ParseExpression()
                     SELF:GetSourceInfo(_start, LastToken, OUT VAR r1, OUT VAR i1, OUT source)
                     SELF:ReadLine()
                     VAR xMember := XSourceMemberSymbol{ids:First(), Kind.Field, _attributes, r1, i1, _missingType} {SingleLine := TRUE}
                     xMember:SourceCode := source
                     xMember:Value      := expr
                     xMember:File := SELF:_file
                     RETURN <XSourceEntity>{xMember}
               ENDIF
               IF Matches(XSharpLexer.COMMA)
                  // foxclassvars        : (Attributes=attributes)? (Modifiers=classvarModifiers)?
                  //                      (Fld=FIELD)? Vars += identifier (COMMA Vars += identifier )*  (AS DataType=datatype)?
                  //                      END=eos
                  DO WHILE Expect(XSharpLexer.COMMA)
                     ids:Add(ParseIdentifier())
                  ENDDO
               ENDIF
               VAR type := SELF:ParseDataType(FALSE)
               SELF:GetSourceInfo(_start, LastToken, OUT VAR range, OUT VAR interval, OUT  source)
               SELF:ReadLine()
               FOREACH VAR id IN ids
                  VAR xMember := XSourceMemberSymbol{id, Kind.Field, _attributes, range, interval, type} {SingleLine := TRUE}
                  IF ids:Count == 1
                     xMember:SourceCode := source
                  ELSEIF String.IsNullOrEmpty(type)
                     xMember:SourceCode := "FIELD "+id
                  ELSE
                     xMember:SourceCode := "FIELD "+id +" AS "+type
                  ENDIF
                  xMember:File := SELF:_file
                  result:Add(xMember)
               NEXT
               RETURN result
            ELSEIF Expect(XSharpLexer.ADD)
               /*
                        foxaddobjectclause  : (Attributes=attributes)? ADD OBJECT (Modifiers=classvarModifiers)?
                                          Id=identifier AS Type=datatype (NoInit=NOINIT)?
                                          (WITH FieldsInits += foxfieldinitializer (COMMA FieldsInits += foxfieldinitializer)* )?
                                          end=eos
                                       ;
               */
               IF ! Expect(XSharpLexer.OBJECT)
                  RETURN NULL
               ENDIF
               VAR mods := SELF:ParseVisibilityAndModifiers()
               VAR id := SELF:ParseIdentifier()
               IF ! Expect(XSharpLexer.AS)
                  RETURN NULL
               ENDIF
               VAR type   := SELF:ParseTypeName()
               VAR noinit := Expect(XSharpLexer.NOINIT)
               // no need to parse the initializers here
               DO WHILE SELF:La1 != XSharpLexer.EOS
                  Consume()
               ENDDO
               SELF:GetSourceInfo(_start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR source)
               SELF:ReadLine()
               VAR xMember := XSourceMemberSymbol{id, Kind.Field, _attributes, range, interval, type} {SingleLine := TRUE}
               xMember:SourceCode := source
               xMember:File := SELF:_file
               RETURN <XSourceEntity>{xMember}
           ENDIF
            // ignore the COMATTRIB rule here
           SELF:ReadLine()
           RETURN NULL
#endregion

#region XPP classes
            PRIVATE METHOD ParseXppClass() AS IList<XSourceEntity>
/*
xppclass           :  (Attributes=attributes)?                                // NEW Optional Attributes
                      (Modifiers=xppclassModifiers)?                          // [STATIC|FREEZE|FINAL]
                       C=CLASS (Namespace=nameDot)? Id=identifier               // CLASS <ClassName>
                       (
                          From=(FROM| SHARING) BaseTypes+=datatype (COMMA BaseTypes+=datatype)*  // [FROM <SuperClass,...>] ;
                       )?                                                                   // [SHARING <SuperClass,...>]
                       (IMPLEMENTS Implements+=datatype (COMMA Implements+=datatype)*)? // NEW Implements
                       // No type parameters and type parameter constraints
                      e=eos
                      (Members+=xppclassMember)*
                      ENDCLASS
                      eos
                    ;
xppclassModifiers   : ( Tokens+=(STATIC | FREEZE | FINAL) )+
                    ; // make sure all tokens are also in the IsModifier method inside XSharpLexerCode.cs

xppclassMember      : Member=xppmethodvis                           #xppclsvisibility
                    | Member=xppclassvars                           #xppclsvars
                    | Member=xppinlineMethod                        #xppclsinlinemethod
                    | Member=xppdeclareMethod                       #xppclsdeclaremethod
                    | Member=xppproperty                            #xppclsproperty
                    | Member=pragma                                 #xpppragma
                    ;
*/
            IF ! Expect(XSharpLexer.CLASS)
               RETURN NULL
            ENDIF
            VAR id         := SELF:ParseQualifiedName()
            VAR baseType   := ""
            IF SELF:ExpectAny(XSharpLexer.FROM, XSharpLexer.SHARING)
               baseType  := SELF:ParseTypeName()
               DO WHILE SELF:Expect(XSharpLexer.COMMA)
                  // consume the other type declarations , ignore them because we do not support multiple inheritance
                  ParseTypeName()
               ENDDO
            ENDIF
            VAR interfaces := List<STRING>{}
            IF SELF:Expect(XSharpLexer.IMPLEMENTS)
               interfaces:Add(SELF:ParseTypeName())
               DO WHILE SELF:Expect(XSharpLexer.COMMA)
                  interfaces:Add(SELF:ParseTypeName())
               ENDDO
            ENDIF
            SELF:GetSourceInfo(_start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR source)
            SELF:ReadLine()

            VAR xType := XSourceTypeSymbol{id,Kind.Class, _attributes, range, interval, _file}
            xType:SourceCode := source

            IF interfaces?:Count > 0
               FOREACH VAR sInterface IN interfaces
                  xType:AddInterface(sInterface)
               NEXT
            ENDIF
            IF ! String.IsNullOrEmpty(baseType)
               xType:BaseTypeName := baseType
            ENDIF
            xType:IsPartial := _attributes:HasFlag(Modifiers.Partial)
            IF CurrentEntity != _globalType .AND. CurrentEntityKind:HasChildren()
               xType:Parent := SELF:CurrentEntity
            ENDIF
            xType:ClassType := XSharpDialect.XPP
            _xppVisibility := Modifiers.Public
            RETURN <XSourceEntity>{xType}

            PRIVATE METHOD ParseXppClassVars() AS IList<XSourceEntity>
                     /*

                     xppclassvars        : (Modifiers=xppmemberModifiers)?                             // [CLASS]
                                           VAR Vars+=identifier                                        // VAR <VarName>
                                           (
                                             Is=xppisin                                                // [IS <Name>] [IN <SuperClass>]
                                             | ((COMMA Vars+=identifier)*                              // <,...>
                                             (AS DataType=datatype)?  )                                // Optional data type

                                           )
                                           (Shared=SHARED)?                                            // [SHARED]
                                           (ReadOnly=READONLY)?                                        // [READONLY]
                                           (Assignment=xppvarassignment)?                              // [ASSIGNMENT HIDDEN | PROTECTED | EXPORTED]
                                           (Nosave= NOSAVE)?                                           // [NOSAVE]
                                           eos
                                         ;

                     xppvarassignment    : ASSIGNMENT xppvisibility                                    // [ASSIGNMENT HIDDEN | PROTECTED | EXPORTED]
                                         ;
                     */
               VAR isStatic := SELF:ParseXPPMemberModifiers()
               IF ! SELF:Expect(XSharpLexer.VAR)
                  RETURN NULL
               ENDIF
               LOCAL type AS STRING
               VAR ids := List<STRING>{}
               IF SELF:IsId(SELF:La1)
                  ids:Add(SELF:ParseIdentifier())
               ELSE
                  RETURN NULL
               ENDIF
               IF SELF:Matches(XSharpLexer.IN,XSharpLexer.IS)
                  SELF:ParseXppIsIn()     // Parse but ignore
               ELSE
                  DO WHILE SELF:Expect(XSharpLexer.COMMA)
                     ids:Add(SELF:ParseIdentifier())
                  ENDDO
                  type := SELF:ParseDataType(FALSE)
               ENDIF
               SELF:GetSourceInfo(_start, SELF:LastToken, OUT VAR range, OUT VAR interval, OUT VAR _)
               // eat all tokens
               DO WHILE ExpectAny(XSharpLexer.SHARED, XSharpLexer.READONLY, XSharpLexer.ASSIGNMENT, ;
                                  XSharpLexer.HIDDEN, XSharpLexer.PROTECTED, XSharpLexer.EXPORTED, XSharpLexer.NOSAVE)
                  NOP
               ENDDO
               SELF:ReadLine()
               VAR result := List<XSourceEntity>{}
               FOREACH VAR id IN ids
                  VAR classvar := XSourceMemberSymbol{id, Kind.Field, _xppVisibility, range, interval, type, FALSE} {SingleLine := TRUE}
                  classvar:SourceCode := ie"{classvar.ModVis}: \r\n{ IIF(isStatic, \"STATIC\",\"\")} VAR {classvar.Name}"
                  IF SELF:CurrentType != NULL
                     SELF:CurrentType:AddMember(classvar)
                  ENDIF
                  result:Add(classvar)
               NEXT
               RETURN result

               PRIVATE METHOD ParseXppProperty AS IList<XSourceEntity>
                  /*
                  xppproperty         : (Attributes=attributes)?                                    // NEW Optional Attributes
                                        (   Access=ACCESS Assign=ASSIGN?                            // ACCESS | ASSIGN  | ACCESS ASSIGN | ASSIGN ACCESS
                                          | ASSIGN=ASSIGN Access=ACCESS?
                                        )
                                        Modifiers=xppmemberModifiers?                               // [CLASS]
                                        M=METHOD Id=identifier                                        // METHOD <MethodName>
                                        (VAR VarName=identifier)?                                   // [VAR <VarName>]
                                        (AS Type=datatype)?                                         // NEW Optional data type
                                        end=eos
                                      ;
                     */
                  VAR la1 := SELF:La1
                  VAR la2 := SELF:La2
                  IF ! Matches(XSharpLexer.ACCESS, XSharpLexer.ASSIGN)
                     RETURN NULL
                  ENDIF
                  DO WHILE ExpectAny(XSharpLexer.ACCESS, XSharpLexer.ASSIGN)
                     NOP
                  ENDDO
                  VAR isStatic := SELF:ParseXPPMemberModifiers()
                  IF ! Expect(XSharpLexer.METHOD)
                     RETURN NULL
                  ENDIF
                  VAR id := SELF:ParseIdentifier()
                  VAR propName := ""
                  IF SELF:Expect(XSharpLexer.VAR)
                     propName := SELF:ParseIdentifier()
                  ENDIF
                  VAR type := SELF:ParseDataType(FALSE)
                  SELF:GetSourceInfo(_start, SELF:LastToken, OUT VAR range, OUT VAR interval, OUT VAR source)
                  SELF:ReadLine()
                  VAR xmember := XSourceMemberSymbol{id+XLiterals.XppDeclaration, Kind.Property, _OR(_xppVisibility, _attributes), range, interval, type, isStatic}
                  xmember:SourceCode := source
                  IF SELF:CurrentType != NULL
                     SELF:CurrentType:AddMember(xmember)
                  ENDIF
                  xmember:SingleLine := TRUE
                  RETURN <XSourceEntity> {xmember}

      PRIVATE METHOD ParseXppMethodDeclaration() AS IList<XSourceEntity>
         /*
         xppdeclareMethod    : (Modifiers=xppdeclareModifiers)?                            // [DEFERRED |FINAL | INTRODUCE | OVERRIDE] [CLASS]
                               METHOD Methods+=identifier                                   // METHOD <MethodName,...>
                               (
                                 xppisin                                                   //  [IS <Name>] [IN <SuperClass>]
                                 | (COMMA Methods+=identifier)*                             // or an optional comma seperated list of other names
                               )
                               eos
                             ;
         xppdeclareModifiers : ( Tokens+=( DEFERRED | FINAL | INTRODUCE | OVERRIDE | CLASS | SYNC ) )+
                             ; // make sure all tokens are also in the IsModifier method inside XSharpLexerCode.cs

         */
         VAR la1      := SELF:La1
         VAR isStatic :=  Expect(XSharpLexer.CLASS)
         IF ! SELF:Expect(XSharpLexer.METHOD)
            RETURN NULL
         ENDIF
         VAR ids := List<STRING>{}
         IF SELF:IsId(SELF:La1)
            ids:Add(SELF:ParseIdentifier())
         ELSE
            RETURN NULL
         ENDIF
         IF SELF:Matches(XSharpLexer.IN,XSharpLexer.IS)
            SELF:ParseXppIsIn()     // Parse but ignore
         ELSE
            DO WHILE SELF:Expect(XSharpLexer.COMMA)
               ids:Add(SELF:ParseIdentifier())
            ENDDO
         ENDIF
         SELF:GetSourceInfo(_start, SELF:LastToken, OUT VAR range, OUT VAR interval, OUT VAR _)
         VAR result := List<XSourceEntity>{}
         FOREACH VAR id IN ids
            VAR xmethod := XSourceMemberSymbol{id+XLiterals.XppDeclaration, Kind.Method, _xppVisibility, range, interval, "", FALSE}
            xmethod:SourceCode := ie"{xmethod.ModVis}: \r\n{ IIF(isStatic, \"STATIC\",\"\")} METHOD {xmethod.Name}"
            IF SELF:CurrentType != NULL
               SELF:CurrentType:AddMember(xmethod)
            ENDIF
            xmethod:SingleLine := TRUE
            result:Add(xmethod)
         NEXT
         RETURN result

       PRIVATE METHOD ParseXppVisibility() AS IList<XSourceEntity>
            /*
            xppmethodvis        : Vis=xppvisibility COLON eos
                                 ;

            xppvisibility       : Token=(HIDDEN | PROTECTED | EXPORTED | INTERNAL | PUBLIC | PRIVATE )
                                 ;

            */
            // the visibility is already parsed as 'start of entity'
            //we're now at the COLON token
            VAR result := SELF:Expect(XSharpLexer.COLON)
            IF result
               SELF:ReadLine()
            ENDIF
            SELF:_xppVisibility := _attributes
            RETURN NULL

       PRIVATE METHOD ParseXppIsIn() AS VOID
            /*
               xppisin             : IS Id=identifier (IN SuperClass=identifier)?                //  IS <Name> [IN <SuperClass>]
                                   | IN SuperClass=identifier								             //  IN <SuperClass> without IS clause
                                   ;
            */
            // we parse this but the feature is not supported.
            LOCAL id, superclass AS STRING
            id := superclass := ""
            IF Expect(XSharpLexer.IS)
               id := SELF:ParseIdentifier()
            ENDIF
            IF Expect(XSharpLexer.IN)
               superclass := SELF:ParseIdentifier()
            ENDIF
            RETURN

       PRIVATE METHOD ParseXppMethod() AS IList<XSourceEntity>
            // detect if we're inside the class definition
            IF SELF:La1 == XSharpLexer.INLINE
               RETURN SELF:ParseXppMethodInLine()
            ELSEIF SELF:InXppClass
               RETURN SELF:ParseXppMethodDeclaration()
            ELSE
               RETURN SELF:ParseXppMethodImplementation()
            ENDIF

       PRIVATE METHOD ParseXppMethodInLine() AS IList<XSourceEntity>
            /*
               xppinlineMethod     : (Attributes=attributes)?                               // NEW Optional Attributes
                                     I=INLINE
                                     (Modifiers=xppmemberModifiers)?                        // [CLASS]
                                     METHOD  Id=identifier                                  // METHOD <MethodName>
                                     // no type parameters
                                     (ParamList=parameterList)?                            // Optional Parameters
                                     (AS Type=datatype)?                                   // NEW Optional return type
                                     // no type constraints
                                     (UDCSEP ExpressionBody=expression)?                   // New: Expression Body
                                     end=eos
                                     StmtBlk=statementBlock
                                     (END METHOD eos)?
                                   ;

               xppmemberModifiers  : ( Tokens+=( CLASS | STATIC) )+
                                   ; // make sure all tokens are also in the IsModifier method inside XSharpLexerCode.cs
            */

            IF ! SELF:Expect(XSharpLexer.INLINE)
               RETURN NULL
            ENDIF
            VAR isStatic := SELF:ParseXPPMemberModifiers()
            IF ! SELF:Expect(XSharpLexer.METHOD)
               RETURN NULL
            ENDIF
            VAR id      := SELF:ParseIdentifier()
            VAR aParams := SELF:ParseParameterList(FALSE, OUT VAR _)
            VAR type    := ""
            IF SELF:La1 == XSharpLexer.AS
               type := SELF:ParseDataType(FALSE)
            ENDIF
            SELF:GetSourceInfo(_start, SELF:LastToken, OUT VAR range, OUT VAR interval, OUT VAR source)
            SELF:ReadLine()
            VAR xmethod := XSourceMemberSymbol{id, Kind.Method, _xppVisibility, range, interval, type, isStatic}
            xmethod:SourceCode := source
            IF SELF:CurrentType != NULL
               SELF:CurrentType:AddMember(xmethod)
            ENDIF
            IF aParams != NULL
               FOREACH VAR par IN aParams
                  xmethod:AddParameter(par)
               NEXT
            ENDIF
            RETURN <XSourceEntity>{xmethod}

       PRIVATE METHOD ParseXPPMemberModifiers() AS LOGIC
            RETURN SELF:ExpectAny(XSharpLexer.CLASS, XSharpLexer.STATIC)

      PRIVATE METHOD ParseXppMethodImplementation() AS IList<XSourceEntity>
            /*
               xppmethod           : (Attributes=attributes)?                              // NEW Optional Attributes
                                     (MethodType=(ACCESS|ASSIGN))?                         // Optional Access or Assign
                                     (Modifiers=xppmemberModifiers)?                       // [CLASS]
                                     M=METHOD (ClassId=identifier COLON)? Id=identifier    // [<ClassName>:] <MethodName>
                                     // no type parameters
                                     (ParamList=parameterList)?                            // Optional Parameters
                                     (AS Type=datatype)?                                   // NEW Optional return type
                                     // no type constraints
                                     // no calling convention
                                     (UDCSEP ExpressionBody=expression)?                   // New: Expression Body
                                     END=eos
                                     StmtBlk=statementBlock
                                     (END METHOD eos)?
                                   ;

            */
            VAR kind := Kind.Method
            IF ExpectAny(XSharpLexer.ACCESS)
               kind := Kind.Access
            ELSEIF ExpectAny(XSharpLexer.ASSIGN)
               kind := Kind.Assign
            ENDIF
            VAR isStatic := SELF:ParseXPPMemberModifiers()
            IF ! SELF:Expect(XSharpLexer.METHOD)
               RETURN NULL
            ENDIF
            VAR classprefix := ""
            IF SELF:La1 == XSharpLexer.ID .AND. SELF:La2 == XSharpLexer.COLON
               classprefix := SELF:ParseIdentifier()
               Expect(XSharpLexer.COLON)
            ENDIF
            VAR id      := SELF:ParseIdentifier()
            VAR aParams := SELF:ParseParameterList(FALSE, OUT VAR _)
            VAR type    := ""
            IF SELF:La1 == XSharpLexer.AS
               type := SELF:ParseDataType(FALSE)
            ENDIF
            SELF:GetSourceInfo(_start, SELF:LastToken, OUT VAR range, OUT VAR interval, OUT VAR source)
            SELF:ReadLine()
            VAR xmethod := XSourceMemberSymbol{id, kind, Modifiers.None, range, interval, type, isStatic}
            // we need to lookup the visibility in the types list
            xmethod:SourceCode := source
            // find the parent in the stack. Either the last one (when the classprefix is empty) or a specific one
            // then find the declared member in this type
            VAR classdef := SELF:FindXPPClass(classprefix)
            IF classdef != NULL
               classdef:AddMember(xmethod)
               FOREACH m AS XSourceMemberSymbol IN classdef:Members
                  IF m:Name == xmethod:Name+XLiterals.XppDeclaration
                     xmethod:Attributes := m:Attributes
                     EXIT
                  ENDIF
               NEXT
            ELSE
               IF SELF:CurrentType != NULL
                  SELF:CurrentType:AddMember(xmethod)
               ENDIF
            ENDIF

            IF aParams != NULL
               FOREACH VAR par IN aParams
                  xmethod:AddParameter(par)
               NEXT
            ENDIF
            RETURN <XSourceEntity>{xmethod}

         PRIVATE METHOD FindXPPClass(sName AS STRING) AS XSourceTypeSymbol
            VAR start := SELF:_EntityList:Count-1
            FOR VAR i := start DOWNTO 0
               VAR element := _EntityList[i]
               IF element IS XSourceTypeSymbol VAR typedef
                  IF typedef:ClassType == XSharpDialect.XPP
                     IF String.IsNullOrEmpty(sName)
                        RETURN typedef
                     ELSEIF String.Compare(typedef:Name, sName, TRUE) == 0
                        RETURN typedef
                     ENDIF
                  ENDIF
               ENDIF
            NEXT
            RETURN NULL

#endregion

      PRIVATE METHOD IsKeywordXs(token AS LONG) AS LOGIC
         SWITCH token
         CASE XSharpLexer.AUTO
         CASE XSharpLexer.CHAR
         CASE XSharpLexer.CONST
         CASE XSharpLexer.DEFAULT
         CASE XSharpLexer.GET
         CASE XSharpLexer.IMPLEMENTS
         CASE XSharpLexer.NEW
         CASE XSharpLexer.OUT
         CASE XSharpLexer.REF
         CASE XSharpLexer.SET
         CASE XSharpLexer.VALUE
         CASE XSharpLexer.VIRTUAL
         CASE XSharpLexer.INTERNAL
         CASE XSharpLexer.ADD
         CASE XSharpLexer.ARGLIST
         CASE XSharpLexer.ASCENDING
         CASE XSharpLexer.ASTYPE
         CASE XSharpLexer.ASYNC
         CASE XSharpLexer.AWAIT
         CASE XSharpLexer.BY
         CASE XSharpLexer.CHECKED
         CASE XSharpLexer.DESCENDING
         CASE XSharpLexer.DYNAMIC
         CASE XSharpLexer.EQUALS
         CASE XSharpLexer.EXTERN
         CASE XSharpLexer.FIXED
         CASE XSharpLexer.FROM
         CASE XSharpLexer.GROUP
         CASE XSharpLexer.INTO
         CASE XSharpLexer.JOIN
         CASE XSharpLexer.LET
         CASE XSharpLexer.NAMEOF
         CASE XSharpLexer.OF
         CASE XSharpLexer.ON
         CASE XSharpLexer.ORDERBY
         CASE XSharpLexer.OVERRIDE
         CASE XSharpLexer.PARAMS
         CASE XSharpLexer.REMOVE
         CASE XSharpLexer.SELECT
         CASE XSharpLexer.UNCHECKED
         CASE XSharpLexer.VAR
         CASE XSharpLexer.VOLATILE
         CASE XSharpLexer.WHERE
         CASE XSharpLexer.CURRENCY
         CASE XSharpLexer.DECIMAL
         CASE XSharpLexer.DATETIME
               // Added as XS keywords to allow them to be treated as IDs
            // the following entity keywords will be never used 'alone' and can therefore be safely defined as identifiers
         CASE XSharpLexer.DELEGATE
         CASE XSharpLexer.ENUM
         CASE XSharpLexer.GLOBAL
         CASE XSharpLexer.INHERIT
         CASE XSharpLexer.STRUCTURE
            // The following 'old' keywords are never used 'alone' and are harmless as identifiers
         CASE XSharpLexer.ALIGN
         CASE XSharpLexer.CALLBACK
         CASE XSharpLexer.CLIPPER
         CASE XSharpLexer.DIM
         CASE XSharpLexer.DOWNTO
         CASE XSharpLexer.DLLEXPORT
         CASE XSharpLexer.FASTCALL
         CASE XSharpLexer.IN
         CASE XSharpLexer.INIT1
         CASE XSharpLexer.INIT2
         CASE XSharpLexer.INIT3
         CASE XSharpLexer.INSTANCE
         CASE XSharpLexer.PASCAL
         CASE XSharpLexer.SEQUENCE
         CASE XSharpLexer.STEP
         CASE XSharpLexer.STRICT
         CASE XSharpLexer.TO
         CASE XSharpLexer.THISCALL
         CASE XSharpLexer.UPTO
         CASE XSharpLexer.USING
         CASE XSharpLexer.WINCALL
               // The following keywords are handled in the fixPositionalKeyword() method of the lexer and will only be keywords at the right place
               // but when they code event->(DoSomething()) we still need them in this rule...
               //        CASE XSharpLexer.DEFINE
               //        CASE XSharpLexer.TRY
               //        CASE XSharpLexer.SWITCH
               //        CASE XSharpLexer.EVENT
               //        CASE XSharpLexer.EXPLICIT
               //        CASE XSharpLexer.FOREACH
               //        CASE XSharpLexer.UNTIL
               //        CASE XSharpLexer.PARAMETERS
               //        CASE XSharpLexer.YIELD
               //        CASE XSharpLexer.MEMVAR
               //        CASE XSharpLexer.NOP
               //        CASE XSharpLexer.PARTIAL
               //        CASE XSharpLexer.SEALED
               //        CASE XSharpLexer.ABSTRACT
               //        CASE XSharpLexer.UNSAFE
               //        CASE XSharpLexer.SCOPE
               //        CASE XSharpLexer.NAMESPACE
               //        CASE XSharpLexer.LOCK
               //        CASE XSharpLexer.IMPLICIT
               //        CASE XSharpLexer.IMPLIED
               //        CASE XSharpLexer.INITONLY
               //        CASE XSharpLexer.PROPERTY
               //        CASE XSharpLexer.INTERFACE
               //        CASE XSharpLexer.VOSTRUCT
               //        CASE XSharpLexer.UNION
               //        CASE XSharpLexer.DECLARE
               //        CASE XSharpLexer.OPERATOR
            RETURN TRUE
         END SWITCH
         // context sensitive keywords
         // ENDCLASS, FREEZE, FINAL, INTRODUCE, SYNC, DEFERRED, INLINE

         RETURN FALSE

      PRIVATE METHOD IsKeywordXpp(token AS LONG) AS LOGIC
        if _dialect != XSharpDialect.XPP
                RETURN FALSE
        ENDIF
        SWITCH token
        CASE XSharpLexer.SHARING
        CASE XSharpLexer.SHARED
        CASE XSharpLexer.ASSIGNMENT
        CASE XSharpLexer.EXPORTED
        CASE XSharpLexer.READONLY
        CASE XSharpLexer.NOSAVE
        RETURN TRUE
        END SWITCH
         RETURN FALSE

      PRIVATE METHOD IsKeywordFox(token AS LONG) AS LOGIC
        if _dialect != XSharpDialect.FoxPro
                RETURN FALSE
        ENDIF
         SWITCH token
         CASE XSharpLexer.OLEPUBLIC
         CASE XSharpLexer.EACH
         CASE XSharpLexer.EXCLUDE
         CASE XSharpLexer.THISACCESS
         CASE XSharpLexer.HELPSTRING
         CASE XSharpLexer.NOINIT
         CASE XSharpLexer.FOX_AND
         CASE XSharpLexer.FOX_OR
         CASE XSharpLexer.FOX_NOT
         CASE XSharpLexer.FOX_XOR
         CASE XSharpLexer.THEN
            RETURN TRUE
         END SWITCH
         // These tokens are already marked as 'only valid in a certain context ' in the lexer
         // ENDDEFINE | TEXT| ENDTEXT | DIMENSION | LPARAMETERS | NOSHOW | TEXTMERGE | PRETEXT | FLAGS | ADDITIVE

         RETURN FALSE

      PRIVATE STATIC METHOD GetText(SELF token AS IToken) AS STRING
         VAR result := token:Text
         IF result:StartsWith("@@")
            result := result:Substring(2)
         ENDIF
         RETURN result

      PRIVATE METHOD GetSourceInfo(startToken AS IToken, endToken AS IToken, range OUT TextRange, interval OUT TextInterval, source OUT STRING) AS VOID
            range    := TextRange{startToken, endToken}
            interval := TextInterval{startToken, endToken}
            source   := GetSource(startToken, endToken)
            RETURN

      PRIVATE METHOD GetSource (startToken AS IToken, endToken AS IToken) AS STRING
         VAR startIndex := ((XSharpToken) startToken):OriginalTokenIndex
         VAR stopIndex  := ((XSharpToken)   endToken):OriginalTokenIndex
         VAR sb         := StringBuilder{}
         FOR VAR i := startIndex TO stopIndex
            sb:Append(_tokens[i].Text)
         NEXT
         RETURN sb:ToString()

      PRIVATE STATIC METHOD Log(cMessage AS STRING) AS VOID
         IF XSettings.EnableParseLog
            XSolution.WriteOutputMessage("XParser: "+cMessage)
         ENDIF
         RETURN
   END CLASS

   DELEGATE DelEndToken(iToken AS LONG) AS LOGIC

END NAMESPACE

//
// Copyright (c) XSharp B.V.  All Rights Reserved.
// Licensed under the Apache License, Version 2.0.
// See License.txt in the project root for license information.
//

// Note that the comment blocks from the various rules have been copied from XSharp.g4 inside the compiler

USING System.Collections.Generic
USING System.Collections
USING System.Text
USING System.Text.RegularExpressions
USING System.IO
USING System.Diagnostics
USING System.Linq
USING LanguageService.CodeAnalysis.XSharp
USING LanguageService.SyntaxTree
USING LanguageService.CodeAnalysis.XSharp.SyntaxParser
USING XSharp.Parser
USING LanguageService.CodeAnalysis.Text

BEGIN NAMESPACE XSharpModel

   CLASS XsParser IMPLEMENTS VsParser.IErrorListener
      PRIVATE  _stream       AS BufferedTokenStream
      PRIVATE  _list         AS XSharpTokenList
      PRIVATE  _file         AS XFile
      PRIVATE  _usings       AS IList<STRING>
      PRIVATE  _staticusings AS IList<STRING>
      PRIVATE  _EntityList    AS IList<XSourceEntity>
      PRIVATE  _EntityStack   AS Stack<XSourceEntity>
      PRIVATE  _BlockList     AS IList<XSourceBlock>
      PRIVATE  _BlockStack    AS Stack<XSourceBlock>
      PRIVATE  _PPBlockStack  AS Stack<XSourceBlock>
      PRIVATE  _locals        AS IList<XSourceVariableSymbol>
      PRIVATE  _collectLocals AS LOGIC
      PRIVATE  _collectBlocks AS LOGIC
      PRIVATE  _errors        AS IList<XError>
      PRIVATE  _globalType    AS XSourceTypeSymbol
      PRIVATE  _dialect       AS XSharpDialect
      PRIVATE  _xppVisibility AS Modifiers
      PRIVATE  _commentTasks  AS IList<XCommentTask>

      PRIVATE  _attributes   AS Modifiers      // for the current entity
      PRIVATE  _start        AS IToken
      PRIVATE  _hasXmlDoc    AS LOGIC
      PRIVATE  _tokens       AS IList<IToken>
      PRIVATE  _firstTokenOnLine as IToken
      PRIVATE  _missingType  AS STRING

      PRIVATE PROPERTY CurrentEntity      AS XSourceEntity GET IIF(_EntityStack:Count > 0, _EntityStack:Peek(), NULL_OBJECT)
      PRIVATE PROPERTY CurrentType        AS XSourceTypeSymbol
         GET
            VAR aStack := _EntityStack:ToArray()
            FOREACH VAR item IN aStack
               IF item IS XSourceTypeSymbol VAR type
                  RETURN type
               ENDIF
            NEXT
            RETURN NULL
         END GET
      END PROPERTY
      PRIVATE PROPERTY CurrentBlock       AS XSourceBlock   GET IIF(_BlockStack:Count > 0, _BlockStack:Peek(), NULL_OBJECT)
      PRIVATE PROPERTY CurrentEntityKind  AS Kind     GET IIF(_EntityStack:Count > 0, CurrentEntity:Kind, Kind.Unknown)
      PRIVATE PROPERTY InFoxClass AS LOGIC GET CurrentType != NULL .AND. CurrentType:ClassType == XSharpDialect.FoxPro
      PRIVATE PROPERTY InXppClass AS LOGIC GET CurrentType != NULL .AND. CurrentType:ClassType == XSharpDialect.XPP
      PROPERTY EntityList AS IList<XSourceEntity>  GET _EntityList
      PROPERTY BlockList  AS IList<XSourceBlock>    GET _BlockList
      PROPERTY Locals     AS IList<XSourceVariableSymbol> GET _locals
      PROPERTY SaveToDisk AS LOGIC AUTO

      CONSTRUCTOR(oFile AS XFile, dialect AS XSharpDialect)
          SELF:SaveToDisk := TRUE
         _errors        := List<XError>{}
         _usings        := List<STRING>{}
         _commentTasks  := List<XCommentTask>{}
         _staticusings  := List<STRING>{}
         _EntityList    := List<XSourceEntity>{}
         _EntityStack   := Stack<XSourceEntity>{}
         _BlockList     := List<XSourceBlock>{}
         _BlockStack    := Stack<XSourceBlock>{}
         _PPBlockStack  := Stack<XSourceBlock>{}
         _file          := oFile
         _dialect       := dialect
         _locals        := List<XSourceVariableSymbol>{}
         _file:Clear()
         _globalType    := _file:GlobalType
         _globalType:ClearMembers()
         _EntityStack:Push(_globalType)
         _missingType := XLiterals.ObjectType
          IF SELF:_file:Project != NULL .and. SELF:_file:Project:ParseOptions:Dialect != XSharpDialect.Core
             _missingType := XLiterals.UsualType
          ENDIF

      #region IErrorListener
      METHOD ReportError(fileName AS STRING, span AS LinePositionSpan, errorCode AS STRING, message AS STRING, args AS OBJECT[]) AS VOID
         SELF:_errors:Add(XError{fileName, span, errorCode, message, args})

      METHOD ReportWarning(fileName AS STRING, span AS LinePositionSpan, errorCode AS STRING, message AS STRING, args AS OBJECT[]) AS VOID
         SELF:_errors:Add(XWarning{fileName, span, errorCode, message, args})
      #endregion

      METHOD Parse(lBlocks AS LOGIC, lLocals AS LOGIC) AS VOID
         VAR cSource  := System.IO.File.ReadAllText(_file:SourcePath)
         VAR options  := XSharpParseOptions.Default
         XSharp.Parser.VsParser.Lex(cSource, SELF:_file:SourcePath, options, SELF, OUT VAR stream, OUT VAR includeFiles)
         SELF:Parse(stream, lBlocks, lLocals)
         RETURN

      METHOD AddCommentLine(comment AS STRING, token AS XSharpToken, cmtToken AS XCommentToken) AS VOID
        comment := comment:Trim()
        IF comment:IndexOf(cmtToken:Text, StringComparison.OrdinalIgnoreCase) == 0 .AND. ;
            comment:Length > cmtToken:Text:Length
            VAR nextchar := comment[cmtToken:Text:Length]
            IF ! Char.IsLetterOrDigit(nextchar)
                VAR item := XCommentTask{}{ File:=_file, Line := token:Line, Column := token:Column, Priority := cmtToken:Priority, Comment := comment}
                _commentTasks:Add(item)
            ENDIF
        ENDIF

      METHOD Parse( tokenStream AS ITokenStream, lBlocks AS LOGIC, lLocals AS LOGIC) AS VOID
         LOCAL aAttribs        AS IList<XSharpToken>
         LOCAL cXmlDoc   := "" AS STRING
         VAR cmtTokens  := XSolution.CommentTokens
         Log(i"Start")

         _collectLocals := lLocals
         _collectBlocks := lBlocks
         _stream        := (BufferedTokenStream) tokenStream
         _tokens        := _stream:GetTokens()
         VAR _input     := List<XSharpToken>{}
         FOREACH token AS XSharpToken IN _tokens
            SWITCH token:Channel
            CASE TokenConstants.HiddenChannel
            CASE XSharpLexer.DEFOUTCHANNEL // Inactive code
               IF XSharpLexer.IsComment(token:Type)
                  FOREACH VAR cmtToken IN cmtTokens
                     VAR pos := token:Text:IndexOf(cmtToken:Text, StringComparison.OrdinalIgnoreCase)
                     VAR include := FALSE
                     IF pos >= 0
                        IF token:Type == XSharpLexer.SL_COMMENT
                            VAR comment := token:Text:Substring(2):Trim()
                            AddCommentLine(comment, token, cmtToken)
                         ELSEIF token:Type == XSharpLexer.ML_COMMENT
                            VAR comment := token:Text:Substring(2, token:Text:Length-4)
                            VAR lines :=comment:Split(<CHAR>{'\r','\n'}, StringSplitOptions.RemoveEmptyEntries)
                            FOREACH VAR line IN lines
                                AddCommentLine(line, token, cmtToken)
                            NEXT
                        ENDIF
                     ENDIF
                  NEXT
               ENDIF
             CASE XSharpLexer.DefaultTokenChannel
             CASE XSharpLexer.PREPROCESSORCHANNEL
               _input:Add(token)
             CASE XSharpLexer.XMLDOCCHANNEL
               _hasXmlDoc := TRUE
             OTHERWISE
               NOP
             END SWITCH
         NEXT
         _file:CommentTasks := _commentTasks
         _list :=XSharpTokenList{_input}

         DO WHILE ! SELF:Eoi()
            VAR tokenBefore := LastToken
            _firstTokenOnLine := SELF:Lt1
            LOCAL first := (XSharpToken) SELF:Lt1  AS XSharpToken
            IF ParsePPLine()
               LOOP
            ENDIF
            IF SELF:ParseUsing()
               LOOP
            ENDIF
            LOCAL startOfTrivia := -1 as LONG
            SELF:ParseUdcTokens()
            aAttribs := SELF:ParseAttributes()
            VAR mods := SELF:ParseVisibilityAndModifiers()
            VAR vis  := _AND(mods, Modifiers.VisibilityMask)
            IF IsStartOfEntity(OUT VAR entityKind, mods)
               IF _hasXmlDoc
                  LOCAL cDoc := first:XmlComments AS STRING
                  if first:HasTrivia
                     foreach Var triv in first:Trivia
                        if triv:Type == XSharpLexer.DOC_COMMENT
                            startOfTrivia := triv:Line -1
                            EXIT
                        ENDIF
                     NEXT
                  ENDIF

                  cDoc := cDoc:Replace("///","")
                  IF ! String.IsNullOrEmpty(cDoc)
                     cXmlDoc := "<doc>"+cDoc+"</doc>"
                  ELSE
                     cXmlDoc := NULL
                  ENDIF
               ENDIF
               // note: do not set this before the IsStartOfEntity check to make sure that
               // single identifiers on a line are not matched with the ClassVar rule
               IF vis == Modifiers.None
                  mods |= Modifiers.Public
               ENDIF
               SELF:_attributes  := mods
               SELF:_start := first
               VAR entities := SELF:ParseEntity(entityKind)
               IF entities != NULL

                  FOREACH VAR entity IN entities
                     IF entity == NULL
                        LOOP
                     ENDIF
                     entity:File := _file
                     IF _hasXmlDoc
                        entity:XmlComments   := cXmlDoc
                        entity:StartOfXmlComments := startOfTrivia
                         if entity:Kind == Kind.Delegate .and. entity is XSourceTypeSymbol var xtype
                            var invoke := xtype:XMembers:First()
                            invoke:XmlComments := cXmlDoc
                            invoke:StartOfXmlComments :=startOfTrivia
                         endif
                     ENDIF
                     IF aAttribs?:Count > 0
                        entity:CustomAttributes := TokensAsString(aAttribs)
                     ENDIF
                     VAR lastEntity := _EntityList:LastOrDefault()
                     IF lastEntity != NULL
                        lastEntity:Range       := lastEntity:Range:WithEnd(tokenBefore)
                        lastEntity:Interval    := lastEntity:Interval:WithEnd(tokenBefore)
                     ENDIF
                     _EntityList:Add(entity)
                     VAR isMember := entity IS XSourceMemberSymbol
                     VAR isType   := entity IS XSourceTypeSymbol
                     var canAddMembers  := CurrentEntityKind:HasMembers()
                     var canAddChildren := CurrentEntityKind:HasChildren()
                     LOCAL mustPop as LOGIC
                     IF _EntityStack:Count == 0
                        mustPop := FALSE
                     ELSEIF isType
                         IF canAddChildren
                            mustPop := FALSE
                         ELSE
                            mustPop := TRUE
                         ENDIF
                     ELSEIF isMember
                        IF canAddMembers
                            mustPop := FALSE
                        ELSE
                            mustPop := TRUE
                        ENDIF
                     ELSEIF CurrentEntityKind:HasBody() .and. entity:Kind:IsLocal()
                        mustPop := FALSE
                     ELSE
                        mustPop := TRUE
                     ENDIF
                     IF mustPop
                        _EntityStack:Pop()
                     ENDIF
                     IF entity:Kind:IsGlobalTypeMember() .AND. entity IS XSourceMemberSymbol VAR xGlobalMember
                        // GLOBAL, DEFINE, FUNCTION, PROCEDURE
                        // also #define, #command etc
                        SELF:_globalType:AddMember(xGlobalMember)
                     ELSEIF entity:Kind:IsLocal()
                        entity:Parent := CurrentEntity
                     ELSEIF canAddMembers .AND. CurrentEntity IS XSourceTypeSymbol VAR xEnt
                        // CurrentEntity should be a type: Class, Structure, Interface, Enum, VoStruct, Union
                        IF entity IS XSourceMemberSymbol VAR xMember .AND. xMember:Parent == NULL
                            xEnt:AddMember( xMember )
                        ENDIF
                     ELSEIF canAddChildren .and. xEnt != NULL .and. entity IS XSourceTypeSymbol VAR xChild .AND.  ;
                        ! XSourceTypeSymbol.IsGlobalType(xEnt) .and. xEnt:Kind:HasChildren()
                        // Namespace, class, structure, interface can have children (nested types)
                        xEnt:AddChild( xChild )
                        xChild:Namespace := xEnt:FullName
                     ENDIF
                     IF ! entity:SingleLine
                        _EntityStack:Push(entity)
                     ENDIF
                     _BlockStack:Clear()
                  NEXT
               ELSE
                  NOP
               ENDIF
            ELSEIF IsEndOfEntity( OUT VAR endKind)
               VAR type := SELF:La2
               // match la2 with current entity
               DO WHILE _EntityStack:Count > 0
                  VAR top := _EntityStack:Pop()
                  // END PROCEDURE and END FUNCTION may indicate normal procedure and local procedure
                  IF top:Kind == Kind.LocalFunc .and. endKind == Kind.Function
                     NOP // Ok
                  ELSEIF top:Kind == Kind.LocalProc .and. endKind == Kind.Procedure
                     NOP // Ok
                  ELSEIF top:Kind != endKind
                     top:Range       := top:Range:WithEnd(tokenBefore)
                     top:Interval    := top:Interval:WithEnd(tokenBefore)
                     LOOP
                  ENDIF
                  top:Range       := top:Range:WithEnd(SELF:Lt2)
                  top:Interval    := top:Interval:WithEnd(SELF:Lt2)
                  EXIT
               ENDDO
               SELF:ReadLine()
            ELSEIF aAttribs:Count > 0 .AND. La1 == XSharpLexer.EOS
                // Add Attribute
                VAR attribute := ParseAttribute(aAttribs)
                _EntityList:Add(attribute)
            ELSEIF ParseBlock()
               NOP
            ELSE
               SELF:ParseStatement()
            ENDIF
         ENDDO
         VAR types := SELF:_EntityList:Where( {x => x IS XSourceTypeSymbol})
         VAR typelist := XDictionary<STRING, XSourceTypeSymbol>{System.StringComparer.InvariantCultureIgnoreCase}
         typelist:Add(_globalType:Name, _globalType)
         LOCAL last  := NULL AS XSourceTypeSymbol
         FOREACH type AS XSourceTypeSymbol IN types
            IF last != NULL .AND. last:Range:StartLine == last:Range:EndLine
               // adjust the end of the type with the start of the current line
               VAR newEndLine := type:Range:StartLine-1
               VAR newEndPos  := type:Interval:Start -1

               last:Range     := TextRange{last:Range:StartLine, last:Range:StartColumn, newEndLine, 0}
               last:Interval  := TextInterval{last:Interval:Start, newEndPos}
            ENDIF
            IF type:Parent != NULL .AND. (type:Parent:Kind:IsType() .OR. type:Parent:Kind == Kind.Namespace)
               IF type:Parent == _globalType
                  type:Namespace := ""
               ELSE
                  type:Namespace := type:Parent:FullName
               ENDIF
            ENDIF
            IF type:Kind == Kind.Namespace
               // Add to usings when needed. Not strictly necessary but makes lookup easier later
               SELF:AddNameSpaceToUsing(type:Name)
            ELSEIF type:Name:Contains(".")
               VAR pos := type:Name:LastIndexOf(".")
               VAR ns  := type:Name:Substring(0, pos)
               SELF:AddNameSpaceToUsing(ns)
               type:Name   := type:Name:Substring(pos+1)
               IF String.IsNullOrEmpty(type:Namespace)
                  type:Namespace := ns
               ELSE
                  type:Namespace += "."+ns
               ENDIF
            ENDIF

            IF ! typelist:ContainsKey(type:FullName)
               type:File := _file

              typelist:Add(type:FullName, type)
            ENDIF
            last := type
         NEXT
         VAR lasttoken := _tokens[_tokens.Count -1]
         IF last != NULL .AND. last:Range:StartLine == last:Range:EndLine .and. ! last:SingleLine
            // adjust the end of the type with the start of the current line
            // find the last token in the stream

            last:Range     := last:Range:WithEnd(lasttoken)
            last:Interval  := last:Interval:WithEnd(lasttoken)
         ENDIF
         Log(i"Completed, found {_EntityList.Count} entities and {typelist.Count} types")
         IF SELF:_EntityList:Count > 0
            LOCAL lastEntity          := SELF:_EntityList:Last() as XSourceEntity
            if lastEntity:Kind:IsClassMember(_dialect)
                // if type has no end clause then also set the end
                if lastEntity:Range:StartLine == lastEntity:Range:EndLine
                    lastEntity:Range        := lastEntity:Range:WithEnd(lasttoken)
                    lastEntity:Interval     := lastEntity:Interval:WithEnd(lasttoken)
                endif

            elseif ! lastEntity:Kind:HasEndKeyword()
                lastEntity:Range        := lastEntity:Range:WithEnd(lasttoken)
                lastEntity:Interval     := lastEntity:Interval:WithEnd(lasttoken)

            ENDIF
         ELSE
             // Add at least one entity that represents the global namespace
             SELF:GetSourceInfo(_tokens[0], _tokens[_tokens:Count-1], OUT VAR range, OUT VAR interval, OUT VAR source)

             VAR xmember := XSourceMemberSymbol{_globalType:Name,Kind.Namespace, ;
                Modifiers.Export, range, interval, "", FALSE}
             xmember.File := _file
             xmember.SourceCode := source
             SELF:_EntityList:Add(xmember)
             SELF:_globalType:AddMember(xmember)
         ENDIF
         IF ! lLocals
	         _file:SetTypes(typelist, _usings, _staticusings, SELF:_EntityList)
             IF SELF:SaveToDisk
                _file:SaveToDatabase()
             ENDIF
         ENDIF
      PRIVATE METHOD AddNameSpaceToUsing(name as STRING) AS VOID
         var pos  := name:LastIndexOf(".")
         SELF:AddUniqueUsing(name)
         DO WHILE pos > 0
            name := name:Substring(0, pos)
            SELF:AddUniqueUsing(name)
            pos  := name:LastIndexOf(".")
         ENDDO
         RETURN

      PRIVATE METHOD AddUniqueUsing(strName as STRING) AS VOID
         FOREACH var u in SELF:_usings
            if String.Compare(u, strName, TRUE) == 0
               RETURN
            ENDIF
         NEXT
         _usings:Add(strName)
         RETURN

      PRIVATE METHOD ParsePPLine() AS LOGIC
         LOCAL entity as XSourceMemberSymbol
         LOCAL kind AS Kind
         VAR token := SELF:La1
         VAR start := SELF:Lt1

         SWITCH token
         CASE XSharpLexer.PP_REGION
         CASE XSharpLexer.PP_IFDEF
         CASE XSharpLexer.PP_IFNDEF
            VAR block := XSourceBlock{ SELF:Lt1, SELF:Lt2}
            _BlockList:Add(block)
            _PPBlockStack:Push(block)
         CASE XSharpLexer.PP_ENDREGION
         CASE XSharpLexer.PP_ENDIF
               // end
            IF _PPBlockStack:Count > 0
               _PPBlockStack:Peek():Children:Add( XSourceBlock{SELF:Lt1,SELF:Lt2})
               _PPBlockStack:Pop()
            ENDIF
         CASE XSharpLexer.PP_ELSE
               // middle
            IF _PPBlockStack:Count > 0
               _PPBlockStack:Peek():Children:Add( XSourceBlock{SELF:Lt1,SELF:Lt2})
            ENDIF
         CASE XSharpLexer.PP_INCLUDE
             var sb := StringBuilder{}
             kind   := Kind.Include
             SELF:Consume()
             VAR eol   := SELF:Lt1
             DO WHILE SELF:La1 != XSharpLexer.EOS
                eol   := SELF:Lt1
                sb:Append(eol:Text)
                SELF:Consume()
             ENDDO
             SELF:GetSourceInfo(start, eol, OUT VAR range, OUT VAR interval, OUT VAR source)
             VAR name := sb:ToString():Trim()
             if name:StartsWith("""") .and. name.EndsWith("""")
                name := name:Substring(1, name:Length-2)
             endif
             entity := XSourceMemberSymbol{name, kind, Modifiers.None, range,interval,"",FALSE}
             entity:SourceCode := source
         CASE XSharpLexer.PP_DEFINE
         CASE XSharpLexer.PP_UNDEF
         CASE XSharpLexer.PP_COMMAND
         CASE XSharpLexer.PP_TRANSLATE

             VAR name  := SELF:Lt2:Text
             VAR eol   := SELF:Lt2
             var hasId := IsId(SELF:La2)
             if SELF:La2 == XSharpLexer.BACKSLASH
                eol   := SELF:Lt3
                if ! hasId .and. IsId(eol:Type)
                    name  := eol:Text
                    hasId := TRUE
                endif
             ENDIF
             DO WHILE SELF:La1 != XSharpLexer.EOS
                eol := SELF:Lt1
                if ! hasId .and. IsId(eol:Type)
                    name  := eol:Text
                    hasId := TRUE
                endif
                SELF:Consume()
             ENDDO
             SELF:GetSourceInfo(start, eol, OUT VAR range, OUT VAR interval, OUT VAR source)
             SWITCH token
             CASE XSharpLexer.PP_DEFINE
                 kind := Kind.Define
             CASE XSharpLexer.PP_UNDEF
                 kind := Kind.Undefine
             CASE XSharpLexer.PP_COMMAND
                 kind := Kind.Command
                 VAR cType := start:Text[1]
                 IF cType == c'x' .OR. cType == c'X'
                    kind := Kind.XCommand
                 ENDIF
             CASE XSharpLexer.PP_TRANSLATE
                 kind := Kind.Translate
                 VAR cType := start:Text[1]
                 IF cType == c'x' .OR. cType == c'X'
                    kind := Kind.XTranslate
                 ENDIF
             END SWITCH
             entity := XSourceMemberSymbol{name, kind, Modifiers.None, range,interval,"",FALSE}
             entity:SourceCode := source
         OTHERWISE
            RETURN FALSE
         END SWITCH
         SELF:ReadLine()
         if entity != NULL
             entity:ReturnType := ""
             entity:File := _file
             entity:SingleLine := TRUE
             _EntityList.Add(entity)
             _globalType:AddMember(entity)
         ENDIF
         RETURN TRUE

        PRIVATE METHOD ParseUsing() AS LOGIC
/*
using_              : USING (Static=STATIC)? (Alias=identifierName Op=assignoperator)? Name=name EOS
                    ;

*/

         IF SELF:La1 != XSharpLexer.USING
            RETURN FALSE
         ENDIF
         IF SELF:La2 == XSharpLexer.VAR             // USING VAR
            RETURN FALSE
         ENDIF
         VAR startToken := SELF:ConsumeAndGet()
         VAR isStatic := FALSE
         VAR alias := ""
         IF Expect(XSharpLexer.STATIC)
            isStatic := TRUE
         ENDIF
         IF IsId(SELF:La1) .AND. SELF:IsAssignOp(SELF:La2)
            // name :=
            alias := SELF:ConsumeAndGetText()
            SELF:Consume()   // :=
         ENDIF
         VAR name := SELF:ParseQualifiedName()
         IF isStatic
            SELF:_staticusings:Add(name)
         ELSE
            SELF:_usings:Add(name)
         ENDIF
         SELF:ReadLine()
         RETURN TRUE
      PRIVATE METHOD ParseUdcTokens() AS VOID
        DO WHILE SELF:La1 == XSharpLexer.UDC_KEYWORD
            SELF:Consume()
        ENDDO
        RETURN

      PRIVATE METHOD ParseAttributes() AS IList<XSharpToken>
/*
attributes          : ( AttrBlk+=attributeBlock )+
                    ;

attributeBlock      : LBRKT Target=attributeTarget? Attributes+=attribute (COMMA Attributes+=attribute)* RBRKT
                    | String=BRACKETED_STRING_CONST
                    ;

attributeTarget     : Token=(ID | CLASS | CONSTRUCTOR | DELEGATE | ENUM | EVENT | FIELD | INTERFACE | METHOD | PROPERTY  | RETURN | STRUCTURE ) COLON
                    ;

attribute           : Name=name (LPAREN (Params+=attributeParam (COMMA Params+=attributeParam)* )? RPAREN )?
                    ;

attributeParam      : Name=identifierName Op=assignoperator Expr=expression     #propertyAttributeParam
                    | Expr=expression                                   #exprAttributeParam
                    ;

*/
         // Please note that in the editor we do not check the contents of the attributes.
         // we simply parse LBRKT ... RBRKT groups until we find no more LBRKT

         VAR tokens := List<XSharpToken>{}
         DO WHILE SELF:La1 == XSharpLexer.LBRKT .AND. ! SELF:Eos()
            tokens:Add(SELF:ConsumeAndGet())
            DO WHILE SELF:La1 != XSharpLexer.RBRKT .AND. ! SELF:Eos()
               tokens:Add(SELF:ConsumeAndGet())
            ENDDO
            IF SELF:La1 == XSharpLexer.RBRKT
               tokens:Add(SELF:ConsumeAndGet())
            ENDIF
         ENDDO
         RETURN tokens

      PRIVATE METHOD IsModifier(num AS LONG) AS LOGIC
            SWITCH num
               // Visibility Alphabetical
            CASE XSharpLexer.EXPORT
            CASE XSharpLexer.HIDDEN
            CASE XSharpLexer.INTERNAL
            CASE XSharpLexer.PRIVATE
            CASE XSharpLexer.PROTECTED
            CASE XSharpLexer.PUBLIC

            // Real modifiers Alphabetical
            CASE XSharpLexer.ABSTRACT
            CASE XSharpLexer.ASYNC
            CASE XSharpLexer.CONST
            CASE XSharpLexer.EXTERN
            CASE XSharpLexer.INITONLY
            CASE XSharpLexer.INSTANCE
            CASE XSharpLexer.NEW
            CASE XSharpLexer.OVERRIDE
            CASE XSharpLexer.PARTIAL
            CASE XSharpLexer.SEALED
            CASE XSharpLexer.STATIC
            CASE XSharpLexer.UNSAFE
            CASE XSharpLexer.VIRTUAL
            CASE XSharpLexer.VOLATILE

            // XPP modifiers
            CASE XSharpLexer.DEFERRED
            CASE XSharpLexer.FINAL
            CASE XSharpLexer.FREEZE
            CASE XSharpLexer.INTRODUCE
            CASE XSharpLexer.SYNC
               RETURN TRUE
            END SWITCH
         RETURN FALSE
      PRIVATE METHOD ParseVisibilityAndModifiers() AS   Modifiers
         VAR result := Modifiers.None
         DO WHILE ! SELF:Eos()
            VAR done := FALSE
            SWITCH SELF:La1
               // Visibility Alphabetical
            CASE XSharpLexer.EXPORT
               result |= Modifiers.Public
            CASE XSharpLexer.HIDDEN
               result |= Modifiers.Private
            CASE XSharpLexer.INTERNAL
               result |= Modifiers.Internal
            CASE XSharpLexer.PRIVATE
               result |= Modifiers.Private
            CASE XSharpLexer.PROTECTED
               result |= Modifiers.Protected
            CASE XSharpLexer.PUBLIC
               result |= Modifiers.Public

            // Real modifiers Alphabetical
            CASE XSharpLexer.ABSTRACT
               result |= Modifiers.Abstract
            CASE XSharpLexer.ASYNC
               result |= Modifiers.Async
            CASE XSharpLexer.CONST
               result |= Modifiers.Const
               result |= Modifiers.Static
            CASE XSharpLexer.EXTERN
               result |= Modifiers.External
            CASE XSharpLexer.INITONLY
               result |= Modifiers.InitOnly
            CASE XSharpLexer.INSTANCE
               result |= Modifiers.Instance
               result |= Modifiers.Protected
            CASE XSharpLexer.NEW
               result |= Modifiers.New
            CASE XSharpLexer.OVERRIDE
               result |= Modifiers.Override
            CASE XSharpLexer.PARTIAL
               result |= Modifiers.Partial
            CASE XSharpLexer.SEALED
               result |= Modifiers.Sealed
            CASE XSharpLexer.STATIC
               result |= Modifiers.Static
            CASE XSharpLexer.UNSAFE
               result |= Modifiers.Unsafe
            CASE XSharpLexer.VIRTUAL
               result |= Modifiers.Virtual
            CASE XSharpLexer.VOLATILE
               result |= Modifiers.Volatile

            // XPP modifiers
            CASE XSharpLexer.DEFERRED
               result |= Modifiers.Deferred
            CASE XSharpLexer.FINAL
               result |= Modifiers.Final
            CASE XSharpLexer.FREEZE
               result |= Modifiers.Freeze
            CASE XSharpLexer.INTRODUCE
               result |= Modifiers.Introduce
            CASE XSharpLexer.SYNC
               result |= Modifiers.Sync
            OTHERWISE
               done := TRUE
            END SWITCH
            IF ! done
               SELF:Consume()
            ELSE
               EXIT
            ENDIF
         ENDDO
         RETURN result

      PRIVATE METHOD IsStartOfEntity(entityKind OUT Kind, mods AS Modifiers) AS LOGIC
         entityKind := Kind.Unknown
         SWITCH SELF:La1
         CASE XSharpLexer.BEGIN
               // namespace ?
            IF SELF:La2 == XSharpLexer.NAMESPACE
                IF SELF:IsId(SELF:La3)
                    entityKind := Kind.Namespace
                ENDIF
            ENDIF
         CASE XSharpLexer.CLASS
            IF SELF:InXppClass
               IF SELF:La2 == XSharpLexer.METHOD  .OR. SELF:IsModifier(SELF:La2)   // XPP has CLASS METHOD = a static method
                  entityKind := Kind.Method
               ELSEIF SELF:La2 == XSharpLexer.VAR                                  // CLASS VAR for XPP
                  entityKind := Kind.Field
               ENDIF
            ELSEIF SELF:IsId(SELF:La2)
               entityKind := Kind.Class
            ENDIF
         CASE XSharpLexer.STRUCTURE
            IF SELF:IsId(SELF:La2)
                entityKind := Kind.Structure
            ENDIF
         CASE XSharpLexer.DELEGATE
            IF SELF:IsId(SELF:La2)
               entityKind := Kind.Delegate
            ENDIF
         CASE XSharpLexer.INTERFACE
            IF SELF:IsId(SELF:La2)
                entityKind := Kind.Interface
            ENDIF
         CASE XSharpLexer.ENUM
            IF SELF:IsId(SELF:La2)
               entityKind := Kind.Enum
            ENDIF
         CASE XSharpLexer.EVENT
            IF SELF:IsId(SELF:La2)
                entityKind := Kind.Event
            ENDIF
         CASE XSharpLexer.METHOD
            IF SELF:IsId(SELF:La2)
                entityKind := Kind.Method
            ENDIF
         CASE XSharpLexer.ACCESS
            IF SELF:IsId(SELF:La2)
                entityKind := Kind.Access
            ENDIF
         CASE XSharpLexer.ASSIGN
            IF SELF:IsId(SELF:La2)
                entityKind := Kind.Assign
            ENDIF
         CASE XSharpLexer.PROPERTY
            IF SELF:IsId(SELF:La2) .or. SELF:La2 == XSharpLexer.SELF
                entityKind := Kind.Property
            ENDIF
         CASE XSharpLexer.OPERATOR
            entityKind := Kind.Operator
         CASE XSharpLexer.CONSTRUCTOR
            entityKind := Kind.Constructor
         CASE XSharpLexer.DESTRUCTOR
            entityKind := Kind.Destructor
         CASE XSharpLexer.DECLARE
               // access, assign, method
            IF SELF:La2 == XSharpLexer.ASSIGN .OR. SELF:La2 == XSharpLexer.ACCESS .OR. SELF:La2 == XSharpLexer.METHOD
               entityKind := Kind.Ignore
            ENDIF
         CASE XSharpLexer.DEFINE
               // define class ?
            IF SELF:IsId(SELF:La2)
               entityKind := Kind.VODefine
            ELSEIF _dialect == XSharpDialect.FoxPro
               entityKind := Kind.Class
            ENDIF
         CASE XSharpLexer.VOSTRUCT
            IF SELF:IsId(SELF:La2)
                entityKind := Kind.VOStruct
            ENDIF
         CASE XSharpLexer.UNION
            IF SELF:IsId(SELF:La2)
                entityKind := Kind.Union
            ENDIF
         CASE XSharpLexer.MEMBER
            IF SELF:IsId(SELF:La2)
                entityKind := Kind.EnumMember
            ENDIF
         CASE XSharpLexer.ADD
            // Todo handle Add Object clause inside Class.
            IF SELF:La2 == XSharpLexer.OBJECT
               entityKind := Kind.Field
            ENDIF
         CASE XSharpLexer.FUNCTION
            IF SELF:IsId(SELF:La2)
                entityKind := Kind.Function
            ENDIF
         CASE XSharpLexer.PROCEDURE
            IF SELF:IsId(SELF:La2)
                entityKind := Kind.Procedure
            ENDIF
         CASE XSharpLexer.GLOBAL
            IF SELF:IsId(SELF:La2)
                entityKind := Kind.VOGlobal
            ENDIF
         CASE XSharpLexer.DLL
            IF SELF:La2 == XSharpLexer.FUNCTION .OR. SELF:La2 == XSharpLexer.PROCEDURE
               entityKind := Kind.VODLL
            ENDIF
         CASE XSharpLexer.FIELD
            // field declaration only inside FoxPro class definition
            IF InFoxClass .AND. (CurrentEntity:Kind == Kind.Class .OR. CurrentEntity:Kind == Kind.Field)
               entityKind := Kind.Field
            ENDIF
         CASE XSharpLexer.IMPLEMENTS
            IF InFoxClass
               entityKind := Kind.Field // Not really a field but handled later
            ENDIF
         CASE XSharpLexer.DIMENSION
            // make sure that DIMENSION inside a method is not matched
            IF InFoxClass
               entityKind := Kind.Field // Not really a field but handled later
            ENDIF
         // XPP code between CLASS .. ENDCLASS
         // fail when partially parseing for locals lookup
         CASE XSharpLexer.VAR WHEN  SELF:InXppClass
            entityKind := Kind.Field // Not really a field but handled later

         CASE XSharpLexer.COLON WHEN SELF:InXppClass
            IF _AND(mods, Modifiers.VisibilityMask) != Modifiers.None
               entityKind := Kind.Field // Not really a field but handled later
            ENDIF
         CASE XSharpLexer.INLINE WHEN SELF:InXppClass
            entityKind := Kind.Method // Not really a field but handled later
         CASE XSharpLexer.LOCAL
            IF SELF:La2 == XSharpLexer.FUNCTION
               entityKind := Kind.LocalFunc
            ELSEIF SELF:La2 == XSharpLexer.PROCEDURE
               entityKind := Kind.LocalProc
            ENDIF
         CASE XSharpLexer.GET
         CASE XSharpLexer.SET
         //CASE XSharpLexer.ADD
         CASE XSharpLexer.REMOVE
            entityKind := Kind.Unknown
         OTHERWISE
            IF IsId(SELF:La1)
               IF mods != Modifiers.None
                  LOCAL parent AS XSourceTypeSymbol
                  IF CurrentEntity IS XSourceTypeSymbol
                     parent := (XSourceTypeSymbol) CurrentEntity
                  ELSEIF CurrentEntity != NULL .AND. CurrentEntity:Parent IS XSourceTypeSymbol
                     parent := (XSourceTypeSymbol) CurrentEntity:Parent
                  ENDIF
                  IF ! XSourceTypeSymbol.IsGlobalType(parent)
                     entityKind := Kind.Field
                  ENDIF
                  // PRIVATE and PUBLIC as memvar declarator
                  // when inside a method or function
                  IF mods == Modifiers.Public .OR. mods == Modifiers.Private
                     var txt := LastToken.Text.ToUpper()
                     if (txt == "PUBLIC" .or. txt == "PRIVATE")
                         IF CurrentEntity IS XSourceMemberSymbol VAR xDef .AND. ! xDef:SingleLine .AND. CurrentEntity.Kind:HasBody()
                            if _file:Project:ParseOptions:SupportsMemvars
                                entityKind := Kind.Unknown
                            endif
                        ENDIF
                     ENDIF
                  ENDIF
               ELSEIF InFoxClass .AND. (CurrentEntity:Kind == Kind.Class .OR. CurrentEntity:Kind == Kind.Field)
                  IF SELF:La1 == XSharpLexer.ID .AND. IsAssignOp(SELF:La2)
                     entityKind := Kind.Field
                  ELSEIF SELF:La1 == XSharpLexer.ID .AND. SELF:Lt1:Text:EndsWith("COMATTRIB", StringComparison.OrdinalIgnoreCase)
                     entityKind := Kind.Field
                  ENDIF
               ELSEIF CurrentType?:Kind == Kind.Enum
                  entityKind := Kind.EnumMember
               ENDIF
            ENDIF

         END SWITCH

         RETURN entityKind != Kind.Unknown

      PRIVATE METHOD ParseBlock() AS LOGIC
         // Adds, updates or removes block token on the block tokens stack
         // Start of block is also added to the _BlockList
         LOCAL nStart  := 0 AS LONG
         LOCAL nMiddle := 0 AS LONG
         LOCAL nEnd    := 0 AS LONG
         SELF:ParseUdcTokens()  // Read UDC tokens on the current line
         SWITCH SELF:La1
         CASE XSharpLexer.BEGIN
            SWITCH SELF:La2
            // tokens in the same order as the rules inside xsharp.g4
            CASE XSharpLexer.SEQUENCE
            CASE XSharpLexer.LOCK
            CASE XSharpLexer.SCOPE
            CASE XSharpLexer.SWITCH
            CASE XSharpLexer.USING
            CASE XSharpLexer.UNSAFE
            CASE XSharpLexer.CHECKED
            CASE XSharpLexer.UNCHECKED
            CASE XSharpLexer.FIXED
               nStart := 2
            END SWITCH

         CASE XSharpLexer.DO
            SWITCH SELF:La2
               CASE XSharpLexer.WHILE
               CASE XSharpLexer.CASE
               CASE XSharpLexer.SWITCH
               nStart := 2
            END SWITCH

            CASE XSharpLexer.IF
            CASE XSharpLexer.FOR
            CASE XSharpLexer.FOREACH
            CASE XSharpLexer.REPEAT
            CASE XSharpLexer.TRY
            CASE XSharpLexer.WITH
            CASE XSharpLexer.TEXT
            CASE XSharpLexer.SWITCH
            CASE XSharpLexer.GET
            CASE XSharpLexer.SET
            CASE XSharpLexer.ADD
            CASE XSharpLexer.REMOVE
               nStart := 1
            // Middle of a block
         CASE XSharpLexer.CASE
         CASE XSharpLexer.OTHERWISE
         CASE XSharpLexer.ELSEIF
         CASE XSharpLexer.ELSE
         CASE XSharpLexer.CATCH
         CASE XSharpLexer.FINALLY
            nMiddle := 1
         CASE XSharpLexer.RECOVER
            IF SELF:La2 == XSharpLexer.USING
               nMiddle := 2
            ENDIF
            // End of a block
         CASE XSharpLexer.ENDCASE
         CASE XSharpLexer.ENDDO
         CASE XSharpLexer.ENDIF
         CASE XSharpLexer.NEXT      // Also covers ENDFOR
         CASE XSharpLexer.UNTIL
            nEnd := 1
         CASE XSharpLexer.END
               SWITCH SELF:La2
               // tokens in the same order as the rules inside xsharp.g4
               CASE XSharpLexer.SEQUENCE
               CASE XSharpLexer.LOCK
               CASE XSharpLexer.SCOPE
               CASE XSharpLexer.SWITCH
               CASE XSharpLexer.USING
               CASE XSharpLexer.UNSAFE
               CASE XSharpLexer.CHECKED
               CASE XSharpLexer.UNCHECKED
               CASE XSharpLexer.FIXED
               CASE XSharpLexer.TRY
               CASE XSharpLexer.WITH

               CASE XSharpLexer.GET        // Accessors
               CASE XSharpLexer.SET
               CASE XSharpLexer.ADD
               CASE XSharpLexer.REMOVE
                     nEnd := 2

               CASE XSharpLexer.EOS        // End without following keyword is usually also allowed
                  nEnd := 1
                END SWITCH
         END SWITCH
         IF nStart > 0
            IF SELF:_collectBlocks
               VAR block := XSourceBlock{ SELF:Lt1, IIF(nStart == 1, SELF:Lt1, SELF:Lt2)}
               _BlockList:Add(block)
               _BlockStack:Push(block)
            ENDIF
            IF SELF:_collectLocals
              IF SELF:La1 == XSharpLexer.SET .OR. SELF:La1 == XSharpLexer.REMOVE .OR. SELF:La1 == XSharpLexer.ADD
                  // Add value token inside accessors
                  VAR id := "Value"
                  VAR strType := SELF:CurrentEntity:TypeName
                  VAR start := SELF:Lt1
                  VAR stop  := SELF:Lt2
                  SELF:GetSourceInfo(start, stop, OUT VAR range, OUT VAR interval, OUT VAR _)
                  VAR xVar := XSourceVariableSymbol{SELF:CurrentEntity, id, range, interval, strType}

                  SELF:_locals:Add(xVar)
               ENDIF
               IF SELF:La1 == XSharpLexer.IF
                  SELF:ParseForLocals()
               ELSE
                  SELF:ParseForLocalDeclaration()
               ENDIF
            ELSE
               SELF:ReadLine()
            ENDIF
            RETURN TRUE
         ELSEIF nMiddle > 0
            // Do something
            IF SELF:_collectBlocks .AND. _BlockStack:Count > 0
               CurrentBlock:Children:Add( XSourceBlock{SELF:Lt1,IIF(nMiddle == 1, SELF:Lt1, SELF:Lt2)})
            ENDIF
            SWITCH SELF:La1
            CASE XSharpLexer.CATCH
               IF SELF:_collectLocals
                  IF SELF:IsId(SELF:La2)
                     SELF:Consume()
                     VAR start   := SELF:Lt1
                     VAR id      := SELF:ParseIdentifier()
                     VAR strType := "System.Exception"
                     IF SELF:La1 == XSharpLexer.AS
                         strType := SELF:ParseDataType(FALSE)
                     ENDIF

                     SELF:GetSourceInfo(start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR _)
                     VAR xVar := XSourceVariableSymbol{SELF:CurrentEntity, id, range, interval, strType}
                     SELF:_locals:Add(xVar)
                  ENDIF
               ENDIF
            CASE XSharpLexer.ELSEIF
            CASE XSharpLexer.CASE
                IF SELF:_collectLocals
                     SELF:ParseForLocals()
                ENDIF
            END SWITCH
            SELF:ReadLine()
            RETURN TRUE
         ELSEIF nEnd > 0
            IF SELF:_collectBlocks .AND. _BlockStack:Count > 0
               CurrentBlock:Children:Add( XSourceBlock{SELF:Lt1,IIF(nEnd == 1, SELF:Lt1, SELF:Lt2)})
               _BlockStack:Pop()
            ENDIF
            SELF:ReadLine()
            RETURN TRUE
         ENDIF
         RETURN FALSE

      PRIVATE METHOD IsEndOfEntity(EntityKind OUT Kind) AS LOGIC
         SWITCH SELF:La1
         CASE XSharpLexer.END
            SWITCH SELF:La2
               // mandatory END <keyword> pairs
            CASE XSharpLexer.NAMESPACE
                  EntityKind := Kind.Namespace
               RETURN TRUE
            CASE XSharpLexer.CLASS
            CASE XSharpLexer.DEFINE // FoxPro
                  EntityKind := Kind.Class
               RETURN TRUE
            CASE XSharpLexer.STRUCTURE
                  EntityKind := Kind.Structure
               RETURN TRUE
            CASE XSharpLexer.ENUM
                  EntityKind := Kind.Enum
                  RETURN TRUE

            CASE XSharpLexer.INTERFACE
                  EntityKind := Kind.Interface
                  RETURN TRUE

            CASE XSharpLexer.PROPERTY
                  EntityKind := Kind.Property
                  RETURN TRUE

            CASE XSharpLexer.EVENT
                  EntityKind := Kind.Event
                  RETURN TRUE

                  // Optional

            CASE XSharpLexer.METHOD
                  EntityKind := Kind.Method
                  RETURN TRUE

            CASE XSharpLexer.ACCESS
                  EntityKind := Kind.Access
                  RETURN TRUE

            CASE XSharpLexer.ASSIGN
                  EntityKind := Kind.Assign
                  RETURN TRUE

            CASE XSharpLexer.OPERATOR
                  EntityKind := Kind.Operator
                  RETURN TRUE

            CASE XSharpLexer.CONSTRUCTOR
                  EntityKind := Kind.Constructor
                  RETURN TRUE

            CASE XSharpLexer.DESTRUCTOR
                  EntityKind := Kind.Destructor
                  RETURN TRUE

            CASE XSharpLexer.FUNCTION
                  IF InFoxClass
                     EntityKind := Kind.Method
                  ELSE
                     EntityKind := Kind.Function
                  ENDIF
                  RETURN TRUE

            CASE XSharpLexer.PROCEDURE
                  IF InFoxClass
                     EntityKind := Kind.Method
                  ELSE
                     EntityKind := Kind.Procedure
                  ENDIF
                  RETURN TRUE

            CASE XSharpLexer.VOSTRUCT
                  EntityKind := Kind.VOStruct
                  RETURN TRUE

            CASE XSharpLexer.UNION
                  EntityKind := Kind.Union
                  RETURN TRUE

               // END <nothing> is NOT allowed for entities since that conflicts with the END for blocks
            END SWITCH
         CASE XSharpLexer.ENDDEFINE
            EntityKind := Kind.Class
            RETURN TRUE

         CASE XSharpLexer.ENDCLASS
            EntityKind := Kind.Class
            RETURN TRUE

         CASE XSharpLexer.UDC_KEYWORD
            IF String.Equals(SELF:Lt1.Text, "ENDFUNC", StringComparison.OrdinalIgnoreCase)
               IF InFoxClass
                  EntityKind := Kind.Method
               ELSE
                  EntityKind := Kind.Function
               ENDIF
               RETURN TRUE
            ELSEIF String.Equals(SELF:Lt1.Text, "ENDPROC",StringComparison.OrdinalIgnoreCase)
               IF InFoxClass
                  EntityKind := Kind.Method
               ELSE
                  EntityKind := Kind.Procedure
               ENDIF
               RETURN TRUE
            ENDIF
         END SWITCH
         EntityKind := Kind.Unknown
         RETURN FALSE

         #region Properties and Methods that are delegated to the XSharpTokenList type
      PRIVATE PROPERTY La1 AS INT => _list:La1
      PRIVATE PROPERTY La2 AS INT => _list:La2
      PRIVATE PROPERTY La3 AS INT => _list:La3
      PRIVATE PROPERTY Lt1 AS XSharpToken => _list:Lt1
      PRIVATE PROPERTY Lt2 AS XSharpToken => _list:Lt2
      PRIVATE PROPERTY Lt3 AS XSharpToken => _list:Lt3
      PRIVATE PROPERTY LastToken AS XSharpToken => _list:LastReadToken
      PRIVATE METHOD La(nToken AS LONG) AS LONG => _list:La(nToken)
      PRIVATE METHOD Lt(nToken AS LONG) AS XSharpToken => _list:Lt(nToken)
      PRIVATE METHOD Eoi() AS LOGIC => _list:Eoi()
      PRIVATE METHOD Eos() AS LOGIC => _list:Eos()
      PRIVATE METHOD Consume() AS VOID =>_list:Consume()
      PRIVATE METHOD ConsumeAndGet() AS XSharpToken => _list:ConsumeAndGet()
      PRIVATE METHOD ConsumeAndGetAny(nTypes PARAMS LONG[]) AS XSharpToken => _list:ConsumeAndGetAny(nTypes)
      PRIVATE METHOD ConsumeAndGetText() AS STRING => _list:ConsumeAndGetText()
      PRIVATE METHOD Expect(nType AS LONG) AS LOGIC => _list:Expect(nType)
      PRIVATE METHOD ExpectAny(nTypes PARAMS LONG[]) AS LOGIC => _list:ExpectAny(nTypes)
      PRIVATE METHOD ExpectAndGet(nType AS LONG, t OUT IToken) AS LOGIC => _list:ExpectAndGet(nType, OUT t)
      PRIVATE METHOD Matches(nTypes PARAMS LONG[]) AS LOGIC => _list:Matches(nTypes)
      PRIVATE METHOD PushBack() AS VOID => _list:PushBack()
      PRIVATE METHOD ReadLine() AS VOID => _list:ReadLine()
         #endregion

      PRIVATE METHOD ExpectAssignOp() AS LOGIC
         RETURN SELF:ExpectAny(XSharpLexer.ASSIGN_OP, XSharpLexer.EQ)

      PRIVATE METHOD IsId(token AS LONG) AS LOGIC
         IF token == XSharpLexer.ID .OR. token == XSharpLexer.KWID
            RETURN TRUE
         ENDIF
         // Soft keywords need to be
         RETURN SELF:IsKeywordXs(token) .OR. SELF:IsKeywordFox(token) .OR. SELF:IsKeywordXpp(token)

      PRIVATE METHOD ParseIdentifier() AS STRING
         IF SELF:IsId(SELF:La1)
            VAR id := SELF:ConsumeAndGetText()
            IF id:StartsWith("@@")
               id := id:Substring(2)
            ENDIF
            RETURN id
         ENDIF
         RETURN ""

      PRIVATE METHOD ParseAttribute(aAttribs AS IList<XSharpToken>) AS XSourceEntity
            VAR name := SELF:TokensAsString(aAttribs, FALSE)
            SELF:GetSourceInfo(aAttribs[0], aAttribs[aAttribs:Count-1], OUT VAR range, OUT VAR interval, OUT VAR source)
            VAR entity := XSourceMemberSymbol{name, Kind.Attribute, Modifiers.None,;
                            range,interval,"",FALSE}
            entity:SourceCode := source
            entity:File := _file
            entity:SingleLine := TRUE
            _globalType:AddMember(entity)
            RETURN entity

      PRIVATE METHOD ParseOptionalClassClause() AS STRING
         // parse the clause after a METHOD, ACCESS, ASSIGN, CONSTRUCTOR, DESTRUCTOR CLASS <Identifier>
         IF SELF:La1 == XSharpLexer.CLASS
            SELF:Consume()
            RETURN SELF:ParseIdentifier()
         ENDIF
         RETURN ""

      PRIVATE METHOD ParseQualifiedName() AS STRING
         LOCAL result := "" AS STRING
         VAR Tokens := List<XSharpToken>{}
         IF SELF:La1 == XSharpLexer.ID .OR. SELF:La1 == XSharpLexer.KWID
            Tokens:Add(SELF:ConsumeAndGet())
            DO WHILE SELF:La1 == XSharpLexer.DOT .AND.  SELF:IsId(SELF:La2) .AND. ! SELF:Eos()
               Tokens:Add(SELF:ConsumeAndGet())
               Tokens:Add(SELF:ConsumeAndGet())
            ENDDO
         ENDIF
         RETURN SELF:TokensAsString(Tokens,FALSE)

      PRIVATE METHOD TokensAsString(tokens AS IList<XSharpToken>, lAddTrivia := TRUE AS LOGIC) AS STRING
         LOCAL sb AS StringBuilder
         IF (tokens == NULL .or. tokens:Count == 0)
            RETURN ""
         ENDIF
         sb := StringBuilder{}

         FOREACH t AS XSharpToken IN tokens
            IF t:HasTrivia .AND. lAddTrivia
                sb:Append(t:TriviaAsText)
            ENDIF
            sb:Append(t:CleanText())
         NEXT
         RETURN sb:ToString():Trim()

      PRIVATE METHOD ParseEntity(entityKind AS Kind) AS IList<XSourceEntity>
         LOCAL result AS IList<XSourceEntity>
         SWITCH entityKind
         CASE Kind.Ignore
            SELF:ReadLine()
         CASE Kind.Namespace
            result := SELF:ParseNamespace()
         CASE Kind.Class
            IF SELF:La1 == XSharpLexer.DEFINE
               result := SELF:ParseFoxClass()
            ELSE
               IF _dialect == XSharpDialect.XPP .AND. SELF:HasXppEndClass()
                  result := SELF:ParseXppClass()
               ELSE
                  result := SELF:ParseTypeDef()
               ENDIF
            ENDIF
         CASE Kind.Structure
            result := SELF:ParseTypeDef()
         CASE Kind.Interface
            result := SELF:ParseTypeDef()
         CASE Kind.Delegate
            result := SELF:ParseDelegate()
         CASE Kind.Access
            IF InXppClass .AND. _dialect == XSharpDialect.XPP
               result := SELF:ParseXppProperty()
            ELSE
               result := SELF:ParseMethod()
            ENDIF
         CASE Kind.Assign
            IF InXppClass .AND. _dialect == XSharpDialect.XPP
               result := SELF:ParseXppProperty()
            ELSE
               result := SELF:ParseMethod()
            ENDIF
         CASE Kind.Method
            IF SELF:_dialect == XSharpDialect.XPP
               result := SELF:ParseXppMethod()
            ELSE
               result := SELF:ParseMethod()
            ENDIF
         CASE Kind.Function
         CASE Kind.Procedure
            IF InFoxClass .AND. _dialect == XSharpDialect.FoxPro
               result := SELF:ParseFoxMethod()
            ELSE
               result := SELF:ParseFuncProc()
            ENDIF
         CASE Kind.VODLL
            result := SELF:ParseVODLL()
         CASE Kind.VOStruct
            result := SELF:ParseVoStruct()
         CASE Kind.Union
            result := SELF:ParseVOUnion()
         CASE Kind.Enum
            result := SELF:ParseEnum()
         CASE Kind.EnumMember
            IF SELF:CurrentEntity:Kind == Kind.Enum
               result := SELF:ParseEnumMember()
            ELSE
               result := SELF:ParseVoStructMember()
            ENDIF
         CASE Kind.VODefine
            result := SELF:ParseVoDefine()
         CASE Kind.VOGlobal
            result := SELF:ParseVOGlobals()
         CASE Kind.Property
            result := SELF:ParseProperty()

         CASE Kind.Event
            result := SELF:ParseEvent()

         CASE Kind.Operator
            result := SELF:ParseOperator()

         CASE Kind.Constructor
            result := SELF:ParseConstructor()

         CASE Kind.Destructor
            result := SELF:ParseDestructor()

         CASE Kind.Field
            IF InXppClass .AND. _dialect == XSharpDialect.XPP
               IF SELF:La1 == XSharpLexer.COLON
                  result := SELF:ParseXppVisibility()
               ELSE
                  result := SELF:ParseXppClassVars()
               ENDIF
            ELSEIF InFoxClass .AND. _dialect == XSharpDialect.FoxPro
               result := SELF:ParseFoxFields()
            ELSE
               result := SELF:ParseClassVars()
            ENDIF
         CASE Kind.LocalFunc
         CASE Kind.LocalProc
               result := SELF:ParseLocalFuncProc()
         END SWITCH
         RETURN result

         PRIVATE METHOD HasXppEndClass() AS LOGIC
            LOCAL iCurrent := 1 AS LONG
            LOCAL iLa AS LONG
            iLa := SELF:La(iCurrent)
            DO WHILE iLa != -1
               IF iLa == XSharpLexer.ENDCLASS
                  RETURN TRUE
               ELSEIF iLa == XSharpLexer.INLINE // Does not occurs in normal class definition
                  RETURN TRUE
               ELSEIF SELF:IsModifier(iLa) .AND. SELF:La(iCurrent+1) == XSharpLexer.COLON
                  // visibility clause
                  RETURN TRUE
               ELSEIF iLa == XSharpLexer.END .AND. SELF:La(iCurrent+1) == XSharpLexer.CLASS
                  RETURN FALSE
               ENDIF
               iCurrent++
               iLa := SELF:La(iCurrent)
            ENDDO
            RETURN FALSE

         #region Main entities

         PRIVATE METHOD ParseFuncProc() AS IList<XSourceEntity>
/*
funcproc      : (Attributes=attributes)? (Modifiers=funcprocModifiers)?
              T=funcproctype Sig=signature
              InitExit=(INIT1|INIT2|INIT3|EXIT)?
              vodummyclauses
              end=eos
              StmtBlk=statementBlock
              (END T2=funcproctype   EOS )?
              ;
funcproctype        : Token=(FUNCTION | PROCEDURE)
                ;

*/
            LOCAL kind AS Kind
            IF ! ParseFuncProcType (OUT kind)
               RETURN NULL
            ENDIF
            SELF:Consume()
            VAR sig := SELF:ParseSignature()

            VAR initexit := ""
            IF SELF:Matches(XSharpLexer.INIT1,XSharpLexer.INIT2,XSharpLexer.INIT3,XSharpLexer.EXIT)
               initexit := SELF:ConsumeAndGetText()
            ENDIF
            SELF:GetSourceInfo(_start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR source)
            SELF:ReadLine()

            VAR xMember := XSourceMemberSymbol{sig, kind, _attributes, range, interval, _attributes:HasFlag(Modifiers.Static)}
            xMember:SourceCode := source
            xMember:File := SELF:_file
            RETURN <XSourceEntity>{xMember}

         PRIVATE METHOD ParseNamespace() AS IList<XSourceEntity>
/*
namespace_          : BEGIN NAMESPACE Name=name e=eos
                      (Entities+=entity)*
                      END NAMESPACE EOS
                    ;
*/
            IF SELF:La1 != XSharpLexer.BEGIN .AND. SELF:La2 != XSharpLexer.NAMESPACE
               RETURN NULL
            ENDIF
            SELF:Consume()   // BEGIN
            SELF:Consume()   // NAMESPACE
            VAR id := SELF:ParseQualifiedName()
            SELF:GetSourceInfo(_start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR source)
            SELF:ReadLine()
            VAR xType := XSourceTypeSymbol{id, Kind.Namespace, _attributes, range, interval,_file}
            xType:SourceCode := source
            RETURN <XSourceEntity>{xType}

         PRIVATE METHOD ParseTypeDef() AS IList<XSourceEntity>
/*
interface_          : (Attributes=attributes)? (Modifiers=classModifiers)?
                      I=INTERFACE (Namespace=nameDot)? Id=identifier
                      TypeParameters=typeparameters?                                      // TypeParameters indicate Generic Interface
                      ((INHERIT|COLON) Parents+=datatype)? (COMMA Parents+=datatype)*
                      (ConstraintsClauses+=typeparameterconstraintsclause)*              // Optional typeparameterconstraints for Generic Interface
                      e=eos
                      (Members+=classmember)*
                      END INTERFACE EOS
                    ;
class_              : (Attributes=attributes)? (Modifiers=classModifiers)?
                      C=CLASS (Namespace=nameDot)? Id=identifier
                      TypeParameters=typeparameters?                                    // TypeParameters indicate Generic Class
                      (INHERIT BaseType=datatype)?
                      (IMPLEMENTS Implements+=datatype (COMMA Implements+=datatype)*)?
                      (ConstraintsClauses+=typeparameterconstraintsclause)*             // Optional typeparameterconstraints for Generic Class
                      e=eos
                      (Members+=classmember)*
                      END CLASS EOS
                    ;
structure_          : (Attributes=attributes)? (Modifiers=classModifiers)?
                      S=STRUCTURE (Namespace=nameDot)? Id=identifier
                      TypeParameters=typeparameters?
                      (IMPLEMENTS Implements+=datatype (COMMA Implements+=datatype)*)?
                      (ConstraintsClauses+=typeparameterconstraintsclause)* e=eos
                      (Members+=classmember)*
                      END STRUCTURE EOS
                    ;

*/
            LOCAL kind AS Kind
            SWITCH SELF:La1
            CASE XSharpLexer.CLASS
               kind := Kind.Class
            CASE XSharpLexer.INTERFACE
               kind := Kind.Interface
            CASE XSharpLexer.STRUCTURE
               kind := Kind.Structure
            OTHERWISE
               RETURN NULL
            END SWITCH
            SELF:Consume()

            LOCAL constraints   AS List<STRING>
            LOCAL parentType    AS STRING
            LOCAL interfaces    AS List<STRING>
            // read Id with optional namespace prefix
            VAR id := SELF:ParseQualifiedName()
            VAR typePars := SELF:ParseTypeParameters()
            // get inherit clause
            IF ExpectAny(XSharpLexer.INHERIT, XSharpLexer.COLON)
               parentType := SELF:ParseTypeName()
            ENDIF
            // get implements clause + list of interfaces
            IF SELF:La1 == XSharpLexer.IMPLEMENTS
               interfaces := List<STRING>{}
               DO WHILE ExpectAny(XSharpLexer.IMPLEMENTS, XSharpLexer.COMMA)
                  interfaces:Add(ParseTypeName())
               ENDDO
            ENDIF
            DO WHILE SELF:La1 == XSharpLexer.WHERE .AND. ! SELF:Eos()
               IF constraints == NULL
                  constraints := List<STRING>{}
               ENDIF
               constraints:Add(SELF:ParseTypeParameterConstraints())
            ENDDO
            // read to EndOfLine
            SELF:GetSourceInfo(_start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR source)
            SELF:ReadLine()
            if typePars != null && typePars:Count > 0
                id += "`"+typePars:Count:ToString()
            endif
            VAR xType := XSourceTypeSymbol{id, kind, _attributes, range, interval, _file}
            xType:SourceCode := source

            IF interfaces?:Count > 0
               FOREACH VAR sInterface IN interfaces
                  xType:AddInterface(sInterface)
               NEXT
            ENDIF
            IF ! String.IsNullOrEmpty(parentType)
               xType:BaseTypeName := parentType
            ENDIF
            xType:IsPartial := _attributes:HasFlag(Modifiers.Partial)
            IF constraints?:Count > 0
               FOREACH VAR constraint IN constraints
                  xType:AddConstraints(constraint)
               NEXT
            ENDIF
            IF typePars?:Count > 0
               FOREACH VAR typepar IN typePars
                  xType:AddTypeParameter(typepar)
               NEXT
            ENDIF
            IF CurrentEntity != _globalType .AND. CurrentEntityKind:HasChildren()
               xType:Parent := SELF:CurrentEntity
            ENDIF

            RETURN <XSourceEntity>{xType}

         PRIVATE METHOD ParseDelegate() AS IList<XSourceEntity>
/*
delegate_           : (Attributes=attributes)? (Modifiers=classModifiers)?
                      D=DELEGATE (Namespace=nameDot)? Id=identifier
                      TypeParameters=typeparameters?
                      ParamList=parameterList?
                      (AS Type=datatype)?
                      (ConstraintsClauses+=typeparameterconstraintsclause)*
                      e=EOS
                    ;

*/
            IF ! Expect(XSharpLexer.DELEGATE)
               RETURN NULL
            ENDIF

            VAR sig      := SELF:ParseSignature()
            SELF:GetSourceInfo(_start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR source)
            VAR xType    := XSourceTypeSymbol{sig:Id, Kind.Delegate,_attributes, range, interval,_file}{SingleLine := TRUE}
            xType:SourceCode := source
            xType:TypeName := sig:DataType
            xType:File     := _file
            VAR xMember  := XSourceMemberSymbol{sig, Kind.Delegate, _attributes, ;
                              range, interval,_attributes:HasFlag(Modifiers.Static)} {SingleLine := TRUE}
            xMember:SourceCode := source
            xMember:Name := "Invoke"
            xType:AddMember(xMember)
            xMember:File       := _file
            RETURN <XSourceEntity>{xType}
            #endregion

         #region ClassMembers
      PRIVATE METHOD ParseMethod(  ) AS IList<XSourceEntity>
/*
// method rule used inside and outside class members rule
method              : (Attributes=attributes)? (Modifiers=memberModifiers)?
                      T=methodtype (ExplicitIface=nameDot)? Sig=signature
                      (CLASS ClassId=identifier)?      // Class Clause needed when entity and allowed when class member
                      vodummyclauses
                      end=eos
                      StmtBlk=statementBlock
                      (END T2=methodtype EOS)?
                    ;

methodtype          : Token=(METHOD | ACCESS | ASSIGN )
                    ;

*/
         LOCAL kind AS Kind
         IF SELF:La1 == XSharpLexer.ACCESS
            kind := Kind.Access
         ELSEIF SELF:La1 == XSharpLexer.ASSIGN
            kind := Kind.Assign
         ELSEIF SELF:La1 == XSharpLexer.METHOD
            kind := Kind.Method
         ELSE
            RETURN NULL
         ENDIF
         SELF:Consume()

         VAR sig := SELF:ParseSignature()
         VAR classClause := SELF:ParseOptionalClassClause()
         // read to EndOfLine
         SELF:GetSourceInfo(_start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR source)
         SELF:ReadLine()

         VAR xMember := XSourceMemberSymbol{sig, kind, _attributes, range, interval, _attributes:HasFlag(Modifiers.Static)}
         IF SELF:CurrentType != NULL
            SELF:CurrentType:AddMember(xMember)
         ENDIF
         xMember:SourceCode := source
         RETURN <XSourceEntity>{xMember}

      PRIVATE METHOD ParseProperty() AS IList<XSourceEntity>
            IF ! Expect(XSharpLexer.PROPERTY)
               RETURN NULL
            ENDIF
         // Note that inside the editor the SET and GET accessors are parsed as blocks and not as entities
/*
property            : (Attributes=attributes)? (Modifiers=memberModifiers)?
                      P=PROPERTY (SELF ParamList=propertyParameterList | (ExplicitIface=nameDot)? Id=identifier)
                      (ParamList=propertyParameterList)?
                      (AS Type=datatype)?
                      ( Auto=AUTO (AutoAccessors+=propertyAutoAccessor)* (Op=assignoperator Initializer=expression)? end=EOS	// Auto
                        | (LineAccessors+=propertyLineAccessor)+ end=EOS													// Single Line
                        | Multi=eos (Accessors+=propertyAccessor)+  END PROPERTY? EOS				// Multi Line
                      )
                    ;

propertyParameterList
                    : LBRKT  (Params+=parameter (COMMA Params+=parameter)*)? RBRKT
                    | LPAREN (Params+=parameter (COMMA Params+=parameter)*)? RPAREN		// Allow Parentheses as well
                    ;

propertyAutoAccessor: Attributes=attributes? Modifiers=accessorModifiers? Key=(GET|SET)
                    ;

propertyLineAccessor: Attributes=attributes? Modifiers=accessorModifiers?
                      ( {InputStream.La(2) != SET}? Key=GET Expr=expression?
                      | {InputStream.La(2) != SET}? Key=UDCSEP Expr=expression?           // New: UDCSep instead of GET
                      | {InputStream.La(2) != GET}? Key=SET ExprList=expressionList?
                      | Key=(GET|SET) )
                    ;
expressionList	    : Exprs+=expression (COMMA Exprs+=expression)*
                    ;

propertyAccessor    : Attributes=attributes? Modifiers=accessorModifiers?
                      ( Key=GET end=eos StmtBlk=statementBlock END GET?
                      | Key=GET UDCSEP ExpressionBody=expression              // New: Expression Body
                      | Key=SET end=eos StmtBlk=statementBlock END SET? )
                      | Key=SET UDCSEP ExpressionBody=expression              // New: Expression Body
                      end=eos
                    ;

*/
         VAR id := ""
         IF SELF:Matches(XSharpLexer.SELF)   // Self property
            id := ConsumeAndGetText()
         ELSE
            id := SELF:ParseQualifiedName()
         ENDIF
         VAR aParams := List<XSourceParameterSymbol>{}
         IF Matches(XSharpLexer.LPAREN)
            aParams := SELF:ParseParameterList( FALSE, OUT VAR _)
         ELSEIF Matches(XSharpLexer.LBRKT)
            aParams := SELF:ParseParameterList(TRUE, OUT VAR _)
         ENDIF
         VAR sType := SELF:ParseDataType(FALSE)
         LOCAL lSingleLine AS LOGIC
         DO WHILE ! SELF:Eos()
            IF Matches(XSharpLexer.AUTO, XSharpLexer.GET,XSharpLexer.SET, XSharpLexer.UDCSEP)
               lSingleLine := TRUE
            ENDIF
            Consume()
         ENDDO
         SELF:GetSourceInfo(_start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR source)
         SELF:ReadLine()
         VAR xMember := XSourceMemberSymbol{id, Kind.Property, _attributes, range, interval,sType} {SingleLine := lSingleLine}
         xMember:SourceCode := source
         IF SELF:CurrentType != NULL
            CurrentType:AddMember(xMember)
         ENDIF
         xMember:AddParameters(aParams)
         RETURN <XSourceEntity>{xMember}

      PRIVATE METHOD ParseEvent() AS IList<XSourceEntity>
            IF ! Expect(XSharpLexer.EVENT)
               RETURN NULL
            ENDIF

         // Inside the editor the ADD and REMOVE accessors are parsed as blocks and not as entities
/*
event_              : (Attributes=attributes)? (Modifiers=memberModifiers)?
                       E=EVENT (ExplicitIface=nameDot)? Id=identifier (AS Type=datatype)?
                       ( end=EOS
                        | (LineAccessors += eventLineAccessor)+ end=EOS
                        | Multi=eos (Accessors+=eventAccessor)+ END EVENT? EOS
                       )
                    ;

eventLineAccessor   : Attributes=attributes? Modifiers=accessorModifiers?
                      ( {InputStream.La(2) != REMOVE}? Key=ADD ExprList=expressionList?
                      | {InputStream.La(2) != ADD}?    Key=REMOVE ExprList=expressionList?
                      | Key=(ADD|REMOVE) )
                    ;
eventAccessor       : Attributes=attributes? Modifiers=accessorModifiers?
                      ( Key=ADD     END=eos StmtBlk=statementBlock END ADD?
                      | Key=ADD     UDCSEP ExpressionBody=expression              // New: Expression Body
                      | Key=REMOVE  END=eos StmtBlk=statementBlock END REMOVE? )
                      | Key=REMOVE  UDCSEP ExpressionBody=expression              // New: Expression Body
                      end=eos
                    ;
*/
         VAR id := SELF:ParseQualifiedName()
         VAR strType  := SELF:ParseDataType(FALSE)
         LOCAL lSingleLine AS LOGIC
         DO WHILE ! SELF:Eos()
            IF Matches( XSharpLexer.ADD,XSharpLexer.REMOVE)
               lSingleLine := TRUE
            ENDIF
            Consume()
         ENDDO
         SELF:GetSourceInfo(_start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR source)
         SELF:ReadLine()
         VAR xMember := XSourceMemberSymbol{id, Kind.Event, _attributes, range, interval,strType}  {SingleLine := lSingleLine}
         xMember:SourceCode := source
         IF SELF:CurrentType != NULL
            CurrentType:AddMember(xMember)
         ENDIF
         RETURN <XSourceEntity>{xMember}

      PRIVATE METHOD IsOperatorToken(token AS INT) AS LOGIC
/*
overloadedOps       : Token= (PLUS | MINUS | NOT | TILDE | INC | DEC | TRUE_CONST | FALSE_CONST |
                              MULT | DIV | MOD | AMP | PIPE | LSHIFT | RSHIFT | EEQ | NEQ | NEQ2 |
                              GT | LT | GTE | LTE |
                              AND | OR )  // these two do not exist in C# and are mapped to & and |
                    ;

conversionOps		: Token=( IMPLICIT | EXPLICIT )
                    ;
*/
            SWITCH token
            CASE XSharpLexer.PLUS
            CASE XSharpLexer.MINUS
            CASE XSharpLexer.NOT
            CASE XSharpLexer.TILDE
            CASE XSharpLexer.INC
            CASE XSharpLexer.DEC
            CASE XSharpLexer.TRUE_CONST
            CASE XSharpLexer.FALSE_CONST
            CASE XSharpLexer.MULT
            CASE XSharpLexer.DIV
            CASE XSharpLexer.MOD
            CASE XSharpLexer.AMP
            CASE XSharpLexer.PIPE
            CASE XSharpLexer.LSHIFT
            CASE XSharpLexer.RSHIFT
            CASE XSharpLexer.EEQ
            CASE XSharpLexer.NEQ
            CASE XSharpLexer.NEQ2
            CASE XSharpLexer.GT
            CASE XSharpLexer.LT
            CASE XSharpLexer.GTE
            CASE XSharpLexer.LTE
            CASE XSharpLexer.AND
            CASE XSharpLexer.OR
            CASE XSharpLexer.IMPLICIT
            CASE XSharpLexer.EXPLICIT
            RETURN TRUE
            END SWITCH
            RETURN FALSE

      PRIVATE METHOD ParseOperator() AS IList<XSourceEntity>
            IF ! Expect(XSharpLexer.OPERATOR)
               RETURN NULL
            ENDIF

/*
operator_           : Attributes=attributes? Modifiers=operatorModifiers?
                      o1=OPERATOR (Operation=overloadedOps | Conversion=conversionOps) Gt=GT?
                      ParamList=parameterList
                      (AS Type=datatype)?
                      (UDCSEP ExpressionBody=expression)?             // New: Expression Body
                      end=eos
                      StmtBlk=statementBlock
                     (END o1=OPERATOR EOS)?
                    ;

*/
         LOCAL t1 AS IToken
         LOCAL t2 AS IToken

         IF SELF:IsOperatorToken(SELF:La1)
            t1 := SELF:ConsumeAndGet()
         ENDIF
         IF SELF:La1 == XSharpLexer.GT
            t2 := SELF:ConsumeAndGet()
         ENDIF
         VAR aParams     := SELF:ParseParameterList(FALSE, OUT VAR _)
         VAR sType       := SELF:ParseDataType(FALSE)
         SELF:ParseExpressionBody()
         SELF:GetSourceInfo(_start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR source)

         VAR id := t1:GetText()+ IIF(t2 != NULL, t2:GetText(),"")
         SELF:ReadLine()
         VAR xMember := XSourceMemberSymbol{id, Kind.Operator, _attributes, range, interval,sType}
         xMember:SourceCode := source
         IF SELF:CurrentType != NULL
            CurrentType:AddMember(xMember)
         ENDIF
         xMember:AddParameters(aParams)
         RETURN <XSourceEntity>{xMember}

      PRIVATE METHOD ParseConstructor() AS IList<XSourceEntity>

/*constructor         :  (Attributes=attributes)? (Modifiers=constructorModifiers)?
                      c1=CONSTRUCTOR (ParamList=parameterList)? (AS VOID)? // As Void is allowed but ignored
                        (CallingConvention=callingconvention)?
                        (CLASS ClassId=identifier)?
                        (UDCSEP ExpressionBody=expression)?               // New: Expression Body
                        end=eos
                      (Chain=constructorchain)?
                      StmtBlk=statementBlock
                      (END c2=CONSTRUCTOR EOS)?
                    ;
*/
         IF ! Expect(XSharpLexer.CONSTRUCTOR)
            RETURN NULL
         ENDIF
         VAR id  := XLiterals.ConstructorName
         VAR aParams     := SELF:ParseParameterList(FALSE, OUT VAR _)
         VAR asType      := SELF:ParseDataType(FALSE)
         VAR callconv    := SELF:ParseCallingConvention()
         VAR classClause := SELF:ParseOptionalClassClause()
         SELF:ParseExpressionBody()
         SELF:GetSourceInfo(_start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR source)
         SELF:ReadLine()
         VAR xMember := XSourceMemberSymbol{id, Kind.Constructor, _attributes, range, interval,""}
         xMember:SourceCode := source
         xMember:AddParameters(aParams)
         IF SELF:CurrentType != NULL
            CurrentType:AddMember(xMember)
         ENDIF

         RETURN <XSourceEntity>{xMember}

      PRIVATE METHOD ParseDestructor() AS IList<XSourceEntity>
/*
destructor          : (Attributes=attributes)? (Modifiers=destructorModifiers)?
                      d1=DESTRUCTOR (LPAREN RPAREN)?
                      (CLASS ClassId=identifier)?
                      (UDCSEP ExpressionBody=expression)?               // New: Expression Body
                      end=eos
                      StmtBlk=statementBlock
                      (END d2=DESTRUCTOR EOS)?
                    ;

*/
         IF ! Expect(XSharpLexer.DESTRUCTOR)
            RETURN NULL
         ENDIF

         VAR id  := XLiterals.DestructorName
         IF SELF:La1 == XSharpLexer.LPAREN .AND. SELF:La2 == XSharpLexer.RPAREN
            Consume()
            Consume()
         ENDIF
         VAR classClause := SELF:ParseOptionalClassClause()
         SELF:ParseExpressionBody()
         SELF:GetSourceInfo(_start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR source)
         SELF:ReadLine()
         VAR xMember := XSourceMemberSymbol{id, Kind.Destructor, _attributes, range, interval,"VOID"}
         xMember:SourceCode := source
         IF SELF:CurrentType != NULL
            SELF:CurrentType:AddMember(xMember)
         ENDIF
         RETURN <XSourceEntity>{xMember}

      PRIVATE METHOD ParseClassVars() AS IList<XSourceEntity>
/*
classvars           : (Attributes=attributes)? Modifiers=classvarModifiers
                      Vars+=classvar (COMMA Vars+=classvar)*
                      eos
                    ;
*/

         VAR result := List<XSourceEntity>{}
         VAR classVars := List<XSourceMemberSymbol>{}
            VAR classVar := ParseClassVar(Kind.Field)
            classVar:SourceCode := classVar:ModVis +" "+classVar:SourceCode
            classVars:Add(classVar)
            DO WHILE Expect(XSharpLexer.COMMA)
                classVar := ParseClassVar(Kind.Field)
                classVar:SourceCode := classVar:ModVis +" "+classVar:SourceCode
                classVars:Add(classVar)
            ENDDO
            SELF:ReadLine()
            CopyClassVarTypes(classVars)
            result:AddRange(classVars)
         RETURN result
         #endregion

     PRIVATE METHOD CopyClassVarTypes(classVars AS List<XSourceMemberSymbol>)  AS VOID
         IF classVars:Count > 1
            VAR type := ""
                classVars:Reverse()
                FOREACH VAR classVar IN classVars
                    IF !classVar:IsTyped
                        classVar:ReturnType := type
                        classVar:SourceCode += " AS "+type
                    ELSE
                        type := classVar:ReturnType
                    ENDIF
                NEXT
                classVars:Reverse()
            ENDIF
         RETURN

      #region Enum
      PRIVATE METHOD ParseEnum() AS IList<XSourceEntity>
/*
enum_               : (Attributes=attributes)? (Modifiers=classModifiers)?
                      E=ENUM (Namespace=nameDot)? Id=identifier ((AS|INHERIT) Type=datatype)? e=eos
                      (Members+=enummember)+
                      END ENUM? EOS
                    ;
*/
         IF ! Expect(XSharpLexer.ENUM)
            RETURN NULL
         ENDIF

         VAR id := SELF:ParseQualifiedName()
         VAR type := ""
         IF Matches(XSharpLexer.AS)
            type := SELF:ParseDataType(FALSE)
         ELSEIF Expect(XSharpLexer.INHERIT)
            type := SELF:ParseTypeName()
         ENDIF
         SELF:GetSourceInfo(_start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR source)
         SELF:ReadLine()
         VAR xType := XSourceTypeSymbol{id, Kind.Enum, _attributes, range, interval, _file} {BaseTypeName := type}
         xType:SourceCode := source
         RETURN <XSourceEntity>{xType}

      PRIVATE METHOD ParseEnumMember() AS IList<XSourceEntity>
/*
enummember          : (Attributes=attributes)? MEMBER? Id=identifier (Op=assignoperator Expr=expression)? eos
                    ;
*/

         VAR att := SELF:ParseAttributes()
         Expect(XSharpLexer.MEMBER)    // Optional !
         VAR strValue := ""
         VAR id := SELF:ParseQualifiedName()
         IF ExpectAssignOp()
            strValue := SELF:ParseExpression()
         ENDIF
         SELF:GetSourceInfo(_start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR source)
         SELF:ReadLine()
         VAR type := CurrentType?:Name
         VAR xMember := XSourceMemberSymbol{id, Kind.EnumMember, _attributes, range, interval, type} {SingleLine := TRUE, @@Value := strValue}
         xMember:File := SELF:_file
         xMember:SourceCode := source
         xMember:IsStatic := TRUE
         RETURN <XSourceEntity>{xMember}

         #endregion

      #region VO Global, Define, Struct, Union
      PRIVATE METHOD ParseVOGlobals() AS IList<XSourceEntity>
/*
                    // [STATIC] GLOBAL [CONST] Identifier [:= Expression] [, Identifier2 [:= Expression2] ] [AS Type]
                    // STATIC          [CONST] Identifier [:= Expression] [, Identifier2 [:= Expression2] ] [AS Type]
voglobal            : (Attributes=attributes)? (Modifiers=funcprocModifiers)? Global=GLOBAL (Const=CONST)? Vars=classVarList end=EOS
                    | (Attributes=attributes)? Static=STATIC (Const=CONST)? Vars=classVarList end=EOS
                    ;
*/
         IF ! Expect(XSharpLexer.GLOBAL)
            RETURN NULL
         ENDIF
         Expect(XSharpLexer.CONST)
         VAR result := List<XSourceEntity>{}
         VAR classVars := List<XSourceMemberSymbol>{}
         VAR classVar := ParseClassVar(Kind.VOGlobal)
         classVar:SourceCode := classVar:ModVis+" GLOBAL "+ classVar:SourceCode
         classVars:Add(classVar)
         DO WHILE Expect(XSharpLexer.COMMA)
            classVar := ParseClassVar(Kind.VOGlobal)
            classVar:SourceCode := classVar:ModVis+" GLOBAL "+ classVar:SourceCode
            classVars:Add(classVar)
        ENDDO
         SELF:ReadLine()
         CopyClassVarTypes(classVars)
         result:AddRange(classVars)
         RETURN result

      PRIVATE METHOD ParseVoDefine() AS IList<XSourceEntity>
/*
vodefine            : (Modifiers=funcprocModifiers)?
                      D=DEFINE Id=identifier Op=assignoperator Expr=expression (AS DataType=typeName)? end=EOS
                    ;

*/
         IF ! Expect(XSharpLexer.DEFINE)
            RETURN NULL
         ENDIF

         VAR id := SELF:ParseQualifiedName()
         LOCAL strValue AS STRING
         IF ExpectAssignOp()
            strValue := SELF:ParseExpression()
         ENDIF
         VAR type := SELF:ParseDataType(FALSE)
         SELF:GetSourceInfo(_start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR source)
         SELF:ReadLine()
         VAR xMember := XSourceMemberSymbol{id, Kind.VODefine, _attributes, range,interval, type} {SingleLine := TRUE, @@Value := strValue}
         xMember:SourceCode := source
         xMember:File := SELF:_file
         RETURN <XSourceEntity>{xMember}

      PRIVATE METHOD ParseVoStruct() AS IList<XSourceEntity>
/*
vostruct            : (Modifiers=votypeModifiers)?
                      V=VOSTRUCT (Namespace=nameDot)? Id=identifier (ALIGN Alignment=INT_CONST)? e=eos
                      (Members+=vostructmember)+
                      (END VOSTRUCT EOS)?
                    ;
*/
         IF ! Expect(XSharpLexer.VOSTRUCT)
            RETURN NULL
         ENDIF

         VAR id := SELF:ParseQualifiedName()
         VAR sAlign := ""
         IF Expect(XSharpLexer.ALIGN)
            IF ExpectAndGet(XSharpLexer.INT_CONST, OUT VAR t2)
               sAlign := t2:GetText() // Align number
            ENDIF
         ENDIF
         // read to EndOfLine
         SELF:GetSourceInfo(_start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR source)
         SELF:ReadLine()
         VAR xType := XSourceTypeSymbol{id, Kind.VOStruct, _attributes, range, interval,_file}
         xType:SourceCode := source
         IF String.IsNullOrEmpty(sAlign)
            xType:AddInterface(sAlign)
         ENDIF
         RETURN <XSourceEntity>{xType}

      PRIVATE METHOD ParseVOUnion() AS IList<XSourceEntity>
/*
vounion             : (Modifiers=votypeModifiers)?
                      U=UNION (Namespace=nameDot)? Id=identifier e=eos
                      (Members+=vostructmember)+
                      (END UNION EOS)?
                    ;
*/
         IF ! Expect(XSharpLexer.UNION)
            RETURN NULL
         ENDIF

         VAR id := SELF:ParseQualifiedName()
         SELF:GetSourceInfo(_start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR source)
         SELF:ReadLine()
         VAR xType := XSourceTypeSymbol{id, Kind.Union, _attributes, range, interval,_file}
         xType:SourceCode := source
         RETURN <XSourceEntity>{xType}

      PRIVATE METHOD ParseVODLL() AS IList<XSourceEntity>
/*

vodll               : (Attributes=attributes)? (Modifiers=funcprocModifiers)? // Optional
                      D=DLL T=funcproctype Id=identifier ParamList=parameterList (AS Type=datatype)?
                      (CallingConvention=dllcallconv)? COLON
                      Dll=identifierString (DOT Extension=identifierString)?
                      (	Ordinal=REAL_CONST
                       |  DOT Entrypoint=identifierString Address=ADDROF? Number=INT_CONST? (NEQ2 INT_CONST)?
                      )

                      ( CharSet=(AUTO | ID) )?  // ID must be either ANSI or UNICODE
                      EOS
                    ;

*/
         IF ! Expect(XSharpLexer.DLL)
            RETURN NULL
         ENDIF
         IF ! ParseFuncProcType (OUT VAR kind)
            RETURN NULL
         ENDIF
         SELF:Consume()
         VAR sig     := SELF:ParseSignature()
         VAR colon   := SELF:ConsumeAndGetText()
         VAR dllName := SELF:ConsumeAndGetText()
         VAR dotName := ""
         IF Expect(XSharpLexer.DOT)
            dotName := SELF:ConsumeAndGetText()
         ENDIF
         SELF:GetSourceInfo(_start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR source)
         SELF:ReadLine()
         VAR xMember := XSourceMemberSymbol{sig, Kind.VODLL, _attributes, range, interval, _attributes:HasFlag(Modifiers.Static)} {SubType := kind }
         xMember:File := SELF:_file
         xMember:SourceCode := source
         RETURN <XSourceEntity>{xMember}

      PRIVATE METHOD ParseVoStructMember() AS IList<XSourceEntity>
/*
vostructmember      : MEMBER Dim=DIM Id=identifier LBRKT ArraySub=arraysub RBRKT (As=(AS | IS) DataType=datatype)? eos
                    | MEMBER Id=identifier (As=(AS | IS) DataType=datatype)? eos
                    ;
*/
         IF ! Expect(XSharpLexer.MEMBER)
            RETURN NULL
         ENDIF

         LOCAL sBracket AS STRING
         VAR isArray := Expect(XSharpLexer.DIM)
         VAR id := SELF:ParseQualifiedName()

         IF isArray
            sBracket := SELF:ParseArraySub()
         ENDIF
         VAR sType := SELF:ParseDataType(TRUE)
         SELF:GetSourceInfo(_start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR source)
         SELF:ReadLine()
         IF isArray
            sType += "[]"
         ENDIF
         VAR xMember := XSourceMemberSymbol{id, Kind.Field, _attributes, range, interval, sType} {SingleLine := TRUE, IsArray := isArray}
         xMember:File := SELF:_file
         xMember:SourceCode := source
         RETURN <XSourceEntity>{xMember}

         #endregion

      PRIVATE METHOD ParseClassVar(eKind AS Kind) AS XSourceMemberSymbol
/*

classvar            : (DIM=DIM)? Id=identifier (LBRKT ArraySub=arraysub RBRKT)?
                      (Op=assignoperator Initializer=expression)?
                      (As=(AS | IS) DataType=datatype)?
                    ;
*/
         LOCAL isDim  := FALSE  AS LOGIC
         LOCAL sId        AS STRING
         LOCAL sDefault   AS STRING
         LOCAL startToken AS IToken
         LOCAL endToken   AS IToken
         startToken := SELF:Lt1
         isDim := Expect(XSharpLexer.DIM)
         sId  := SELF:ParseIdentifier()
         VAR sBracket  := SELF:ParseArraySub()
         IF ExpectAssignOp()
            sDefault := SELF:ParseExpression()
         ENDIF
         VAR sType := SELF:ParseDataType(TRUE)
         endToken := SELF:LastToken
         SELF:GetSourceInfo(startToken, endToken, OUT VAR range, OUT VAR interval, OUT VAR source)
         VAR xMember   := XSourceMemberSymbol{sId,eKind, SELF:_attributes, range,interval,sType} {SingleLine := TRUE}
         xMember:ReturnType := sType
         xMember:SourceCode := source
         xMember:Value := sDefault
         IF isDim .OR. !String.IsNullOrEmpty(sBracket)
            xMember:IsArray := TRUE
         ENDIF
         RETURN xMember

      PRIVATE METHOD ParseArraySub AS STRING
/*
arraysub            : ArrayIndex+=expression (RBRKT LBRKT ArrayIndex+=expression)+		// x][y
                    | ArrayIndex+=expression (COMMA ArrayIndex+=expression)+			// x,y
                    | ArrayIndex+=expression
                    ;
*/
         LOCAL sBracket:= "" AS STRING
         DO WHILE SELF:La1 == XSharpLexer.LBRKT           // allow SomeId[1][2]
            sBracket += SELF:ConsumeAndGetText()
            DO WHILE SELF:La1 != XSharpLexer.RBRKT .AND. ! SELF:Eos()
               sBracket += SELF:ConsumeAndGetText()
            ENDDO
            IF SELF:La1 == XSharpLexer.RBRKT
               sBracket += SELF:ConsumeAndGetText()
            ENDIF
         ENDDO
         RETURN sBracket

      PRIVATE METHOD ParseArraySubFox AS STRING
/*
arraysub            : ArrayIndex+=expression (RPAREN LPAREN ArrayIndex+=expression)+		// x)(y
                    | ArrayIndex+=expression (COMMA ArrayIndex+=expression)+			// x,y
                    | ArrayIndex+=expression
                    ;
*/
         LOCAL sBracket:= "" AS STRING
         DO WHILE SELF:La1 == XSharpLexer.LPAREN           // allow SomeId(1)(2)
            sBracket += SELF:ConsumeAndGetText()
            DO WHILE SELF:La1 != XSharpLexer.RPAREN .AND. ! SELF:Eos()
               sBracket += SELF:ConsumeAndGetText()
            ENDDO
            IF SELF:La1 == XSharpLexer.RPAREN
               sBracket += SELF:ConsumeAndGetText()
            ENDIF
         ENDDO
         RETURN sBracket

      PRIVATE METHOD ParseSignature() AS XMemberSignature
/*
signature             : Id=identifier
                      TypeParameters=typeparameters?
                      (ParamList=parameterList)?
                      (AS Type=datatype)?
                      (ConstraintsClauses+=typeparameterconstraintsclause)*
                      (CallingConvention=callingconvention)?
                      (UDCSEP ExpressionBody=expression)?
                      ;

*/
         LOCAL oSig          AS XMemberSignature
         oSig                := XMemberSignature{}
         oSig:Id             := SELF:ParseQualifiedName()
         IF SELF:La1 == XSharpLexer.LT
            oSig:TypeParameters := SELF:ParseTypeParameters()
         ENDIF
         IF SELF:La1 == XSharpLexer.LPAREN
            var params        := SELF:ParseParameterList(FALSE,  OUT VAR isSelf)
            oSig:Parameters:AddRange(params)
            oSig:IsExtension  := isSelf
         ENDIF
         oSig:DataType       := SELF:ParseDataType(FALSE)
         DO WHILE SELF:La1 == XSharpLexer.WHERE .AND. ! SELF:Eos()
            oSig:TypeParameterContraints:Add(SELF:ParseTypeParameterConstraints())
         ENDDO
         oSig:CallingConvention  := SELF:ParseCallingConvention()
         IF oSig:CallingConvention == CallingConvention.Clipper
             FOREACH var parameter in oSig:Parameters
                 parameter:TypeName := XLiterals.UsualType
                 parameter:ParamType := ParamType.As
             NEXT
         ENDIF
         SELF:ParseExpressionBody()
         RETURN oSig

      PRIVATE METHOD ParseTypeParameters AS List<STRING>
         IF SELF:La1 != XSharpLexer.LT
            RETURN NULL
         ENDIF
         Consume()
         VAR aTypeParams := List<STRING>{}
         DO WHILE SELF:La1 != XSharpLexer.GT .AND. !SELF:Eos()
            VAR sParam := SELF:TokensAsString(ParseAttributes())

            IF SELF:La1 == XSharpLexer.IN .OR. SELF:La1 == XSharpLexer.OUT
               sParam += ConsumeAndGetText()+" "
            ENDIF
            IF SELF:IsId(SELF:La1)
               sParam += ConsumeAndGetText()
            ELSE
               EXIT
            ENDIF
            aTypeParams:Add(sParam)
            SELF:Expect(XSharpLexer.COMMA)
         ENDDO
         SELF:Expect(XSharpLexer.GT)
         RETURN aTypeParams

      PRIVATE METHOD ParseParameterList(lBracketed AS LOGIC, isSelf OUT LOGIC) AS List<XSourceParameterSymbol>
         isSelf := FALSE
         IF SELF:La1 != XSharpLexer.LPAREN .AND. ! lBracketed
            RETURN NULL
         ENDIF
         IF SELF:La1 != XSharpLexer.LBRKT .AND. lBracketed
            RETURN NULL
         ENDIF
         Consume()   // LParen
         VAR aResult  := List<XSourceParameterSymbol>{}
         LOCAL cond AS DelEndToken
         cond := { token => IIF (lBracketed, token == XSharpLexer.RBRKT, token == XSharpLexer.RPAREN ) }
         DO WHILE !cond(SELF:La1) .AND. ! SELF:Eos()
            LOCAL defaultExpr := NULL AS IList<XSharpToken>

            VAR start := SELF:Lt1
            VAR atts := SELF:TokensAsString(ParseAttributes())
            VAR sId   := ""
            VAR sTypeName := ""
            IF SELF:La1 == XSharpLexer.SELF
               isSelf := TRUE
               Consume()
            ENDIF
            IF SELF:IsId(SELF:La1)
               sId  += SELF:ConsumeAndGetText()
            ENDIF
            IF ExpectAssignOp()
               defaultExpr := SELF:ParseExpressionAsTokens()
            ENDIF

            VAR token := SELF:ConsumeAndGetAny(XSharpLexer.AS, XSharpLexer.IN, XSharpLexer.OUT, XSharpLexer.REF,XSharpLexer.PARAMS)
            IF token != NULL
               IF SELF:La1 == XSharpLexer.CONST
                  Consume()
               ENDIF
               sTypeName := SELF:ParseTypeName()
            ELSE
                IF _file:Project:ParseOptions:VOUntypedAllowed
                    sTypeName := "USUAL"
                ENDIF
            ENDIF
            LOCAL variable AS XSourceParameterSymbol
            VAR endToken := SELF:LastToken
            SELF:GetSourceInfo(start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR _)
            variable := XSourceParameterSymbol{CurrentEntity, sId, range,interval, sTypeName}
            IF token != NULL
               variable:ParamType := (ParamType) token.Type
            ENDIF
            IF defaultExpr != NULL
               variable:Value := SELF:TokensAsString(defaultExpr)
            ENDIF
            aResult:Add(variable)
            IF SELF:La1 == XSharpLexer.COMMA
               Consume()
            ELSE
               EXIT
            ENDIF
         ENDDO
         IF cond(SELF:La1)
            Consume()
         ENDIF
         RETURN aResult

      PRIVATE METHOD ParseTypeParameterConstraints() AS STRING

         IF SELF:La1 != XSharpLexer.WHERE
            RETURN ""
         ENDIF
         VAR result := SELF:ConsumeAndGetText() +" "
         IF SELF:IsId(SELF:La1) .AND. SELF:La2 == XSharpLexer.IS
            result     += SELF:ParseIdentifier() +" "
            result     += SELF:ConsumeAndGetText()+" "
            result     += SELF:ParseTypeParameterConstraint()+" "
            DO WHILE SELF:La1 == XSharpLexer.COMMA .AND. ! SELF:Eos()
               result += SELF:ConsumeAndGetText()+" "
               result     += SELF:ParseTypeParameterConstraint()+" "
            ENDDO
         ENDIF
         RETURN result

      PRIVATE METHOD ParseTypeParameterConstraint() AS STRING
         IF SELF:La1 == XSharpLexer.CLASS .OR. SELF:La1 == XSharpLexer.STRUCTURE
            RETURN ConsumeAndGetText()
         ELSEIF SELF:La1 == XSharpLexer.NEW .AND. SELF:La2 == XSharpLexer.LPAREN .AND. La3 == XSharpLexer.RPAREN
            RETURN "NEW()"
         ENDIF
         RETURN ParseTypeName()

      PRIVATE METHOD ParseDataType(lIsAllowed AS LOGIC) AS STRING
         IF SELF:La1 == XSharpLexer.AS .OR. (SELF:La1 == XSharpLexer.IS .AND. lIsAllowed)
            SELF:Consume()
            RETURN SELF:ParseTypeName()
         ENDIF
         RETURN ""

      PRIVATE METHOD ParseExpressionBody() AS STRING
         IF SELF:La1 != XSharpLexer.UDCSEP
            RETURN ""
         ENDIF
         SELF:Consume()
         RETURN SELF:ParseExpression()

      PRIVATE METHOD ParseCallingConvention() AS CallingConvention
/*
callingconvention	: Convention=(CLIPPER | STRICT | PASCAL | ASPEN | WINCALL | CALLBACK | FASTCALL | THISCALL)
                    ;
*/
         SWITCH SELF:La1
         CASE XSharpLexer.CLIPPER
         CASE XSharpLexer.STRICT
         CASE XSharpLexer.PASCAL
         CASE XSharpLexer.ASPEN
         CASE XSharpLexer.WINCALL
         CASE XSharpLexer.CALLBACK
         CASE XSharpLexer.FASTCALL
         CASE XSharpLexer.THISCALL
            VAR result := (CallingConvention) SELF:La1
            Consume()
            RETURN result
         END SWITCH
         RETURN CallingConvention.None

      PRIVATE METHOD ParseFuncProcType( kind OUT Kind) AS LOGIC

            IF SELF:La1 == XSharpLexer.FUNCTION
                kind := Kind.Function
            ELSEIF SELF:La1 == XSharpLexer.PROCEDURE
                kind := Kind.Procedure
            ELSE
                kind := Kind.Unknown
                RETURN FALSE
            ENDIF
            RETURN TRUE

      PRIVATE METHOD ParseLocalFuncProc() AS IList<XSourceEntity>
         /*

         localfuncproc       :  (Modifiers=localfuncprocModifiers)?
                                 LOCAL T=funcproctype Sig=signature
                                 end=eos
                                 StmtBlk=statementBlock
                                 END T2=funcproctype   EOS
                     ;

         */
            LOCAL kind AS Kind
            IF SELF:La1 != XSharpLexer.LOCAL
                RETURN NULL
            ENDIF
            SELF:Consume()      // Local
            IF ! ParseFuncProcType (OUT kind)
               RETURN NULL
            ENDIF
            SELF:Consume()      // Function or Procedure
            IF kind == Kind.Function
                kind := Kind.LocalFunc
            ELSEIF kind == Kind.Procedure
                kind := Kind.LocalProc
            ENDIF
            VAR sig := SELF:ParseSignature()
            SELF:GetSourceInfo(_start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR source)
            SELF:ReadLine()
            _attributes |= Modifiers.Private
            _attributes &= (~Modifiers.Export)
            VAR xMember := XSourceMemberSymbol{sig, kind, _attributes, range, interval, false}
            xMember:SourceCode := source
            xMember:File := SELF:_file
            RETURN <XSourceEntity>{xMember}

      PRIVATE METHOD IsAssignOp (nToken AS LONG) AS LOGIC
         RETURN nToken == XSharpLexer.ASSIGN_OP .OR. nToken == XSharpLexer.EQ

      PRIVATE METHOD ParseTypeName() AS STRING
         LOCAL result := "" AS STRING
         SWITCH SELF:La1

         CASE XSharpLexer.ID
            result := SELF:ParseQualifiedName()

         CASE XSharpLexer.ARRAY
            result :=  ConsumeAndGetText()
            IF SELF:La1 == XSharpLexer.OF
               SELF:Consume()
               var subtype := SELF:ParseTypeName()
               result := "ARRAY<"+subtype+">"
            ENDIF
        CASE XSharpLexer.CODEBLOCK
        CASE XSharpLexer.DATE
        CASE XSharpLexer.FLOAT
        CASE XSharpLexer.PSZ
        CASE XSharpLexer.SYMBOL
        CASE XSharpLexer.USUAL
            result :=  ConsumeAndGetText()
        CASE XSharpLexer.BYTE
        CASE XSharpLexer.CHAR
        CASE XSharpLexer.DATETIME
        CASE XSharpLexer.DECIMAL
        CASE XSharpLexer.DWORD
        CASE XSharpLexer.DYNAMIC
        CASE XSharpLexer.INT
        CASE XSharpLexer.INT64
        CASE XSharpLexer.LOGIC
        CASE XSharpLexer.LONGINT
        CASE XSharpLexer.OBJECT
        CASE XSharpLexer.PTR
        CASE XSharpLexer.REAL4
        CASE XSharpLexer.REAL8
        CASE XSharpLexer.SHORTINT
        CASE XSharpLexer.STRING
        CASE XSharpLexer.UINT64
        CASE XSharpLexer.VOID
         CASE XSharpLexer.WORD
            result :=  SELF:ConsumeAndGetText()
         OTHERWISE
            IF XSharpLexer.IsKeyword(SELF:La1)
               result :=  SELF:ConsumeAndGetText()
            ENDIF
         END SWITCH
         IF result:Length > 0
            VAR suffix := ParseTypeSuffix()
            IF suffix:Trim() == "?"
                result := "Nullable<"+result+">"
            ELSE
                result += suffix
            ENDIF
         ENDIF
         RETURN result:Trim()

      PRIVATE METHOD ParseTypeSuffix AS STRING
         IF SELF:La1 == XSharpLexer.PTR
            RETURN " "+SELF:ConsumeAndGetText()
         ELSEIF SELF:La1 = XSharpLexer.QMARK
            RETURN SELF:ConsumeAndGetText()
         ELSEIF SELF:La1 == XSharpLexer.LBRKT .and. (SELF:La2 != XSharpLexer.ID .and. SELF:La2 != XSharpLexer.UDC_KEYWORD .and. ! XSharpLexer.IsKeyword(SELF:La2))
            VAR tokens := List<XSharpToken>{}
            tokens:Add(SELF:ConsumeAndGet())
            LOCAL openCount := 1 as LONG
            LOCAL closed := FALSE AS LOGIC
            DO WHILE ! SELF:Eos() .AND. ! closed
               SWITCH SELF:La1
               CASE XSharpLexer.RBRKT
                  tokens:Add(SELF:ConsumeAndGet())
                  openCount -= 1
                  IF openCount == 0 .and. SELF:La1 != XSharpLexer.LBRKT
                     closed := TRUE
                  ENDIF
               CASE XSharpLexer.LBRKT
                  openCount += 1
                  tokens:Add(SELF:ConsumeAndGet())
               CASE XSharpLexer.COMMA
                  tokens:Add(SELF:ConsumeAndGet())
               OTHERWISE
                  closed := TRUE
               END SWITCH
            ENDDO
            RETURN SELF:TokensAsString(tokens)
         ELSEIF SELF:La1 == XSharpLexer.LT
            VAR result := ""
            DO WHILE SELF:La1 != XSharpLexer.GT .AND. ! SELF:Eos()
               result += SELF:ConsumeAndGetText()
            ENDDO
            IF SELF:La1 == XSharpLexer.GT
               result += SELF:ConsumeAndGetText()
            ENDIF
            RETURN result

         ENDIF
         RETURN ""

      PRIVATE METHOD ParseExpressionAsTokens AS IList<XSharpToken>
         // parse until SELF:Eos() or tokens such as AS, IS,
         LOCAL nested := 0 AS LONG
         LOCAL done  := FALSE AS LOGIC
         VAR tokens := List<XSharpToken>{}

         DO WHILE ! SELF:Eos() .AND. ! done
            SWITCH SELF:La1
            CASE XSharpLexer.LT
            CASE XSharpLexer.LPAREN
            CASE XSharpLexer.LBRKT
            CASE XSharpLexer.LCURLY
               nested++
            CASE XSharpLexer.GT
            CASE XSharpLexer.RPAREN
            CASE XSharpLexer.RBRKT
            CASE XSharpLexer.RCURLY
                  nested--

            CASE XSharpLexer.AS
            CASE XSharpLexer.IS
            CASE XSharpLexer.COMMA
                  // The comma is used for a comma separated list of expression.
                  // however inside a method call or constructor call we should have nested > 0
                  IF (nested == 0)
                     done := TRUE
                  ENDIF

            OTHERWISE
                  // other keywords, operators etc.
                  //
               NOP
            END SWITCH
            IF !done
               tokens:Add(ConsumeAndGet())
            ENDIF
         ENDDO
         RETURN tokens

      PRIVATE METHOD ParseExpression() AS STRING
         RETURN TokensAsString(SELF:ParseExpressionAsTokens())

      PRIVATE METHOD ParseForLocalDeclaration() AS LOGIC
         VAR tokens := List<IToken>{}
         VAR firstToken := SELF:Lt1
         SWITCH firstToken.Type
         CASE XSharpLexer.LOCAL
         CASE XSharpLexer.VAR
         CASE XSharpLexer.STATIC
            RETURN SELF:ParseDeclarationStatement()
         CASE XSharpLexer.PUBLIC
         CASE XSharpLexer.PRIVATE
         CASE XSharpLexer.MEMVAR
         CASE XSharpLexer.PARAMETERS
         CASE XSharpLexer.LPARAMETERS
         CASE XSharpLexer.DIMENSION
         CASE XSharpLexer.DECLARE
            RETURN SELF:ParseXBaseDeclarationStatement()

         CASE XSharpLexer.ID WHEN SELF:_firstTokenOnLine != NULL
            // The PRIVATE and PUBLIC keyword may have been parsed as Modifier
            SWITCH SELF:_firstTokenOnLine:Type
            CASE XSharpLexer.PRIVATE
            CASE XSharpLexer.PUBLIC
                SELF:ParseMemvarAllocationStatement(SELF:_firstTokenOnLine:Type)
                RETURN TRUE
            END SWITCH

         END SWITCH

         DO WHILE ! SELF:Eos()
            IF SELF:Matches(XSharpLexer.IMPLIED, XSharpLexer.VAR)
               IF SELF:IsId(SELF:La2)
                  LOCAL kind := ImpliedKind.Assignment as ImpliedKind
                  VAR start := SELF:Lt2
                  Consume()      // IMPLIED or VAR
                  VAR id    := SELF:ParseIdentifier()
                  LOCAL expr AS IList<XSharpToken>
                  IF SELF:IsAssignOp(SELF:La1) .OR. SELF:La1 == XSharpLexer.IN
                     IF SELF:La1 == XSharpLexer.IN
                        kind := ImpliedKind.InCollection
                     ELSE
                        kind := ImpliedKind.Assignment
                     ENDIF
                     SWITCH firstToken.Type
                     CASE XSharpLexer.FOR
                        kind := ImpliedKind.LoopCounter
                     CASE XSharpLexer.FOREACH
                        kind := ImpliedKind.InCollection
                     CASE XSharpLexer.BEGIN
                     CASE XSharpLexer.USING
                        kind := ImpliedKind.Using
                     END SWITCH
                     Consume()
                     // get the text after IN or the expression after the assignment operator
                     expr := SELF:ParseExpressionAsTokens()
                  ENDIF
                  SELF:GetSourceInfo(start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR _)
                  VAR xVar := XSourceImpliedVariableSymbol{SELF:CurrentEntity, id, range, interval }
                  xVar:Expression  := expr
                  xVar:ImpliedKind := kind
                  xVar:TypeName    := TokensAsString(expr):Trim()
                  SELF:_locals:Add(xVar)
                  SELF:ReadLine()
               ENDIF
            ELSEIF SELF:IsId(SELF:La1) .AND. SELF:La2 == XSharpLexer.AS
               VAR start := SELF:Lt1
               VAR id    := SELF:ParseIdentifier()
               VAR type  := SELF:ParseDataType(FALSE)
               SELF:GetSourceInfo(start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR _)
               VAR xVar := XSourceVariableSymbol{SELF:CurrentEntity, id, range, interval, IIF(type == "", _missingType, type) }
               SELF:_locals:Add(xVar)

            ELSEIF SELF:La1 == XSharpLexer.LOCAL .AND. SELF:IsId(SELF:La2)
               VAR start := SELF:Lt1
               LOCAL expression := List<XSharpToken>{} AS IList<XSharpToken>
               Consume()
               VAR id    := SELF:ParseIdentifier()
               IF SELF:ExpectAssignOp()
                   expression := SELF:ParseExpressionAsTokens()
               ENDIF
               LOCAL type := "" AS STRING
               LOCAL lIsIs := SELF:La1 == XSharpLexer.IS AS LOGIC
               IF SELF:ExpectAny(XSharpLexer.AS, XSharpLexer.IS)
                    type  := SELF:ParseTypeName()
               ELSE
                    type := SELF:_missingType
               ENDIF
               SELF:GetSourceInfo(start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR _)
               VAR xVar := XSourceVariableSymbol{SELF:CurrentEntity, id, range, interval, type }
               IF lIsIs
                    xVar:LocalType := LocalType.Is
               ENDIF
               xVar:Expression := expression
               SELF:_locals:Add(xVar)

            ENDIF
            Consume()
         ENDDO
         RETURN TRUE

      PRIVATE METHOD ParseForLocals() AS VOID
         // Check for method call with OUT VAR or OUT id AS TYPE
         // Check for <expr> IS <type> VAR <id>
         // also parses the EOS characters following the line
         DO WHILE ! SELF:Eos()
            IF SELF:La1 == XSharpLexer.IS
               VAR start := SELF:Lt1
               Consume() // IS token
               VAR type := SELF:ParseTypeName()
               IF SELF:La1 == XSharpLexer.VAR
                  Consume()   // VAR
                  VAR id            := SELF:ParseIdentifier()
                  SELF:GetSourceInfo(start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR _)
                  VAR xVar          := XSourceImpliedVariableSymbol{SELF:CurrentEntity, id, range, interval}
                  xVar:TypeName     := type:Trim()
                  xVar:ImpliedKind  := ImpliedKind.TypeCheck
                  SELF:_locals:Add(xVar)
               ENDIF
            ELSEIF SELF:La1 == XSharpLexer.OUT
               // OUT Id AS Type
               VAR start := SELF:Lt1
               Consume() // OUT
               IF SELF:IsId(SELF:La2) .AND. La3 == XSharpLexer.AS
                  VAR id   := SELF:ParseIdentifier()
                  VAR type := SELF:ParseDataType(FALSE)
                  SELF:GetSourceInfo(start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR _)
                  VAR xVar     := XSourceVariableSymbol{SELF:CurrentEntity, id, range, interval, iif(type == "", _missingType, type)}
                  SELF:_locals:Add(xVar)
               ELSEIF SELF:La2 == XSharpLexer.VAR
                  // OUT VAR Id, when Id = '_' then discard and do not create a local
                  Consume()   // Var
                  VAR id         := SELF:ParseIdentifier()
                  IF id          == "_"
                     SELF:GetSourceInfo(start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR _)
                     VAR xVar           := XSourceImpliedVariableSymbol{SELF:CurrentEntity, id, range, interval}
                     xVar:ImpliedKind := ImpliedKind.OutParam
                     // Todo: Get Expression for OUT variable
                     SELF:_locals:Add(xVar)
                  ENDIF
               ELSEIF SELF:La2 == XSharpLexer.NULL
                  // OUT NULL, also discard
                  Consume()
               ENDIF
            ELSEIF LastToken:Type == XSharpLexer.CASE .and. SELF:IsId(SELF:La1) .and. SELF:La2 == XSharpLexer.AS       // CASE x as SomeType
               VAR start := SELF:Lt1
               VAR id    := SELF:ParseIdentifier()
               VAR type  := SELF:ParseDataType(FALSE)
               SELF:GetSourceInfo(start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR _)
               VAR xVar     := XSourceVariableSymbol{SELF:CurrentEntity, id, range, interval, iif(type == "", _missingType, type)}
               SELF:_locals:Add(xVar)
            ELSE
               Consume()
            ENDIF
         ENDDO
         DO WHILE SELF:La1 == XSharpLexer.EOS
            Consume() // Consume the EOS
         ENDDO

      PRIVATE METHOD ParseStatement() AS VOID
         IF ! SELF:_collectLocals
            SELF:ReadLine()
            RETURN
         ENDIF
         DO WHILE SELF:Eos() .AND. !SELF:Eoi()
             Consume()
         ENDDO
         SELF:ParseUdcTokens()  // Read UDC tokens on the current line
         SWITCH SELF:La1
         CASE XSharpLexer.FIELD
            IF !SELF:ParseFieldStatement()
               SELF:ReadLine()
            ENDIF

         CASE XSharpLexer.FOREACH
         CASE XSharpLexer.FOR
            SELF:Consume()
            IF ! SELF:ParseForLocalDeclaration()
               SELF:ReadLine()
            ENDIF
         CASE XSharpLexer.LOCAL
         CASE XSharpLexer.STATIC
         CASE XSharpLexer.VAR
            IF ! SELF:ParseDeclarationStatement()
               SELF:ReadLine()
            ENDIF
         CASE XSharpLexer.MEMVAR
         CASE XSharpLexer.PARAMETERS
         CASE XSharpLexer.LPARAMETERS
         CASE XSharpLexer.DIMENSION
         CASE XSharpLexer.DECLARE
            IF ! SELF:ParseXBaseDeclarationStatement()
               SELF:ReadLine()
            ENDIF
         CASE XSharpLexer.ID
            IF SELF:LastToken != NULL .AND. (SELF:LastToken:Type == XSharpLexer.PUBLIC .OR. SELF:LastToken:Type == XSharpLexer.PRIVATE)
               SELF:PushBack()
               IF ! SELF:ParseXBaseDeclarationStatement()
                  SELF:ReadLine()
               ENDIF
            ELSE
               SELF:ParseForLocals()
            ENDIF

         OTHERWISE
            SELF:ParseForLocals()
         END SWITCH
         RETURN

      PRIVATE METHOD ParseDeclarationStatement() AS LOGIC
//
//localdecl          : LOCAL (STATIC=STATIC)? LocalVars+=localvar (COMMA LocalVars+=localvar)*			END=eos #commonLocalDecl
//                   | STATIC=STATIC LOCAL    LocalVars+=localvar (COMMA LocalVars+=localvar)*			END=eos #commonLocalDecl
//                   | {!XSharpLexer.IsKeyword(InputStream.La(2))}?   // STATIC Identifier , but not STATIC <Keyword>
//                     STATIC=STATIC          LocalVars+=localvar (COMMA LocalVars+=localvar)*			END=eos #commonLocalDecl
//                   // The following rules allow STATIC in the parser,
//                   // but the treetransformation will produce an error 9044 for STATIC implied
//                   | STATIC=STATIC? VAR           ImpliedVars+=impliedvar (COMMA ImpliedVars+=impliedvar)*  END=eos #varLocalDecl
//                   | STATIC=STATIC LOCAL? IMPLIED ImpliedVars+=impliedvar (COMMA ImpliedVars+=impliedvar)*  END=eos #varLocalDecl

         LOCAL lStatic AS LOGIC
         LOCAL lImplied := FALSE AS LOGIC
         IF SELF:La1 == XSharpLexer.LOCAL
            Consume()
            IF SELF:La1 == XSharpLexer.STATIC
               lStatic := TRUE
               Consume()
            ENDIF
            IF SELF:La1 == XSharpLexer.IMPLIED
               lImplied := TRUE
               Consume()
            ENDIF
            IF SELF:La1 == XSharpLexer.STATIC
               lStatic := TRUE
               Consume()
            ENDIF
            IF SELF:La1 == XSharpLexer.ARRAY .and. _dialect == XSharpDialect.FoxPro
               Consume()
            ENDIF
         ELSEIF SELF:La1 == XSharpLexer.STATIC
            lStatic := TRUE
            Consume()
            IF SELF:La1 == XSharpLexer.LOCAL
               Consume()
            ELSEIF SELF:La1 == XSharpLexer.VAR
               Consume()
               lImplied  := TRUE
            ELSEIF XSharpLexer.IsKeyword(SELF:La1)      // STATIC Keyword should exit
               RETURN FALSE
            ENDIF
            IF SELF:La1 == XSharpLexer.IMPLIED
               lImplied := TRUE
               Consume()
            ENDIF
         ELSEIF SELF:La1 == XSharpLexer.VAR
            Consume()
            lImplied  := TRUE
         ELSE
            RETURN FALSE
         ENDIF
         VAR result := List<XSourceVariableSymbol>{}
         IF lImplied
            VAR xVar := SELF:ParseImpliedVar()
            result:Add(xVar)
            DO WHILE SELF:La1 == XSharpLexer.COMMA
               Consume()
               xVar := SELF:ParseImpliedVar()
               result:Add(xVar)
            ENDDO
         ELSE
            VAR xVar := SELF:ParseLocalVar()
            result:Add(xVar)
            DO WHILE SELF:La1 == XSharpLexer.COMMA
               Consume()
               xVar := SELF:ParseLocalVar()
               result:Add(xVar)
            ENDDO
         ENDIF
         // copy type forward
         VAR missingTypes := List<XSourceVariableSymbol>{}
         FOREACH VAR xVar IN result
            IF xVar:TypeName == XLiterals.NoType
               missingTypes:Add(xVar)
            ELSE
               IF missingTypes:Count > 0
                  FOREACH VAR xMissing IN missingTypes
                     xMissing:TypeName := xVar:TypeName
                  NEXT
                  missingTypes:Clear()
               ENDIF
            ENDIF
            SELF:_locals:Add(xVar)
         NEXT
         FOREACH VAR xMissing IN missingTypes
            xMissing:TypeName := _missingType
         NEXT
         SELF:ReadLine()
         RETURN TRUE

      PRIVATE METHOD ParseLocalVar AS XSourceVariableSymbol
/*
//localvar           : (CONST=CONST)? ( DIM=DIM )? Id=identifier (LBRKT ArraySub=arraysub RBRKT)?
//                     (Op=assignoperator Expression=expression)? (AS=(AS | IS) DataType=datatype)?
//                   ;
*/
         LOCAL lConst   := FALSE AS LOGIC
         LOCAL lDim     := FALSE AS LOGIC
         LOCAL expr     AS IList<XSharpToken>
         LOCAL start    := SELF:Lt1 AS IToken
         IF SELF:La1 == XSharpLexer.CONST
            lConst := TRUE
            Consume()
         ENDIF
         IF SELF:La1 == XSharpLexer.DIM
            lDim := TRUE
            Consume()
         ENDIF
         VAR id := SELF:ParseIdentifier()
         VAR arraysub := SELF:ParseArraySub()
         IF ExpectAssignOp()
            expr       := SELF:ParseExpressionAsTokens()
         ENDIF
         VAR type     := SELF:ParseDataType(TRUE)
         SELF:GetSourceInfo(start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR _)
         IF String.IsNullOrEmpty(type)
            type := XLiterals.NoType
         ENDIF
         VAR xVar     := XSourceVariableSymbol{SELF:CurrentEntity, id, range, interval, type} {IsArray := lDim .OR. !String.IsNullOrEmpty(arraysub)}
         IF type:EndsWith("[]")
            xVar:IsArray := TRUE
         ENDIF
         xVar:Expression := expr
         IF expr != NULL
            var sb := StringBuilder{}
            foreach var token in expr
                sb:Append(token:TextWithTrivia)
            next
            xVar:Value := sb:ToString():Trim()
         endif
         RETURN xVar

      PRIVATE METHOD ParseImpliedVar() AS XSourceVariableSymbol
         // impliedvar: (Const=CONST)? Id=identifier Op=assignoperator Expression=expression
         LOCAL lConst   := FALSE AS LOGIC
         LOCAL lDim     := FALSE AS LOGIC
         LOCAL expr     AS IList<XSharpToken>
         LOCAL start    := SELF:Lt1 AS IToken
         IF SELF:La1 == XSharpLexer.CONST
            lConst := TRUE
            Consume()
         ENDIF
         VAR id := SELF:ParseIdentifier()
         IF ExpectAssignOp()
            expr := SELF:ParseExpressionAsTokens()
         ENDIF
         SELF:GetSourceInfo(start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR _)
         VAR xVar     := XSourceImpliedVariableSymbol{SELF:CurrentEntity, id,  range, interval}
         xVar:Expression := expr
         xVar:ImpliedKind := ImpliedKind.Assignment
         xVar:TypeName    := Self:TokensAsString(expr):Trim()
         RETURN xVar

      PRIVATE METHOD ParseXBaseDeclarationStatement() AS LOGIC
         /*
            xbasedecl           : T=(MEMVAR|PARAMETERS|LPARAMETERS)      // MEMVAR  Foo, Bar or PARAMETERS Foo, Bar
                                  Vars+=identifierName (COMMA Vars+=identifierName)*
                                  end=eos
                                | T=(PRIVATE | PUBLIC)
                                  XVars+=xbasevar (COMMA XVars+=xbasevar)*   // PRIVATE Foo := 123,  PUBLIC Bar, PUBLIC MyArray[5,2]
                                  end=eos
                                // FoxPro dimension statement
                                | T=(DIMENSION|DECLARE) DimVars += dimensionVar (COMMA DimVars+=dimensionVar)*    end=eos
                                ;

            xbasevar            : (Amp=AMP)?  Id=identifierName (LBRKT ArraySub=arraysub RBRKT)? (Op=assignoperator Expression=expression)?
                                ;

            dimensionVar        : Id=identifierName  ( LBRKT ArraySub=arraysub RBRKT | LPAREN ArraySub=arraysub RPAREN ) (AS DataType=datatype)?
                                ;

         */
         SWITCH SELF:La1
         CASE XSharpLexer.MEMVAR
         CASE XSharpLexer.PARAMETERS
         CASE XSharpLexer.LPARAMETERS
               ParseMemvarDeclarationStatement()
         CASE XSharpLexer.PUBLIC
         CASE XSharpLexer.PRIVATE
            ParseMemvarAllocationStatement(SELF:La1)
         CASE XSharpLexer.DIMENSION
         CASE XSharpLexer.DECLARE
            ParseFoxProDim()
         END SWITCH
         RETURN TRUE

      PRIVATE METHOD ParseMemvarDeclarationStatement() AS VOID
            /*
            xbasedecl           : T=(MEMVAR|PARAMETERS|LPARAMETERS)      // MEMVAR  Foo, Bar or PARAMETERS Foo, Bar
                                  Vars+=identifierName (COMMA Vars+=identifierName)*
                                  end=eos
            */
            VAR start := SELF:Lt1
            IF ! ExpectAny(XSharpLexer.MEMVAR, XSharpLexer.PARAMETERS, XSharpLexer.LPARAMETERS)
               RETURN
            ENDIF
            VAR Ids := List<STRING>{}
            IF ! SELF:IsId(SELF:La1)
               RETURN
            ENDIF
            Ids:Add(SELF:ParseIdentifier())
            DO WHILE Expect(XSharpLexer.COMMA)
               Ids:Add(SELF:ParseIdentifier())
            ENDDO
            SELF:GetSourceInfo(start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR _)
            SELF:ReadLine()
            FOREACH VAR id IN Ids
               VAR xVar := XSourceVariableSymbol{SELF:CurrentEntity, id, range, interval,XLiterals.UsualType}
               IF start.Type != XSharpLexer.MEMVAR
                  xVar.Kind := Kind.Parameter
               ELSE
                  xVar.Kind := Kind.MemVar
               ENDIF
               SELF:_locals:Add(xVar)
            NEXT
            RETURN

          PRIVATE METHOD ParseMemvarAllocationStatement(nType as LONG) AS VOID
            /*
                                | T=(PRIVATE | PUBLIC)
                                  XVars+=xbasevar (COMMA XVars+=xbasevar)*   // PRIVATE Foo := 123,  PUBLIC Bar, PUBLIC MyArray[5,2]
                                  end=eos
            */
            VAR start := SELF:_firstTokenOnLine
            IF nType != XSharpLexer.PRIVATE && nType != XSharpLexer.PUBLIC
               RETURN
            ENDIF
            IF SELF:La1 == nType
                Consume()
            ENDIF
            VAR result := List<XSourceVariableSymbol>{}
            VAR xVar := SELF:ParseXBaseVar()
            result:Add(xVar)
            DO WHILE SELF:La1 == XSharpLexer.COMMA
               Consume()
               xVar := SELF:ParseXBaseVar()
               result:Add(xVar)
            ENDDO
            SELF:ReadLine()
            FOREACH VAR mv IN result
               SELF:_locals:Add(mv)
            NEXT
            RETURN

      PRIVATE METHOD ParseXBaseVar AS XSourceVariableSymbol
         /*
         xbasevar            : (Amp=AMP)?  Id=identifierName (LBRKT ArraySub=arraysub RBRKT)? (Op=assignoperator Expression=expression)?
                              ;
         */
         LOCAL lAmp     := FALSE AS LOGIC
         LOCAL expr     AS IList<XSharpToken>
         LOCAL start    := SELF:Lt1 AS IToken
         IF SELF:La1 == XSharpLexer.AMP
            lAmp := TRUE
            Consume()
         ENDIF
         VAR id         := SELF:ParseIdentifier()
         VAR arraysub   := SELF:ParseArraySub()
         IF ExpectAssignOp()
            expr        := SELF:ParseExpressionAsTokens()
         ENDIF
         VAR type       := XLiterals.UsualType
         SELF:GetSourceInfo(start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR _)
         VAR xVar       := XSourceVariableSymbol{SELF:CurrentEntity, id, range, interval, type} {IsArray := !String.IsNullOrEmpty(arraysub)}
         xVar.Kind      := Kind.MemVar
         xVar:Expression := expr
         if expr != NULL
                var sb := StringBuilder{}
                foreach var token in expr
                    sb:Append(token:TextWithTrivia)
                next
                xVar:Value := sb:ToString()
         endif

         RETURN xVar

      PRIVATE METHOD ParseFoxProDim AS VOID
         /*
                                | T=(DIMENSION|DECLARE) DimVars += dimensionVar (COMMA DimVars+=dimensionVar)*    END=eos

            dimensionVar        : Id=identifierName  ( LBRKT ArraySub=arraysub RBRKT | LPAREN ArraySub=arraysub RPAREN ) (AS DataType=datatype)?
                                ;
         */
         IF _dialect == XSharpDialect.FoxPro
            IF ! ExpectAny(XSharpLexer.DIMENSION, XSharpLexer.DECLARE)
               RETURN
            ENDIF
            ParseDimensionVar()
            DO WHILE Expect(XSharpLexer.COMMA)
               ParseDimensionVar()
            ENDDO
         ENDIF
         SELF:ReadLine()
         RETURN

      PRIVATE METHOD ParseDimensionVar() AS VOID
         LOCAL start    := SELF:Lt1 AS IToken
         LOCAL arraysub AS STRING
         VAR id       := SELF:ParseIdentifier()
         IF SELF:La1 == XSharpLexer.LBRKT
            arraysub := SELF:ParseArraySub()
         ELSEIF SELF:La1 == XSharpLexer.LPAREN
            arraysub := SELF:ParseArraySubFox()
         ENDIF
         VAR type := SELF:ParseDataType(FALSE)
         SELF:GetSourceInfo(start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR _)
         IF String.IsNullOrEmpty(type)
            type := "ARRAY"
         ENDIF
         VAR xVar     := XSourceVariableSymbol{SELF:CurrentEntity, id, range, interval, type}
         xVar:Kind    := Kind.MemVar
         IF type != "ARRAY"
            xVar:IsArray := !String.IsNullOrEmpty(arraysub)
         ENDIF
         SELF:_locals:Add(xVar)

      PRIVATE METHOD ParseFieldStatement() AS LOGIC
            /*
               fielddecl          : FIELD Fields+=identifierName (COMMA Fields+=identifierName)* (IN Alias=identifierName)? end=eos
                                  ;

            */
            VAR start := SELF:Lt1
            IF ! Expect(XSharpLexer.FIELD)
               RETURN FALSE
            ENDIF
            VAR Ids := List<STRING>{}
            IF ! SELF:IsId(SELF:La1)
               RETURN FALSE
            ENDIF
            Ids:Add(SELF:ParseIdentifier())
            DO WHILE Expect(XSharpLexer.COMMA)
               Ids:Add(SELF:ParseIdentifier())
            ENDDO
            VAR area := ""
            IF Expect(XSharpLexer.IN)
               area := SELF:ParseIdentifier()
            ENDIF
            SELF:GetSourceInfo(start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR _)
            SELF:ReadLine()
            FOREACH VAR id IN Ids
               VAR xVar := XSourceVariableSymbol{SELF:CurrentEntity, id, range, interval,XLiterals.UsualType}
               xVar.Kind := Kind.DbField
               IF ! String.IsNullOrEmpty(area)
                  xVar:Value := area
               ENDIF
               SELF:_locals:Add(xVar)
            NEXT
            RETURN TRUE

#region FoxPro class definitions
         PRIVATE METHOD ParseFoxClass() AS IList<XSourceEntity>
         /*
         foxclass            : (Attributes=attributes)?
                               D=DEFINE (Modifiers=classModifiers)?
                               CLASS (Namespace=nameDot)? Id=identifier
                               TypeParameters=typeparameters?
                               (AS BaseType=datatype)?
                               (OF Classlib=identifier) ?
                               (ConstraintsClauses+=typeparameterconstraintsclause)*             // Optional typeparameterconstraints for Generic Class
                               (OLEPUBLIC) ?
                               e=eos
                               (Members+=foxclassmember)*
                               (ENDDEFINE | END DEFINE) eos
                             ;

            */
            IF ! Expect(XSharpLexer.DEFINE)
               RETURN NULL
            ENDIF
            VAR mods := SELF:ParseVisibilityAndModifiers()
            IF ! Expect(XSharpLexer.CLASS)
               RETURN NULL
            ENDIF
            VAR id := SELF:ParseQualifiedName()
            VAR typePars := SELF:ParseTypeParameters()
            VAR baseType := ""
            VAR classlib := ""
            IF Expect(XSharpLexer.AS)
               baseType := SELF:ParseTypeName()
            ENDIF
            IF Expect(XSharpLexer.OF)
               classlib := SELF:ParseIdentifier()
            ENDIF
            LOCAL constraints   AS List<STRING>
            DO WHILE SELF:La1 == XSharpLexer.WHERE
               IF constraints == NULL
                  constraints := List<STRING>{}
               ENDIF
               constraints:Add(SELF:ParseTypeParameterConstraints())
            ENDDO
            SELF:GetSourceInfo(_start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR source)
            SELF:ReadLine()
            VAR xType := XSourceTypeSymbol{id, Kind.Class, mods, range, interval, _file}
            xType:SourceCode := source
            IF constraints?:Count > 0
               FOREACH VAR constraint IN constraints
                  xType:AddConstraints(constraint)
               NEXT
            ENDIF
            IF typePars?:Count > 0
               FOREACH VAR typepar IN typePars
                  xType:AddTypeParameter(typepar)
               NEXT
            ENDIF
            xType:ClassType := XSharpDialect.FoxPro
            RETURN <XSourceEntity>{xType}

         PRIVATE METHOD ParseFoxMethod() AS IList<XSourceEntity>
           /*
         foxmethod           : (Attributes=attributes)? (Modifiers=memberModifiers)?
                               T=foxmethodtype  Sig=signature
                               (HelpString=HELPSTRING HelpText=expression)?
                               (ThisAccess=THISACCESS LPAREN MemberId=identifier RPAREN)?
                               end=eos
                               StmtBlk=statementBlock
                               (END T2=foxmethodtype  EOS)?
                               ;
         funcproctype        : Token=(FUNCTION | PROCEDURE)
                              ;

            */
            LOCAL hs := "" AS STRING
            LOCAL thisId := "" AS STRING
            IF ! ParseFuncProcType (OUT VAR kind)
               RETURN NULL
            ENDIF
            VAR sig := SELF:ParseSignature()
            IF Expect(XSharpLexer.HELPSTRING)
               hs  := SELF:ParseExpression()
            ENDIF
            IF Expect(XSharpLexer.THISACCESS) .AND. Expect(XSharpLexer.LPAREN)
               thisId := SELF:ParseIdentifier()
               Expect(XSharpLexer.RPAREN)
            ENDIF
            SELF:GetSourceInfo(_start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR source)
            SELF:ReadLine()
            kind := Kind.Method
            // Check for _ACCESS or _ASSIGN
            VAR id := sig:Id
            IF id:EndsWith("_access", StringComparison.OrdinalIgnoreCase)
               sig:Id   := sig:Id:Substring(0, sig:Id:Length - "_access":Length)
               kind := Kind.Access
            ELSEIF id:EndsWith("_assign", StringComparison.OrdinalIgnoreCase)
               sig:Id   := sig:Id:Substring(0, sig:Id:Length - "_assign":Length)
               kind := Kind.Assign
            ENDIF

            VAR xMember := XSourceMemberSymbol{sig, kind, _attributes, range, interval, _attributes:HasFlag(Modifiers.Static)}
            xMember:SourceCode := source
            xMember:File := SELF:_file
            RETURN <XSourceEntity>{xMember}

         PRIVATE METHOD ParseFoxFields() AS IList<XSourceEntity>
         /*
            foxclassvars        : (Attributes=attributes)? (Modifiers=classvarModifiers)?
                                  (Fld=FIELD)? Vars += identifier (COMMA Vars += identifier )*  (AS DataType=datatype)?
                                  end=eos
                                ;

            foxfield            : (Modifiers=classvarModifiers)? (Fld=FIELD)? F=foxfieldinitializer end=eos
                                ;
            foxfieldinitializer : Name=name assignoperator Expr=expression
                                ;

            foximplementsclause : IMPLEMENTS Type=datatype (Excl=EXCLUDE)? (IN Library=expression)?
                                  end=eos
                                ;

            foxaddobjectclause  : (Attributes=attributes)? ADD OBJECT (Modifiers=classvarModifiers)?
                                  Id=identifier AS Type=datatype (NoInit=NOINIT)?
                                  (WITH FieldsInits += foxfieldinitializer (COMMA FieldsInits += foxfieldinitializer)* )?
                                  end=eos
                                ;

            foxpemcomattrib     : DIMENSION Id=identifier LBRKT expression RBRKT end=eos
                                | Id=identifier LBRKT expression RBRKT assignoperator expression end=eos
                                ;
*/

            IF CurrentType == NULL .OR. CurrentType:ClassType != XSharpDialect.FoxPro
               RETURN NULL
            ENDIF
            VAR typedef := CurrentType
            VAR result := List<XSourceEntity>{}
            // Detect which variant we need
            IF Expect(XSharpLexer.IMPLEMENTS)
               //  foximplementsclause : IMPLEMENTS Type=datatype (Excl=EXCLUDE)? (IN Library=expression)? END=eos
               VAR interf    := SELF:ParseTypeName()
               VAR exclude   := Expect(XSharpLexer.EXCLUDE)
               IF Expect(XSharpLexer.IN)
                  VAR Lib := SELF:ParseExpression()
               ENDIF
               typedef:AddInterface(interf)
               SELF:ReadLine()
               RETURN NULL
            ELSEIF Expect(XSharpLexer.DIMENSION)
               // foxpemcomattrib     : DIMENSION Id=identifier LBRKT expression RBRKT end=eos
               VAR id := SELF:ParseIdentifier()
               VAR expr := ""
               IF ! Expect(XSharpLexer.LBRKT)
                  RETURN NULL
               ENDIF
               expr := SELF:ParseExpression()
               IF !Expect(XSharpLexer.RBRKT)
                  RETURN NULL
               ENDIF
               SELF:GetSourceInfo(_start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR source)
               SELF:ReadLine()
               VAR xMember := XSourceMemberSymbol{id, Kind.Field, _attributes, range, interval,"ARRAY"} {SingleLine := TRUE}
               xMember:SourceCode := source
               xMember:File := SELF:_file
               RETURN <XSourceEntity>{xMember}
            ELSEIF Matches(XSharpLexer.ID, XSharpLexer.FIELD)
               VAR hasField := Expect(XSharpLexer.FIELD)
               IF ! IsId(SELF:La1)
                  RETURN NULL
               ENDIF
               VAR ids      := List<STRING>{}
               ids:Add(ParseIdentifier())
               IF ids:First():EndsWith("COMATTRIB", StringComparison.OrdinalIgnoreCase)
                  RETURN NULL
               ENDIF
               VAR source := ""

               IF ExpectAssignOp()
                     // foxfield            : (Modifiers=classvarModifiers)? (Fld=FIELD)? F=foxfieldinitializer END=eos
                     //                     ;
                     // foxfieldinitializer : Name=name assignoperator Expr=expression
                     //                     ;
                     VAR expr := SELF:ParseExpression()
                     SELF:GetSourceInfo(_start, LastToken, OUT VAR r1, OUT VAR i1, OUT source)
                     SELF:ReadLine()
                     VAR xMember := XSourceMemberSymbol{ids:First(), Kind.Field, _attributes, r1, i1, _missingType} {SingleLine := TRUE}
                     xMember:SourceCode := source
                     xMember:Value      := expr
                     xMember:File := SELF:_file
                     RETURN <XSourceEntity>{xMember}
               ENDIF
               IF Matches(XSharpLexer.COMMA)
                  // foxclassvars        : (Attributes=attributes)? (Modifiers=classvarModifiers)?
                  //                      (Fld=FIELD)? Vars += identifier (COMMA Vars += identifier )*  (AS DataType=datatype)?
                  //                      END=eos
                  DO WHILE Expect(XSharpLexer.COMMA)
                     ids:Add(ParseIdentifier())
                  ENDDO
               ENDIF
               VAR type := SELF:ParseDataType(FALSE)
               SELF:GetSourceInfo(_start, LastToken, OUT VAR range, OUT VAR interval, OUT  source)
               SELF:ReadLine()
               FOREACH VAR id IN ids
                  VAR xMember := XSourceMemberSymbol{id, Kind.Field, _attributes, range, interval, type} {SingleLine := TRUE}
                  IF ids:Count == 1
                     xMember:SourceCode := source
                  ELSEIF String.IsNullOrEmpty(type)
                     xMember:SourceCode := "FIELD "+id
                  ELSE
                     xMember:SourceCode := "FIELD "+id +" AS "+type
                  ENDIF
                  xMember:File := SELF:_file
                  result:Add(xMember)
               NEXT
               RETURN result
            ELSEIF Expect(XSharpLexer.ADD)
               /*
                        foxaddobjectclause  : (Attributes=attributes)? ADD OBJECT (Modifiers=classvarModifiers)?
                                          Id=identifier AS Type=datatype (NoInit=NOINIT)?
                                          (WITH FieldsInits += foxfieldinitializer (COMMA FieldsInits += foxfieldinitializer)* )?
                                          end=eos
                                       ;
               */
               IF ! Expect(XSharpLexer.OBJECT)
                  RETURN NULL
               ENDIF
               VAR mods := SELF:ParseVisibilityAndModifiers()
               VAR id := SELF:ParseIdentifier()
               IF ! Expect(XSharpLexer.AS)
                  RETURN NULL
               ENDIF
               VAR type   := SELF:ParseTypeName()
               VAR noinit := Expect(XSharpLexer.NOINIT)
               // no need to parse the initializers here
               DO WHILE SELF:La1 != XSharpLexer.EOS
                  Consume()
               ENDDO
               SELF:GetSourceInfo(_start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR source)
               SELF:ReadLine()
               VAR xMember := XSourceMemberSymbol{id, Kind.Field, _attributes, range, interval, type} {SingleLine := TRUE}
               xMember:SourceCode := source
               xMember:File := SELF:_file
               RETURN <XSourceEntity>{xMember}
           ENDIF
            // ignore the COMATTRIB rule here
           SELF:ReadLine()
           RETURN NULL
#endregion

#region XPP classes
            PRIVATE METHOD ParseXppClass() AS IList<XSourceEntity>
/*
xppclass           :  (Attributes=attributes)?                                // NEW Optional Attributes
                      (Modifiers=xppclassModifiers)?                          // [STATIC|FREEZE|FINAL]
                       C=CLASS (Namespace=nameDot)? Id=identifier               // CLASS <ClassName>
                       (
                          From=(FROM| SHARING) BaseTypes+=datatype (COMMA BaseTypes+=datatype)*  // [FROM <SuperClass,...>] ;
                       )?                                                                   // [SHARING <SuperClass,...>]
                       (IMPLEMENTS Implements+=datatype (COMMA Implements+=datatype)*)? // NEW Implements
                       // No type parameters and type parameter constraints
                      e=eos
                      (Members+=xppclassMember)*
                      ENDCLASS
                      eos
                    ;
xppclassModifiers   : ( Tokens+=(STATIC | FREEZE | FINAL) )+
                    ; // make sure all tokens are also in the IsModifier method inside XSharpLexerCode.cs

xppclassMember      : Member=xppmethodvis                           #xppclsvisibility
                    | Member=xppclassvars                           #xppclsvars
                    | Member=xppinlineMethod                        #xppclsinlinemethod
                    | Member=xppdeclareMethod                       #xppclsdeclaremethod
                    | Member=xppproperty                            #xppclsproperty
                    | Member=pragma                                 #xpppragma
                    ;
*/
            IF ! Expect(XSharpLexer.CLASS)
               RETURN NULL
            ENDIF
            VAR id         := SELF:ParseQualifiedName()
            VAR baseType   := ""
            IF SELF:ExpectAny(XSharpLexer.FROM, XSharpLexer.SHARING)
               baseType  := SELF:ParseTypeName()
               DO WHILE SELF:Expect(XSharpLexer.COMMA)
                  // consume the other type declarations , ignore them because we do not support multiple inheritance
                  ParseTypeName()
               ENDDO
            ENDIF
            VAR interfaces := List<STRING>{}
            IF SELF:Expect(XSharpLexer.IMPLEMENTS)
               interfaces:Add(SELF:ParseTypeName())
               DO WHILE SELF:Expect(XSharpLexer.COMMA)
                  interfaces:Add(SELF:ParseTypeName())
               ENDDO
            ENDIF
            SELF:GetSourceInfo(_start, LastToken, OUT VAR range, OUT VAR interval, OUT VAR source)
            SELF:ReadLine()

            VAR xType := XSourceTypeSymbol{id,Kind.Class, _attributes, range, interval, _file}
            xType:SourceCode := source

            IF interfaces?:Count > 0
               FOREACH VAR sInterface IN interfaces
                  xType:AddInterface(sInterface)
               NEXT
            ENDIF
            IF ! String.IsNullOrEmpty(baseType)
               xType:BaseTypeName := baseType
            ENDIF
            xType:IsPartial := _attributes:HasFlag(Modifiers.Partial)
            IF CurrentEntity != _globalType .AND. CurrentEntityKind:HasChildren()
               xType:Parent := SELF:CurrentEntity
            ENDIF
            xType:ClassType := XSharpDialect.XPP
            _xppVisibility := Modifiers.Public
            RETURN <XSourceEntity>{xType}

            PRIVATE METHOD ParseXppClassVars() AS IList<XSourceEntity>
                     /*

                     xppclassvars        : (Modifiers=xppmemberModifiers)?                             // [CLASS]
                                           VAR Vars+=identifier                                        // VAR <VarName>
                                           (
                                             Is=xppisin                                                // [IS <Name>] [IN <SuperClass>]
                                             | ((COMMA Vars+=identifier)*                              // <,...>
                                             (AS DataType=datatype)?  )                                // Optional data type

                                           )
                                           (Shared=SHARED)?                                            // [SHARED]
                                           (ReadOnly=READONLY)?                                        // [READONLY]
                                           (Assignment=xppvarassignment)?                              // [ASSIGNMENT HIDDEN | PROTECTED | EXPORTED]
                                           (Nosave= NOSAVE)?                                           // [NOSAVE]
                                           eos
                                         ;

                     xppvarassignment    : ASSIGNMENT xppvisibility                                    // [ASSIGNMENT HIDDEN | PROTECTED | EXPORTED]
                                         ;
                     */
               VAR isStatic := SELF:ParseXPPMemberModifiers()
               IF ! SELF:Expect(XSharpLexer.VAR)
                  RETURN NULL
               ENDIF
               LOCAL type AS STRING
               VAR ids := List<STRING>{}
               IF SELF:IsId(SELF:La1)
                  ids:Add(SELF:ParseIdentifier())
               ELSE
                  RETURN NULL
               ENDIF
               IF SELF:Matches(XSharpLexer.IN,XSharpLexer.IS)
                  SELF:ParseXppIsIn()     // Parse but ignore
               ELSE
                  DO WHILE SELF:Expect(XSharpLexer.COMMA)
                     ids:Add(SELF:ParseIdentifier())
                  ENDDO
                  type := SELF:ParseDataType(FALSE)
               ENDIF
               SELF:GetSourceInfo(_start, SELF:LastToken, OUT VAR range, OUT VAR interval, OUT VAR _)
               // eat all tokens
               DO WHILE ExpectAny(XSharpLexer.SHARED, XSharpLexer.READONLY, XSharpLexer.ASSIGNMENT, ;
                                  XSharpLexer.HIDDEN, XSharpLexer.PROTECTED, XSharpLexer.EXPORTED, XSharpLexer.NOSAVE)
                  NOP
               ENDDO
               SELF:ReadLine()
               VAR result := List<XSourceEntity>{}
               FOREACH VAR id IN ids
                  VAR classvar := XSourceMemberSymbol{id, Kind.Field, _xppVisibility, range, interval, type, FALSE} {SingleLine := TRUE}
                  classvar:SourceCode := ie"{classvar.ModVis}: \r\n{ IIF(isStatic, \"STATIC\",\"\")} VAR {classvar.Name}"
                  IF SELF:CurrentType != NULL
                     SELF:CurrentType:AddMember(classvar)
                  ENDIF
                  result:Add(classvar)
               NEXT
               RETURN result

               PRIVATE METHOD ParseXppProperty AS IList<XSourceEntity>
                  /*
                  xppproperty         : (Attributes=attributes)?                                    // NEW Optional Attributes
                                        (   Access=ACCESS Assign=ASSIGN?                            // ACCESS | ASSIGN  | ACCESS ASSIGN | ASSIGN ACCESS
                                          | ASSIGN=ASSIGN Access=ACCESS?
                                        )
                                        Modifiers=xppmemberModifiers?                               // [CLASS]
                                        M=METHOD Id=identifier                                        // METHOD <MethodName>
                                        (VAR VarName=identifier)?                                   // [VAR <VarName>]
                                        (AS Type=datatype)?                                         // NEW Optional data type
                                        end=eos
                                      ;
                     */
                  VAR la1 := SELF:La1
                  VAR la2 := SELF:La2
                  IF ! Matches(XSharpLexer.ACCESS, XSharpLexer.ASSIGN)
                     RETURN NULL
                  ENDIF
                  DO WHILE ExpectAny(XSharpLexer.ACCESS, XSharpLexer.ASSIGN)
                     NOP
                  ENDDO
                  VAR isStatic := SELF:ParseXPPMemberModifiers()
                  IF ! Expect(XSharpLexer.METHOD)
                     RETURN NULL
                  ENDIF
                  VAR id := SELF:ParseIdentifier()
                  VAR propName := ""
                  IF SELF:Expect(XSharpLexer.VAR)
                     propName := SELF:ParseIdentifier()
                  ENDIF
                  VAR type := SELF:ParseDataType(FALSE)
                  SELF:GetSourceInfo(_start, SELF:LastToken, OUT VAR range, OUT VAR interval, OUT VAR source)
                  SELF:ReadLine()
                  VAR xmember := XSourceMemberSymbol{id+XLiterals.XppDeclaration, Kind.Property, _OR(_xppVisibility, _attributes), range, interval, type, isStatic}
                  xmember:SourceCode := source
                  IF SELF:CurrentType != NULL
                     SELF:CurrentType:AddMember(xmember)
                  ENDIF
                  xmember:SingleLine := TRUE
                  RETURN <XSourceEntity> {xmember}

      PRIVATE METHOD ParseXppMethodDeclaration() AS IList<XSourceEntity>
         /*
         xppdeclareMethod    : (Modifiers=xppdeclareModifiers)?                            // [DEFERRED |FINAL | INTRODUCE | OVERRIDE] [CLASS]
                               METHOD Methods+=identifier                                   // METHOD <MethodName,...>
                               (
                                 xppisin                                                   //  [IS <Name>] [IN <SuperClass>]
                                 | (COMMA Methods+=identifier)*                             // or an optional comma seperated list of other names
                               )
                               eos
                             ;
         xppdeclareModifiers : ( Tokens+=( DEFERRED | FINAL | INTRODUCE | OVERRIDE | CLASS | SYNC ) )+
                             ; // make sure all tokens are also in the IsModifier method inside XSharpLexerCode.cs

         */
         VAR la1      := SELF:La1
         VAR isStatic :=  Expect(XSharpLexer.CLASS)
         IF ! SELF:Expect(XSharpLexer.METHOD)
            RETURN NULL
         ENDIF
         VAR ids := List<STRING>{}
         IF SELF:IsId(SELF:La1)
            ids:Add(SELF:ParseIdentifier())
         ELSE
            RETURN NULL
         ENDIF
         IF SELF:Matches(XSharpLexer.IN,XSharpLexer.IS)
            SELF:ParseXppIsIn()     // Parse but ignore
         ELSE
            DO WHILE SELF:Expect(XSharpLexer.COMMA)
               ids:Add(SELF:ParseIdentifier())
            ENDDO
         ENDIF
         SELF:GetSourceInfo(_start, SELF:LastToken, OUT VAR range, OUT VAR interval, OUT VAR _)
         VAR result := List<XSourceEntity>{}
         FOREACH VAR id IN ids
            VAR xmethod := XSourceMemberSymbol{id+XLiterals.XppDeclaration, Kind.Method, _xppVisibility, range, interval, "", FALSE}
            xmethod:SourceCode := ie"{xmethod.ModVis}: \r\n{ IIF(isStatic, \"STATIC\",\"\")} METHOD {xmethod.Name}"
            IF SELF:CurrentType != NULL
               SELF:CurrentType:AddMember(xmethod)
            ENDIF
            xmethod:SingleLine := TRUE
            result:Add(xmethod)
         NEXT
         RETURN result

       PRIVATE METHOD ParseXppVisibility() AS IList<XSourceEntity>
            /*
            xppmethodvis        : Vis=xppvisibility COLON eos
                                 ;

            xppvisibility       : Token=(HIDDEN | PROTECTED | EXPORTED | INTERNAL | PUBLIC | PRIVATE )
                                 ;

            */
            // the visibility is already parsed as 'start of entity'
            //we're now at the COLON token
            VAR result := SELF:Expect(XSharpLexer.COLON)
            IF result
               SELF:ReadLine()
            ENDIF
            SELF:_xppVisibility := _attributes
            RETURN NULL

       PRIVATE METHOD ParseXppIsIn() AS VOID
            /*
               xppisin             : IS Id=identifier (IN SuperClass=identifier)?                //  IS <Name> [IN <SuperClass>]
                                   | IN SuperClass=identifier								             //  IN <SuperClass> without IS clause
                                   ;
            */
            // we parse this but the feature is not supported.
            LOCAL id, superclass AS STRING
            id := superclass := ""
            IF Expect(XSharpLexer.IS)
               id := SELF:ParseIdentifier()
            ENDIF
            IF Expect(XSharpLexer.IN)
               superclass := SELF:ParseIdentifier()
            ENDIF
            RETURN

       PRIVATE METHOD ParseXppMethod() AS IList<XSourceEntity>
            // detect if we're inside the class definition
            IF SELF:La1 == XSharpLexer.INLINE
               RETURN SELF:ParseXppMethodInLine()
            ELSEIF SELF:InXppClass
               RETURN SELF:ParseXppMethodDeclaration()
            ELSE
               RETURN SELF:ParseXppMethodImplementation()
            ENDIF

       PRIVATE METHOD ParseXppMethodInLine() AS IList<XSourceEntity>
            /*
               xppinlineMethod     : (Attributes=attributes)?                               // NEW Optional Attributes
                                     I=INLINE
                                     (Modifiers=xppmemberModifiers)?                        // [CLASS]
                                     METHOD  Id=identifier                                  // METHOD <MethodName>
                                     // no type parameters
                                     (ParamList=parameterList)?                            // Optional Parameters
                                     (AS Type=datatype)?                                   // NEW Optional return type
                                     // no type constraints
                                     (UDCSEP ExpressionBody=expression)?                   // New: Expression Body
                                     end=eos
                                     StmtBlk=statementBlock
                                     (END METHOD eos)?
                                   ;

               xppmemberModifiers  : ( Tokens+=( CLASS | STATIC) )+
                                   ; // make sure all tokens are also in the IsModifier method inside XSharpLexerCode.cs
            */

            IF ! SELF:Expect(XSharpLexer.INLINE)
               RETURN NULL
            ENDIF
            VAR isStatic := SELF:ParseXPPMemberModifiers()
            IF ! SELF:Expect(XSharpLexer.METHOD)
               RETURN NULL
            ENDIF
            VAR id      := SELF:ParseIdentifier()
            VAR aParams := SELF:ParseParameterList(FALSE, OUT VAR _)
            VAR type    := ""
            IF SELF:La1 == XSharpLexer.AS
               type := SELF:ParseDataType(FALSE)
            ENDIF
            SELF:GetSourceInfo(_start, SELF:LastToken, OUT VAR range, OUT VAR interval, OUT VAR source)
            SELF:ReadLine()
            VAR xmethod := XSourceMemberSymbol{id, Kind.Method, _xppVisibility, range, interval, type, isStatic}
            xmethod:SourceCode := source
            IF SELF:CurrentType != NULL
               SELF:CurrentType:AddMember(xmethod)
            ENDIF
            IF aParams != NULL
               FOREACH VAR par IN aParams
                  xmethod:AddParameter(par)
               NEXT
            ENDIF
            RETURN <XSourceEntity>{xmethod}

       PRIVATE METHOD ParseXPPMemberModifiers() AS LOGIC
            RETURN SELF:ExpectAny(XSharpLexer.CLASS, XSharpLexer.STATIC)

      PRIVATE METHOD ParseXppMethodImplementation() AS IList<XSourceEntity>
            /*
               xppmethod           : (Attributes=attributes)?                              // NEW Optional Attributes
                                     (MethodType=(ACCESS|ASSIGN))?                         // Optional Access or Assign
                                     (Modifiers=xppmemberModifiers)?                       // [CLASS]
                                     M=METHOD (ClassId=identifier COLON)? Id=identifier    // [<ClassName>:] <MethodName>
                                     // no type parameters
                                     (ParamList=parameterList)?                            // Optional Parameters
                                     (AS Type=datatype)?                                   // NEW Optional return type
                                     // no type constraints
                                     // no calling convention
                                     (UDCSEP ExpressionBody=expression)?                   // New: Expression Body
                                     END=eos
                                     StmtBlk=statementBlock
                                     (END METHOD eos)?
                                   ;

            */
            VAR kind := Kind.Method
            IF ExpectAny(XSharpLexer.ACCESS)
               kind := Kind.Access
            ELSEIF ExpectAny(XSharpLexer.ASSIGN)
               kind := Kind.Assign
            ENDIF
            VAR isStatic := SELF:ParseXPPMemberModifiers()
            IF ! SELF:Expect(XSharpLexer.METHOD)
               RETURN NULL
            ENDIF
            VAR classprefix := ""
            IF SELF:La1 == XSharpLexer.ID .AND. SELF:La2 == XSharpLexer.COLON
               classprefix := SELF:ParseIdentifier()
               Expect(XSharpLexer.COLON)
            ENDIF
            VAR id      := SELF:ParseIdentifier()
            VAR aParams := SELF:ParseParameterList(FALSE, OUT VAR _)
            VAR type    := ""
            IF SELF:La1 == XSharpLexer.AS
               type := SELF:ParseDataType(FALSE)
            ENDIF
            SELF:GetSourceInfo(_start, SELF:LastToken, OUT VAR range, OUT VAR interval, OUT VAR source)
            SELF:ReadLine()
            VAR xmethod := XSourceMemberSymbol{id, kind, Modifiers.None, range, interval, type, isStatic}
            // we need to lookup the visibility in the types list
            xmethod:SourceCode := source
            // find the parent in the stack. Either the last one (when the classprefix is empty) or a specific one
            // then find the declared member in this type
            VAR classdef := SELF:FindXPPClass(classprefix)
            IF classdef != NULL
               classdef:AddMember(xmethod)
               FOREACH m AS XSourceMemberSymbol IN classdef:Members
                  IF m:Name == xmethod:Name+XLiterals.XppDeclaration
                     xmethod:Attributes := m:Attributes
                     EXIT
                  ENDIF
               NEXT
            ELSE
               IF SELF:CurrentType != NULL
                  SELF:CurrentType:AddMember(xmethod)
               ENDIF
            ENDIF

            IF aParams != NULL
               FOREACH VAR par IN aParams
                  xmethod:AddParameter(par)
               NEXT
            ENDIF
            RETURN <XSourceEntity>{xmethod}

         PRIVATE METHOD FindXPPClass(sName AS STRING) AS XSourceTypeSymbol
            VAR start := SELF:_EntityList:Count-1
            FOR VAR i := start DOWNTO 0
               VAR element := _EntityList[i]
               IF element IS XSourceTypeSymbol VAR typedef
                  IF typedef:ClassType == XSharpDialect.XPP
                     IF String.IsNullOrEmpty(sName)
                        RETURN typedef
                     ELSEIF String.Compare(typedef:Name, sName, TRUE) == 0
                        RETURN typedef
                     ENDIF
                  ENDIF
               ENDIF
            NEXT
            RETURN NULL

#endregion

      PRIVATE METHOD IsKeywordXs(token AS LONG) AS LOGIC
         SWITCH token
         CASE XSharpLexer.AUTO
         CASE XSharpLexer.CHAR
         CASE XSharpLexer.CONST
         CASE XSharpLexer.DEFAULT
         CASE XSharpLexer.GET
         CASE XSharpLexer.IMPLEMENTS
         CASE XSharpLexer.NEW
         CASE XSharpLexer.OUT
         CASE XSharpLexer.REF
         CASE XSharpLexer.SET
         CASE XSharpLexer.VALUE
         CASE XSharpLexer.VIRTUAL
         CASE XSharpLexer.INTERNAL
         CASE XSharpLexer.ADD
         CASE XSharpLexer.ARGLIST
         CASE XSharpLexer.ASCENDING
         CASE XSharpLexer.ASTYPE
         CASE XSharpLexer.ASYNC
         CASE XSharpLexer.AWAIT
         CASE XSharpLexer.BY
         CASE XSharpLexer.CHECKED
         CASE XSharpLexer.DESCENDING
         CASE XSharpLexer.DYNAMIC
         CASE XSharpLexer.EQUALS
         CASE XSharpLexer.EXTERN
         CASE XSharpLexer.FIXED
         CASE XSharpLexer.FROM
         CASE XSharpLexer.GROUP
         CASE XSharpLexer.INTO
         CASE XSharpLexer.JOIN
         CASE XSharpLexer.LET
         CASE XSharpLexer.NAMEOF
         CASE XSharpLexer.OF
         CASE XSharpLexer.ON
         CASE XSharpLexer.ORDERBY
         CASE XSharpLexer.OVERRIDE
         CASE XSharpLexer.PARAMS
         CASE XSharpLexer.REMOVE
         CASE XSharpLexer.SELECT
         CASE XSharpLexer.UNCHECKED
         CASE XSharpLexer.VAR
         CASE XSharpLexer.VOLATILE
         CASE XSharpLexer.WHERE
         CASE XSharpLexer.CURRENCY
         CASE XSharpLexer.DECIMAL
         CASE XSharpLexer.DATETIME
               // Added as XS keywords to allow them to be treated as IDs
            // the following entity keywords will be never used 'alone' and can therefore be safely defined as identifiers
         CASE XSharpLexer.DELEGATE
         CASE XSharpLexer.ENUM
         CASE XSharpLexer.GLOBAL
         CASE XSharpLexer.INHERIT
         CASE XSharpLexer.STRUCTURE
            // The following 'old' keywords are never used 'alone' and are harmless as identifiers
         CASE XSharpLexer.ALIGN
         CASE XSharpLexer.CALLBACK
         CASE XSharpLexer.CLIPPER
         CASE XSharpLexer.DIM
         CASE XSharpLexer.DOWNTO
         CASE XSharpLexer.DLLEXPORT
         CASE XSharpLexer.FASTCALL
         CASE XSharpLexer.IN
         CASE XSharpLexer.INIT1
         CASE XSharpLexer.INIT2
         CASE XSharpLexer.INIT3
         CASE XSharpLexer.INSTANCE
         CASE XSharpLexer.PASCAL
         CASE XSharpLexer.SEQUENCE
         CASE XSharpLexer.STEP
         CASE XSharpLexer.STRICT
         CASE XSharpLexer.TO
         CASE XSharpLexer.THISCALL
         CASE XSharpLexer.UPTO
         CASE XSharpLexer.USING
         CASE XSharpLexer.WINCALL
               // The following keywords are handled in the fixPositionalKeyword() method of the lexer and will only be keywords at the right place
               // but when they code event->(DoSomething()) we still need them in this rule...
               //        CASE XSharpLexer.DEFINE
               //        CASE XSharpLexer.TRY
               //        CASE XSharpLexer.SWITCH
               //        CASE XSharpLexer.EVENT
               //        CASE XSharpLexer.EXPLICIT
               //        CASE XSharpLexer.FOREACH
               //        CASE XSharpLexer.UNTIL
               //        CASE XSharpLexer.PARAMETERS
               //        CASE XSharpLexer.YIELD
               //        CASE XSharpLexer.MEMVAR
               //        CASE XSharpLexer.NOP
               //        CASE XSharpLexer.PARTIAL
               //        CASE XSharpLexer.SEALED
               //        CASE XSharpLexer.ABSTRACT
               //        CASE XSharpLexer.UNSAFE
               //        CASE XSharpLexer.SCOPE
               //        CASE XSharpLexer.NAMESPACE
               //        CASE XSharpLexer.LOCK
               //        CASE XSharpLexer.IMPLICIT
               //        CASE XSharpLexer.IMPLIED
               //        CASE XSharpLexer.INITONLY
               //        CASE XSharpLexer.PROPERTY
               //        CASE XSharpLexer.INTERFACE
               //        CASE XSharpLexer.VOSTRUCT
               //        CASE XSharpLexer.UNION
               //        CASE XSharpLexer.DECLARE
               //        CASE XSharpLexer.OPERATOR
            RETURN TRUE
         END SWITCH
         // context sensitive keywords
         // ENDCLASS, FREEZE, FINAL, INTRODUCE, SYNC, DEFERRED, INLINE

         RETURN FALSE

      PRIVATE METHOD IsKeywordXpp(token AS LONG) AS LOGIC
        if _dialect != XSharpDialect.XPP
                RETURN FALSE
        ENDIF
        SWITCH token
        CASE XSharpLexer.SHARING
        CASE XSharpLexer.SHARED
        CASE XSharpLexer.ASSIGNMENT
        CASE XSharpLexer.EXPORTED
        CASE XSharpLexer.READONLY
        CASE XSharpLexer.NOSAVE
        RETURN TRUE
        END SWITCH
         RETURN FALSE

      PRIVATE METHOD IsKeywordFox(token AS LONG) AS LOGIC
        if _dialect != XSharpDialect.FoxPro
                RETURN FALSE
        ENDIF
         SWITCH token
         CASE XSharpLexer.OLEPUBLIC
         CASE XSharpLexer.EACH
         CASE XSharpLexer.EXCLUDE
         CASE XSharpLexer.THISACCESS
         CASE XSharpLexer.HELPSTRING
         CASE XSharpLexer.NOINIT
         CASE XSharpLexer.FOX_AND
         CASE XSharpLexer.FOX_OR
         CASE XSharpLexer.FOX_NOT
         CASE XSharpLexer.FOX_XOR
         CASE XSharpLexer.THEN
            RETURN TRUE
         END SWITCH
         // These tokens are already marked as 'only valid in a certain context ' in the lexer
         // ENDDEFINE | TEXT| ENDTEXT | DIMENSION | LPARAMETERS | NOSHOW | TEXTMERGE | PRETEXT | FLAGS | ADDITIVE

         RETURN FALSE

      PRIVATE STATIC METHOD GetText(SELF token AS IToken) AS STRING
         VAR result := token:Text
         IF result:StartsWith("@@")
            result := result:Substring(2)
         ENDIF
         RETURN result

      PRIVATE METHOD GetSourceInfo(startToken AS IToken, endToken AS IToken, range OUT TextRange, interval OUT TextInterval, source OUT STRING) AS VOID
            range    := TextRange{startToken, endToken}
            interval := TextInterval{startToken, endToken}
            source   := GetSource(startToken, endToken)
            RETURN

      PRIVATE METHOD GetSource (startToken AS IToken, endToken AS IToken) AS STRING
         VAR startIndex := ((XSharpToken) startToken):OriginalTokenIndex
         VAR stopIndex  := ((XSharpToken)   endToken):OriginalTokenIndex
         VAR sb         := StringBuilder{}
         FOR VAR i := startIndex TO stopIndex
            sb:Append(_tokens[i].Text)
         NEXT
         RETURN sb:ToString()

      PRIVATE STATIC METHOD Log(cMessage AS STRING) AS VOID
         IF XSettings.EnableParseLog
            XSolution.WriteOutputMessage("XParser: "+cMessage)
         ENDIF
         RETURN
   END CLASS

   DELEGATE DelEndToken(iToken AS LONG) AS LOGIC

END NAMESPACE