编译器的结构
代码是字符流类,经过编译器的处理最后生成可执行的机器语言。那么编译器做了什么呢?
字符流,也就是我们的代码,通过词法分析器,生成符号流,再通过语法分析,生成语法树,然后通过语义分析变成规则的语法树,再通过中间代码生成器,以中间表示形式放入到代码生成器里,变成目标机器语言,最后用机器相关代优化器变成可执行的目标机器 语言。
词法分析
专业名词解析
词法单元:
<token-name,attribute-value>
token-name是一个由语法分析步骤使用的抽象符号
attribute-value指向符号表中关于这个词法单元的条目,符号表条目的信息会被语义分析和代码生成步骤使用。
一个源程序 包含如下的赋值语句
position= inital+rate*60;
然后进入语法分析阶段
position是一个词素,会被映射成词法单元<id,1>
id是表示标识符的抽象符号。
1指向符号表中的Postion对应的条目。一个标识符对应的符号表条目存放该标识符有关的信息,比如它的名字和类型。
赋值符号=是一个词素,被映射成词法单元<=>。因为这个词法单元不需要属性值,所以我们省略第二个分量,也可以使用aasign这样的wmqj符号作为词法单元的名字,但是为了标记上的方便,我们方便使用词素本身作为抽象符号的名字
initial是一个词素,被映射成词法单元<id,2>,其中2指向inital对应的符号表条目。
下边+和=一样
然后是60
被映射成词法单元<60>,(从技术上讲,我们应该为语法单元60建立一个<number,4>,其中4指向符号表中对应整数60的条目,但是我们到下面才讨论数字的语法单元。)
所以上面的代码经过词法分析器的结果就是
<id,1> <=><id ,2><+><id,3><*><60>
然后是语法分析器
=
/ \
<id,1> +
/ \
<id,2> *
/ \
<id,3> 60
语义分析器
=
/ \
<id,1> +
/ \
<id,2> *
/ \
<id,3> inttofloat
||
60
中间代码生成器
t1=inttofloat(60)
t2= id3*t1
t3=id2+t2
id1=t3
代码优化器
t1=id3*60
id1=id2+t1
代码生成器
LDF R2,id3
MULF R2,R2,#60.0
LDF R1,id2
ADDF R1,R1,R2
STF id1,R1
没怎么看过汇编,不过分析大概也得明白其中的语义
LDF 应该是赋值
MULF 是乘法
ADDF 是加
STF 也像是赋值,但是和LDF是反的,感觉一个像存一个像取
继续读下去有讲解,可以和猜测比对一下、。
F是告诉我们操作的数据是浮点数,
R代表的是寄存器
LDF R2,id3 //把id3的内容加载到寄存器R2中
MULF R2,R2,#60.0 //相乘
LDF R1,id2 //把id2移动到R2
ADDF R1,R1,R2 //加
STF id1,R1 //R1的值存放到Id1的地址
语义分析
使用语法树和符号表中的信息来检查源程序是否和语言定义的语义一致。
一个重要部分是类型检查。比如很多程序设计语言的定义中要求一个数组的下标必须是整数。如果用浮点数作为数组下标,就必须报告错误。