Исправление переменных с именем, совпадающим с именами сущностей из других модулей, в SPython (#3360)

* Fix variables with special names declaration in SPython

* Add new syntax tree converters applying flag checking in Intellisense

* Fix IsVariableOrConstant function in Intellisense

* Optimize CollectNamesFromUsedUnits function in Compiler
This commit is contained in:
Александр Земляк 2025-11-29 14:27:00 +03:00 committed by GitHub
parent c54412c8aa
commit 4e871c1f3a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 110 additions and 51 deletions

View file

@ -2718,7 +2718,7 @@ namespace CodeCompletion
}
}
if (currentUnitLanguage.ApplySyntaxTreeConvertersForIntellisense)
if (currentUnitLanguage.ApplySyntaxTreeConvertersForIntellisense && currentUnitLanguage.LanguageInformation.SyntaxTreeIsConvertedAfterUsedModulesCompilation)
{
var namesFromUsedUnits = CollectNamesFromUsedUnits(cur_scope);
@ -2791,36 +2791,48 @@ namespace CodeCompletion
}
private Dictionary<string, HashSet<string>> CollectNamesFromUsedUnits(SymScope currentUnitScope)
private Dictionary<string, Dictionary<string, bool>> CollectNamesFromUsedUnits(SymScope currentUnitScope)
{
var namesFromUsedUnits = new Dictionary<string, HashSet<string>>();
var namesFromUsedUnits = new Dictionary<string, Dictionary<string, bool>>();
bool IsVariableOrConstant(SymbolKind symKind)
{
return symKind == SymbolKind.Variable
|| symKind == SymbolKind.Constant
|| symKind == SymbolKind.Event;
}
foreach (var unitScope in currentUnitScope.used_units)
{
if (unitScope is InterfaceUnitScope interfaceScope)
{
namesFromUsedUnits.Add(unitScope.Name, new HashSet<string>());
namesFromUsedUnits.Add(unitScope.Name, new Dictionary<string, bool>());
foreach (var symbol in unitScope.symbol_table)
{
var scopesOrScope = ((DictionaryEntry)symbol).Value;
string name;
bool isVariable;
if (scopesOrScope is List<SymScope> scopes)
{
name = scopes[0].Name;
isVariable = IsVariableOrConstant(scopes[0].SymbolInfo.Kind);
}
else
{
name = ((SymScope)scopesOrScope).Name;
var scope = (SymScope)scopesOrScope;
name = scope.Name;
isVariable = IsVariableOrConstant(scope.SymbolInfo.Kind);
}
if (name == unitScope.Name)
continue;
namesFromUsedUnits[unitScope.Name].Add(name);
namesFromUsedUnits[unitScope.Name][name] = isVariable;
}
}
}
@ -3287,7 +3299,7 @@ namespace CodeCompletion
}
}
if (currentUnitLanguage.ApplySyntaxTreeConvertersForIntellisense)
if (currentUnitLanguage.ApplySyntaxTreeConvertersForIntellisense && currentUnitLanguage.LanguageInformation.SyntaxTreeIsConvertedAfterUsedModulesCompilation)
{
var namesFromUsedUnits = CollectNamesFromUsedUnits(cur_scope);

View file

@ -12,7 +12,7 @@ def DrawStatusText():
StatusRect.Text = ('Удалено квадратов: ' + str(CurrentDigit-1)
+ '\tОшибок: ' + str(Mistakes))
else:
StatusRect.Text = ('Игра окончена. Время: ' + str(Milliseconds // 1000)
StatusRect.Text = ('Игра окончена. Время: ' + str(Milliseconds() // 1000)
+ ' с.\tОшибок: ' + str(Mistakes))

View file

@ -141,12 +141,12 @@
#define DEBUG
using Languages.Facade;
using PascalABCCompiler.Errors;
using PascalABCCompiler.PCU;
using PascalABCCompiler.SemanticTreeConverters;
using PascalABCCompiler.SyntaxTreeConverters;
using PascalABCCompiler.TreeRealization;
using System;
using System.CodeDom.Compiler;
using System.Collections;
@ -155,8 +155,6 @@ using System.IO;
using System.Linq;
using System.Reflection;
using Languages.Facade;
namespace PascalABCCompiler
{
public enum UnitState { BeginCompilation, InterfaceCompiled, Compiled }
@ -240,8 +238,10 @@ namespace PascalABCCompiler
/// <summary>
/// Глобальные имена сущностей из зависимостей интерфейсной части
/// Внешний словарь по имени модуля выдает словарь сущностей.
/// Внутренний словарь по имени сущности выдает флаг о том, является ли она переменной.
/// </summary>
public Dictionary<string, HashSet<string>> NamesFromUsedUnits { get; } = new Dictionary<string, HashSet<string>>();
public Dictionary<string, Dictionary<string, bool>> NamesFromUsedUnits { get; } = new Dictionary<string, Dictionary<string, bool>>();
public UnitState State = UnitState.BeginCompilation;
}
@ -3019,11 +3019,14 @@ namespace PascalABCCompiler
//Console.WriteLine("Compiling Interface "+ unitFileName);//DEBUG
// заполняем currentUnit.NamesFromUsedUnits
CollectNamesFromUsedUnits(currentUnit);
// конвертация синтаксического дерева с использованием данных из откомпилированных зависимостей
ConvertSyntaxTreeAfterUsedModulesCompilation(currentUnit);
if (currentUnit.Language.LanguageInformation.SyntaxTreeIsConvertedAfterUsedModulesCompilation)
{
// заполняем currentUnit.NamesFromUsedUnits
CollectNamesFromUsedUnits(currentUnit);
// конвертация синтаксического дерева с использованием данных из откомпилированных зависимостей
ConvertSyntaxTreeAfterUsedModulesCompilation(currentUnit);
}
// компилируем интерфейс текущего модуля EVA
CompileCurrentUnitInterface(unitFileName, currentUnit, docs);
@ -3115,10 +3118,34 @@ namespace PascalABCCompiler
string unitName = Path.GetFileNameWithoutExtension(unit.UnitFileName);
currentUnit.NamesFromUsedUnits.Add(unitName, new HashSet<string>());
foreach (var names in (unit.SemanticTree as common_unit_node).scope.Symbols.DictCaseSensitive.Skip(1))
currentUnit.NamesFromUsedUnits.Add(unitName, new Dictionary<string, bool>());
var unitScope = (unit.SemanticTree as common_unit_node).scope;
foreach (var names in unitScope.Symbols.DictCaseSensitive.Skip(1))
{
currentUnit.NamesFromUsedUnits[unitName].Add(names.Key);
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);
}
}
}

View file

@ -22,6 +22,8 @@ namespace Languages.SPython.Frontend.Data
public override string[] SystemUnitNames => new string[] { "SPythonSystem", "SPythonHidden", "SPythonSystemPys" };
public override bool SyntaxTreeIsConvertedAfterUsedModulesCompilation => true;
public override bool ApplySyntaxTreeConvertersForIntellisense => true;
public override BaseKeywords KeywordsStorage { get; } = new SPythonParser.SPythonKeywords();

View file

@ -7,7 +7,7 @@ namespace Languages.SPython.Frontend.Converters
{
public HashSet<string> variablesUsedAsGlobal = new HashSet<string>();
public NameCorrectVisitor(Dictionary<string, HashSet<string>> namesFromUsedUnits, HashSet<string> definedFunctionsNames) : base(namesFromUsedUnits)
public NameCorrectVisitor(Dictionary<string, Dictionary<string, bool>> namesFromUsedUnits, HashSet<string> definedFunctionsNames) : base(namesFromUsedUnits)
{
foreach (string definedFunctionName in definedFunctionsNames)
{
@ -60,7 +60,8 @@ namespace Languages.SPython.Frontend.Converters
switch (nameType)
{
case NameKind.GlobalVariable:
case NameKind.ImportedNameAlias:
case NameKind.ImportedVariableAlias:
case NameKind.ImportedNotVariableAlias:
break;
case NameKind.Unknown:
throw new SPythonSyntaxVisitorError("UNKNOWN_NAME_{0}",
@ -91,7 +92,8 @@ namespace Languages.SPython.Frontend.Converters
id.name = symbolTable.AliasToRealName(id.name);
break;
case NameKind.ImportedNameAlias:
case NameKind.ImportedVariableAlias:
case NameKind.ImportedNotVariableAlias:
_named_type_reference.names.Insert(0, new ident(symbolTable.AliasToModuleName(id.name), id.source_context));
_named_type_reference.names[1].name = symbolTable.AliasToRealName(_named_type_reference.names[1].name);
break;
@ -114,7 +116,8 @@ namespace Languages.SPython.Frontend.Converters
switch (nameKind)
{
case NameKind.ImportedNameAlias:
case NameKind.ImportedVariableAlias:
case NameKind.ImportedNotVariableAlias:
_named_type_reference.names.Insert(0, new ident(symbolTable.AliasToModuleName(fullName), _named_type_reference.names[0].source_context));
_named_type_reference.names[1].name = symbolTable.AliasToRealName(fullName);
break;
@ -135,7 +138,8 @@ namespace Languages.SPython.Frontend.Converters
_ident.name = symbolTable.AliasToRealName(_ident.name);
break;
case NameKind.ImportedNameAlias:
case NameKind.ImportedVariableAlias:
case NameKind.ImportedNotVariableAlias:
Replace(_ident, new dot_node(new ident(symbolTable.AliasToModuleName(_ident.name), sc)
, new ident(symbolTable.AliasToRealName(_ident.name), sc), sc));
break;

View file

@ -10,7 +10,7 @@ namespace Languages.SPython.Frontend.Converters
{
protected SymbolTable symbolTable;
public SymbolTableFillingVisitor(Dictionary<string, HashSet<string>> par) {
public SymbolTableFillingVisitor(Dictionary<string, Dictionary<string, bool>> par) {
symbolTable = new SymbolTable(par);
}
@ -164,18 +164,20 @@ namespace Languages.SPython.Frontend.Converters
if (_from_import_statement.is_star)
{
foreach (string name in symbolTable.ModuleNameToSymbols[module_name])
symbolTable.AddAlias(name, name, module_name);
foreach (var kv in symbolTable.ModuleNameToSymbols[module_name])
symbolTable.AddAlias(kv.Key, kv.Value, kv.Key, module_name);
}
else
{
foreach (as_statement as_Statement in _from_import_statement.imported_names.as_statements)
{
if (!symbolTable.ModuleNameToSymbols[module_name].Contains(as_Statement.real_name.name))
var namesDict = symbolTable.ModuleNameToSymbols[module_name];
if (!namesDict.ContainsKey(as_Statement.real_name.name))
throw new SPythonSyntaxVisitorError("MODULE_{0}_HAS_NO_NAME_{1}",
_from_import_statement.source_context, module_real_name, as_Statement.real_name.name);
symbolTable.AddAlias(as_Statement.real_name.name, as_Statement.alias.name, module_name);
symbolTable.AddAlias(as_Statement.real_name.name, namesDict[as_Statement.real_name.name], as_Statement.alias.name, module_name);
}
}
}
@ -196,21 +198,23 @@ namespace Languages.SPython.Frontend.Converters
protected enum NameKind
{
// Имя отсутствует в текущем контексте
Unknown = 0b_0000_0000,
Unknown = 0b_0000_0000,
// Ключевые слова языка
Keyword = 0b_0000_0001,
Keyword = 0b_0000_0001,
// Имя глобальной переменной
GlobalVariable = 0b_0000_0010,
GlobalVariable = 0b_0000_0010,
// Имя глобальной функции
GlobalFunction = 0b_0000_0100,
// Имя подключённого модуля или его псевдоним
ModuleAlias = 0b_0000_1000,
// Имя, подключённое из модуля, или его псевдоним
ImportedNameAlias = 0b_0001_0000,
GlobalFunction = 0b_0000_0100,
// Имя подключённого модуля или его псевдоним
ModuleAlias = 0b_0000_1000,
// Имя, подключённое из модуля (не переменная или константа), или его псевдоним
ImportedNotVariableAlias = 0b_0001_0000,
// Подключенная из модуля константа или переменная
ImportedVariableAlias = 0b_0010_0000,
// Локальня переменная
LocalVariable = 0b_0010_0000,
LocalVariable = 0b_0100_0000,
// Имя глобальной forward-объявленной функции
ForwardDeclaredFunction = 0b_0100_0000,
ForwardDeclaredFunction = 0b_1000_0000,
}
protected class SymbolTable
@ -230,7 +234,7 @@ namespace Languages.SPython.Frontend.Converters
nameTypes.Add(keyword, NameKind.Keyword);
}
public SymbolTable(Dictionary<string, HashSet<string>> par)
public SymbolTable(Dictionary<string, Dictionary<string, bool>> par)
{
ModuleNameToSymbols = par;
FillKeywords();
@ -275,9 +279,9 @@ namespace Languages.SPython.Frontend.Converters
private LocalScope localVariables = new LocalScope();
// moduleRealName -> functions and global variables in this module real names
private Dictionary<string, HashSet<string>> moduleNameToSymbols;
private Dictionary<string, Dictionary<string, bool>> moduleNameToSymbols;
public Dictionary<string, HashSet<string>> ModuleNameToSymbols
public Dictionary<string, Dictionary<string, bool>> ModuleNameToSymbols
{
get => moduleNameToSymbols;
set
@ -289,8 +293,8 @@ namespace Languages.SPython.Frontend.Converters
foreach (string standardLibrary in StandardLibraries)
{
if (moduleNameToSymbols.ContainsKey(standardLibrary))
foreach (string name in moduleNameToSymbols[standardLibrary])
AddAlias(name, name, standardLibrary);
foreach (var kv in moduleNameToSymbols[standardLibrary])
AddAlias(kv.Key, kv.Value, kv.Key, standardLibrary);
}
}
@ -315,10 +319,11 @@ namespace Languages.SPython.Frontend.Converters
public bool IsVisibleToAssign(string name)
{
NameKind kind = this[name];
if (kind == NameKind.Unknown) return false;
if (kind == NameKind.Unknown || kind == NameKind.ImportedNotVariableAlias) return false;
if (!isInFunctionBody) return true;
return (kind != NameKind.GlobalVariable &&
kind != NameKind.ImportedNameAlias) ||
kind != NameKind.ImportedVariableAlias) ||
NamesAddedByGlobal.Contains(name);
}
@ -363,12 +368,12 @@ namespace Languages.SPython.Frontend.Converters
else nameTypes.Add(name, nameType);
}
public void AddAlias(string realName, string alias, string moduleName)
public void AddAlias(string realName, bool isVariable, string alias, string moduleName)
{
if (aliasToRealNameAndModuleName.ContainsKey(alias))
aliasToRealNameAndModuleName[alias] = Tuple.Create(realName, moduleName);
else aliasToRealNameAndModuleName.Add(alias, Tuple.Create(realName, moduleName));
AddGlobalName(alias, NameKind.ImportedNameAlias);
AddGlobalName(alias, isVariable ? NameKind.ImportedVariableAlias : NameKind.ImportedNotVariableAlias);
}
public void AddModuleAlias(string realName, string alias)

View file

@ -44,6 +44,8 @@ namespace PascalABCCompiler.Parsers
public abstract string ProcedureName { get; }
public abstract string FunctionName { get; }
public abstract bool SyntaxTreeIsConvertedAfterUsedModulesCompilation { get; }
public abstract bool ApplySyntaxTreeConvertersForIntellisense { get; }
public abstract bool IncludeDotNetEntities { get; }

View file

@ -170,6 +170,11 @@ namespace PascalABCCompiler.Parsers
int FindParamDelimForIndexer(string descriptionAfterOpeningParenthesis, int number);
/// <summary>
/// Нужно ли вызывать конверторы синтаксического дерева, срабатывающие после компиляции зависимостей
/// </summary>
bool SyntaxTreeIsConvertedAfterUsedModulesCompilation { get; }
/// <summary>
/// Вызывать ли преобразователей синтаксического дерева в работе Intellisense
/// </summary>

View file

@ -26,6 +26,8 @@ namespace Languages.Pascal.Frontend.Data
public override string[] SystemUnitNames => StringConstants.pascalDefaultStandardModules;
public override bool SyntaxTreeIsConvertedAfterUsedModulesCompilation => false;
public override bool ApplySyntaxTreeConvertersForIntellisense => false;
protected IParser Parser

View file

@ -6,11 +6,11 @@ namespace PascalABCCompiler
{
public struct CompilationArtifactsUsedBySyntaxConverters
{
public CompilationArtifactsUsedBySyntaxConverters(Dictionary<string, HashSet<string>> namesFromUsedUnits)
public CompilationArtifactsUsedBySyntaxConverters(Dictionary<string, Dictionary<string, bool>> namesFromUsedUnits)
{
NamesFromUsedUnits = namesFromUsedUnits;
}
public Dictionary<string, HashSet<string>> NamesFromUsedUnits { get; }
public Dictionary<string, Dictionary<string, bool>> NamesFromUsedUnits { get; }
}
}