编译原理课程实验一——词法分析
实验目的
- 巩固对词法分析的基本功能和原理的认识。
- 能够应用自动机的知识进行词法分析。
- 理解并处理词法分析中的异常和错误。
实验要求
- 掌握词法分析的基本功能,并将其实现。
- 词法分析程序应具有较好的可扩展性,应清晰明确。
- 除对相关问题的全面考虑外,还需对局部作一些优化考虑(如符号表)
实验内容
- 给出语言的词法规则描述
- 标识符、关键字、整常数、字符常数、浮点常数
- 单界符:+,-,X,:,…
- 双界符:/*,:=,…
- 注释
- 针对这种单词的状态转换图和程序框图
- 核心数据结构的设计(如符号表、关键字等)
- 错误处理(错误的位置及类型等)
代码
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <string.h>
#define SIZE 10240 //默认大小
int token; // 当前标记
char *src; // 指向源代码字符串指针
int poolsize; // 默认文本/数据大小
int line; // 源码行号
char *keywords[26]={
"main", "if", "then", "while", "do", "static", "default",
"do", "int", "double", "struct", "break", "else", "long",
"switch", "case", "typedf", "char", "return", "const", "float",
"short", "continue", "for", "void", "sizeof"
};
typedef struct node{
int key;
char value[100];
struct node *next;
}node;
//用于词法分析,获取下一个标记,它将自动忽略空白字符。
struct node* next(struct node *p){
int key = 0, i, j;
char value[100];
for(i = 0;i < 100; i++){
value[i] = 0;
}
while(1){ //跳过不识别的字符以及处理空白字符
token = *src;
src++;
if (token == '\n'){ //处理换行符,当前的行号加一
++line;
}
else if(token == ' '){
if(*src == ' '){
src++;
}
}
else if (token == '#'){ //处理宏定义
while (*src != 0 && *src != '\n'){
src++;
}
key = 0;
value[0] = '#';
break;
}
else if ((token >= 'a' && token <= 'z') || (token >= 'A' && token <= 'Z') || (token == '_')){
// 解析标识符
i = 0;
value[i]=token;
while ((*src >= 'a' && *src <= 'z') || (*src >= 'A' && *src <= 'Z') || (*src >= '0' && *src <= '9') || (*src == '_')){
value[i+1] = *src;
src++;
i++;
}
value[i+1] = '\0';
key = 301;
//查询已有的标识符
for (j = 0; j < 26; j++){
if (strcmp(value, keywords[j]) == 0){
key = j + 1;
break;
}
}
break;
}
//整数 和 浮点数
else if (token >= '0' && token <= '9'){
value[0] = token;
if (token > '0'){
for (i = 1; *src >= '0' && *src <= '9'; i++){
value[i] = *src;
src++;
}
int temp = i;
if (*src == '.'){
value[temp] = *src;
src++;
for (int j = temp + 1; *src >= '0' && *src <= '9'; j++){
value[j] = *src;
src++;
}
key = 402;
break;
}else{
key = 401;
break;
}
}
else{
if (*src == '.'){
value[1] = *src;
src++;
for (i = 2; *src >= '0' && *src <= '9'; i++){
value[i] = *src;
src++;
}
key = 402;
break;
}else{
key = 401;
break;
}
}
}
//字符串
else if (token == 34){
value[0] = (char)34;
i = 1;
while (*src != (char)34){
value[i] = *src;
i++;
src++;
}
value[i] = (char)34;
src++;
key = 403;
break;
}
//注释
else if (token == '/'){
if (*src == '/'){
// skip comments
value[0] = '/';
value[1] = '/';
i = 2;
src++;
while (*src != '\n'){
value[i] = *src;
i++;
src++;
}
key = 501;
break;
}
else if (*src == '*'){
value[0] = '/';
value[1] = '*';
i = 2;
src++;
while (*src != '\n'){
value[i] = *src;
i++;
src++;
}
key = 502;
break;
}
else{
// divide operator
key = 101;
value[0] = token;
break;
}
}
else if (token == '+'){
// parse '+' and '++'
if (*src == '+'){
src++;
key = 102;
value[0] = value[1]='+';
break;
}
else{
key = 103;
value[0] = '+';
break;
}
}
else if (token == '-'){
// parse '-' and '--'
if (*src == '-'){
src++;
key = 104;
value[0] = value[1]='-';
break;
}
else{
key = 105;
value[0] = '-';
break;
}
}
else if (token == '*'){
key = 106;
value[0] = '*';
break;
}
else if (token == '='){
// parse '==' and '='
if (*src == '='){
src++;
key = 107;
value[0] = value[1] = '=';
break;
}
else{
key = 108;
value[0] = '=';
break;
}
}
else if (token == '!'){
// parse '!='
if (*src == '='){
src++;
key = 109;
value[0] = '!';
value[1] = '=';
break;
}
}
else if (token == '<'){
// parse '<=', '<<' or '<'
if (*src == '='){
src++;
key = 110;
value[0] = '<';
value[1] = '=';
break;
}
else if (*src == '<'){
src++;
key = 111;
value[0] = value[1]='<';
break;
}
else{
key = 112;
value[0] = '<';
break;
}
}
else if (token == '>'){
// parse '>=', '>>' or '>'
if (*src == '='){
src++;
key = 113;
value[0] = '>';
value[1] = '=';
break;
}
else if (*src == '>'){
src++;
key = 114;
value[0] = value[1]='>';
break;
}
else{
key = 115;
value[0] = '>';
break;
}
}
else if (token == '|'){
// parse '|' or '||'
if (*src == '|'){
src++;
key = 116;
value[0] = value[1]='|';
break;
}
else{
key = 117;
value[0] = '|';
break;
}
}
else if (token == '&'){
// parse '&' and '&&'
if (*src == '&'){
src++;
key = 118;
value[0] = value[1]='&';
break;
}
else{
key = 119;
value[0] = '&';
break;
}
}
else if (token == '^'){
key = 120;
value[0] = '^';
break;
}
else if (token == '%'){
key = 121;
value[0] = '%';
break;
}
else if (token == '['){
key = 122;
value[0] = '[';
break;
}
else if (token == ']'){
key = 123;
value[0] = ']';
break;
}
else if (token == ';'){
key = 201;
value[0] = ';';
break;
}
else if (token == '{'){
key = 251;
value[0] = '{';
break;
}
else if (token == '}'){
key = 252;
value[0] = '}';
break;
}
else if (token == '('){
key = 253;
value[0] = '(';
break;
}
else if (token == ')'){
key = 254;
value[0] = ')';
break;
}
else if (token == ','){
key = 202;
value[0] = ',';
break;
}
else if(token==0){
break;
}
else {
printf("Error,第%d行出现错误!\n", line);
src++;
}
}
p->key = key;
for(i = 0;i < 100;i++){
p->value[i] = value[i];
}
node *q = (node *)malloc(sizeof(node));
q->next = NULL;
p->next = q;
return p;
}
//词法分析的入口,分析整个 C 语言程序。
void program(struct node *p){
printf("词法分析产生的词法二元式为: \n");
p=next(p); // 获取下一个标记
while (token > 0){
printf("< %d , %s >\n", p->key, p->value);
p=p->next;
next(p);
}
}
//主函数
int main(void){
int i;
FILE *fd;
char path[100];
struct node *head;
line = 1;
head = (node *)malloc(sizeof(node));
head->next = NULL;
printf("词法分析器\n"
"请输入源语言文件绝对路径: ");
scanf("%s", path);
if ((fd = fopen(path, "r")) == NULL){
printf("could not open(%s)\n", path);
return -1;
}
if (!(src = (char *)malloc(SIZE))){
printf("could not malloc(%d) for source area\n", poolsize);
return -1;
}
// 读取源文件
if ((i = fread(src,1,SIZE,fd)) <= 0){
printf("read() returned %d\n", i);
return -1;
}
src[i] = 0; // 添加EOF字符
fclose(fd);
program(head);
return 0;
}
符号表
测试程序
#include <stdio.h>
//get fibonacci : programmer
int fibonacci(int i) {
if (i <= 1) {
return 1;
}
else{
return fibonacci(i-1) + fibonacci(i-2);
}
}
/* main */
int main()
{
int i;
float f, ff;
f = 0.123;
ff = 12.30;
i = 0;
char temp[100] = "Hello";
while (i <= 10) {
//printf("fibonacci(%2d) = %d\n", i, fibonacci(i));
fibonacci(i);
?
i = i + 1;
}
return 0;
}
词法分析结果(部分)
结语
本博客做得十分匆忙,因此内容十分粗糙。并且本文的词法分析程序实现的方式比较low(逐个字符进行判断),没有用到自动机等内容(暂时没有这么多时间来实现)。算是先挖个坑吧,以后有时间了再认真重新更新一篇(然后大概率就忘了)。