kojote/parse/+test/lexer.ha

117 lines
2.7 KiB
Hare

use memio;
use fmt;
use strings;
use io;
@test fn lex() void = {
const cases: [_](str, []token) = [
(`"hello" \greeting def`,
["hello", mksym("greeting"), mkword("def")]),
(`[dup *] (a -- a) \square def`,
[quotstart, mkword("dup"), mkword("*"), quotend,
mkcomment("a -- a"), mksym("square"),
mkword("def")]),
(`#t #f`, [true, false]),
(`#\a #\space #\nul`, ['a', ' ', '\0']),
(`"\x0a;" "\x2014;" "\x2f9f4;"`, ["\n", "", "嶲"]),
(`#\x #\x0a; #\x2014; #\x2f9f4;`, ['x', '\n', '—', '嶲']),
];
for (let i = 0z; i < len(cases); i += 1) {
const src = strings::toutf8(cases[i].0);
const src = memio::fixed(src);
const lexer = newlexer(&src, "<string>");
defer close(&lexer);
for (let j = 0z; j < len(cases[i].1); j += 1) {
const want = cases[i].1[j];
const have = match (lex(&lexer)) {
case let tok: token =>
yield tok;
case io::EOF =>
assert(false, "reached EOF");
return;
case let err: error =>
assert(false, strerror(err));
return;
};
if (!tokeq(want, have)) {
fmt::printfln("Case {}: {}", i, cases[i].0)!;
fmt::print("\tExpected: ")!;
tokpp(want);
fmt::print("\tGot: ")!;
tokpp(have);
assert(false);
};
};
assert(lex(&lexer) is io::EOF);
};
};
fn tokeq(have: token, want: token) bool = {
match (want) {
case quotstart =>
return have is quotstart;
case quotend =>
return have is quotend;
case mapstart =>
return have is mapstart;
case mapend =>
return have is mapend;
case let w: word =>
return (have as word).v == w.v;
case let s: str =>
return have as str == s;
case let s: symbol =>
return (have as symbol).v == s.v;
case let c: comment =>
return (have as comment).v == c.v;
case let r: rune =>
return have as rune == r;
case let b: bool =>
return have as bool == b;
};
};
fn tokpp(tok: token) void = {
match (tok) {
case quotstart =>
fmt::println("[")!;
case quotend =>
fmt::println("]")!;
case mapstart =>
fmt::println("{")!;
case mapend =>
fmt::println("}")!;
case let w: word =>
fmt::println(w.v)!;
case let s: symbol =>
fmt::printfln("{}{}", if (s.kw) ":" else "\\", s.v)!;
case let s: str =>
fmt::printfln(`"{}"`, s)!;
case let c: comment =>
fmt::printfln("({})", c.v)!;
case let r: rune =>
for (let i = 0z; i < len(longcharnames); i += 1) {
if (r == longcharnames[i].1) {
fmt::printfln("#\\{}", longcharnames[i].0)!;
return;
};
};
fmt::printfln("#\\{}", r)!;
case let b: bool =>
fmt::println(if (b) "#t" else "#f")!;
};
};
fn mkword(v: const str) word =
word{ v = v };
fn mkcomment(v: const str) comment =
comment{ v = v };
fn mksym(v: const str, kw: bool = false) symbol =
symbol{ v = v, kw = kw };