词法分析器负责输入源码字符串,输出一串词法单元序列。它的接口是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
pub enum FinalToken { ... }
pub struct IRLexer<'src> { ... }
impl<'src> Iterator for IRLexer<'src> { type Item = (Result<FinalToken, String>, logos::Span);
fn next(&mut self) -> Option<Self::Item> { ... } } impl<'src> IRLexer<'src> { pub fn new(source: &'src str) -> Self { ... } }
|
PrimToken 和 FinalToken
最初我直接让 FinalToken 实现 logos::Logos 的注解, 但这么做在面对浮点常量之类的语法规则时很容易写得非常丑陋. 例如, 浮点常量一共有这么几条规则:
- 十进制浮点, 小数模式:
[+-]?[0-9]*\.[0-9]+
- 十进制浮点, 整数模式:
[+-]?[0-9]+\.
- 十进制浮点, 指数模式1:
[+-]?[0-9]*\.[0-9]+[eE][+-]?[0-9]+
- 十进制浮点, 指数模式2:
[+-]?[0-9]+(\.)?[eE][+-]?[0-9]+
- 十六进制浮点, 模式1:
[+-]?0[xX][0-9A-Fa-f]*\.[0-9A-Fa-f]+[pP][+-]?[0-9]+
- 十六进制浮点, 模式2:
[+-]?0[xX][0-9A-Fa-f]+(\.)?[pP][+-]?[0-9]+
这几个要混在一起写的话, 且不说这些变体的反序列化能否一起做, 就正则表达式的可维护性都够烂了。因此我得这么做:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| #[derive(Logos)] pub enum ???Token { ...
#[regex(r"[+-]?[0-9]*\.[0-9]+", PrimToken::parse_dec_fp)] LitFPDec0Std(f64),
#[regex(r"[+-]?[0-9]+\.", PrimToken::parse_dec_fp)] LitFPDec1Std(f64),
#[regex(r"[+-]?[0-9]*\.[0-9]+[eE][+-]?[0-9]+", PrimToken::parse_dec_fp)] LitFPDec0Exp(f64),
#[regex(r"[+-]?[0-9]+(\.)?[eE][+-]?[0-9]+", PrimToken::parse_dec_fp)] LitFPDec1Exp(f64),
#[regex(r"[+-]?0[xX][0-9A-Fa-f]*\.[0-9A-Fa-f]+[pP][+-]?[0-9]+")] LitFPHex0(&'lex str),
#[regex(r"[+-]?0[xX][0-9A-Fa-f]+(\.)?[pP][+-]?[0-9]+")] LitFPHex1(&'lex str),
... }
|
不过 parser 要是看到一个浮点数有这么多变体的话, 匹配都匹配不过来了. 因此我选择把“负责做词法分析的那个 token 类型”和“负责给 parser 看的那个 token 类型”拆分开来, 搞了私有的 PrimToken 和开放的 FinalToken.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| #[derive(Logos)] enum PrimToken<'src> { ... }
impl<'src> PrimToken<'src> { fn into_final(self) -> Result<FinalToken, String> { } }
pub struct IRLexer<'src> { spanned: logos::SpannedIter<'lex, PrimToken<'lex>>, } impl<'src> Iterator for IRLexer<'src> { type Item = (Result<FinalToken, String>, logos::Span);
fn next(&mut self) -> Option<Self::Item> { let (res, span) = self.spanned.next()?; let res = res .map_err(|()| String::from("Internal lexer error")) .and_then(|token| token.into_final()); Some((res, span)) } }
|
关键字解析
Remusys-IR 中充斥着大量的上下文关键字;甚至, 由于基本块标签语法的存在, 任意的 Word 都可以不是关键字。比如这样的:
1 2 3 4 5 6 7 8 9 10 11 12
| define dso_local i32 @main(i32 %argc, ptr %argv) { define: %ptr = load ptr, ptr %argv, align 8 %cond = icmp eq ptr %ptr, null br i1 %cond, label %add, label %sub add: %res = add nuw i32 %argc, 1 ret i32 %res sub: %res2 = sub nuw i32 %argc, 1 ret i32 %res2 }
|
注意看, 所有看上去像关键字的都可以放在标签上. 所以我选择在词法分析阶段只解析出 word 来,不做任何关键字判断. 到了哪个上下文要用关键字的时候再具体比较 word 内字符的内容。
写文章的时候突然想到可以把 [_A-Za-z][0-9_A-Za-z]*\: 当成一个独立的 token 来着… 不过写都写完了就这样吧.
附录
1. 所有词法规则
这些词法规则不是一开始就设计好的, 而是有什么需求就加什么.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
| #[derive(Debug, Clone, Logos)] enum PrimToken<'lex> { #[regex(r"[+-]?[0-9]+", |lex| PrimToken::parse_dec_int(lex.slice()))] LitDecInt(i128),
#[regex(r"[+-]?(0X|0x)[0-9A-Fa-f]+", |lex| PrimToken::parse_hex_int(lex.slice()))] LitHexInt(i128),
#[regex(r#"c\"([^\\\"]|(\\[0-9A-Fa-f][0-9A-Fa-f]))*\""#)] LitBytes(&'lex str),
#[regex(r"[+-]?[0-9]*\.[0-9]+", PrimToken::parse_dec_fp)] LitFPDec0Std(f64),
#[regex(r"[+-]?[0-9]+\.", PrimToken::parse_dec_fp)] LitFPDec1Std(f64),
#[regex(r"[+-]?[0-9]*\.[0-9]+[eE][+-]?[0-9]+", PrimToken::parse_dec_fp)] LitFPDec0Exp(f64),
#[regex(r"[+-]?[0-9]+(\.)?[eE][+-]?[0-9]+", PrimToken::parse_dec_fp)] LitFPDec1Exp(f64),
#[regex(r"[+-]?0[xX][0-9A-Fa-f]*\.[0-9A-Fa-f]+[pP][+-]?[0-9]+")] LitFPHex0(&'lex str),
#[regex(r"[+-]?0[xX][0-9A-Fa-f]+(\.)?[pP][+-]?[0-9]+")] LitFPHex1(&'lex str),
#[regex(r"%[a-z0-9A-Z_]+", |lex| &lex.slice()[1..])] PIdent(&'lex str),
#[regex(r"@[a-z0-9A-Z_]+", |lex| &lex.slice()[1..])] AIdent(&'lex str),
#[regex(r"[a-zA-Z_][a-z0-9A-Z_]*")] Word(&'lex str),
#[regex(r":")] Colon, #[regex(r",")] Comma, #[regex(r"!")] Exclaim, #[regex(r"\.\.=")] DotDotEq, #[regex(r"\.\.\.")] Ellipsis, #[regex(r"=")] Eq,
#[regex(r"\(")] LParen, #[regex(r"\)")] RParen,
#[regex(r"\[")] LBracket, #[regex(r"\]")] RBracket,
#[regex(r"\{")] LBrace, #[regex(r"\}")] RBrace,
#[regex(r"<")] LAngle, #[regex(r">")] RAngle,
#[regex(r"[ \n\r\t\f]+", logos::skip)] Space,
#[regex(r";[^\n\r]*", logos::skip, allow_greedy = true)] LineComment, }
|