我很难理解我应该做什么。我唯一想到的是我需要在cminus.y文件中使用yacc。在那之后,我对一切都感到困惑。有人可以用不同的方式向我解释这一点,以便我能理解我需要做什么吗?
引言:
我们将使用lex / flex和yacc / Bison来生成LALR解析器。我会给你一个名为cminus.y的文件。这是一个yacc格式的语法文件,用于一种简单的C语言,叫做C-minus,来自Kenneth C. Louden编写的Compiler Construction一书。我认为语法应该是相当明显的。 Yahoo组有几个关于如何使用yacc的描述的链接。既然你知道flex,那么学习yacc应该相当容易。唯一的基类型是int。 int是4个字节。布尔值作为整数处理,如在C中。(实际上语法允许你将变量声明为类型void,但是我们不这样做。)你可以拥有一维数组。 没有指针,但应该将对数组元素的引用视为指针(如C中所示)。 该语言提供赋值,IF-ELSE,WHILE和函数调用和返回。 我们希望我们的编译器输出MIPS汇编代码,然后我们就可以在SPIM上运行它了。对于这样一个没有优化的简单编译器,不需要IR。我们可以一次性直接输出汇编代码。但是,我们的第一步是生成符号表。
符号表:
我喜欢Barrett博士的方法,它使用了很多指针来处理不同类型的对象。本质上,符号表的元素是标识符,类型和指向属性对象的指针。属性对象的结构将根据类型而不同。我们只有少数类型可以处理。我建议使用线性搜索来查找表中的符号,至少要开始。如果您想要更好的性能,可以稍后将其更改为散列。 (如果要保留C,可以使用malloc动态分配对象。) 首先,您需要列出所有不同类型的符号 - 没有多少 - 以及每个符号需要哪些属性。一定要允许添加新属性,因为我们 尚未涵盖所有问题。查看语法,函数参数列表的问题是需要将一些思想放入设计的地方。我建议使用更多符号表条目和指针。
测试:
语法是正确的,因此按原样使用现有语法并生成解析器,解析器将接受正确的C-minus程序,但它不会产生任何输出,因为没有与规则关联的代码片段。 我们想要添加代码片段来构建符号表并打印信息。 声明标识符时,应将输入的信息打印到符号表中。如果找到同一范围内相同符号的先前声明,则应打印错误消息。 引用标识符时,您应该在表中查找它以确保它存在。如果尚未在当前范围中声明错误消息,则应打印该错误消息。 关闭范围时,应为未引用的标识符生成警告。 你的测试输入应该是一个正确形成的C-minus程序,但是在这一点上,大多数生产规则都不会发生什么。
作用域:
最基本的方法具有全局范围和声明的每个函数的范围。 该语言允许在任何复合语句中声明,即范围嵌套。实现这一点需要某种范围编号或堆叠方案。 (堆叠最适合一次通过 编译器,这是我们正在构建的。)
答案 0 :(得分:3)
(免责声明)我对编译器课程没有多少经验(如在编译器的学校课程中),但这是我的理解:
1)你需要使用上面提到的工具来创建一个解析器,当给定输入时,它将告诉用户输入是否是cminus.y中定义的语法的正确程序。我从来没有使用yacc / bison所以我不知道它是如何完成的,但这似乎是做的:
2)似乎输出需要检查变量的一致性(即,你不能使用你没有声明与任何编程语言相同的变量),这是通过符号表完成的。简而言之,每次声明某些内容时,都会将其添加到符号表中。当您遇到标识符时,如果它不是语言标识符之一(例如if
或while
或for
),您将在符号表中查找它以确定它是否符号已经宣布。如果它在那里,继续。如果不是 - 打印一些错误
注意:第(2)点符号表有简化的表示;实际上,他们比我刚写的更多,但这应该让你开始。
我从yacc示例开始 - 看看yacc可以做什么以及它是如何做到的。我想必须有一些大的示例 - 完整符号表,你可以阅读以进一步理解。
示例:
我们来看输入A:
int main()
{
int a;
a = 5;
return 0;
}
输入B:
int main()
{
int a;
b = 5;
return 0;
}
并假设我们使用C语法进行解析。您的解析器应该认为输入A是正确的,但是对于输入B应该大喊“b未声明”。