pascalabcnet/AdditionalLanguages/SPython/SyntaxTreeConverters/SPythonStandardTreeConverter/SymbolTableFillingVisitor.cs
2026-04-09 09:47:15 +03:00

492 lines
19 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 PascalABCCompiler;
using PascalABCCompiler.Parsers;
using PascalABCCompiler.SyntaxTree;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Languages.SPython.Frontend.Converters
{
// заполняет таблицу символов динамически, при проходе по дереву сверху вниз
public class SymbolTableFillingVisitor : BaseChangeVisitor
{
protected SymbolTable symbolTable;
private readonly ILanguageInformation languageInformation = Facade.LanguageProvider.Instance.SelectLanguageByName("SPython").LanguageInformation;
private readonly bool forIntellisense;
public SymbolTableFillingVisitor(string unitName, bool forIntellisense, Dictionary<string, Dictionary<string, bool>> par) {
symbolTable = new SymbolTable(unitName, par);
this.forIntellisense = forIntellisense;
}
// нужны методы из 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.AddToParentScope(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)
{
if (forIntellisense)
return;
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 (languageInformation.SpecialModulesAliases.ContainsKey(real_name))
real_name = languageInformation.SpecialModulesAliases[real_name];
symbolTable.AddModuleAlias(real_name, alias);
}
}
public override void visit(from_import_statement _from_import_statement)
{
if (forIntellisense)
return;
string module_real_name = _from_import_statement.module_name.name;
string module_name = module_real_name;
if (languageInformation.SpecialModulesAliases.ContainsKey(module_name))
module_name = languageInformation.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];
// Учитываем, что в именах может быть обозначение шаблонности
var foundName = namesDict.FirstOrDefault(kv => !kv.Key.Contains('`') && kv.Key == as_Statement.real_name.name
|| kv.Key.Contains('`') && kv.Key.Substring(0, kv.Key.IndexOf('`')) == as_Statement.real_name.name).Key;
if (foundName == null)
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[foundName], as_Statement.alias.name, module_name);
}
}
}
// Возможно рудимент (Выпилить, если не понадобится) 13.12.2025 EVA
/* 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 readonly HashSet<string> forwardDeclaredFunctions = new HashSet<string>();
private readonly string[] Keywords = {
"int", "float", "str", "bool", "bigint", // standard types
"break", "continue", "exit", "halt", // standard ops
"true", "false", // constants
};
private void FillKeywords()
{
foreach (var keyword in Keywords)
currentScope.Add(keyword, NameKind.Keyword);
}
public SymbolTable(string unitName, Dictionary<string, Dictionary<string, bool>> par)
{
ModuleNameToSymbols = par;
entryScope = new LocalScope();
currentScope = entryScope;
FillKeywords();
if (!StandardLibraries.Contains(unitName))
AddAliasesFromStandartLibraries();
}
// names added to current function with global statements
private readonly 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();
}
}
}
private readonly string[] StandardLibraries = Facade.LanguageProvider.Instance.SelectLanguageByName("SPython").SystemUnitNames;
private LocalScope currentScope;
private readonly LocalScope entryScope;
// moduleRealName -> functions and global variables in this module real names
public Dictionary<string, Dictionary<string, bool>> ModuleNameToSymbols { get; set; }
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)
{
return entryScope.GetRealName(alias);
}
public string AliasToModuleName(string alias)
{
return entryScope.GetDeclaredModuleName(alias);
}
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 (currentScope.Contains(name))
return currentScope.GetNameKind(name);
if (forwardDeclaredFunctions.Contains(name))
return NameKind.ForwardDeclaredFunction;
return NameKind.Unknown;
}
}
public void Add(string name, NameKind nameType)
{
switch (nameType)
{
case NameKind.LocalVariable:
case NameKind.GlobalVariable:
case NameKind.GlobalFunction:
currentScope.Add(name, nameType);
break;
case NameKind.ForwardDeclaredFunction:
forwardDeclaredFunctions.Add(name);
break;
default:
throw new NotImplementedException();
}
}
public void AddToParentScope(string name, NameKind nameType)
{
currentScope.OuterScope.Add(name, nameType);
}
public void AddAlias(string realName, bool isVariable, string alias, string moduleName)
{
// оставляем только обозначение шаблонности
if (alias.Contains("`"))
{
alias = alias.Substring(0, alias.IndexOf('`') + 1);
}
// в realName не должно быть обозначения шаблонности, потому что оно пойдет в синтаксический узел
if (realName.Contains("`"))
{
realName = realName.Substring(0, realName.IndexOf('`'));
}
entryScope.Add(alias, realName, isVariable ? NameKind.ImportedVariableAlias : NameKind.ImportedNotVariableAlias, moduleName);
}
public void AddModuleAlias(string realName, string alias)
{
entryScope.Add(alias, realName, NameKind.ModuleAlias);
}
// Возможно рудимент (Выпилить, если не понадобится) 13.12.2025 EVA
/*private void EraseNameAsLocal(string name)
{
localVariables.EraseName(name);
}*/
public void OpenLocalScope()
{
currentScope = new LocalScope(currentScope);
}
public void CloseLocalScope()
{
currentScope.ClearScope();
}
public bool IsOutermostScope()
{
return currentScope.IsOutermostScope();
}
private class LocalScope
{
private class SymbolInfo
{
internal string realName;
internal NameKind kind;
internal string declaredModuleName;
internal SymbolInfo(string realName, NameKind kind, string declaredModuleName)
{
this.realName = realName;
this.kind = kind;
this.declaredModuleName = declaredModuleName;
}
}
public LocalScope OuterScope { get; private set; }
private Dictionary<string, SymbolInfo> symbolsInfos = new Dictionary<string, SymbolInfo>();
public LocalScope(LocalScope outerScope = null)
{
this.OuterScope = outerScope;
}
public bool Contains(string name) => FindNameRecursive(name) != null;
// Возможно рудимент (Выпилить, если не понадобится) 13.12.2025 EVA
/*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, string realName, NameKind kind, string declaredModuleName = null)
{
symbolsInfos[ident] = new SymbolInfo(realName, kind, declaredModuleName);
}
public void Add(string ident, NameKind kind)
{
Add(ident, ident, kind);
}
private SymbolInfo FindNameRecursive(string name)
{
LocalScope curr = this;
while (curr != null)
{
if (curr.symbolsInfos.ContainsKey(name))
return curr.symbolsInfos[name];
curr = curr.OuterScope;
}
return null;
}
public string GetRealName(string ident) => FindNameRecursive(ident).realName;
public NameKind GetNameKind(string ident) => FindNameRecursive(ident).kind;
public string GetDeclaredModuleName(string ident) => FindNameRecursive(ident).declaredModuleName;
public void ClearScope()
{
if (OuterScope != null)
{
symbolsInfos = OuterScope.symbolsInfos;
OuterScope = OuterScope.OuterScope;
}
}
public bool IsOutermostScope()
{
return OuterScope.OuterScope == null;
}
}
}
}
}