Рассматривая тангенс для слегка отличающегося аромата решения, как насчет того, чтобы это сделать 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) - и это тоже невероятно быстро, так как транскодирование не выполняется. Куски стенографических двоичных данных копируются с минимальными издержками на обход.