pascalabcnet/LanguagePlugins/SPython/SyntaxTreeConverters/SPythonStandardTreeConverter/SymbolTableFillingVisitor.cs
Александр Земляк 4e871c1f3a
Исправление переменных с именем, совпадающим с именами сущностей из других модулей, в 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
2025-11-29 14:27:00 +03:00

465 lines
17 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using PascalABCCompiler;
using PascalABCCompiler.SyntaxTree;
namespace Languages.SPython.Frontend.Converters
{
// заполняет таблицу символов динамически, при проходе по дереву сверху вниз
public class SymbolTableFillingVisitor : BaseChangeVisitor
{
protected SymbolTable symbolTable;
public SymbolTableFillingVisitor(Dictionary<string, Dictionary<string, bool>> par) {
symbolTable = new SymbolTable(par);
}
// нужны методы из BaseChangeVisitor, но порядок обхода из WalkingVisitorNew
public override void DefaultVisit(syntax_tree_node n)
{
if (n == null) return;
for (var i = 0; i < n.subnodes_count; i++)
ProcessNode(n[i]);
}
public override void Enter(syntax_tree_node stn)
{
if (stn is statement_list)
{
symbolTable.OpenLocalScope();
}
if (stn is procedure_definition || stn is function_lambda_definition)
{
symbolTable.OpenLocalScope();
symbolTable.IsInFunctionBody = true;
}
base.Enter(stn);
}
public override void Exit(syntax_tree_node stn)
{
if (stn is procedure_definition || stn is function_lambda_definition)
{
symbolTable.IsInFunctionBody = false;
symbolTable.CloseLocalScope();
}
if (stn is statement_list)
{
symbolTable.CloseLocalScope();
}
base.Exit(stn);
}
public override void visit(semantic_check_sugared_statement_node _)
{
// Скипаем. Узел нужен на семантике, здесь порождает лишние ошибки.
}
private bool IsForwardDeclaration(procedure_header _procedure_header)
{
foreach (procedure_attribute attr in _procedure_header.proc_attributes.proc_attributes)
if (attr.attribute_type is proc_attribute.attr_forward)
return true;
return false;
}
public override void visit(procedure_header _procedure_header)
{
string procedure_name = _procedure_header.name.meth_name.name;
// Сейчас здесь не могут быть встречены forward-объявления функций
// но на будущее...
if (IsForwardDeclaration(_procedure_header))
{
symbolTable.Add(procedure_name, NameKind.ForwardDeclaredFunction);
}
else
{
symbolTable.Add(procedure_name, NameKind.GlobalFunction);
}
base.visit(_procedure_header);
}
public override void visit(function_header _function_header)
{
visit(_function_header as procedure_header);
}
public override void visit(function_lambda_definition _function_lambda_definition)
{
foreach (ident id in _function_lambda_definition.ident_list.list)
symbolTable.Add(id.name, NameKind.LocalVariable);
base.visit(_function_lambda_definition);
}
public override void visit(foreach_stmt _foreach_stmt)
{
symbolTable.OpenLocalScope();
symbolTable.Add(_foreach_stmt.identifier.name, NameKind.LocalVariable);
if (_foreach_stmt.ext is ident_list _ident_list)
{
foreach (ident _ident in _ident_list.idents)
{
symbolTable.Add(_ident.name, NameKind.LocalVariable);
}
}
base.visit(_foreach_stmt);
symbolTable.CloseLocalScope();
}
public override void visit(interface_node _interface_node)
{
ProcessNode(_interface_node.uses_modules);
ProcessNode(_interface_node.using_namespaces);
ProcessNode(_interface_node.interface_definitions);
}
public override void visit(global_statement _global_statement)
{
foreach (ident _ident in _global_statement.idents.idents)
{
symbolTable.MakeVisibleForAssignment(_ident.name);
}
}
public override void visit(typed_parameters _typed_parameters)
{
foreach (ident id in _typed_parameters.idents.idents)
symbolTable.Add(id.name, NameKind.LocalVariable);
base.visit(_typed_parameters);
}
public override void visit(var_statement _var_statement)
{
foreach (ident id in _var_statement.var_def.vars.idents)
symbolTable.Add(id.name, NameKind.LocalVariable);
base.visit(_var_statement);
}
public override void visit(import_statement _import_statement)
{
foreach (as_statement as_Statement in _import_statement.modules_names.as_statements)
{
string real_name = as_Statement.real_name.name;
string alias = as_Statement.alias.name;
if (SymbolTable.specialModulesAliases.ContainsKey(real_name))
real_name = SymbolTable.specialModulesAliases[real_name];
symbolTable.AddModuleAlias(real_name, alias);
}
}
public override void visit(from_import_statement _from_import_statement)
{
string module_real_name = _from_import_statement.module_name.name;
string module_name = module_real_name;
if (SymbolTable.specialModulesAliases.ContainsKey(module_name))
module_name = SymbolTable.specialModulesAliases[module_name];
if (_from_import_statement.is_star)
{
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)
{
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, namesDict[as_Statement.real_name.name], as_Statement.alias.name, module_name);
}
}
}
public override void visit(variable_definitions _variable_definitions)
{
foreach (var_def_statement vds in _variable_definitions.var_definitions)
{
foreach (ident id in vds.vars.idents)
{
symbolTable.Add(id.name, NameKind.GlobalVariable);
base.visit(_variable_definitions);
}
}
}
[Flags]
protected enum NameKind
{
// Имя отсутствует в текущем контексте
Unknown = 0b_0000_0000,
// Ключевые слова языка
Keyword = 0b_0000_0001,
// Имя глобальной переменной
GlobalVariable = 0b_0000_0010,
// Имя глобальной функции
GlobalFunction = 0b_0000_0100,
// Имя подключённого модуля или его псевдоним
ModuleAlias = 0b_0000_1000,
// Имя, подключённое из модуля (не переменная или константа), или его псевдоним
ImportedNotVariableAlias = 0b_0001_0000,
// Подключенная из модуля константа или переменная
ImportedVariableAlias = 0b_0010_0000,
// Локальня переменная
LocalVariable = 0b_0100_0000,
// Имя глобальной forward-объявленной функции
ForwardDeclaredFunction = 0b_1000_0000,
}
protected class SymbolTable
{
private Dictionary<string, NameKind> nameTypes = new Dictionary<string, NameKind>();
private HashSet<string> forwardDeclaredFunctions = new HashSet<string>();
static string[] Keywords = {
"int", "float", "str", "bool", "tuple", // standard types
"break", "continue", "exit", "halt", // standard ops
"true", "false", // constants
};
private void FillKeywords()
{
foreach (var keyword in Keywords)
nameTypes.Add(keyword, NameKind.Keyword);
}
public SymbolTable(Dictionary<string, Dictionary<string, bool>> par)
{
ModuleNameToSymbols = par;
FillKeywords();
AddAliasesFromStandartLibraries();
}
public static Dictionary<string, string> specialModulesAliases = new Dictionary<string, string>
{
{ "time", "time1" },
{ "random", "random1" },
};
// names added to current function with global statements
private HashSet<string> NamesAddedByGlobal = new HashSet<string>();
private bool isInFunctionBody = false;
public bool IsInFunctionBody {
get { return isInFunctionBody; }
set
{
isInFunctionBody = value;
if (isInFunctionBody)
{
Add(StringConstants.result_var_name, NameKind.LocalVariable);
}
else
{
NamesAddedByGlobal.Clear();
}
}
}
// module alias -> module real name
private Dictionary<string, string> modulesAliases = new Dictionary<string, string>();
private List<string> StandardLibraries = new List<string> { "SPythonSystem", "SPythonHidden", "SPythonSystemPys" };
// alias of function or global variable from module -> real name and module real name
private Dictionary<string, Tuple<string, string>> aliasToRealNameAndModuleName = new Dictionary<string, Tuple<string, string>>();
private LocalScope localVariables = new LocalScope();
// moduleRealName -> functions and global variables in this module real names
private Dictionary<string, Dictionary<string, bool>> moduleNameToSymbols;
public Dictionary<string, Dictionary<string, bool>> ModuleNameToSymbols
{
get => moduleNameToSymbols;
set
{ moduleNameToSymbols = value; }
}
private void AddAliasesFromStandartLibraries()
{
foreach (string standardLibrary in StandardLibraries)
{
if (moduleNameToSymbols.ContainsKey(standardLibrary))
foreach (var kv in moduleNameToSymbols[standardLibrary])
AddAlias(kv.Key, kv.Value, kv.Key, standardLibrary);
}
}
public string AliasToRealName(string alias)
{
if (modulesAliases.ContainsKey(alias))
return modulesAliases[alias];
else
return aliasToRealNameAndModuleName[alias].Item1;
}
public string AliasToModuleName(string alias)
{
return aliasToRealNameAndModuleName[alias].Item2;
}
public void MakeVisibleForAssignment(string name)
{
NamesAddedByGlobal.Add(name);
}
public bool IsVisibleToAssign(string name)
{
NameKind kind = this[name];
if (kind == NameKind.Unknown || kind == NameKind.ImportedNotVariableAlias) return false;
if (!isInFunctionBody) return true;
return (kind != NameKind.GlobalVariable &&
kind != NameKind.ImportedVariableAlias) ||
NamesAddedByGlobal.Contains(name);
}
public NameKind this[string name]
{
get {
if (localVariables.Contains(name))
return NameKind.LocalVariable;
if (nameTypes.ContainsKey(name))
return nameTypes[name];
if (forwardDeclaredFunctions.Contains(name))
return NameKind.ForwardDeclaredFunction;
return NameKind.Unknown;
}
}
public void Add(string name, NameKind nameType)
{
switch (nameType)
{
case NameKind.LocalVariable:
localVariables.Add(name);
break;
case NameKind.GlobalVariable:
case NameKind.GlobalFunction:
AddGlobalName(name, nameType);
break;
case NameKind.ForwardDeclaredFunction:
forwardDeclaredFunctions.Add(name);
break;
default:
throw new NotImplementedException();
}
}
private void AddGlobalName(string name, NameKind nameType)
{
EraseNameAsLocal(name);
if (nameTypes.ContainsKey(name))
nameTypes[name] = nameType;
else nameTypes.Add(name, nameType);
}
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, isVariable ? NameKind.ImportedVariableAlias : NameKind.ImportedNotVariableAlias);
}
public void AddModuleAlias(string realName, string alias)
{
if (modulesAliases.ContainsKey(alias))
modulesAliases[alias] = realName;
else modulesAliases.Add(alias, realName);
AddGlobalName(alias, NameKind.ModuleAlias);
}
private void EraseNameAsLocal(string name)
{
localVariables.EraseName(name);
}
public void OpenLocalScope()
{
localVariables = new LocalScope(localVariables);
}
public void CloseLocalScope()
{
localVariables.ClearScope();
}
public bool IsOutermostScope()
{
return localVariables.IsOutermostScope();
}
public class LocalScope
{
private LocalScope outerScope;
private HashSet<string> symbols = new HashSet<string>();
public LocalScope(LocalScope outerScope = null)
{
this.outerScope = outerScope;
}
public bool Contains(string name)
{
LocalScope curr = this;
while (curr != null)
{
if (curr.symbols.Contains(name))
return true;
curr = curr.outerScope;
}
return false;
}
public void EraseName(string name)
{
LocalScope curr = this;
while (curr != null)
{
//if (curr.symbols.Contains(name))
curr.symbols.Remove(name);
curr = curr.outerScope;
}
}
public void Add(string ident)
{
symbols.Add(ident);
}
public void ClearScope()
{
if (outerScope != null)
{
symbols = outerScope.symbols;
outerScope = outerScope.outerScope;
}
}
public bool IsOutermostScope()
{
return outerScope.outerScope == null;
}
}
}
}
}