Files
Interpreter/Doc/compiler/词法.md
T
Admin d4e2c1ae7c 实现 12.4 词法:25 关键字、小写折叠、TIME 字面量、lex error。
- Lexer.h/cpp:关键字表(大小写不敏感折小写)、(* *) 注释不嵌套、整数/T# 段式字面量、最长匹配符号
- token 带 source_file + 行列(起始位置,修了消费后行列的偏差)
- lexer_test:75 断言(line1 三文件 token 流 21/34/27、大小写、符号、注释、行列、5 类负例)
- Doc/compiler/词法.md:设计文档 + 执行计划
2026-08-21 10:51:45 +08:00

4.4 KiB
Raw Blame History

词法(12.4

compiler 模块的词法分析。输入:单个 .st 源文件内容;输出:稳定 token 流(带 source_file + 行列)。语法与字段表见 初步计划.md 12.1、12.4。

做 / 不做

  • (* *) 注释、标识符、关键字、整数、TIME 字面量、:=、比较符、括号
  • 关键字与标识符统一折成小写内部形(与 IEC 一致,大小写不敏感)
  • 每个 token 带 source_file + 行列,供以后报错
  • 非法字符 → lex error 硬错误(带行列)

不做(第一版)

  • 字符串字面量、REAL 字面量、十六进制/科学计数
  • 注释嵌套((* 到第一个 *) 结束,未闭合报错)
  • 浮点数、_ 数字分隔符

关键字表(12.1 冻结,只认这些)

PROGRAM FUNCTION FUNCTION_BLOCK END_PROGRAM END_FUNCTION END_FUNCTION_BLOCK
VAR VAR_INPUT VAR_OUTPUT VAR_GLOBAL VAR_EXTERNAL END_VAR
BOOL INT TIME
IF ELSIF ELSE END_IF WHILE END_WHILE
AND OR NOT
TRUE FALSE
TON TOF CTU

其余单词一律当标识符;VAR_IN_OUTREFCLASS 等由语法层(12.5)出明确错误。

Token

enum class Tok {
    // 关键字(25 个,逐一对应上表)
    ...
    // 字面量
    IDENT,      // text = 小写名
    INT_LIT,    // int_value
    TIME_LIT,   // int_value = 毫秒
    // 符号
    ASSIGN,     // :=
    EQ, NE, LT, LE, GT, GE,          // = <> < <= > >=
    PLUS, MINUS, STAR, SLASH,        // + - * /
    LPAREN, RPAREN, COMMA, SEMI,     // ( ) , ;
    COLON, DOT,                      // : .
    END,                             // 文件结束
};

struct Token {
    Tok type;
    std::string text;      // 标识符/关键字的小写文本;字面量原文
    int64_t int_value;     // INT_LIT / TIME_LIT(毫秒)
    std::string source_file;
    uint32_t line;         // 从 1 起
    uint32_t col;          // 从 1 起
};

规则

大小写

  • 关键字匹配大小写不敏感program / PROGRAM / Program 同义)
  • 标识符统一折小写存入 text(内部形);原始大小写不保留(v1 无字符串字面量,报错靠行列定位)

字面量

  • 整数:[0-9]+int64_t 存储;超 INT64_MAXlex error
  • TIMET#(不敏感)+ 一个或多个 <整数><单位> 段,单位不敏感:ms s m h d,累加为毫秒:
    • T#10ms → 10
    • T#1s250ms → 1250
    • T#2h → 7200000
    • 缺单位 / 未知单位 / 空段 → lex error
  • 负数不是字面量:-5MINUS + 整数,由语法层处理

符号

  • := 优先于 :<> 优先于 </><=/>= 单独匹配

注释

  • (* ... *)不嵌套;未闭合 → lex error
  • 注释内不产 token

错误

稳定前缀 lex error,带文件与行列:

lex error: illegal character '@' (motor.st, line 3, col 7)
lex error: unterminated comment (main.st, line 1, col 1)
lex error: bad time literal 'T#3q' (main.st, line 4, col 10)

完成标准

  1. 第 11 节三个文件(examples/line1/*.stlex 成稳定 token 流;lexer_test 断言数量、类型、小写化、行列、END 收尾
  2. 非法字符 / 未闭合注释 / 坏 TIME 字面量 → lex error 负例过
  3. 全部构建 + ctest 无回归

执行计划(单步确认)

  1. Lexer.h(新建 compiler/include/compiler/Lexer.h

    • Tok 枚举:25 关键字 + IDENT/INT_LIT/TIME_LIT + 符号(:= = <> < <= > >= + - * / ( ) , ; : .+ END
    • Token { type, text, int_value, source_file, line, col }(行列从 1 起)
    • bool lex_file(path, out, err) / bool lex(source_file, content, out, err)
  2. Lexer.cpp(重写占位)

    • 25 关键字小写映射表,大小写不敏感,标识符统一折小写
    • (* *) 注释不嵌套、未闭合报错;非法字符报错
    • 整数 [0-9]+(超 INT64_MAX 报错);T#<int><单位> 段式解析累加毫秒(ms s m h d
    • := / <> / <= / >= 最长匹配优先
    • 错误前缀 lex error,带 file/line/col
  3. lexer_testtests/src/lexer_test.cpp + CMake,链 compiler 库)

    • line1 三文件:token 数量 / 类型序列 / 小写化 / 行列 / END 收尾断言
    • 负例:@ 非法字符、未闭合注释、T#3q 坏字面量、超大整数 → lex error 类别
  4. 验证cmake --build + ctest(新增 lexer_test 后 5/5 全绿)

  5. 提交Lexer.h/cpp + lexer_test + 文档