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:
parent
1fe5076544
commit
04efdcac8e
3 changed files with 112 additions and 36 deletions
2
Makefile
2
Makefile
|
@ -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
|
||||||
|
|
|
@ -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")!;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -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...];
|
Loading…
Reference in a new issue