AI开发C语言应用按步走,表达式计算器calc的第四步,交互式 REPL 模式

calc4 --- 交互式 REPL 模式

1. 概述

本次迭代在 calc3 求值器基础上增加交互式 REPL 模式(Read-Eval-Print Loop),提供实时运算体验,并显示运算步骤。

运行模式对比

模式 启动方式 输出 用途
批处理 ./calc "表达式" 仅结果数字 脚本调用、自动化测试
交互式 ./calc(无参数) 运算步骤 + 结果 手动输入探索

交互示例

复制代码
$ ./build/calc
calc> (90-18)/3+15
  90 - 18 = 72
  72 / 3 = 24
  24 + 15 = 39
  result: 39
calc> 3+5*2
  5 * 2 = 10
  3 + 10 = 13
  result: 13
calc> exit

2. 变更清单

文件 操作 说明
eval.h 编辑 新增 eval_set_print_steps(int enable) 声明
eval.c 编辑 新增 print_steps 标志;parse_exprparse_term 中每步运算插入步骤打印;新增 eval_set_print_steps 实现
main.c 编辑 新增 repl() 函数;main() 按参数有无分流批处理/交互模式
其余文件 无需改动 parse.h/parse.c/Makefile/test.expr 不变

3. 交互模式设计

3.1 main.c --- REPL 循环

c 复制代码
static int repl(void)
{
    char buf[MAX_INPUT];
    eval_set_print_steps(1);

    while (1) {
        printf("calc> ");
        fflush(stdout);

        if (!fgets(buf, sizeof(buf), stdin))
            break;

        /* 去掉末尾换行符 */
        size_t len = strlen(buf);
        if (len > 0 && buf[len - 1] == '\n')
            buf[len - 1] = '\0';

        if (strcmp(buf, "exit") == 0)
            break;
        if (*buf == '\0')
            continue;

        int result;
        if (eval_expr(buf, &result) != 0)
            continue;

        printf("  result: %d\n", result);
    }
    return 0;
}

核心逻辑

  1. 打印 calc> 提示符(fflush 确保立即显示)
  2. fgets 读取一行用户输入
  3. 去掉末尾换行符
  4. 判断 exit → 退出循环
  5. 判断空行 → 跳过
  6. 调用 eval_expr 求值 → 输出结果
  7. 循环往复,直到 EOF 或 exit

3.2 main() 分流

c 复制代码
int main(int argc, char *argv[])
{
    if (argc > 1) {
        /* 批处理模式:从命令行参数读取 */
        return parse_and_eval(argv[1]);
    }
    /* 交互模式 */
    return repl();
}
  • 有参数 → 批处理(eval_set_print_steps(0),不输出步骤)
  • 无参数 → 交互式 REPL(eval_set_print_steps(1),输出步骤)

4. 运算步骤打印

4.1 eval_set_print_steps API

c 复制代码
void eval_set_print_steps(int enable);

eval.c 中通过 print_steps 静态变量控制,eval_expr 每次调用时会重置 eval_error保留 print_steps 标志。

4.2 步骤打印位置

parse_exprparse_term 的每个二元运算完成后插入打印:

c 复制代码
// parse_expr 中 ------ 处理 + -
int result = left + right;
if (print_steps)
    printf("  %d + %d = %d\n", left, right, result);
left = result;

// parse_term 中 ------ 处理 * / %
int result = left * right;
if (print_steps)
    printf("  %d * %d = %d\n", left, right, result);
left = result;

4.3 优先级自动体现

利用递归下降的天然求值顺序------内层语法层先求值,外层后求值------步骤打印顺序自动反映运算符优先级:

输入 步骤输出顺序 说明
3+5*2 5 * 2 = 103 + 10 = 13 * 行先于 +
(90-18)/3+15 90 - 18 = 7272 / 3 = 2424 + 15 = 39 括号内先求值

无需额外排序逻辑,打印顺序等于求值顺序。

5. 目录结构(更新后)

复制代码
calc/
├── Makefile         # 构建脚本
├── parse.h          # tokenizer 头文件
├── parse.c          # tokenizer 实现
├── eval.h           # 求值器头文件(含 eval_set_print_steps)
├── eval.c           # 递归下降求值器实现(含步骤打印)
├── main.c           # 入口程序(批处理 + REPL 双模式)
├── test.expr        # 测试用例文件
├── doc/
│   ├── calc1.md     # 第一次构建:tokenizer 基础
│   ├── calc2.md     # 第二次构建:取模、负号区分、测试套件
│   ├── calc3.md     # 第三次构建:表达式求值器
│   └── calc4.md     # 本次构建:交互式 REPL
└── build/
    └── calc

6. 测试验证

6.1 回归测试

批处理模式不受影响,make test 9/9 全部 PASS:

bash 复制代码
$ make test
============================================
  calc --- 测试套件
============================================
  PASS  [ 1] (90-18)/3+15           → 39
  PASS  [ 2] 10%3                   → 1
  PASS  [ 3] -5+3                   → -2
  PASS  [ 4] 3+-5                   → -2
  PASS  [ 5] (-3)                   → -3
  PASS  [ 6] (-8+20)%-3             → 0
  PASS  [ 7] 3 + 5                  → 8
  PASS  [ 8] 3+@5                   → error
  PASS  [ 9] <empty>                → error
============================================
  9 passed, 0 failed, 9 total
============================================

6.2 交互模式验证

复制代码
$ ./build/calc
calc> (90-18)/3+15
  90 - 18 = 72
  72 / 3 = 24
  24 + 15 = 39
  result: 39
calc> 3+5*2
  5 * 2 = 10
  3 + 10 = 13
  result: 13
calc> 10%3
  10 % 3 = 1
  result: 1
calc> --5
  result: 5
calc> exit

6.3 管道输入验证

bash 复制代码
$ echo "(90-18)/3+15" | ./build/calc
calc>   90 - 18 = 72
  72 / 3 = 24
  24 + 15 = 39
  result: 39

7. 设计要点

  1. 双模式共存main() 通过 argc 简单分流,两种模式互不干扰
  2. 步骤打印嵌入求值过程 :在递归下降的每个二元运算点插入 printf,无需额外遍历
  3. 打印顺序自动反映优先级 :内层 term 先于外层 expr 求值,* / % 的步骤行自然出现在 + - 之前
  4. 静默求值不受影响eval_set_print_steps(0)print_steps 为假,不产生任何额外输出,批处理和测试路径完全透明
相关推荐
猫猫不是喵喵.18 小时前
双亲委派机制与类加载过程
java·开发语言
布朗克16818 小时前
Go入门到精通-22-同步原语
开发语言·后端·golang·同步原语
帅次18 小时前
Kotlin Flow 与 StateFlow:UI 单向数据流基础
开发语言·ui·kotlin·stateflow·onlifecycle
十月的皮皮19 小时前
stm20260719-STM32F103RCT6_HAL库_三人抢答器
c语言·stm32·单片机·嵌入式硬件·stm32cubemx·hal库
艾伦野鸽ggg19 小时前
JavaScript 浏览器本地存储
开发语言·javascript·ecmascript
AOwhisky20 小时前
Python 学习笔记(第十一期)——运维自动化(上·后篇):进程级监控与子进程管理——psutil进阶
运维·开发语言·python·学习·云原生·运维开发
人工干智能20 小时前
Python 的链式调用:一连串的“点”调用
开发语言·python
wuyk55520 小时前
53.嵌入式开发中栈溢出的深度解析:从HardFault到解决方案
c语言·stm32·单片机
就不掉头发20 小时前
如何优化程序的性能
开发语言