C ++: концептуальная проблема при проектировании интерпретатора - PullRequest
1 голос
/ 31 марта 2010

Я программирую переводчик для экспериментального языка программирования (образовательный, веселый, ...) Пока что все прошло хорошо (Tokenizer & Parser), но у меня возникает огромная проблема с некоторыми структурами данных в той части, которая фактически выполняет размеченный и проанализированный код.

Мой язык программирования в основном имеет только два типа, int и string, и они представлены в виде строк C ++ (класс std) и целых чисел

Вот краткая версия структуры данных, которую я использую для передачи значений:

enum DataType
{
  Null,
  Int,
  String
}

class Symbol
{
public:
 string identifier;

 DataType type;
 string stringValue;
 int intValue;
}

Я не могу использовать union, потому что string не позволяет мне.

Эта структура выше вызывает у меня головную боль.

Мне нужно разбросать код по всему так, чтобы он работал, он начинает становиться неуправляемым:

if( mySymbol.type == Int )
{
  mySymbol.intValue = 1234;
} else {
  mySymbol.stringValue = "abcde";
}

Я использую структуру данных Symbol для переменных, возвращаемые значения для функций и общее представление значений на языке программирования.

  • Есть ли лучший способ решить эту проблему? Я надеюсь на это!

Ответы [ 3 ]

4 голосов
/ 31 марта 2010

То, что вы делаете сейчас, является своего рода ублюдком дискриминационного союза . Проблема в том, что вы не используете объединение, а функциональность различаемого объединения является частью самого класса Symbol.

Я предлагаю две альтернативы, в порядке или предпочтении:

1) Используйте тип варианта. Вариант типа похож на дискриминационный союз на стероидах. Одна реализация может быть найдена в Boost .

2) Создайте правильный различаемый союз, определенный отдельно от Symbol класса.

РЕДАКТИРОВАТЬ : дискриминационный союз не обязательно должен иметь тип union. Это также может быть struct.

2 голосов
/ 31 марта 2010

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

Было бы лучше полиморфно создать символы:

class Symbol
{
public:
    virtual Symbol& operator = (int val) = 0; // Pure virtual
    virtual Symbol& operator = (string val) = 0; // Pure virtual
private:
    string identifier;
};

class IntSymbol : public Symbol
{
public:
    virtual Symbol& operator = (int val)
    {
        this->val = val;
        return *this; // to make multiple assignments possible
    }
    virtual Symbol& operator = (string val)
    {
        throw new exception("Programm error");
        return *this; // to make it compile
    }
private:
    int val;
};

Вы делаете то же самое для StringSymbol

1 голос
/ 31 марта 2010

Я бы, вероятно, использовал наследование - определите базовый класс, который реализует базовые операции, которые вы хотите поддерживать, поэтому большая часть другого кода может просто использовать их. Например:

class value { 
public:
    virtual value &add(value const &other) = 0;
    virtual value &assign(value const &other) = 0;
};

class string_val : public value {
    std::string data;
public:
    string_val &add(string_val const &other)  { data += other.data; return *this; }
    string_val &assign(string_val const &other) { data = other.data; return *this; }
};

Вместо того, чтобы использовать чистые виртуалы, как у меня здесь, вы можете предпочесть, чтобы базовый класс фактически определял эти функции, но каждый из них выбрасывал исключение. Они будут вызываться только в тех случаях, когда производные классы не обеспечивают перегрузку. Это будет использоваться для случаев, таких как попытка разделить «xyz» на «abc». Только с двумя производными типами это не сохранит лот , но чем больше производных типов вы можете добавить, тем больше он (потенциально) сохраняет.

...