词法分析(英语:lexical analysis):
是计算机科学中将字符序列转换为标记(token)序列的过程。进行词法分析的程序或者函数叫作词法分析器(lexical analyzer,简称lexer),也叫扫描器(scanner)。词法分析器一般以函数的形式存在,供语法分析器调用。
(上文摘自维基百科)
分析:
我们知道,语法分析器是分析每个词的,既然这样的话,我们就要对每个词进行分析,然后识别保存,那么如何进行识别呢?
我们通常采用的是种别码,那什么是种别码呢?初学者可能会对这个概念比较模糊。其实种别码就是对每个词的定位,比如我们可以把数字这个词的种别码定义为10,把标识符的种别码定义为11等等。
代码实际讲解:
种别码
/保留字的种别码。从1开始到25
//出现过的标识符的种别码设为26
//未出现过的标识符种别码为52,将该标识符保存到标识符表中
//数字种别码设为27
// 等号种别码为28
// <= 种别码为29.
// <> 种别码为30。
// < 种别码为31
// >= 种别码为32
// > 种别码为33.
// + 种别码为34
// - 种别码为35
// * 种别码为36
// / 种别码 53
// ;种别码为38
// = 种别码为28
//1到51为正常的。空格为100, 换行符为101, #号为102,错误为404
//case ‘(’: syn = 39;
//case ‘)’: syn = 40;
上面这部分是我一部分字符的种别码,没有列出所有,其他种别码读者可以在代码中查询。
其中有一个需要注意一下的是,标识符的种别码分为两种,一种是之前出现过的,为26,一种是新出现的,为52。
我们设定以#为结束符。
关于代码可以看一下我上一篇关于词法分析器的文章,主要不一样是关于种别码的,也是跟语法分析器配合
源代码:
#include<stdio.h>
#include<conio.h>
#include<math.h>
#include<string.h>
#include<stdlib.h>
int wordanalysis();
using namespace std;
//BEGIN
//
//
//a = 10;
//b = a + 10;
//e = a + b
//end#
//保留字的种别码。从1开始到25
//出现过的标识符的种别码设为26
//未出现过的标识符种别码为52,将该标识符保存到标识符表中
//数字种别码设为27
// 等号种别码为28
// <= 种别码为29.
// <> 种别码为30。
// < 种别码为31
// >= 种别码为32
// > 种别码为33.
// + 种别码为34
// - 种别码为35
// * 种别码为36
// / 种别码 53
// ;种别码为38
// = 种别码为28
//1到51为正常的。空格为100, 换行符为101, #号为102,错误为404
//case '(': syn = 39;
//case ')': syn = 40;
int i, row = 0, line = 0; //line用来保存标识符的个数。row来保存数字个数。i保存读到哪个字符了
char test[1000]; //这是读取文件的内容的。
int number[100]; //常数表,有个疑问,数字需要常数来保存吗?
char mark[100][5]; //用来存放标识符的。
int count = 1; //记录出错的行号
int syn;
int wu;
int final_flag = 0;
//关键字:
char pro[100][100] = { "PROGRAM", "BEGIN", "END", "VAR", "INTEGER", "WHILE",
"IF", "THEN", "ELSE", "DO", "PROCEDURE" ,"char",
"int","if","else","var" ,"return","break",
"do","while","for","double","float","short", "main" };
//关键字:从1开始到25.
//过滤,但是一般时候这样做的效率是非常低的。
void guolv() {
char temp[1000];
int flag = 0;
int j = 0;
while (test[flag] != '#') {
//单行注释//
if (test[flag] == '/' && test[flag + 1] == '/') {
while (test[flag] != '\n') {
flag++;
}
}
else if (test[flag] == '/' && test[flag + 1] == '*') {
//多行注释/**/
flag = flag + 2;
while (test[flag] != '*' && test[flag + 1] != '/') {
flag++;
}
flag = flag + 2;
}
temp[j] = test[flag];
flag++;
j++;
}
temp[j] = '#';
strcpy(test, temp);
}
//词法分析
int wordanalysis()
{
//标识符和保留字
//当第一个字符是字母的时候
if ((test[i] >= 'A'&&test[i] <= 'Z') || (test[i] >= 'a'&&test[i] <= 'z'))
{
char word[10];
//保留字的种别码。从1开始到25
int n = 0;
word[n++] = test[i++];
while ((test[i] >= 'A'&&test[i] <= 'Z') || (test[i] >= '0' && test[i] <= '9') || (test[i] >= 'a'&&test[i] <= 'z'))
{
word[n++] = test[i++];
}
word[n] = '\0';
i--;
//判断该标识符是否为保留字
for (n = 0; n < 100; n++)
{
if (strcmp(word, pro[n]) == 0)
{
syn = n + 1;
printf(">> %s\t(%d,-) 保留字\n", pro[n], syn);
return syn;
}
}
//不是关键字的话,判断该标识符是否在标识符表中
int m = 0;
if (line != 0)
{
int q = 0;
while (q<line)
{
if (strcmp(word, mark[q++]) == 0)
{
syn = 26; //出现过的标识符的种别码设为26
printf(">> %s\t(%d,%d) 标识符\n", word, syn, q);
return syn;
}
}
}
//未出现过的标识符种别码为52,将该标识符保存到标识符表中
strcpy(mark[line], word);
syn = 52;
printf(">> %s\t(%d, %d) 标识符\n", word, syn, line + 1);
line++;
return syn;
}
//数字
else if (test[i] >= '0' && test[i] <= '9')
{
char x[100];
int n = 0;
x[n++] = test[i++];
while (test[i] >= '0' && test[i] <= '9')
{
x[n++] = test[i++];
}
x[n] = '\0';
i--;
int num = atoi(x); //将字符串转换成int型
//判断该常数是否存在于常数表中
if (row != 0)
{
for (int y = 0; y<row; y++)
{
if (number[y] == num)
{
syn = 27; //数字种别码设为27
printf(">> %d\t(%d,%d)\n", num, syn, y + 1);
return syn;
}
}
}
//将该常数保存到标识符表中
number[row] = num;
int line = row;
syn = 27;
printf(">> %d\t(%d,%d)\n", num, syn, line + 1);
row++;
return syn;
}
//各种符号
else {
switch (test[i])
{
case '=': syn = 28; // 等号种别码为28
printf(">> =\t(%d,-)\n", syn); return syn;
case '<':
i++;
if (test[i] == '=')
{
syn = 29; // <= 种别码为29.
printf(">> <= \t(%d,-)\n", syn);
return syn;
}
else if (test[i] == '>')
{
syn = 30; // <> 种别码为30。
printf(">> <>\t(%d,-)\n", syn);
return syn;
}
else
{
i--;
syn = 31; // < 种别码为31
printf(">> <\t(%d,-)\n", syn);
return syn;
}
case '>':
i++;
if (test[i] == '=')
{
syn = 32; // >= 种别码为32
printf(">> >=\t(%d,-)\n", syn);
return syn;
}
else
{
i--;
syn = 33; // > 种别码为33.
printf(">> >\t(%d,-)\n", syn);
return syn;
}
case '+': syn = 34; printf(">> +\t(%d,-)\n", syn); return syn; // + 种别码为34
case '-': syn = 35; printf(">> -\t(%d,-)\n", syn); return syn; // - 种别码为35
case '*': syn = 36; printf(">> *\t(%d,-)\n", syn); return syn; // * 种别码为36
case '/': syn = 53; printf(">> *\t(%d, -)\n", syn); return syn; // /的种别码为53
case ':': syn = 37; printf(">> :\t(%d,-)\n", syn); return syn;
case ';': syn = 38; printf(">> ;\t(%d,-)\n", syn); return syn;
case '(': syn = 39; printf(">> (\t(%d,-)\n", syn); return syn;
case ')': syn = 40; printf(">> )\t(%d,-)\n", syn); return syn;
case '{': syn = 41; printf(">> {\t(%d,-)\n", syn); return syn;
case '}': syn = 42; printf(">> }\t(%d,-)\n", syn); return syn;
case '[': syn = 43; printf(">> [\t(%d,-)\n", syn); return syn;
case ']': syn = 44; printf(">> ]\t(%d,-)\n", syn); return syn;
case '|': syn = 45; printf(">> |\t(%d,-)\n", syn); return syn;
case '"': syn = 46; printf(">> \"\t(%d,-)\n", syn); return syn;
case ',': syn = 47; printf(">> ,\t(%d,-)\n", syn); return syn;
case '\'': syn = 48; printf(">> '\t(%d,-)\n", syn); return syn;//单引号
case '&':
i++;
if (test[i] != '&') {
i--;
// & 种别码为 49;
syn = 49;
printf(">> &\t(%d,-)\n", syn); return syn;
}
//&&种别码为50
syn = 50;
printf(">> &&\t(%d,-)\n", syn); return syn;
// \ 种别码为51
case '\\': syn = 51; printf(">> \\\t(%d,-)\n", syn); return syn;
case ' ':
syn = 100; // 空格种别码为28
return syn;
case '\n':count++;
syn = 101; // 换行符种别码为29
return syn;
case '#': syn = 102; //结束符#种别码为30.
return syn;
default:
syn = 404; //错误的种别码为404
printf(">> %c error in %d row\n", test[i], count);
return syn;
}
}
}
int main()
{
int c = 0;
int m;
i = 0;
//读取文件
FILE *fp;
fp = fopen("D:\\yes.txt", "r");
if (fp == NULL)
{
printf("can't open file!\n");
exit(0);
}
//把文件内容读取到test数组中
while (!feof(fp))
{
test[c++] = fgetc(fp);
}
test[c] = '#';
while (test[i] != '#') {
printf("%c", test[i]);
i++;
}
printf("\n");
printf("------------过滤后的-------------\n");
guolv();
i = 0;
while (test[i] != '#') {
printf("%c", test[i]);
i++;
}
printf("\n");
printf("输出文法分析后的:\n");
//1到51为正常的。空格为100, 换行符为101, #号为102,错误为404
i = 0;
while(1){
int is = wordanalysis();
if(is == 102){
break;
}
else{
i++;
}
}
return 0;
}
运行结果:
BEGIN
a = 10;
//fad a = b + c;
b = a + 10;
e = a + b;
d = (a + b) + e;
d = (a + b)
END
------------过滤后的-------------
BEGIN
a = 10;
b = a + 10;
e = a + b;
d = (a + b) + e;
d = (a + b)
END
输出文法分析后的:
>> BEGIN (2,-) 保留字
>> a (52, 1) 标识符
>> = (28,-)
>> 10 (27,1)
>> ; (38,-)
>> b (52, 2) 标识符
>> = (28,-)
>> a (26,1) 标识符
>> + (34,-)
>> 10 (27,1)
>> ; (38,-)
>> e (52, 3) 标识符
>> = (28,-)
>> a (26,1) 标识符
>> + (34,-)
>> b (26,2) 标识符
>> ; (38,-)
>> d (52, 4) 标识符
>> = (28,-)
>> ( (39,-)
>> a (26,1) 标识符
>> + (34,-)
>> b (26,2) 标识符
>> ) (40,-)
>> + (34,-)
>> e (26,3) 标识符
>> ; (38,-)
>> d (26,4) 标识符
>> = (28,-)
>> ( (39,-)
>> a (26,1) 标识符
>> + (34,-)
>> b (26,2) 标识符
>> ) (40,-)
>> END (3,-) 保留字