[编译原理随记]简单的中序转后缀算术翻译器 ii

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

上级文章:

[编译原理随记]词法分析器(继中序转后缀算术翻译器):https://blog.youkuaiyun.com/qq_28033719/article/details/116273702


准备知识

操作符的优先级和结合性:

平时计算的时候 * /(乘除)比 + - 优先级高,并且为左结合,所以写产生式的时候是 +- 组合 的下级是
*/ 组合,下面产生式没有消除左递归,也没有定义出空串。

expr \rightarrow expr + term | expr - term

term\rightarrow term\ast factor\mid term/factor\mid termMODfactor\mid termDIVfactor

factor\rightarrow digital\mid \left ( expr \right )

左结合:

就是运算对象先和左边算术运算符进行计算,比如 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运行即可

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值