1-3 S语言词法分析器设计

一、实验目的

了解词法分析程序的两种设计方法

  1. 根据状态转换图直接编程的方式;
  2. 利用DFA编写通用的词法分析程序。(选做)

二、实验内容

1. 根据状态转换图直接编程

编写一个词法分析程序,它从左到右逐个字符的对源程序进行扫描,产生一个个的单词的二元式,形成二元式(记号)流文件输出。在此,词法分析程序作为单独的一遍,如下图所示。

具体任务有:

  1. 组织源程序的输入
  2. 识别单词的类别并记录类别编号和值,形成二元式输出,得到单词流文件
  3. 删除注释、空格和无用符号
  4. 发现并定位词法错误,需要输出错误的位置在源程序中的第几行。将错误信息输出到屏幕上。
  5. 对于普通标识符和常量,分别建立标识符表和常量表(使用线性表存储),当遇到一个标识符或常量时,查找标识符表或常量表,若存在,则返回位置,否则填写符号表或常量表并且返回位置。
  6. 标识符表结构:变量名,类型(整型、实型、字符型),分配的数据区地址

注:词法分析阶段只填写变量名,其它部分在语法分析、语义分析、代码生成等阶段逐步填入。

常量表结构:常量名,常量值

2.编写DFA模拟程序(选做)

算法如下:

DFA(S=S0,MOVE[ ][ ],F[ ],ALPHABET[ ])
/*S为状态,初值为DFA的初态,MOVE[ ][ ]为状态转换矩阵,F[ ] 为终态集,ALPHABET[] 为字母表,其中的字母顺序与MOVE[ ][ ] 中列标题的字母顺序一致。*/
{
Char  Wordbuffer[10]=“”//单词缓冲区置空
Nextchar=getchar();//读
i=0;
while(nextchar!=NULL)//NULL代表此类单词
{ if (nextchar!∈ALPHABET[]) {ERROR(“非法字符”),return(“非法字符”);}
      S=MOVE[S][nextchar]  //下一状态
     if(S=NULL)return(“不接受”);//下一状态为空,不能识别,单词错误
      wordbuffer[i]=nextchar ; //保存单词符号
      i++;
      nextchar=getchar();
}
Wordbuffer[i]=‘\0’;
If(S∈F)return(wordbuffer);  //接受
    Else  return(“不接受”);
}

该算法要求:实现DFA算法,给定一个DFA(初态、状态转换矩阵、终态集、字母表),调用DFA(),识别给定源程序中的单词,查看结果是否正确。

三、实验要求

1. 能对任何S语言源程序进行分析(S语言定义见下面)

在运行词法分析程序时,应该用问答形式输入要被分析的S源语言程序的文件名,然后对该程序完成词法分析任务。

2.能检查并处理某些词法分析错误

词法分析程序能给出的错误信息包括:总的出错个数,每个错误所在的行号,错误的编号及错误信息。

本实验要求处理以下两种错误(编号分别为1,2):

  1. 非法字符:单词表中不存在的字符处理为非法字符,处理方式是删除该字符,给出错误信息,“某某字符非法”。
  2. 源程序文件结束而注释未结束。注释格式为:/* …… */

四、S语言定义

保留字和特殊符号表

种别代码type 单词 类别class 内码值
1 int 关键字
2 char 关键字
3 float 关键字
4 void 关键字
5 const 关键字
6 if 关键字
7 else 关键字
8 for 关键字
9 while 关键字
10 switch 关键字
11 break 关键字
12 begin 关键字
13 end 关键字
14 - 运算符
15 / 运算符
16 <= 运算符
17 + 运算符
18 * 运算符
19 % 运算符
20 < 运算符
21 > 运算符
22 >= 运算符
23 == 运算符
24 != 运算符
25 /= 运算符
26 += 运算符
27 -= 运算符
28 %= 运算符
29 *= 运算符
30 || 运算符
31 && 运算符
32 ! 运算符
33+1 [ 界符
34+1 ] 界符
35 , 界符
36 ) 界符
37 ( 界符
38 ; 界符
39+1 整形 整形 在常数表中的位置
40+1 标识符 标识符 在符号表中的位置
33 = 运算符

单词的构词规则:

  • 字母=[A-Z a-z]
  • 数字=[0-9]
  • 标识符=`(字母|)(字母|数字|)*
  • 数字=数字(数字)*( .数字+|ε)

S语言表达式和语句说明

1.算术表达式:+、-、*、/、% 2.关系运算符:>、>=、<、<=、==、!=
3.赋值运算符:=,+=、-=、*=、/=、%= 4.变量说明:类型标识符 变量名表; 5.类型标识符:int char float 6.If语句:if 表达式then 语句 [else 语句] 7.For语句:for(表达式1;表达式2;表达式3) 语句 8.While语句:while 表达式 do 语句`
9.S语言程序:由函数构成,函数不能嵌套定义。

函数格式为:

返回值   函数名(参数)
begin
   数据说明
    语句
end

复合语句构成

begin
    语句序列
end

五、程序结构参考说明

1.Initscanner函数:程序初始化:输入并打开源程序文件和目标程序文件,初始化保留字表
2.Scanner函数:若文件未结束,反复调用lexscan函数识别单词。
3.Lexscan函数:根据读入的单词的第一个字符确定调用不同的单词识别函数
4.Isalpha函数:识别保留字和标识符
5.Isnumber函数:识别整数,如有精力,可加入识别实数部分工功能
6.Isanotation函数:处理除号/和注释
7.Isother函数识别其他特殊字符
8.Output函数:输出单词的二元式到目标文件,输出格式(单词助记符,单词内码值),如(int,-)(rlop,>)……
9.Error函数:输出错误信息到屏幕
10.除此之外,还可以设置查符号表,填写符号表等函数,学生可自行设计。

六、朴素实现

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

// 记录已识别的单词
int node_num = 0;
int line = 1;
char buf[1024];

// 四元组
typedef struct Node{
    int type; // 种别代码 最小的分类单位
    char name[10]; // 变量名
    int* addr; // 变量地址
    int line; // 第几行
} Node;

Node nodes[1024] = {};

/*
 *  这一块是表的等价,如果需要添加或修改关键字,只需要改动一下代码,解耦
 */
// 保留字和特殊符号
// 关键字表(共13个)
char keyword[][10] = {
    "int", // 1
    "char", // 2
    "float", // 3
    "void", // 4
    "const", // 5
    "if", // 6
    "else", // 7
    "for", // 8
    "while", // 9
    "switch", //10
    "break", //11
    "begin", //12
    "end" //13
};

// 运算符表(共19个)
char calculation[][10] = {
    "-", //14
    "/", //15
    "<=", //16
    "+", //17
    "*", //18
    "%", //19
    "<", //20
    ">", //21
    ">=", //22
    "==", //23
    "!=", //24
    "/=", //25
    "+=", //26
    "-=", //27
    "%=", //28
    "*=", //29
    "||", //30
    "&&", //31
    "!" //32
    "=" // 41
};

// 界符表(共6个)
char delimiter[][10] = {
    "[", //33
    "]", //34
    ",", //35
    ")", //36
    "(", //37
    ";" //38
};


// 分别表示关键字、界符、运算符的个数
int nk = sizeof(keyword) / sizeof(keyword[0]);
int nd = sizeof(delimiter) / sizeof(delimiter[0]);
int nc = sizeof(calculation) / sizeof(calculation[0]);
// 分别是关键字、界符、运算符、整形、标识符在表中的偏移量 offset偏移o make标识符m 整形i
int ok = 1, oc = 14, od = 34, oi = 40, om = 41;

// -----函数声明-------
// 判断是否是整数
bool is_integer(char ch){
    return ch >= '0' && ch <= '9';
};
// 判断是否字母
bool is_character(char ch){
    return ch >= 'a' && ch <= 'z' || ch >= 'A' && ch <= 'Z';
}

// 判断字符串是否是关键字,是则返回下标,否则返回-1;不是关键字 则为标识符
int keyword_index_of(const char s[]){
    for (int i = 0; i < nk; i++){
        if (strcmp(s, keyword[i]) == 0){
            return i;
        };
    }
    return -1;
};
// 判断字符串是否纯字母
bool is_pureCharacter(const char s[], int len){
    bool flag = true;
    for (int i = 0; i < len; i++){
        if (!is_character(s[i]))
            flag = false;
    }
    return flag;
}

// 判断是否界符,是则返回下标,否则返回-1
int delimiter_index_of(char ch){
    for (int i = 0; i < nd; i++)
        if (ch == delimiter[i][0])
            return i;
    return -1;
};


// 根据种别代码范围判断类别
char* getClassStr(int n){
    static char name[20];

    if (n >= ok && n <= ok + nk - 1) strcpy(name, "关键字");
    else if (n >= oc && n <= oc + nc - 1) strcpy(name, "运算符");
    else if (n >= od && n <= od + nd - 1) strcpy(name, "界符");

    else if (n == oi) strcpy(name, "实数");
    else if (n == om) strcpy(name, "标识符");
    else strcpy(name, "未识别");

    return name;
}

bool annotation = false; // 是否处于注释中

// offset nodes数组元素偏移量,识别一行的单词,返回单词数量
int Lexscan(const char* s, Node* srcNode){
    Node* node = srcNode;
    char tokens[20];
    int i = 0; //记录token起止 i为开始 j为偏移量


    // 注意 fgets的末尾,\0结尾
    while (s[i] != '\0'){
        int j = 0;
        // "自旋"
        while (s[i] == '\n' || s[i] == '\r' || s[i] == '\t' || s[i] == ' '){
            i++;
        }
        // 如果注释中,匹配到终止符
        if (annotation){
            while ((s[i] != '*' && s[i + 1] != '/') && s[i] != '\0'){
                i++;
            }
            if (s[i] == '\0')break;
            else annotation = false;
        }
        if (s[i] == '\0') break; // !!!! 可能空格后没有单词
        // 字母 下划线始
        if (is_character(s[i]) || s[i + j] == '_'){
            j++;
            while (is_character(s[i + j]) || is_integer(s[i + j]) || s[i + j] == '_'){
                j++;
            }
            // 非字母下划线数字
            j--;
            if (is_pureCharacter(s + i, j + 1)){
                strncpy(tokens, s + i, j + 1);
                tokens[j + 1] = '\0';
                // 如果纯字母,判断是否关键字
                int index = keyword_index_of(s + i);
                if (index != -1){
                    // 是关键字
                    strcpy(node->name, tokens);
                    node->type = index + ok;
                    node++;
                    i = j + 1;

                    // printf("(%s, 关键字, %d)\n", tokens, node->type);

                    continue;
                }
            }
            // 是标识符
            strncpy(tokens, s + i, j + 1);
            tokens[j + 1] = '\0';
            node->type = om;
            strncpy(node->name, tokens, j + 1);
            node->name[j + 1] = '\0';
            node->line = line;
            node++;

            // printf("(%s, 标识符, %d)\n", tokens, om);
            i += j + 1;
        }
        // 数字
        else if (is_integer(s[i])){
            j++;
            while (is_integer(s[i + j])){
                j++;
            }
            
            if(s[i+j] == '.'){
                j++;
                while (is_integer(s[i + j])){
                    j++;
                }
            }
            // 非数字
            j--;

            strncpy(node->name, s + i, j + 1);
            node->type = oi;
            node->line = line;
            node++;

            i += j + 1;
        }
        // 运算符
        else if (s[i] == '-'){
            if (s[i + 1] == '='){
                j++;
                strcpy(tokens, "-=");
                tokens[j + 1] = '\0';
                strncpy(node->name, s + i, j + 1);
                node->type = 27;
            }
            else{
                strcpy(tokens, "-");
                tokens[j + 1] = '\0';
                strncpy(node->name, s + i, j + 1);
                node->type = 14;
            }
            node->line = line;
            i += j + 1;
            node++;
        }
        else if (s[i] == '/'){
            if (s[i + 1] == '='){
                j++;
                strcpy(tokens, "/=");
                tokens[j + 1] = '\0';
                strncpy(node->name, s + i, j + 1);
                node->type = 25;
            }
            else if (s[i + 1] == '*'){
                i += 2;
                annotation = true;
                continue;
            }
            else if (s[i + 1] == '/') break; // 读取下一行
            else{
                strcpy(tokens, "/");
                tokens[j + 1] = '\0';
                strncpy(node->name, s + i, j + 1);
                node->type = 15;
            }
            node->line = line;
            i += j + 1;
            node++;
        }
        else if (s[i] == '<'){
            if (s[i + 1] == '='){
                strcpy(tokens, "<=");
                tokens[2] = '\0';
                strncpy(node->name, s + i, 2);
                node->type = 16;
                i += 2;
            }
            else{
                strcpy(tokens, "<");
                tokens[1] = '\0';
                strncpy(node->name, s + i, 1);
                node->type = 20;
            }
            node->line = line;
            node++;
        }
        else if (s[i] == '+'){
            if (s[i + 1] == '='){
                j++;
                strcpy(tokens, "+=");
                tokens[2] = '\0';
                strncpy(node->name, s + i, j + 1);
                node->type = 26;
            }
            else{
                strcpy(tokens, "+");
                tokens[1] = '\0';
                strncpy(node->name, s + i, j + 1);
                node->type = 17;
            }
            node->line = line;
            i += j + 1;
            node++;
        }
        else if (s[i] == '*'){
            if (s[i + 1] == '='){
                j++;
                strcpy(tokens, "*=");
                tokens[2] = '\0';
                strncpy(node->name, s + i, j + 1);
                node->type = 29;
                i += j + 1;
            }
            else{
                strcpy(tokens, "*");
                tokens[1] = '\0';
                strncpy(node->name, s + i, j + 1);
                node->type = 18;
                i += j + 1;
            }
            node->line = line;
            node++;
        }
        else if (s[i] == '%'){
            if (s[i + 1] == '='){
                strcpy(tokens, "%=");
                tokens[2] = '\0';
                strncpy(node->name, s + i, 2);
                node->type = 28;
                i += 2;
            }
            else{
                strcpy(tokens, "%");
                tokens[1] = '\0';
                strncpy(node->name, s + i, 1);
                node->type = 19;
                i += 1;
            }
            node->line = line;
            node++;
        }
        else if (s[i] == '>'){
            if (s[i + 1] == '='){
                strcpy(tokens, ">=");
                tokens[2] = '\0';
                strncpy(node->name, s + i, 2);
                node->type = 22;
                i += 2;
            }
            else{
                strcpy(tokens, ">");
                tokens[1] = '\0';
                strncpy(node->name, s + i, 1);
                node->type = 21;
                i += 1;
            }
            node->line = line;
            node++;
        }
        else if (s[i] == '='){
            if (s[i + 1] == '='){
                strcpy(tokens, "==");
                tokens[2] = '\0';
                strncpy(node->name, s + i, 2);
                node->type = 23;
                i += 2;
            }
            else{
                strcpy(tokens, "=");
                tokens[1] = '\0';
                strncpy(node->name, s + i, 1);
                node->type = 33; /* 适当的类型编号 */
                i += 1;
            }
            node->line = line;
            node++;
        }
        else if (s[i] == '!'){
            if (s[i + 1] == '='){
                strcpy(tokens, "!=");
                tokens[2] = '\0';
                strncpy(node->name, s + i, 2);
                node->type = 24;
                i += 2;
            }
            else{
                strcpy(tokens, "!");
                tokens[1] = '\0';
                strncpy(node->name, s + i, 1);
                node->type = 32;
                i += 1;
            }
            node->line = line;
            node++;
        }
        else if (s[i] == '&' && s[i + 1] == '&'){
            strcpy(tokens, "&&");
            tokens[2] = '\0';
            strncpy(node->name, s + i, 2);
            node->type = 31;
            node->line = line;
            node++;
            i += 2;
        }
        else if (s[i] == '|' && s[i + 1] == '|'){
            strcpy(tokens, "||");
            tokens[2] = '\0';
            strncpy(node->name, s + i, 2);
            node->type = 30;
            node->line = line;
            node++;
            i += 2;
        }


        // 界符
        else if (delimiter_index_of(s[i]) != -1){
            int index = delimiter_index_of(s[i]);
            strncpy(tokens, s + i, 1);
            tokens[1] = '\0';
            strncpy(node->name, tokens, 1);
            node->name[1] = '\0';
            node->type = od + index;
            i += strlen(delimiter[index]);
            // printf("(%s, 界符, %d)\n", delimiter[index], node->type);
            node->line = line;
            node++;
        }
        else{
            printf("未知符号: %d\n", (int)s[i]);
            perror("Lexscan Error");
            exit(0);
        }
    }
    return node - srcNode;
}

// 扫描函数
int Scanner(FILE* fp, Node* nodes){
    char text[1024] = {};
    int Maxline = sizeof(text) / sizeof(text[0]) - 1;
    int num = 0;
    while (fgets(text, Maxline, fp) != NULL){
        int len = strlen(text);
        // printf(">>>识别第 %d 代码: %s", line++, text);
        num += Lexscan(text, nodes + num);
        line++;
    }
    printf("识别出:%d 单词\n", num);
    for (int i = 0; i < num; i++){
        printf("(%s, %s, %d, %d)\n", nodes[i].name, getClassStr(nodes[i].type), nodes[i].type, nodes[i].line);
    }
    return num;
}


int main(){
    char in_file[100] = "./text.txt";
    char out_file[100] = "out.txt";
    FILE *fpin, *fpout;
    printf("<<<<<<<<<<<<WELCOME>>>>>>>>>>>>\n");

    fpin = fopen(in_file, "r");
    if (fpin == NULL){
        perror("Error opening input file");
        return 1;
    }

    fpout = fopen(out_file, "w");
    if (fpout == NULL){
        perror("Error opening output file");
        fclose(fpin);
        return 1;
    }

    node_num = Scanner(fpin, nodes);

    fclose(fpin);
    fclose(fpout);

    return 0;
}

测试文本

int main()[
/* 
注释 
*/
abc123 += 123
1 + 2
begin end
_abc123
abc_123
131.2321
]

输出

七、基于状态转换图的DFA 识别长注释(选做)

#include <stdio.h>

// 长注释的状态转移表
int MOVE[][1024] = {{1, 0, 0}, {0, 2, 0}, {2, 3, 2}, {4, 3, 2}};
// 枚举输入符号
enum IN_CHAR {
    FORWARD_SLASH = 0,
    STAR,
    OTHER
};
int curStatus = 0; // 当前状态
char tokens[1024];
char *tp;

int main() {

    FILE *fp;
    fp = fopen("text.txt", "r");
    if (fp == NULL) {
        perror("打开文件失败");
        return 1;
    }

    char ch;
    // 进入状态1时,开始记录字符串,到达终态时,输出
    while ((ch = fgetc(fp)) != EOF && curStatus != 4) { // 未达终态即继续
        switch (ch) {
        case '/':
            curStatus = MOVE[curStatus][FORWARD_SLASH];
            break;
        case '*':
            curStatus = MOVE[curStatus][STAR];
            break;
        default:
            curStatus = MOVE[curStatus][OTHER];
        }

        printf("当前状态: %d, 输入字符: %c ...\n", curStatus, ch);
        if (curStatus == 1) { // 在状态1,注释头,重置注释记录数组指针
            tp = tokens;
        }
        if (curStatus != 0)
            *tp++ = ch; // 不在状态0,进入长注释识别,开始记录注释
    }

    if (curStatus == 4) {
        *tp = '\0';
        printf("\n以下是读取的注释>>\n\n%s\n", tokens);
    } else {
        perror("未成功匹配,未达终态");
    }

    // 关闭文件释放资源
    fclose(fp);

    return 0; // 正常退出
}

text.txt

fwehoeeiho/**********
这里是乱七***八糟的注释 
abc123 += 123
1 + 2
begin end
_abc123
******/

结果

完整。。。畏难

八、使用C++编写优化朴素lexer

增加缓冲区,对IO操作进行合并
同时peek预读在编译器中也较为常见
使用map对关键字和符号匹配进行优化
对一些识别逻辑封装为函数,简化结构,但是一些边界问题识别变得异常复杂

#include <cstddef>
#include <cstdlib>
#include <fcntl.h>
#include <iostream>
#include <iterator>
#include <queue>
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <unistd.h>
#include <vector>
#include <map>
#include <deque>

#define BUFSIZE 1024


using namespace std;

class Token {
public:
    Token() {}
    Token(const string type_, const string value_, int line, int column)
        : type_(type_), value_(value_), line_(line), column_(column) {}
    ~Token() {}
    string token_type() const { return type_; }
    string token_value() const { return value_; }
    int line() const { return line_; }
    int column() const { return column_; }

private:
    string type_;
    string value_;
    int line_;
    int column_;
};

class Lexer {
public:
    Lexer(const char* file) : curLine(1) {
        fd = open(file, O_RDONLY);
        if (fd == -1) {
            perror("open error");
            exit(-1);
        }

        // 初始化关键词
        initializeKeywords();
        // 初始化运算符
        initializeOperators();
    }
    ~Lexer() {
        close(fd);
        for (auto token : tokens) {
            delete token;
        }
    }

    void initializeKeywords() {
        keyword_["auto"] = "auto";
        keyword_["break"] = "break";
        keyword_["case"] = "case";
        keyword_["char"] = "char";
        keyword_["const"] = "const";
        keyword_["continue"] = "continue";
        keyword_["default"] = "default";
        keyword_["do"] = "do";
        keyword_["double"] = "double";
        keyword_["else"] = "else";
        keyword_["enum"] = "enum";
        keyword_["extern"] = "extern";
        keyword_["float"] = "float";
        keyword_["for"] = "for";
        keyword_["goto"] = "goto";
        keyword_["if"] = "if";
        keyword_["int"] = "int";
        keyword_["long"] = "long";
        keyword_["register"] = "register";
        keyword_["return"] = "return";
        keyword_["short"] = "short";
        keyword_["signed"] = "signed";
        keyword_["sizeof"] = "sizeof";
        keyword_["static"] = "static";
        keyword_["struct"] = "struct";
        keyword_["switch"] = "switch";
        keyword_["typedef"] = "typedef";
        keyword_["union"] = "union";
        keyword_["unsigned"] = "unsigned";
        keyword_["void"] = "void";
        keyword_["volatile"] = "volatile";
        keyword_["while"] = "while";
    }

    void initializeOperators() {
        operator_["+"] = "add";
        operator_["-"] = "sub";
        operator_["*"] = "mul";
        operator_["/"] = "div";
        operator_["%"] = "mod";
        operator_["++"] = "inc";
        operator_["--"] = "dec";
        operator_["="] = "assign";
        operator_["+="] = "addeq";
        operator_["-="] = "subeq";
        operator_["=="] = "eq";
        operator_["!="] = "ne";
        operator_["<"] = "lt";
        operator_["<="] = "le";
        operator_[">"] = "gt";
        operator_[">="] = "ge";
        operator_["."] = "dot";
        operator_["->"] = "arrow";
        operator_["!"] = "not";
        operator_["~"] = "bit_not";
        operator_["&"] = "and";
        operator_["|"] = "or";
        operator_["^"] = "xor";
        operator_["&&"] = "and_and";
        operator_["||"] = "or_or";
        operator_["<<"] = "shift_left";
        operator_[">>"] = "shift_right";
    }
    // 读取下一个字符,使用buf缓冲区合并多次IO,单向移动,不可预取。临界peek下一字符会导致数据覆盖
    char nextChar() {
        static int cur_char_index = -1;
        cur_char_index = (cur_char_index + 1) % BUFSIZE;

        if (cur_char_index == 0) {
            int ret = read(fd, buf, BUFSIZE);
            if (ret == 0) return EOF;
            if (ret < BUFSIZE) buf[ret] = EOF;
        }

        return buf[cur_char_index];
    }
    // 读取下一个字符,使用环形队列支持预取和缓冲路径
    char readChar() {
        if (dq.empty()) { // cur 小于缓冲区
            curChar = nextChar();
            dq.push_back(curChar);
            cur = 0;
        } else {
            cur++;
            if (cur < dq.size()) {
                curChar = dq[cur];
            } else {
                curChar = nextChar();
                dq.push_back(curChar);
            }
        }
        return curChar;
    }
   // 窥视下一字符,不改变状态,使用缓冲区
    char peekChar() {
        if (cur + 1 < dq.size()) {
            return dq[cur + 1];
        } else {
            char ch = nextChar();
            dq.push_back(ch);
            return ch;
        }
    }
  // 辅助函数
    bool isLetter(char ch) {
        return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || ch == '_';
    }

    bool isDigit(char ch) {
        return ch >= '0' && ch <= '9';
    }

    bool isSpace(char ch) {
        return ch == ' ' || ch == '\t' || ch == '\n';
    }
    // 获取当前路径形成的字符串
    string getStr() {
        string s;
        for (int i = 0; i <= cur; i++) {
            s.push_back(dq[i]);
        }
        cur = -1;
        dq.erase(dq.begin(), dq.begin() + (s.length() + 1));
        return s;
    }
   // 请掉缓冲区当前路径,保存到tokens
    void skipSpace() {
        while (readChar() != EOF && isSpace(curChar)) {
            if (curChar == '\n') curLine++;
        }
        if (cur > 0) cur--;
    }
    // 识别逻辑分块封装
    bool findOperator() {
        static const size_t maxOpLength = 2;
        string op(1, curChar);
        if (cur + 1 < dq.size() && operator_.find(string(1, dq[cur + 1])) != operator_.end()) {
            op.push_back(dq[++cur]);
        }
        if (operator_.find(op) != operator_.end()) {
            Token* token = new Token(operator_[op], op, curLine, getColumn());
            tokens.push_back(token);
            return true;
        }
        return false;
    }

    bool findKeywordOrIdentifier() {
        if (!isLetter(curChar)) return false;

        while (isLetter(peekChar()) || isDigit(peekChar())) {
            readChar();
        }
        string s = getStr();

        if (keyword_.find(s) != keyword_.end()) {
            tokens.push_back(new Token("keyword", s, curLine, getColumn()));
        } else {
            tokens.push_back(new Token("identifier", s, curLine, getColumn()));
        }
        return true;
    }

    bool findNumber() {
        if (!isDigit(curChar)) return false;

        while (isDigit(peekChar())) {
            readChar();
        }
        string s = getStr();
        tokens.push_back(new Token("number", s, curLine, getColumn()));
        return true;
    }

    int getColumn() {
        int column = 0;
        for (auto it = dq.begin(); it != dq.begin() + cur + 1; ++it) {
            if (*it == '\n') column = 0;
            else column++;
        }
        return column;
    }

    void getToken() {
        while (readChar() != EOF) {
            skipSpace();
            if (findOperator()) continue;
            if (isLetter(curChar)) {
                findKeywordOrIdentifier();
            } else if (isDigit(curChar)) {
                findNumber();
            } else {
                // 处理其他字符
                string s(1, curChar);
                tokens.push_back(new Token("char", s, curLine, getColumn()));
                readChar(); // 跳过当前字符
            }
        }
        showTokens();
    }

    void showTokens() {
        for (auto token : tokens) {
            cout << "Type: " << token->token_type() << ", Value: " << token->token_value()
                 << ", Line: " << token->line() << ", Column: " << token->column() << endl;
        }
    }

private:
    int fd;
    char buf[BUFSIZE];   // 读取缓冲
    std::deque<char> dq; // 缓冲字符路径,同时实现预取
    int cur = -1;             // 当前状态索引
    int curChar;
    int curLine;                   // 当前行
    map<string, string> operator_; // 运算符
    map<string, string> keyword_;  // 保留字
    vector<Token *> tokens;        // 保留识别到的token
};

int main() {
    const char* file = "./text.txt";
    Lexer lex(file); // 词法解释器
    lex.getToken();
    return 0;
}

九、参考Clang Lexer源码的实现

https://clang.llvm.org/doxygen/Lexer_8cpp_source.html

  1. 对运算符单独处理,代码可读性好、可修改性高
  2. 结构简单清晰

在 Lexer 的初始化过程中,文件内容通常会被一次性读取到一个内存缓冲区中。这个缓冲区由 MemoryBuffer 或 MemoryBufferRef 对象管理。
好处是:
​高效性:一次性读取文件内容可以减少频繁的 I/O 操作,提高词法分析的效率。
​灵活性:词法分析器可以在整个文件内容中自由移动指针,方便处理跨行的语法结构(如多行注释、字符串字面量等),避免了如上面代码的复杂的临界问题

#include <ctype.h>
#include <map>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>

// 定义支持的词法单元类型
typedef enum {
    TOK_EOF,            // 文件结束
    TOK_IDENTIFIER,     // 标识符
    TOK_KEYWORD,        // 关键字
    TOK_NUMBER,         // 整数
    TOK_FLOAT,          // 小数
    TOK_STRING_LITERAL, // 字符串字面量
    TOK_CHAR_LITERAL,   // 字符字面量
    TOK_PLUS,
    TOK_MINUS, // +, -
    TOK_STAR,
    TOK_SLASH,     // *, /
    TOK_SEMICOLON, // ;
    TOK_LPAREN,
    TOK_RPAREN, // (, )
    TOK_LBRACE,
    TOK_RBRACE, // {, }
    TOK_LBRACKET,
    TOK_RBRACKET,    // [, ]
    TOK_PLUS_EQ,     // +=
    TOK_MINUS_EQ,    // -=
    TOK_STAR_EQ,     // *=
    TOK_SLASH_EQ,    // /=
    TOK_PLUS_PLUS,   // ++
    TOK_MINUS_MINUS, // --
    TOK_LESS,        // <
    TOK_GREATER,     // >
    TOK_LESS_EQ,     // <=
    TOK_GREATER_EQ,  // >=
    TOK_EQUAL,       // == (用于 `==`)
    TOK_ASSIGN,      // =  (用于 `=`)
    TOK_NOT,         // !
    TOK_NOT_EQUAL,   // !=
    TOK_PIPE,        // |
    TOK_LOGICAL_OR,  // ||
    TOK_AMP,         // &
    TOK_LOGICAL_AND, // &&
    TOK_UNKNOWN      // 未知类型
} TokenType;

// 定义词法分析器结构
typedef struct {
    const char *buffer;  // 输入代码的缓冲区
    const char *current; // 当前分析的位置
    const char *end;     // 缓冲区结束位置
    int line;            // 当前行号
    int column;          // 当前列号
} Lexer;

// 定义词法单元结构
typedef struct {
    TokenType type;      // 词法单元类型
    const char *start;   // 词法单元开始位置
    const char *end;     // 词法单元结束位置
    int line;            // 词法单元所在行号
    int column;          // 词法单元所在列号
} Token;


// 初始化词法分析器
void lexer_init(Lexer *lexer, const char *buffer, size_t length) {
    lexer->buffer = buffer;       // 设置缓冲区
    lexer->current = buffer;      // 设置当前指针
    lexer->end = buffer + length; // 设置缓冲区结束位置
    lexer->line = 1;              // 初始化行号为1
    lexer->column = 1;            // 初始化列号为1
}
// 检查是否到达文件末尾
static inline int is_eof(const Lexer *lexer) {
    return lexer->current >= lexer->end || *lexer->current == '\0';
}

// 获取当前字符
static inline char peek_char(const Lexer *lexer) {
    return is_eof(lexer) ? '\0' : *lexer->current;
}

// 向前移动指针
static inline void advance(Lexer *lexer) {
    if (lexer->current < lexer->end) {
        if (*lexer->current == '\n') { // 遇到换行符
            lexer->line++;             // 行号加 1
            lexer->column = 1;         // 列号重置为 1
        } else {
            lexer->column++;           // 其他字符,列号加 1
        }
        lexer->current++;              // 移动当前指针
    }
}

// 判断是否为空白字符
static int is_whitespace(char c) {
    return c == ' ' || c == '\t' || c == '\n' || c == '\r';
}

// 判断是否为字母
static int is_alpha(char c) {
    return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_';
}

// 判断是否为数字
static int is_digit(char c) {
    return c >= '0' && c <= '9';
}

// 跳过空白字符
static void skip_whitespace(Lexer *lexer) {
    while (!is_eof(lexer) && is_whitespace(peek_char(lexer))) {
        advance(lexer);
    }
}

// 跳过注释
static void skip_comments(Lexer *lexer) {
    if (peek_char(lexer) != '/')
        return;

    advance(lexer);                // 跳过第一个 '/'
    if (peek_char(lexer) == '/') { // 单行注释
        advance(lexer);
        while (!is_eof(lexer) && peek_char(lexer) != '\n') {
            advance(lexer);
        }
    } else if (peek_char(lexer) == '*') { // 多行注释
        advance(lexer);
        while (!is_eof(lexer)) {
            if (peek_char(lexer) == '*' && lexer->current + 1 < lexer->end && *(lexer->current + 1) == '/') {
                advance(lexer);
                advance(lexer);
                break;
            }
            advance(lexer);
        }
    }
}

// 跳过空白和注释
static void skip_whitespace_and_comments(Lexer *lexer) {
    while (!is_eof(lexer)) {
        const char *prev = lexer->current; // 记录当前指针位置
        skip_whitespace(lexer);            // 跳过空白字符
        skip_comments(lexer);              // 跳过注释
        if (lexer->current == prev) {      // 如果指针没有移动,说明没有更多的空白或注释
            break;
        }
    }
}

// 判断是否为关键字
// static TokenType is_keyword(const char *str, size_t len) {
//     if (len == 3 && strncmp(str, "int", 3) == 0) return TOK_KEYWORD;
//     if (len == 4 && strncmp(str, "void", 4) == 0) return TOK_KEYWORD;
//     if (len == 4 && strncmp(str, "char", 4) == 0) return TOK_KEYWORD;
//     if (len == 2 && strncmp(str, "if", 2) == 0) return TOK_KEYWORD;
//     if (len == 4 && strncmp(str, "else", 4) == 0) return TOK_KEYWORD;
//     if (len == 5 && strncmp(str, "while", 5) == 0) return TOK_KEYWORD;
//     if (len == 6 && strncmp(str, "return", 6) == 0) return TOK_KEYWORD;
//     return TOK_IDENTIFIER; // 默认为标识符
// }

// 判断是否为关键字 map优化
// 判断是否为关键字
static TokenType is_keyword(const char *str, size_t len) {
    static std::map<std::string, TokenType> keyword_map = {
        {"int", TOK_KEYWORD},      {"void", TOK_KEYWORD},     {"char", TOK_KEYWORD},      {"if", TOK_KEYWORD},
        {"else", TOK_KEYWORD},     {"while", TOK_KEYWORD},    {"return", TOK_KEYWORD},    {"begin", TOK_KEYWORD},
        {"end", TOK_KEYWORD},      {"for", TOK_KEYWORD},      {"do", TOK_KEYWORD},        {"break", TOK_KEYWORD},
        {"continue", TOK_KEYWORD}, {"switch", TOK_KEYWORD},   {"case", TOK_KEYWORD},      {"default", TOK_KEYWORD},
        {"struct", TOK_KEYWORD},   {"typedef", TOK_KEYWORD},  {"enum", TOK_KEYWORD},      {"union", TOK_KEYWORD},
        {"sizeof", TOK_KEYWORD},   {"static", TOK_KEYWORD},   {"const", TOK_KEYWORD},     {"extern", TOK_KEYWORD},
        {"register", TOK_KEYWORD}, {"auto", TOK_KEYWORD},     {"volatile", TOK_KEYWORD},  {"restrict", TOK_KEYWORD},
        {"inline", TOK_KEYWORD},   {"signed", TOK_KEYWORD},   {"unsigned", TOK_KEYWORD},  {"short", TOK_KEYWORD},
        {"long", TOK_KEYWORD},     {"float", TOK_KEYWORD},    {"double", TOK_KEYWORD},    {"bool", TOK_KEYWORD},
        {"true", TOK_KEYWORD},     {"false", TOK_KEYWORD},    {"NULL", TOK_KEYWORD},      {"include", TOK_KEYWORD},
        {"define", TOK_KEYWORD},   {"ifdef", TOK_KEYWORD},    {"ifndef", TOK_KEYWORD},    {"endif", TOK_KEYWORD},
        {"elif", TOK_KEYWORD},     {"pragma", TOK_KEYWORD},   {"error", TOK_KEYWORD},     {"warning", TOK_KEYWORD},
        {"line", TOK_KEYWORD},     {"undef", TOK_KEYWORD},    {"import", TOK_KEYWORD},    {"module", TOK_KEYWORD},
        {"export", TOK_KEYWORD},   {"public", TOK_KEYWORD},   {"private", TOK_KEYWORD},   {"protected", TOK_KEYWORD},
        {"friend", TOK_KEYWORD},   {"class", TOK_KEYWORD},    {"namespace", TOK_KEYWORD}, {"using", TOK_KEYWORD},
        {"template", TOK_KEYWORD}, {"typename", TOK_KEYWORD}, {"this", TOK_KEYWORD},      {"new", TOK_KEYWORD},
        {"delete", TOK_KEYWORD},   {"operator", TOK_KEYWORD}, {"virtual", TOK_KEYWORD},   {"override", TOK_KEYWORD},
        {"final", TOK_KEYWORD}};

    // 将输入字符串转换为 std::string
    std::string key(str, len);
    if (keyword_map.find(key) != keyword_map.end()) {
        return keyword_map[key];
    }
    return TOK_IDENTIFIER; // 默认为标识符
}
// 读取标识符或关键字
static Token read_identifier_or_keyword(Lexer *lexer) {
    const char *start = lexer->current; // 记录起始位置
    while (!is_eof(lexer) && (is_alpha(peek_char(lexer)) || is_digit(peek_char(lexer)) || peek_char(lexer) == '_')) {
        advance(lexer); // 跳过标识符字符
    }
    size_t len = lexer->current - start; // 计算长度
    Token tok = {is_keyword(start, len), start, lexer->current, lexer->line, lexer->column}; // 创建词法单元
    return tok;
}

// 读取数字
static Token read_number(Lexer *lexer) {
    const char *start = lexer->current; // 记录起始位置
    int has_decimal = 0;                // 标记是否包含小数点

    while (!is_eof(lexer) && (is_digit(peek_char(lexer)) || (peek_char(lexer) == '.' && !has_decimal))) {
        if (peek_char(lexer) == '.') {
            has_decimal = 1; // 标记已经读取了小数点
        }
        advance(lexer); // 跳过数字或小数点
    }

    if (has_decimal) {
        return (Token){TOK_FLOAT, start, lexer->current, lexer->line, lexer->column}; // 返回小数词法单元
    } else {
        return (Token){TOK_NUMBER, start, lexer->current, lexer->line, lexer->column}; // 返回整数词法单元
    }
}

// 读取字符串字面量
static Token read_string_literal(Lexer *lexer) {
    const char *start = lexer->current; // 记录起始位置
    advance(lexer);                     // 跳过开头的 '"'
    while (!is_eof(lexer) && peek_char(lexer) != '"') {
        if (peek_char(lexer) == '\\') {
            advance(lexer); // 跳过转义字符
            if (!is_eof(lexer)) {
                advance(lexer); // 跳过被转义的字符
            }
        } else {
            advance(lexer); // 跳过普通字符
        }
    }
    advance(lexer); // 跳过结尾的 '"'
    return (Token){TOK_STRING_LITERAL, start, lexer->current, lexer->line, lexer->column}; // 返回字符串词法单元
}

// 读取字符字面量
static Token read_char_literal(Lexer *lexer) {
    const char *start = lexer->current; // 记录起始位置
    advance(lexer);                     // 跳过开头的 '\''
    if (!is_eof(lexer)) {
        if (peek_char(lexer) == '\\') {
            advance(lexer); // 跳过转义字符
            if (!is_eof(lexer)) {
                advance(lexer); // 跳过被转义的字符
            }
        } else {
            advance(lexer); // 跳过普通字符
        }
    }
    if (!is_eof(lexer) && peek_char(lexer) == '\'') {
        advance(lexer); // 跳过结尾的 '\''
    }
    return (Token){TOK_CHAR_LITERAL, start, lexer->current, lexer->line, lexer->column}; // 返回字符词法单元
}

// 获取下一个词法单元
Token get_next_token(Lexer *lexer) {
    skip_whitespace_and_comments(lexer); // 跳过空白和注释

    if (is_eof(lexer)) {
        return (Token){TOK_EOF, 0, 0, lexer->line, lexer->column}; // 返回文件结束标记
    }

    const char *start = lexer->current; // 记录起始位置
    int line = lexer->line;             // 记录当前行号
    int column = lexer->column;         // 记录当前列号
    char c = peek_char(lexer);          // 获取当前字符
    Token tok;

    switch (c) {
        case 'a' ... 'z':
        case 'A' ... 'Z':
        case '_':
            return read_identifier_or_keyword(lexer); // 读取标识符或关键字
        case '0' ... '9':
            return read_number(lexer); // 读取数字
        case '"':
            return read_string_literal(lexer); // 读取字符串字面量
        case '\'':
            return read_char_literal(lexer); // 读取字符字面量
        case '+':
            advance(lexer);
            if (!is_eof(lexer) && peek_char(lexer) == '+') {
                advance(lexer); // 检查并跳过第二个 '+'
                return (Token){TOK_PLUS_PLUS, start, lexer->current, line, column}; // 返回 '++' 词法单元
            } else if (!is_eof(lexer) && peek_char(lexer) == '=') {
                advance(lexer); // 检查并跳过 '='
                return (Token){TOK_PLUS_EQ, start, lexer->current, line, column}; // 返回 '+=' 词法单元
            }
            return (Token){TOK_PLUS, start, lexer->current, line, column}; // 返回 '+' 词法单元
        case '-':
            advance(lexer);
            if (!is_eof(lexer) && peek_char(lexer) == '-') {
                advance(lexer); // 检查并跳过第二个 '-'
                return (Token){TOK_MINUS_MINUS, start, lexer->current, line, column}; // 返回 '--' 词法单元
            } else if (!is_eof(lexer) && peek_char(lexer) == '=') {
                advance(lexer); // 检查并跳过 '='
                return (Token){TOK_MINUS_EQ, start, lexer->current, line, column}; // 返回 '-=' 词法单元
            }
            return (Token){TOK_MINUS, start, lexer->current, line, column}; // 返回 '-' 词法单元
        case '*':
            advance(lexer);
            if (!is_eof(lexer) && peek_char(lexer) == '=') {
                advance(lexer); // 检查并跳过 '='
                return (Token){TOK_STAR_EQ, start, lexer->current, line, column}; // 返回 '*=' 词法单元
            }
            return (Token){TOK_STAR, start, lexer->current, line, column}; // 返回 '*' 词法单元
        case '/':
            advance(lexer);
            if (!is_eof(lexer) && peek_char(lexer) == '=') {
                advance(lexer); // 检查并跳过 '='
                return (Token){TOK_SLASH_EQ, start, lexer->current, line, column}; // 返回 '/=' 词法单元
            }
            return (Token){TOK_SLASH, start, lexer->current, line, column}; // 返回 '/' 词法单元
        case '<':
            advance(lexer);
            if (!is_eof(lexer) && peek_char(lexer) == '=') {
                advance(lexer); // 检查并跳过 '='
                return (Token){TOK_LESS_EQ, start, lexer->current, line, column}; // 返回 '<=' 词法单元
            }
            return (Token){TOK_LESS, start, lexer->current, line, column}; // 返回 '<' 词法单元
        case '>':
            advance(lexer);
            if (!is_eof(lexer) && peek_char(lexer) == '=') {
                advance(lexer); // 检查并跳过 '='
                return (Token){TOK_GREATER_EQ, start, lexer->current, line, column}; // 返回 '>=' 词法单元
            }
            return (Token){TOK_GREATER, start, lexer->current, line, column}; // 返回 '>' 词法单元
        case ';':
            advance(lexer);
            return (Token){TOK_SEMICOLON, start, lexer->current, line, column}; // 返回 ';' 词法单元
        case '(':
            advance(lexer);
            return (Token){TOK_LPAREN, start, lexer->current, line, column}; // 返回 '(' 词法单元
        case ')':
            advance(lexer);
            return (Token){TOK_RPAREN, start, lexer->current, line, column}; // 返回 ')' 词法单元
        case '{':
            advance(lexer);
            return (Token){TOK_LBRACE, start, lexer->current, line, column}; // 返回 '{' 词法单元
        case '}':
            advance(lexer);
            return (Token){TOK_RBRACE, start, lexer->current, line, column}; // 返回 '}' 词法单元
        case '[':
            advance(lexer);
            return (Token){TOK_LBRACKET, start, lexer->current, line, column}; // 返回 '[' 词法单元
        case ']':
            advance(lexer);
            return (Token){TOK_RBRACKET, start, lexer->current, line, column}; // 返回 ']' 词法单元
        case '=':
            advance(lexer);
            if (!is_eof(lexer) && peek_char(lexer) == '=') {
                advance(lexer); // 检查并跳过第二个 '='
                return (Token){TOK_EQUAL, start, lexer->current, line, column}; // 返回 '==' 词法单元
            }
            return (Token){TOK_ASSIGN, start, lexer->current, line, column}; // 返回 '=' 词法单元
        case '!':
            advance(lexer);
            if (!is_eof(lexer) && peek_char(lexer) == '=') {
                advance(lexer); // 检查并跳过 '='
                return (Token){TOK_NOT_EQUAL, start, lexer->current, line, column}; // 返回 '!=' 词法单元
            }
            return (Token){TOK_NOT, start, lexer->current, line, column}; // 返回 '!' 词法单元
        case '|':
            advance(lexer);
            if (!is_eof(lexer) && peek_char(lexer) == '|') {
                advance(lexer); // 检查并跳过第二个 '|'
                return (Token){TOK_LOGICAL_OR, start, lexer->current, line, column}; // 返回 '||' 词法单元
            }
            return (Token){TOK_PIPE, start, lexer->current, line, column}; // 返回 '|' 词法单元
        case '&':
            advance(lexer);
            if (!is_eof(lexer) && peek_char(lexer) == '&') {
                advance(lexer); // 检查并跳过第二个 '&'
                return (Token){TOK_LOGICAL_AND, start, lexer->current, line, column}; // 返回 '&&' 词法单元
            }
            return (Token){TOK_AMP, start, lexer->current, line, column}; // 返回 '&' 词法单元
        default:
            advance(lexer);
            return (Token){TOK_UNKNOWN, start, lexer->current, line, column}; // 返回未知词法单元
    }
}

// 打印识别到的 Token
void print_token(const Token *tok) {
    const char *type_str;
    switch (tok->type) {
        case TOK_EOF: type_str = "TOK_EOF"; break;
        case TOK_IDENTIFIER: type_str = "TOK_IDENTIFIER"; break;
        case TOK_KEYWORD: type_str = "TOK_KEYWORD"; break;
        case TOK_NUMBER: type_str = "TOK_NUMBER"; break;
        case TOK_FLOAT: type_str = "TOK_FLOAT"; break;
        case TOK_STRING_LITERAL: type_str = "TOK_STRING_LITERAL"; break;
        case TOK_CHAR_LITERAL: type_str = "TOK_CHAR_LITERAL"; break;
        case TOK_PLUS: type_str = "TOK_PLUS"; break;
        case TOK_MINUS: type_str = "TOK_MINUS"; break;
        case TOK_STAR: type_str = "TOK_STAR"; break;
        case TOK_SLASH: type_str = "TOK_SLASH"; break;
        case TOK_SEMICOLON: type_str = "TOK_SEMICOLON"; break;
        case TOK_LPAREN: type_str = "TOK_LPAREN"; break;
        case TOK_RPAREN: type_str = "TOK_RPAREN"; break;
        case TOK_LBRACE: type_str = "TOK_LBRACE"; break;
        case TOK_RBRACE: type_str = "TOK_RBRACE"; break;
        case TOK_LBRACKET: type_str = "TOK_LBRACKET"; break;
        case TOK_RBRACKET: type_str = "TOK_RBRACKET"; break;
        case TOK_PLUS_EQ: type_str = "TOK_PLUS_EQ"; break;
        case TOK_MINUS_EQ: type_str = "TOK_MINUS_EQ"; break;
        case TOK_STAR_EQ: type_str = "TOK_STAR_EQ"; break;
        case TOK_SLASH_EQ: type_str = "TOK_SLASH_EQ"; break;
        case TOK_PLUS_PLUS: type_str = "TOK_PLUS_PLUS"; break;
        case TOK_MINUS_MINUS: type_str = "TOK_MINUS_MINUS"; break;
        case TOK_LESS: type_str = "TOK_LESS"; break;
        case TOK_GREATER: type_str = "TOK_GREATER"; break;
        case TOK_LESS_EQ: type_str = "TOK_LESS_EQ"; break;
        case TOK_GREATER_EQ: type_str = "TOK_GREATER_EQ"; break;
        case TOK_EQUAL: type_str = "TOK_EQUAL"; break;
        case TOK_ASSIGN: type_str = "TOK_ASSIGN"; break;
        case TOK_NOT: type_str = "TOK_NOT"; break;
        case TOK_NOT_EQUAL: type_str = "TOK_NOT_EQUAL"; break;
        case TOK_PIPE: type_str = "TOK_PIPE"; break;
        case TOK_LOGICAL_OR: type_str = "TOK_LOGICAL_OR"; break;
        case TOK_AMP: type_str = "TOK_AMP"; break;
        case TOK_LOGICAL_AND: type_str = "TOK_LOGICAL_AND"; break;
        default: type_str = "TOK_UNKNOWN"; break;
    }

    printf("Type: %s, Value: %.*s, Line: %d, Column: %d\n",
           type_str, (int)(tok->end - tok->start), tok->start, tok->line, tok->column);
}


// 从文件中读取内容并初始化词法分析器
int main(int argc, char *argv[]) {
    if (argc != 2) {
        fprintf(stderr, "Usage: %s <input_file>\n", argv[0]);
        return EXIT_FAILURE;
    }

    FILE *file = fopen(argv[1], "r");
    if (!file) {
        perror("Failed to open file");
        return EXIT_FAILURE;
    }

    // 获取文件大小
    fseek(file, 0, SEEK_END);
    long file_size = ftell(file);
    fseek(file, 0, SEEK_SET);

    // 分配内存并读取文件内容
    char *buffer = (char *)malloc(file_size + 1);
    if (!buffer) {
        perror("Failed to allocate memory");
        fclose(file);
        return EXIT_FAILURE;
    }

    size_t bytes_read = fread(buffer, 1, file_size, file);
    if (bytes_read != (size_t)file_size) {
        perror("Failed to read file");
        free(buffer);
        fclose(file);
        return EXIT_FAILURE;
    }

    buffer[file_size] = '\0'; // 确保字符串以 '\0' 结尾
    fclose(file);

    // 初始化词法分析器
    Lexer lexer;
    lexer_init(&lexer, buffer, file_size);

    // 词法分析并打印结果
    Token tok;
    do {
        tok = get_next_token(&lexer);
        print_token(&tok);
    } while (tok.type != TOK_EOF);

    // 释放内存
    free(buffer);
    return EXIT_SUCCESS;
}

程序流程图

flowchart TD A[开始] --> B[读取命令行参数] B -->|参数数量正确| C[打开文件] B -->|参数数量错误| E[打印用法信息] E --> A C -->|文件打开成功| D[获取文件大小] C -->|文件打开失败| F[打印错误并退出] F --> A D --> G[分配内存并读取文件内容] G -->|读取成功| H[初始化词法分析器] G -->|读取失败| F H --> I[词法分析循环] I -->|生成词法单元| J[打印词法单元] I -->|到达文件末尾| K[释放内存] J --> I K --> L[结束] classDef success fill:#a4edba; classDef error fill:#f44336; classDef process fill:#2196f3; classDef info fill:#ffeb3b; class A,L info; class B,E process; class C,F error; class D,G,H,I success; class J process; class K info;
posted @ 2025-02-21 21:53  丘狸尾  阅读(106)  评论(0)    收藏  举报