using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using PascalABCCompiler; using PascalABCCompiler.SyntaxTree; using PascalABCCompiler.ParserTools; using PascalABCCompiler.Errors; using PascalABCCompiler.YieldHelpers; namespace SyntaxVisitors { public static class CapturedNamesHelper { public static int CurrentLocalVariableNum = 0; public static string MakeCapturedFormalParameterName(string formalParamName) { return string.Format("<>{0}__{1}", YieldConsts.ReservedNum.MethodFormalParam, formalParamName); } public static string MakeCapturedLocalName(string localName) { return string.Format("<{0}>{1}__{2}", localName, YieldConsts.ReservedNum.MethodLocalVariable, ++CurrentLocalVariableNum); } } public class ProcessYieldCapturedVarsVisitor : BaseChangeVisitor { int clnum = 0; public string NewYieldClassName() { clnum++; return "clyield#" + clnum.ToString(); } public FindMainIdentsVisitor mids; // захваченные переменные процедуры по всем её yield public int countNodesVisited; public bool hasYields = false; public static ProcessYieldCapturedVarsVisitor New { get { return new ProcessYieldCapturedVarsVisitor(); } } public ProcessYieldCapturedVarsVisitor() { //PrintInfo = false; } /*public override void Enter(syntax_tree_node st) { base.Enter(st); countNodesVisited++; // сокращение обходимых узлов. Как сделать фильтр по тем узлам, которые необходимо обходить? Например, все операторы (без выражений и описаний), все описания (без операторов) if (st.GetType()==typeof(assign) || st.GetType()==typeof(var_def_statement) || st is procedure_call || st is procedure_header || st is expression) { visitNode = false; // фильтр - куда не заходить } }*/ /*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 fields, // локальные переменные IDictionary localsMap, // отображение для захваченных имен локальных переменных IDictionary formalParamsMap//, // отображение для захваченных имен формальных параметров //IDictionary localsCloneMap // отображение для оберток локальных переменных ) { 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(vds), null); // SSM - убрал localsCloneMap[vds] - заменил на vds - не знаю, зачем вообще это отображение делалось - всё равно оно было тождественным!!! } else { throw new SyntaxVisitorError("Variable defenition without type and value!",vds.source_context); // SSM - быть такого не может - грамматика не пропустит } } 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 lid = new List(); var pars = fh.parameters; if (pars != null) foreach (var ps in pars.params_list) { if (ps.param_kind != parametr_kind.none) throw new SyntaxVisitorError("FUNCTIONS_WITH_YIELDS_CANNOT_CONTAIN_VAR_CONST_PARAMS_MODIFIERS", pars.source_context); if (ps.inital_value != null) throw new SyntaxVisitorError("FUNCTIONS_WITH_YIELDS_CANNOT_CONTAIN_DEFAULT_PARAMETERS", pars.source_context); //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)); } var GetEnumeratorBody = new statement_list(); // Системные поля и методы для реализации интерфейса 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")) new procedure_definition("System.Collections.IEnumerable.GetEnumerator", "System.Collections.IEnumerator", GetEnumeratorBody) ); // 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 && !pd.proc_header.class_keyword) { 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(); if (cd != null) { // Если метод описан в классе // frninja 10/12/15 - заменить на function_header и перенести описание тела в declarations Replace(pd, fh); var decls = UpperTo(); if (decls != null) { // frninja 12/05/16 - забыли копировать return //function_header nfh = ObjectCopier.Clone(fh); function_header nfh = fh.TypedClone(); //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), GetEnumeratorBody), 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 - поддержка шаблонных классов // frninja 05/06/16 - фикс. Поддержка where секции var helperClassDefinition = this.CreateHelperClassDefinition(className, pd, interfaces1, cm1); helperClassDefinition.where_section = this.GetMethodWhereSection(pd); var td1 = new type_declaration(className, helperClassDefinition); //SyntaxTreeBuilder.BuildClassDefinition(interfaces1, cm1)); var stl1 = new statement_list(new var_statement("$res", new new_expr(this.CreateClassReference(className), new expression_list()))); stl1.AddMany(lid.Select(id => new assign(new dot_node("$res", new ident(formalParamsMap[id.name])), new ident(formalParamsMap[id.name])))); // Переприсваивание self if (iteratorClassName != null && !pd.proc_header.class_keyword) { stl1.Add(new assign(new dot_node("$res", YieldConsts.Self), new ident(YieldConsts.Self))); } stl1.Add(new assign("Result", "$res")); GetEnumeratorBody.Add(new if_node(new bin_expr(new ident(YieldConsts.State), new int32_const(0), Operators.Equal), new assign("Result", "Self"), stl1)); var cct = new type_declarations(/*td*/); cct.Add(td1); return cct; } private void CollectFormalParams(procedure_definition pd, ISet collectedFormalParams) { if (pd.proc_header.parameters != null) collectedFormalParams.UnionWith(pd.proc_header.parameters.params_list.Select(tp => new var_def_statement(tp.idents, tp.vars_type))); } private void CollectFormalParamsNames(procedure_definition pd, ISet collectedFormalParamsNames) { if (pd.proc_header.parameters != null) collectedFormalParamsNames.UnionWith(pd.proc_header.parameters.params_list.SelectMany(tp => tp.idents.idents).Select(id => id.name)); } /// /// Создает обращение к имени класса по имени класса /// /// Имя класса /// private type_definition CreateClassReference(ident className) { if (className is template_type_name) { return new template_type_reference( new named_type_reference(className), new template_param_list(string.Join(",", (className as template_type_name).template_args.idents.Select(id => id.name))) ); } return new named_type_reference(className); } /// /// Создает имя вспомогательного класса /// /// Имя вспомогательного класса /// Имя класса /// private ident CreateHelperClassName(string helperName, ident className, procedure_definition pd) { if (className is template_type_name) { return new template_type_name(helperName, (className as template_type_name).template_args); } else if (pd.proc_header.template_args != null) { return new template_type_name(helperName, pd.proc_header.template_args); } return new ident(helperName); } private class_definition CreateHelperClassDefinition(ident className, procedure_definition pd, named_type_reference_list parents, params class_members[] cms) { if (className is template_type_name) { return SyntaxTreeBuilder.BuildClassDefinition(parents, (className as template_type_name).template_args , cms); } else if (pd.proc_header.template_args != null) { return SyntaxTreeBuilder.BuildClassDefinition(parents, pd.proc_header.template_args, cms); } return SyntaxTreeBuilder.BuildClassDefinition(parents, cms); } /// /// Получает имя класса, в котором описан метод-итератор /// /// /// private ident GetClassName(procedure_definition pd) { if (pd.proc_header.name.class_name != null) { // Объявление вне класса его метода return pd.proc_header.name.class_name; } else { // Объявление функции в классе? var classDef = UpperNode(3) as class_definition; if (classDef != null) { var td = UpperNode(4) as type_declaration; if (td != null) { return td.type_name; } } } return null; } private class_definition GetMethodClassDefinition(procedure_definition pd) { if (!IsClassMethod(pd)) { return null; } var cd = UpperTo(); if (cd != null) { // Метод класса описан в классе return cd; } else { // Метод класса описан вне класса return UpperTo().list .OfType() .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) .DefaultIfEmpty() .First(); } } private where_definition_list GetMethodWhereSection(procedure_definition pd) { if (!IsClassMethod(pd)) { if (pd.proc_header.where_defs != null) { return ObjectCopier.Clone(pd.proc_header.where_defs); //return pd.proc_header.where_defs.TypedClone(); } else { var pdPredefs = UpperTo().defs .OfType() .Where(lpd => lpd.proc_body == null && lpd.proc_header.name.meth_name.name == pd.proc_header.name.meth_name.name && lpd.proc_header.proc_attributes.proc_attributes.FindIndex(attr => attr.attribute_type == proc_attribute.attr_forward) != -1); if (pdPredefs.Count() > 0) { return ObjectCopier.Clone(pdPredefs.First().proc_header.where_defs); //return pdPredefs.First().proc_header.where_defs.TypedClone(); } } } else { class_definition cd = GetMethodClassDefinition(pd); if (cd != null) { return ObjectCopier.Clone(cd.where_section); } } return null; } /// /// Определяет описан ли метод-итератор в некотором классе /// /// /// private bool IsClassMethod(procedure_definition pd) { return GetClassName(pd) != null; } private void CollectClassFieldsNames(procedure_definition pd, ISet collectedFields) { ident className = GetClassName(pd); if (className != null) { CollectClassFieldsVisitor fieldsVis = new CollectClassFieldsVisitor(className); var cu = UpperTo(); if (cu != null) { cu.visit(fieldsVis); // Collect collectedFields.UnionWith(fieldsVis.CollectedFields.Select(id => id.name)); } } } private void CollectClassMethodsNames(procedure_definition pd, ISet collectedMethods) { ident className = GetClassName(pd); if (className != null) { CollectClassMethodsVisitor methodsVis = new CollectClassMethodsVisitor(className); var cu = UpperTo(); if (cu != null) { cu.visit(methodsVis); // Collect collectedMethods.UnionWith(methodsVis.CollectedMethods.Select(id => id.name)); } } } private void CollectClassPropertiesNames(procedure_definition pd, ISet collectedProperties) { ident className = GetClassName(pd); if (className != null) { CollectClassPropertiesVisitor propertiesVis = new CollectClassPropertiesVisitor(className); var cu = UpperTo(); if (cu != null) { cu.visit(propertiesVis); // Collect collectedProperties.UnionWith(propertiesVis.CollectedProperties.Select(id => id.name)); } } } private void CollectUnitGlobalsNames(procedure_definition pd, ISet collectedUnitGlobalsName) { var cu = UpperTo(); if (cu != null) { var ugVis = new CollectUnitGlobalsVisitor(); cu.visit(ugVis); // Collect collectedUnitGlobalsName.UnionWith(ugVis.CollectedGlobals.Select(id => id.name)); } } private void CreateCapturedLocalsNamesMap(ISet localsNames, IDictionary capturedLocalsNamesMap) { foreach (var localName in localsNames) { capturedLocalsNamesMap.Add(localName, CapturedNamesHelper.MakeCapturedLocalName(localName)); } } private void CreateCapturedFormalParamsNamesMap(ISet formalParamsNames, IDictionary captueedFormalParamsNamesMap) { foreach (var formalParamName in formalParamsNames) { captueedFormalParamsNamesMap.Add(formalParamName, CapturedNamesHelper.MakeCapturedFormalParameterName(formalParamName)); } } // frninja 31/05/16 /// /// Вставляет метод-хелпер /// /// Метод-итератор /// Метод-хелпер private void InsertHelperMethod(procedure_definition pd, procedure_definition helper) { helper.proc_header.is_yield_helper = true; if (IsClassMethod(pd)) { var cd = UpperTo(); if (cd != null) { // Метод класса описан в классе var classMembers = UpperTo(); classMembers.Add(helper); } else { // Метод класса описан вне класса var decls = UpperTo(); 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(helper.proc_header); helperPredefHeader.name.class_name = null; classMembers.First().members.Insert(0, helperPredefHeader); // Вставляем тело метода-хелпера UpperTo().InsertBefore(pd, helper); } } else { UpperTo().InsertBefore(pd, helper); } } // frninja 31/05/16 /// /// Вставляет клон метода-итератора для того, чтобы проверить ошибки существующим бэкендом (пересечение диапазонов case, повторные объявления переменных) /// /// private void CreateErrorCheckerHelper(procedure_definition pd) { // Клонируем исходный метод для проверок ошибок бэкендом //var pdCloned = ObjectCopier.Clone(pd); var pdCloned = (procedure_definition)pd.Clone(); pdCloned.has_yield = false; // Добавляем в класс метод с обертками для локальных переменных pdCloned.proc_header.name.meth_name = new ident(YieldConsts.YieldHelperMethodPrefix + "_error_checkerr>" + pd.proc_header.name.meth_name.name, // frninja 05/06/16 - фиксим source_context pd.proc_header.name.meth_name.source_context); // = new method_name("" + pd.proc_header.className.meth_name.className); //pdCloned.is_yield_helper = true; InsertHelperMethod(pd, pdCloned); } /// /// Обработка локальных переменных метода и их типов для корректного захвата /// /// Объявление метода /// Коллекция посещенных локальных переменных // SSM - Эта функция уже не нужна - я иногда использую ее чтобы посмотреть как выглядит функция после Loweringа private void CreateLocalVariablesTypeProxies(procedure_definition pd, out IEnumerable localsClonesCollection) { // Выполняем определение типов локальных переменных с автовыводом типов // Клонируем исходный метод для вставки оберток-хелперов для локальных переменных и дальнейшей обработки на семантике var pdCloned = ObjectCopier.Clone(pd); //pd. //var pdCloned = (procedure_definition)pd.Clone(); pdCloned.has_yield = false; // Заменяем локальные переменные с неизвестным типом на обертки-хелперы (откладываем до семантики) var localsTypeDetectorHelperVisitor = new LocalVariablesTypeDetectorHelperVisior(); 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, // frninja 05/06/16 - фиксим source_context pd.proc_header.name.meth_name.source_context); // = new method_name("" + pd.proc_header.className.meth_name.className); InsertHelperMethod(pd, pdCloned); // SSM 13.07.16 - вызов этого метода можно не добавлять } /// /// Отображение локальных в клонированные локальные /// /// Откуда /// Куда /// Отображение /*private Dictionary CreateLocalsClonesMap(IEnumerable from, IEnumerable to) { // Нужно тк клонировали метод для создания хелпера-определителя типов локальных переменных - Eq не будет работать // Строим отображение из локальных переменных клона оригинального метода в локальные переменные основного метода Dictionary localsClonesMap = new Dictionary(); var localsArr = from.ToArray(); var localsClonesArr = to.ToArray(); // Create map :: locals -> cloned locals for (int i = 0; i < localsArr.Length; ++i) { localsClonesMap.Add(localsArr[i], localsClonesArr[i]); } return localsClonesMap; }*/ /// /// Вставляем описание классов-хелперов для yield перед методом-итератором в зависимости от его описания /// /// Метод-итератор /// Описание классов-хелперов для yield private void InsertYieldHelpers(procedure_definition pd, type_declarations cct) { if (IsClassMethod(pd)) { var cd = UpperTo(); if (cd != null) { // Если метод класса описан в классе var td = UpperTo(); // frninja 20/04/16 - выпилено //foreach (var helperName in cct.types_decl.Select(ttd => ttd.type_name)) //{ // var helperPredef = new type_declaration(helperName, new class_definition()); //td.types_decl.Insert(0, helperPredef); //} // Insert class predefenition! //var iteratorClassPredef = new type_declaration(GetClassName(pd), new class_definition(null)); //td.types_decl.Insert(0, iteratorClassPredef); foreach (var helper in cct.types_decl) { td.types_decl.Add(helper); } } else { // Метод класса описан вне класса UpperTo().InsertBefore(pd, cct); } } else { UpperTo().InsertBefore(pd, cct); } } /// /// Захватываем имена в методе /// /// Метод-итератор /// Коллекция удаленных локальных переменных /// Построенное отображение имен локальных переменных в захваченные имена /// Построенное отображение имен формальных параметров в захваченные имена private void ReplaceCapturedVariables(procedure_definition pd, IEnumerable deletedLocals, out IDictionary capturedLocalsNamesMap, out IDictionary capturedFormalParamsNamesMap) { // Структуры данных под классификацию имен в методе // Classification ISet CollectedLocalsNames = new HashSet(); ISet CollectedFormalParamsNames = new HashSet(); ISet CollectedClassFieldsNames = new HashSet(); ISet CollectedClassMethodsNames = new HashSet(); ISet CollectedClassPropertiesNames = new HashSet(); ISet CollectedUnitGlobalsNames = new HashSet(); ISet CollectedLocals = new HashSet(); ISet CollectedFormalParams = new HashSet(); // Map from ident idName -> captured ident idName capturedLocalsNamesMap = new Dictionary(); capturedFormalParamsNamesMap = new Dictionary(); // Собираем инфу о именах // Collect locals CollectedLocals.UnionWith(deletedLocals); CollectedLocalsNames.UnionWith(deletedLocals.SelectMany(vds => vds.vars.idents).Select(id => id.name)); // Collect formal params CollectFormalParams(pd, CollectedFormalParams); CollectFormalParamsNames(pd, CollectedFormalParamsNames); // Collect class fields CollectClassFieldsNames(pd, CollectedClassFieldsNames); // Collect class methods CollectClassMethodsNames(pd, CollectedClassMethodsNames); // Collect class properties CollectClassPropertiesNames(pd, CollectedClassPropertiesNames); // Collect unit globals CollectUnitGlobalsNames(pd, CollectedUnitGlobalsNames); // Строим отображения для имён захваченных локальных переменных и формальных параметров // Create maps :: idName -> captureName CreateCapturedLocalsNamesMap(CollectedLocalsNames, capturedLocalsNamesMap); CreateCapturedFormalParamsNamesMap(CollectedFormalParamsNames, capturedFormalParamsNamesMap); // Выполняем замену захват имён в теле метода // AHAHA test! ReplaceCapturedVariablesVisitor rcapVis = new ReplaceCapturedVariablesVisitor( CollectedLocalsNames, CollectedFormalParamsNames, CollectedClassFieldsNames, CollectedClassMethodsNames, CollectedClassPropertiesNames, CollectedUnitGlobalsNames, capturedLocalsNamesMap, capturedFormalParamsNamesMap, IsClassMethod(pd), pd.proc_header.class_keyword, GetClassName(pd) ); // Replace (pd.proc_body as block).program_code.visit(rcapVis); } private bool IsExtensionMethod(procedure_definition pd) { var tdecls = UpperTo().defs.OfType().SelectMany(tds => tds.types_decl); var isExtension = pd.proc_header.proc_attributes.proc_attributes.Any(attr => attr.name == "extensionmethod"); if (isExtension) { // Метод объявлен как extensionmethod // !!!!!!!! TODO: Проверить что имя класса не находится в этом модуле. // Убираем за ненадобностью имя класса ибо оно указано как тип обязательного параметра self pd.proc_header.name.class_name = null; return true; } else { // Если не похоже на метод-расширение или объявление вне класса if (pd.proc_header.name.class_name == null) return false; // Разрешаем только имена типов из этого модуля (не расширения) if (!tdecls.Any(td => td.type_name.name == pd.proc_header.name.class_name.name)) { // Имя в модуле не найдено -> метод расширение описанный без extensionmethod. Ругаемся!!! throw new SyntaxVisitorError("Possible extension-method definintion without extensionmethod keyword. Please use extensionmethod syntax", pd.proc_header.source_context); } } return false; } /// /// Вставляет предописание метода-итератора для рекурсивных вызовов, если метод описан вне класса /// private bool InsertGlobalIteratorMethodPredefinition(procedure_definition pd) { if (IsExtensionMethod(pd)) // SSM 17.07.16 - нельзя генерировать предописания для extension-методов! { return false; } if (IsClassMethod(pd)) { return false; } // SSM 20/08/16 и еще нельзя генерировать если это функция, описанная в interface и implementation модуля var un = UpperNode(2) as implementation_node; if (un != null) { var cu = UpperNode(3) as unit_module; if (cu != null) // а это всегда так { var fhh = cu.interface_part.interface_definitions.defs.OfType().Where(fh => fh.name.meth_name.name.ToLower()==pd.proc_header.name.meth_name.name.ToLower()); if (fhh.Any()) // если еcть в разделе интерфейса такие же имена, то не генерировать предописание return false; // вообще-то надо сравнивать заголовки - не только имена - но даже так устраняется основная масса ошибок } } var pdPredefs = UpperTo().defs .OfType() .Where(lpd => lpd.proc_body == null && lpd.proc_header.name.meth_name.name == pd.proc_header.name.meth_name.name && lpd.proc_header.proc_attributes.proc_attributes.FindIndex(attr => attr.attribute_type == proc_attribute.attr_forward) != -1); bool isPredefined = pdPredefs.Count() > 0; if (!isPredefined) { var fh = ObjectCopier.Clone(pd.proc_header as function_header); fh.proc_attributes.Add(new procedure_attribute(proc_attribute.attr_forward)); procedure_definition predef = new procedure_definition() { proc_header = fh }; // frninja 05/06/16 - для шаблонов с where predef.proc_header.where_defs = ObjectCopier.Clone(pd.proc_header.where_defs); UpperTo().InsertBefore(pd, predef); return true; } return false; } private void CheckInnerMethodsWithYield(procedure_definition pd) { var pdWithYields = pd.DescendantNodes(includeSelf: true) .OfType() .Where(npd => npd.has_yield); var pddds = pdWithYields.Count(); } public override void visit(procedure_definition pd) { // frninja 14/06/16 - проверяем наличие yield у вложенных методов (и запрещаем ) CheckInnerMethodsWithYield(pd); if (!pd.has_yield) return; // frninja 05/06/16 - вставляем предописание если метод-итератор описан не в классе (обычная функция) чтоб работали рекурсивные вызовы // Нужен также для верной работы функции, проверяющей разные ошибки на этапе семантики // Как только уберу -функцию, от этого тоже можно избавиться bool methodPredefCreated = InsertGlobalIteratorMethodPredefinition(pd); // frninja 24/05/16 - оборачиваем одиночные операторы в begin..end AddBeginEndsVisitor.Accept(pd); /* // Проверяем проблемы имен для for CheckVariablesRedefenitionVisitor checkVarRedefVisitor = new CheckVariablesRedefenitionVisitor( new HashSet( pd.proc_header.parameters != null ? pd.proc_header.parameters.params_list.SelectMany(fp => fp.idents.idents.Select(id => id.name)) : Enumerable.Empty())); pd.visit(checkVarRedefVisitor); */ // Выносим выражение из yield в отдельную переменную ReplaceYieldExprByVarVisitor.Accept(pd); // Раскрываем операторы yield sequence. На семантике они не существуют LoweringYieldSequenceVisitor.Accept(pd); // frninja 31/05/16 - добавляем метод-хелпер, возьмет на себя проверку разных ошибок уже существующим бэкендом CreateErrorCheckerHelper(pd); // SSM 14/07/16 - переставил до переименования переменных чтобы отлавливались ошибки одинаковых имен в разных пространствах имен // SSM - можно сделать спец визитор, который бы отлавливал дубли имен - тогда этого не надо // Переименовываем одинаковые имена в мини-ПИ: begin var a := 1 end; begin var a := 1 end; RenameSameBlockLocalVarsVisitor.Accept(pd); // SSM 01/08/16 - надо захватить и переименовать еще все формальные параметры. // Это решит проблему их изменения в pd при следующем вызове запроса. var bb = pd.proc_body as block; if (pd.proc_header.parameters != null) { var fpids = pd.proc_header.parameters.params_list.SelectMany(tp => tp.idents.idents); foreach (var v in fpids) { var vds = new var_statement(new ident("$fp_"+v.name, v.source_context), v); bb.program_code.AddFirst(vds); } } // Теперь lowering LoweringVisitor.Accept(pd); // frninja 13/04/16 - убираем лишние begin..end DeleteRedundantBeginEnds.Accept(pd); // Обработка метода для корректного захвата локальных переменных и их типов // - это уже не надо - иногда можно включать чтобы посмотреть, что собой представляет функция после Loweringа //IEnumerable localsClonesCollection; //CreateLocalVariablesTypeProxies(pd, out localsClonesCollection); // frninja 16/11/15: перенес ниже чтобы работал захват для lowered for var dld = MoveAllLocalDefsToLists.Accept(pd); // Удалить в локальных и блочных описаниях этой процедуры все переменные и вынести их в отдельный список var_def_statement // Строим отображение из локальных переменных клона оригинального метода в локальные переменные основного метода //Dictionary localsCloneMap = CreateLocalsClonesMap(dld.LocalDeletedDefs, localsClonesCollection); // frninja 08/12/15 // Выполняем захват имён IDictionary CapturedLocalsNamesMap; IDictionary CapturedFormalParamsNamesMap; ReplaceCapturedVariables(pd, dld.LocalDeletedDefs, out CapturedLocalsNamesMap, out CapturedFormalParamsNamesMap); //mids.vars.Except(dld.LocalDeletedDefsNames); // параметры остались. Их тоже надо исключать - они и так будут обработаны // В результате работы в mids.vars что-то осталось. Это не локальные переменные и с ними непонятно что делать // Обработать параметры! // Как? Ищем в mids formal_parametrs, но надо выделить именно обращение к параметрам - не полям класса, не глобальным переменным var cfa = new ConstructFiniteAutomata(pd.proc_body as block); cfa.Transform(); (pd.proc_body as block).program_code = cfa.res; // Конструируем определение класса var cct = GenClassesForYield(pd, dld.LocalDeletedDefs, CapturedLocalsNamesMap, CapturedFormalParamsNamesMap/*, localsCloneMap*/); // все удаленные описания переменных делаем описанием класса // Вставляем классы-хелперы InsertYieldHelpers(pd, cct); // frninja 20/05/16 - фикс для повторного обхода pd.has_yield = false; // frninja 05/06/16 - убираем where-секцию у описания метода. Они уже скопированы куда надо /*if (methodPredefCreated) { pd.proc_header.where_defs = null; }*/ //mids = null; // вдруг мы выйдем из процедуры, не зайдем в другую, а там - оператор! Такого конечно не может быть } } /*class ConstructFiniteAutomata1 { public statement_list res = new statement_list(); statement_list stl; int curState = 0; statement_list curStatList; statement_list StatListAfterCase = new statement_list(); case_node cas; // формируемый case //private Dictionary> dispatches = new Dictionary>(); private labeled_statement OuterLabeledStatement; private Dictionary Dispatches = new Dictionary(); public ConstructFiniteAutomata1(block bl) { this.stl = bl.program_code; } private void AddState(out int stateNumber, out ident resumeLabel) { stateNumber = curState++; resumeLabel = null; } public void Process(statement st) { if (!(st is yield_node || st is labeled_statement)) { curStatList.Add(st); } if (st is yield_node) { var yn = st as yield_node; curState += 1; curStatList.AddMany( new assign(YieldConsts.Current, yn.ex), new assign(YieldConsts.State, curState), new assign("Result", true), new procedure_call("exit") ); curStatList = new statement_list(); case_variant cv = new case_variant(new expression_list(new int32_const(curState)), curStatList); cas.conditions.variants.Add(cv); } if (st is labeled_statement) { var ls = st as labeled_statement; // frninja 13/04/16 - диспетчерезация к следующему состоянию curStatList.Add(new goto_statement(ls.label_name)); curStatList = StatListAfterCase; curStatList.Add(new labeled_statement(ls.label_name)); Process(ls.to_statement); } } public void Transform() { cas = new case_node(new ident(YieldConsts.State)); curStatList = new statement_list(); case_variant cv = new case_variant(new expression_list(new int32_const(curState)), curStatList); cas.conditions.variants.Add(cv); foreach (var st in stl.subnodes) Process(st); // frninja 13/04/16 - фикс для зависающего в последнем состоянии var lastStateCV = cas.conditions.variants.Last().exec_if_true as statement_list; if (lastStateCV != null) { lastStateCV.Add(new procedure_call("exit")); } stl.subnodes = BaseChangeVisitor.SeqStatements(cas, StatListAfterCase).ToList(); //statement_list res = new statement_list(cas); res = stl; } }*/ // SSM 07/07/16 - изменен алгоритм генерации конечного автомата на более простой. Вместо case генерируется последовательность if ... goto состояние. class ConstructFiniteAutomata { public statement_list res = new statement_list(); // сюда писать результат statement_list stl; // это исходные операторы тела функции declarations defs; // это декларации функции - для добавления меток int curState = 0; public ConstructFiniteAutomata(block bl) { this.stl = bl.program_code; defs = bl.defs; } public void Process(statement st) { if (st is yield_node) { var yn = st as yield_node; curState += 1; res.AddMany( new assign(YieldConsts.Current, yn.ex, yn.source_context), new assign(YieldConsts.State, curState), new assign("Result", true), new procedure_call("exit"), new labeled_statement(YieldConsts.LabelStatePrefix+curState.ToString()) ); } else if (st is labeled_statement) { var ls = st as labeled_statement; res.Add(new labeled_statement(ls.label_name)); Process(ls.to_statement); } else { res.Add(st); } } public void Transform() { res.Add(new labeled_statement(YieldConsts.LabelStatePrefix+curState.ToString())); foreach (var st in stl.subnodes) Process(st); // добавим метки состояний var idseq = Enumerable.Range(0, curState + 1).Select(i => new ident("lbstate#" + i.ToString())); var idl = new ident_list(idseq.ToList()); defs.Add(new label_definitions(idl)); statement ifgoto = new goto_statement(YieldConsts.LabelStatePrefix + curState.ToString()); for (var i = curState - 1; i >= 0; i--) ifgoto = new if_node(new bin_expr(new ident(YieldConsts.State), new int32_const(i), Operators.Equal), new goto_statement(YieldConsts.LabelStatePrefix + i.ToString()), ifgoto ); res.AddFirst(ifgoto); res.AddFirst(new assign("Result", false)); } } }