跳转到内容

Compiler 2014: Mail List Summary Phase 2

来自ACM Class Wiki

中期检查

语法支持gcc不支持的请一律报错即可

数据是一样的,周六周日都会测,取两次的最高分

makefile和midtermvars.sh是自己提供的

we regard the 'midterm' tag as your submitted version, so don't forget to push the tags.

statspim下载

https://bitbucket.org/xjia/statspim

semantic analysis 和 translation

Q:

应该如何处理这两部分?

A:

semantic analysis 和 translation 可以分开写,也可以在一起写

我记得虎书上是先让你写 semantic analysis,然后接下来让你修改代码,加入 translation 的功能;我这样写过一次,后来觉得有点乱

所以我第二次写的时候把两部分分开了,semantic analysis 的时候完全是判断是不是符合语言使用规则,translation 的时候就可以认为目前的输入一定正确了,

写起来会简单一些

semantic analysis 得到的类型信息可以添加到 AST 上,叫做 annotated parse tree,龙书上有详细的讨论;translation 可以直接利用这些信息

按我的经验,写大作业最重要的是不要怕,知道什么就写什么,写到最后写不出来了,你就知道问题所在了,比如可能有什么东西你还没学过

我基本都是写了一遍,然后再推倒重写的;在实际项目中第一次写的叫 prototype,第二次写的才是 production

关于type的问题

那些Type类只是一个例子,应该自己自己考虑设计自己的类,来使自己的编程尽量方便


Q:

wiki:http://acm.sjtu.edu.cn/wiki/Compiler_2014:_Types

Array类型是继承自Type类型的,我觉得应该是继承自Pointer类型比较好吧?


A:

Array behaves differently from Pointers in type checking. For example,

int *c = xxx;  c = yyy; ... // changing where c points 

is allowed, but

int c[] = {1,2,3};  c = .... 

is not allowed.


Q:

Function类型里argumentType不是一个list,结合上面被划掉的一句话,我的理解是:类似LISP里用一元函数嵌套起来实现多元函数的方法,把argumentType这样套起来。

不过上面那句话说的是把returnType套起来,我不太理解。

这样做(而不用list)的好处是方便实现可变参数吗?


A:

This is called http://en.wikipedia.org/wiki/Currying

This design enables first-class functions, which is a bonus.

zero length array资料

官方的说明里面表示这是对于flexible array member的C99之前的写法,相比之下,zero length array的优势是这样定义出来的结构体是完整的,

可以在栈上或者静态分配空间,可以使用sizeof运算符,可以作为数组成员,可以出现在结构体的任何位置(这一条感觉不太靠谱)。

http://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html

http://stackoverflow.com/questions/11733981/what-is-the-purpose-of-a-zero-length-array-in-a-struct

实现上可能需要注意的事情是,对于初始化列表要特殊处理,避免错误覆盖了紧跟其后的数据。

资料分享

有用的资料:

http://stackoverflow.com/questions/21275992/redeclaration-of-global-variable-vs-local-variable

这个是关于在全局声明和在局部声明语义上不同的解释。


http://eli.thegreenplace.net/2011/05/02/the-context-sensitivity-of-c’s-grammar-revisited/

https://en.wikipedia.org/wiki/The_lexer_hack

http://stackoverflow.com/questions/22904848/what-is-c-local-function-declaration-mechanism#22913970

有关typedef的实现

Q:

typedef int A;
int main(){
   A A;
}

在里面两个A分别是外层的typedef name和内层的未声明的标识符。。。就是这种情况不太好处理


A:

在 action 里维护 scope 就可以了

例子 :

https://bitbucket.org/xjia/typedef-example/src/32154c5d65e84ffd9dc49e8f2a0d0113f2a4d5a1/src/typedef/syntactic/typedef.cup?at=master

typedef int A;
int main() {
   A
}

gcc给的编译错误是缺少identifier

int main() {
   A
}

给的编译错误是缺少类型

所以说其实就是lexer先去查一下table看看是不是有了记录(是id或是typename),没有的话就假定是id。

另外在parser分析语法吞下一个type specifier的时候,立马把状态强行切到id。再多考虑一下应该就没问题了。

cast 那部分还需要类似调整context

就是这个在得到一个type-specifier的时候强制变成id,在下面的代码可能会出现问题吧

typedef int A;
int main(){
   A a = (A)'a';
}

type-name 后面应该不能紧跟另一个 type-name

所以 lexer 吐出一个 type-name 之后,就不能紧跟着再吐一个 type-name

是吐出一个 typedef-name 之后不能连续再吐一个 typedef-name 只能吐成 identifier

A a 这里 a 必须是 identifier 因为刚刚已经吐了一个 typedef-name A 了

A a = (A 这里 A 可以是 typedef-name 因为刚刚吐的是左括号

嵌套struct定义

Q:

嵌套struct定义,内层的struct作用域为何算在file scope?


A:

因为规范中说 struct union enum 共用一个命名空间导致的吧

http://stackoverflow.com/questions/3793952/understanding-c-namespaces

同一个scope里可以有相同名字的属于不同namespace的东西的

gcc的一些问题

Q:

  #include "stdio.h"
  int b,a=b;
  int main(){}

这样gcc是无法编译通过的

  #include "stdio.h"
  int main()
  {int b,a=b;}

为何这样就能通过了呢?


A:

file scope初始化要求常量,而其他的不要求

C对于struct成员的限制的, struct 成员不能是函数类型

对jflex+cup用户可能有用的东西

在.cup文件里,变量名加上left是lexer里面生成token时的第一个整型参数,加right是第二个整型参数

比如.flex里构造token时

   new java_cup.runtime.Symbol(type, yyline, yycolumn, value);

在.cup里一条语法规则

   constant ::= INTEGER:i {:  :};

ileft就是yyline,iright是yycolumn,这样就可以获取token的位置信息了

这个对生成编译信息应该有帮助

p.s. 应该有更简单的方法