+test.ha (3261B)
1 use lexical::lex; 2 3 fn literal( 4 scan: *lex::scanner, 5 lexeme: const str, 6 user: nullable *opaque, 7 ) (str | *lex::token | lex::error) = { 8 return lex::scan_token(scan, void, lexeme); 9 }; 10 11 fn skip( 12 scan: *lex::scanner, 13 lexeme: const str, 14 user: nullable *opaque, 15 ) (str | *lex::token | lex::error) = { 16 return lexeme; 17 }; 18 19 def FOO = "FOO"; 20 def BAR = "BAR"; 21 def NAME = "NAME"; 22 23 def keywords = [ 24 (FOO, "foo"), 25 (BAR, "bar"), 26 ]; 27 28 def exprs = [ 29 (NAME, `([a-z]|[A-Z])([a-z]|[A-Z]|[0-9]|_)*`), 30 ]; 31 32 @test fn test_parse() void = { 33 let actions: []lex::action = []; 34 defer free(actions); 35 for (let keyword .. keywords) { 36 append(actions, lex::action { 37 expr = keyword.1, 38 cb = &literal, 39 name = keyword.0, 40 ... 41 })!; 42 }; 43 for (let expr .. exprs) { 44 append(actions, lex::action { 45 expr = expr.1, 46 cb = &literal, 47 name = expr.0, 48 ... 49 })!; 50 }; 51 append(actions, lex::action { 52 expr = "( |\t|\n|\r)+", 53 cb = &skip, 54 ... 55 })!; 56 const be = lex::def_backend()!(actions)!; 57 defer lex::destroy(be); 58 59 const in = "foo bar foobar foo"; 60 const lexer = lex::init(be, in); 61 defer lex::finish(&lexer); 62 63 const res = want(&lexer, BAR, lex::EOF); 64 assert(res is lex::error); 65 assert(res is lex::syntax); 66 const res = res as lex::syntax; 67 assert(res.1 == "Unexpected 'FOO', was expecting 'BAR', 'EOF'"); 68 assert(res.0.line == 1); 69 assert(res.0.col == 1); 70 assert(res.0.off == 0); 71 72 const res = want(&lexer, BAR, FOO); 73 assert(res is *lex::token); 74 const res = res as *lex::token; 75 assert(res.name == FOO); 76 assert(res.lexeme == "foo"); 77 assert(res.value is void); 78 assert(res.start.line == 1); 79 assert(res.start.col == 1); 80 assert(res.start.off == 0); 81 assert(res.end.line == 1); 82 assert(res.end.col == 3); 83 assert(res.end.off == 2); 84 85 const res = try(&lexer, FOO); 86 assert(res is void); 87 88 const res = want(&lexer, FOO); 89 assert(res is lex::error); 90 assert(res is lex::syntax); 91 const res = res as lex::syntax; 92 assert(res.1 == "Unexpected 'BAR', was expecting 'FOO'"); 93 assert(res.0.line == 1); 94 assert(res.0.col == 5); 95 assert(res.0.off == 4); 96 97 const res = try(&lexer, BAR); 98 assert(res is *lex::token); 99 const res = res as *lex::token; 100 assert(res.name == BAR); 101 assert(res.lexeme == "bar"); 102 assert(res.value is void); 103 assert(res.start.line == 1); 104 assert(res.start.col == 5); 105 assert(res.start.off == 4); 106 assert(res.end.line == 1); 107 assert(res.end.col == 7); 108 assert(res.end.off == 6); 109 110 const res = peek(&lexer, lex::EOF); 111 assert(res is void); 112 113 const res = peek(&lexer, NAME); 114 assert(res is *lex::token); 115 116 const res = want(&lexer, NAME); 117 assert(res is *lex::token); 118 const res = res as *lex::token; 119 assert(res.name == NAME); 120 assert(res.lexeme == "foobar"); 121 assert(res.value is void); 122 assert(res.start.line == 1); 123 assert(res.start.col == 9); 124 assert(res.start.off == 8); 125 assert(res.end.line == 1); 126 assert(res.end.col == 14); 127 assert(res.end.off == 13); 128 129 const res = want(&lexer, FOO); 130 assert(res is *lex::token); 131 132 const res = want(&lexer, FOO); 133 assert(res is lex::error); 134 assert(res is lex::syntax); 135 const res = res as lex::syntax; 136 assert(res.1 == "Unexpected 'EOF', was expecting 'FOO'"); 137 assert(res.0.line == 1); 138 assert(res.0.col == 19); 139 assert(res.0.off == 18); 140 141 const res = want(&lexer, lex::EOF); 142 assert(res is *lex::token); 143 };