AI开发C语言应用按步走,表达式计算器calc的第一步,中缀表达式词法分析

calc1 --- 中缀表达式词法分析器

1. 项目概述

构建一个 C 语言中缀表达式词法分析器(tokenizer),将中缀表达式字符串拆分为独立的 token 序列,包括数字、四则运算符和圆括号。

输入示例

复制代码
(90-18)/3+15

输出示例

复制代码
(       LEFT_PAREN
90      NUMBER
-       MINUS
18      NUMBER
)       RIGHT_PAREN
/       SLASH
3       NUMBER
+       PLUS
15      NUMBER

2. 目录结构

复制代码
calc/
├── Makefile       # 构建脚本(gcc -Wall -Wextra -std=c99 -pedantic)
├── parse.h        # 头文件:TokenType 枚举、Token 结构体、API 声明
├── parse.c        # 词法分析器实现
├── main.c         # 入口程序:输入源选择 + 解析打印
└── build/
    ├── calc       # 可执行文件
    ├── parse.o    # 编译目标文件
    └── main.o     # 编译目标文件

3. 文件说明

3.1 parse.h

定义三个核心类型和三个 API 函数:

TokenType 枚举

枚举值 对应字符 说明
TOKEN_NUMBER --- 整数字面量
TOKEN_PLUS + 加号
TOKEN_MINUS - 减号
TOKEN_STAR * 乘号
TOKEN_SLASH / 除号
TOKEN_LPAREN ( 左括号
TOKEN_RPAREN ) 右括号
TOKEN_END --- 输入结束
TOKEN_ERROR --- 非法字符

Token 结构体

c 复制代码
typedef struct {
    TokenType type;   // token 类型
    int       value;  // 数值(仅 TOKEN_NUMBER 有效)
    int       col;    // 在输入字符串中的列号(1-based)
} Token;

API 函数

c 复制代码
void parse_init(const char *input);      // 初始化解析器
Token parse_next(void);                  // 读取下一个 token
const char *token_name(TokenType type);  // 将类型转为可读字符串

3.2 parse.c

词法分析器实现,采用静态状态机模式:

  • 内部状态input_pos(当前扫描游标)、line(行号,保留)、column(列号,1-based)
  • parse_init:将游标指向输入字符串起始位置,重置行列号
  • parse_next :核心逻辑
    1. 跳过空白字符(isspace),同步更新列号
    2. 遇到 \0 → 返回 TOKEN_END
    3. 遇到 0-9 → 用 strtol 连续读入数字,返回 TOKEN_NUMBER
    4. 遇到 + - * / ( ) → 返回对应类型
    5. 其他字符 → 返回 TOKEN_ERROR
  • token_name:将枚举值映射为可读字符串

设计要点:零动态内存分配,纯游标扫描,无需 malloc

3.3 main.c

入口程序,分为两个函数:

parse_and_print(static 函数)

c 复制代码
static int parse_and_print(const char *input);

职责:空输入校验 → parse_init → 循环调用 parse_next 打印每个 token → 遇到 TOKEN_ERROR 时输出错误位置并返回 1。

main 函数

c 复制代码
int main(int argc, char *argv[]);

职责:输入源选择(argv[1] 优先,否则从 stdin 读取)→ 调用 parse_and_print 并返回其状态码。

3.4 Makefile

沿用项目中 nslist/ 的构建风格(C99 严格模式):

目标 作用
all 编译生成 build/calc 可执行文件
run 运行 build/calc,需传入 EXPR=...
clean 删除 build/ 目录

编译选项:-Wall -Wextra -std=c99 -pedantic -g -O2

4. 构建与运行

bash 复制代码
# 编译
$ cd calc
$ make
mkdir -p build
gcc -Wall -Wextra -std=c99 -pedantic -g -O2 -I. -c parse.c -o build/parse.o
gcc -Wall -Wextra -std=c99 -pedantic -g -O2 -I. -c main.c -o build/main.o
gcc -Wall -Wextra -std=c99 -pedantic -g -O2  build/parse.o build/main.o -o build/calc

# 命令行参数运行
$ ./build/calc "(90-18)/3+15"

# 管道输入运行
$ echo "(90-18)/3+15" | ./build/calc

# 通过 Makefile 运行
$ make run EXPR="(90-18)/3+15"

# 清理
$ make clean

5. 测试验证

5.1 基本表达式

bash 复制代码
$ ./build/calc "(90-18)/3+15"
(       LEFT_PAREN
90      NUMBER
-       MINUS
18      NUMBER
)       RIGHT_PAREN
/       SLASH
3       NUMBER
+       PLUS
15      NUMBER

5.2 带空格

bash 复制代码
$ ./build/calc " 3 + 5 "
3       NUMBER
+       PLUS
5       NUMBER

5.3 非法字符

bash 复制代码
$ ./build/calc "3+@5"
3       NUMBER
+       PLUS
col 3: ERROR --- illegal character

5.4 空输入

bash 复制代码
$ ./build/calc ""
error: empty input

5.5 管道输入

bash 复制代码
$ echo "10-2" | ./build/calc
10      NUMBER
-       MINUS
2       NUMBER

6. 设计要点

  1. 零动态内存分配 :词法分析器使用纯游标扫描,不依赖 malloc,适合嵌入式或资源受限环境
  2. C99 严格模式 :编译启用 -std=c99 -pedantic,确保跨平台兼容
  3. 列号定位:每个 token 记录输入中的列号,支持精确的错误位置报告
  4. 模块分离parse.h / parse.c 为纯词法分析模块,main.c 仅负责输入输出编排,职责清晰
  5. 两种输入方式:支持命令行参数和标准输入管道,方便脚本集成
相关推荐
神仙别闹2 小时前
基于 C++ 实现(控制台)学生成绩管理系统
开发语言·c++
伞伞悦读2 小时前
【第36期】Python 目录与路径详解:pathlib、文件遍历、创建、复制、移动和删除风险
开发语言·python
free-elcmacom4 小时前
C语言字符指针与字符串字面量——从一道“剑指 Offer”笔试题说起
c语言·visual studio
2601_966949655 小时前
只拿到股票代码还不够:量化程序到底还需要哪些标的信息?——从数据建模开始理解股票元数据
开发语言·python·数据分析·pandas·量化交易·股票数据·quantdash
m0_734571765 小时前
深入理解5g <十八>传输块tbsize的计算
开发语言·5g
RAN11426760087 小时前
1.3第一个程序的介绍
开发语言·qt
blue_ice .7 小时前
Verilog DDS 数字信号发生器:DAC 输出模拟波形与 ADC 回采观测
开发语言·单片机·嵌入式硬件·fpga开发
远翔调光芯片^138287988728 小时前
ECP5702能芯科技PD取电芯片在市场上的优势有哪些?
开发语言·人工智能·单片机·嵌入式硬件·智能家居
泡海椒8 小时前
规则引擎对比:JQuick-Java vs Drools 轻量场景选型对比
java·开发语言
泡泡鱼(敲代码中)9 小时前
Python 容器类型学习笔记:列表、元组、字典、集合
开发语言·笔记·python·pycharm