Рефакторинг визитора SPython и исправление global statetement (#3375)

* Delete nameTypes dictionary in SymbolTable

* Refactor SymbolTableFillingVisitor

* Disallow dotted idents in global statement
This commit is contained in:
Александр Земляк 2026-01-16 21:51:17 +03:00 committed by GitHub
parent cb09e2340f
commit 42d7ca97a3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 738 additions and 781 deletions

View file

@ -65,13 +65,13 @@
%right STARSTAR
%left BINNOT
%type <id> ident dotted_ident func_name_ident type_decl_identifier
%type <ex> extended_expr expr intellisense_dotted_ident proc_func_call const_value variable optional_condition act_param extended_new_expr new_expr is_expr variable_as_type
%type <id> ident func_name_ident type_decl_identifier
%type <ex> extended_expr expr dotted_ident proc_func_call const_value variable optional_condition act_param extended_new_expr new_expr is_expr variable_as_type
%type <stn> act_param_list optional_act_param_list proc_func_decl return_stmt break_stmt continue_stmt global_stmt pass_stmt
%type <stn> var_stmt assign_stmt if_stmt stmt proc_func_call_stmt while_stmt for_stmt optional_else optional_elif exit_stmt
%type <stn> expr_list
%type <stn> stmt_list block
%type <stn> program param_name form_param_sect form_param_list optional_form_param_list dotted_ident_list
%type <stn> program param_name form_param_sect form_param_list optional_form_param_list
%type <stn> ident_as_ident ident_as_ident_list ident_list
%type <td> proc_func_header type_ref simple_type_identifier template_type
%type <stn> import_clause template_type_params template_param_list parts stmt_or_expression expr_mapping_list
@ -174,11 +174,11 @@ type_decl_identifier
;
variable_as_type
: intellisense_dotted_ident
: dotted_ident
{
$$ = $1;
}
| intellisense_dotted_ident template_type_params
| dotted_ident template_type_params
{
$$ = new ident_with_templateparams($1 as addressed_value, $2 as template_param_list, @$);
}
@ -293,7 +293,7 @@ exit_stmt
;
global_stmt
: GLOBAL dotted_ident_list
: GLOBAL ident_list
{
$$ = new global_statement($2 as ident_list, @$);
}
@ -306,38 +306,17 @@ ident
}
;
// этот нетерминал аналог dotted_identifier из ABCPascal.y
dotted_ident
: ident
{
$$ = $1;
$$ = $1;
}
| dotted_ident DOT ident
{
$$ = new ident($1.name + "." + $3.name, @$);
}
;
// этот нетерминал аналог dotted_identifier из ABCPascal.y
intellisense_dotted_ident
: ident
{
$$ = $1;
}
| intellisense_dotted_ident DOT ident
{
$$ = new dot_node($1 as addressed_value, $3 as addressed_value, @$);
}
;
dotted_ident_list
: dotted_ident
{
$$ = new ident_list($1, @$);
}
| dotted_ident_list COMMA dotted_ident
{
$$ = ($1 as ident_list).Add($3, @$);
}
;
ident_as_ident
: ident AS ident

View file

@ -1,7 +1,8 @@
using System;
using System.Collections.Generic;
using PascalABCCompiler;
using PascalABCCompiler;
using PascalABCCompiler.Parsers;
using PascalABCCompiler.SyntaxTree;
using System;
using System.Collections.Generic;
namespace Languages.SPython.Frontend.Converters
{
@ -10,6 +11,8 @@ namespace Languages.SPython.Frontend.Converters
{
protected SymbolTable symbolTable;
private readonly ILanguageInformation languageInformation = Facade.LanguageProvider.Instance.SelectLanguageByName("SPython").LanguageInformation;
public SymbolTableFillingVisitor(Dictionary<string, Dictionary<string, bool>> par) {
symbolTable = new SymbolTable(par);
}
@ -77,7 +80,7 @@ namespace Languages.SPython.Frontend.Converters
}
else
{
symbolTable.Add(procedure_name, NameKind.GlobalFunction);
symbolTable.AddToParentScope(procedure_name, NameKind.GlobalFunction);
}
base.visit(_procedure_header);
}
@ -149,8 +152,8 @@ namespace Languages.SPython.Frontend.Converters
{
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];
if (languageInformation.SpecialModulesAliases.ContainsKey(real_name))
real_name = languageInformation.SpecialModulesAliases[real_name];
symbolTable.AddModuleAlias(real_name, alias);
}
}
@ -159,8 +162,8 @@ namespace Languages.SPython.Frontend.Converters
{
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 (languageInformation.SpecialModulesAliases.ContainsKey(module_name))
module_name = languageInformation.SpecialModulesAliases[module_name];
if (_from_import_statement.is_star)
{
@ -182,17 +185,18 @@ namespace Languages.SPython.Frontend.Converters
}
}
public override void visit(variable_definitions _variable_definitions)
// Возможно рудимент (Выпилить, если не понадобится) 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);
}
}
}
base.visit(_variable_definitions);
}*/
[Flags]
protected enum NameKind
@ -219,10 +223,9 @@ namespace Languages.SPython.Frontend.Converters
protected class SymbolTable
{
private Dictionary<string, NameKind> nameTypes = new Dictionary<string, NameKind>();
private HashSet<string> forwardDeclaredFunctions = new HashSet<string>();
private readonly HashSet<string> forwardDeclaredFunctions = new HashSet<string>();
static string[] Keywords = {
private readonly string[] Keywords = {
"int", "float", "str", "bool", "tuple", // standard types
"break", "continue", "exit", "halt", // standard ops
"true", "false", // constants
@ -231,24 +234,20 @@ namespace Languages.SPython.Frontend.Converters
private void FillKeywords()
{
foreach (var keyword in Keywords)
nameTypes.Add(keyword, NameKind.Keyword);
currentScope.Add(keyword, NameKind.Keyword);
}
public SymbolTable(Dictionary<string, Dictionary<string, bool>> par)
{
ModuleNameToSymbols = par;
entryScope = new LocalScope();
currentScope = entryScope;
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 readonly HashSet<string> NamesAddedByGlobal = new HashSet<string>();
private bool isInFunctionBody = false;
@ -268,47 +267,33 @@ namespace Languages.SPython.Frontend.Converters
}
}
// module alias -> module real name
private Dictionary<string, string> modulesAliases = new Dictionary<string, string>();
private readonly string[] StandardLibraries = Facade.LanguageProvider.Instance.SelectLanguageByName("SPython").SystemUnitNames;
private List<string> StandardLibraries = new List<string> { "SPythonSystem", "SPythonHidden", "SPythonSystemPys" };
private LocalScope currentScope;
// 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();
private readonly LocalScope entryScope;
// 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; }
}
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])
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;
return entryScope.GetRealName(alias);
}
public string AliasToModuleName(string alias)
{
return aliasToRealNameAndModuleName[alias].Item2;
return entryScope.GetDeclaredModuleName(alias);
}
public void MakeVisibleForAssignment(string name)
@ -330,10 +315,8 @@ namespace Languages.SPython.Frontend.Converters
public NameKind this[string name]
{
get {
if (localVariables.Contains(name))
return NameKind.LocalVariable;
if (nameTypes.ContainsKey(name))
return nameTypes[name];
if (currentScope.Contains(name))
return currentScope.GetNameKind(name);
if (forwardDeclaredFunctions.Contains(name))
return NameKind.ForwardDeclaredFunction;
return NameKind.Unknown;
@ -345,11 +328,9 @@ namespace Languages.SPython.Frontend.Converters
switch (nameType)
{
case NameKind.LocalVariable:
localVariables.Add(name);
break;
case NameKind.GlobalVariable:
case NameKind.GlobalFunction:
AddGlobalName(name, nameType);
currentScope.Add(name, nameType);
break;
case NameKind.ForwardDeclaredFunction:
forwardDeclaredFunctions.Add(name);
@ -359,76 +340,70 @@ namespace Languages.SPython.Frontend.Converters
}
}
private void AddGlobalName(string name, NameKind nameType)
public void AddToParentScope(string name, NameKind nameType)
{
EraseNameAsLocal(name);
if (nameTypes.ContainsKey(name))
nameTypes[name] = nameType;
else nameTypes.Add(name, nameType);
currentScope.OuterScope.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);
{
entryScope.Add(alias, realName, isVariable ? NameKind.ImportedVariableAlias : NameKind.ImportedNotVariableAlias, moduleName);
}
public void AddModuleAlias(string realName, string alias)
{
if (modulesAliases.ContainsKey(alias))
modulesAliases[alias] = realName;
else modulesAliases.Add(alias, realName);
AddGlobalName(alias, NameKind.ModuleAlias);
entryScope.Add(alias, realName, NameKind.ModuleAlias);
}
private void EraseNameAsLocal(string name)
// Возможно рудимент (Выпилить, если не понадобится) 13.12.2025 EVA
/*private void EraseNameAsLocal(string name)
{
localVariables.EraseName(name);
}
}*/
public void OpenLocalScope()
{
localVariables = new LocalScope(localVariables);
currentScope = new LocalScope(currentScope);
}
public void CloseLocalScope()
{
localVariables.ClearScope();
currentScope.ClearScope();
}
public bool IsOutermostScope()
{
return localVariables.IsOutermostScope();
return currentScope.IsOutermostScope();
}
public class LocalScope
private class LocalScope
{
private LocalScope outerScope;
private HashSet<string> symbols = new HashSet<string>();
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;
this.OuterScope = outerScope;
}
public bool Contains(string name)
{
LocalScope curr = this;
public bool Contains(string name) => FindNameRecursive(name) != null;
while (curr != null)
{
if (curr.symbols.Contains(name))
return true;
curr = curr.outerScope;
}
return false;
}
public void EraseName(string name)
// Возможно рудимент (Выпилить, если не понадобится) 13.12.2025 EVA
/*public void EraseName(string name)
{
LocalScope curr = this;
@ -438,25 +413,50 @@ namespace Languages.SPython.Frontend.Converters
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)
public void Add(string ident, NameKind kind)
{
symbols.Add(ident);
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)
if (OuterScope != null)
{
symbols = outerScope.symbols;
outerScope = outerScope.outerScope;
symbolsInfos = OuterScope.symbolsInfos;
OuterScope = OuterScope.OuterScope;
}
}
public bool IsOutermostScope()
{
return outerScope.outerScope == null;
return OuterScope.OuterScope == null;
}
}
}

View file

@ -0,0 +1,8 @@
import PABCSystem as pabc
from PABCSystem import AllDelimiters as delims
def f():
print(delims)
f()
print(pabc.Range(0, 1))