pascalabcnet/AdditionalLanguages/SPython/SyntaxTreeConverters/SPythonStandardTreeConverter/SymbolTableFillingVisitor.cs
Александр Земляк 17f6a9b956
Spython test suite (#3362)
* Reorganize files in TestSuite dir for multiple languages

* Add SPython samples to TestSuite

* Update TestRunner to support multiple languages

* Delete dll files accidentally pushed

* Update .gitignore

* Revert "Delete dll files accidentally pushed"

This reverts commit 62bcba2b821d58ae51ec98e9dadcf621bb0fc003.

* Update testing dir path in Testing.cs

* Improve TestRunner output

* Add random module and it's usage sample

* Reorganize gitignore files

* Move pascal tests back to TestSuite and create new TestSuiteLanguagePlugins folder for SPython

* Revert "Update testing dir path in Testing.cs"

This reverts commit 7d94eafb25292421bce8315520b4f6a0de001bbd.

* Revert crlf changes in pascal tests

* Test changes of _RebuildReleaseAndRunTestsForGitHubActions.bat

* Revert "Test changes of _RebuildReleaseAndRunTestsForGitHubActions.bat"

This reverts commit 234ceae3e3a339fbc68a6170e2bd201dab14d67d.

* Test changes in StorageLocationPicker

* Another test changes in StorageLocationPicker.cs

* Rename LanguagePlugins folder to AdditionalLanguages

* Decouple TestRunner from language names and parameters

* Revert calling TestRunner without parameters in .bat files

* Fix formatter tests nogui exception handling

* Small refactoring in Testing.cs and TestRunner fix of working directory
2025-12-21 21:11:52 +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;
}
}
}
}
}