编译器构造(C语言描述)第二章

本文介绍了一个简单的编译器——Micro的构建过程,包括词法分析器、语法分析器的设计与实现,以及如何进行语义分析生成目标代码。文章详细解释了词法分析器如何识别关键字、操作符和标识符,并介绍了递归下降语法分析方法及其具体应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1Micro编译器结构

(1)词法分析器:读取源程序,产生记号表示流,供语法分析器调用

(2)语法分析器:一直处理记号,知道遇到了需要语义处理的语法结构,直接调用语义例程

(3)语义例程:调用适当的支持例程生成代码

(4)符号表:有语义例程使用(接口描述)


2Micro词法分析器

词法记号集:token(枚举类型)

Typedef enum token_types{

BEGIN, END, READ, WRITE, ID, INTLITERAL, LPAREN, RPAREN, SEMI COLON, COMMA, ASSIGNOP, PLUSOP, MINUSOP, SCANEOF

}token;

Extern token scanner(void);//不带参数,返回token类型值的函数(语法分析器)

 


词法分析器的主要识别循环过程:

#include <stdio.h>

#include <ctype.h>

 

 

 

//识别操作符,注释,分隔符,标识符,整数常量的词法分析器循环

int in_char, c;

while((in_char getchar()) != EOF)

{

//遇到空格就直接进行下一个token的读取

if(isspace(in_char))

continue;

//如果以字母开头,表示的是标识符

else if(isalpha(in_char))

{

//如果读取的字符是数字,字母,或者下划线,则继续读取,直到获取最长的符合序列

for(c getchar(); isalnum(c) || == '_'getchar());

//将额外字符压回输入流

ungetc(c, stdin);

//返回此标识符

return ID;

}

//如果以数字开头,表示的是整数常量

else if(isdigit(in_char))

{

//如果读取的字符是数字,则继续读取,知道获取最长的符合序列

while(isdigit(c getchar()));

//将额外的字符压回输入流

ungetc(c, stdin);

//返回此整数常量

return INTLITERAL;

}

else if(in_char == '(')

return LPAREN;

else if(in_char == ')')

return RPAREN;

else if(in_char == ';')

return SEMICOLON;

else if(in_char == ',')

return COMMA;

else if(in_char == '+')

return PLUSOP;

else if(in_char == ':')

{

//查找赋值操作":="

getchar();

if(c == '=')

return ASSIGNOP;

else

{

ungetc(c, stdin);

lexical_error(in_char);

}

}

else if(in_char == '-')

{

//查找注释符"--"

getchar();

if(c == '-')

{

while((in_char getchar()) != '\n');

}

//否则为减号

else

{

ungetc(c, stdin);

return MINUSOP;

}

}

else

//不是以字母或者数字开头,则为无效字符,出错

lexical_error(in_char);

}

 

 


保留字的识别方式:

(1)词法分析器中有一张保留字表,每当一个标识符被识别时,检查保留字表,如果在此表中,则被解释成保留字;

(2)保留字作为编译器符号表中的初始部分,含有特殊属性reserved,词法分析器识别一个标识符后,在符号中找到该标识符,如果有特殊属性,则识别为保留字。

(3)要检查保留字需要额外的缓存来存放每一个被检查了的标识符,所以,另外定义例程来实现这个功能:check_reserved()buffer_char()


代码修改如下:

#include <stdio.h>

#include <ctype.h>

 

 

 

//识别操作符,注释,分隔符,标识符,整数常量的词法分析器循环

int in_char, c;

clear_buffer();

if(feof(stdin))

return SCANEOF;

while((in_char getchar()) != EOF)

{

//遇到空格就直接进行下一个token的读取

if(isspace(in_char))

continue;

//如果以字母开头,表示的是标识符

else if(isalpha(in_char))

{

//缓存每个字符

buffer_char(in_char);

//如果读取的字符是数字,字母,或者下划线,则继续读取,直到获取最长的符合序列

for(c getchar(); isalnum(c) || == '_'getchar())

buffer_cahr(c);

//将额外字符压回输入流

ungetc(c, stdin);

//返回标识符并检测其是否为保留字

return check_reserved();

}

//如果以数字开头,表示的是整数常量

else if(isdigit(in_char))

{

//缓存每个字符

buffer_char(in_char);

//如果读取的字符是数字,则继续读取,知道获取最长的符合序列

while(isdigit(c getchar()))

buffer_char(c);

//将额外的字符压回输入流

ungetc(c, stdin);

//返回此整数常量

return INTLITERAL;

}

else if(in_char == '(')

return LPAREN;

else if(in_char == ')')

return RPAREN;

else if(in_char == ';')

return SEMICOLON;

else if(in_char == ',')

return COMMA;

else if(in_char == '+')

return PLUSOP;

else if(in_char == ':')

{

//查找赋值操作":="

getchar();

if(c == '=')

return ASSIGNOP;

else

{

ungetc(c, stdin);

lexical_error(in_char);

}

}

else if(in_char == '-')

{

//查找注释符"--"

getchar();

if(c == '-')

{

while((in_char getchar()) != '\n');

}

//否则为减号

else

{

ungetc(c, stdin);

return MINUSOP;

}

}

else

//不是以字母或者数字开头,则为无效字符,出错

lexical_error(in_char);

}

 

 


3Micro语法

使用CFGContext-Free Grammar,上下文无关文法,也成BNF文法)给出定义:

产生式:--> ... Z

A:左部(Left-Hand Side, LHS

... Z:右部(Right-Hand Side, RHS

产生式代表了一个规则,左部文法符号可以由其右部文法符号代替,如:

<program> --> begin <statement list> end

CFG中有两种文法符号:

(1)非终结符:

通常由'<''>'限定或者出现在产生式的左边来被识别,它们实际上是占位符,必须根据以它作为左部的产生式来替换或者重写

(2)终结符:

从来不被重写,代表语言的词法记号(token

 

CFG的全部目的:指定哪些终结符序列式合法的。

CFG的检测方式:有一个开始符号(start)或目标符号(goal)的非终结符开始,随后应用产生式重写非非终结符,知道仅剩下终结符,任何可由此产生的终结符序列都被认为是合法的。

扩展的BNF可以定义:

(1)可选项:"[""]"括起来

(2)可选的项列表:"{""}"括起来

但是上述功能也可以通过BNF来实现,所以,扩展的BNF实际上跟BNF是一致的。


下面定义扩展的CFG

1. <program>                 --> begin <statement list> end

2. <statement list>          --> <statement> {<statement>}

3. <statement>               --> ID := <expression>;

4. <statement>               --> read (<id list>);

5. <statement>               --> write (<expr list>);

6. <id list>                 --> ID {, ID}

7. <expr list>               --> <expression> {<expression>}

8. <expression>              --> <primary> {<add op> <primary>}

9. <primary>                 --> (<expression>)

10.<primary>                 --> ID

11.<primary>                 --> INTLITERAL

12.<add op>                  --> PLUSOP

13.<add op>                  --> MINUSOP

14.<system goal>             --> <program> SCANEOF

 


使用上述的CFG推导一个程序:begin ID := ID (INTLITERAL ID); end

<program>

begin <statement list> end

begin <statement> {<statement>} end

begin <statement> end

begin ID := <expression>; end

begin ID := <primary> {<add op> <primary>}; end

begin ID := <primary> <add op> <primary>; end

begin ID := <primary> <primary>; end

begin ID := ID <primary>; end

begin ID := ID (<expression>); end

begin ID := ID (<primary> {<add op> <primary>}); end

begin ID := ID (<primary> <add op> <primary>); end

begin ID := ID (<primary> <primary>); end

begin ID := ID (INTLITERAL <primary>); end

begin ID := ID (INTLITERAL ID); end

 


CFG不包含优先级,如果要包含优先级,可定义如下:

<expression>   --> <factor> {<add op> <factor>}

<factor>       --> <primary> {<mult op> <primary>}

<primary>     --> (<expression>)

<primary>     --> ID

<primary>     --> INTLITERAL

 


4:递归下降语法分析

递归下降:

递归语法分析例程,当处理一个程序的时候,它下降遍历所识别的分析树。

基本思想:

每个非终结符都有一个相关的语法分析过程(parsing procedure)用以识别由该非终结符生成的任意词法记号序列。


对应于Micro文法产生式所编写的语法分析例程:

void system_goal(void)

{

 

program();

match(SCANEOF);

}

void program(void)

{

 

match(BEGIN);

statement_list();

match(END);

}

void statement_list(void)

{

 

statement();

while(true)

{

switch(next_token())

{

case ID:

case READ:

case WRITE:

statement();

default:

return ;

}

}

}

void statement(void)

{

token tok next_token();

switch(tok)

{

case ID:

match(ID);

match(ASSIGNOP);

expression_r();

match(SEMICOLON);

break;

case READ:

match(READ);

match(LPAREN);

id_list();

match(RPAREN);

match(SEMICOLON);

break;

case WRITE:

match(WRITE);

match(LPAREN);

expr_list();

match(RPAREN);

match(SEMICOLON);

break;

default:

syntax_error(tok);

break;

}

}

void id_list(void)

{

 

match(ID);

while(next_token() == COMMA)

{

match(COMMA);

match(ID);

}

}

void expression_r(void)

{

 

primary();

for(t next_token(); == PLUSOP || == MINUSOP; next_token())

{

add_op();

primary();

}

}

void expr_list(void)

{

 

expression_r();

while(next_token == COMMA)

{

match(COMMA);

expression_r();

}

}

void add_op(void)

{

token tok next_token();

 

if(add_op == PLUSOP || add_op == MINUSOP)

match(tok);

else

syntax_error(tok);

}

void primary(void)

{

token tok next_token();

swith(tok)

{

case LPAREN:

match(LPAREN);

expression_r();

match(RPAREN);

break;

case ID:

match(ID);

break;

case INTLITERAL:

match(INTLITERAL);

break;

default:

syntax_error();

break;

}

}

 

 


5:翻译Micro

5.1:目标语言(三地址机器代码)

 

5.2:临时变量(Temp&X)

5.3:动作符号(见代码)

5.4:语义信息(见代码)

5.5Micro动作符号(见代码)

 


文法符号的语义记录:

#define MAXIDLEN 31

typedef char string[MAXIDLEN];

 

typedef struct operator

{

enum op {PLUS, MINUS} operator;

}op_rec;

enum expr {IDEXPR, INTLITERALEXPR, TEMPEXPR};

typedef struct expression

{

enum expr kind;

union

{

string name;

int val;

};

}expr_rec;

 


带有符号动作的Micro文法:

<program>      --> #start begin <statement list> end

<statement list>  --> <statement> {<statement>}

<statement>     --> <ident> := <expression> #assign

<statement>     --> read (<id list>);

<statement>     --> write (<expr list>)

<id list>        --> <ident> #read_id {, <ident> #read_id}

<expr list>      --> <expression> #write_expr

                     {, <expression> #write_expr}

<expression>    --> <primary> {<add op> <primary> #gen_infix}

<primary>      --> (<expression>)

<primary>      --> <ident>

<primary>      --> INTLITERAL #process_iteral

<add op>       --> PLUSOP #process_op

<add op>       --> MINUSOP #process_op

<ident>        --> ID #process_id

<system goal>   --> <program> SCANEOF #finish

 


符号表例程的规范及相应于Micro动作符号的语义例程所必须的辅助例程

//判断s是否在符号表中

extern int lookup(string s);

 

//s无条件地加入符号表

extern void enter(string s);

 

//辅助例程check_id():把一个变量加入符号表

//并生成一条预留存储空间的汇编命令语句来声明变量

void check_id(string s)

{

if(! lookup(s))

{

enter(s);

//创建一条完整的指令

generate("Declare"s, "Integer"");

}

}

 

//函数get_temp()分配临时变量

char *get_temp(void)

{

//目前为止最大的临时分配空间

static int max_temp 0;

static char tempname[MAXIDLEN];

max_temp++;

sprintf(tempname, "Temp&%d"max_temp);

check_id(tempname);

return tempname;

}

 

 


Micro的动作例程:

void start(void)

{

 

}

void finish(void)

{

 

generate("Halt"""");

}

void assign(expr_rec target, expr_rec source)

{

 

generate("Store"extract(source), target.name, ");

}

op_rec process_op(void)

{

 

op_rec o;

if(current_token == PLUSOP)

o.operator PLUS;

else

o.operator MINUS;

return o;

}

expr_rec gen_infix(expr_rec e1, op_rec op, expr_rec e2)

{

expr_rec erec;

 

erec.kind TEMPEXPR;

 

strcpy(erec.name, get_temp());

generate(extract(op), extract(e1), extract(e2), erec.name);

return erec;

}

void read_id(expr_rec in_var)

{

 

generate("Read"in_var.name, "Integer"");

}

expr_rec process_id(void)

{

expr_rec t;

 

check_id(token_buffer);

t.kind IDEXPR;

strcpy(t.name, token_buffer);

return t;

}

expr_rec process_literal(void)

{

expr_rec t;

 

t.kind LITERALEXPR;

(void)sscanf(token_buffer, "%d"&t.val);

return t;

}

void write_expr(expr_rec out_expr)

{

generate("Write"extract(out_expr), "Integer"");

}

 

 


修改语法分析过程以包含语义处理的例子:

未包含语义处理之前:

void expression_r(void)

{

 

primary();

for(t next_token(); == PLUSOP || == MINUSOP; next_token())

{

add_op();

primary();

}

}


包含了语义处理之后:

void expression_r(expr_rec *result)

{

expr_rec left_operand, right_operand;

op_rec op;

primary(&left_operand);

while(naex_token() == PLUSOP || next_token == MINUSOP)

{

add_op(&op);

primary(&right_operand);

left_operand gen_infix(left_operand, op, right_operand);

}

*result left_operand;

}

 


递归下降语法分析和翻译示例:begin := BB 314 A; end SCANEOF

上述过程中语法分析器动作有:

1:调用语法分析例程(如:system_goal()statement_list()等)找出输入中的一个字符串来匹配文法中的非终结符

2:调用match()来匹配文法终结符和一个输入词法记号(如:match(BEGIN)match(ID)等)

3:调用语义动作例程(如:start()process_id()等)

 


分析步骤记录:

 Step   Parser Action            Remaining Input                     Generated Code

_______________________________________________________________________________

(1)Call system_goal()          begin := BB 314 A; end SCANEOF

(2)Call program()             begin := BB 314 A; end SCANEOF

(3)Semantic Action: start()      begin A:= BB 314 A; end SCANEOF

(4)Math(BEGIN)             begin A:= BB 314 A; end SCANEOF

(5)Call statement_list()         := BB 314 A; end SCANEOF

(6)Call statement()             := BB 314 A; end SCANEOF

(7)Call ident()                 := BB 314 A; end SCANEOF

(8)match(ID)                 := BB 314 A; end SCANEOF

(9)Semantic Action:           := BB 314 A; end SCANEOF       Declare A, Integer

       process_id()

(10)match(ASSIGNOP)        := BB 314 A; end SCANEOF

(11)Call expression_r()          BB 314 A; end SCANEOF

(12)Call primary()             BB 314 A; end SCANEOF

(13)Call ident()               BB 314 A; end SCANEOF

(14)match(ID)                BB 314 A; end SCANEOF

(15)Semantic Action:          314 A; end SCANEOF          Declare BB, Integer

          process_id()

(16)Call add_op()             314 A; end SCANEOF

(17)match(MINUSOP)         314 A; end SCANEOF

(18)Semantic Action:          314 A; end SCANEOF

           process_op()

(19)Call primary()             314 A; end SCANEOF

(20)match(INTLITERAL)       314 A; end SCANEOF

(21)Semantic Action:           A; end SCANEOF

           process_literal()

(22)Semantic Action:           A; end SCANEOF       Declare Temp&1, Integer               

           Gen_infix()                                  Sub BB, 314, Temp&1    

(23)Call add_op()              A; end SCANEOF

(24)mathc(PLUSOP)           A; end SCANEOF

(25)Semantic Action:            A; end SCANEOF

           process_op()

(26)Call primary()              A; end SCANEOF

(27)Call ident()                 A; end SCANEOF

(28)match(ID)                  A; end SCANEOF

(29)Semantic Action:            end SCANEOF

           process_id()

(30)Semantic Action:            end SCANEOF          Declare Temp&2, Integer

           gen_infix()                                   Add Temp&1, A, Temp&2

(31)Semantic Action: assign()     end SCANEOF          Store Temp&2, A

(32)match(SEMICOLON)        end SCANEOF

(33)match(END)                end SCANEOF

(34)match(SCANEOF)           SCANEOF

(35)Semantic Action: finish()                                  Halt

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值