上级文章:
[编译原理随记]词法分析器(继中序转后缀算术翻译器):https://blog.youkuaiyun.com/qq_28033719/article/details/116273702
准备知识
操作符的优先级和结合性:
平时计算的时候 * /(乘除)比 + - 优先级高,并且为左结合,所以写产生式的时候是 +- 组合 的下级是
*/ 组合,下面产生式没有消除左递归,也没有定义出空串。
左结合:
就是运算对象先和左边算术运算符进行计算,比如 a - b + c先将 a - b 左边结合计算减号(-),然后再和 + c 进行计算加号(+)。
深度优先遍历:
就是算法中DFS,解析语句时候就是都是“贪心”地先处理最下级的东西。
结构处理
1、首先列一下算术运算产生式清单,定义应该是:
start -> list eof
list -> expr; list | ε (厄普西隆,空串的意思,相当于输入为null)
expr -> expr + term | expr - term
term -> term * factor | term \ factor | term MOD(求模) factor | term DIV(求余) factor
factor -> digital | (expr)
然后翻译模式处理一下:
start -> list eof
list -> expr; list
| ε
expr -> expr + term {printf("+")}
| expr - term {printf("-")}
term -> term * factor {printf("*")}
| term / factor {printf("/")}
| term MOD(求模) factor {printf("MOD")}
| term DIV(求余) factor {printf("DIV")}
factor -> digital {printf(digital.val)}
| (expr)
消除左递归:
start -> list eof
list -> expr; list
| ε
expr -> term expr'
expr' -> + term {printf("+")} expr'
| - term {printf("-")} expr'
| ε
term -> factor term'
term' -> * factor {printf("*")} term'
| / factor {printf("/")} term'
| MOD(求模) factor {printf("MOD")} term'
| DIV(求余) factor {printf("DIV")} term'
| ε
factor -> digital {printf(digital.val)}
| (expr)
2、然后我们需要:
词法分析器模块(lexer)| 语法分析器模块(parser)| 输出模块(emitter)| 符号表模块(symbol init)| 错误处理模块(error)
各个模块调用关系应该是(没有使用UML类图):

因此可以将一个简单的编译器做出来,将中序表达式变为后缀表达式,一共八个文件,用devcpp建立项目就好:
1、中间流程四个文件
global.h 全局头文件:
#pragma once
#include <stdio.h>
#include <ctype.h>
#include <cstdlib>
#include <cstring>
#define BSIZE 128
#define NONE -1
#define EOS '\0'
#define NUM 256
#define DIV 257
#define MOD 258
#define ID 259
#define DONE 260
struct entry {
char *lexptr;
int token;
};
extern int token_val; // 必须有外部覆盖变量
extern int line_no;
extern struct entry symtable[];
// lexan.c
int lexan();
// parser.c
void parse();
void expr();
void term();
void factor();
bool match(int t);
// symbol.c
int lookup(char s[]);
int insert(char s[], int tok);
// init.c
void init();
// error.c
void error(char *msg);
// emitter.c
void emit(int t, int t_val);
lexer.c 词法分析器:
#include "global.h"
char lex_buf[BSIZE];
int line_no = 1;
int token_val;
int lexan() {
int t = 1;
while(1) {
t = getchar();
if(t == ' ' || t == '\t');
else if(t == '\n') line_no ++;
else if(isdigit(t)) {
ungetc(t, stdin);
scanf("%d", &token_val);
return NUM;
} else if(isalpha(t)) {
int p, b = 0;
lex_buf[b ++] = t;
t = getchar();
while(isalpha(t)) {
if(b + 1 >= BSIZE) error("too long alp");
lex_buf[b ++] = t;
t = getchar();
}
lex_buf[b] = EOS;
if(t != EOF) ungetc(t, stdin);
p = lookup(lex_buf); // 查符号表是否是关键字
if(p == 0) p = insert(lex_buf, 1); // 插入新符号 , 拿出指针位置
token_val = p;
return symtable[p].token;
} else if(t == EOF) {
return DONE;
} else {
token_val = NONE;
return t;
}
}
}
parser.c 语法分析器:
#include "global.h"
int lookahead;
/**
expr -> term moreterms
moreterms -> + term moreterms
| - term moreterms
| ε
term -> factor more factors
morefactors -> * factor
| / factor
| div factor
| mod factor
| ε
factor -> (expr)
| id
| num
**/
void parse() {
lookahead = lexan();
while(lookahead != DONE) {
expr(); match(';');
}
printf("\n");
}
void expr() {
int t;
term();
while(1) {
switch(lookahead) {
case '+' : case '-' :
t = lookahead;
match(lookahead);
term();
emit(t, NONE);
continue;
default: return;
}
}
}
void term() {
int t;
factor();
while(1)
switch(lookahead) {
case '*': case'/': case DIV: case MOD:
t = lookahead;
match(lookahead);
factor();
emit(t, NONE);
continue;
default: return;
}
}
void factor() {
switch(lookahead) {
case '(':
match('('); expr(); match(')'); break;
case NUM:
emit(NUM, token_val); match(NUM); break;
case ID:
emit(ID, token_val); match(ID); break;
default: error("no symbol in function factor");
}
}
bool match(int t) {
if(lookahead == t) {
lookahead = lexan();
} else error("wrong lookahead in function match");
}
emiter.c 输出器:
#include "global.h"
void emit(int t, int t_val) {
switch(t) {
case '+': case '-': case '*': case '/':
printf("%c ", t); break;
case DIV:
printf("DIV "); break;
case MOD:
printf("MOD "); break;
case NUM:
printf("%d ", t_val); break;
case ID:
printf("%s ", symtable[t_val].lexptr); break;
default: printf("token %d, tokenval %d\n", t, t_val);
}
}
2、符号表两个文件
init.c 初始化符号表
#include "global.h"
struct entry keywords[] = {
{"div", DIV},
{"mod", MOD},
{"DIV", DIV},
{"MOD", MOD},
{0, 0},
};
void init() {
struct entry *ptr;
for(ptr = keywords; ptr->token; ptr ++) {
insert(ptr->lexptr, ptr->token);
}
}
symbol.c 符号表
#include "global.h"
#define STRMAX 999
#define SYMMAX 100
int last_char = -1;
int last_entry = 0;
char lexemes[STRMAX];
struct entry symtable[SYMMAX];
int lookup(char s[]) {
int p;
for(p = last_entry; p > 0; p --) {
if(strcmp(symtable[p].lexptr, s) == 0) return p;
}
return 0;
}
int insert(char s[], int tok) {
int len = strlen(s);
/** 先检查空间是否充足 **/
if(last_entry + 1 >= SYMMAX) error("symbol table full in function insert");
if(last_char + len + 1 >= STRMAX) error("lexemes array full in function insert");
last_entry ++;
symtable[last_entry].token = tok;
symtable[last_entry].lexptr = &lexemes[last_char + 1];
/** 先会把s拷贝到lexptr指向的地址处 **/
last_char += len + 1;
strcpy(symtable[last_entry].lexptr, s);
return last_entry;
}
3、错误打印一个文件:
error.c 错误输出文件
#include "global.h"
void error(char *msg) {
printf("line %d: %s\n", line_no, msg);
system("pause");
exit(1);
}
4、运行文件:
main.c 程序入口
#include "global.h"
int main()
{
printf("记得用;结尾\n");
init();
parse();
return 0;
}
5、最后就是在devc++F11运行即可


这篇博客介绍了如何设计一个简单的中序转后缀算术翻译器,涉及操作符优先级和结合性、深度优先遍历等编译原理知识。文章详细阐述了算术运算产生式、消除左递归的过程,并提到了编译器的模块组成,包括词法分析器、语法分析器、输出模块和符号表等,以及它们之间的调用关系。还给出了项目中各个源文件的组织结构和运行步骤。
680

被折叠的 条评论
为什么被折叠?



