作业模拟实现词法分析器,记录一下。
题目:
一、待分析的C语言子集的词法
1. 关键字
main if else int return void while (都是小写)2. 专用符号
= + — * / < <= < >= = = != ; : ,{ } [ ] ( )3. 其他标记
STRING::= " [^"]* "
ID::=letter(letter|digit)*
INT::=digit digit*
letter::= a|…|z|A|…|Z
digit::= 0|…|9
- 空格由空白、制表符和换行符组成
空格一般用来分隔ID、NUM、专用符号和关键字,词法分析阶段通常被忽略。
二、部分单词符号对应的种别码(可自行扩展)
单词符号 种别码 单词符号 种别码
三、词法分析程序的功能
输入:所给文法的源程序字符串
输出:二元组(syn, token或sum)构成的序列。其中syn 为单词种别码;token 为存放的单词自身字符串;sum为整型常量(作为常量的值)。实现时,可将单词的二元组用结构进行处理。
思路:
词法分析器就是把代码分成一个一个的单词,所以函数主体就是对单个单词进行分类。
从文件读入字符,然后对字符进行判断;
- 如果第一个字符是字母,就放入暂存单词数组,并且后面只要是字母或者数字或者_都加入存入数组,然后再和保留字数组中的保留字一一对比,符合就是相应的保留字,否则就是标识符。
- 如果第一个字符是数字,那么只要是数字或者.都放入暂存数组,直都不符合,已经存入的就是常量。
- 如果第一个字符是各种符号,直接用switch就好了。
- 最后外面再弄一个循环不停地调用函数,每调用一次就识别一个单词,然后进行输出,一直到最后结束循环,词法分析完成。
源码:
#include<iostream>
#include<fstream>
using namespace std;
#define M 12
#define wordNum 10//关键字数量
const int max_word = 505;
char token[M] ;
char in[105];
FILE* fin, * fout;
int cnt = 0, token_num = 0;
int row = 1;
int flag = 0;
char ch;
//关键字
const char keyWord[wordNum][20] = { "main","int","char","if","else","for","while",
"return","void" };
void setNULL() {
for (int i = 0; i < M; i++) {
token[i] = NULL;
}
}
int judge_token() {
setNULL();
if (flag == 0) {
ch = getc(fin);
}
flag = 1;
while (ch == ' ' || ch == '\t' || ch == '\n') {//遇到这些就继续获取后面的内容
if (ch == '\n') {//遇上换行符就行号加一
row++;
}
ch = getc(fin);
}
token_num = 0;
if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) {
//可能为标识符或者变量名
while ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9')) {
token[token_num++] = ch;
ch = getc(fin);
}
token[token_num++] = '\0';
for (int i = 0; i < wordNum; i++) {
if (strcmp(token, keyWord[i]) == 0) {
//关键词字
return i+1;
}
}
//标识符
return 10;
}
//第一个是数字
else if (ch >= '0' && ch <= '9') {
while ((ch >= '0' && ch <= '9') || ch == '.') {
token[token_num++] = ch;
ch = getc(fin);
}
return 20;//常量
}
else {
token[token_num++] = ch;
switch (ch) {
case '(': ch = getc(fin); return 26;
case ')': ch = getc(fin); return 27;
case '[': ch = getc(fin); return 28;
case ']': ch = getc(fin); return 29;
case '{': ch = getc(fin); return 30;
case '}': ch = getc(fin); return 31;
case '+':
ch = getc(fin);
if (ch == '+') {
token[token_num++] = ch;
ch = getc(fin);
return 41;//递增
}
else {
return 22;//加号
}
case '-':
ch = getc(fin);
if (ch == '-') {
token[token_num++] = ch;
ch = getc(fin);
return 42;//递减
}
else {
return 23;//减号
}
case '*':ch = getc(fin); return 24;
case '/':
ch = getc(fin);
if (ch == '/') {
token[token_num++] = ch;
ch = getc(fin);
return 43;//注释
}
else {
return 25;//除号
}
//这里要重新编码
case '=':
ch = getc(fin);
if (ch == '=') {
token[token_num++] = ch;
ch = getc(fin);
return 29;//比较符号
}
else {
return 21;//赋值符号
}
case '>':
ch = getc(fin);
if (ch == '=') {
token[token_num++] = ch;
ch = getc(fin);
return 37;//大于等于
}
else if (ch == '>') {
token[token_num++] = ch;
ch = getc(fin);
return 44;//输入符号或者右移符号
}
else {
return 35;//大于号
}
case '<':
ch = getc(fin);
if (ch == '=') {
token[token_num++] = ch;
ch = getc(fin);
return 38;//小于等于号
}
else if(ch=='<'){//输出符号或者右移符号
token[token_num++] = ch;
ch = getc(fin);
return 45;
}
else {
return 36;//小于号
}
case ',': ch = getc(fin); return 32;
case ':': ch = getc(fin); return 33;
case ';':ch = getc(fin); return 34;
case '!':
ch = getc(fin);
if (ch == '=') {
token[token_num++] = ch;
ch = getc(fin);
return 40;//不等于
}
else {
return 46;//非
}
case EOF: return -1;
default: ch = getc(fin); return -10;//没考虑到的情况
}
}
}
void getWord() {
int temp;
while (1) {
temp = judge_token();
if (temp == -1) {
break;//文件已经扫描完毕,结束循环
}
switch (temp) {
case -10:
cout << "第 " << row << " 行出现错误." << endl;
break;
default:
cout << "<" << temp << "," << token << ">" << endl;
break;
}
}
}
void main() {
fopen_s(&fin,"C://Users//羽墨轩//source//repos//Project4//ABC.txt", "r");
getWord();
}