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")), ""); 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")), ""); defer close(&lex); skipws(&lex)!; assert(nextrune(&lex)! == 'a'); assert(lex.loc.0 == 2u && lex.loc.1 == 2u); };