public ParseNode_Base Recover(SyntaxTreeBuilder pSyntaxTreeBuilder, out int numMissing)
    {
        numMissing = 0;

        var current = this;

        while (current.parent != null)
        {
            var next = current.parent.NextAfterChild(current, pSyntaxTreeBuilder);
            if (next == null)
            {
                break;
            }

            var nextId = next as ParseNode_Id;
            if (nextId != null && nextId.Name == "attribute")
            {
                return(nextId);
            }

            var nextMatchespSyntaxTreeBuilder = next.Matches(pSyntaxTreeBuilder);
            while (next != null && !nextMatchespSyntaxTreeBuilder && next.FirstSet.ContainsEmpty())
            {
                next = next.parent.NextAfterChild(next, pSyntaxTreeBuilder);
                nextMatchespSyntaxTreeBuilder = next != null && next.Matches(pSyntaxTreeBuilder);
            }

            if (nextMatchespSyntaxTreeBuilder && pSyntaxTreeBuilder.TokenScanner.Current.text == ";" && next is ParseNode_Many_Opt)
            {
                return(null);
            }

            ++numMissing;
            if (nextMatchespSyntaxTreeBuilder)
            {
                if (pSyntaxTreeBuilder.TokenScanner.Current.text == "{" ||
                    pSyntaxTreeBuilder.TokenScanner.Current.text == "}" ||
                    pSyntaxTreeBuilder.PreCheck(next, 3))//next.Scan(clone))
                {
                    return(next);
                }
            }

            if (numMissing <= 1 && pSyntaxTreeBuilder.TokenScanner.Current.text != "{" && pSyntaxTreeBuilder.TokenScanner.Current.text != "}")
            {
                //TODO using
                var lapSyntaxTreeBuilder = pSyntaxTreeBuilder.Clone();
                if (lapSyntaxTreeBuilder.TokenScanner.MoveNext() && next.Matches(lapSyntaxTreeBuilder) && lapSyntaxTreeBuilder.PreCheck(next, 3))
                {
                    return(null);
                }
            }
            current = next;
        }
        return(null);
    }
    public bool CollectCompletions(TokenSet tokenSet, SyntaxTreeBuilder pSyntaxTreeBuilder, int identifierId)
    {
        var clone = pSyntaxTreeBuilder.Clone();

        var hasName = false;
        var current = this;

        while (current != null && current.parent != null)
        {
            tokenSet.Add(current.FirstSet);

            if (current.FirstSet.Contains(identifierId))
            {
                ParseNode_Rule currentRule = null;
                var            currentId   = current as ParseNode_Id;
                if (currentId == null)
                {
                    var rule = current.parent;
                    while (rule != null && !(rule is ParseNode_Rule))
                    {
                        rule = rule.parent;
                    }
                    currentId   = rule != null ? rule.parent as ParseNode_Id : null;
                    currentRule = rule as ParseNode_Rule;
                }

                if (currentId != null)
                {
                    var peerAsRule = currentId.LinkedTarget as ParseNode_Rule;
                    if (peerAsRule != null && peerAsRule.contextualKeyword)
                    {
                        Debug.Log(currentId.Name);
                    }
                    else if (currentRule != null && currentRule.contextualKeyword)
                    {
                        Debug.Log("currentRule " + currentRule.NtName);
                    }
                    else
                    {
                        var id = currentId.Name;
                        if (Array.IndexOf(new[]
                        {
                            "constantDeclarators",
                            "constantDeclarator",
                        }, id) >= 0)
                        {
                            hasName = true;
                        }
                    }
                }
            }

            if (!current.FirstSet.ContainsEmpty())
            {
                break;
            }

            current = current.parent.NextAfterChild(current, clone);
        }
        tokenSet.RemoveEmpty();

        return(hasName);
    }