// Expect retcode = 0 // 词法单元:用带载荷 ADT 表达 TokenKind;关键词/算符各自独立 enum。 // 整型与字符在词法期解码并随载荷携带;文本一律借用源切片(不复制)。 type .{ .ByteView } = @import("utils/byte_view.spl"); type Keyword = enum { Any, As, Bool, Break, Catch, Comptime, Const, Continue, Defer, Else, Enum, Errdefer, False, Fn, For, If, Loop, Match, Null, Ret, Shape, Struct, True, Try, Type, Undefined, Union, Var, While, Underscore, } type KeywordResult = enum { Yes: Keyword, No, } type Punct = enum { Eq, Assign, AddAssign, Add, SubAssign, Sub, MulAssign, Mul, DivAssign, Div, ModAssign, Mod, RightArrow, LeftArrow, ArrowBoth, FatArrowBoth, RightFatArrow, AndAnd, AndAssign, And, OrOr, OrAssign, Or, XorAssign, Xor, LShAssign, LSh, Le, Lt, RShAssign, RSh, Ge, Gt, Not, Neq, BitNot, LBracket, RBracket, LParen, RParen, LBrace, RBrace, Semicolon, Comma, Colon, ColonAssign, Dot, Range, Ellipsis, Cond, Sharp, At, Dollar, } type Radix = enum { Dec, Bin, Oct, Hex, } type TokenKind = enum { Keyword: Keyword, Punct: Punct, Ident, Int: Radix, // 仅记进制;数值由 parser 按任意宽度(mp)解析 Float, Char: u8, Str, LineComment, Endline, Eof, Unknown: u8, } type Token = @self; kind: TokenKind, text: ByteView, line: usize, col: usize, offset: usize, // 源文件字节偏移(供 AST Span 使用) fn make(kind: TokenKind, text: ByteView, line: usize, col: usize, offset: usize): Token { ret Token { .kind = kind, .text = text, .line = line, .col = col, .offset = offset }; } fn is_trivia(kind: TokenKind): bool { match kind { .Endline => { ret true; }, .LineComment => { ret true; }, _ => { ret false; }, } } fn keyword_of(text: ByteView): KeywordResult { if is(text, "any") { ret .Yes(.Any); } if is(text, "as") { ret .Yes(.As); } if is(text, "bool") { ret .Yes(.Bool); } if is(text, "break") { ret .Yes(.Break); } if is(text, "catch") { ret .Yes(.Catch); } if is(text, "comptime") { ret .Yes(.Comptime); } if is(text, "const") { ret .Yes(.Const); } if is(text, "continue") { ret .Yes(.Continue); } if is(text, "defer") { ret .Yes(.Defer); } if is(text, "else") { ret .Yes(.Else); } if is(text, "enum") { ret .Yes(.Enum); } if is(text, "errdefer") { ret .Yes(.Errdefer); } if is(text, "false") { ret .Yes(.False); } if is(text, "fn") { ret .Yes(.Fn); } if is(text, "for") { ret .Yes(.For); } if is(text, "if") { ret .Yes(.If); } if is(text, "loop") { ret .Yes(.Loop); } if is(text, "match") { ret .Yes(.Match); } if is(text, "null") { ret .Yes(.Null); } if is(text, "ret") { ret .Yes(.Ret); } if is(text, "shape") { ret .Yes(.Shape); } if is(text, "struct") { ret .Yes(.Struct); } if is(text, "true") { ret .Yes(.True); } if is(text, "try") { ret .Yes(.Try); } if is(text, "type") { ret .Yes(.Type); } if is(text, "undefined") { ret .Yes(.Undefined); } if is(text, "union") { ret .Yes(.Union); } if is(text, "var") { ret .Yes(.Var); } if is(text, "while") { ret .Yes(.While); } if is(text, "_") { ret .Yes(.Underscore); } ret .No; } // ---- token 类型名(与 stage1 spl_tok_type_name 逐字一致,供 dump/diff 使用)---- fn keyword_name(kw: Keyword): []u8 { match kw { .Any => { ret "KW_ANY"; }, .As => { ret "KW_AS"; }, .Bool => { ret "KW_BOOL"; }, .Break => { ret "KW_BREAK"; }, .Catch => { ret "KW_CATCH"; }, .Comptime => { ret "KW_COMPTIME"; }, .Const => { ret "KW_CONST"; }, .Continue => { ret "KW_CONTINUE"; }, .Defer => { ret "KW_DEFER"; }, .Else => { ret "KW_ELSE"; }, .Enum => { ret "KW_ENUM"; }, .Errdefer => { ret "KW_ERRDEFER"; }, .False => { ret "KW_FALSE"; }, .Fn => { ret "KW_FN"; }, .For => { ret "KW_FOR"; }, .If => { ret "KW_IF"; }, .Loop => { ret "KW_LOOP"; }, .Match => { ret "KW_MATCH"; }, .Null => { ret "KW_NULL"; }, .Ret => { ret "KW_RET"; }, .Shape => { ret "KW_SHAPE"; }, .Struct => { ret "KW_STRUCT"; }, .True => { ret "KW_TRUE"; }, .Try => { ret "KW_TRY"; }, .Type => { ret "KW_TYPE"; }, .Undefined => { ret "KW_UNDEFINED"; }, .Union => { ret "KW_UNION"; }, .Var => { ret "KW_VAR"; }, .While => { ret "KW_WHILE"; }, .Underscore => { ret "KW_UNDERSCORE"; }, } ret "?"; } fn punct_name(punct: Punct): []u8 { match punct { .Eq => { ret "TOK_EQ"; }, .Assign => { ret "TOK_ASSIGN"; }, .AddAssign => { ret "TOK_ASSIGN_ADD"; }, .Add => { ret "TOK_ADD"; }, .SubAssign => { ret "TOK_ASSIGN_SUB"; }, .Sub => { ret "TOK_SUB"; }, .MulAssign => { ret "TOK_ASSIGN_MUL"; }, .Mul => { ret "TOK_MUL"; }, .DivAssign => { ret "TOK_ASSIGN_DIV"; }, .Div => { ret "TOK_DIV"; }, .ModAssign => { ret "TOK_ASSIGN_MOD"; }, .Mod => { ret "TOK_MOD"; }, .RightArrow => { ret "TOK_RIGHT_ARROW"; }, .LeftArrow => { ret "TOK_LEFT_ARROW"; }, .ArrowBoth => { ret "TOK_ARROW_BOTH"; }, .FatArrowBoth => { ret "TOK_FAT_ARROW_BOTH"; }, .RightFatArrow => { ret "TOK_RIGHT_FAT_ARROW"; }, .AndAnd => { ret "TOK_AND_AND"; }, .AndAssign => { ret "TOK_ASSIGN_AND"; }, .And => { ret "TOK_AND"; }, .OrOr => { ret "TOK_OR_OR"; }, .OrAssign => { ret "TOK_ASSIGN_OR"; }, .Or => { ret "TOK_OR"; }, .XorAssign => { ret "TOK_ASSIGN_XOR"; }, .Xor => { ret "TOK_XOR"; }, .LShAssign => { ret "TOK_ASSIGN_L_SH"; }, .LSh => { ret "TOK_L_SH"; }, .Le => { ret "TOK_LE"; }, .Lt => { ret "TOK_LT"; }, .RShAssign => { ret "TOK_ASSIGN_R_SH"; }, .RSh => { ret "TOK_R_SH"; }, .Ge => { ret "TOK_GE"; }, .Gt => { ret "TOK_GT"; }, .Not => { ret "TOK_NOT"; }, .Neq => { ret "TOK_NEQ"; }, .BitNot => { ret "TOK_BIT_NOT"; }, .LBracket => { ret "TOK_L_BRACKET"; }, .RBracket => { ret "TOK_R_BRACKET"; }, .LParen => { ret "TOK_L_PAREN"; }, .RParen => { ret "TOK_R_PAREN"; }, .LBrace => { ret "TOK_L_BRACE"; }, .RBrace => { ret "TOK_R_BRACE"; }, .Semicolon => { ret "TOK_SEMICOLON"; }, .Comma => { ret "TOK_COMMA"; }, .Colon => { ret "TOK_COLON"; }, .ColonAssign => { ret "TOK_COLON_ASSIGN"; }, .Dot => { ret "TOK_DOT"; }, .Range => { ret "TOK_RANGE"; }, .Ellipsis => { ret "TOK_ELLIPSIS"; }, .Cond => { ret "TOK_COND"; }, .Sharp => { ret "TOK_SHARP"; }, .At => { ret "TOK_AT"; }, .Dollar => { ret "TOK_DOLLAR"; }, } ret "?"; } fn kind_name(kind: TokenKind): []u8 { match kind { .Keyword(kw) => { ret keyword_name(kw); }, .Punct(punct) => { ret punct_name(punct); }, .Ident => { ret "TOK_IDENT"; }, .Int(value) => { ret "TOK_INT_LITERAL"; }, .Float => { ret "TOK_FLOAT_LITERAL"; }, .Char(value) => { ret "TOK_CHAR_LITERAL"; }, .Str => { ret "TOK_STRING_LITERAL"; }, .LineComment => { ret "TOK_LINE_COMMENT"; }, .Endline => { ret "TOK_ENDLINE"; }, .Eof => { ret "TOK_EOF"; }, .Unknown(value) => { ret "TOK_UNKNOWN"; }, } ret "?"; } // ---- 测试辅助 ---- fn view(text: []u8): ByteView { ret ByteView.from_slice(text); } fn is(text: ByteView, literal: []u8): bool { ret ByteView.eq(text, view(literal)); } fn expect_keyword(result: KeywordResult, expected: Keyword) { match result { .Yes(kw) => { @assert(@enum2int(kw) == @enum2int(expected), "keyword"); }, .No => { @assert(false, "expected keyword"); }, } } fn test_keywords() { expect_keyword(keyword_of(view("fn")), .Fn); expect_keyword(keyword_of(view("while")), .While); expect_keyword(keyword_of(view("_")), .Underscore); match keyword_of(view("foo")) { .Yes(kw) => { @assert(false, "foo is not a keyword"); }, .No => {}, } match keyword_of(view("")) { .Yes(kw) => { @assert(false, "empty is not a keyword"); }, .No => {}, } } fn test_adt_payloads() { var t = make(.Int(.Hex), view("0xFF"), 3, 5, 0); @assert(t.line == 3 && t.col == 5, "position"); match t.kind { .Int(radix) => { var hex: Radix = .Hex; @assert(@enum2int(radix) == @enum2int(hex), "int radix"); }, _ => { @assert(false, "expected int"); }, } var c = make(.Char('a'), view("'a'"), 1, 1, 0); match c.kind { .Char(value) => { @assert(value == 'a', "char value"); }, _ => { @assert(false, "expected char"); }, } var p = make(.Punct(.ArrowBoth), view("<->"), 1, 1, 0); match p.kind { .Punct(punct) => { var both: Punct = .ArrowBoth; @assert(@enum2int(punct) == @enum2int(both), "punct"); }, _ => { @assert(false, "expected punct"); }, } var k = make(.Keyword(.Ret), view("ret"), 1, 1, 0); match k.kind { .Keyword(kw) => { var ret_kw: Keyword = .Ret; @assert(@enum2int(kw) == @enum2int(ret_kw), "keyword kind"); }, _ => { @assert(false, "expected keyword"); }, } } fn test_trivia() { @assert(is_trivia(.Endline), "endline trivia"); @assert(is_trivia(.LineComment), "comment trivia"); @assert(!is_trivia(.Ident), "ident not trivia"); @assert(!is_trivia(.Eof), "eof not trivia"); } fn expect_kind_name(kind: TokenKind, expected: []u8) { @assert(ByteView.eq(ByteView.from_slice(kind_name(kind)), ByteView.from_slice(expected)), "token kind name"); } fn test_kind_names() { expect_kind_name(.Keyword(.Any), "KW_ANY"); expect_kind_name(.Keyword(.As), "KW_AS"); expect_kind_name(.Keyword(.Bool), "KW_BOOL"); expect_kind_name(.Keyword(.Break), "KW_BREAK"); expect_kind_name(.Keyword(.Catch), "KW_CATCH"); expect_kind_name(.Keyword(.Comptime), "KW_COMPTIME"); expect_kind_name(.Keyword(.Const), "KW_CONST"); expect_kind_name(.Keyword(.Continue), "KW_CONTINUE"); expect_kind_name(.Keyword(.Defer), "KW_DEFER"); expect_kind_name(.Keyword(.Else), "KW_ELSE"); expect_kind_name(.Keyword(.Enum), "KW_ENUM"); expect_kind_name(.Keyword(.Errdefer), "KW_ERRDEFER"); expect_kind_name(.Keyword(.False), "KW_FALSE"); expect_kind_name(.Keyword(.Fn), "KW_FN"); expect_kind_name(.Keyword(.For), "KW_FOR"); expect_kind_name(.Keyword(.If), "KW_IF"); expect_kind_name(.Keyword(.Loop), "KW_LOOP"); expect_kind_name(.Keyword(.Match), "KW_MATCH"); expect_kind_name(.Keyword(.Null), "KW_NULL"); expect_kind_name(.Keyword(.Ret), "KW_RET"); expect_kind_name(.Keyword(.Shape), "KW_SHAPE"); expect_kind_name(.Keyword(.Struct), "KW_STRUCT"); expect_kind_name(.Keyword(.True), "KW_TRUE"); expect_kind_name(.Keyword(.Try), "KW_TRY"); expect_kind_name(.Keyword(.Type), "KW_TYPE"); expect_kind_name(.Keyword(.Undefined), "KW_UNDEFINED"); expect_kind_name(.Keyword(.Union), "KW_UNION"); expect_kind_name(.Keyword(.Var), "KW_VAR"); expect_kind_name(.Keyword(.While), "KW_WHILE"); expect_kind_name(.Keyword(.Underscore), "KW_UNDERSCORE"); expect_kind_name(.Punct(.Eq), "TOK_EQ"); expect_kind_name(.Punct(.Assign), "TOK_ASSIGN"); expect_kind_name(.Punct(.AddAssign), "TOK_ASSIGN_ADD"); expect_kind_name(.Punct(.Add), "TOK_ADD"); expect_kind_name(.Punct(.SubAssign), "TOK_ASSIGN_SUB"); expect_kind_name(.Punct(.Sub), "TOK_SUB"); expect_kind_name(.Punct(.MulAssign), "TOK_ASSIGN_MUL"); expect_kind_name(.Punct(.Mul), "TOK_MUL"); expect_kind_name(.Punct(.DivAssign), "TOK_ASSIGN_DIV"); expect_kind_name(.Punct(.Div), "TOK_DIV"); expect_kind_name(.Punct(.ModAssign), "TOK_ASSIGN_MOD"); expect_kind_name(.Punct(.Mod), "TOK_MOD"); expect_kind_name(.Punct(.RightArrow), "TOK_RIGHT_ARROW"); expect_kind_name(.Punct(.LeftArrow), "TOK_LEFT_ARROW"); expect_kind_name(.Punct(.ArrowBoth), "TOK_ARROW_BOTH"); expect_kind_name(.Punct(.FatArrowBoth), "TOK_FAT_ARROW_BOTH"); expect_kind_name(.Punct(.RightFatArrow), "TOK_RIGHT_FAT_ARROW"); expect_kind_name(.Punct(.AndAnd), "TOK_AND_AND"); expect_kind_name(.Punct(.AndAssign), "TOK_ASSIGN_AND"); expect_kind_name(.Punct(.And), "TOK_AND"); expect_kind_name(.Punct(.OrOr), "TOK_OR_OR"); expect_kind_name(.Punct(.OrAssign), "TOK_ASSIGN_OR"); expect_kind_name(.Punct(.Or), "TOK_OR"); expect_kind_name(.Punct(.XorAssign), "TOK_ASSIGN_XOR"); expect_kind_name(.Punct(.Xor), "TOK_XOR"); expect_kind_name(.Punct(.LShAssign), "TOK_ASSIGN_L_SH"); expect_kind_name(.Punct(.LSh), "TOK_L_SH"); expect_kind_name(.Punct(.Le), "TOK_LE"); expect_kind_name(.Punct(.Lt), "TOK_LT"); expect_kind_name(.Punct(.RShAssign), "TOK_ASSIGN_R_SH"); expect_kind_name(.Punct(.RSh), "TOK_R_SH"); expect_kind_name(.Punct(.Ge), "TOK_GE"); expect_kind_name(.Punct(.Gt), "TOK_GT"); expect_kind_name(.Punct(.Not), "TOK_NOT"); expect_kind_name(.Punct(.Neq), "TOK_NEQ"); expect_kind_name(.Punct(.BitNot), "TOK_BIT_NOT"); expect_kind_name(.Punct(.LBracket), "TOK_L_BRACKET"); expect_kind_name(.Punct(.RBracket), "TOK_R_BRACKET"); expect_kind_name(.Punct(.LParen), "TOK_L_PAREN"); expect_kind_name(.Punct(.RParen), "TOK_R_PAREN"); expect_kind_name(.Punct(.LBrace), "TOK_L_BRACE"); expect_kind_name(.Punct(.RBrace), "TOK_R_BRACE"); expect_kind_name(.Punct(.Semicolon), "TOK_SEMICOLON"); expect_kind_name(.Punct(.Comma), "TOK_COMMA"); expect_kind_name(.Punct(.Colon), "TOK_COLON"); expect_kind_name(.Punct(.ColonAssign), "TOK_COLON_ASSIGN"); expect_kind_name(.Punct(.Dot), "TOK_DOT"); expect_kind_name(.Punct(.Range), "TOK_RANGE"); expect_kind_name(.Punct(.Ellipsis), "TOK_ELLIPSIS"); expect_kind_name(.Punct(.Cond), "TOK_COND"); expect_kind_name(.Punct(.Sharp), "TOK_SHARP"); expect_kind_name(.Punct(.At), "TOK_AT"); expect_kind_name(.Punct(.Dollar), "TOK_DOLLAR"); expect_kind_name(.Ident, "TOK_IDENT"); expect_kind_name(.Int(.Dec), "TOK_INT_LITERAL"); expect_kind_name(.Float, "TOK_FLOAT_LITERAL"); expect_kind_name(.Char('a'), "TOK_CHAR_LITERAL"); expect_kind_name(.Str, "TOK_STRING_LITERAL"); expect_kind_name(.LineComment, "TOK_LINE_COMMENT"); expect_kind_name(.Endline, "TOK_ENDLINE"); expect_kind_name(.Eof, "TOK_EOF"); expect_kind_name(.Unknown(0), "TOK_UNKNOWN"); } fn main() { test_keywords(); test_adt_payloads(); test_trivia(); test_kind_names(); }