【编译原理笔记】3.2 Input Buffering

1、为什么需要输入缓冲?

  • 词法分析器在识别标记时经常需要向前查看(lookahead)一个或多个字符才能确定当前标记的结束位置。

例如:

  1. 标识符识别:读到count后需要看到空格或运算符才能确定标识符结束

  2. 多字符运算符:在C语言中:

    • -可能是->的一部分

    • =可能是==的一部分

    • <可能是<=的一部分

  • 效率问题:逐个字符读取效率低下

  • 回退需求:确定标记边界后需要回退指针


2、缓冲对(Buffer Pairs)技术

2.1 双缓冲区设计

 [缓冲区1: N字节] [缓冲区2: N字节]
  • 缓冲区大小:通常为磁盘块大小(如4096字节)

  • 设计原理:利用系统调用批量读取,减少I/O开销

2.2 关键指针机制

 输入示例: E = M * C ^ 2 eof
           ↑     ↑
           lexemeBegin forward

两个核心指针

  1. lexemeBegin指针:标记当前词素的开始位置

  2. forward指针:向前扫描直到找到模式匹配

2.3 工作流程

 初始化: lexemeBegin和forward都指向输入开始
 ​
 循环执行:
   1. forward向前移动,检查字符
   2. 当确定标记边界时:
      - 记录lexemeBegin到forward之间的词素
      - lexemeBegin移动到forward位置
      - 返回标记给解析器
   
   3. 缓冲区管理:
      - 当forward到达缓冲区末端时
      - 加载另一个缓冲区
      - forward移动到新缓冲区开始

2.2.4 缓冲区切换算法

 // 伪代码示例
 while (有输入字符) {
     if (forward到达缓冲区1末端) {
         加载缓冲区2;
         forward = 缓冲区2起始位置;
     }
     else if (forward到达缓冲区2末端) {
         加载缓冲区1; 
         forward = 缓冲区1起始位置;
     }
     
     // 处理当前字符
     processCharacter(*forward);
     forward++;
 }

2.3 哨兵(Sentinels)优化技术

2.3.1 问题:频繁的缓冲区末端检查

原始方案需要对每个字符进行两次测试:

  1. 是否到达缓冲区末端?

  2. 当前字符是什么?

2.3.2 哨兵解决方案

在每个缓冲区末尾添加特殊字符(哨兵),将两个测试合并为一个。

哨兵字符选择:使用eof(文件结束符)

2.3.3 带哨兵的缓冲区结构

 [正常字符...][eof哨兵] [正常字符...][eof哨兵]

2.3.4 优化后的算法

 switch (*forward++) {
     case eof:
         if (forward在缓冲区1末端) {
             加载缓冲区2;
             forward = 缓冲区2起始;
         }
         else if (forward在缓冲区2末端) {
             加载缓冲区1;
             forward = 缓冲区1起始; 
         }
         else {
             // 真正的输入结束
             终止词法分析;
         }
         break;
         
     // 其他字符处理...
     case '<': 处理小于运算符; break;
     case '=': 处理等于运算符; break;
     // ...
 }

2.4 关键技术细节

2.4.1 缓冲区大小保证

重要定理:只要词素长度加上向前查看距离不超过N,就永远不会在确定词素前覆盖缓冲区。

数学表达:lexeme长度 + 向前查看距离 ≤ N

2.4.2 实际应用考虑

  • 现代语言:大多数标记很短,1-2字符向前查看足够

  • 长字符串处理:对于跨多行的长字符串,采用分段处理策略

  • Java示例:使用+运算符连接多行字符串片段

2.4.3 错误处理集成

 // 集成错误处理的完整算法
 char* forward = input_buffer;
 char* lexemeBegin = forward;
 ​
 while (*forward != EOF) {
     switch (*forward) {
         case eof:
             // 缓冲区管理逻辑
             handleBufferEnd();
             break;
             
         case '<':
             // 可能需要向前查看
             if (*(forward+1) == '=') {
                 // 识别为<=运算符
                 returnToken(RELOP_LE);
                 forward += 2;
             } else {
                 // 识别为<运算符  
                 returnToken(RELOP_LT);
                 forward++;
             }
             lexemeBegin = forward;
             break;
             
         // 其他字符处理...
     }
 }

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

colus_SEU

留下你的鼓励,予我前行动力😄

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值