hare

The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

commit ce2dbbfe3fb9e05d02cab0a6296a570227c13b95
parent 0650341725382046212753b715113b323b10fc23
Author: Drew DeVault <sir@cmpwn.com>
Date:   Sat,  8 Jan 2022 09:39:41 +0100

pathbuf: initial commit

This will be expanded upon.

Signed-off-by: Drew DeVault <sir@cmpwn.com>

Diffstat:
Mpath/+freebsd.ha | 3+++
Mpath/+linux.ha | 3+++
Apathbuf/buffer.ha | 127+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mscripts/gen-stdlib | 7+++++++
Mstdlib.mk | 32++++++++++++++++++++++++++++++++
5 files changed, 172 insertions(+), 0 deletions(-)

diff --git a/path/+freebsd.ha b/path/+freebsd.ha @@ -1,2 +1,5 @@ // Path separator, platform-specific. export def PATHSEP: u8 = '/': u32: u8; + +// Maximum length of a file path for this platform. +export def PATH_MAX: size = 4096; diff --git a/path/+linux.ha b/path/+linux.ha @@ -1,2 +1,5 @@ // Path separator, platform-specific. export def PATHSEP: u8 = '/': u32: u8; + +// Maximum length of a file path for this platform. +export def PATH_MAX: size = 4096; diff --git a/pathbuf/buffer.ha b/pathbuf/buffer.ha @@ -0,0 +1,127 @@ +use bytes; +use errors; +use path; +use strings; + +export type buffer = struct { + buf: [path::PATH_MAX]u8, + cur: []u8, +}; + +// Initializes a new path buffer. +export fn init() buffer = { + let buf = buffer { ... }; + reset(&buf); + return buf; +}; + +// Initializes a caller-allocated path buffer. +// +// let buf = pathbuf::buffer { ... }; +// pathbuf::init_static(&buf); +export fn init_static(buf: *buffer) void = { + reset(buf); +}; + +// Resets a path buffer to its initial state (an empty path). +export fn reset(buf: *buffer) void = { + buf.cur = buf.buf[..0]; +}; + +// Creates a copy of another path buffer, which can be modified without +// affecting the original. +export fn dup(buf: *buffer) buffer = { + let new = buffer { ... }; + new.buf[..] = buf.buf[..]; + new.cur = new.buf[..0]; + return new; +}; + +// Like [[dup]], but the new buffer is allocated by the caller. +export fn dup_static(new: *buffer, old: *buffer) void = { + new.buf[..] = old.buf[..]; + new.cur = old.buf[..0]; +}; + +// Returns the current path stored in this buffer. The path will always be +// normalized, which is to say that it will not include any "." or ".." +// components, or repeated path separators (e.g. "/usr//bin/../bin/./hare" +// becomes "/usr/bin/hare"). +export fn path(buf: *buffer) str = { + return strings::fromutf8(buf.cur); +}; + +// Overwrites the contents of a [[buffer]] with an arbitrary path. +export fn setpath(buf: *buffer, path: str) (void | errors::overflow) = { + const path = strings::toutf8(path); + const tok = bytes::tokenize(path, [path::PATHSEP]); + for (true) { + const next = match (bytes::next_token(&tok)) { + case let tok: []u8 => + yield tok; + case void => + break; + }; + appendnorm(buf, next)?; + }; +}; + +// Normalizes and appends a path component to a buffer. +// +// Invariant: elem must either be equal to [path::PATHSEP], or contain no path +// separators. +fn appendnorm(buf: *buffer, elem: (str | []u8)) (void | errors::overflow) = { + const elem = match (elem) { + case let elem: []u8 => + yield elem; + case let string: str => + yield strings::toutf8(string); + }; + if (len(elem) == 1 && elem[0] == path::PATHSEP) { + if (len(buf.cur) == 0) { + static append(buf.cur, path::PATHSEP); + return; + }; + return; + } else if (bytes::equal(elem, ['.': u8])) { + return; + } else if (bytes::equal(elem, ['.': u8, '.': u8])) { + abort(); // TODO + }; + if (len(buf.cur) + len(elem) + 1 >= path::PATH_MAX) { + return errors::overflow; + }; + if (len(buf.cur) > 1 && buf.cur[len(buf.cur) - 1] != path::PATHSEP) { + static append(buf.cur, path::PATHSEP); + }; + static append(buf.cur, elem...); +}; + +@test fn appendnorm() void = { + let buf = init(); + assert(path(&buf) == ""); + appendnorm(&buf, "foo")!; + appendnorm(&buf, "bar")!; + appendnorm(&buf, "baz")!; + assert(path(&buf) == "foo/bar/baz"); + appendnorm(&buf, ".")!; + appendnorm(&buf, "bad")!; + appendnorm(&buf, ".")!; + assert(path(&buf) == "foo/bar/baz/bad"); + + let buf = init(); + appendnorm(&buf, "/")!; + appendnorm(&buf, "foo")!; + appendnorm(&buf, "bar")!; + appendnorm(&buf, "baz")!; + assert(path(&buf) == "/foo/bar/baz"); + appendnorm(&buf, "/")!; + appendnorm(&buf, "/")!; + assert(path(&buf) == "/foo/bar/baz"); + + let buf = init(); + appendnorm(&buf, "/")!; + appendnorm(&buf, "/")!; + appendnorm(&buf, "/")!; + assert(path(&buf) == "/"); +}; diff --git a/scripts/gen-stdlib b/scripts/gen-stdlib @@ -920,6 +920,12 @@ path() { gen_ssa path strings bufio bytes io } +pathbuf() { + gen_srcs pathbuf \ + buffer.ha + gen_ssa pathbuf path +} + gensrcs_strconv() { gen_srcs strconv \ types.ha \ @@ -1181,6 +1187,7 @@ math::random os linux freebsd os::exec linux freebsd path +pathbuf shlex slice sort diff --git a/stdlib.mk b/stdlib.mk @@ -500,6 +500,12 @@ stdlib_deps_any+=$(stdlib_path_any) stdlib_path_linux=$(stdlib_path_any) stdlib_path_freebsd=$(stdlib_path_any) +# gen_lib pathbuf (any) +stdlib_pathbuf_any=$(HARECACHE)/pathbuf/pathbuf-any.o +stdlib_deps_any+=$(stdlib_pathbuf_any) +stdlib_pathbuf_linux=$(stdlib_pathbuf_any) +stdlib_pathbuf_freebsd=$(stdlib_pathbuf_any) + # gen_lib shlex (any) stdlib_shlex_any=$(HARECACHE)/shlex/shlex-any.o stdlib_deps_any+=$(stdlib_shlex_any) @@ -1487,6 +1493,16 @@ $(HARECACHE)/path/path-any.ssa: $(stdlib_path_any_srcs) $(stdlib_rt) $(stdlib_st @HARECACHE=$(HARECACHE) $(HAREC) $(HAREFLAGS) -o $@ -Npath \ -t$(HARECACHE)/path/path.td $(stdlib_path_any_srcs) +# pathbuf (+any) +stdlib_pathbuf_any_srcs= \ + $(STDLIB)/pathbuf/buffer.ha + +$(HARECACHE)/pathbuf/pathbuf-any.ssa: $(stdlib_pathbuf_any_srcs) $(stdlib_rt) $(stdlib_path_$(PLATFORM)) + @printf 'HAREC \t$@\n' + @mkdir -p $(HARECACHE)/pathbuf + @HARECACHE=$(HARECACHE) $(HAREC) $(HAREFLAGS) -o $@ -Npathbuf \ + -t$(HARECACHE)/pathbuf/pathbuf.td $(stdlib_pathbuf_any_srcs) + # shlex (+any) stdlib_shlex_any_srcs= \ $(STDLIB)/shlex/split.ha @@ -2251,6 +2267,12 @@ testlib_deps_any+=$(testlib_path_any) testlib_path_linux=$(testlib_path_any) testlib_path_freebsd=$(testlib_path_any) +# gen_lib pathbuf (any) +testlib_pathbuf_any=$(TESTCACHE)/pathbuf/pathbuf-any.o +testlib_deps_any+=$(testlib_pathbuf_any) +testlib_pathbuf_linux=$(testlib_pathbuf_any) +testlib_pathbuf_freebsd=$(testlib_pathbuf_any) + # gen_lib shlex (any) testlib_shlex_any=$(TESTCACHE)/shlex/shlex-any.o testlib_deps_any+=$(testlib_shlex_any) @@ -3274,6 +3296,16 @@ $(TESTCACHE)/path/path-any.ssa: $(testlib_path_any_srcs) $(testlib_rt) $(testlib @HARECACHE=$(TESTCACHE) $(HAREC) $(TESTHAREFLAGS) -o $@ -Npath \ -t$(TESTCACHE)/path/path.td $(testlib_path_any_srcs) +# pathbuf (+any) +testlib_pathbuf_any_srcs= \ + $(STDLIB)/pathbuf/buffer.ha + +$(TESTCACHE)/pathbuf/pathbuf-any.ssa: $(testlib_pathbuf_any_srcs) $(testlib_rt) $(testlib_path_$(PLATFORM)) + @printf 'HAREC \t$@\n' + @mkdir -p $(TESTCACHE)/pathbuf + @HARECACHE=$(TESTCACHE) $(HAREC) $(TESTHAREFLAGS) -o $@ -Npathbuf \ + -t$(TESTCACHE)/pathbuf/pathbuf.td $(testlib_pathbuf_any_srcs) + # shlex (+any) testlib_shlex_any_srcs= \ $(STDLIB)/shlex/split.ha \