* 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
465 lines
17 KiB
C#
465 lines
17 KiB
C#
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;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|