Language interface change (#3356)

* Move standard language info assigning to LanguageInformation classes

* Move Language name in compilation_unit.Language variable assigning in BaseParser

* Documentation update
This commit is contained in:
AlexanderZemlyak 2025-11-07 19:43:29 +02:00 committed by GitHub
parent 21e2141e02
commit 7a59245eaf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 119 additions and 76 deletions

View file

@ -5,7 +5,6 @@ using PascalABCCompiler.Parsers;
using System.Collections.Generic;
using PascalABCCompiler.SyntaxTreeConverters;
using PascalABCCompiler.TreeConverter;
using PascalABCCompiler.SyntaxTree;
namespace Languages.Facade
{
@ -18,29 +17,27 @@ namespace Languages.Facade
/// <summary>
/// Все параметры должны быть не null (и не пустым массивом), кроме IDocParser в случае, если он не требуется
/// </summary>
public BaseLanguage(string name, string version, string copyright, ILanguageInformation languageInformation,
IParser parser, IDocParser docParser, List<ISyntaxTreeConverter> syntaxTreeConverters, bool applySyntaxTreeConvertersForIntellisense,
string[] filesExtensions, bool caseSensitive, string[] systemUnitNames)
public BaseLanguage(ILanguageInformation languageInformation,
IParser parser, IDocParser docParser, List<ISyntaxTreeConverter> syntaxTreeConverters)
{
this.Name = name;
this.Version = version;
this.Copyright = copyright;
this.LanguageInformation = languageInformation;
this.Parser = parser;
this.Parser.LanguageInformation = languageInformation;
this.DocParser = docParser;
this.SyntaxTreeConverters = syntaxTreeConverters;
this.ApplySyntaxTreeConvertersForIntellisense = applySyntaxTreeConvertersForIntellisense;
this.FilesExtensions = filesExtensions;
this.CaseSensitive = caseSensitive;
this.SystemUnitNames = systemUnitNames;
}
public virtual string Name { get; protected set; }
public string Name => LanguageInformation.Name;
public virtual string Version { get; protected set; }
public string Version => LanguageInformation.Version;
public virtual string Copyright { get; protected set; }
public string Copyright => LanguageInformation.Copyright;
public string[] FilesExtensions => LanguageInformation.FilesExtensions;
public bool CaseSensitive => LanguageInformation.CaseSensitive;
public string[] SystemUnitNames => LanguageInformation.SystemUnitNames;
public virtual ILanguageInformation LanguageInformation { get; }
@ -50,16 +47,10 @@ namespace Languages.Facade
public virtual List<ISyntaxTreeConverter> SyntaxTreeConverters { get; protected set; }
public virtual bool ApplySyntaxTreeConvertersForIntellisense { get; protected set; }
public bool ApplySyntaxTreeConvertersForIntellisense => LanguageInformation.ApplySyntaxTreeConvertersForIntellisense;
public virtual syntax_tree_visitor SyntaxTreeToSemanticTreeConverter { get; protected set; }
public virtual string[] FilesExtensions { get; protected set; }
public virtual bool CaseSensitive { get; protected set; }
public virtual string[] SystemUnitNames { get; protected set; }
public abstract void SetSemanticConstants();
public abstract void SetSyntaxTreeToSemanticTreeConverter();

View file

@ -45,6 +45,9 @@ namespace Languages.Facade
/// </summary>
List<ISyntaxTreeConverter> SyntaxTreeConverters { get; }
/// <summary>
/// Вызывать ли преобразователей синтаксического дерева в работе Intellisense
/// </summary>
bool ApplySyntaxTreeConvertersForIntellisense { get; }
/// <summary>

View file

@ -9,20 +9,12 @@ namespace Languages.SPython
public class SPythonLanguage : BaseLanguage
{
public SPythonLanguage() : base(
name: "SPython",
version: "0.0.1",
copyright: "Copyright © 2023-2025 by Vladislav Krylov, Egor Movchan",
languageInformation: new Frontend.Data.SPythonLanguageInformation(),
parser: new SPythonParser.SPythonLanguageParser(),
docParser: null,
syntaxTreeConverters: new List<ISyntaxTreeConverter>() { new Frontend.Converters.StandardSyntaxTreeConverter(), new SyntaxSemanticVisitors.LambdaAnyConverter() },
applySyntaxTreeConvertersForIntellisense: true,
filesExtensions: new string[] { ".pys" },
caseSensitive: true,
systemUnitNames: new string[] { "SPythonSystem", "SPythonHidden", "SPythonSystemPys" }
syntaxTreeConverters: new List<ISyntaxTreeConverter>() { new Frontend.Converters.StandardSyntaxTreeConverter(), new SyntaxSemanticVisitors.LambdaAnyConverter() }
)
{
((SPythonParser.SPythonLanguageParser)Parser).SyntaxTreeConvertersForIntellisense = SyntaxTreeConverters;

View file

@ -1,4 +1,5 @@
using PascalABCCompiler.Parsers;
using PascalABCCompiler;
using PascalABCCompiler.Parsers;
using PascalABCCompiler.ParserTools.Directives;
using PascalABCCompiler.SyntaxTree;
using System;
@ -11,6 +12,18 @@ namespace Languages.SPython.Frontend.Data
{
internal class SPythonLanguageInformation : BaseLanguageInformation
{
public override string Name => "SPython";
public override string Version => "0.0.1";
public override string Copyright => "Copyright © 2023-2025 by Vladislav Krylov, Egor Movchan";
public override string[] FilesExtensions => new string[] { ".pys" };
public override string[] SystemUnitNames => new string[] { "SPythonSystem", "SPythonHidden", "SPythonSystemPys" };
public override bool ApplySyntaxTreeConvertersForIntellisense => true;
public override BaseKeywords KeywordsStorage { get; } = new SPythonParser.SPythonKeywords();
public override Dictionary<string, DirectiveInfo> ValidDirectives { get; protected set; }

View file

@ -1116,7 +1116,6 @@ optional_semicolon
public program_module NewProgramModule(program_name progName, Object optHeadCompDirs, uses_list mainUsesClose, syntax_tree_node progBlock, Object optPoint, LexLocation loc)
{
var progModule = new program_module(progName, mainUsesClose, progBlock as block, null, loc);
progModule.Language = "SPython";
if (optPoint == null && progBlock != null)
{
var fp = progBlock.source_context.end_position;

View file

@ -4,7 +4,7 @@
// GPPG version 1.3.6
// Machine: DESKTOP-V3E9T2U
// DateTime: 14.09.2025 15:38:38
// DateTime: 07.11.2025 13:33:42
// UserName: alex
// Input file <SPythonParser.y>
@ -1649,7 +1649,6 @@ public partial class SPythonGPPGParser: ShiftReduceParser<ValueType, LexLocation
public program_module NewProgramModule(program_name progName, Object optHeadCompDirs, uses_list mainUsesClose, syntax_tree_node progBlock, Object optPoint, LexLocation loc)
{
var progModule = new program_module(progName, mainUsesClose, progBlock as block, null, loc);
progModule.Language = "SPython";
if (optPoint == null && progBlock != null)
{
var fp = progBlock.source_context.end_position;

View file

@ -10,6 +10,18 @@ namespace PascalABCCompiler.Parsers
{
public abstract class BaseLanguageInformation : ILanguageInformation
{
public abstract string Name { get; }
public abstract string Version { get; }
public abstract string Copyright { get; }
public abstract bool CaseSensitive { get; }
public abstract string[] FilesExtensions { get; }
public abstract string[] SystemUnitNames { get; }
public abstract BaseKeywords KeywordsStorage { get; }
public abstract Dictionary<string, DirectiveInfo> ValidDirectives { get; protected set; }
@ -32,7 +44,7 @@ namespace PascalABCCompiler.Parsers
public abstract string ProcedureName { get; }
public abstract string FunctionName { get; }
public abstract bool CaseSensitive { get; }
public abstract bool ApplySyntaxTreeConvertersForIntellisense { get; }
public abstract bool IncludeDotNetEntities { get; }

View file

@ -3,7 +3,6 @@
using System.Collections.Generic;
using PascalABCCompiler.Errors;
using PascalABCCompiler.SyntaxTree;
using System;
namespace PascalABCCompiler.Parsers
{
@ -109,6 +108,7 @@ namespace PascalABCCompiler.Parsers
if (root != null && root is compilation_unit compilationUnit)
{
compilationUnit.Language = LanguageInformation.Name;
compilationUnit.file_name = FileName;
compilationUnit.compiler_directives = CompilerDirectives;

View file

@ -11,6 +11,36 @@ namespace PascalABCCompiler.Parsers
/// </summary>
public interface ILanguageInformation
{
/// <summary>
/// Название языка
/// </summary>
string Name { get; }
/// <summary>
/// Версия языка
/// </summary>
string Version { get; }
/// <summary>
/// Авторское право
/// </summary>
string Copyright { get; }
/// <summary>
/// Чувствительность к регистру
/// </summary>
bool CaseSensitive { get; }
/// <summary>
/// Расширения файлов, относящиеся к языку
/// </summary>
string[] FilesExtensions { get; }
/// <summary>
/// Названия системных модулей (стандартной библиотеки и др.)
/// </summary>
string[] SystemUnitNames { get; }
/// <summary>
/// Получить полное описание элемента (в желтой подсказке)
/// </summary>
@ -140,6 +170,11 @@ namespace PascalABCCompiler.Parsers
int FindParamDelimForIndexer(string descriptionAfterOpeningParenthesis, int number);
/// <summary>
/// Вызывать ли преобразователей синтаксического дерева в работе Intellisense
/// </summary>
bool ApplySyntaxTreeConvertersForIntellisense { get; }
Dictionary<string, string> SpecialModulesAliases { get; }
BaseKeywords KeywordsStorage
@ -172,10 +207,6 @@ namespace PascalABCCompiler.Parsers
{
get;
}
bool CaseSensitive
{
get;
}
bool IncludeDotNetEntities
{
get;

View file

@ -427,16 +427,14 @@ unit_file
unit_header interface_part implementation_part initialization_part tkPoint
{
$$ = new unit_module($1 as unit_name, $2 as interface_node, $3 as implementation_node,
($4 as initfinal_part).initialization_sect, ($4 as initfinal_part).finalization_sect, /*$1 as attribute_list*/ null, @$);
($$ as compilation_unit).Language = PascalABCCompiler.StringConstants.pascalLanguageName;
($4 as initfinal_part).initialization_sect, ($4 as initfinal_part).finalization_sect, /*$1 as attribute_list*/ null, @$);
}
|
//attribute_declarations
unit_header abc_interface_part initialization_part tkPoint
{
$$ = new unit_module($1 as unit_name, $2 as interface_node, null,
($3 as initfinal_part).initialization_sect, ($3 as initfinal_part).finalization_sect, /*$1 as attribute_list*/ null, @$);
($$ as compilation_unit).Language = PascalABCCompiler.StringConstants.pascalLanguageName;
($3 as initfinal_part).initialization_sect, ($3 as initfinal_part).finalization_sect, /*$1 as attribute_list*/ null, @$);
}
;

View file

@ -1,9 +1,9 @@
// (see accompanying GPPGcopyright.rtf)
// GPPG version 1.3.6
// Machine: DESKTOP-L2INHKG
// DateTime: 07.10.2025 10:49:02
// UserName: user
// Machine: DESKTOP-V3E9T2U
// DateTime: 07.11.2025 13:32:32
// UserName: alex
// Input file <ABCPascal.y>
// options: no-lines gplex
@ -3146,15 +3146,13 @@ public partial class GPPGParser: ShiftReduceParser<PascalABCCompiler.ParserTools
// initialization_part, tkPoint
{
CurrentSemanticValue.stn = new unit_module(ValueStack[ValueStack.Depth-5].stn as unit_name, ValueStack[ValueStack.Depth-4].stn as interface_node, ValueStack[ValueStack.Depth-3].stn as implementation_node,
(ValueStack[ValueStack.Depth-2].stn as initfinal_part).initialization_sect, (ValueStack[ValueStack.Depth-2].stn as initfinal_part).finalization_sect, /*$1 as attribute_list*/ null, CurrentLocationSpan);
(CurrentSemanticValue.stn as compilation_unit).Language = PascalABCCompiler.StringConstants.pascalLanguageName;
(ValueStack[ValueStack.Depth-2].stn as initfinal_part).initialization_sect, (ValueStack[ValueStack.Depth-2].stn as initfinal_part).finalization_sect, /*$1 as attribute_list*/ null, CurrentLocationSpan);
}
break;
case 48: // unit_file -> unit_header, abc_interface_part, initialization_part, tkPoint
{
CurrentSemanticValue.stn = new unit_module(ValueStack[ValueStack.Depth-4].stn as unit_name, ValueStack[ValueStack.Depth-3].stn as interface_node, null,
(ValueStack[ValueStack.Depth-2].stn as initfinal_part).initialization_sect, (ValueStack[ValueStack.Depth-2].stn as initfinal_part).finalization_sect, /*$1 as attribute_list*/ null, CurrentLocationSpan);
(CurrentSemanticValue.stn as compilation_unit).Language = PascalABCCompiler.StringConstants.pascalLanguageName;
(ValueStack[ValueStack.Depth-2].stn as initfinal_part).initialization_sect, (ValueStack[ValueStack.Depth-2].stn as initfinal_part).finalization_sect, /*$1 as attribute_list*/ null, CurrentLocationSpan);
}
break;
case 49: // unit_header -> unit_key_word, unit_name, tkSemiColon,

View file

@ -46,7 +46,6 @@ namespace Languages.Pascal.Frontend.Core
public program_module NewProgramModule(program_name progName, Object optHeadCompDirs, uses_list mainUsesClose, syntax_tree_node progBlock, Object optPoint, LexLocation loc)
{
var progModule = new program_module(progName, mainUsesClose, progBlock as block, null, loc);
progModule.Language = PascalABCCompiler.StringConstants.pascalLanguageName;
if (optPoint == null && progBlock != null)
{
var fp = progBlock.source_context.end_position;

View file

@ -3,7 +3,6 @@
using System.Collections.Generic;
using Languages.Facade;
using PascalABCCompiler;
using PascalABCCompiler.SyntaxTreeConverters;
using PascalABCCompiler.SystemLibrary;
using PascalABCCompiler.TreeConverter;
@ -14,20 +13,12 @@ namespace Languages.Pascal
{
public PascalABCLanguage() : base(
name: StringConstants.pascalLanguageName,
version: "1.2",
copyright: "Copyright © 2005-2025 by Ivan Bondarev, Stanislav Mikhalkovich",
languageInformation: new Frontend.Data.PascalABCLanguageInformation(),
parser: new Frontend.Wrapping.PascalABCNewLanguageParser(),
docParser: new Frontend.Documentation.PascalDocTagsLanguageParser(),
syntaxTreeConverters: new List<ISyntaxTreeConverter>() { new Frontend.Converters.StandardSyntaxTreeConverter(), new SyntaxSemanticVisitors.LambdaAnyConverter() },
applySyntaxTreeConvertersForIntellisense: false,
filesExtensions: new string[] { StringConstants.pascalSourceFileExtension },
caseSensitive: false,
systemUnitNames: StringConstants.pascalDefaultStandardModules
syntaxTreeConverters: new List<ISyntaxTreeConverter>() { new Frontend.Converters.StandardSyntaxTreeConverter(), new SyntaxSemanticVisitors.LambdaAnyConverter() }
) { }

View file

@ -16,6 +16,18 @@ namespace Languages.Pascal.Frontend.Data
{
public class PascalABCLanguageInformation : BaseLanguageInformation
{
public override string Name => StringConstants.pascalLanguageName;
public override string Version => "1.2";
public override string Copyright => "Copyright © 2005-2025 by Ivan Bondarev, Stanislav Mikhalkovich";
public override string[] FilesExtensions => new string[] { StringConstants.pascalSourceFileExtension };
public override string[] SystemUnitNames => StringConstants.pascalDefaultStandardModules;
public override bool ApplySyntaxTreeConvertersForIntellisense => false;
protected IParser Parser
{
get

View file

@ -1,5 +1,5 @@
### Шаг 1
Необходимо создать папку, называющуюся так же, как Ваш язык в папке *LanguagePlugins*, расположенной в корне репозитория. Далее создать в ней два обязательных проекта. Это проект парсера Вашего языка, а также проект, содержащий класс Вашего языка (унаследованный от класса `BaseLanguage`, находящегося в стандартном проекте `LanguageIntegrator`).
### Шаг 1
Необходимо создать папку, называющуюся так же, как Ваш язык в папке *LanguagePlugins*, расположенной в корне репозитория. Далее создать в ней два обязательных проекта. Это проект парсера Вашего языка, а также проект, содержащий класс Вашего языка (унаследованный от класса `BaseLanguage`, находящегося в стандартном проекте `LanguageIntegrator`) и класс "базы данных" языка (унаследованный от класса `BaseLanguageInformation` из проекта `ParserTools`).
Для совместимости целевой платформой Ваших проектов должна быть *.NET Framework 4.0*. В свойствах проекта и для Release, и для Debug версии должен быть выбран выходной путь `..\..\..\bin\`.
@ -7,7 +7,7 @@
> Чтобы платформа PascalABC.NET смогла подключить библиотеку с Вашим языком нужно, чтобы имя Вашей основной сборки (содержащей наследника `BaseLanguage`) заканчивалось на "LanguageInfo" (то есть имело вид \*LanguageInfo.dll). Имя наследника `BaseLanguage` должно иметь постфикс "Language".
### Шаг 2
В проекты Вы добавляете все необходимые для работы ссылки. Вам точно понадобятся ссылки на проекты `ParserTools`, `Errors`, `SyntaxTree` в проекте Вашего парсера и на проекты `LanguageIntegrator`, `SyntaxVisitors`, `TreeConverter` в проекте с наследником `BaseLanguage`.
В проекты Вы добавляете все необходимые для работы ссылки. Вам точно понадобятся ссылки на проекты `ParserTools`, `Errors`, `SyntaxTree` в проекте Вашего парсера и на проекты `LanguageIntegrator`, `ParserTools`, `SyntaxTree` в проекте с наследником `BaseLanguage`.
### Шаг 3
Вам нужно унаследовать Ваш парсер (который является оберткой над GPPG парсером) от класса `BaseParser`, либо напрямую реализовать интерфейс `IParser`, если Вы не хотите пользоваться функциональностью из `BaseParser`.
@ -16,21 +16,26 @@
Для реализации `BuildTreeInNormalMode` Вам потребуется вызвать в этом методе сгенерированный GPPG парсер Вашего языка. Возвращаемое значение должно быть корнем полученного в результате парсинга синтаксического дерева.
Для реализации парсера Вы можете также использовать такие базовые абстрактные классы как `BaseKeywords` и `BaseParserTools`.
### Шаг 4
В сделанном Вами наследнике `BaseLanguage` Вы пишете конструктор, который передает в `base()` следующие параметры:
Для реализации методов в наследнике `BaseLanguageInformation` на первое время можно взять методы из `PascalABCLanguageInformation`, они используются в системе *Intellisense*. Но Вам также нужно задать следующие параметры языка:
1) имя языка, версия языка, строка с указанием авторских прав
2) класс "базы данных" языка, хранящий информацию в основном необходимую для Intellisense (наследник `BaseLanguageInformation`). На первое время можно использовать PascalABCLangaugeInformation.
3) парсер Вашего языка (наследник `BaseParser`)
4) парсер XML-комментариев (если его нет, то можно передавать `null`)
5) список преобразователей синтаксического дерева с одним обязательным элементом (для него можно взять стандартную базовую версию под названием `DefaultSyntaxTreeConverter`)
6) флаг дающий системе Intellisense информацию о том, нужно ли запускать преобразователи синтаксического дерева
7) расширения файлов Вашего языка
8) чувствительность к регистру
9) имена стандартных модулей языка
2) расширения файлов Вашего языка
3) имена стандартных модулей языка
4) чувствительность к регистру
и т. д.
### Шаг 5
В сделанном Вами наследнике `BaseLanguage` Вы пишете конструктор, который передает в `base()` следующие параметры:
1) класс "базы данных" языка, хранящий информацию в основном необходимую для Intellisense (наследник `BaseLanguageInformation`).
2) парсер Вашего языка (наследник `BaseParser`)
3) парсер XML-комментариев (если его нет, то можно передавать `null`)
4) список преобразователей синтаксического дерева с одним обязательным элементом (для него можно взять стандартную базовую версию под названием `DefaultSyntaxTreeConverter`)
Также Вы реализуете метод `SetSemanticConstants`, в котором задаете необходимые параметры (значения переменных) из файла *SemanticRulesConstants.cs* (проект TreeConverter).
И наконец, реализуете метод `SetSyntaxTreeToSemanticTreeConverter`, в котором создаете новый экземпляр преобразователя синтаксического дерева в семантическое (присваиваете его свойству `SyntaxTreeToSemanticTreeConverter`). Для простых языков в качестве преобразователя Вам подойдет стандартный `syntax_tree_visitor`.
И наконец, реализуете метод `SetSyntaxTreeToSemanticTreeConverter`, в котором создаете новый экземпляр преобразователя синтаксического дерева в семантическое (присваиваете его свойству `SyntaxTreeToSemanticTreeConverter`). Для простых языков в качестве преобразователя можно присваивать
экземпляр класса `syntax_tree_visitor`, получив его следующим образом: `LanguageProvider.Instance.MainLanguage.SyntaxTreeToSemanticTreeConverter`.
---
Если описанные выше шаги проделаны правильно, то при запуске оболочки в окне сообщений компилятора Вы увидите информацию о Вашем языке в списке подключенных. Если же в этом окне будет выводиться ошибка, связанная с подключением сборки Вашего языка, то обратитесь к файлу *log.txt* в папке `bin` (запись в лог ведется только в режиме Debug).