Как избежать двусмысленности при поиске в таблице символов?

#c #compiler-construction #symbol-table #semantic-analysis

#c #компилятор-конструкция #таблица символов #семантический анализ

Вопрос:

У меня есть довольно элементарная таблица символов для сопоставления идентификатора с символом, хранения VariableSymbols, MethodSymbols, BuiltInTypeSymbols и т.д. Это отлично работало для моего простого интерпретатора. Однако теперь я хочу реализовать более продвинутые типы символов, такие как кортежи (скажем, — TupleSymbol ), в которых хранится массив встроенных типов.

Я могу легко реализовать TupleSymbol и сохранить его в таблице, но я понял, что продолжение использования моего текущего подхода «сопоставление с символом» для хранения типов в конечном итоге приведет к появлению множества неоднозначных типов символов при поиске в таблице символов. Например, если я хочу присвоить кортеж переменной кортежа с именем «test», в рамках операции присваивания мне нужно будет проверить, является ли символ, хранящийся в таблице для идентификатора «test», символом TupleSymbol или BuiltInTypeSymbol, и мне нужно будет сделать то же самое для значения, которое яхотите присвоить ему.

Есть ли лучший способ реализации таблицы символов? Например, лучше ли иметь несколько областей в области таблицы символов, хранящих каждый тип символа отдельно, т.Е. std::map<std::string, MethodSymbol> для методов и std::map<std::string, VarSymbol> для переменных?

Редактировать

Вот некоторый код, который поможет визуализировать мой текущий дизайн. Обратите внимание, как карта таблицы символов symbolTable использует базовый класс символов.

 class BuiltInTypeSymbol;
class Symbol {
    public:
       BuiltInTypeSymbol* type;
       std::string name;
       Symbol(std::string inname, BuiltInTypeSymbol* intype) : name(inname), type(intype){}
};

class BuiltInTypeSymbol : public Symbol {
    public:
       BuiltInTypeSymbol(std::string inname) : Symbol(inname, this) {}
}

class VarSymbol : public Symbol {
    public:
       VarSymbol(std::string inname, BuiltInTypeSymbol* typesymbol) : Symbol(inname, typesymbol) {}
}

BuiltInTypeSymbol* intType = new BuiltInTypeSymbol("int");
BuiltInTypeSymbol* floatType = new BuiltInTypeSymbol("float");

std::map<std::string, Symbol*> symbolTable = {
     {intType->name, intType}, // registering int type
     {floatType->name, floatType}, // registering float type
     {"a", new VarSymbol("a", intType)} // registering test variable "a" of type int
};
 

Комментарии:

1. Как это должно использоваться? Почему вы так часто используете указатели? Что такое int->name ?

2. @super. Исправлена опечатка, она должна была быть IntType->name . Код не так важен, просто иллюстрация того, как представлена моя текущая таблица символов. В первую очередь я ищу совет относительно наилучшего способа создания символа для сопоставления идентификаторов с символами разных типов, например VarSymbol, BuiltInTypeSymbol, MethodSymbol и т. Д.

3. Почему? Конечно, вы можете добиться полипорфизма с вашими различными Symbol типами?

Ответ №1:

Если вы разрешаете повторяющиеся имена символов для разных типов, как допускает большинство языков, например, одно и то же имя для переменной и функции, вам нужно использовать разные map s для каждого типа.

Также вам, вероятно, потребуется выполнить итерацию по какому-либо типу символов, например встроенным символам — неплохо также создать для них карту.