Написание компилятора: как заставить работать простые шаблоны? - PullRequest
8 голосов
/ 08 декабря 2011

У меня есть язык с очень похожим на C ++ синтаксисом.Лексер и парсер находятся на своем месте и выдают правильный AST.Для большей части также выполняется бэкэнд.

Базовая система, используемая компилятором для создания типов, очень проста: все типы считаются встроенными, а все экземпляры являются глобальными.Так что есть просто простая карта, которая сопоставляет имя типа с методом, который создает Variable , который в основном является универсальным типом, таким как boost :: any.Другая карта с именем переменной в качестве ключа и переменной в качестве значения служит глобальной областью действия:

std::map< std::string, std::function< Variable() > typeList;

  //register some types
typeList[ "X" ] = Variable::Create<X>;
typeList[ "Y" ] = CreateInstanceOfY;
....

Когда компилятор получает узел AST для инициализации, такой как X myVar;, он в основном делает

std::map< std::string, Variable > globalScope;
globalScope[ "myVar" ] = typeList[ "X" ]();

Когда myVar используется позже, к нему можно получить доступ посредством простой диспетчеризации типов, например

X& x = myVar.GetReference<X>();

Теперь я хотел бы немного расширить это и использовать простые шаблоны.Предположим, есть тип «массив», который реализован с использованием вектора.Я мог бы зарегистрировать все как

typeList[ "array<X>" ] = Variable::Create< std::vector< X > >;

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

typeList.CreateTemplateVariable( "array", "X" )

, которое затем создаст экземпляр Variable, который внутренне содержит std :: vector .Я очень старался, но не могу понять, как это сделать.Может быть, я просто неправильно начал с простого сопоставления типов и по этой причине я не могу разобраться с этим.

Итак, вопрос прост: возможно ли это сделать?И как?

Ответы [ 2 ]

2 голосов
/ 09 декабря 2011

Я не уверен, что правильно понял вашу проблему, но если у вас есть M параметрических типов (vector<>, list<>, ...) и N простых типов (int, double, ...) вам понадобится M * N реальных реализаций, если вы хотите поддерживать все комбинации. Все эти реализации должны быть известны во время компиляции (или в принципе вы можете запускать компилятор C ++ на лету). Вы действительно этого хотите?

Обходным путем может быть использование нетипизированных контейнеров. Например, vector<Object*> хранит указатели, которые впоследствии могут быть преобразованы в требуемый тип, например, с помощью dynamic_cast. Таким образом, вам потребуется только M реализаций для параметрических типов, и вы можете разрешить «массив» в vector и «X» в X независимо.

1 голос
/ 09 декабря 2011

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

std::function< Variable() > getType(std::string name) {
    auto rv = typeList[name];
    if (rv) return rv;
    auto template_start = name.find('<');
    if (template_start != string::npos) {
        auto template_end = name.rfind('>');
        std::string arg = name.substr(template_start+1, template_end);
        std::string base = name.substr(0, template_start);
        typeList[name] = rv = InstantiateTemplate(base, arg);
        return rv; }
    throw UnknownTypeError(name);
}

Это вызывается для любого типа, указанного в программе, создавая необходимые экземпляры шаблона по требованию.

...