add pound literals for booleans and characters

replaces the old character literal syntax that i really didn't like and
feels more scheme-ish
This commit is contained in:
Lobo Torres 2024-12-04 21:40:14 -03:00
parent 1fe5076544
commit 04efdcac8e
3 changed files with 112 additions and 36 deletions

View file

@ -9,6 +9,6 @@ HAREPATH=vendor/hare-unicode:${HARESRCDIR}/stdlib:${HARESRCDIR}/third-party
.PHONY: check build .PHONY: check build
check: check:
@env HAREPATH=${HAREPATH} hare test @env HAREPATH=${HAREPATH} hare test -T +test
build: build:
@env HAREPATH=${HAREPATH} hare build @env HAREPATH=${HAREPATH} hare build

View file

@ -4,13 +4,28 @@ use encoding::utf8;
use io; use io;
use memio; use memio;
use unicode; use unicode;
use strconv;
// Testing dependency
use fmt; use fmt;
use strings; use strings;
// my cod prob sux :( // my cod prob sux :(
def longcharnames: [_](str, rune) = [
("nul", '\u0000'),
("alarm", '\u0007'),
("backspace", '\u0008'),
("newline", '\u000a'),
("tab", '\u0009'),
("linefeed", '\u000a'),
("vtab", '\u000b'),
("page", '\u000c'),
("return", '\u000d'),
("esc", '\u001b'),
("space", '\u0020'),
("delete", '\u007f'),
];
export type lexer = struct { export type lexer = struct {
in: io::handle, in: io::handle,
strbuf: memio::stream, strbuf: memio::stream,
@ -70,8 +85,8 @@ export fn lex(lex: *lexer) (token | io::EOF | error) = {
} else { } else {
return symbol{ v = v, kw = true }; return symbol{ v = v, kw = true };
}; };
case '\'' => case '#' =>
return scanchar(lex)?; return scanhash(lex)?;
case '"' => case '"' =>
return scanstr(lex)?; return scanstr(lex)?;
case => case =>
@ -197,7 +212,30 @@ fn scanstr(lex: *lexer) (str | error) = {
return memio::string(&lex.strbuf)!; return memio::string(&lex.strbuf)!;
}; };
fn scanhash(lex: *lexer) (token | error) = {
const rn = match (nextrune(lex)?) {
case let rn: rune =>
yield rn;
case io::EOF =>
return ("hash literal", lex.loc.0, lex.loc.1): unterminated;
};
switch (rn) {
case 't' =>
return true;
case 'f' =>
return false;
case '\\' =>
return scanchar(lex)?;
case =>
return lex.loc: invalid;
};
};
fn scanchar(lex: *lexer) (rune | error) = { fn scanchar(lex: *lexer) (rune | error) = {
static let namebuf: [16]u8 = [0...];
let namebuf = memio::fixed(namebuf);
const rn = match (nextrune(lex)?) { const rn = match (nextrune(lex)?) {
case let rn: rune => case let rn: rune =>
yield rn; yield rn;
@ -205,10 +243,23 @@ fn scanchar(lex: *lexer) (rune | error) = {
return ("character literal", lex.loc.0, lex.loc.1): unterminated; return ("character literal", lex.loc.0, lex.loc.1): unterminated;
}; };
switch (rn) { match (nextrune(lex)?) {
case '\\' => case let rnn: rune =>
return scanescape(lex)?; unget(lex, rnn);
case => if (isspace(rnn)) {
return rn;
} else {
memio::appendrune(&namebuf, rn)!;
memio::concat(&namebuf, scanword(lex)?)!;
const name = memio::string(&namebuf)!;
for (let i = 0z; i < len(longcharnames); i += 1) {
if (name == longcharnames[i].0) {
return longcharnames[i].1;
};
};
return lex.loc: invalid;
};
case io::EOF =>
return rn; return rn;
}; };
}; };
@ -218,7 +269,7 @@ fn scanescape(lex: *lexer) (rune | error) = {
case let rn: rune => case let rn: rune =>
yield rn; yield rn;
case io::EOF => case io::EOF =>
return lex.loc: invalid; return ("escape sequence", lex.loc.0, lex.loc.1): unterminated;
}; };
switch (rn) { switch (rn) {
@ -230,8 +281,6 @@ fn scanescape(lex: *lexer) (rune | error) = {
return '\n'; return '\n';
case 't' => case 't' =>
return '\t'; return '\t';
case 's' =>
return ' ';
case => case =>
return lex.loc: invalid; return lex.loc: invalid;
}; };
@ -250,9 +299,8 @@ fn isspace(rn: rune) bool = {
}; };
}; };
def delimiters = `()[]{}\:'`;
fn isdelimiter(rn: rune) bool = { fn isdelimiter(rn: rune) bool = {
match (strings::index(delimiters, rn)) { match (strings::index(`()[]{}\:#`, rn)) {
case size => case size =>
return true; return true;
case => case =>
@ -262,27 +310,14 @@ fn isdelimiter(rn: rune) bool = {
@test fn lex() void = { @test fn lex() void = {
const cases: [_](str, []token) = [ const cases: [_](str, []token) = [
( (`"hello" \greeting def`,
`"hello" \greeting def`, ["hello", mksym("greeting"), mkword("def")]),
[ (`[dup *] (a -- a) \square def`,
"hello", [quotstart, mkword("dup"), mkword("*"), quotend,
mksym("greeting"), mkcomment("a -- a"), mksym("square"),
mkword("def"), mkword("def")]),
] (`#t #f`, [true, false]),
), (`#\a #\space #\nul`, ['a', ' ', '\0']),
(
`[dup *] (a -- a) \square def`,
[
quotstart,
mkword("dup"),
mkword("*"),
quotend,
mkcomment("a -- a"),
mksym("square"),
mkword("def"),
]
),
(`'\s`, [' '])
]; ];
for (let i = 0z; i < len(cases); i += 1) { for (let i = 0z; i < len(cases); i += 1) {
@ -294,7 +329,15 @@ fn isdelimiter(rn: rune) bool = {
for (let j = 0z; j < len(cases[i].1); j += 1) { for (let j = 0z; j < len(cases[i].1); j += 1) {
const want = cases[i].1[j]; const want = cases[i].1[j];
const have = lex(&lexer)! as token; const have = lex(&lexer)! as token;
assert(tokeq(want, have));
if (!tokeq(want, have)) {
fmt::printfln("Case {}: {}", i, cases[i].0)!;
fmt::print("\tExpected: ")!;
tokpprint(want);
fmt::print("\tGot: ")!;
tokpprint(have);
assert(false);
};
}; };
assert(lex(&lexer) is io::EOF); assert(lex(&lexer) is io::EOF);
@ -321,6 +364,39 @@ fn tokeq(have: token, want: token) bool = {
return (have as comment).v == c.v; return (have as comment).v == c.v;
case let r: rune => case let r: rune =>
return have as rune == r; return have as rune == r;
case let b: bool =>
return have as bool == b;
};
};
fn tokpprint(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")!;
}; };
}; };

View file

@ -15,7 +15,7 @@ export type word = struct { v: str };
export type symbol = struct { v: str, kw: bool }; export type symbol = struct { v: str, kw: bool };
export type token = (quotstart | quotend | mapstart | mapend | export type token = (quotstart | quotend | mapstart | mapend |
word | symbol | comment | str | rune); word | symbol | comment | str | rune | bool);
export fn strerror(err: error) const str = { export fn strerror(err: error) const str = {
static let buf: [64]u8 = [0...]; static let buf: [64]u8 = [0...];