在Flex / Bison中嵌入其他语言

时间:2010-11-12 19:08:56

标签: c++ yacc glsl lex cg

底线:

如果你想使用Flex / Bison在C ++中添加一个非常小的功能,你会怎么做?例如,能够使用语法声明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进行了许多技巧和黑客攻击。

现在我想写一个干净的Bison语法并正确地做。但是包括GLSL的整体语法让我害怕。这是一个非常庞大而复杂的语言,不断发展。而且我最常做的就是传递所有这些GLSL代码。

如果您对GLSL / Cg /图形编程没有任何经验,这并不是很重要。这个问题可以改写成“底线”。

所以,如果你想使用Flex / Bison在C ++中添加一个非常小的特性,你会怎么做?例如,能够使用语法声明void xxx()函数:foo%%: xxx

1 个答案:

答案 0 :(得分:1)

我为学校项目添加了多线程机制一次,所以我不得不处理这个问题。我所做的是我找到了pascal的BNF定义并复制了它,主要是使我的标记在99%的情况下等于文本,并在我添加的1%新标记中添加新的中间语言代码。 Pascal很简单,因此BNF定义相对简单。 C ++,不是那么多。

Stroustroup的C ++编程语言具有几乎可以解析的语言语法,但是手动复制的代码很多。也许certain website可以帮助您找到“标准”C ++的bison / lex集,然后您可以修改它。

编辑: 我找到了我的yacc文件,所以这里有一个来自我的pascal代码的小例子

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*/
}

前两个元素只是标准的pascal:我将输入标记逐字复制到输出字符串中(即我实际上没有修改输入文件中的任何内容)。

第三个元素(我的THREAD关键字)显然不是标准的pascal。所以我将输出转换为我可以用标准pascal编译的东西。

基本上编译我的“线程”pascal,我不得不把我的源文件传递给我的解析器,然后编译输出。