Встраивание другого языка в Flex / Bison - PullRequest
2 голосов
/ 12 ноября 2010

Нижняя строка:

Если вы хотите добавить одну очень маленькую функцию в C ++ с помощью Flex / Bison, как бы вы это сделали? Например, возможность объявления void xxx() функций с синтаксисом: foo%%: xxx?

Вся история:

Однажды я написал собственную программу обработки шейдеров, которая построила готовые к использованию пиксельные и вершинные шейдеры Cg / GLSL из нескольких блоков. Я добавил несколько функций, в основном связанных со статической компиляцией (что-то вроде «лучшего препроцессора»).

Например, что-то похожее на

#if LIGHT_TYPE == POINT
    float lightStrength = dot(N, normalize(pos - lightPos));
#elif LIGHT_TYPE == DIRECTIONAL
    float lightStrength = dot(N, lightDir);
#endif

с чистыми макросами, выглядит как

[LightType = Point]
[Require: pos, lightPos]
float LightStrength()
{
    !let %N Normal
    return dot(%N, normalize(pos - lightPos));
}

на моем "языке". Как видите, «функции» могут быть предоставлены для переменных типов света / материала. Также есть возможность «вызывать» другие функции и отмечать, какие унифицированные / изменяющиеся атрибуты требуются для конкретного шейдера.

Зачем все эти усилия? Потому что (особенно в ранних картах, таких как SM 2.0), были многочисленные ограничения атрибутов, и мой «компилятор» создал готовый к использованию шейдер со списком необходимых атрибутов / переменных, управляемыми параметрами между пиксельными и вершинными шейдерами, оптимизировал некоторые статические вещи ( просто для удобства чтения, поскольку компилятор Cg все равно оптимизирует его позже).

Хорошо, но я не пишу все это, чтобы похвалить себя или что-то в этом роде.

Я написал этот «компилятор» на C #, и изначально он был очень маленькой программой. Но со временем многие функции были добавлены, и теперь программа представляет собой полный беспорядок без вариантов рефакторинга. Кроме того, кодирование в C # означает, что я не могу встроить этот компилятор непосредственно в игру C ++, что вынуждает меня создавать процессы для компиляции шейдеров (это занимает много времени).

Я хотел бы переписать свой язык / компилятор с использованием инструментария C ++ и Flex / Bison. Я уже написал эффективный математический парсер в Flex / Bison, поэтому у меня есть некоторый опыт в этом вопросе. Однако есть одна вещь, которую я не могу решить сама, и это очень тема моего вопроса.

Как я могу встраивать GLSL в мой язык? В компиляторе C # я просто проходил построчно и проверял, начинается ли строка специальными символами (например,% или [), а позже делал много хитростей и хаков, используя string_replace и / или regexes.

Теперь я хотел бы записать чистую грамматику бизонов и сделать это правильно. Но в том числе весь синтаксис GLSL меня пугает. Это довольно большой и сложный язык, постоянно развивающийся. И самое большее, что я хотел бы сделать - это пропустить весь этот код GLSL.

Если вы совсем не знакомы с программированием GLSL / Cg / Graphics, это не совсем важно. Вопрос можно переписать в «нижнюю строчку».

Итак, если вы хотите добавить одну очень маленькую функцию в C ++ с помощью Flex / Bison, как бы вы это сделали? Например, возможность объявления void xxx() функций с синтаксисом: foo%%: xxx?

1 Ответ

1 голос
/ 13 ноября 2010

Я добавил механизмы многопоточности в паскаль для школьного проекта, поэтому мне пришлось с этим разобраться. Я обнаружил определение паскаля в BNF и скопировал его, в основном делая мои токены равными тексту в 99% случаев, и добавляя новый код промежуточного языка в 1% новых добавленных токенов. Паскаль прост, поэтому определение BNF является относительно простым. С ++ не так уж и много.

Язык программирования C ++ от Stroustroup имеет грамматику языка, которая практически готова для синтаксического анализа, но это большой код, который нужно скопировать вручную. Возможно, определенный веб-сайт может помочь вам найти набор бизонов / лекс для «стандартного» C ++, а затем вы можете изменить это.

EDIT: Я нашел мои файлы yacc, так что вот небольшой пример из моего кода паскаля

procheader: PROCEDURE ID paramslist ';'{thread::inLocalDecl=true; $$="procedure "+$2+$3+";\n";}
|            FUNCTION ID paramslist ':' ID ';'{thread::inLocalDecl=true; $$="function "+$2+$3+":"+$5+";\n";}
|  THREAD {thread::inThreadDecl=true;
       thread::inArgDecl=true;}
   ID {thread::curName=$3;} paramslist { thread::inArgDecl=false;} ';'
{ 
   /*a lot of code to transform this custom construct into standard pascal*/
}

первые два элемента были просто стандартными паскалями: я дословно скопировал входные токены в выходные строки (то есть я фактически ничего не изменил из входного файла).

третий элемент (мое ключевое слово THREAD) был, очевидно, не стандартным паскалем. Поэтому я преобразовал вывод в нечто, что я мог бы на самом деле скомпилировать в стандартном паскале.

В основном, чтобы скомпилировать мой «резьбовой» паскаль, мне пришлось взять исходный файл, пропустить его через мой анализатор, а затем скомпилировать вывод.

...