Можно ли написать edsl на C ++, который связывает значения с именами переменных? Например, я могу написать edsl в Haskell, который позволяет мне написать следующее (см. также этот вопрос):
prog3 :: StackProg Expr
prog3 = do
push (IntL 3)
push (IntL 4)
a <- pop
b <- pop
return (Plus a b)
И это производит АСТ, где a
а также b
переменные. Возможно ли что-то подобное в C ++? Я хотел бы иметь (в порядке важности)
Если вы хотите создать синтаксис действительных выражений C ++ (возможно, как подмножество всех выражений C ++, подобно тому, как do-нотация ограничивает себя операциями с монадой), проверять их статически и использовать их, тогда вам лучше всего Boost.Proto. Чтобы описать это кратко, это — само EDSL, чтобы написать и описать EDSL.
Я не буду вдаваться в подробности о том, как его использовать. Хотя это может быть трудно научиться использовать, особенно если вы не привыкли к метапрограммированию C ++, документация великолепна, и если вы когда-нибудь писали грамматику, я думаю, вы найдете свои оценки. В еще один из моих ответов Я рассказывал, как написать EDSL с грамматикой, которая принимает только простые арифметические выражения и использует их для вычисления их производных, так что вы можете проверить это.
Что касается вашего точного вопроса, я боюсь, что ответ должен быть либо коротким «нет, вы не можете сделать это», либо длинным «вы можете сделать это до такой степени, как показывает Boost.Phoenix, но это, вероятно, не стоит потратить ваше время на его внедрение, учитывая загадочные ошибки и / или дополнительное время компиляции для ваших пользователей EDSL «. Я рассуждаю по этому поводу о том, что вы хотите сделать, подходит на двух уровнях: do-нотация — это особенность Haskell, при этом она использует синтаксическое дерево и дает ему семантику на уровне самого EDSL.
Как это и происходит, типичные EDSL в стиле Proto-style являются допустимыми выражениями C ++, и язык не предлагает область видимости на этом уровне, переменные объявляются в отдельных выражениях. Например, _a + _b
является допустимым фрагментом C ++, потому что _a
а также _b
объявляются переменные C ++, предоставляемые Phoenix, но не являются допустимой программой в EDSL, потому что _a
а также _b
остаются несвязанными. Да, ошибка будет обнаружена, но вы должны реализовать это самостоятельно. В сравнении do-notation является частью Haskell, поэтому любой EDSL наследует ее бесплатно. То есть return (a + b)
сам по себе никогда не действует — должно быть какое-то a
и немного b
,
Есть некоторые вещи, которые нужно иметь в виду, хотя. C ++ 11 предлагает лямбда-выражения, так что вы можете получить некоторую область видимости здесь — но они будут непрозрачными в EDSL, синтаксическое дерево покажет только переменную. Некоторый самоанализ может показать, что эта переменная может вызываться для некоторых типов, но это так. Даже если вам требуется, чтобы лямбда-выражения возвращали значение в EDSL, ничего не сказано что-то еще они могли бы сделать. Об этом не всегда стоит беспокоиться, и я бы сказал, что он идеально подходит для некоторых EDSL.
Точно так же C ++ 11 значительно упрощает «выделение» частей выражения EDSL. Это не так много эквивалентно a <- foo
сахар нотации, но это эквивалентно let a = foo
, Таким образом, вы можете на самом деле без особого труда заставить «сделать правильную вещь» следующим образом:
auto double_pop = make_tuple(pop(), pop());
auto program = (push(3), push(4), consume(double_pop));
Что может быть эквивалентно однотипному и надуманному следующему:
program = do
let a = pop
return consume `ap` a `ap` a
(Поскольку Boost.Proto начинался как библиотека C ++ 03, убедитесь, что изучаете документы перед использованием C ++ 11 auto
с этим, IIRC есть оговорка.)
Нет, вы не можете напрямую смоделировать это с помощью C ++. Однако в C ++ вы можете встраивать другие движки, такие как Lua, который часто используется как движок расширения и подходит для DSL.
Проверьте этот ответ, также в StackOVerflow.
Шаги для встраивания Lua в C ++: