public SyntacticBlockList Clone( )
        {
            var r = new SyntacticBlockList();

            r.AddRange(this);
            r.IsSelected = IsSelected;
            return(r);
        }
        public SyntacticBlockList Parse(string s)
        {
            var t        = s.ToCharArray();
            int i        = 0;
            var paths    = new SyntacticBlockList();
            var rootNode = _gramTree;

            Parse(ref t, i, rootNode, rootNode, paths);
            return(paths);
        }
        void Parse(
            ref char[] chars,
            int i,
            TreeNode rootNode,
            TreeNode node,
            SyntacticBlockList paths,
            TreePath currentPath   = null,
            bool returnImmediately = false,
            int recurseLevel       = 0
            )
        {
            if (chars.Length == 0 || i == chars.Length)
            {
                return;
            }

            bool matching        = false;
            bool partialMatching = false;

            while (i < chars.Length)
            {
                bool lastChar = i == chars.Length - 1;
                var  c        = chars[i];
                var  sc       = "" + c;
                matching = false;
                var beginIndex = i;

                /*if (recurseLevel==0 && node==null) {
                 *  // start by grammar multi roots
                 *  foreach ( var kv in rootNode.SubNodes )
                 *  {
                 *      _Parse(ref chars,i,rootNode,kv.Value,paths,currentPath,recurseLevel);
                 *  }
                 *  SelectPaths(ref chars,ref i,ref matching,ref partialMatching,rootNode,ref node);
                 * }*/

                if (!node.IsRoot)
                {
                    if (node.Label == sc)
                    {
                        // exact match - potential matching sequence
                        matching        = true;
                        partialMatching = false;
                    }
                    else
                    {
                        if (node.Label == SCF)
                        {
                            matching        = c < 32 && c != 27;
                            partialMatching = false;
                        }
                        else if (node.Label == CHAR)
                        {
                            matching        = true;
                            partialMatching = false;
                        }
                        else if (node.Label == TEXT)
                        {
                            // @TODO: develop
                            matching        = true;
                            partialMatching = true;
                        }
                        else if (node.Label == NUMLIST)
                        {
                            if (char.IsDigit(c) || c == ';')
                            {
                                matching        = true;
                                partialMatching = true;
                            }
                            else
                            {
                                matching = !partialMatching;
                            }
                        }
                        else if (node.Label == NUM)
                        {
                            if (char.IsDigit(c))
                            {
                                matching        = true;
                                partialMatching = true;
                            }
                        }
                        else
                        {
                            matching = false;
                        }
                    }

                    i++;
                }
                else
                {
                    matching        = true;
                    partialMatching = false;
                }

                #region functions

                void AddRemainingText(ref char[] chars, SyntacticBlock selected)
                {
                    if (selected.Index > 0)
                    {
                        if (paths.Count > 1)
                        {
                            int holeSize, blockIndex;
                            if (paths.Count > 1)
                            {
                                var previous = paths[^ 2];
        void _Parse(
            ref char[] chars,
            int i,
            TreeNode rootNode,
            TreeNode node,
            SyntacticBlockList paths,
            TreePath currentPath   = null,
            bool returnImmediately = false,
            int recurseLevel       = 0
            )
        {
            if (chars.Length == 0 || i == chars.Length)
            {
                return;
            }

            bool matching        = false;
            bool partialMatching = false;

            while (i < chars.Length)
            {
                bool lastChar = i == chars.Length - 1;
                var  c        = chars[i];
                var  sc       = "" + c;
                matching = false;
                var beginIndex = i;

                /*if (recurseLevel==0 && node==null) {
                 *  // start by grammar multi roots
                 *  foreach ( var kv in rootNode.SubNodes )
                 *  {
                 *      _Parse(ref chars,i,rootNode,kv.Value,paths,currentPath,recurseLevel);
                 *  }
                 *  SelectPaths(ref chars,ref i,ref matching,ref partialMatching,rootNode,ref node);
                 * }*/

                if (!node.IsRoot)
                {
                    if (node.Label == sc)
                    {
                        // exact match - potential matching sequence
                        matching        = true;
                        partialMatching = false;
                    }
                    else
                    {
                        if (node.Label == SCF)
                        {
                            matching        = c < 32 && c != 27;
                            partialMatching = false;
                        }
                        else if (node.Label == CHAR)
                        {
                            matching        = true;
                            partialMatching = false;
                        }
                        else if (node.Label == TEXT)
                        {
                            // @TODO: develop
                            matching        = true;
                            partialMatching = true;
                        }
                        else if (node.Label == NUMLIST)
                        {
                            if (char.IsDigit(c) || c == ';')
                            {
                                matching        = true;
                                partialMatching = true;
                            }
                            else
                            {
                                matching = !partialMatching;
                            }
                        }
                        else if (node.Label == NUM)
                        {
                            if (char.IsDigit(c))
                            {
                                matching        = true;
                                partialMatching = true;
                            }
                        }
                        else
                        {
                            matching = false;
                        }
                    }

                    i++;
                }
                else
                {
                    matching        = true;
                    partialMatching = false;
                }

                #region functions

                void AddRemainingText(ref char[] chars, SyntacticBlock selected)
                {
                    if (selected.Index > 0)
                    {
                        if (paths.Count > 1)
                        {
                            int holeSize, blockIndex;
                            if (paths.Count > 1)
                            {
                                var previous = paths[paths.Count - 2];
                                holeSize   = selected.Index - (previous.Index + previous.Text.Length - 1) - 1;
                                blockIndex = previous.Index + previous.Text.Length;
                            }
                            else
                            {
                                holeSize   = selected.Index;
                                blockIndex = 0;
                            }

                            if (holeSize > 0)
                            {
                                var textBlock = new SyntacticBlock(
                                    blockIndex,
                                    null,
                                    new string(chars, blockIndex, holeSize),
                                    true, false
                                    );
                                paths.Insert(paths.Count - 1, textBlock);
                            }
                        }
                        else
                        {
                            var textBlock = new SyntacticBlock(
                                0,
                                null,
                                new string(chars, 0, selected.Index),
                                true, false
                                );
                            paths.Insert(paths.Count - 1, textBlock);
                        }
                    }
                }

                void SelectPaths(
                    ref char[] chars,
                    ref int i,
                    ref bool matching,
                    ref bool partialMatching,
                    TreeNode rootNode,
                    ref TreeNode node
                    )
                {
                    // any sub syntax of another syntax is removed
                    // equivalent are selected dependings on rule priority
                    var _paths = paths.Where(x => !x.IsSelected).ToList();

                    if (_paths.Count == 0)
                    {
                        return;
                    }
                    List <SyntacticBlock> t;

                    if (_paths.Count > 1)
                    {
                        var maxLength = _paths.Max(x => x.Text.Length);
                        var longests  = _paths.Where(x => x.Text.Length == maxLength);
                        t = new List <SyntacticBlock>(longests);
                        t.Sort((x, y) => x.SyntacticRule.Rule.ID.CompareTo(y.SyntacticRule.Rule.ID));
                    }
                    else
                    {
                        t = _paths;
                    }

                    // keep only the right syntax
                    var selected = t.First();

                    selected.IsSelected = true;
                    matching            = false;
                    partialMatching     = false;
                    var tmp = new List <SyntacticBlock>(paths);

                    foreach (var p in tmp)
                    {
                        if (!p.IsSelected && p != selected)
                        {
                            paths.Remove(p);
                        }
                    }

                    // add text node if any

                    AddRemainingText(ref chars, selected);

                    // setup parser to go on on next position

                    i += selected.Text.Length - 1;

                    if (rootNode != null)
                    {
                        node = rootNode;
                    }
                    currentPath = null;
                }

                #endregion

                if (
                    (matching && !partialMatching) ||
                    (!matching && partialMatching)
                    )
                {
                    if (!partialMatching || !matching)
                    {
                        if (!node.IsRoot)
                        {
                            if (currentPath == null)
                            {
                                currentPath = new TreePath(null, beginIndex);
                            }
                            else
                            {
                                currentPath = new TreePath(currentPath.Rule, currentPath.Index, currentPath);
                            }
                            currentPath.Add(node);
                        }

                        if (node.SubNodes.Count == 0)
                        {
                            // gram path match
                            var sblock =
                                new SyntacticBlock(
                                    currentPath.Index,
                                    currentPath,
                                    new string(chars, currentPath.Index, i - currentPath.Index ));
                            var key = sblock.SyntacticRule.Key;
                            if (_rulesIndex.TryGetValue(key, out var matchingRule))
                            {
                                sblock.SyntacticRule.Rule = matchingRule;
                            }
                            else
                            {
                                throw new Exception($"a syntactic pattern has been recognized but no corresponding rule can be found (grammar is ambiguous ?) : {sblock}");
                            }

                            paths.Add(sblock);

                            if (recurseLevel == 0)
                            {
                                SelectPaths(ref chars, ref i, ref matching, ref partialMatching, rootNode, ref node);
                            }
                            else
                            {
                                return; // stop on result
                            }
                        }
                        else
                        {
                            i += (partialMatching)?-1:0; /*ignore last match*/
                            //var m = matching;
                            //var pm = partialMatching;
                            //matching = partialMatching = false;
                            // explore gram next
                            foreach (var kv in node.SubNodes)
                            {
                                var pathsCount = paths.Count;
                                _Parse(
                                    ref chars, i, rootNode, kv.Value, paths, currentPath,
                                    node.IsRoot,
                                    recurseLevel + 1);
                                var hasNewPath = paths.Count > pathsCount;
                                //if (node.IsRoot)
                                //    SelectPaths(ref chars,ref i,ref matching,ref partialMatching,rootNode,ref node);
                            }

                            if (recurseLevel == 0)
                            {
                                SelectPaths(ref chars, ref i, ref matching, ref partialMatching, rootNode, ref node);
                                if (node.IsRoot)
                                {
                                    i++;
                                }
                            }
                            else
                            {
                                // stop on result
                                return;
                            }
                        }
                    }
                    else
                    {
                        // else scan next of current pattern
                    }
                }
                else
                {
                    if (!(matching && partialMatching))
                    {
                        if (currentPath != null)
                        {
                            // path doesn't match
                            currentPath = null;
                            return; // rule not fit at i - stop on result
                        }
                        else
                        {
                            if (returnImmediately)
                            {
                                return;
                            }
                        }
                    }
                    else
                    {
                        // scan next of current pattern ('node' partial matching)
                    }
                }
            }

            // end string

            #region handle tail

            if (currentPath != null)
            {
                // path match to the end of the string but not ended
                // incomplete path
                var sblock = new SyntacticBlock(
                    currentPath.Index,
                    currentPath,
                    new string(chars, currentPath.Index, i - currentPath.Index ),
                    true
                    );
                var key = sblock.SyntacticRule.Key;
                if (_rulesIndex.TryGetValue(key, out var matchingRule))
                {
                    sblock.SyntacticRule.Rule = matchingRule;
                }
                paths.Add(sblock);
            }
            else
            {
                // add remaining text
                int beginIndex = 0;
                if (paths.Count > 0)
                {
                    var last = paths.Last();
                    beginIndex = last.Index + last.Text.Length;
                }
                var textBlock = new SyntacticBlock(
                    beginIndex,
                    null,
                    new string(chars, beginIndex, chars.Length - beginIndex),
                    true, false
                    );
                paths.Add(textBlock);
            }

            #endregion
        }