第一部分:词法分析
我们从一个简单的词法分析器开始我们的编译器编写。词法分析器的工作是依次扫视字符串形式源程序中的各个字符,逐个识别出其中的单词,并将其转换为内部编码形式的单词符号串作确为输出。
从简单的单词开始:
五个基本的数学运算符(*,/,+,-,% )和十进制整数(0…9)
扫描到的每个单词都将存储在此结构中:
// 单词结构体
struct token
{
int token;
int intvalue;
};
其中 token 字段可以为以下值之一:
enum
{
T_EOF, T_ADD, T_SUB, T_MUL, T_DIV, T_MOD, T_INT
};
它们分别代表:"EOF","+", "-", "*", "/", "%", "int"
当 token 为 T_INT 时,即单词为整数时,intvalue=整数值
核心代码:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
// 单词类型
enum
{
T_EOF, T_ADD, T_SUB, T_MUL, T_DIV, T_MOD, T_INT
};
// 单词结构体
struct token
{
int token;
int intvalue;
};
int scan(struct token *t);
// data.h
int Line; //当前读入行数
int Putbackchr;
FILE *Infile;
// 返回字符 c 在字符串 s 中的位置,如果没有找到 c,则返回 -1
int get_chrpos(char *s, int c)
{
char *p;
p = strchr(s, c);
return p ? p - s : -1;
}
// 从输入文件中获取下一个字符
int get_nextchr()
{
int c;
if (Putbackchr)
{
c = Putbackchr;
Putbackchr = 0;
return c;
}
c = fgetc(Infile);
if ('\n' == c)
{
Line++;
}
return c;
}
// 放回多拿的字符
void put_backchr(int c)
{
Putbackchr = c;
}
// 跳过空格、换行符,返回需要处理的第一个字符
int skip_getchr()
{
int c;
c = get_nextchr();
while (' ' == c || '\t' == c || '\n' == c || '\r' == c || '\f' == c)
{
c = get_nextchr();
}
return c;
}
// 扫描输入文件,返回一个整数文字值
int scan_int(int c)
{
int k, val = 0;
while ((k = get_chrpos("0123456789", c)) >= 0)
{
val = val * 10 + k;
c = get_nextchr();
}
put_backchr(c);
return val;
}
// 扫描并返回在输入中找到的下一个单词。
// 如果标记有效则返回 1,如果没有标记则返回 0
int scan(struct token *t)
{
int c;
c = skip_getchr();
switch (c)
{
case EOF: return (0);
case '+': t->token = T_ADD; break;
case '-': t->token = T_SUB; break;
case '*': t->token = T_MUL; break;
case '/': t->token = T_DIV; break;
case '%': t->token = T_MOD; break;
default: if (isdigit(c))
{
t->intvalue = scan_int(c);
t->token = T_INT;
break;
}
printf("Unrecognised character %c on line %d\n", c, Line);
exit(1);
}
return 1;
}
void init()
{
Line = 1;
Putbackchr = '\n';
}
// 单词列表
char *tokstr[] = { "+", "-", "*", "/", "%", "int" };
// 扫描文件
void scan_file()
{
struct token T;
while (scan(&T))
{
printf("Token %s", tokstr[T.token]);
if (T.token == T_INT)
{
printf(", value %d", T.intvalue);
}
printf("\n");
}
}
// 用法 compiler -o -s outfile infile
int main(int argc, char *argv[])
{
if(argc != 5)
{
fprintf(stderr, "compiler -o -s outfile infile\n");
exit(1);
}
init();
if ((Infile = fopen(argv[4], "r")) == NULL)
{
fprintf(stderr, "Unable to open %s: %s\n", argv[1], strerror(errno));
exit(1);
}
scan_file();
}
测试样例
100 + 10
98 - 9
90 * 2
1 / 18
15 % 5
Token int, value 100
Token +
Token int, value 10
Token int, value 98
Token -
Token int, value 9
Token int, value 90
Token *
Token int, value 2
Token int, value 1
Token /
Token int, value 18
Token int, value 15
Token %
Token int, value 5