AST: получить значение листа, когда листья разных типов - PullRequest
0 голосов
/ 19 мая 2019

Мне нужно представить в AST такую ​​структуру:

struct {
  int data;
  double doubleDataArray[10];
  struct {
    int nestedData;
  };
};

Я создаю AST как этот:

enter image description here

Мне нужно получить данные из листьев. У меня проблема в том, что листья содержат разнородные данные. Лист может представлять целочисленное значение, двойное число, строку и т. Д.

Я могу создать классы типа IntValue, DoubleValue, которые наследуются от Value и хранить соответствующие данные, выполнить dynamic_cast для преобразования Value в тип, указанный в его атрибуте type. Что-то вроде

switch (value->getType()) {
  case Type::Int: {
   auto iv = dynamic_cast<IntValue>(value);
   int value = iv->getValue();
  } break;
  case Type::Double() {
   auto dv = dynamic_cast<DoubleValue>(value);
   double value = dv->getValue();
  } break;
  //…
}

но я бы хотел знать, есть ли лучший способ, потому что такой переключатель не легко обслуживать и читать.

Я видел какой-то пример, как в boost::program_options, что-то вроде:

int value = value->getValue().as<int>();

Это лучший способ? Как я могу воспроизвести это?

Ответы [ 2 ]

3 голосов
/ 19 мая 2019

Вы можете сделать что-то подобное, используя c ++ 17

struct node {
    //... other stuff
    std::variant</*your types of nodes here*/> type;
}

, а затем вызвать этого посетителя на своих узлах

std::visit([](auto&& node) {
    if constexpr(std::is_same_v<std::decay_t<decltype(node)>, /* your type here */>) {
        // ...
    }
    else if constexpr(/* ... */) {
        // ...
    }
}, node0.type);
1 голос
/ 12 июля 2019

Рассматривая тангенс для слегка отличающегося аромата решения, как насчет того, чтобы это сделать capnproto ? Собственный компилятор схемы Capnproto представляет AST в памяти, используя проводное кодирование Capnproto. Схема поддерживает теговые объединения. Лексер и синтаксический анализатор для схемы создаются с использованием комбинаторов (хотя я предполагаю, что у вас уже есть хороший синтаксический анализатор, который создает AST).

Структура может быть выражена следующим образом с использованием схемы capnp:

# MyAst.capnp

struct Struct {
  fields @0 :List(Field);
}

struct Field {
  name @4 :Text;
  union {
    integer @0 :List(Int32);
    fpoint @1 :List(Double);
    text @2 :List(Text);
    structure @3 :Struct;
  }
}

Компилятор схемы сгенерирует для этого код C ++ со следующими важными классами Struct::Reader, Struct::Builder, Field::Reader и Field::Builder. Что бы ни делало AST, он использовал бы тип Struct::Builder для создания экземпляра структуры со своими данными. Затем вы пройдете по структуре следующим образом:

void processData(Struct::Reader reader) {
  auto fields = reader.getFields();
  for (auto &field : fields) {
    if (field.hasInteger()) {
      int32_t val = field.getInteger();
      ...
    } else if (field.hasFpoint()) {
      double val = field.getFpoint();
      ...
    } else if (field.hasText()) {
      kj::StringPtr val = field.getText();
      ...
    } else if (field.hasStructure()) {
      processData(field.getStructure());
    }
  }
}

Фреймворк kj (включенный в capnproto) имеет немало полезных функций для компиляции, таких как области памяти. Foo::Builder будет затем получено из Orphan<Foo>, а сирота произведена детским домом, который извлекает память из распределителя арены. Если весь ваш AST построен на арене с одним или несколькими большими непрерывными сегментами, это будет работать лучше, чем распределение всех этих типов в куче общего назначения (при условии, что ваш AST не крошечный). Это представление также сериализуется непосредственно на диск или в сеть без транскодирования: вы можете сделать двоичный дамп арены детского дома, а затем загрузить его напрямую, и вы получите все свои данные обратно с нулевым усилием и без транскодирования. Типы Foo::Reader и Foo::Builder предоставляют очень быстрые средства доступа, которые не выполняют ни декодирование, ни преобразование данных - это преимущество кодирования capnproto. Если вы измените данные в AST, приют может вырасти, но он также предоставляет операцию копирования, которая копирует только ссылочные области (если хотите, копирование GC) - и это тоже невероятно быстро, так как транскодирование не выполняется. Куски стенографических двоичных данных копируются с минимальными издержками на обход.

...