// Copyright (c) Ivan Bondarev, Stanislav Mikhalkovich (for details please see \doc\copyright.txt) // This code is distributed under the GNU LGPL (for details please see \doc\license.txt) /*************************************************************************** * * Управляющий блок компилятора, алгоритм компиляции модулей * Зависит от Errors,SyntaxTree,SemanticTree,Parsers,TreeConvertor, * CodeGenerators * ***************************************************************************/ #region algorithm /*************************************************************************** * Рекурсивный алгоритм компиляции модулей * версия 1.4 * * CompileUnit(ИмяФайла) * 1.CompileUnit(new СписокМодулей,ИмяФайла) * 2.Докомпилировать модули из СписокОтложенойКомпиляции; * * CompileUnit(СписокМодулей,ИмяФайла) * 1.ТекущийМодуль=ТаблицаМодулей[ИмяФайла]; * Если (ТекущийМодуль!=0) то * Если (ТекущийМодуль.Состояние==BeginCompilation) * СписокМодулей.Добавить(ТекущийМодуль); * Выход; * иначе перейти к пункту 5 * * 2.Если ЭтоФайлDLL(ИмяФайла) то * Если ((ТекущийМодуль=СчитатьDLL(ИмяФайла))!=0) то * СписокМодулей.Добавить(ТекущийМодуль); * ТаблицаМодулей.Добавить(ТекущийМодуль); * Выход; * иначе * Ошибка("Не могу подключить сборку"); * Выход; * * 3.Если ЭтоФайлPCU(ИмяФайла) то * Если ((ТекущийМодуль=СчитатьPCU(ИмяФайла))!=0) то * СписокМодулей.Добавить(ТекущийМодуль); * ТаблицаМодулей.Добавить(ТекущийМодуль); * Выход; * иначе * иначе перейти к пункту 4; * * 4.ТекущийМодуль=новыйМодуль(); * ТекущийМодуль.СинтаксическоеДерево=Парасеры.Парсить(ИмяФайла,ТекущийМодуль.СписокОшибок); * Если (ТекущийМодуль.СинтаксическоеДерево==0) то * Если (ТекущийМодуль.СписокОшибок.Количество==0) то * Ошибка("Модуль не неайден"); * иначе * Ошибка(ТекущийМодуль.СписокОшибок[0]); * ТаблицаМодулей[ИмяФайла]=ТекущийМодуль; * ТекущийМодуль.Состояние=BeginCompilation; * * 5.СинтаксическийСписокМодулей=ТекущийМодуль.СинтаксическоеДерево.Interface.usesList; * Для(i=СинтаксическийСписокМодулей.Количество-1-ТекущийМодуль.КомпилированыеВInterface.Количество;i>=0;i--) * ТекушийМодуль.ТекущийUsesМодуль=СинтаксическийСписокМодулей[i].ИмяФайла; * ИмяUsesФайла=СинтаксическийСписокМодулей[i].ИмяФайла; * Если (ТаблицаМодулей[ИмяUsesФайла]!=0) * Если (ТаблицаМодулей[ИмяUsesФайла].Состояние==BeginCompilation) * Если (ТаблицаМодулей[ТаблицаМодулей[ИмяUsesФайла].ТекущийUsesМодуль].Состояние=BeginCompilation) * Ошибка("Циклическая связь модулей"); * CompileUnit(ТекущийМодуль.КомпилированыеВInterface,ИмяUsesФайла); * Если (ТекушийМодуль.Состояние==Compiled) то * СписокМодулей.Добавить(ТекушийМодуль); * Выход; * * 6.ТекущийМодуль.СемантическоеДерево=КонверторДерева.КонвертироватьInterfaceЧасть( * ТекущийМодуль.СинтаксическоеДерево, * ТекущийМодуль.КомпилированыеВInterface, * ТекущийМодуль.СписокОшибок); * СписокМодулей.Добавить(ТекущийМодуль); * СинтаксическийСписокМодулей=ТекущийМодуль.СинтаксическоеДерево.Implementation.usesList; * Для(i=СинтаксическийСписокМодулей.Количество-1;i>=0;i--) * Если (ТаблицаМодулей[СинтаксическийСписокМодулей[i].ИмяФайла].Состояние=BeginCompilation) * СписокОтложенойКомпиляции.Добавить(ТаблицаМодулей[СинтаксическийСписокМодулей[i].ИмяФайла]); * иначе * CompileUnit(ТекущийМодуль.КомпилированыеВImplementation,СинтаксическийСписокМодулей[i].ИмяФайла); * Если(ДобавлялиХотябыОдинВСписокОтложенойКомпиляции) * СписокОтложенойКомпиляции.Добавить(ТекущийМодуль); * выход; * иначе * КонверторДерева.КонвертироватьImplementationЧасть( * ТекущийМодуль.СинтаксическоеДерево, * ТекущийМодуль.СемантическоеДерево, * ТекущийМодуль.КомпилированыеВImplementation * ТекущийМодуль.СписокОшибок); * ТекущийМодуль.Состояние=Compiled; * СохранитьPCU(ТекущийМодуль); * * * * [краткая верcия алгоритма компиляции модулей] * CompileUnit(ИмяФайла) * 1.CompileUnit(new СписокМодулей,ИмяФайла) * 2.Докомпилировать модули из СписокОтложенойКомпиляции; * * CompileUnit(СписокМодулей,ИмяФайла); * 1.Если у этого модуля откомпилирован хотябы интерфейс то * добавить его в СписокМодулей * выход * 2.Если это DLL то * считать * добавить его в СписокМодулей * выход * 3.Если это PCU то * считать * добавить его в СписокМодулей * выход * 4.создать новый компилируемыйМодуль * РаспарситьТекст(ИмяФайла) * Состояние компилируемогоМодуля установить на BeginCompilation * 5.Для всех модулей из Interface части компилируемогоМодуля справа налево * Если мы уже начаинали компилировать этот модуль * Если состояние модуля BeginCompilation * Если состояние последнего компилируемого им модуля BeginCompilation * ошибка("Циклическая связь модулей") * выход * CompileUnit(Список из Interface части компилируемогоМодуля,модуль.имя) * Если компилируемыйМодуль.Состояние Compiled то * добавить его в СписокМодулей * выход * 6.Откомпилировать Interface часть компилируемогоМодуля * Для всех модулей из Implementation части компилируемогоМодуля справа налево * Если состояние очередного модуля BeginCompilation то * добавить его в список отложеной компиляции; * иначе * CompileUnit(Список из Implementation части компилируемогоМодуля,модуль.имя) * Если Добавляли Хотябы Один В Список Отложеной Компиляции то * добавить компилируемыйМодуль в список отложеной компиляции * выход * Откомпилировать Implementation часть компилируемогоМодуля * Состояние компилируемогоМодуля установить на Compiled * добавить его в СписокМодулей * Сохранить компилируемыйМодуль в виде PCU файла на диск * * * ***************************************************************************/ #endregion #define DEBUG using Languages.Facade; using PascalABCCompiler.Errors; using PascalABCCompiler.PCU; using PascalABCCompiler.SemanticTreeConverters; using PascalABCCompiler.SyntaxTreeConverters; using PascalABCCompiler.TreeRealization; using System; using System.CodeDom.Compiler; using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; namespace PascalABCCompiler { public enum UnitState { BeginCompilation, InterfaceCompiled, Compiled } public class CompilationUnit { internal bool CaseSensitive = false; /// /// поле для проверки на циклическую зависимость интерфейсов модулей /// public string currentUsedUnitId; public SyntaxError syntax_error; public string UnitFileName; public List ErrorList = new List(); public bool Documented; internal List possibleNamespaces = new List(); //internal List AssemblyReferences = new List(); //private SemanticTree.compilation_unitArrayList _interfaceUsedUnits=new SemanticTree.compilation_unitArrayList(); // название языка модуля public ILanguage Language { get; set; } /// /// Только "реальные" юниты (не dll и namespace) /// public Dictionary InterfaceUsedDirectUnits { get; } = new Dictionary(); public unit_node_list InterfaceUsedUnits { get; } = new unit_node_list(); /// /// Только "реальные" юниты (не dll и namespace) /// public Dictionary ImplementationUsedDirectUnits { get; } = new Dictionary(); public unit_node_list ImplementationUsedUnits { get; } = new unit_node_list(); public bool ForEachDirectCompilationUnit(Func on_unit) { foreach (var kvp in InterfaceUsedDirectUnits) if (!on_unit(kvp.Value, InterfaceUsedUnits.unit_uses_paths[kvp.Key])) return false; foreach (var kvp in ImplementationUsedDirectUnits) if (!on_unit(kvp.Value, ImplementationUsedUnits.unit_uses_paths[kvp.Key])) return false; return true; } private SyntaxTree.compilation_unit _syntaxTree = null; public SyntaxTree.compilation_unit SyntaxTree { get { return _syntaxTree; } set { _syntaxTree = value; } } private PascalABCCompiler.TreeRealization.unit_node _semanticTree = null; public PascalABCCompiler.TreeRealization.unit_node SemanticTree { get { return _semanticTree; } set { _semanticTree = value; } } /*private SyntaxTree.unit_or_namespace _syntaxUnitName = null; public SyntaxTree.unit_or_namespace SyntaxUnitName { get { return _syntaxUnitName; } set { _syntaxUnitName = value; } }*/ private TreeRealization.using_namespace_list _interface_using_namespace_list = new TreeRealization.using_namespace_list(); public TreeRealization.using_namespace_list InterfaceUsingNamespaceList { get { return _interface_using_namespace_list; } set { _interface_using_namespace_list = value; } } private TreeRealization.using_namespace_list _implementation_using_namespace_list = new TreeRealization.using_namespace_list(); public TreeRealization.using_namespace_list ImplementationUsingNamespaceList { get { return _implementation_using_namespace_list; } set { _implementation_using_namespace_list = value; } } /// /// Глобальные имена сущностей из зависимостей интерфейсной части /// Внешний словарь по имени модуля выдает словарь сущностей. /// Внутренний словарь по имени сущности выдает флаг о том, является ли она переменной. /// public Dictionary> NamesFromUsedUnits { get; } = new Dictionary>(); /// /// Объект, хранящий счетчики сгенерированных имен для текущего модуля (используется на семантике) /// public CoreUtils.GeneratedNamesManager GeneratedNamesManager { get; } = new CoreUtils.GeneratedNamesManager(); public UnitState State = UnitState.BeginCompilation; } public class CompilationUnitHashTable : Hashtable { public CompilationUnitHashTable() : base(StringComparer.InvariantCultureIgnoreCase) { } public CompilationUnit this[string key] { get { if (key != null) return (base[key] as CompilationUnit); return null; } set { base[key] = value; } } } /// /// Опции компиляции /// //[Serializable()] public class CompilerOptions : MarshalByRefObject { public enum OutputType { ClassLibrary = 0, ConsoleApplicaton = 1, WindowsApplication = 2, PascalCompiledUnit = 3, SemanticTree = 4 } public bool Debug = false; public bool ForDebugging = false; public bool ForIntellisense = false; public bool Rebuild = false; public bool Optimise = false; public bool DisableStandardUnits = false; // true устанавливается соответствующей директивой public bool SavePCUInThreadPull = false; public bool RunWithEnvironment = false; public string CompiledUnitExtension = StringConstants.pascalCompiledUnitExtension; public bool ProjectCompiled = false; public IProjectInfo CurrentProject = null; public OutputType OutputFileType = OutputType.ConsoleApplicaton; public bool GenerateCode = true; public bool SaveDocumentation = true; public bool SavePCU = true; public bool IgnoreRtlErrors = true; public bool Only32Bit = false; public string Locale = "ru"; public SyntaxTree.compilation_unit UnitSyntaxTree = null; private string sourceFileName = null; ///имя исходного файла ///при измененнии меняется OutputFileName,OutputDirectory,SourceFileDirectory public string SourceFileName { get { return sourceFileName; } set { sourceFileName = value; OutputFileName = value; sourceFileDirectory = Path.GetDirectoryName(value); if (sourceFileDirectory == "") sourceFileDirectory = Environment.CurrentDirectory; outputDirectory = SourceFileDirectory; useOutputDirectory = false; } } private string sourceFileDirectory = null; public string SourceFileDirectory { get { return sourceFileDirectory; } } private string outputFileName = null; // имя выходного файла без расширения // если имя указано без пути то в качестве пути используетя OutputDirectory public string OutputFileName { get { string FileName = outputFileName; switch (OutputFileType) { case OutputType.ConsoleApplicaton: case OutputType.WindowsApplication: FileName = FileName + ".exe"; break; case OutputType.ClassLibrary: FileName = FileName + ".dll"; break; case OutputType.PascalCompiledUnit: FileName = FileName + CompiledUnitExtension; break; } if (Path.GetDirectoryName(FileName) == "") FileName = Path.Combine(OutputDirectory, FileName); return FileName; } set { outputFileName = Path.GetFileNameWithoutExtension(value); } } public string SystemDirectory; public List ForceDefines = new List(); public List SearchDirectories; private bool useDllForSystemUnits = false; public bool UseDllForSystemUnits { get { return useDllForSystemUnits; } set { useDllForSystemUnits = value; NetHelper.NetHelper.UsePABCRtl = value; } } // LeftToAll - слева во все модули, RightToMain - справа, только в основную программу public enum StandardModuleAddMethod { LeftToAll, RightToMain }; [Serializable()] public class StandardModule : MarshalByRefObject { public string name; public StandardModuleAddMethod addMethod; public string languageToAdd; public StandardModule(string name, StandardModuleAddMethod addMethod = StandardModuleAddMethod.LeftToAll, string languageToAdd = StringConstants.pascalLanguageName) { this.name = name; this.languageToAdd = languageToAdd; this.addMethod = addMethod; } } private Dictionary> standardModules = new Dictionary>(); /// /// Списки стандартных модулей для поддерживаемых языков (первым в списке должен быть модуль "System") /// public Dictionary> StandardModules { get { if (standardModules.Count == 0) LoadStandardModules(); return standardModules; } set { standardModules = value; } } /// /// Заполняет словарь стандартных модулей для всех поддерживаемых языков /// private void LoadStandardModules() { foreach (ILanguage language in LanguageProvider.Instance.Languages) { standardModules[language.Name] = language.SystemUnitNames.Select(unitName => new StandardModule(unitName, StandardModuleAddMethod.LeftToAll, language.Name)).ToList(); } } public void RemoveStandardModule(string language, string name) { int moduleIndex = StandardModules[language].FindIndex(module => module.name == name); if (moduleIndex != -1) StandardModules[language].RemoveAt(moduleIndex); } public void RemoveStandardModuleAtIndex(string language, int index) { if (index < StandardModules[language].Count) StandardModules[language].RemoveAt(index); } internal string outputDirectory = null; internal bool useOutputDirectory = false; //для поиска pcu во вторую очередь //для сохранения сюда .exe, .dll и .pdb файлов public string OutputDirectory { get { return outputDirectory; } set { outputDirectory = value; useOutputDirectory = outputDirectory != null; } } //для поиска pcu в третью очередь исп. путь к pas файлу public Dictionary StandardDirectories; private void SetDirectories() { SystemDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().ManifestModule.FullyQualifiedName); StandardDirectories = new Dictionary(StringComparer.InvariantCultureIgnoreCase) { { "%PABCSYSTEM%", SystemDirectory } }; SearchDirectories = new List(); } public CompilerOptions() { SetDirectories(); } public CompilerOptions(string SourceFileName, OutputType OutputFileType) { SetDirectories(); this.SourceFileName = SourceFileName; this.OutputFileType = OutputFileType; } } public enum CompilerState { Ready, CompilationStarting, Reloading, ParserConnected, BeginCompileFile, BeginParsingFile, EndParsingFile, CompileInterface, CompileImplementation, EndCompileFile, ReadDLL, ReadPCUFile, SavePCUFile, CodeGeneration, CompilationFinished, PCUReadingError, PCUWritingError, SemanticTreeConverterConnected, SemanticTreeConversion, SyntaxTreeConversion } [Serializable()] public class CompilerInternalDebug { public bool CodeGeneration = true; public bool SemanticAnalysis = true; public bool PCUGenerate = true; public bool SkipPCUErrors = true; public bool IncludeDebugInfoInPCU = true; public bool AlwaysGenerateXMLDoc = false; public bool SkipInternalErrorsIfSyntaxTreeIsCorrupt = true; public bool UseStandarParserForIntellisense = true; public bool RunOnMono = false; #if DEBUG public bool DebugVersion { get { return true; } } #elif !DEBUG public bool DebugVersion { get { return false; } } #endif } public delegate void ChangeCompilerStateEventDelegate(ICompiler sender, CompilerState State, string FileName); public class Compiler : MarshalByRefObject, ICompiler { int pABCCodeHealth = 0; public int PABCCodeHealth { get { return pABCCodeHealth; } } public static string Version { get { return RevisionClass.FullVersion; } } public static string ShortVersion { get { if (RevisionClass.Build == "0") return RevisionClass.MainVersion; else return RevisionClass.MainVersion + "." + RevisionClass.Build; } } public static DateTime VersionDateTime { get { return File.GetLastWriteTime(System.Reflection.Assembly.GetExecutingAssembly().ManifestModule.FullyQualifiedName); } } public static string Banner { get { return "PascalABCCompiler.Core v" + Version; } } public override string ToString() { return Banner; } private SemanticTreeConvertersController semanticTreeConvertersController = null; public SemanticTreeConvertersController SemanticTreeConvertersController { get { return semanticTreeConvertersController; } } private Hashtable BadNodesInSyntaxTree = new Hashtable(); program_node semanticTree = null; public SemanticTree.IProgramNode SemanticTree { get { return semanticTree; } } public List CompiledVariables = new List(); private uint linesCompiled; public uint LinesCompiled { get { return linesCompiled; } } private CompilerInternalDebug internalDebug; public CompilerInternalDebug InternalDebug { get { return internalDebug; } set { internalDebug = value; } } private CompilerState state = CompilerState.Ready; public CompilerState State { get { return state; } } private CompilationUnitHashTable unitTable = new CompilationUnitHashTable(); public CompilationUnitHashTable UnitTable { get { return unitTable; } } public List UnitsTopologicallySortedList = new List(); private List StandardModules = new List(); public CompilerOptions CompilerOptions { get; set; } private Dictionary DLLCache = new Dictionary(); private LanguageProvider LanguageProvider => LanguageProvider.Instance; public TreeConverter.SyntaxTreeToSemanticTreeConverter SyntaxTreeToSemanticTreeConverter = null; public CodeGenerators.Controller CodeGeneratorsController = null; //public LLVMConverter.Controller LLVMCodeGeneratorsController = null; //public PascalToCppConverter.Controller PABCToCppCodeGeneratorsController = null; /// /// список отложенной компиляции реализации (она будет откомпилирована в Compile, а не в СompileUnit) /// private List UnitsToCompileDelayedList = new List(); public HashSet RecompileList = new HashSet(StringComparer.OrdinalIgnoreCase); private CompilationUnit currentCompilationUnit = null; private CompilationUnit firstCompilationUnit = null; private bool PCUReadersAndWritersClosed; private static Dictionary pcuCompilationUnits = new Dictionary(); private bool _clear_after_compilation = true; public bool ClearAfterCompilation { get { return _clear_after_compilation; } set { _clear_after_compilation = value; } } /// /// Начало основной программы /// public int beginOffset; public int BeginOffset { get { return beginOffset; } } /// /// Положение первых переменных в пространстве имен основной программы /// public int varBeginOffset; public int VarBeginOffset { get { return varBeginOffset; } } private List warnings = new List(); public List Warnings { get { return warnings; } } public Dictionary, Tuple> SourceFileNamesDictionary { get; } = new Dictionary, Tuple>(); public Dictionary, Tuple> PCUFileNamesDictionary { get; } = new Dictionary, Tuple>(); public Dictionary, List> GetUnitFileNameCache { get; } = new Dictionary, List>(); public void AddWarnings(List WarningList) { foreach (CompilerWarning cw in WarningList) warnings.Add(cw); } public event ChangeCompilerStateEventDelegate OnChangeCompilerState; private void ChangeCompilerStateEvent(ICompiler sender, CompilerState State, string FileName) { this.state = State; } private bool SourceFileExists(string FileName) { if (FileName == null) return false; return (bool)SourceFilesProvider(FileName, SourceFileOperation.Exists); } private DateTime SourceFileGetLastWriteTime(string FileName) { return (DateTime)SourceFilesProvider(FileName, SourceFileOperation.GetLastWriteTime); } private SourceFilesProviderDelegate sourceFilesProvider = SourceFilesProviders.DefaultSourceFilesProvider; public SourceFilesProviderDelegate SourceFilesProvider { get { return sourceFilesProvider; } set { sourceFilesProvider = value; } } private List errorsList = new List(); public List ErrorsList { get { return errorsList; } } public Compiler() { OnChangeCompilerState += ChangeCompilerStateEvent; Reload(); } public Compiler(ICompiler comp, SourceFilesProviderDelegate SourceFilesProvider, ChangeCompilerStateEventDelegate ChangeCompilerState) { internalDebug = comp.InternalDebug; OnChangeCompilerState += ChangeCompilerStateEvent; if (SourceFilesProvider != null) sourceFilesProvider = SourceFilesProvider; if (ChangeCompilerState != null) OnChangeCompilerState += ChangeCompilerState; // 29.07.2024 EVA CompilerOptions = new CompilerOptions(); } public Compiler(SourceFilesProviderDelegate SourceFilesProvider, ChangeCompilerStateEventDelegate ChangeCompilerState) { OnChangeCompilerState += ChangeCompilerStateEvent; if (SourceFilesProvider != null) sourceFilesProvider = SourceFilesProvider; if (ChangeCompilerState != null) OnChangeCompilerState += ChangeCompilerState; Reload(); } public void Reload() { OnChangeCompilerState(this, CompilerState.Reloading, null); pABCCodeHealth = 0; ClearAll(); errorsList.Clear(); Warnings.Clear(); InternalDebug = new CompilerInternalDebug(); // 29.07.2024 EVA CompilerOptions = new CompilerOptions(); SyntaxTreeToSemanticTreeConverter = new TreeConverter.SyntaxTreeToSemanticTreeConverter(); CodeGeneratorsController = new CodeGenerators.Controller(); semanticTreeConvertersController = new SemanticTreeConvertersController(this); semanticTreeConvertersController.ChangeState += semanticTreeConvertersController_ChangeState; semanticTreeConvertersController.AddConverters(); OnChangeCompilerState(this, CompilerState.Ready, null); } void semanticTreeConvertersController_ChangeState(SemanticTreeConvertersController.State State, ISemanticTreeConverter SemanticTreeConverter) { switch (State) { case SemanticTreeConvertersController.State.Convert: OnChangeCompilerState(this, CompilerState.SemanticTreeConversion, SemanticTreeConverter.Name); break; case SemanticTreeConvertersController.State.ConnectConverter: OnChangeCompilerState(this, CompilerState.SemanticTreeConverterConnected, SemanticTreeConverter.Name); break; } } #region COMPILER DIRECTIVES /// /// Формирует словарь директив компилятора, собирая их из всех переданных модулей /// /// /// /// private Dictionary> GetCompilerDirectives(List Units) { Dictionary> directives = new Dictionary>(StringComparer.CurrentCultureIgnoreCase); for (int i = 0; i < Units.Count; i++) { common_unit_node unitNode = Units[i].SemanticTree as common_unit_node; if (unitNode != null) { foreach (compiler_directive cd in unitNode.compiler_directives) { if (!directives.ContainsKey(cd.name)) directives.Add(cd.name, new List()); // TODO: сделать проверку на дубликаты централизованной (в другом месте) EVA else if (cd.name.Equals("mainresource", StringComparison.CurrentCultureIgnoreCase)) throw new DuplicateDirective(cd.location.file_name, "mainresource", cd.location); directives[cd.name].Insert(0, cd); } } } return directives; } /*private Hashtable GetCompilerDirectives(CompilationUnit Unit) { Hashtable Directives = new Hashtable(StringComparer.CurrentCultureIgnoreCase); TreeRealization.common_unit_node cun = Unit.SemanticTree as TreeRealization.common_unit_node; if (cun != null) foreach (TreeRealization.compiler_directive cd in cun.compiler_directives) Directives[cd.name] = cd; return Directives; }*/ /// /// преобразует в директивы семантического уровня | в syntax_tree_visitor такая же функция EVA /// private List GetDirectivesAsSemanticNodes(List compilerDirectives, string unitFileName) { List list = new List(); foreach (SyntaxTree.compiler_directive directive in compilerDirectives) { list.Add(new compiler_directive(directive.Name.text, directive.Directive?.text ?? "", GetLocationFromTreenode(directive, unitFileName), unitFileName)); } return list; } #endregion private TreeRealization.location GetLocationFromTreenode(SyntaxTree.syntax_tree_node tn, string FileName) { if (tn.source_context == null) { return null; } return new TreeRealization.location(tn.source_context.begin_position.line_num, tn.source_context.begin_position.column_num, tn.source_context.end_position.line_num, tn.source_context.end_position.column_num, FileName); } public void StartCompile() { System.Threading.Thread th = new System.Threading.Thread(() => Compile()); th.SetApartmentState(System.Threading.ApartmentState.STA); th.Start(); } public string Compile(CompilerOptions CompilerOptions) { this.CompilerOptions = CompilerOptions; return Compile(); } private void Reset() { SourceFileNamesDictionary.Clear(); PCUFileNamesDictionary.Clear(); GetUnitFileNameCache.Clear(); Warnings.Clear(); errorsList.Clear(); //if (!File.Exists(CompilerOptions.SourceFileName)) throw new SourceFileNotFound(CompilerOptions.SourceFileName); currentCompilationUnit = null; firstCompilationUnit = null; linesCompiled = 0; pABCCodeHealth = 0; PCUReadersAndWritersClosed = false; SyntaxTreeToSemanticTreeConverter.Reset(); CodeGeneratorsController.Reset(); //PABCToCppCodeGeneratorsController.Reset(); UnitsToCompileDelayedList.Clear(); DLLCache.Clear(); project = null; // обнуляем здесь, чтобы значение не сохранялось между запусками EVA CompilerOptions.DisableStandardUnits = false; } void CheckErrorsAndThrowTheFirstOne() { if (CompilerOptions.ForIntellisense) return; if (ErrorsList.Count > 0) throw ErrorsList[0]; } /*private void MoveSystemUnitForwardInUnitsTopologicallySortedList() { if (CompilerOptions.StandardModules.Count == 0) return; CompilationUnit systemUnit = null; foreach (CompilationUnit unit in UnitsTopologicallySortedList) { if (unit.SemanticTree == null || !(unit.SemanticTree is common_unit_node)) continue; string unitName = (unit.SemanticTree as common_unit_node).unit_name; // Пока что сделана проверка для всех языков | Вопрос EVA if (CompilerOptions.StandardModules.Select(kv => kv.Value[0].name).Contains(unitName)) { systemUnit = unit; break; } } if (systemUnit != null && systemUnit != UnitsTopologicallySortedList[0]) { UnitsTopologicallySortedList.Remove(systemUnit); UnitsTopologicallySortedList.Insert(0, systemUnit); } }*/ public string CompileCS() { OnChangeCompilerState(this, CompilerState.CompilationStarting, CompilerOptions.SourceFileName); OnChangeCompilerState(this, CompilerState.BeginCompileFile, CompilerOptions.SourceFileName); Reset(); var d = new Dictionary(); d["CompilerVersion"] = "v4.0"; var cscp = new Microsoft.CSharp.CSharpCodeProvider(d); //var cscp = new Microsoft.CSharp.CSharpCodeProvider(); var comp_opt = new CompilerParameters(); comp_opt.IncludeDebugInformation = CompilerOptions.Debug; comp_opt.GenerateExecutable = true; comp_opt.WarningLevel = 3; comp_opt.OutputAssembly = CompilerOptions.OutputFileName; //comp_opt.ReferencedAssemblies.Add() string source = GetSourceFileText(CompilerOptions.SourceFileName); using (StringReader sr = new StringReader(source)) { do { var s = sr.ReadLine(); if (s == null) break; if (s.ToLower().StartsWith("//#reference ")) { s = s.Remove(0, 13); s = s.Trim(); comp_opt.ReferencedAssemblies.Add(s); } else break; } while (true); } var res = cscp.CompileAssemblyFromSource(comp_opt, source); if (res.Errors.Count > 0) { for (int i = 0; i < res.Errors.Count; i++) { if (!res.Errors[i].IsWarning && errorsList.Count == 0 /*&& dlls.Errors[i].file_name != redirect_fname*/) { if (File.Exists(res.Errors[i].FileName)) errorsList.Add(new Errors.CommonCompilerError(res.Errors[i].ErrorText, res.Errors[i].FileName, res.Errors[i].Line != 0 ? res.Errors[i].Line : 1, res.Errors[i].Column != 0 ? res.Errors[i].Column : 1)); else errorsList.Add(new Errors.CommonCompilerError(res.Errors[i].ErrorText, CompilerOptions.SourceFileName, res.Errors[i].Line != 0 ? res.Errors[i].Line : 1, res.Errors[i].Column != 0 ? res.Errors[i].Column : 1)); } else if (res.Errors[i].IsWarning) { warnings.Add(new Errors.CommonWarning(res.Errors[i].ErrorText, res.Errors[i].FileName, res.Errors[i].Line, res.Errors[i].Column)); } } } linesCompiled = (uint)File.ReadAllLines(CompilerOptions.SourceFileName).Length; OnChangeCompilerState(this, CompilerState.CompilationFinished, CompilerOptions.SourceFileName); ClearAll(); OnChangeCompilerState(this, CompilerState.Ready, null); if (errorsList.Count > 0) return null; else return res.PathToAssembly; } private ProjectInfo project; private void InitializeProjectInfoAndFillCompilerOptionsFromIt() { project = new ProjectInfo(); project.Load(CompilerOptions.SourceFileName); //LoadProject(CompilerOptions.SourceFileName); switch (project.ProjectType) { case ProjectType.ConsoleApp: CompilerOptions.OutputFileType = CompilerOptions.OutputType.ConsoleApplicaton; break; case ProjectType.WindowsApp: CompilerOptions.OutputFileType = CompilerOptions.OutputType.WindowsApplication; break; case ProjectType.Library: CompilerOptions.OutputFileType = CompilerOptions.OutputType.ClassLibrary; break; } CompilerOptions.SourceFileName = project.main_file; CompilerOptions.Debug = project.include_debug_info; CompilerOptions.OutputFileName = project.output_file_name; CompilerOptions.OutputDirectory = project.output_directory; } public static bool CheckPathValid(string path) { return !Path.GetInvalidPathChars().Any(path.Contains); } public static void TryThrowInvalidPath(string path, SyntaxTree.SourceContext loc) { if (CheckPathValid(path)) return; throw new InvalidPathError(loc); } private void CompileUnitsFromDelayedList() { // проход по всем юнитам из списка отложенной компиляции foreach (CompilationUnit CurrentUnit in UnitsToCompileDelayedList) { if (CurrentUnit.State != UnitState.Compiled) { currentCompilationUnit = CurrentUnit; string unitFileName = currentCompilationUnit.SyntaxTree.file_name; // получение списка используемых модулей в файле (uses 1, 2, 3...) List implementationUsesList = GetImplementationUsesSection(CurrentUnit.SyntaxTree); CurrentUnit.possibleNamespaces.Clear(); if (HasIncludeNamespaceDirective(CurrentUnit)) CompilerOptions.UseDllForSystemUnits = false; if (implementationUsesList != null) { SetUseDLLForSystemUnits(Path.GetDirectoryName(unitFileName), implementationUsesList, implementationUsesList.Count - 1, CurrentUnit.Language); for (int i = implementationUsesList.Count - 1; i >= 0; i--) { if (!IsPossibleNetNamespaceOrStandardPasFile(implementationUsesList[i], true, Path.GetDirectoryName(unitFileName), CurrentUnit.Language)) { // докомпилируем юнит, если он не является пространством имен или стандартным pas файлом из Lib CompileUnit(CurrentUnit.ImplementationUsedUnits, CurrentUnit.ImplementationUsedDirectUnits, implementationUsesList[i], Path.GetDirectoryName(unitFileName), CurrentUnit.Language); } else { // добавление в списки только пространств имен CurrentUnit.ImplementationUsedUnits.AddElement(new namespace_unit_node(GetNamespace(implementationUsesList[i])), null); CurrentUnit.possibleNamespaces.Add(implementationUsesList[i]); } } } AddNamespacesToUsingList(CurrentUnit.ImplementationUsingNamespaceList, CurrentUnit.possibleNamespaces, true, null); // Console.WriteLine("Compiling implementation delayed " + unitFileName); CompileCurrentUnitImplementation(unitFileName, CurrentUnit, null); CurrentUnit.State = UnitState.Compiled; // отметка о скомпилированности OnChangeCompilerState(this, CompilerState.EndCompileFile, unitFileName); // состояние конец компиляции //SavePCU(compilationUnit, unitFileName); CurrentUnit.UnitFileName = unitFileName; } } } private void SetOutputFileTypeOption(Dictionary> compilerDirectives) { if (compilerDirectives.ContainsKey(StringConstants.compiler_directive_apptype)) { string outputFileType = compilerDirectives[StringConstants.compiler_directive_apptype][0].directive.ToLower(); switch (outputFileType) { case "console": CompilerOptions.OutputFileType = CompilerOptions.OutputType.ConsoleApplicaton; break; case "windows": CompilerOptions.OutputFileType = CompilerOptions.OutputType.WindowsApplication; break; case "dll": CompilerOptions.OutputFileType = CompilerOptions.OutputType.ClassLibrary; break; case "pcu": CompilerOptions.OutputFileType = CompilerOptions.OutputType.PascalCompiledUnit; break; default: ErrorsList.Add(new UnsupportedOutputFileType(outputFileType, compilerDirectives[StringConstants.compiler_directive_apptype][0].location)); break; } } // передача информации о типе выходного файла системному юниту if (UnitsTopologicallySortedList.Count > 0) { bool isConsoleApplication = CompilerOptions.OutputFileType == CompilerOptions.OutputType.ConsoleApplicaton; common_unit_node systemUnit = UnitsTopologicallySortedList[0].SemanticTree as common_unit_node; systemUnit.IsConsoleApplicationVariable = isConsoleApplication; } } private void SetOutputPlatformOption(NETGenerator.CompilerOptions netCompilerOptions, Dictionary> compilerDirectives) { List compilerDirectivesList = new List(); if (compilerDirectives.TryGetValue(StringConstants.compiler_directive_platformtarget, out compilerDirectivesList)) { string platformName = compilerDirectivesList[0].directive.ToLower(); switch (platformName) { case "x86": netCompilerOptions.platformtarget = NETGenerator.CompilerOptions.PlatformTarget.x86; break; case "x64": netCompilerOptions.platformtarget = NETGenerator.CompilerOptions.PlatformTarget.x64; break; case "anycpu": netCompilerOptions.platformtarget = NETGenerator.CompilerOptions.PlatformTarget.AnyCPU; break; case "dotnet5win": netCompilerOptions.platformtarget = NETGenerator.CompilerOptions.PlatformTarget.dotnet5win; break; case "dotnet5linux": netCompilerOptions.platformtarget = NETGenerator.CompilerOptions.PlatformTarget.dotnet5linux; break; case "dotnet5macos": netCompilerOptions.platformtarget = NETGenerator.CompilerOptions.PlatformTarget.dotnet5macos; break; case "native": if (Environment.OSVersion.Platform == PlatformID.Unix) { netCompilerOptions.platformtarget = NETGenerator.CompilerOptions.PlatformTarget.dotnetlinuxnative; } else if (Environment.OSVersion.Platform == PlatformID.MacOSX) { netCompilerOptions.platformtarget = NETGenerator.CompilerOptions.PlatformTarget.dotnetmacosnative; } else { netCompilerOptions.platformtarget = NETGenerator.CompilerOptions.PlatformTarget.dotnetwinnative; } break; default: ErrorsList.Add(new UnsupportedTargetPlatform(platformName, compilerDirectivesList[0].location)); break; } if (CompilerOptions.Only32Bit) netCompilerOptions.platformtarget = NETGenerator.CompilerOptions.PlatformTarget.x86; } if (compilerDirectives.TryGetValue(StringConstants.compiler_directive_targetframework, out compilerDirectivesList)) { netCompilerOptions.TargetFramework = compilerDirectivesList[0].directive; if (!(new string[] { "net40", "net403", "net45", "net451", "net452", "net46", "net461", "net462", "net47", "net471", "net472", "net48", "net481" }) .Contains(netCompilerOptions.TargetFramework)) { ErrorsList.Add(new UnsupportedTargetFramework(netCompilerOptions.TargetFramework, compilerDirectivesList[0].location)); } } } private void FillNetCompilerOptionsFromCompilerDirectives(NETGenerator.CompilerOptions netCompilerOptions, Dictionary> compilerDirectives) { List compilerDirectivesList; if (compilerDirectives.TryGetValue(StringConstants.compiler_directive_product_string, out compilerDirectivesList)) { netCompilerOptions.Product = compilerDirectivesList[0].directive; } if (compilerDirectives.TryGetValue(StringConstants.compiler_directive_version_string, out compilerDirectivesList)) { netCompilerOptions.ProductVersion = compilerDirectivesList[0].directive; } if (compilerDirectives.TryGetValue(StringConstants.compiler_directive_company_string, out compilerDirectivesList)) { netCompilerOptions.Company = compilerDirectivesList[0].directive; } if (compilerDirectives.TryGetValue(StringConstants.compiler_directive_trademark_string, out compilerDirectivesList)) { netCompilerOptions.TradeMark = compilerDirectivesList[0].directive; } if (compilerDirectives.TryGetValue(StringConstants.compiler_directive_copyright_string, out compilerDirectivesList)) { netCompilerOptions.Copyright = compilerDirectivesList[0].directive; } if (compilerDirectives.TryGetValue(StringConstants.compiler_directive_title_string, out compilerDirectivesList)) { netCompilerOptions.Title = compilerDirectivesList[0].directive; } if (compilerDirectives.TryGetValue(StringConstants.compiler_directive_description_string, out compilerDirectivesList)) { netCompilerOptions.Description = compilerDirectivesList[0].directive; } if (compilerDirectives.TryGetValue(StringConstants.compiler_directive_main_resource_string, out compilerDirectivesList)) { if (compilerDirectives.ContainsKey(StringConstants.compiler_directive_product_string) || compilerDirectives.ContainsKey(StringConstants.compiler_directive_version_string) || compilerDirectives.ContainsKey(StringConstants.compiler_directive_company_string) || compilerDirectives.ContainsKey(StringConstants.compiler_directive_trademark_string) || compilerDirectives.ContainsKey(StringConstants.compiler_directive_title_string) || compilerDirectives.ContainsKey(StringConstants.compiler_directive_description_string) || compilerDirectives.ContainsKey(StringConstants.compiler_directive_copyright_string)) { ErrorsList.Add(new MainResourceNotAllowed(compilerDirectivesList[0].location.file_name, compilerDirectivesList[0].location)); } TryThrowInvalidPath(compilerDirectivesList[0].directive, compilerDirectivesList[0].location); // Тут не обязательно нормализовывать путь // И если он слишком длинный - File.Exists вернёт false netCompilerOptions.MainResourceFileName = Path.Combine(Path.GetDirectoryName(compilerDirectivesList[0].source_file), compilerDirectivesList[0].directive); if (!File.Exists(netCompilerOptions.MainResourceFileName)) ErrorsList.Add(new ResourceFileNotFound(compilerDirectivesList[0].location.file_name, compilerDirectivesList[0].directive, compilerDirectivesList[0].location)); } } private void FillNetCompilerOptionsFromProject(NETGenerator.CompilerOptions netCompilerOptions) { if (project != null) { if (!(project.major_version == 0 && project.minor_version == 0 && project.build_version == 0 && project.revision_version == 0)) netCompilerOptions.ProductVersion = project.major_version + "." + project.minor_version + "." + project.build_version + "." + project.revision_version; if (!string.IsNullOrEmpty(project.product)) netCompilerOptions.Product = project.product; if (!string.IsNullOrEmpty(project.company)) netCompilerOptions.Company = project.company; if (!string.IsNullOrEmpty(project.trademark)) netCompilerOptions.TradeMark = project.trademark; if (!string.IsNullOrEmpty(project.copyright)) netCompilerOptions.Copyright = project.copyright; if (!string.IsNullOrEmpty(project.title)) netCompilerOptions.Title = project.title; if (!string.IsNullOrEmpty(project.description)) netCompilerOptions.Description = project.description; if (project.ProjectType == ProjectType.WindowsApp) netCompilerOptions.target = NETGenerator.TargetType.WinExe; // при использовании учесть удаление res_file из этой функции при кодогенерации EVA // CreateRCFile(compilerOptions); } } /*private void CreateRCFile(NETGenerator.CompilerOptions compilerOptions) { if (!string.IsNullOrEmpty(project.app_icon)) { //cdo.MainResourceFileName = project.app_icon; string rc_file = Path.GetFileNameWithoutExtension(project.app_icon) + ".rc"; StreamWriter sw = File.CreateText(rc_file); sw.WriteLine("1 ICON \"" + project.app_icon.Replace("\\", "\\\\") + "\""); if (compilerOptions.NeedDefineVersionInfo) { compilerOptions.NeedDefineVersionInfo = false; sw.WriteLine("1 VERSIONINFO"); string ver = project.major_version + "," + project.minor_version + "," + project.build_version + "," + project.revision_version; sw.WriteLine("FILEVERSION " + ver); // это было закомментировано ***** EVA sw.WriteLine("FILEFLAGSMASK VS_FFI_FILEFLAGSMASK"); sw.WriteLine("FILEFLAGS VER_DEBUG"); sw.WriteLine("FILEOS VOS__WINDOWS32"); if (project.project_type != ProjectType.Library) sw.WriteLine("FILETYPE VFT_APP"); else sw.WriteLine("FILETYPE VFT_DLL"); sw.WriteLine("FILESUBTYPE VFT2_UNKNOWN"); // до сюда ******* sw.WriteLine("BEGIN \r\n BLOCK \"StringFileInfo\"\r\n BEGIN \r\n BLOCK \"041904E3\"\r\nBEGIN"); sw.WriteLine("VALUE \"ProductName\"," + "\"" + compilerOptions.Product + "\""); sw.WriteLine("VALUE \"FileVersion\"," + "\"" + ver + "\""); sw.WriteLine("VALUE \"ProductVersion\"," + "\"" + ver + "\""); sw.WriteLine("VALUE \"FileDescription\"," + "\"" + compilerOptions.Description + "\""); sw.WriteLine("VALUE \"OriginalFileName\"," + "\"" + Path.GetFileName(CompilerOptions.OutputFileName) + "\""); sw.WriteLine("VALUE \"InternalName\"," + "\"" + Path.GetFileNameWithoutExtension(CompilerOptions.OutputFileName) + "\""); sw.WriteLine("VALUE \"CompanyName\"," + "\"" + compilerOptions.Company + "\""); sw.WriteLine("VALUE \"LegalTrademarks1\"," + "\"" + compilerOptions.TradeMark + "\""); sw.WriteLine("VALUE \"LegalCopyright\"," + "\"" + compilerOptions.Copyright + "\""); sw.WriteLine("END"); sw.WriteLine("END"); sw.WriteLine("BLOCK \"VarFileInfo\"\r\nBEGIN"); sw.WriteLine("VALUE \"Translation\", 0x0419, 1251"); sw.WriteLine("END"); sw.WriteLine("END"); } sw.Close(); System.Diagnostics.Process prc = new System.Diagnostics.Process(); prc.StartInfo.FileName = Path.Combine(this.CompilerOptions.SystemDirectory, "rc.exe"); prc.StartInfo.Arguments = Path.Combine(Path.GetDirectoryName(project.app_icon), Path.GetFileNameWithoutExtension(project.app_icon) + ".rc"); prc.StartInfo.CreateNoWindow = true; prc.StartInfo.UseShellExecute = false; prc.StartInfo.RedirectStandardOutput = true; prc.StartInfo.RedirectStandardError = true; prc.Start(); prc.WaitForExit(); string res_file = Path.Combine(Path.GetDirectoryName(project.app_icon), Path.GetFileNameWithoutExtension(project.app_icon) + ".res"); if (File.Exists(res_file)) { compilerOptions.MainResourceFileName = res_file; // !!! главный ресурсный файл } File.Delete(rc_file); } }*/ private void SetTargetTypeOption(NETGenerator.CompilerOptions netCompilerOptions) { netCompilerOptions.ForRunningWithEnvironment = CompilerOptions.RunWithEnvironment; // тип выходного файла if (netCompilerOptions.target == NETGenerator.TargetType.Exe) // если еще не установлен согласно проекту { switch (CompilerOptions.OutputFileType) { case CompilerOptions.OutputType.ClassLibrary: netCompilerOptions.target = NETGenerator.TargetType.Dll; break; case CompilerOptions.OutputType.ConsoleApplicaton: netCompilerOptions.target = NETGenerator.TargetType.Exe; break; case CompilerOptions.OutputType.WindowsApplication: netCompilerOptions.target = NETGenerator.TargetType.WinExe; break; } } // Debug / Release netCompilerOptions.dbg_attrs = CompilerOptions.Debug ? NETGenerator.DebugAttributes.Debug : NETGenerator.DebugAttributes.Release; if (CompilerOptions.ForDebugging) netCompilerOptions.dbg_attrs = NETGenerator.DebugAttributes.ForDebugging; } public string Compile() { try { // компиляция C# if (Path.GetExtension(CompilerOptions.SourceFileName) == ".cs") { return CompileCS(); } // вызов события смены состояния компилятора - начало компиляции // информация о состояниях выводится в сообщениях компилятора OnChangeCompilerState(this, CompilerState.CompilationStarting, CompilerOptions.SourceFileName); // очистка всех переменных и списков, используемых в процессе Reset(); // если проект скомпилирован, то заполнение информации о проекте в опциях компилятора if (CompilerOptions.ProjectCompiled) { InitializeProjectInfoAndFillCompilerOptionsFromIt(); } #region CONSTRUCTING SYNTAX AND SEMANTIC TREES // компиляция всех юнитов произойдет рекурсивно (кроме отложенных) CompileUnit( new unit_node_list(), new Dictionary(), new SyntaxTree.uses_unit_in(null, new SyntaxTree.string_const(Path.GetFullPath(CompilerOptions.SourceFileName))), null); // компиляция юнитов из списка отложенной компиляции, если он не пуст CompileUnitsFromDelayedList(); #endregion // Закрытие чтения и записи .pcu файлов ClosePCUReadersAndWriters(); PrebuildMainSemanticTreeActions(out var netCompilerOptions, out var resourceFiles); #region GENERATING CODE if (ErrorsList.Count == 0) { //TODO: Разобратся c location для program_node и правильно передавать main_function. Добавить генератор main_function в SyntaxTreeToSemanticTreeConverter. | Отложено на потом EVA // получние полного семантического дерева, включающего все зависимости program_node semanticTree = ConstructMainSemanticTree(netCompilerOptions); if (firstCompilationUnit.SyntaxTree is SyntaxTree.unit_module && CompilerOptions.OutputFileType != CompilerOptions.OutputType.ClassLibrary) { // если мы комилируем PCU CompilerOptions.OutputFileType = CompilerOptions.OutputType.PascalCompiledUnit; } // генерация IL кода else if (CompilerOptions.GenerateCode) { if (CompilerOptions.UseDllForSystemUnits) netCompilerOptions.RtlPABCSystemType = NetHelper.NetHelper.FindRtlType("PABCSystem.PABCSystem"); GenerateILCode(semanticTree, netCompilerOptions, resourceFiles); } } #endregion } // TODO: просмотреть возможные ParserError EVA catch (TreeConverter.ParserError) { // конвертор уткнулся в ошибку. ничего не делаем } catch (CompilerInternalError err) { AddInternalErrorToErrorList(err); } catch (Error err) { // здесь учитывается позиция Located error AddErrorToErrorListConsideringPosition(err); } catch (Exception err) { // здесь добавляются только ошибки генерации кода AddCodeGenerationErrorToErrorList(err); } // на случай если мы вывалились по исключению, но у нас есть откомпилированные модули try { ClosePCUReadersAndWriters(); } catch (Exception e) { ErrorsList.Add(new CompilerInternalError("Compiler.ClosePCUReadersAndWriters", e)); } // если есть семантические ошибки в RTL, то очистить ошибки и повторно перекомпилировать без RTL bool recompilationNeeded = CheckForRTLErrorsAndClearAllErrorsIfFound(); OnChangeCompilerState(this, CompilerState.CompilationFinished, CompilerOptions.SourceFileName); // compilation finished state if (ClearAfterCompilation) ClearAll(); if (!recompilationNeeded) OnChangeCompilerState(this, CompilerState.Ready, null); // компилятор окончательно завершил работу if (ErrorsList.Count > 0) { return null; } else if (recompilationNeeded) { //Compiler c = new Compiler(sourceFilesProvider,OnChangeCompilerState); //return c.Compile(this.compilerOptions); return Compile(); } else return CompilerOptions.OutputFileName; } /// /// Сохраняет документацию для модулей; /// Выясняет тип выходного файла, целевой фреймворк, платформу; /// Заполняет опции .NET компиляции согласно директивам и/или информации из проекта; /// Находит ресурсные файлы из директив /// private void PrebuildMainSemanticTreeActions(out NETGenerator.CompilerOptions netCompilerOptions, out List resourceFiles) { if (CompilerOptions.SaveDocumentation) { SaveDocumentationsForUnits(); } Dictionary> compilerDirectives = GetCompilerDirectives(UnitsTopologicallySortedList); // выяснение типа выходного файла по соотв. директиве компилятора SetOutputFileTypeOption(compilerDirectives); // перемещаем PABCSystem в начало списка // MoveSystemUnitForwardInUnitsTopologicallySortedList(); netCompilerOptions = new NETGenerator.CompilerOptions(); // выяснение TargetFramework и целевой платформы SetOutputPlatformOption(netCompilerOptions, compilerDirectives); // заполнение опций компилятора из директив FillNetCompilerOptionsFromCompilerDirectives(netCompilerOptions, compilerDirectives); // получние путей к файлам ресурсов из директив resourceFiles = GetResourceFilesFromCompilerDirectives(compilerDirectives); // заполнение опций компилятора из заголовка проекта FillNetCompilerOptionsFromProject(netCompilerOptions); // Устанавливает опции компилятора, связанные с типом выходного файла SetTargetTypeOption(netCompilerOptions); } private program_node ConstructMainSemanticTree(NETGenerator.CompilerOptions compilerOptions) { program_node mainSemanticTree = new program_node(null, null); for (int i = 0; i < UnitsTopologicallySortedList.Count; i++) mainSemanticTree.units.AddElement(UnitsTopologicallySortedList[i].SemanticTree as common_unit_node); bool targetTypeIsExe = compilerOptions.target == NETGenerator.TargetType.Exe || compilerOptions.target == NETGenerator.TargetType.WinExe; // если компилируем exe или WinExe (первый модуль - основная программа) if (firstCompilationUnit.SyntaxTree is SyntaxTree.program_module && targetTypeIsExe && UnitsTopologicallySortedList.Count > 0) { mainSemanticTree.main_function = ((common_unit_node)UnitsTopologicallySortedList.Last().SemanticTree).main_function; PrepareFinalMainFunctionForExe(mainSemanticTree); } // если мы компилируем dll else if (firstCompilationUnit.SyntaxTree is SyntaxTree.unit_module) { // TODO: посмотреть инициализирующий код .dll EVA mainSemanticTree.create_main_function_as_in_module(); } if (CompilerOptions.GenerateCode) // семантические преобразования с оптимизацией кода mainSemanticTree = semanticTreeConvertersController.Convert(mainSemanticTree) as program_node; semanticTree = mainSemanticTree; return mainSemanticTree; } public void PrepareFinalMainFunctionForExe(program_node mainSemanticTree) { // вычисляем номер строку первой переменной и строки с началом основной программы if (mainSemanticTree.main_function.function_code.location != null) { common_namespace_node main_ns = mainSemanticTree.main_function.namespace_node; foreach (namespace_variable variable in main_ns.variables) { if (variable.inital_value?.location != null && !(variable.inital_value is constant_node) && !(variable.inital_value is record_initializer) && !(variable.inital_value is array_initializer)) { varBeginOffset = variable.inital_value.location.begin_line_num; break; } } beginOffset = mainSemanticTree.main_function.function_code.location.begin_line_num; } // локализация Dictionary config_dict = new Dictionary(); if (CompilerOptions.Locale != null && StringResourcesLanguage.GetLCIDByTwoLetterISO(CompilerOptions.Locale) != null) { config_dict["locale"] = CompilerOptions.Locale; config_dict["full_locale"] = StringResourcesLanguage.GetLCIDByTwoLetterISO(CompilerOptions.Locale); } mainSemanticTree.create_main_function(StandardModules.ToArray(), config_dict); } private void AddErrorToErrorListConsideringPosition(Error err) { if (ErrorsList.Count == 0) ErrorsList.Add(err); else if (err != ErrorsList[0]) { if (err is SemanticError) { int position = FindPositionForSemanticErrorInTheErrorList(err); // семантические ошибки отсортированы по location ErrorsList.Insert(position, err); } else ErrorsList.Add(err); } } private int FindPositionForSemanticErrorInTheErrorList(Error err) { int position = ErrorsList.Count; SourceLocation location = (err as SemanticError).SourceLocation; SourceLocation locationTemp; if (location != null) { for (int i = 0; i < ErrorsList.Count; i++) { if ((locationTemp = (ErrorsList[i] as LocatedError)?.SourceLocation) != null) { if (locationTemp > location) { position = i; break; } } } } return position; } private void AddCodeGenerationErrorToErrorList(Exception err) { string fileName = Path.GetFileName(currentCompilationUnit?.SyntaxTree?.file_name) ?? "Compiler"; CompilerInternalError compilationError = new CompilerInternalError(string.Format("Compiler.Compile[{0}]", fileName), err); AddInternalErrorToErrorList(compilationError); } private void AddInternalErrorToErrorList(CompilerInternalError internalError) { if (ErrorsList.Count == 0) ErrorsList.Add(internalError); else { #if DEBUG if (!InternalDebug.SkipInternalErrorsIfSyntaxTreeIsCorrupt) ErrorsList.Add(internalError); #endif } } private bool CheckForRTLErrorsAndClearAllErrorsIfFound() { bool anyRTLErrors = false; if (ErrorsList.Count > 0) { if (CompilerOptions.UseDllForSystemUnits && !HasOnlySyntaxErrors(ErrorsList) && CompilerOptions.IgnoreRtlErrors) { CompilerOptions.UseDllForSystemUnits = false; ErrorsList.Clear(); anyRTLErrors = true; } } return anyRTLErrors; } private void GenerateILCode(program_node programNode, NETGenerator.CompilerOptions compilerOptions, List resourceFiles) { if (CompilerOptions.OutputFileType != CompilerOptions.OutputType.SemanticTree) #if DEBUG if (InternalDebug.CodeGeneration) #endif { // генерация файла .pdb для дебага DebugOutputFileCreationUsingPDB(); OnChangeCompilerState(this, CompilerState.CodeGeneration, CompilerOptions.OutputFileName); // состояние генерации кода // трансляция в IL-код | В semanticTree находится ЕДИНСТВЕННОЕ семантическое дерево, содержащее программу и все семантические модули CodeGeneratorsController.GenerateILCodeAndSaveAssembly(programNode, CompilerOptions.OutputFileName, CompilerOptions.SourceFileName, compilerOptions, CompilerOptions.StandardDirectories, resourceFiles?.ToArray()); CodeGeneratorsController.EmitAssemblyRedirects( assemblyResolveScope, CompilerOptions.OutputFileName); } } private void DebugOutputFileCreationUsingPDB() { int n = 1; try { n = 2; var fs = File.Create(CompilerOptions.OutputFileName); n = 3; fs.Close(); n = 4; ///////File.Delete(CompilerOptions.OutputFileName); string pdb_file_name = Path.ChangeExtension(CompilerOptions.OutputFileName, ".pdb"); if (File.Exists(pdb_file_name)) File.Delete(pdb_file_name); n = 5; // PVS 01/2022 } catch (Exception e) { throw new UnauthorizedAccessToFile(CompilerOptions.OutputFileName + " -- " + n + " " + e.ToString()); //throw e; } } private List GetResourceFilesFromCompilerDirectives(Dictionary> compilerDirectives) { List ResourceFiles = null; if (compilerDirectives.ContainsKey(StringConstants.compiler_directive_resource)) { ResourceFiles = new List(); List ResourceDirectives = compilerDirectives[StringConstants.compiler_directive_resource]; foreach (compiler_directive cd in ResourceDirectives) { TryThrowInvalidPath(cd.directive, cd.location); var resourceFileName = Path.Combine(Path.GetDirectoryName(cd.source_file), cd.directive); // Так же как с main_resource if (File.Exists(resourceFileName)) ResourceFiles.Add(resourceFileName); else ErrorsList.Add(new ResourceFileNotFound(cd.location.file_name, cd.directive, cd.location)); } } return ResourceFiles; } private bool HasOnlySyntaxErrors(List errors) { foreach (Error err in errors) { if (!(err is SyntaxError)) return false; } return true; } private void SaveDocumentationsForUnits() { DocXmlManager dxm = new DocXmlManager(); foreach (CompilationUnit cu in UnitsTopologicallySortedList) { if (cu.Documented) dxm.SaveXml(cu); } } private void ClosePCUReadersAndWriters() { if (PCUReadersAndWritersClosed) return; PCUReadersAndWritersClosed = true; bool all_restored = false; while (!all_restored) { foreach (PCUReader p in PCUReader.AllReaders) { p.AddInitFinalMethods(); SystemLibrary.SystemLibInitializer.RestoreStandardFunctions(); p.ProcessWaitedToRestoreFields(); p.RestoreWaitedMethodCodes(); } bool rest = true; for (int i = 0; i < PCUReader.AllReaders.Count; i++) if (PCUReader.AllReaders[i].waited_types_to_restore_fields.Count != 0) { rest = false; break; } all_restored = rest; } PCUReader.AddUsedMembersInAllUnits(); if (CompilerOptions.SavePCUInThreadPull) AsyncClosePCUWriters(); else ClosePCUWriters(); } private void WaitCallback_ClosePCUWriters(object state) { ClosePCUWriters(); } private void AsyncClosePCUWriters() { System.Threading.ThreadPool.QueueUserWorkItem(WaitCallback_ClosePCUWriters); } private void ClosePCUWriters() { foreach (CompilationUnit cu in UnitsTopologicallySortedList) { SavePCU(cu); } foreach (PCUWriter pw in PCUWriter.AllWriters) { pw.CloseWriter(); } } /// /// Бросает ошибку если находит дупликаты в секции uses /// void CheckForDuplicatesInUsesSection(List usesList) { if (usesList == null) return; List names = new List(); foreach (SyntaxTree.unit_or_namespace un in usesList) { string name = SyntaxTree.Utils.IdentListToString(un.name.idents, ".").ToLower(); if (un.source_context != null) { if (names.Contains(name)) throw new DuplicateUsesUnit(currentCompilationUnit.SyntaxTree.file_name, name, un.source_context); else names.Add(name); } } } /// /// Возвращает список зависимостей из интерфейсной части модуля (или основной программы) /// public List GetInterfaceUsesSection(SyntaxTree.compilation_unit unitSyntaxTree) { List usesList = null; if (unitSyntaxTree is SyntaxTree.unit_module unitModule) { if (unitModule.interface_part.uses_modules == null) { if (CompilerOptions.StandardModules[currentCompilationUnit.Language.Name].Count > 0) { unitModule.interface_part.uses_modules = new SyntaxTree.uses_list(); unitModule.interface_part.uses_modules.source_context = new SyntaxTree.SourceContext(); } else return null; } usesList = unitModule.interface_part.uses_modules.units; } else if (unitSyntaxTree is SyntaxTree.program_module programModule) { if (programModule.used_units == null) { if (CompilerOptions.StandardModules[currentCompilationUnit.Language.Name].Count > 0) { programModule.used_units = new SyntaxTree.uses_list(); programModule.used_units.source_context = new SyntaxTree.SourceContext(); } else return null; } usesList = programModule.used_units.units; } CheckForDuplicatesInUsesSection(usesList); return usesList; } private List GetImplementationUsesSection(SyntaxTree.compilation_unit unitSyntaxTree) { List usesSection = (unitSyntaxTree as SyntaxTree.unit_module)?.implementation_part?.uses_modules?.units; CheckForDuplicatesInUsesSection(usesSection); return usesSection; } public string FindPCUFileName(string fileName, string currentPath, out int folderPriority, ILanguage currentUnitLanguage) { if (string.IsNullOrEmpty(Path.GetExtension(fileName))) fileName += CompilerOptions.CompiledUnitExtension; var cacheKey = Tuple.Create(fileName.ToLower(), currentPath?.ToLower()); if (!PCUFileNamesDictionary.TryGetValue(cacheKey, out var fileNameWithPriority)) { if (Path.GetExtension(fileName) != CompilerOptions.CompiledUnitExtension) fileNameWithPriority = null; else if (FindFileWithExtensionInDirs(fileName, out _, currentUnitLanguage, currentPath) is string resultFileName1) fileNameWithPriority = Tuple.Create(resultFileName1, 1); else if (CompilerOptions.OutputDirectory != CompilerOptions.SourceFileDirectory && FindFileWithExtensionInDirs(Path.GetFileName(fileName), out _, currentUnitLanguage, CompilerOptions.OutputDirectory) is string resultFileName2) fileNameWithPriority = Tuple.Create(resultFileName2, 2); else if (FindFileWithExtensionInDirs( fileName, out var dirIndex, currentUnitLanguage, GetCurrentSearchDirectories(currentUnitLanguage)) is string resultFileName3) fileNameWithPriority = Tuple.Create(resultFileName3, 3 + dirIndex); else fileNameWithPriority = null; PCUFileNamesDictionary[cacheKey] = fileNameWithPriority; } folderPriority = fileNameWithPriority?.Item2 ?? 0; return fileNameWithPriority?.Item1; } public string[] GetCurrentSearchDirectories(ILanguage currentUnitLanguage) { var libDirs = LanguageProvider.Languages .Where(lang => lang == currentUnitLanguage) .Concat(LanguageProvider.Languages.Where(lang => lang != currentUnitLanguage)) .Select(lang => Path.Combine(CompilerOptions.SystemDirectory, "Lib", lang.Name.Replace(StringConstants.pascalLanguageName, ""))) // для PascalABC.NET прямо в Lib, остальные во внутренних папках .Where(dir => Directory.Exists(dir)); return CompilerOptions.SearchDirectories.Concat(libDirs).ToArray(); } public string FindSourceFileName(string fileName, string currentPath, out int folderPriority, ILanguage currentUnitLanguage) { var cacheKey = Tuple.Create(fileName.ToLower(), currentPath?.ToLower()); if (!SourceFileNamesDictionary.TryGetValue(cacheKey, out var fileNameWithPriority)) { if (FindSourceFileNameInDirs(fileName, out _, currentUnitLanguage, currentPath) is string resultFileName1) fileNameWithPriority = Tuple.Create(resultFileName1, 1); else if (FindSourceFileNameInDirs(fileName, out var dirIndex, currentUnitLanguage, GetCurrentSearchDirectories(currentUnitLanguage)) is string resultFileName2) fileNameWithPriority = Tuple.Create(resultFileName2, 3 + dirIndex); else fileNameWithPriority = null; SourceFileNamesDictionary[cacheKey] = fileNameWithPriority; } folderPriority = fileNameWithPriority?.Item2 ?? 0; return fileNameWithPriority?.Item1; } public string FindSourceFileNameInDirs(string fileName, out int foundDirIndex, ILanguage currentUnitLanguage, params string[] Dirs) { var fileNameExtension = Path.GetExtension(fileName); var isExtensionEmpty = string.IsNullOrEmpty(fileNameExtension); // Ищем сначала по расширениям текущего языка, потом по всем остальным foreach (ILanguage lang in new ILanguage[] { currentUnitLanguage }.Concat(LanguageProvider.Languages.Where(l => l != currentUnitLanguage))) { foreach (string extension in lang.FilesExtensions) { if (isExtensionEmpty || fileNameExtension == extension) { var resultFileName = FindFileWithExtensionInDirs(isExtensionEmpty ? fileName + extension : fileName, out foundDirIndex, currentUnitLanguage, Dirs); if (resultFileName != null) return resultFileName; } } } foundDirIndex = 0; return null; } public static string CombinePathsRelatively(string path1, string path2) { if (Path.IsPathRooted(path2)) return path2; int i = 0; foreach (var s in path2.Split(new[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar })) { if (s == ".") continue; if (s == ".." && !string.IsNullOrWhiteSpace(path1) && !path1.EndsWith("..")) { path1 = Path.GetDirectoryName(path1); if (path1 == null) return null; // Path.GetDirectoryName("C:\") возвращает null } else path1 = Path.Combine(path1, s); } return path1; } public static string GetUnitPath(CompilationUnit u1, CompilationUnit u2) { if (u1 == null) throw new ArgumentNullException(nameof(u1)); if (u2 == null) throw new ArgumentNullException(nameof(u2)); var curr = new Dictionary(); var done = new HashSet(); var res_path = default(string); Func register_unit = (CompilationUnit u, string path) => { if (!done.Add(u)) return true; if (u == u2) { res_path = path; return false; } curr.Add(u, path); return true; }; if (!u1.ForEachDirectCompilationUnit(register_unit)) return res_path; while (curr.Count != 0) { var prev = curr; curr = new Dictionary(); foreach (var kvp in prev) if (!kvp.Key.ForEachDirectCompilationUnit((u, path) => { // Важно "..\a\b" + "..\c\d" превращать в "..\a\c\d", а не полный путь return register_unit(u, CombinePathsRelatively(Path.GetDirectoryName(kvp.Value), path)); })) return res_path; } throw new InvalidOperationException($"Could not find path to \"{u2.UnitFileName}\" relative to \"{u1.UnitFileName}\""); } private string FindFileWithExtensionInDirs(string fileName, out int foundDirIndex, ILanguage currentUnitLanguage, params string[] dirs) { if (Path.IsPathRooted(fileName)) { foundDirIndex = 0; if (currentUnitLanguage.CaseSensitive) { var foundFileName = Directory.GetFiles(Path.GetDirectoryName(fileName), Path.GetFileName(fileName), SearchOption.TopDirectoryOnly) .FirstOrDefault(f => f == fileName); return foundFileName; } return File.Exists(fileName) ? fileName : null; } for (int dirIndex = 0; dirIndex < dirs.Length; ++dirIndex) { try { var dir = dirs[dirIndex]; var fullFileName = Path.Combine(dir, fileName); if (currentUnitLanguage.CaseSensitive) { var foundFileName = Directory.GetFiles(dir, fileName, SearchOption.TopDirectoryOnly) .FirstOrDefault(f => f == fullFileName); if (foundFileName != null) { foundDirIndex = dirIndex; return Path.GetFullPath(foundFileName); } } else if (File.Exists(fullFileName)) { foundDirIndex = dirIndex; // Path.GetFullPath чтобы нормализовать // File.Exists не может кинуть исключение или дать true // если путь слишком длинный или содержит неправильные знаки return Path.GetFullPath(fullFileName); } } catch (PathTooLongException) { } } foundDirIndex = 0; return null; } public static string GetReferenceFileName(string FileName, string curr_path = null) { // Вначале - кешированные стандартные dll if (standart_assembly_dict.ContainsKey(FileName)) return standart_assembly_dict[FileName]; if (curr_path != null && System.IO.File.Exists(Path.Combine(curr_path, FileName))) return Path.Combine(curr_path, FileName); if (System.IO.File.Exists(FileName)) { return FileName;//.ToLower();//? а надо ли tolover? } else { return get_assembly_path(FileName, false); } } private string GetReferenceFileName(string FileName, SyntaxTree.SourceContext sc, string curr_path, bool overwrite) { FileName = FileName.Trim(); if (standart_assembly_dict.ContainsKey(FileName)) return standart_assembly_dict[FileName]; // Наверное, этот код MikhailoMMX лишний //MikhailoMMX PABCRtl.dll будем искать сначала в GAC, а потом в папке с программой if (FileName == StringConstants.pabc_rtl_dll_name) { string name = get_assembly_path(FileName, true); if ((name != null) && (File.Exists(name))) return name; } //\MikhailoMMX try { var FullFileName = Path.Combine(curr_path, FileName); if (File.Exists(FullFileName)) { var NewFileName = Path.GetFullPath(Path.Combine(CompilerOptions.OutputDirectory, Path.GetFileName(FullFileName))); if (FullFileName != NewFileName) { if (overwrite) File.Copy(FullFileName, NewFileName, true); else if (!File.Exists(NewFileName)) File.Copy(FullFileName, NewFileName, false); } return NewFileName; } else { string name = get_assembly_path(FileName, false);//? а надо ли tolover? if (name == null) throw new AssemblyNotFound(currentCompilationUnit.SyntaxTree.file_name, FileName, sc); else if (File.Exists(name)) return name; else throw new AssemblyNotFound(currentCompilationUnit.SyntaxTree.file_name, FileName, sc); } } catch (ArgumentException ex) { throw new InvalidAssemblyPathError(currentCompilationUnit.SyntaxTree.file_name, sc); } } public string GetUnitFileName(SyntaxTree.unit_or_namespace unitNode, string currentPath, ILanguage currentUnitLanguage) { if (unitNode is SyntaxTree.uses_unit_in unitNodeCasted && unitNodeCasted.name == null) return unitNodeCasted.in_file.Value; if (currentPath == null) throw new InvalidOperationException(unitNode.UsesPath()); var unitName = unitNode.name.idents[0].name; if (unitNode is SyntaxTree.uses_unit_in uui) { TryThrowInvalidPath(uui.in_file.Value, uui.in_file.source_context); if (unitName.ToLower() != Path.GetFileNameWithoutExtension(uui.in_file.Value).ToLower()) throw new UsesInWrongName(unitNode.source_context.FileName, unitName, Path.GetFileNameWithoutExtension(uui.in_file.Value), uui.in_file.source_context); } return GetUnitFileName(unitName, unitNode.UsesPath(), currentPath, unitNode.source_context, currentUnitLanguage); } public string GetUnitFileName(string unitName, string usesPath, string currentPath, SyntaxTree.SourceContext sourceContext, ILanguage currentUnitLanguage) { var cacheKey = Tuple.Create(usesPath.ToLower(), currentPath?.ToLower()); bool foundInCache = false; if (GetUnitFileNameCache.TryGetValue(cacheKey, out var unitPaths)) { if (currentUnitLanguage.CaseSensitive) { foreach (var unitPath in unitPaths) { if (Path.GetFileNameWithoutExtension(unitPath) == unitName) { return unitPath; } } } else return unitPaths[0]; foundInCache = true; } // число приоритета меньше означает, что папка более важная var sourceFileName = FindSourceFileName(usesPath, currentPath, out var sourceFilePriority, currentUnitLanguage); var pcuFileName = FindPCUFileName(usesPath, currentPath, out var pcuFilePriority, currentUnitLanguage); bool sourceFileExists = sourceFileName != null; bool pcuFileExists = pcuFileName != null; if (CompilerOptions.Rebuild && sourceFileExists) pcuFileExists = false; // если нет ни одного типа файла или нет исходника и режим Rebuild, то ошибка if (!sourceFileExists && !pcuFileExists) { if (unitName == null) // вызов с "unitFileName == null" должен быть только там, где уже известно что хотя бы какой то файл есть // если где то ещё будет исопльзоваться unitFileName или source_context - надо будет добавить такую же проверку throw new InvalidOperationException(nameof(unitName)); else throw new UnitNotFound(sourceContext.FileName, unitName, sourceContext); } if (pcuFileExists && sourceFileExists) { // если приоритет папки исходника выше, то берем исходник if (sourceFilePriority < pcuFilePriority) pcuFileExists = false; // проверка на правильность установки приоритета - для удобства else if (sourceFilePriority == pcuFilePriority && Path.GetDirectoryName(sourceFileName) != Path.GetDirectoryName(pcuFileName)) throw new InvalidOperationException("Не верно задан приоритет папок!"); // также если время модификации pcu раньше, чем исходника, то нужна перекомпиляция (при этом они должны быть в одной папке) else if (sourceFilePriority == pcuFilePriority && File.GetLastWriteTime(pcuFileName) < File.GetLastWriteTime(sourceFileName)) pcuFileExists = false; } string unitFileName; if (pcuFileExists) unitFileName = Path.Combine(currentPath, pcuFileName); else if (sourceFileExists) unitFileName = Path.Combine(currentPath, sourceFileName); else // значит в предыдущем блоке кода ошибка - проверка для удобства throw new InvalidOperationException("Сброшено значение pcuFileExists и sourceFileExists. Такого здесь быть не должно."); if (foundInCache) { GetUnitFileNameCache[cacheKey].Add(unitFileName); } else { GetUnitFileNameCache[cacheKey] = new List() { unitFileName }; } return unitFileName; } public void AddStandardUnitsToInterfaceUsesSection(CompilationUnit currentUnit) { if (CompilerOptions.StandardModules[currentUnit.Language.Name].Count == 0) return; List usesList = GetInterfaceUsesSection(currentUnit.SyntaxTree); string currentModuleName = Path.GetFileNameWithoutExtension(currentUnit.SyntaxTree.file_name).ToLower(); foreach (CompilerOptions.StandardModule module in CompilerOptions.StandardModules[currentUnit.Language.Name]) { string moduleName = Path.GetFileNameWithoutExtension(module.name); if (moduleName.ToLower() == currentModuleName) return; } foreach (CompilerOptions.StandardModule module in CompilerOptions.StandardModules[currentUnit.Language.Name]) { // если мы компилируем не основную программу, а добавлять нужно в основную программу, то пропускаем if (module.addMethod == CompilerOptions.StandardModuleAddMethod.RightToMain && currentUnit != firstCompilationUnit) continue; string moduleName = Path.GetFileNameWithoutExtension(module.name); // если стандартный модуль уже подключен bool isModuleAlreadyInUsesSection = false; foreach (SyntaxTree.unit_or_namespace currentUnitNode in usesList) { if (currentUnitNode.name.idents.Count == 1 && currentUnitNode.name.idents[0].name.ToLower() == moduleName.ToLower()) { isModuleAlreadyInUsesSection = true; break; } } if (isModuleAlreadyInUsesSection) continue; // здесь присвоится либо юнит, либо пространство имен SyntaxTree.unit_or_namespace unitToAdd; if (Path.GetExtension(module.name) != "" /*&& Path.GetExtension(ModuleFileName).ToLower() != ".dll"*/) { unitToAdd = new SyntaxTree.uses_unit_in( new SyntaxTree.ident_list(new SyntaxTree.ident(moduleName)), new SyntaxTree.string_const(module.name)); //uses_unit_in.source_context = uses_unit_in.in_file.source_context = uses_unit_in.name.source_context = new SyntaxTree.SourceContext(1, 1, 1, 1); } else { unitToAdd = new SyntaxTree.unit_or_namespace(new SyntaxTree.ident_list(new SyntaxTree.ident(moduleName))); //uses_unit.source_context = uses_unit.name.source_context = new SyntaxTree.SourceContext(1, 1, 1, 1); } // добавление if (module.addMethod == CompilerOptions.StandardModuleAddMethod.RightToMain) { usesList.Add(unitToAdd); } else if (module.addMethod == CompilerOptions.StandardModuleAddMethod.LeftToAll) { usesList.Insert(0, unitToAdd); } } } private Assembly PreloadReference(compiler_directive reference) { var sc = GetSourceContext(reference); var fileName = GetReferenceFileName(reference.directive, sc, Path.GetDirectoryName(reference.source_file), true); return assemblyResolveScope.PreloadAssembly(fileName); } private CompilationUnit CompileReference(unit_node_list dlls, compiler_directive reference) { var sourceContext = GetSourceContext(reference); string unitName; try { unitName = GetReferenceFileName(reference.directive, sourceContext, Path.GetDirectoryName(reference.source_file), false); } catch (AssemblyNotFound) { throw; } // ToDo плохо, пока дебажил - тут постоянно ловились другие исключения, не связанные с неправильным знаками в пути к сборке | // EVA (проверить) catch (Exception) { throw new InvalidAssemblyPathError(currentCompilationUnit.SyntaxTree.file_name, sourceContext); } CompilationUnit currentUnit = null; if (UnitTable.Count == 0) throw new ProgramModuleExpected(unitName, null); if ((currentUnit = ReadDLL(unitName, sourceContext)) != null) { dlls.AddElement(currentUnit.SemanticTree, null); UnitTable[unitName] = currentUnit; return currentUnit; } else throw new AssemblyReadingError(currentCompilationUnit.SyntaxTree.file_name, unitName, sourceContext); } private SyntaxTree.SourceContext GetSourceContext(compiler_directive directive) { var loc = directive.location; if (loc == null) return null; return new SyntaxTree.SourceContext(loc.begin_line_num, loc.begin_column_num, loc.end_line_num, loc.end_column_num, 0, 0); } private bool HasIncludeNamespaceDirective(CompilationUnit unit) { var directives = GetDirectivesAsSemanticNodes(unit.SyntaxTree.compiler_directives, unit.SyntaxTree.file_name); return directives.Any(directive => directive.name.ToLower() == StringConstants.compiler_directive_include_namespace); } private Dictionary PrepareUserNamespacesUsedInTheCurrentUnit(CompilationUnit compilationUnit) { var directives = GetDirectivesAsSemanticNodes(compilationUnit.SyntaxTree.compiler_directives, compilationUnit.SyntaxTree.file_name); List files = GetIncludedFilesFromDirectives(compilationUnit, directives); Dictionary namespaces = new Dictionary(StringComparer.OrdinalIgnoreCase); List namespaceModules = new List(); foreach (string file in files) { SyntaxTree.compilation_unit syntaxTree = GetNamespaceSyntaxTree(file); #region SEMANTIC CHECKS : PASCAL NAMESPACE SemanticCheckIsUserNamespace(syntaxTree); #endregion SyntaxTree.unit_module unitModule = syntaxTree as SyntaxTree.unit_module; SyntaxTree.syntax_namespace_node namespaceNode = null; if (!namespaces.TryGetValue(unitModule.unit_name.idunit_name.name, out namespaceNode)) { namespaceNode = new SyntaxTree.syntax_namespace_node(unitModule.unit_name.idunit_name.name); namespaceNode.referenced_units = new unit_node_list(); namespaces[unitModule.unit_name.idunit_name.name] = namespaceNode; } AddDeclarationsAndReferencedUnitsToNamespaces(namespaceModules, file, unitModule, namespaceNode, compilationUnit.Language); } // корневой модуль является чем-то одним из этого SyntaxTree.unit_module mainLibrary = compilationUnit.SyntaxTree as SyntaxTree.unit_module; SyntaxTree.program_module mainProgram = compilationUnit.SyntaxTree as SyntaxTree.program_module; AddNamespacesToMainDefinitions(mainLibrary, mainProgram, namespaces); AddNamespacesToMainUsesList(mainLibrary, mainProgram, namespaceModules); return namespaces; } private void AddNamespacesToMainDefinitions(SyntaxTree.unit_module mainLibrary, SyntaxTree.program_module main_program, Dictionary namespaces) { foreach (string moduleName in namespaces.Keys) { if (mainLibrary != null) mainLibrary.interface_part.interface_definitions.Insert(0, namespaces[moduleName]); else main_program.program_block.defs.Insert(0, namespaces[moduleName]); } } private void AddNamespacesToMainUsesList(SyntaxTree.unit_module mainLibrary, SyntaxTree.program_module main_program, List namespaceModules) { SyntaxTree.uses_list mainUsesList; if (mainLibrary != null) mainUsesList = mainLibrary.interface_part.uses_modules; else mainUsesList = main_program.used_units; if (mainUsesList == null) mainUsesList = new SyntaxTree.uses_list(); HashSet set = new HashSet(); foreach (SyntaxTree.unit_or_namespace name_space in namespaceModules) { string name = SyntaxTree.Utils.IdentListToString(name_space.name.idents, ".").ToLower(); if (!set.Contains(name)) { mainUsesList.Add(name_space); set.Add(name); } } if (mainLibrary != null) mainLibrary.interface_part.uses_modules = mainUsesList; else main_program.used_units = mainUsesList; } private void AddDeclarationsAndReferencedUnitsToNamespaces(List namespace_modules, string file, SyntaxTree.unit_module unitModule, SyntaxTree.syntax_namespace_node namespaceNode, ILanguage currentUnitLanguage) { if (unitModule.interface_part.interface_definitions != null) { foreach (SyntaxTree.declaration decl in unitModule.interface_part.interface_definitions.defs) { namespaceNode.defs.Add(decl); } if (unitModule.interface_part.uses_modules != null) { CheckForDuplicatesInUsesSection(unitModule.interface_part.uses_modules.units); foreach (SyntaxTree.unit_or_namespace name_space in unitModule.interface_part.uses_modules.units) { if (IsPossibleNetNamespaceOrStandardPasFile(name_space, false, Path.GetDirectoryName(file), currentUnitLanguage)) { namespaceNode.referenced_units.AddElement(new namespace_unit_node(GetNamespace(name_space), GetLocationFromTreenode(name_space, unitModule.file_name)), null); } else { namespace_modules.Add(name_space); } } } } } private void SemanticCheckIsUserNamespace(SyntaxTree.compilation_unit unitSyntaxTree) { if (!(unitSyntaxTree is SyntaxTree.unit_module)) throw new NamespaceModuleExpected(unitSyntaxTree.source_context); SyntaxTree.unit_module unitModule = unitSyntaxTree as SyntaxTree.unit_module; if (unitModule.unit_name.HeaderKeyword != SyntaxTree.UnitHeaderKeyword.Namespace) throw new NamespaceModuleExpected(unitModule.unit_name.source_context); if (unitModule.implementation_part != null) throw new NamespaceModuleExpected(unitModule.implementation_part.source_context); if (unitModule.finalization_part != null) throw new NamespaceModuleExpected(unitModule.finalization_part.source_context); if (unitModule.initialization_part != null && unitModule.initialization_part.list.Count > 0) throw new NamespaceModuleExpected(unitModule.initialization_part.source_context); } private static List GetIncludedFilesFromDirectives(CompilationUnit compilationUnit, List directives) { List files = new List(); foreach (compiler_directive cd in directives) { if (cd.name.ToLower() == StringConstants.compiler_directive_include_namespace) { string directive = cd.directive.Replace('/', Path.DirectorySeparatorChar).Replace('\\', Path.DirectorySeparatorChar); if (directive == "*.pas" || directive.EndsWith(Path.DirectorySeparatorChar + "*.pas")) { string dir = Path.Combine(Path.GetDirectoryName(compilationUnit.SyntaxTree.file_name), directive.Replace(Path.DirectorySeparatorChar + "*.pas", "")); foreach (string file in Directory.EnumerateFiles(dir, "*.pas")) { if (!File.Exists(file)) throw new FileNotFound(file, cd.location); files.Add(file); } } else { string file = Path.Combine(Path.GetDirectoryName(compilationUnit.SyntaxTree.file_name), directive); if (!File.Exists(file)) throw new FileNotFound(file, cd.location); files.Add(file); } } } return files; } private void SemanticCheckNoIncludeNamespaceDirectivesInUnit(CompilationUnit currentUnit) { if (currentUnit.SyntaxTree is SyntaxTree.unit_module unitModule && HasIncludeNamespaceDirective(currentUnit) && unitModule.unit_name.HeaderKeyword != SyntaxTree.UnitHeaderKeyword.Library) { throw new IncludeNamespaceInUnitError(currentUnit.SyntaxTree.file_name, currentUnit.SyntaxTree.source_context); } } private SyntaxTree.compilation_unit GetNamespaceSyntaxTree(string fileName) { string sourceText = GetSourceFileText(fileName); List definesList = new List { "PASCALABC" }; // SSM 11/07/20 if (!CompilerOptions.Debug && !CompilerOptions.ForDebugging) definesList.Add("RELEASE"); else definesList.Add("DEBUG"); definesList.AddRange(CompilerOptions.ForceDefines); ILanguage language = LanguageProvider.SelectLanguageByExtension(fileName); SyntaxTree.compilation_unit syntaxTree = InternalParseText(language, fileName, sourceText, errorsList, warnings, definesList); if (errorsList.Count > 0) throw errorsList[0]; syntaxTree = ConvertSyntaxTree(syntaxTree, language.SyntaxTreeConverters); return syntaxTree; } public unit_node_list GetReferences(CompilationUnit compilationUnit) { unit_node_list dlls = new unit_node_list(); List directives; if (compilationUnit.SemanticTree is common_unit_node) directives = (compilationUnit.SemanticTree as common_unit_node).compiler_directives; else directives = GetDirectivesAsSemanticNodes(compilationUnit.SyntaxTree.compiler_directives, compilationUnit.SyntaxTree.file_name); DisablePABCRtlIfUsingDotnet5(directives); if (CompilerOptions.UseDllForSystemUnits) { directives.Add(new compiler_directive("reference", "%GAC%\\PABCRtl.dll", null, ".")); AddReferencesToNetSystemLibraries(compilationUnit, directives); } var referenceDirectives = new List(); foreach (compiler_directive directive in directives) { if (directive.name.ToLower() == StringConstants.compiler_directive_reference) { referenceDirectives.Add(directive); } } if (CompilerOptions.ProjectCompiled) { foreach (ReferenceInfo ri in project.references) { referenceDirectives.Add(new compiler_directive("reference", ri.full_assembly_name, null, project.MainFile)); } } if (assemblyResolveScope == null) assemblyResolveScope = new NetHelper.AssemblyResolveScope(AppDomain.CurrentDomain); // It's important to preload all the referenced assemblies before starting the compilation. During the // compilation, we need to access types from every referenced assembly. An attempt to access them could fail // if a transitively dependent assembly is not loaded, yet. // // It's not always possible to solve by re-ordering the references, since there are cases of // mutually-dependent assemblies (i.e. dependency loops) in the wild. foreach (var reference in referenceDirectives) { try { PreloadReference(reference); } catch (FileLoadException ex) { throw new CommonCompilerError(ex.Message, compilationUnit.SyntaxTree.file_name, reference.location.begin_line_num, reference.location.end_line_num); } } foreach (var reference in referenceDirectives) CompileReference(dlls, reference); return dlls; } private void DisablePABCRtlIfUsingDotnet5(List directives) { foreach (compiler_directive cd in directives) { if (cd.name.ToLower() == StringConstants.compiler_directive_platformtarget && !string.IsNullOrEmpty(cd.directive) && cd.directive.IndexOf("dotnet5") != -1) { CompilerOptions.UseDllForSystemUnits = false; } } } /// /// Добавляет ссылки на стандартные системные dll .NET - версия с директивами уровня семантики /// /// /// private void AddReferencesToNetSystemLibraries(CompilationUnit compilationUnit, List directives) { IEnumerable librariesToAdd = StringConstants.netSystemLibraries.Select(dll => $"%GAC%\\{dll}") .Except(directives.Where(directive => directive.name.Equals("reference", StringComparison.CurrentCultureIgnoreCase)) .Select(directive => directive.directive), StringComparer.CurrentCultureIgnoreCase); directives.AddRange(librariesToAdd.Select(dll => new compiler_directive("reference", dll, null, "."))); if (compilationUnit.SyntaxTree is SyntaxTree.program_module program && program.used_units != null) { var graph3DUnit = program.used_units.units.FirstOrDefault(u => u.name.ToString() == "Graph3D"); if (graph3DUnit != null) { IEnumerable graphLibrariesToAdd = StringConstants.graph3DDependencies.Select(dll => $"%GAC%\\{dll}") .Except(directives.Where(directive => directive.name.Equals("reference", StringComparison.CurrentCultureIgnoreCase)) .Select(directive => directive.directive), StringComparer.CurrentCultureIgnoreCase); directives.AddRange(graphLibrariesToAdd.Select(dll => new compiler_directive("reference", dll, null, "."))); } } } /// /// Добавляет ссылки на стандартные системные dll .NET - версия с директивами уровня синтаксиса /// /// /// private void AddReferencesToNetSystemLibraries(CompilationUnit compilationUnit, List directives) { IEnumerable librariesToAdd = StringConstants.netSystemLibraries.Select(dll => $"%GAC%\\{dll}") .Except(directives.Where(directive => directive.Name.text.Equals("reference", StringComparison.CurrentCultureIgnoreCase)) .Select(directive => directive.Directive.text), StringComparer.CurrentCultureIgnoreCase); directives.AddRange(librariesToAdd.Select(dll => new SyntaxTree.compiler_directive(new SyntaxTree.token_info("reference"), new SyntaxTree.token_info(dll)))); if (compilationUnit.SyntaxTree is SyntaxTree.program_module program && program.used_units != null) { var graph3DUnit = program.used_units.units.FirstOrDefault(u => u.name.ToString() == "Graph3D"); if (graph3DUnit != null) { IEnumerable graphLibrariesToAdd = StringConstants.graph3DDependencies.Select(dll => $"%GAC%\\{dll}") .Except(directives.Where(directive => directive.Name.text.Equals("reference", StringComparison.CurrentCultureIgnoreCase)) .Select(directive => directive.Directive.text), StringComparer.CurrentCultureIgnoreCase); directives.AddRange(graphLibrariesToAdd.Select(dll => new SyntaxTree.compiler_directive(new SyntaxTree.token_info("reference"), new SyntaxTree.token_info(dll)))); } } } NetHelper.AssemblyResolveScope assemblyResolveScope; private bool IsPossibleNetNamespaceOrStandardPasFile(SyntaxTree.unit_or_namespace name_space, bool addToStandardModules, string currentPath, ILanguage currentUnitLanguage) { if (name_space is SyntaxTree.uses_unit_in) return false; // если это "что-то"."что-то"... (полный путь к пространству имен) if (name_space.name.idents.Count > 1) return true; string sourceFileName = FindSourceFileName(name_space.name.idents[0].name, currentPath, out _, currentUnitLanguage); string pcuFileName = FindPCUFileName(name_space.name.idents[0].name, currentPath, out _, currentUnitLanguage); // если нет исходников и pcu if (sourceFileName == null && pcuFileName == null) return true; // если есть что-то одно string fileName = sourceFileName ?? pcuFileName; string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fileName); // если в программе используются эти модули, то RTL не используется string[] standardFilesExcludedFromRTL = new string[] { "PT4", "School", "CRT", "Arrays", "MPI", "Collections", "Core"}; bool includeInRTL = standardFilesExcludedFromRTL.All(file => !file.Equals(fileNameWithoutExtension, StringComparison.CurrentCultureIgnoreCase)); // если это исходный файл из папки Lib (стандартные паскалевские модули) if (CompilerOptions.UseDllForSystemUnits && Path.GetDirectoryName(fileName).Equals(Path.Combine(CompilerOptions.SystemDirectory, "Lib"), StringComparison.CurrentCultureIgnoreCase) && includeInRTL) { string s = Path.GetFileNameWithoutExtension(fileName).ToLower(); if (addToStandardModules && !StandardModules.Contains(s)) StandardModules.Add(s); return true; } return false; } public using_namespace GetNamespace(SyntaxTree.unit_or_namespace _name_space) { return new using_namespace(SyntaxTree.Utils.IdentListToString(_name_space.name.idents, ".")); } /// /// Формирует узел семантического дерева, соответствующий пространству имен (.NET или пользовательскому) /// /// /// private using_namespace GetNamespace(using_namespace_list usingList, string fullNamespaceName, SyntaxTree.unit_or_namespace name_space, bool mightBeUnit, Dictionary namespaces) { if (!NetHelper.NetHelper.NamespaceExists(fullNamespaceName) && (namespaces == null || !namespaces.ContainsKey(fullNamespaceName))) { if (mightBeUnit && !fullNamespaceName.Contains(".")) throw new UnitNotFound(currentCompilationUnit.SyntaxTree.file_name, fullNamespaceName, name_space.source_context); throw new TreeConverter.NamespaceNotFound(fullNamespaceName, GetLocationFromTreenode(name_space.name, currentCompilationUnit.SyntaxTree.file_name)); } return new using_namespace(fullNamespaceName); } public void AddNamespacesToUsingList(using_namespace_list usingList, List possibleNamespaces, bool mightContainUnits, Dictionary namespaces) { foreach (SyntaxTree.unit_or_namespace name_space in possibleNamespaces) { usingList.AddElement(GetNamespace(usingList, SyntaxTree.Utils.IdentListToString(name_space.name.idents, "."), name_space, mightContainUnits, namespaces)); } } public void AddNamespacesToUsingList(using_namespace_list using_list, SyntaxTree.using_list ul) { if (ul != null) AddNamespacesToUsingList(using_list, ul.namespaces, false, null); } /// /// получение списка using - legacy code !!! /// public SyntaxTree.using_list GetInterfaceUsingList(SyntaxTree.compilation_unit cu) { if (cu is SyntaxTree.unit_module) return (cu as SyntaxTree.unit_module).interface_part.using_namespaces; if (cu is SyntaxTree.program_module) return (cu as SyntaxTree.program_module).using_namespaces; return null; } /// /// получение списка using - legacy code !!! /// private SyntaxTree.using_list GetImplementationSyntaxUsingList(SyntaxTree.compilation_unit cu) { if (cu is SyntaxTree.unit_module) if ((cu as SyntaxTree.unit_module).implementation_part != null) return (cu as SyntaxTree.unit_module).implementation_part.using_namespaces; return null; } public string GetSourceFileText(string FileName) { return (string)SourceFilesProvider(FileName, SourceFileOperation.GetText); } public SyntaxTree.compilation_unit ParseText(string fileName, string text, List errorList, List warnings) { Reset(); ILanguage language = LanguageProvider.SelectLanguageByExtension(fileName); OnChangeCompilerState(this, CompilerState.CompilationStarting, fileName); SyntaxTree.compilation_unit cu = InternalParseText(language, fileName, text, ErrorsList, warnings, null, false); OnChangeCompilerState(this, CompilerState.Ready, fileName); return cu; } private void CalculateLinesCompiled(List errorList, SyntaxTree.compilation_unit unitSyntaxTree) { if (errorList.Count == 0 && unitSyntaxTree != null && unitSyntaxTree.source_context != null) { linesCompiled += (uint)(unitSyntaxTree.source_context.end_position.line_num - unitSyntaxTree.source_context.begin_position.line_num + 1); } } private void CalculatePascalProgramHealth(SyntaxTree.compilation_unit unitSyntaxTree) { // Это только для локального компилятора? var stat = new SyntaxVisitors.ABCStatisticsVisitor(); stat.ProcessNode(unitSyntaxTree); pABCCodeHealth = stat.CalcHealth(out int aaa, out int bbb); } private SyntaxTree.compilation_unit InternalParseText(ILanguage language, string fileName, string text, List errorList, List warnings, List definesList = null, bool calculateHealth = true) { OnChangeCompilerState(this, CompilerState.BeginParsingFile, fileName); SyntaxTree.compilation_unit unitSyntaxTree = language.Parser.GetCompilationUnit(fileName, text, ErrorsList, warnings, Parsers.ParseMode.Normal, CurrentUnitIsNotMainProgram(), definesList); OnChangeCompilerState(this, CompilerState.EndParsingFile, fileName); // Вычисляем сколько строк скомпилировали CalculateLinesCompiled(errorList, unitSyntaxTree); // 500 - это наибольшая программа для начинающих. БОльшая программа - здоровье кода только по кнопке (чтобы не замедлять) if (calculateHealth && language.Name == StringConstants.pascalLanguageName && linesCompiled <= 500) { // TODO: при подсчете здоровья программы на других языках необходимо вынести StatisticsVisitor в интерфейс ILanguage EVA CalculatePascalProgramHealth(unitSyntaxTree); } return unitSyntaxTree; } /// /// Проверяет, является ли модуль dll по соответствующей директиве /// public static bool IsDll(SyntaxTree.compilation_unit unitSyntaxTree) { foreach (SyntaxTree.compiler_directive directive in unitSyntaxTree.compiler_directives) { if (string.Equals(directive.Name.text, "apptype", StringComparison.CurrentCultureIgnoreCase) && string.Equals(directive.Directive.text, "dll", StringComparison.CurrentCultureIgnoreCase)) { return true; } } return false; } /// /// Проверяет, является ли модуль dll по соответствующей директиве и возвращает эту директиву выходным параметром /// public static bool IsDll(SyntaxTree.compilation_unit unitSyntaxTree, out SyntaxTree.compiler_directive dllDirective) { foreach (SyntaxTree.compiler_directive directive in unitSyntaxTree.compiler_directives) { if (string.Equals(directive.Name.text, StringConstants.compiler_directive_apptype, StringComparison.CurrentCultureIgnoreCase) && string.Equals(directive.Directive.text, "dll", StringComparison.CurrentCultureIgnoreCase)) { dllDirective = directive; return true; } } dllDirective = null; return false; } /// /// Компилирует основную программу и все используемые ей юниты рекурсивно /// /// Вспомогательная переменная для заполнения CompilationUnit.interfaceUsedUnits и /// CompilationUnit.implementationUsedUnits (здесь могут содержаться юниты и dll) /// /// Вспомогательная переменная для заполнения CompilationUnit.interfaceUsedDirectUnits и /// CompilationUnit.implementationUsedDirectUnits /// /// Синтаксический узел текущего модуля (или пространства имен) /// Директория родительского модуля /// Скомпилированный юнит public CompilationUnit CompileUnit(unit_node_list unitsFromUsesSection, Dictionary directUnitsFromUsesSection, SyntaxTree.unit_or_namespace currentUnitNode, string previousPath, ILanguage previousUnitLanguage = null) { string unitFileName = GetUnitFileName(currentUnitNode, previousPath, previousUnitLanguage); string unitId = Path.ChangeExtension(unitFileName, null); // Имя папки, в которой лежит текущий модуль // Используется для подключения модулей, $include и т.п. из модуля, подключённого с uses-in var currentDirectory = Path.GetDirectoryName(unitFileName); // вернет null, если юнит еще не был инициализирован CompilationUnit currentUnit = UnitTable[unitId]; Dictionary docs = null; if (currentUnit != null) { #region SEMANTIC CHECKS : USES IN SECTION LOGIC // ошибка - пространство имен не может содержать in секцию (для указания файла) SemanticCheckUsesInIsNotNamespace(currentUnitNode, currentUnit); #endregion // если модуль уже скомпилирован - возвращаем (возможно, только интерфейс модуля и тогда он докомпилируется в другом рекурсивном вызове) EVA if (currentUnit.State != UnitState.BeginCompilation || currentUnit.SemanticTree != null) //TODO: ИЗБАВИТЬСЯ ОТ ВТОРОГО УСЛОВИЯ { AddCurrentUnitAndItsReferencesToUsesLists(unitsFromUsesSection, directUnitsFromUsesSection, currentUnitNode, currentUnit, GetReferences(currentUnit)); return currentUnit; } } else { // если есть pcu - возврат EVA if (UnitHasPCU(unitsFromUsesSection, directUnitsFromUsesSection, currentUnitNode, ref unitFileName, ref currentUnit, previousUnitLanguage)) return currentUnit; // нет pcu и модуль не откомпилирован => новый модуль EVA InitializeNewUnit(unitFileName, unitId, ref currentUnit, out docs); } // формирование списков зависимостей текущего модуля (uses list, dll, пространства имен) CreateDependencyListsForCurrentUnit(currentUnit, currentDirectory, out var interfaceUsesList, out var references, out var namespaces); #region INTERFACE PART // комплируем зависимости из интерфейса EVA if (interfaceUsesList != null) { CompileInterfaceDependencies(unitsFromUsesSection, directUnitsFromUsesSection, currentUnitNode, unitFileName, currentDirectory, currentUnit, interfaceUsesList, references, namespaces, out bool shouldReturnCurUnit); if (shouldReturnCurUnit) return currentUnit; } currentCompilationUnit = currentUnit; currentUnit.InterfaceUsedUnits.AddRange(references); // Добавление пространств имен из uses list (могут быть разных видов) AddNamespacesToUsingList(currentUnit.InterfaceUsingNamespaceList, currentUnit.possibleNamespaces, true, namespaces); #region USING LIST LEGACY CODE // Добавление пространств имен NET из using list - устаревшее ключевое слово using AddNamespacesToUsingList(currentUnit.InterfaceUsingNamespaceList, GetInterfaceUsingList(currentUnit.SyntaxTree)); #endregion //Console.WriteLine("Compiling Interface "+ unitFileName);//DEBUG if (currentUnit.Language.LanguageInformation.SyntaxTreeIsConvertedAfterUsedModulesCompilation) { // заполняем currentUnit.NamesFromUsedUnits CollectNamesFromUsedUnits(currentUnit); // конвертация синтаксического дерева с использованием данных из откомпилированных зависимостей ConvertSyntaxTreeAfterUsedModulesCompilation(currentUnit); } // компилируем интерфейс текущего модуля EVA CompileCurrentUnitInterface(unitFileName, currentUnit, docs); // интерфейс скомпилирован - переходим к секции реализации currentUnit.State = UnitState.InterfaceCompiled; // заполнение списков uses семантического уровня AddCurrentUnitAndItsReferencesToUsesLists(unitsFromUsesSection, directUnitsFromUsesSection, currentUnitNode, currentUnit, references); #endregion #region IMPLEMENTATION PART // берем модули из секции uses в реализации List implementationUsesList = GetImplementationUsesSection(currentUnit.SyntaxTree); currentUnit.ImplementationUsedUnits.Clear(); currentUnit.possibleNamespaces.Clear(); common_unit_node semanticTreeAsCommonNode = currentUnit.SemanticTree as common_unit_node; // Компиляция зависимостей в области реализации EVA CompileImplementationDependencies(currentDirectory, currentUnit, implementationUsesList, namespaces, semanticTreeAsCommonNode, out bool shouldReturnCurrentUnit); if (shouldReturnCurrentUnit) return currentUnit; // Console.WriteLine("Compiling Implementation "+ unitFileName);//DEBUG // компилируем реализацию текущего модуля EVA CompileCurrentUnitImplementation(unitFileName, currentUnit, docs); #endregion currentUnit.State = UnitState.Compiled; if (semanticTreeAsCommonNode != null) { if (!UnitsTopologicallySortedList.Contains(currentUnit))//vnimanie zdes inogda pri silnoj zavisimosti modulej moduli popadajut neskolko raz UnitsTopologicallySortedList.Add(currentUnit); } OnChangeCompilerState(this, CompilerState.EndCompileFile, unitFileName); //SavePCU(currentUnit, unitFileName); return currentUnit; /*if(currentUnit.State!=UnitState.Compiled) { //Console.WriteLine("Compile Interface "+unitFileName);//DEBUG currentUnit.SemanticTree=SyntaxTreeToSemanticTreeConverter.CompileInterface(currentUnit.SyntaxTree, currentUnit.InterfaceUsedUnits,currentUnit.syntax_error); currentUnit.State=UnitState.InterfaceCompiled; implementationUsesList=GetSemanticImplementationUsesList(currentUnit.SyntaxTree); if(implementationUsesList!=null) for(int i=implementationUsesList.Count-1;i>=0;i--) CompileUnit(currentUnit.ImplementationUsedUnits,implementationUsesList[i]); //Console.WriteLine("Compile Implementation "+unitFileName);//DEBUG if (currentUnit.SyntaxTree is SyntaxTree.unit_module) { SyntaxTreeToSemanticTreeConverter.CompileImplementation(currentUnit.SemanticTree, currentUnit.SyntaxTree,currentUnit.ImplementationUsedUnits,currentUnit.syntax_error); } currentUnit.State=UnitState.Compiled; unitsFromUsesSection.Add(currentUnit.SemanticTree); SaveSemanticTreeToFile(currentUnit,unitFileName); }*/ } private void ConvertSyntaxTreeAfterUsedModulesCompilation(CompilationUnit currentUnit) { var artifacts = new CompilationArtifactsUsedBySyntaxConverters(currentUnit.NamesFromUsedUnits); foreach (ISyntaxTreeConverter converter in currentUnit.Language.SyntaxTreeConverters) { OnChangeCompilerState(this, CompilerState.SyntaxTreeConversion, converter.Name); currentUnit.SyntaxTree = (SyntaxTree.compilation_unit)converter.ConvertAfterUsedModulesCompilation(currentUnit.SyntaxTree, false, in artifacts); } } private void CollectNamesFromUsedUnits(CompilationUnit currentUnit) { if (currentUnit.NamesFromUsedUnits.Count == 0) { // пространства имен .NET пока не поддерживаются foreach (var pair in currentUnit.InterfaceUsedDirectUnits) { CompilationUnit unit = pair.Value; string unitName = Path.GetFileNameWithoutExtension(unit.UnitFileName); currentUnit.NamesFromUsedUnits.Add(unitName, new Dictionary()); var unitScope = (unit.SemanticTree as common_unit_node).scope; foreach (var names in unitScope.Symbols.DictCaseSensitive.Skip(1)) { definition_node symInfo = names.Value.InfoList[0].sym_info; // Если достаем из pcu, то надо восстановить полную информацию if (symInfo is wrapped_definition_node wdn) { // У некоторых дубликатов sym_info offset не задан, ищем тот, где задан symInfo = names.Value.InfoList.Find(si => ((wrapped_definition_node)si.sym_info).offset > 0)?.sym_info; if (symInfo == null) continue; wdn = (wrapped_definition_node)symInfo; // Если это не синоним типа, то восстанавливаем семантическую информацию if (!wdn.is_synonim) symInfo = wdn.PCUReader.CreateInterfaceMember(wdn.offset, names.Key); } currentUnit.NamesFromUsedUnits[unitName].Add(names.Key, symInfo.general_node_type == general_node_type.variable_node || symInfo.general_node_type == general_node_type.constant_definition || symInfo.general_node_type == general_node_type.event_node); } } } } private void CreateDependencyListsForCurrentUnit(CompilationUnit currentUnit, string currentDirectory, out List interfaceUsesList, out unit_node_list references, out Dictionary namespaces) { interfaceUsesList = GetInterfaceUsesSection(currentUnit.SyntaxTree); SetUseDLLForSystemUnits(currentDirectory, interfaceUsesList, interfaceUsesList.Count - 1 - currentUnit.InterfaceUsedUnits.Count, currentUnit.Language); references = GetReferences(currentUnit); // Надо подумать, как мы будем подключать про-ва имен из других языков EVA namespaces = PrepareUserNamespacesUsedInTheCurrentUnit(currentUnit); } private void AddCurrentUnitAndItsReferencesToUsesLists(unit_node_list unitsFromUsesSection, Dictionary directUnitsFromUsesSection, SyntaxTree.unit_or_namespace currentUnitNode, CompilationUnit currentUnit, unit_node_list references) { if (unitsFromUsesSection != null) { if (unitsFromUsesSection.AddElement(currentUnit.SemanticTree, currentUnitNode.UsesPath())) directUnitsFromUsesSection.Add(currentUnit.SemanticTree, currentUnit); unitsFromUsesSection.AddRange(references); } } private void SemanticCheckUsesInIsNotNamespace(SyntaxTree.unit_or_namespace currentUnitNode, CompilationUnit currentUnit) { if (currentUnit.SemanticTree is dot_net_unit_node && currentUnitNode is SyntaxTree.uses_unit_in ui && ui.in_file != null) // значит, это пространство имен и секция in у него должна отсутствовать { ErrorsList.Add(new NamespaceCannotHaveInSection(ui.in_file.source_context)); } } private void CompileCurrentUnitImplementation(string UnitFileName, CompilationUnit currentUnit, Dictionary docs) { if (currentUnit.SyntaxTree is SyntaxTree.unit_module) { #if DEBUG if (InternalDebug.SemanticAnalysis) #endif { OnChangeCompilerState(this, CompilerState.CompileImplementation, UnitFileName); TreeConverter.SemanticRulesConstants.SymbolTableCaseSensitive = currentUnit.CaseSensitive; var data = new TreeConverter.TreeConversion.InitializationDataForCompilingImplementation( currentUnit.syntax_error, BadNodesInSyntaxTree, buildImplementationUsesList(currentUnit), currentUnit.InterfaceUsingNamespaceList, currentUnit.ImplementationUsingNamespaceList, currentUnit.SyntaxTree, (common_unit_node)currentUnit.SemanticTree, ErrorsList, Warnings, docs, CompilerOptions.Debug, CompilerOptions.ForDebugging, CompilerOptions.ForIntellisense, currentUnit.GeneratedNamesManager ); SyntaxTreeToSemanticTreeConverter.CompileImplementation(currentUnit.Language, data, CompiledVariables); CheckErrorsAndThrowTheFirstOne(); } } } /// /// Компилирует модули из секции uses текущего модуля реализации рекурсивно /// private void CompileImplementationDependencies(string currentPath, CompilationUnit currentUnit, List implementationUsesList, Dictionary namespaces, common_unit_node commonUnitNode, out bool shouldReturnCurrentUnit) { shouldReturnCurrentUnit = false; if (implementationUsesList != null) { for (int i = implementationUsesList.Count - 1; i >= 0; i--) { if (!IsPossibleNetNamespaceOrStandardPasFile(implementationUsesList[i], true, currentPath, currentUnit.Language)) { CompilationUnit unitFromUsesSection = UnitTable[Path.ChangeExtension(GetUnitFileName(implementationUsesList[i], currentPath, currentUnit.Language), null)]; // защита от попадания в бесконечный цикл (когда мы вернемся в тот юнит, в котором уже были (еще не скомпилированный), а затем спустимся по дереву зависимостей сюда же и т.д.) // первая часть условия - если мы встречаем юнит не первый раз, вторая часть - если интерфейс еще не скомпилирован if (unitFromUsesSection != null && unitFromUsesSection.State == UnitState.BeginCompilation) { UnitsToCompileDelayedList.Add(unitFromUsesSection); shouldReturnCurrentUnit = true; // обрубаем компиляцию реализации в CompileUnit - не все интерфейсы еще откомпилированы !!! #if DEBUG // Console.WriteLine("[DEBUGINFO]Send compile to end " + Path.GetFileName(GetUnitFileName(implementationUsesList[i])));//DEBUG #endif } else { CompileUnit(currentUnit.ImplementationUsedUnits, currentUnit.ImplementationUsedDirectUnits, implementationUsesList[i], currentPath, currentUnit.Language); } } else { currentUnit.ImplementationUsedUnits.AddElement(new TreeRealization.namespace_unit_node(GetNamespace(implementationUsesList[i])), null); currentUnit.possibleNamespaces.Add(implementationUsesList[i]); } } } currentCompilationUnit = currentUnit; AddNamespacesToUsingList(currentUnit.ImplementationUsingNamespaceList, currentUnit.possibleNamespaces, true, namespaces); #region USING LIST LEGACY CODE AddNamespacesToUsingList(currentUnit.ImplementationUsingNamespaceList, GetImplementationSyntaxUsingList(currentUnit.SyntaxTree)); #endregion if (shouldReturnCurrentUnit) { // помещаем текущий модуль в список отложенной компиляции UnitsToCompileDelayedList.Add(currentUnit); if (commonUnitNode != null) { if (!UnitsTopologicallySortedList.Contains(currentUnit))//vnimanie zdes inogda pri silnoj zavisimosti modulej moduli popadajut neskolko raz UnitsTopologicallySortedList.Add(currentUnit); } //Console.WriteLine("Send compile to end "+unitFileName);//DEBUG } } private void CompileCurrentUnitInterface(string UnitFileName, CompilationUnit currentUnit, Dictionary docs) { #if DEBUG if (InternalDebug.SemanticAnalysis) #endif { if (currentUnit.State != UnitState.InterfaceCompiled) { OnChangeCompilerState(this, CompilerState.CompileInterface, UnitFileName); TreeConverter.SemanticRulesConstants.SymbolTableCaseSensitive = currentUnit.CaseSensitive; var data = new TreeConverter.TreeConversion.InitializationDataForCompilingInterface( currentUnit.syntax_error, BadNodesInSyntaxTree, currentUnit.InterfaceUsedUnits, currentUnit.InterfaceUsingNamespaceList, currentUnit.SyntaxTree, ErrorsList, Warnings, docs, CompilerOptions.Debug, CompilerOptions.ForDebugging, CompilerOptions.ForIntellisense, currentUnit.GeneratedNamesManager ); currentUnit.SemanticTree = SyntaxTreeToSemanticTreeConverter.CompileInterface(currentUnit.Language, data, CompiledVariables); CheckErrorsAndThrowTheFirstOne(); } } } /// /// Компилирует модули из секции uses интерфейса текущего модуля рекурсивно /// /// private void CompileInterfaceDependencies(unit_node_list unitsFromUsesSection, Dictionary directUnitsFromUsesSection, SyntaxTree.unit_or_namespace currentUnitNode, string unitFileName, string currentPath, CompilationUnit currentUnit, List interfaceUsesList, unit_node_list references, Dictionary namespaces, out bool shouldReturnCurrentUnit) { shouldReturnCurrentUnit = false; for (int i = interfaceUsesList.Count - 1 - currentUnit.InterfaceUsedUnits.Count; i >= 0; i--) // здесь откидываются модули с уже откомпилированными интерфейсами из секции uses (см. комментарий, обозначенный #1710) { if (IsPossibleNetNamespaceOrStandardPasFile(interfaceUsesList[i], true, currentPath, currentUnit.Language) || namespaces.ContainsKey(interfaceUsesList[i].name.idents[0].name)) { currentUnit.InterfaceUsedUnits.AddElement(new namespace_unit_node(GetNamespace(interfaceUsesList[i])), null); currentUnit.possibleNamespaces.Add(interfaceUsesList[i]); } else { #region SEMANTIC CHECKS : CYCLE DEPENDENCY OF INTERFACES SemanticCheckNoLoopDependenciesOfInterfaces(currentUnit, unitFileName, interfaceUsesList[i], currentPath); #endregion // компиляция модулей из интерфейса текущего модуля CompileUnit(currentUnit.InterfaceUsedUnits, currentUnit.InterfaceUsedDirectUnits, interfaceUsesList[i], currentPath, currentUnit.Language); // если текущий модуль был откомпилирован в другом рекурсивном вызове if (currentUnit.State == UnitState.Compiled) { AddCurrentUnitAndItsReferencesToUsesLists(unitsFromUsesSection, directUnitsFromUsesSection, currentUnitNode, currentUnit, references); // #1710 добавление в список модулей из uses происходит только в конце компиляции интерфейса юнита или позже во всех случаях shouldReturnCurrentUnit = true; } } } } private void SemanticCheckNoLoopDependenciesOfInterfaces(CompilationUnit currentUnit, string unitFileName, SyntaxTree.unit_or_namespace usedUnitNode, string currentPath) { var usedUnitFileName = GetUnitFileName(usedUnitNode, currentPath, currentUnit.Language); var usedUnitId = Path.ChangeExtension(usedUnitFileName, null); // когда образуется цикл здесь сохранится смежная вершина графа (используемый юнит), которая тоже принадлежит циклу currentUnit.currentUsedUnitId = usedUnitId; CompilationUnit usedUnit = UnitTable[usedUnitId]; // если используемый юнит имеет не скомпилированный интерфейс, но был инициализирован (то есть мы попали в цикл) if (usedUnit != null && usedUnit.State == UnitState.BeginCompilation) { // если в цикле где-то присутствует дуга из implementation (тогда интерфейс этого модуля будет откомпилирован), то такой цикл допускается while (usedUnit != currentUnit) { string nextUnitId = usedUnit.currentUsedUnitId; usedUnit = UnitTable[nextUnitId]; if (usedUnit.State != UnitState.BeginCompilation) return; } throw new CycleUnitReference(unitFileName, usedUnitNode); } } /// /// Если в программе в секции uses есть не про-во имен и не стандартный модуль, то использование PABCRtl.dll отменяется /// private void SetUseDLLForSystemUnits(string currentDirectory, List usesList, int lastUnitIndex, ILanguage currentUnitLanguage) { if (usesList != null && CompilerOptions.UseDllForSystemUnits) { for (int i = lastUnitIndex; i >= 0; i--) { if (!IsPossibleNetNamespaceOrStandardPasFile(usesList[i], false, currentDirectory, currentUnitLanguage)) { CompilerOptions.UseDllForSystemUnits = false; break; } } } } /// /// Получение исходного кода модуля, заполнение документации, /// генерация синтаксического дерева, /// обработка синтаксических ошибок /// private void InitializeNewUnit(string unitFileName, string UnitId, ref CompilationUnit currentUnit, out Dictionary docs) { currentUnit = new CompilationUnit(); if (firstCompilationUnit == null) firstCompilationUnit = currentUnit; currentCompilationUnit = currentUnit; // запоминание языка currentUnit.Language = LanguageProvider.SelectLanguageByExtension(unitFileName); currentUnit.CaseSensitive = currentUnit.Language.CaseSensitive; currentUnit.UnitFileName = unitFileName; // получение итогового синтаксического дерева после сахарных преобразований ConstructSyntaxTreeAndRunSugarConversions(unitFileName, currentUnit, out docs); InitializeCompilerOptionsRelatedToStandardUnits(currentUnit.SyntaxTree); RunSemanticChecks(unitFileName, currentUnit); // местоположение этой строчки важно, потому что проверяется UnitTable.Count > 0 выше EVA UnitTable[UnitId] = currentUnit; // здесь добавляем стандартные модули в секцию uses интерфейса if (!CompilerOptions.DisableStandardUnits) AddStandardUnitsToInterfaceUsesSection(currentUnit); else { AddReferencesToNetSystemLibraries(currentUnit, currentUnit.SyntaxTree.compiler_directives); } } /// /// Строит синтаксическое дерево, бросает первую из найденных ошибок (если они есть) и запускает сахарные преобразования /// private void ConstructSyntaxTreeAndRunSugarConversions(string unitFileName, CompilationUnit currentUnit, out Dictionary docs) { OnChangeCompilerState(this, CompilerState.BeginCompileFile, unitFileName); // начало компиляции модуля // получение синтаксического дерева string sourceText = GetSourceCode(unitFileName, currentUnit); currentUnit.SyntaxTree = ConstructSyntaxTree(unitFileName, currentUnit, sourceText); // сопоставление нодам ошибок EVA MatchSyntaxErrorsToBadNodes(currentUnit); CheckErrorsAndThrowTheFirstOne(); // SSM 2/05/16 - для преобразования синтаксических деревьев извне (синтаксический сахар) currentUnit.SyntaxTree = ConvertSyntaxTree(currentUnit.SyntaxTree, currentUnit.Language.SyntaxTreeConverters); // генерация документации к узлам синтаксического дерева EVA docs = GenUnitDocumentation(currentUnit, sourceText); } /// /// Устанавливает значения опций DisableStandardUnits и UseDllForSystemUnits /// private void InitializeCompilerOptionsRelatedToStandardUnits(SyntaxTree.compilation_unit unitSyntaxTree) { // проверяем только для основной программы или dll if (UnitTable.Count == 0) { var disableStandardUnitsDirective = unitSyntaxTree.compiler_directives.Find(directive => directive.Name.text.Equals(StringConstants.compiler_directive_disable_standard_units, StringComparison.CurrentCultureIgnoreCase)); if (disableStandardUnitsDirective != null) CompilerOptions.DisableStandardUnits = true; } if (unitSyntaxTree is SyntaxTree.unit_module) CompilerOptions.UseDllForSystemUnits = false; } /// /// Семантические проверки по директивам и по типу файла /// private void RunSemanticChecks(string unitFileName, CompilationUnit currentUnit) { // SSM 21/05/20 Проверка, что мы не записали apptype dll в небиблиотеку bool isDll = IsDll(currentUnit.SyntaxTree, out var dllDirective); SemanticCheckDLLDirectiveOnlyForLibraries(currentUnit.SyntaxTree, isDll, dllDirective); // ошибка - компилируем вторую основную программу или вторую dll вместо юнита SemanticCheckCurrentUnitMustBeUnitModule(unitFileName, currentUnit, isDll); // ошибка директива include в паскалевском юните SemanticCheckNoIncludeNamespaceDirectivesInUnit(currentUnit); SemanticCheckDisableStandardUnitsDirectiveInUnit(currentUnit.SyntaxTree); CheckErrorsAndThrowTheFirstOne(); } private SyntaxTree.compilation_unit ConvertSyntaxTree(SyntaxTree.compilation_unit syntaxTree, List converters) { foreach (ISyntaxTreeConverter converter in converters) { OnChangeCompilerState(this, CompilerState.SyntaxTreeConversion, converter.Name); syntaxTree = converter.Convert(syntaxTree, false) as SyntaxTree.compilation_unit; } return syntaxTree; } private void SemanticCheckNamespacesOnlyInProjects(CompilationUnit currentUnit) { // legacy if (currentUnit.SyntaxTree is SyntaxTree.unit_module) { // Проверка на явный namespace (паскалевский) if ((currentUnit.SyntaxTree as SyntaxTree.unit_module).unit_name.HeaderKeyword == SyntaxTree.UnitHeaderKeyword.Namespace) throw new NamespacesCanBeCompiledOnlyInProjects(currentUnit.SyntaxTree.source_context); } } // Синтактико-семантическая ошибка - проверка, что compilationUnit является модулем, // а не основной программой и не dll EVA private void SemanticCheckCurrentUnitMustBeUnitModule(string UnitFileName, CompilationUnit currentUnit, bool isDll) { if (UnitTable.Count > 0) // если это не главный модуль (программа в unittable всегда идет первой) { if (currentUnit.SyntaxTree is SyntaxTree.program_module) throw new UnitModuleExpected(UnitFileName, currentUnit.SyntaxTree.source_context.LeftSourceContext); else if (isDll) throw new UnitModuleExpectedLibraryFound(UnitFileName, currentUnit.SyntaxTree.source_context.LeftSourceContext); } } private void MatchSyntaxErrorsToBadNodes(CompilationUnit currentUnit) { if (errorsList.Count > 0) { currentUnit.syntax_error = errorsList[0] as SyntaxError; foreach (Error er in errorsList) if (er is SyntaxError && (er as SyntaxError).bad_node != null) BadNodesInSyntaxTree[(er as SyntaxError).bad_node] = er; } } /// /// Проверка, что директива dll только в Library - требует передачи директивы dll /// private void SemanticCheckDLLDirectiveOnlyForLibraries(SyntaxTree.compilation_unit unitSyntaxTree, bool isDll, SyntaxTree.compiler_directive dllDirective) { // Если Library и apptype dll не указано, то никакой ошибки нет EVA if (isDll) { if (!(unitSyntaxTree is SyntaxTree.unit_module) || (unitSyntaxTree is SyntaxTree.unit_module unitNode && unitNode.unit_name.HeaderKeyword != SyntaxTree.UnitHeaderKeyword.Library)) { // если в директивах появилось {$apptype dll}, но это не Library ErrorsList.Add(new AppTypeDllIsAllowedOnlyForLibraries(unitSyntaxTree.file_name, dllDirective.source_context)); } } } /// /// Ошибка указания директивы DisableStandardUnits в подключенном модулей /// /// private void SemanticCheckDisableStandardUnitsDirectiveInUnit(SyntaxTree.compilation_unit unitSyntaxTree) { // проверяем для используемых модулей if (UnitTable.Count > 0) { var foundDirective = unitSyntaxTree.compiler_directives.Find(directive => directive.Name.text.Equals(StringConstants.compiler_directive_disable_standard_units, StringComparison.CurrentCultureIgnoreCase)); if (foundDirective != null) { ErrorsList.Add(new DisableStandardUnitsDirectiveDisallowedInUsedUnits(unitSyntaxTree.file_name, foundDirective.source_context)); } } } private SyntaxTree.compilation_unit ConstructSyntaxTree(string unitFileName, CompilationUnit currentUnit, string sourceText) { List DefinesList = new List { "PASCALABC" }; if (!CompilerOptions.Debug && !CompilerOptions.ForDebugging) DefinesList.Add("RELEASE"); else DefinesList.Add("DEBUG"); DefinesList.AddRange(CompilerOptions.ForceDefines); SyntaxTree.compilation_unit syntaxTree; if (CompilerOptions.UnitSyntaxTree != null) { syntaxTree = CompilerOptions.UnitSyntaxTree; CompilerOptions.UnitSyntaxTree = null; } // синтаксический анализ else syntaxTree = InternalParseText(currentUnit.Language, unitFileName, sourceText, errorsList, warnings, DefinesList); // проверка, что пространства имен только в проектах SemanticCheckNamespacesOnlyInProjects(currentUnit); return syntaxTree; } private string GetSourceCode(string UnitFileName, CompilationUnit currentUnit) { string SourceText = null; if (CompilerOptions.UnitSyntaxTree == null) { SourceText = GetSourceFileText(UnitFileName); if (SourceText == null) { if (currentUnit == firstCompilationUnit) throw new SourceFileNotFound(UnitFileName); else throw new UnitNotFound(currentUnit.SyntaxTree.file_name, UnitFileName, currentUnit.SyntaxTree.source_context); } } return SourceText; } /// /// Возвращает true, если текущий компилируемый модуль не является основной программой (program_module) /// /// private bool CurrentUnitIsNotMainProgram() { return UnitTable.Count > 0; } private Dictionary GenUnitDocumentation(CompilationUnit currentUnit, string SourceText) { Dictionary docs = null; if (IsDocumentationNeeded(currentUnit.SyntaxTree)) { if (SourceText != null) { docs = AddDocumentationToNodes(currentUnit, SourceText); if (docs != null) currentUnit.Documented = true; } } return docs; } private bool UnitHasPCU(unit_node_list unitsFromUsesSection, Dictionary directUnitsFromUsesSection, SyntaxTree.unit_or_namespace currentUnitNode, ref string UnitFileName, ref CompilationUnit currentUnit, ILanguage previousUnitLanguage) { if (Path.GetExtension(UnitFileName).ToLower() == CompilerOptions.CompiledUnitExtension) { if (File.Exists(UnitFileName)) { if (UnitTable.Count == 0) throw new ProgramModuleExpected(UnitFileName, null); try { if ((currentUnit = ReadPCU(UnitFileName, previousUnitLanguage)) != null) { AddCurrentUnitAndItsReferencesToUsesLists(unitsFromUsesSection, directUnitsFromUsesSection, currentUnitNode, currentUnit, GetReferences(currentUnit)); UnitTable[Path.ChangeExtension(UnitFileName, null)] = currentUnit; return true; } } catch (InvalidPCUFile) { //Перекомпилируем.... } // Так надо (для дебага) catch (Error) { throw; } catch (Exception e) { OnChangeCompilerState(this, CompilerState.PCUReadingError, UnitFileName); // ошибка чтения PCU #if DEBUG if (!InternalDebug.SkipPCUErrors) throw new CompilerInternalError("PCUReader", e); #endif } string SourceFileName = FindSourceFileName(Path.ChangeExtension(UnitFileName, null), null, out _, previousUnitLanguage); if (SourceFileName == null) throw new ReadPCUError(UnitFileName); else UnitFileName = SourceFileName; } } return false; } private Dictionary AddDocumentationToNodes(CompilationUnit currentUnit, string text) { SyntaxTree.documentation_comment_list docCommentList = currentUnit.Language.DocParser.BuildTree(text); return new DocumentationConstructor().Construct(currentUnit.SyntaxTree, docCommentList); } private bool IsDocumentationNeeded(SyntaxTree.compilation_unit unitSyntaxTree) { if (project != null && project.generate_xml_doc) return true; if (unitSyntaxTree == null) return false; foreach (SyntaxTree.compiler_directive directive in unitSyntaxTree.compiler_directives) { if (string.Equals(directive.Name.text, "gendoc", StringComparison.CurrentCultureIgnoreCase) && string.Equals(directive.Directive.text, "true", StringComparison.CurrentCultureIgnoreCase)) { return true; } } return false; } private TreeRealization.unit_node_list buildImplementationUsesList(CompilationUnit cu) { TreeRealization.unit_node_list unl = new PascalABCCompiler.TreeRealization.unit_node_list(); unl.AddRange(cu.ImplementationUsedUnits); foreach (TreeRealization.unit_node un in cu.InterfaceUsedUnits) { if (un is TreeRealization.dot_net_unit_node) unl.AddElement(un, null); } return unl; } public void SavePCU(CompilationUnit Unit) { //#if DEBUG try { if (Unit.SyntaxTree != null && Unit.SyntaxTree is SyntaxTree.unit_module) { if (((SyntaxTree.unit_module)Unit.SyntaxTree).unit_name.HeaderKeyword == PascalABCCompiler.SyntaxTree.UnitHeaderKeyword.Library) return; foreach (SyntaxTree.compiler_directive cd in Unit.SyntaxTree.compiler_directives) if (cd.Name.text.ToLower() == StringConstants.compiler_directive_savepcu) if (!Convert.ToBoolean(cd.Directive.text)) return; } } catch { } //#endif PCUWriter writer = null; try { #if DEBUG if (InternalDebug.PCUGenerate) #endif if (CompilerOptions.SavePCU) if ((Unit.SemanticTree as TreeRealization.common_unit_node).namespaces.Count > 1 && Unit.SyntaxTree != null && Unit.State == UnitState.Compiled) { writer = new PCUWriter(this, pr_ChangeState); bool dbginfo = true;/*CompilerOptions.Debug*/ #if DEBUG dbginfo = InternalDebug.IncludeDebugInfoInPCU; #endif writer.SaveSemanticTree(Unit, Path.ChangeExtension(Unit.UnitFileName, CompilerOptions.CompiledUnitExtension), dbginfo); } } catch (Exception err) { //ErrorsList.Add(new Errors.CompilerInternalError(string.Format("Compiler.Compile[{0}]", Path.GetFileName(this.currentCompilationUnit.SyntaxTree.file_name)), err)); OnChangeCompilerState(this, CompilerState.PCUWritingError, Unit.UnitFileName); #if DEBUG if (!InternalDebug.SkipPCUErrors) throw new CompilerInternalError(string.Format("Compiler.Compile[{0}]", Path.GetFileName(this.currentCompilationUnit.SyntaxTree.file_name)), err); writer.RemoveSelf(); #endif } } public CompilationUnit ReadPCU(string FileName, ILanguage currentUnitLanguage) { if (CompilerOptions.ForIntellisense && false) { CompilationUnit unit = null; if (pcuCompilationUnits.ContainsKey(FileName)) { unit = pcuCompilationUnits[FileName]; return unit; } PCUReader pr = new PCUReader(this, pr_ChangeState); unit = pr.GetCompilationUnit(FileName, CompilerOptions.Debug, currentUnitLanguage); pcuCompilationUnits[FileName] = unit; return unit; } else { PCUReader pr = new PCUReader(this, pr_ChangeState); return pr.GetCompilationUnit(FileName, CompilerOptions.Debug, currentUnitLanguage); } } void pr_ChangeState(object Sender, PCUReaderWriterState State, object obj) { switch (State) { case PCUReaderWriterState.BeginReadTree: OnChangeCompilerState(this, CompilerState.ReadPCUFile, (Sender as PCUReader).FileName); break; case PCUReaderWriterState.EndReadTree: CompilationUnit cu = obj as CompilationUnit; cu.State = UnitState.Compiled; unitTable[(Sender as PCUReader).FileName] = cu; UnitsTopologicallySortedList.Add(cu); GetReferences(cu); break; case PCUReaderWriterState.EndSaveTree: OnChangeCompilerState(this, CompilerState.SavePCUFile, (Sender as PCUWriter).FileName); break; } } /* TreeRealization.common_unit_node cun11 = currentUnit.SemanticTree as TreeRealization.common_unit_node; if (cun11 != null) UnitsLogicallySortedList.AddElement(cun11);*/ static string standartAssemblyPath = Path.GetDirectoryName(System.Reflection.Assembly.GetAssembly(typeof(string)).ManifestModule.FullyQualifiedName); public static Dictionary standart_assembly_dict = new Dictionary(); static Compiler() { string[] ss = new string[] { "mscorlib.dll", "System.dll", "System.Core.dll", "System.Numerics.dll", "System.Windows.Forms.dll", "PABCRtl.dll" }; foreach (var x in ss) standart_assembly_dict[x] = get_standart_assembly_path(x); } public static string get_standart_assembly_path(string name) { name = name.Replace("%GAC%\\", ""); string ttn = System.IO.Path.GetFileNameWithoutExtension(name); string tn = Path.Combine(standartAssemblyPath, name); if (File.Exists(tn)) return tn; if (Environment.OSVersion.Platform != PlatformID.Unix && Environment.OSVersion.Platform != PlatformID.MacOSX) { string windir = Path.Combine(Environment.GetEnvironmentVariable("windir"), "Microsoft.NET"); tn = windir + @"\assembly\GAC_MSIL\"; tn += ttn + "\\"; System.IO.DirectoryInfo di = new System.IO.DirectoryInfo(tn); if (!di.Exists) { tn = windir + @"\assembly\GAC_64\"; tn += ttn + "\\"; di = new System.IO.DirectoryInfo(tn); if (!di.Exists) { tn = windir + @"\assembly\GAC_32\"; tn += ttn + "\\"; di = new System.IO.DirectoryInfo(tn); if (!di.Exists) { tn = windir + @"\assembly\GAC\"; tn += ttn + "\\"; di = new System.IO.DirectoryInfo(tn); if (!di.Exists) { windir = Environment.GetEnvironmentVariable("windir"); tn = windir + @"\assembly\GAC_MSIL\"; tn += ttn + "\\"; di = new System.IO.DirectoryInfo(tn); if (!di.Exists) { tn = windir + @"\assembly\GAC_64\"; tn += ttn + "\\"; di = new System.IO.DirectoryInfo(tn); if (!di.Exists) { tn = windir + @"\assembly\GAC_32\"; tn += ttn + "\\"; di = new System.IO.DirectoryInfo(tn); if (!di.Exists) { tn = windir + @"\assembly\GAC\"; tn += ttn + "\\"; di = new System.IO.DirectoryInfo(tn); if (!di.Exists) { return null; } } } } } } } } System.IO.DirectoryInfo[] diarr = di.GetDirectories(); tn = Path.Combine((diarr[0]).FullName, name); } else { string gac_path = "/usr/lib/mono/4.0/gac"; DirectoryInfo di = new DirectoryInfo(Path.Combine(gac_path, ttn)); if (di.Exists) { System.IO.DirectoryInfo[] diarr = di.GetDirectories(); tn = Path.Combine((diarr[diarr.Length - 1]).FullName, name); } else return null; } return tn; } public static string get_assembly_path(string name, bool search_for_intellisense) { //если явно задан каталог то ищем только там if (Environment.OSVersion.Platform != PlatformID.Unix && Environment.OSVersion.Platform != PlatformID.MacOSX && !name.StartsWith("%GAC%\\")) { if (Path.GetDirectoryName(name) != string.Empty) if (File.Exists(name)) return name; else return null; } if (Environment.OSVersion.Platform == PlatformID.Unix || Environment.OSVersion.Platform == PlatformID.MacOSX) { string SystemDirectory = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().ManifestModule.FullyQualifiedName); string SearchDirectory = Path.Combine(SystemDirectory, "Lib"); if (File.Exists(Path.Combine(Environment.CurrentDirectory, name))) return Path.Combine(Environment.CurrentDirectory, name); if (File.Exists(Path.Combine(SearchDirectory, name))) { File.Copy(Path.Combine(SearchDirectory, name), Path.Combine(Environment.CurrentDirectory, name), true); return Path.Combine(Environment.CurrentDirectory, name); } } if (!search_for_intellisense && !name.StartsWith("%GAC%\\")) { string dir = Environment.CurrentDirectory; if (File.Exists(Path.Combine(dir, name))) { return Path.Combine(dir, name); } } return get_standart_assembly_path(name); } public CompilationUnit ReadDLL(string FileName, SyntaxTree.SourceContext sc = null) { if (DLLCache.ContainsKey(FileName)) return DLLCache[FileName]; OnChangeCompilerState(this, CompilerState.ReadDLL, FileName); TreeRealization.using_namespace_list using_namespaces = new TreeRealization.using_namespace_list(); try { TreeRealization.dot_net_unit_node un = new TreeRealization.dot_net_unit_node(new NetHelper.NetScope(using_namespaces, /*System.Reflection.Assembly.LoadFrom(file_name)*/ PascalABCCompiler.NetHelper.NetHelper.LoadAssembly(FileName), SyntaxTreeToSemanticTreeConverter.SymbolTable)); CompilationUnit cu = new CompilationUnit(); cu.SemanticTree = un; //un.dotNetScope=new PascalABCCompiler.NetHelper.NetScope(using_namespaces, // System.Reflection.Assembly.LoadFrom(file_name),SyntaxTreeToSemanticTreeConverter.SymbolTable); DLLCache[FileName] = cu; return cu; } catch (ReflectionTypeLoadException e) { foreach (var assm in assemblyResolveScope.missingAssemblies) errorsList.Add(new AssemblyNotFound(currentCompilationUnit.UnitFileName, assm, sc)); /*Console.Error.WriteLine(e.Message); foreach (var eLoaderException in e.LoaderExceptions) { Console.Error.WriteLine(eLoaderException.Message); }*/ return null; } catch (Exception e) { Console.Error.WriteLine(e.Message); return null; } } /*public CompilationUnit RecompileUnit(string unit_name) { Console.WriteLine("recompile {0}", unit_name); currentUnit = new SyntaxTree.uses_unit_in(new SyntaxTree.string_const(program_folder + "\\" + unit_name+".pas")); CompileUnit(unitsFromUsesSection, compilationUnit); CompilationUnit compilationUnit = new CompilationUnit(); if (unitsFromUsesSection.Count != 0) compilationUnit.SemanticTree = unitsFromUsesSection[unitsFromUsesSection.Count - 1]; else return null; return compilationUnit; }*/ public bool NeedRecompiled(string pcu_name, string[] included, PCUReader pr, ILanguage currentUnitLanguage) { if (!Path.IsPathRooted(pcu_name)) throw new InvalidOperationException(); string pas_name = FindSourceFileName(Path.ChangeExtension(pcu_name, null), null, out _, currentUnitLanguage); var dir = Path.GetDirectoryName(pcu_name); if (UnitTable[Path.ChangeExtension(pas_name, null)] != null) return true; bool need = false; for (int i = 0; i < included.Length; i++) { //if (included[i].Contains("$")) // continue; var used_unit_fname = GetUnitFileName(Path.GetFileNameWithoutExtension(included[i]), included[i], dir, null, currentUnitLanguage); var used_unit_is_pcu = Path.GetExtension(used_unit_fname) == CompilerOptions.CompiledUnitExtension; if (!used_unit_is_pcu) { if (UnitTable[Path.ChangeExtension(used_unit_fname, null)] != null) return true; need = true; RecompileList.Add(pcu_name); } } if (need) return true; if (!SourceFileExists(pas_name)) return false; // NeedRecompiled вызывается уже после успешного прочтения заголовка модуля (иначе откуда included) //if (!File.Exists(pcu_name)) return true; if (File.GetLastWriteTime(pcu_name) < SourceFileGetLastWriteTime(pas_name)) return true; //Console.WriteLine("{0} {1}",name,RecompileList.Count); for (int i = 0; i < included.Length; i++) { string pcu_name2 = FindPCUFileName(included[i], dir, out _, currentUnitLanguage); //TODO: Спросить у Сащи насчет < и <=. if ((File.Exists(pcu_name2) && File.GetLastWriteTime(pcu_name) < File.GetLastWriteTime(pcu_name2) && !pr.AlreadyCompiled(pcu_name2))) { pr.AddAlreadyCompiledUnit(pcu_name2); return true; } if (RecompileList.Contains(pcu_name2)) { return true; } } return false; } public void ClearAll(bool close_pcu = true) { semanticTree = null; if (close_pcu) { PCUReader.CloseUnits(); PCUWriter.Clear(); } RecompileList.Clear(); UnitTable.Clear(); UnitsTopologicallySortedList.Clear(); //TreeRealization.PCUReturner.Clear(); BadNodesInSyntaxTree.Clear(); if (close_pcu) PCUReader.AllReaders.Clear(); project = null; StandardModules.Clear(); CompiledVariables.Clear(); if (assemblyResolveScope != null) assemblyResolveScope.Dispose(); assemblyResolveScope = null; if (!close_pcu) { SyntaxTreeToSemanticTreeConverter = new TreeConverter.SyntaxTreeToSemanticTreeConverter(); } //SystemLibrary.SystemLibrary.RestoreStandartNames(); } public CompilerType CompilerType { get { return CompilerType.Standart; } } public void Free() { } } }