// 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 PascalABCCompiler.CoreUtils;
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()
{
}
}
}