Antlr4 ——词法规则
in 默认分类 with 0 comment

好的,我们来详细展开讲解 第三步:定义"单词"(词法规则 / Lexer Rules)。这是整个语法解析的基础,决定了如何将原始的字符流转换成有意义的语言单元。

1. 词法规则的核心概念

什么是词法规则?

词法规则定义了如何将输入的字符序列分解成一系列的词法符号(Tokens)。这个过程称为词法分析标记化

简单比喻:词法分析就像阅读时的"认字"过程——将连续的笔画识别成一个个独立的汉字/单词。

词法规则的基本结构

TokenName : pattern ;

2. 词法规则的命名规范

大小写约定

类型命名规范示例
词法规则首字母大写,驼峰式INT, Identifier, StringLiteral
语法规则首字母小写,驼峰式expression, ifStatement, variableDeclaration

常用命名模式

// 1. 关键字:全大写,描述性
IF : 'if';
ELSE : 'else';
WHILE : 'while';
RETURN : 'return';

// 2. 标识符:描述性名称
IDENTIFIER : [a-zA-Z_][a-zA-Z_0-9]*;

// 3. 字面量:类型_LITERAL 格式
INT_LITERAL : [0-9]+;
FLOAT_LITERAL : [0-9]+ '.' [0-9]*;
STRING_LITERAL : '"' .*? '"';

// 4. 运算符:简洁明确
ASSIGN : '=';
PLUS : '+';
EQ : '==';

// 5. 分隔符
LPAREN : '(';
RPAREN : ')';
LBRACE : '{';
RBRACE : '}';
SEMI : ';';
COMMA : ',';

3. 模式(Pattern)的语法详解

3.1 基本字符匹配

// 字面量字符
PLUS : '+';
MINUS : '-';
ASSIGN : '=';

// 字符序列
IF : 'if';
FOR : 'for';

3.2 字符类(Character Classes)

// 单个字符范围
DIGIT : [0-9];           // 0到9的数字
LETTER : [a-zA-Z];       // 所有字母
HEX_DIGIT : [0-9a-fA-F]; // 十六进制数字

// 字符组合
OPERATOR : [+-*/];       // 四个运算符中的任意一个
BINARY : [01];           // 二进制数字

// 取反字符类
NON_ZERO_DIGIT : [1-9];          // 非零数字
NON_DIGIT : ~[0-9];              // 非数字字符
NON_QUOTE : ~["];                // 非引号字符

3.3 量词(Quantifiers)

// ? : 0次或1次(可选)
OPTIONAL_MINUS : '-'? DIGIT+;

// * : 0次或多次
DIGITS : DIGIT*;                    // 零个或多个数字
WHITESPACE : [ \t]*;                // 零个或多个空格/tab

// + : 1次或多次(至少一个)
NUMBER : DIGIT+;                    // 一个或多个数字
IDENTIFIER : [a-zA-Z_]+;            // 至少一个字母/下划线

// {n,m} : n到m次
PRECISE_DIGITS : DIGIT{3,5};        // 3到5个数字
AT_LEAST_TWO : DIGIT{2,};           // 至少2个数字
EXACTLY_THREE : DIGIT{3};           // 正好3个数字

3.4 转义字符

// 特殊字符需要转义
LPAREN : '(';           // 正常字符,不需要转义
// LBRACKET : '[';      // 错误!'[' 是元字符,需要转义
LBRACKET : '\\[';       // 正确:转义方括号
ESCAPED_QUOTE : '\\"';  // 转义引号
NEWLINE : '\\n';        // 换行符
TAB : '\\t';            // 制表符
BACKSLASH : '\\\\';     // 反斜杠本身

4. 高级模式技巧

4.1 选择运算符(|)

// 匹配多种模式之一
OPERATOR : '+' | '-' | '*' | '/';
COMPARISON : '<' | '>' | '<=' | '>=' | '==' | '!=';
BOOLEAN : 'true' | 'false';

4.2 通配符和范围

ANY_CHAR : . ;                    // 匹配任意单个字符(除换行符)
ANY_CHAR_INCL_NL : .? ;          // 匹配任意字符包括换行符(非贪婪)

// 复杂范围
WORD_CHAR : [a-zA-Z0-9_];        // 单词字符
NON_SPACE : ~[ \t\r\n];          // 非空白字符

4.3 非贪婪匹配

// .*? : 非贪婪匹配(最短匹配)
STRING : '"' .*? '"';            // 匹配两个引号之间的最短内容
COMMENT : '/*' .*? '*/';         // 匹配注释内容(遇到第一个*/就结束)

// 对比贪婪匹配(错误示例)
// BAD_STRING : '"' .* '"';      // 错误!会匹配到最后一个引号

5. 片段规则(Fragments)

片段规则是可重用的词法规则组件,本身不生成 Token,只被其他词法规则引用。

为什么要用片段规则?

  1. 提高可读性:将复杂模式分解
  2. 促进重用:避免重复定义
  3. 便于维护:修改一处,影响全局

片段规则示例

// 定义片段(不生成独立Token)
fragment DIGIT : [0-9];
fragment LETTER : [a-zA-Z];
fragment HEX_DIGIT : [0-9a-fA-F];
fragment ESCAPED_CHAR : '\\\\' [btnfr"'\\];  // 转义字符

// 使用片段构建完整规则
INT : DIGIT+;
FLOAT : DIGIT+ '.' DIGIT*;
IDENTIFIER : LETTER (LETTER | DIGIT)*;
HEX_NUMBER : '0' [xX] HEX_DIGIT+;
STRING : '"' (ESCAPED_CHAR | ~["\\])* '"';

6. 词法规则指令(Lexer Commands)

词法规则指令用 -> 符号指定,控制匹配后的行为。

6.1 跳过指令(skip)

// 最常见的指令:跳过匹配的内容
WS : [ \t\r\n]+ -> skip;         // 跳过空白字符
COMMENT : '//' ~[\r\n]* -> skip; // 跳过单行注释
MULTILINE_COMMENT : '/*' .*? '*/' -> skip; // 跳过多行注释

6.2 通道指令(channel)

// 将Token发送到特定通道(不跳过,但语法规则通常忽略)
WHITESPACE : [ \t\r\n]+ -> channel(HIDDEN);  // 隐藏通道
COMMENTS : '//' ~[\r\n]* -> channel(HIDDEN);  // 注释也隐藏

6.3 模式指令(mode)

用于处理复杂的分词状态(如字符串内的转义字符),属于高级特性。


7. 完整的词法规则示例

让我们看一个完整的编程语言词法规则示例:

lexer grammar CommonTokens;

// ===== 关键字 =====
IF : 'if';
ELSE : 'else';
WHILE : 'while';
FOR : 'for';
RETURN : 'return';
VAR : 'var';
FUNCTION : 'function';

// ===== 类型关键字 =====
INT_TYPE : 'int';
FLOAT_TYPE : 'float';
STRING_TYPE : 'string';
BOOLEAN_TYPE : 'boolean';

// ===== 字面量 =====
fragment DIGIT : [0-9];
fragment LETTER : [a-zA-Z_];
fragment ESCAPE : '\\\\' [btnfr"'\\];

INT_LITERAL : DIGIT+;
FLOAT_LITERAL : DIGIT+ '.' DIGIT*;
STRING_LITERAL : '"' (ESCAPE | ~["\\])* '"';
BOOLEAN_LITERAL : 'true' | 'false';

// ===== 标识符 =====
IDENTIFIER : LETTER (LETTER | DIGIT)*;

// ===== 运算符 =====
ASSIGN : '=';
PLUS : '+';
MINUS : '-';
MUL : '*';
DIV : '/';
MOD : '%';

EQ : '==';
NEQ : '!=';
LT : '<';
GT : '>';
LTE : '<=';
GTE : '>=';

// ===== 分隔符 =====
LPAREN : '(';
RPAREN : ')';
LBRACE : '{';
RBRACE : '}';
LBRACKET : '[';
RBRACKET : ']';
SEMICOLON : ';';
COMMA : ',';
DOT : '.';

// ===== 需要跳过的内容 =====
WHITESPACE : [ \t\r\n]+ -> skip;
LINE_COMMENT : '//' ~[\r\n]* -> skip;
BLOCK_COMMENT : '/*' .*? '*/' -> skip;

8. 词法规则的优先级和冲突解决

8.1 优先级原则

ANTLR 词法规则的两个重要原则:

  1. 最长匹配原则:优先匹配最长的字符串
  2. 最先定义原则:如果多个规则匹配相同长度,选择最先定义的

8.2 常见冲突和解决方案

冲突1:关键字 vs 标识符

// 正确:关键字必须先定义
IF : 'if';
ELSE : 'else';
IDENTIFIER : [a-zA-Z_][a-zA-Z_0-9]*;  // 后定义,不会覆盖关键字

// 错误:如果IDENTIFIER先定义,会匹配到'if'等关键字

冲突2:运算符的歧义

// 注意顺序:更具体的规则要先定义
LTE : '<=';    // 必须先于LT
LT : '<';      // 后定义

GTE : '>=';
GT : '>';

EQ : '==';
ASSIGN : '=';

8.3 测试优先级

输入 <= 的匹配过程:

  1. 尝试匹配 LTE:成功(2个字符)
  2. 尝试匹配 LT:成功(1个字符),但比 LTE
  3. 结果:选择 LTE(最长匹配)

9. 调试技巧和最佳实践

9.1 调试词法规则

// 临时添加调试规则
DEBUG_TOKEN : 'DEBUG_' .*? 'END' {System.out.println("Debug: " + getText());};

9.2 最佳实践

  1. 关键字优先:总是先定义关键字,再定义标识符
  2. 具体优先:更具体的模式要先定义
  3. 使用片段:复杂模式分解为片段规则
  4. 充分测试:为边界情况编写测试用例
  5. 处理所有字符:确保语法能处理所有可能的输入字符

9.3 错误模式示例

// ❌ 错误:关键字被标识符覆盖
IDENTIFIER : [a-zA-Z]+;  // 这个会匹配到'if'
IF : 'if';               // 永远不会被匹配到!

// ✅ 正确:关键字先定义
IF : 'if';
IDENTIFIER : [a-zA-Z]+;  // 不会匹配'if',因为IF规则优先

10. 完整示例:简单计算器的词法规则

grammar SimpleCalculator;

// === 词法规则 ===

// 数字
INT : [0-9]+;
FLOAT : [0-9]+ '.' [0-9]*;

// 运算符  
PLUS : '+';
MINUS : '-';
MULTIPLY : '*';
DIVIDE : '/';
POWER : '^';
MODULO : '%';

// 括号
LPAREN : '(';
RPAREN : ')';

// 函数和常量
SQRT : 'sqrt';
SIN : 'sin';
COS : 'cos';
PI : 'pi';
E : 'e';

// 空白字符(跳过)
WS : [ \t\r\n]+ -> skip;

// === 语法规则(下一节详细讲解)===
expression : term ( (PLUS | MINUS) term )* ;
term : factor ( (MULTIPLY | DIVIDE) factor )* ;
factor : (PLUS | MINUS)? (INT | FLOAT | function | LPAREN expression RPAREN) ;
function : (SQRT | SIN | COS) LPAREN expression RPAREN ;

总结

词法规则是 ANTLR4 语法的基础,它负责:

掌握了词法规则,你就为后续的语法规则(如何将这些"单词"组成"句子")打下了坚实基础。下一步,我们将进入语法规则的学习。