/*public override void visit(class_members cm)
        {
            foreach (var decl in cm.members)
            {
                if (decl is procedure_header || decl is procedure_definition)
                    decl.visit(this);
            }
            base.visit(cm);
        }*/

        type_declarations GenClassesForYield(procedure_definition pd,
            IEnumerable<var_def_statement> fields, // локальные переменные
            IDictionary<string, string> localsMap, // отображение для захваченных имен локальных переменных
            IDictionary<string, string> formalParamsMap, // отображение для захваченных имен формальных параметров
            IDictionary<var_def_statement, var_def_statement> localsCloneMap, // отображение для оберток локальных переменных 
            yield_locals_type_map_helper localTypeMapHelper) // вспомогательный узел для типов локальных переменных
        {
            var fh = (pd.proc_header as function_header);
            if (fh == null)
                throw new SyntaxError("Only functions can contain yields", "", pd.proc_header.source_context, pd.proc_header);
            var seqt = fh.return_type as sequence_type;
            if (seqt == null)
                throw new SyntaxError("Functions with yields must return sequences", "", fh.return_type.source_context, fh.return_type);

            // Теперь на месте функции генерируем класс

            // Захваченные локальные переменные
            var cm = class_members.Public;
            var capturedFields = fields.Select(vds =>
                                    {
                                        ident_list ids = new ident_list(vds.vars.idents.Select(id => new ident(localsMap[id.name])).ToArray());
                                        if (vds.vars_type == null) //&& vds.inital_value != null)
                                        {
                                            if (vds.inital_value != null)
                                            {
                                                //return new var_def_statement(ids, new yield_unknown_expression_type(localsCloneMap[vds], varsTypeDetectorHelper), null);
                                                return new var_def_statement(ids, new yield_unknown_expression_type(localsCloneMap[vds], localTypeMapHelper), null);
                                            }
                                            else
                                            {
                                                throw new Exception("Variable defenition without type and value!");
                                            }
                                        }
                                        else
                                        {
                                            return new var_def_statement(ids, vds.vars_type, null);
                                        }
                                        
                                        //return new var_def_statement(ids, vds.vars_type, vds.inital_value);
                                    });

            foreach (var m in capturedFields)
                cm.Add(m);

            // Параметры функции
            List<ident> lid = new List<ident>();
            var pars = fh.parameters;
            if (pars != null)
                foreach (var ps in pars.params_list)
                {
                    if (ps.param_kind != parametr_kind.none)
                        throw new SyntaxError("Parameters of functions with yields must not have 'var', 'const' or 'params' modifier", "", pars.source_context, pars);
                    if (ps.inital_value != null)
                        throw new SyntaxError("Parameters of functions with yields must not have initial values", "", pars.source_context, pars);
                    //var_def_statement vds = new var_def_statement(ps.idents, ps.vars_type);
                    ident_list ids = new ident_list(ps.idents.list.Select(id => new ident(formalParamsMap[id.name])).ToArray());
                    var_def_statement vds = new var_def_statement(ids, ps.vars_type);
                    cm.Add(vds); // все параметры функции делаем полями класса
                    //lid.AddRange(vds.vars.idents);
                    lid.AddRange(ps.idents.list);
                }

            var stels = seqt.elements_type;

            var iteratorClassName = GetClassName(pd);

            // frninja 08/18/15 - Для захвата self
            if (iteratorClassName != null)
            {
                // frninja 20/04/16 - поддержка шаблонных классов
                var iteratorClassRef = CreateClassReference(iteratorClassName);

                cm.Add(new var_def_statement(YieldConsts.Self, iteratorClassRef));
            }

            // Системные поля и методы для реализации интерфейса IEnumerable
            cm.Add(new var_def_statement(YieldConsts.State, "integer"),
                new var_def_statement(YieldConsts.Current, stels),
                procedure_definition.EmptyDefaultConstructor,
                new procedure_definition("Reset"),
                new procedure_definition("MoveNext", "boolean", pd.proc_body),
                new procedure_definition("System.Collections.IEnumerator.get_Current", "object", new assign("Result", YieldConsts.Current)),
                new procedure_definition("System.Collections.IEnumerable.GetEnumerator", "System.Collections.IEnumerator", new assign("Result", "Self"))
                );

            
            

            // frninja 20/04/16 - поддержка шаблонных классов
            var yieldClassName = NewYieldClassName();
            var yieldClassHelperName = yieldClassName + "Helper";

            var className = this.CreateHelperClassName(yieldClassName, iteratorClassName, pd);
            var classNameHelper = this.CreateHelperClassName(yieldClassHelperName, iteratorClassName, pd);
            

            var interfaces = new named_type_reference_list("System.Collections.IEnumerator", "System.Collections.IEnumerable");

            // frninja 24/04/16 - поддержка шаблонных классов
            //var td = new type_declaration(classNameHelper, this.CreateHelperClassDefinition(classNameHelper, pd, interfaces, cm));
                //SyntaxTreeBuilder.BuildClassDefinition(interfaces, cm));

            // Изменение тела процедуры
            

            // frninja 20/04/16 - поддержка шаблонных классов
            var stl = new statement_list(new var_statement("res", new new_expr(this.CreateClassReference(className), new expression_list())));
            

            //stl.AddMany(lid.Select(id => new assign(new dot_node("res", id), id)));
            stl.AddMany(lid.Select(id => new assign(new dot_node("res", new ident(formalParamsMap[id.name])), id)));

            // frninja 08/12/15 - захват self
            if (iteratorClassName != null)
            {
                stl.Add(new assign(new dot_node("res", YieldConsts.Self), new ident("self")));
            }

            stl.Add(new assign("Result", "res"));

            // New body
            pd.proc_body = new block(stl);

            if (iteratorClassName != null)
            {
                var cd = UpperTo<class_definition>();
                if (cd != null)
                {
                    // Если метод описан в классе 
                    // frninja 10/12/15 - заменить на function_header и перенести описание тела в declarations
                    Replace(pd, fh);
                    var decls = UpperTo<declarations>();
                    if (decls != null)
                    {
                        // frninja 12/05/16 - забыли копировать return
                        function_header nfh = ObjectCopier.Clone(fh);
                        //function_header nfh = new function_header();
                        //nfh.name = new method_name(fh.name.meth_name.name);

                        // Set className
                        nfh.name.class_name = iteratorClassName;
                        //nfh.parameters = fh.parameters;
                        //nfh.proc_attributes = fh.proc_attributes;
                        //nfh.return_type = fh.return_type;

                        procedure_definition npd = new procedure_definition(nfh, new block(stl));

                        // Update header
                        //pd.proc_header.className.class_name = GetClassName(pd);
                        // Add to decls
                        decls.Add(npd);
                    }
                }
            }

            // Второй класс

            var tpl = new template_param_list(stels);

            var IEnumeratorT = new template_type_reference("System.Collections.Generic.IEnumerator", tpl);

            var cm1 = cm.Add( //class_members.Public.Add(
                //procedure_definition.EmptyDefaultConstructor,
                new procedure_definition(new function_header("get_Current", stels), new assign("Result", YieldConsts.Current)),
                new procedure_definition(new function_header("GetEnumerator", IEnumeratorT), new assign("Result", "Self")),
                new procedure_definition("Dispose")
            );


            // frninja 20/04/16 - поддержка шаблонных классов
            var interfaces1 = new named_type_reference_list(/*this.CreateClassReference(classNameHelper) as named_type_reference*/);
            var IEnumerableT = new template_type_reference("System.Collections.Generic.IEnumerable", tpl);

            interfaces1.Add(IEnumerableT).Add(IEnumeratorT);

            // frninja 24/04/16 - поддержка шаблонных классов
            var td1 = new type_declaration(className, this.CreateHelperClassDefinition(className, pd, interfaces1, cm1));
                //SyntaxTreeBuilder.BuildClassDefinition(interfaces1, cm1));

            var cct = new type_declarations(/*td*/);
            cct.Add(td1);

            return cct;
        }
 public LocalVariablesTypeDetectorHelperVisior(yield_locals_type_map_helper varTypeMap)
 {
     this.VarTypeMap = varTypeMap;
 }
        /// <summary>
        /// Обработка локальных переменных метода и их типов для корректного захвата
        /// </summary>
        /// <param className="pd">Объявление метода</param>
        /// <returns>Коллекция посещенных локальных переменных</returns>
        private void CreateLocalVariablesTypeProxies(procedure_definition pd, out IEnumerable<var_def_statement> localsClonesCollection, out yield_locals_type_map_helper localsTypeMapHelper)
        {
            // Выполняем определение типов локальных переменных с автовыводом типов

            // Клонируем исходный метод для вставки оберток-хелперов для локальных переменных и дальнейшей обработки на семантике
            var pdCloned = ObjectCopier.Clone(pd);

            // Заменяем локальные переменные с неизвестным типом на обертки-хелперы (откладываем до семантики)
            localsTypeMapHelper = new yield_locals_type_map_helper();
            LocalVariablesTypeDetectorHelperVisior localsTypeDetectorHelperVisitor = new LocalVariablesTypeDetectorHelperVisior(localsTypeMapHelper);
            pdCloned.visit(localsTypeDetectorHelperVisitor);

            // frninja 16/03/16 - строим список локальных переменных в правильном порядке
            localsTypeDetectorHelperVisitor.LocalDeletedDefs.AddRange(localsTypeDetectorHelperVisitor.LocalDeletedVS);
            localsTypeDetectorHelperVisitor.LocalDeletedVS.Clear();

            localsClonesCollection = localsTypeDetectorHelperVisitor.LocalDeletedDefs.ToArray();

            // Добавляем в класс метод с обертками для локальных переменных
            pdCloned.proc_header.name.meth_name = new ident(YieldConsts.YieldHelperMethodPrefix+ "_locals_type_detector>" + pd.proc_header.name.meth_name.name); // = new method_name("<yield_helper_locals_type_detector>" + pd.proc_header.className.meth_name.className);
            if (IsClassMethod(pd))
            {
                var cd = UpperTo<class_definition>();
                if (cd != null)
                {
                    // Метод класса описан в классе
                    var classMembers = UpperTo<class_members>();
                    classMembers.Add(pdCloned);
                }
                else
                {
                    // Метод класса описан вне класса

                    var decls = UpperTo<declarations>();
                    var classMembers = decls.list
                        .Select(decl => decl as type_declarations)
                        .Where(tdecls => tdecls != null)
                        .SelectMany(tdecls => tdecls.types_decl)
                        .Where(td => td.type_name.name == GetClassName(pd).name)
                        .Select(td => td.type_def as class_definition)
                        .Where(_cd => _cd != null)
                        .SelectMany(_cd => _cd.body.class_def_blocks);


                    // Вставляем предописание метода-хелпера 
                    var helperPredefHeader = ObjectCopier.Clone(pdCloned.proc_header);
                    helperPredefHeader.name.class_name = null;
                    classMembers.First().members.Insert(0, helperPredefHeader);

                    // Вставляем тело метода-хелпера
                    UpperTo<declarations>().InsertBefore(pd, pdCloned);
                }
            }
            else
            {
                UpperTo<declarations>().InsertBefore(pd, pdCloned);
            }
        }