kojote/lex.ha

106 lines
1.9 KiB
Hare

use bufio;
use fmt;
use io;
use memio;
use os;
use encoding::utf8;
use strings;
use ascii;
// my cod prob sux :(
export type lexer = struct {
in: io::handle,
strbuf: memio::stream,
path: str,
loc: (uint, uint),
prevloc: (uint, uint),
unread: (rune | void),
};
export fn newlexer(in: io::handle, path: str) lexer = {
return lexer {
in = in,
strbuf = memio::dynamic(),
path = path,
loc = (1, 0),
unread = void,
...
};
};
export fn close(lex: *lexer) void = {
io::close(&lex.strbuf)!;
};
fn updateloc(lex: *lexer, rn: rune) void = {
if (rn == '\n') {
lex.loc = (lex.loc.0 + 1, 0);
} else {
lex.loc.1 += 1;
};
};
fn nextrune(lex: *lexer) (rune | io::error | io::EOF | utf8::invalid) = {
match (lex.unread) {
case let rn: rune =>
lex.prevloc = lex.loc;
lex.unread = void;
updateloc(lex, rn);
return rn;
case void =>
yield;
};
match (bufio::read_rune(lex.in)?) {
case let rn: rune =>
lex.prevloc = lex.loc;
updateloc(lex, rn);
return rn;
case io::EOF =>
return io::EOF;
};
};
fn unget(lex: *lexer, rn: rune) void = {
assert(lex.unread is void);
lex.unread = rn;
lex.loc = lex.prevloc;
};
fn skipws(lex: *lexer) (void | io::EOF | io::error | utf8::invalid) = {
for (true) {
match (nextrune(lex)?) {
case io::EOF => return io::EOF;
case let rn: rune =>
if (!ascii::isspace(rn)) {
unget(lex, rn);
return;
};
};
};
};
// Tests! :)
@test fn test_nextrune() void = {
let lex = newlexer(&memio::fixed(strings::toutf8("a\nb")),
"<string>");
defer close(&lex);
assert(nextrune(&lex)! == 'a');
assert(nextrune(&lex)! == '\n');
assert(nextrune(&lex)! == 'b');
assert(lex.loc.0 == 2u && lex.loc.1 == 1u);
};
@test fn test_skipws() void = {
let lex = newlexer(&memio::fixed(strings::toutf8("\n a")),
"<string>");
defer close(&lex);
skipws(&lex)!;
assert(nextrune(&lex)! == 'a');
assert(lex.loc.0 == 2u && lex.loc.1 == 2u);
};