iterator, formatting, memory, & testing stuff
This commit is contained in:
parent
93cefa516f
commit
cbc28723a2
63
build
63
build
|
@ -4,18 +4,19 @@ set -u
|
|||
# shellcheck disable=SC2155
|
||||
readonly LILY_SCRIPT_DIR="$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd)"
|
||||
readonly LILY_SRC_DIR="${LILY_SRC_DIR:-$LILY_SCRIPT_DIR/src}"
|
||||
readonly LILY_TEST_DIR="${LILY_TEST_DIR:-$LILY_SCRIPT_DIR/test}"
|
||||
readonly LILY_TEST_DIR="${LILY_TEST_DIR:-$LILY_SCRIPT_DIR/hbc-tests}"
|
||||
readonly LILY_BUILD_DIR="${LILY_BUILD_DIR:-$LILY_SCRIPT_DIR/out}"
|
||||
|
||||
readonly HBLANG_GIT="https://git.ablecorp.us/mlokis/hblang"
|
||||
readonly HBLANG_COMMIT="78bb8ef107d6fa9944632eee98863b4d10023396"
|
||||
readonly HBLANG_COMMIT="ee74cf6b3a7bf5b0c149147c737ab685d4dc215e"
|
||||
readonly HBC_FLAGS="--path-projection lily $LILY_SRC_DIR/lib.hb ${HBC_FLAGS:-}"
|
||||
readonly HBC_BINARY="hbc"
|
||||
readonly HBC_TESTS="${HBC_TESTS:-0}"
|
||||
|
||||
die() { error "$2" && exit "$1"; }
|
||||
error() { printf "\033[31mERROR\033[0m: %s\n" "$1" 1>&2; }
|
||||
log() { printf "\033[32mINFO\033[0m: %s\n" "$1" 1>&2; }
|
||||
warn() { printf "\033[33mWARN\033[0m: %s\n" "$1" 1>&2; }
|
||||
die() { error "$2" 1>&2 && exit "$1"; }
|
||||
error() { printf "\033[31mERROR\033[0m: %b\n" "$1"; }
|
||||
log() { printf "\033[32mINFO\033[0m: %b\n" "$1"; }
|
||||
warn() { printf "\033[33mWARN\033[0m: %b\n" "$1"; }
|
||||
|
||||
fetch_step() {
|
||||
command -v zig >/dev/null 2>&1 || die 101 "'zig' binary not found in PATH"
|
||||
|
@ -46,7 +47,8 @@ fetch_step() {
|
|||
git checkout -f "$HBLANG_COMMIT" >/dev/null 2>&1 || die 1 "failed to checkout commit"
|
||||
fi
|
||||
|
||||
zig build install >/dev/null 2>&1 || die 1 "failed to build hblang"
|
||||
# do not build with optimisations if you want to report bugs.
|
||||
zig build install >/dev/null || die $? "failed to build hblang. exit code=$?"
|
||||
|
||||
PATH="$hblang_dir/zig-out/bin:$PATH"
|
||||
cd "$LILY_SCRIPT_DIR" || die 1 "failed to return to script directory"
|
||||
|
@ -56,11 +58,56 @@ main() {
|
|||
mkdir -p "$LILY_BUILD_DIR" || die 1 "failed to create build directory"
|
||||
fetch_step
|
||||
|
||||
do_test() {
|
||||
# shellcheck disable=SC2086
|
||||
$HBC_BINARY $HBC_FLAGS --vendored-test "$file" >"$LILY_BUILD_DIR/$test_name.test" 2>&1
|
||||
exit="$?"
|
||||
if [ ! $exit = 0 ]; then
|
||||
error "\e[1;31m⨯\033[0m $test_name \e[1;30m(exit=\e[1;31m$exit\e[1;30m) ($(basename "$LILY_BUILD_DIR")/$test_name.test)"
|
||||
echo "0" >>"$result_file"
|
||||
else
|
||||
# shellcheck disable=SC2086
|
||||
len=$($HBC_BINARY $HBC_FLAGS "$file" | wc -c | numfmt --to=iec-i)
|
||||
log "✓ $test_name \e[1;30m(size=\e[1;32m$len\e[1;30m)"
|
||||
# log "\e[0;32m✓\033[0m $test_name"
|
||||
echo "1" >>"$result_file"
|
||||
# surely good idea.
|
||||
rm "$LILY_BUILD_DIR/$test_name.test"
|
||||
# shellcheck disable=SC2086
|
||||
$HBC_BINARY $HBC_FLAGS "$file" --fmt >/dev/null 2>&1
|
||||
fi
|
||||
}
|
||||
|
||||
if [ "$HBC_TESTS" = 1 ]; then
|
||||
tmpfile=$(mktemp)
|
||||
result_file=$(mktemp)
|
||||
find "$LILY_TEST_DIR" -type f >"$tmpfile"
|
||||
while IFS= read -r file; do
|
||||
# test_name=$(basename "$file" .hb)
|
||||
test_name=$(echo "$(basename -- "$(dirname -- "$file")")/$(basename -- "$file" .hb)" | tr '/' '.')
|
||||
do_test 2>/dev/null &
|
||||
done <"$tmpfile"
|
||||
rm -f "$tmpfile"
|
||||
wait
|
||||
|
||||
failures=$(grep -c "0" "$result_file")
|
||||
successes=$(grep -c "1" "$result_file")
|
||||
|
||||
rm -f "$result_file"
|
||||
|
||||
if [ "$failures" = 0 ]; then
|
||||
log "$successes/$((successes + failures)) tests passed"
|
||||
else
|
||||
warn "\e[0;31m$successes\033[0m/$((successes + failures)) tests passed"
|
||||
fi
|
||||
exit
|
||||
fi
|
||||
|
||||
inp="${1:-main.hb}"
|
||||
[ ! -e "$inp" ] && die 1 "source file '$inp' does not exist"
|
||||
|
||||
# shellcheck disable=SC2086
|
||||
if $HBC_BINARY $HBC_FLAGS "$inp"; then
|
||||
if $HBC_BINARY $HBC_FLAGS "$inp" >"$LILY_BUILD_DIR/out.axe"; then
|
||||
$HBC_BINARY $HBC_FLAGS "$inp" --fmt >/dev/null 2>&1
|
||||
else
|
||||
exit $?
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
expectations := .{
|
||||
return_value: 0,
|
||||
return_value: 0,
|
||||
}
|
||||
|
||||
A := struct {
|
||||
apply := fn(self: ^@CurrentScope(), $func: type): uint {
|
||||
return func()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
main := fn(): uint {
|
||||
return A.().apply(fn(): uint {
|
||||
return 0
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
expectations := .{
|
||||
return_value: 0,
|
||||
return_value: 0,
|
||||
}
|
||||
|
||||
generic := fn(v: @Any()): uint {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
expectations := .{
|
||||
return_value: 0,
|
||||
return_value: 0,
|
||||
}
|
||||
|
||||
index := fn(buf: @Any()): u8 {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
expectations := .{
|
||||
return_value: 0,
|
||||
return_value: 0,
|
||||
}
|
||||
|
||||
opaque := fn(v: @Any()): bool {
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
expectations := .{
|
||||
return_value: 0,
|
||||
return_value: 0,
|
||||
}
|
||||
|
||||
main := fn(): uint {
|
||||
ptr0: ^u8 = @bit_cast(0)
|
||||
ptr1 := ptr0 + 100
|
||||
if ptr1 != @bit_cast(100) return 1
|
||||
return 0
|
||||
}
|
||||
ptr0: ^u8 = @bit_cast(0)
|
||||
ptr1 := ptr0 + 100
|
||||
if ptr1 != @bit_cast(100) return 1
|
||||
return 0
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
expectations := .{
|
||||
return_value: 0,
|
||||
return_value: 0,
|
||||
}
|
||||
|
||||
Vec := struct {
|
||||
|
|
|
@ -12,8 +12,6 @@ Struct := struct {
|
|||
|
||||
main := fn(): uint {
|
||||
a := Struct.(null)
|
||||
// ! (compiler) bug: adding type here makes
|
||||
// ! self get passed by value and not by ref
|
||||
a.modify(void)
|
||||
if a.inner == null return 1
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
expectations := .{
|
||||
return_value: 0,
|
||||
return_value: 0,
|
||||
}
|
||||
|
||||
opaque := fn(): ?^u8 {
|
||||
|
@ -20,4 +20,4 @@ main := fn(): u8 {
|
|||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
expectations := .{
|
||||
return_value: 0,
|
||||
return_value: 0,
|
||||
}
|
||||
|
||||
main := fn(): u8 {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
expectations := .{
|
||||
return_value: 0,
|
||||
return_value: 0,
|
||||
}
|
||||
|
||||
TypeOf := fn(v: @Any()): type return Generic(@TypeOf(v))
|
||||
Generic := fn($T: type): type return struct { .inner: T }
|
||||
Generic := fn($T: type): type return struct{.inner: T}
|
||||
|
||||
main := fn(): uint {
|
||||
$if TypeOf(@as(uint, 1)) != Generic(uint) {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
$if TypeOf(@as(uint, 1)) != Generic(uint) {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
expectations := .{
|
||||
return_value: 0,
|
||||
return_value: 0,
|
||||
}
|
||||
|
||||
sum_days := uint.[0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365]
|
||||
|
@ -7,7 +7,7 @@ sum_days := uint.[0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365]
|
|||
unix_timestamp_secs_lookup_table := fn(year: uint, month: uint, day: uint, hour: uint, minute: uint, second: uint): uint {
|
||||
is_leap := year % 4 == 0 & (year % 100 != 0 | year % 400 == 0)
|
||||
days_since_epoch := year * 365 + (year - 1) / 4 - (year - 1) / 100 + (year - 1) / 400 - 719527
|
||||
total_days := days_since_epoch + day + sum_days[month - 1] + is_leap * (month > 2) - 1;
|
||||
total_days := days_since_epoch + day + sum_days[month - 1] + is_leap * (month > 2) - 1
|
||||
return total_days * 86400 + hour * 3600 + minute * 60 + second
|
||||
}
|
||||
|
||||
|
@ -34,4 +34,4 @@ main := fn(): bool {
|
|||
r |= unix_timestamp_secs_lookup_table(2025, 1, 31, 21, 53, 26) != unix_timestamp_secs(2025, 1, 31, 21, 53, 26)
|
||||
r |= @inline(unix_timestamp_secs_lookup_table, 2025, 1, 31, 21, 53, 26) != unix_timestamp_secs(2025, 1, 31, 21, 53, 26)
|
||||
return r
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
expectations := .{
|
||||
return_value: 5,
|
||||
ecalls: .(
|
||||
.(3, 2): 1,
|
||||
),
|
||||
ecalls: .(
|
||||
.(3, 2): 1,
|
||||
),
|
||||
}
|
||||
|
||||
lily.{fmt, log, mem, alloc, target} := @use("../../src/lib.hb")
|
||||
lily.{mem, alloc} := @use("../../src/lib.hb")
|
||||
|
||||
main := fn(): uint {
|
||||
arena := alloc.Arena.new()
|
||||
|
@ -13,7 +13,7 @@ main := fn(): uint {
|
|||
|
||||
_ = arena.alloc(u8, 1).?
|
||||
|
||||
iter := mem.bytes(mem.reverse("Hello, World!")[1..]).take(5)
|
||||
iter := mem.iter(mem.reverse("Hello, World!")[1..]).take(5)
|
||||
str := iter.collect_vec(&arena)
|
||||
return str.len()
|
||||
}
|
||||
|
|
18
hbc-tests/lily/fmt.hb
Normal file
18
hbc-tests/lily/fmt.hb
Normal file
|
@ -0,0 +1,18 @@
|
|||
expectations := .{
|
||||
return_value: 0,
|
||||
}
|
||||
|
||||
lily.{fmt} := @use("../../src/lib.hb")
|
||||
|
||||
scratch: [4096]u8 = idk
|
||||
|
||||
main := fn(): uint {
|
||||
len := fmt.fmt_int(scratch[..], 4096, 10)
|
||||
if len != 4 return 1
|
||||
if scratch[0] != '4' | scratch[1] != '0' | scratch[2] != '9' | scratch[3] != '6' return 1
|
||||
|
||||
len = fmt.fmt_int(scratch[..], 5050, 16)
|
||||
if len != 6 return 1
|
||||
if scratch[0] != '0' | scratch[1] != 'x' | scratch[2] != '1' | scratch[3] != '3' | scratch[4] != 'B' | scratch[5] != 'A' return 1
|
||||
return 0
|
||||
}
|
14
hbc-tests/lily/mem-equals.hb
Normal file
14
hbc-tests/lily/mem-equals.hb
Normal file
|
@ -0,0 +1,14 @@
|
|||
expectations := .{
|
||||
return_value: 0,
|
||||
}
|
||||
|
||||
lily.{mem} := @use("../../src/lib.hb")
|
||||
|
||||
main := fn(): uint {
|
||||
abc := "abc"
|
||||
a_b_c := u8.['a', 'b', 'c'][..]
|
||||
if !mem.equals(abc, abc) return 1
|
||||
if !mem.equals(a_b_c, abc) return 1
|
||||
|
||||
return 0
|
||||
}
|
6
main.hb
6
main.hb
|
@ -1,14 +1,10 @@
|
|||
lily.{fmt, log, mem, alloc, target} := @use("lily")
|
||||
|
||||
b: []u8 = &.[]
|
||||
|
||||
main := fn(): void {
|
||||
arena := alloc.Arena.new()
|
||||
defer arena.deinit()
|
||||
|
||||
b = arena.alloc(u8, 1).?
|
||||
|
||||
iter := mem.bytes(mem.reverse("Hello, World!")[1..]).take(5)
|
||||
iter := mem.iter(mem.reverse("Hello, World!")[1..]).take(5)
|
||||
str := iter.collect_vec(&arena)
|
||||
log.info(str.slice)
|
||||
}
|
||||
|
|
|
@ -42,7 +42,6 @@ Arena := struct {
|
|||
}
|
||||
|
||||
loop {
|
||||
// lily.log.debug("arena.hb:45: if i dont print this it crashes")
|
||||
if header.len + size <= header.cap {
|
||||
header.len += size
|
||||
break
|
||||
|
@ -59,21 +58,20 @@ Arena := struct {
|
|||
$alloc_zeroed := fn(self: ^Self, $T: type, count: uint): ?[]T {
|
||||
slice := self.alloc(T, count)
|
||||
if slice == null return null
|
||||
mem.set(slice.?.ptr, 0, slice.?.len)
|
||||
mem.set(mem.as_bytes(slice.?), 0)
|
||||
return slice
|
||||
}
|
||||
$realloc := fn(self: ^Self, $T: type, prev: []T, count_new: uint): ?[]T {
|
||||
slice := self.alloc(T, mem.size(T, count_new))
|
||||
slice := self.alloc(T, count_new)
|
||||
if slice == null return null
|
||||
mem.copy(@bit_cast(slice.?.ptr), @bit_cast(prev.ptr), mem.size(T, prev.len))
|
||||
mem.copy(mem.as_bytes(slice.?), mem.as_bytes(prev))
|
||||
return slice
|
||||
}
|
||||
$dealloc := fn(self: ^Self, $T: type, prev: []T): void {
|
||||
}
|
||||
deinit := fn(self: ^Self): void {
|
||||
if self.allocation == null {
|
||||
// lily.log.error("fixme: double free arena. can't fix due to compiler.")
|
||||
die
|
||||
return
|
||||
}
|
||||
allocation: ^AllocationHeader = @bit_cast(self.allocation)
|
||||
loop {
|
||||
|
|
36
src/fmt.hb
36
src/fmt.hb
|
@ -2,31 +2,29 @@
|
|||
|
||||
fmt_int := fn(buf: []u8, v: @Any(), radix: @TypeOf(v)): uint {
|
||||
if radix == 0 {
|
||||
mem.copy(buf.ptr, @bit_cast(&v), @size_of(@TypeOf(v)))
|
||||
mem.copy(buf, mem.as_bytes(&v))
|
||||
return @size_of(@TypeOf(v))
|
||||
}
|
||||
|
||||
prefix_len := 0
|
||||
if Type(@TypeOf(v)).is_signed_int() & v < 0 {
|
||||
v = -v
|
||||
// 0x2D == '-'
|
||||
buf[0] = 0x2D
|
||||
buf[0] = '-'
|
||||
prefix_len += 1
|
||||
}
|
||||
if radix == 16 {
|
||||
mem.copy(buf.ptr + prefix_len, "0x".ptr, 2)
|
||||
mem.copy(buf[prefix_len..], "0x")
|
||||
prefix_len += 2
|
||||
} else if radix == 8 {
|
||||
mem.copy(buf.ptr + prefix_len, "0o".ptr, 2)
|
||||
mem.copy(buf[prefix_len..], "0o")
|
||||
prefix_len += 2
|
||||
} else if radix == 2 {
|
||||
mem.copy(buf.ptr + prefix_len, "0b".ptr, 2)
|
||||
mem.copy(buf[prefix_len..], "0b")
|
||||
prefix_len += 2
|
||||
}
|
||||
|
||||
if v == 0 {
|
||||
// 0x30 == '0'
|
||||
buf[prefix_len] = 0x30
|
||||
buf[prefix_len] = '0'
|
||||
return prefix_len + 1
|
||||
}
|
||||
|
||||
|
@ -35,11 +33,9 @@ fmt_int := fn(buf: []u8, v: @Any(), radix: @TypeOf(v)): uint {
|
|||
remainder := v % radix
|
||||
v /= radix
|
||||
if remainder > 9 {
|
||||
// 0x41 == 'A'
|
||||
buf[i] = @int_cast(remainder - 10 + 0x41)
|
||||
buf[i] = @int_cast(remainder - 10 + 'A')
|
||||
} else {
|
||||
// 0x30 == '0'
|
||||
buf[i] = @int_cast(remainder + 0x30)
|
||||
buf[i] = @int_cast(remainder + '0')
|
||||
}
|
||||
i += 1
|
||||
}
|
||||
|
@ -49,31 +45,29 @@ fmt_int := fn(buf: []u8, v: @Any(), radix: @TypeOf(v)): uint {
|
|||
|
||||
fmt_bool := fn(buf: []u8, v: bool): uint {
|
||||
if v {
|
||||
mem.copy(buf.ptr, "true".ptr, 4)
|
||||
mem.copy(buf, "true")
|
||||
return 4
|
||||
} else {
|
||||
mem.copy(buf.ptr, "false".ptr, 5)
|
||||
mem.copy(buf, "false")
|
||||
return 5
|
||||
}
|
||||
}
|
||||
|
||||
fmt_optional := fn(buf: []u8, v: @Any()): uint {
|
||||
if v != null return format(buf, @as(@ChildOf(@TypeOf(v)), v.?))
|
||||
|
||||
mem.copy(buf.ptr, @name_of(@TypeOf(v)).ptr, @name_of(@TypeOf(v)).len)
|
||||
mem.copy(buf.ptr + @name_of(@TypeOf(v)).len, ".null".ptr, 5)
|
||||
return @name_of(@TypeOf(v)).len + 5
|
||||
mem.copy(buf, "null")
|
||||
return 4
|
||||
}
|
||||
|
||||
// todo: cleanup
|
||||
fmt_enum := fn(buf: []u8, v: @Any()): uint {
|
||||
T := @TypeOf(v)
|
||||
len := @name_of(T).len
|
||||
mem.copy(buf.ptr, @name_of(T).ptr, len)
|
||||
mem.copy(buf.ptr + len, ".(".ptr, 2)
|
||||
mem.copy(buf, @name_of(T))
|
||||
mem.copy(buf[len..], ".(")
|
||||
len += 2
|
||||
len += fmt_int(buf[len..], @as(Type(T).USize(), @bit_cast(v)), 10)
|
||||
mem.copy(buf.ptr + len, ")".ptr, 1)
|
||||
mem.copy(buf[len..], ")")
|
||||
return len + 1
|
||||
}
|
||||
|
||||
|
|
73
src/iter.hb
73
src/iter.hb
|
@ -27,6 +27,12 @@ Iterator := fn(T: type): type return struct {
|
|||
$take := fn(self: ^Self, end: uint): Iterator(Take(T)) {
|
||||
return .(.(self, 0, end))
|
||||
}
|
||||
$skip := fn(self: ^Self, n: uint): Iterator(Skip(T)) {
|
||||
return .(.(self, n))
|
||||
}
|
||||
$chain := fn(self: ^Self, rhs: @Any()): Iterator(Chain(T, @TypeOf(rhs))) {
|
||||
return .(.(self, rhs, .Iter0))
|
||||
}
|
||||
for_each := fn(self: ^Self, $func: type): void {
|
||||
loop {
|
||||
x := self.next()
|
||||
|
@ -34,6 +40,23 @@ Iterator := fn(T: type): type return struct {
|
|||
_ = func(x.val)
|
||||
}
|
||||
}
|
||||
fold := fn(self: ^Self, $func: type, sum: @Any()): @TypeOf(sum) {
|
||||
loop {
|
||||
x := self.next()
|
||||
if x.finished return sum
|
||||
sum = func(sum, x.val)
|
||||
}
|
||||
}
|
||||
nth := fn(self: ^Self, n: uint): ?IterVal {
|
||||
i := 0
|
||||
loop {
|
||||
defer i += 1
|
||||
x := self.next()
|
||||
if x.finished return null else {
|
||||
if i == n return x.val
|
||||
}
|
||||
}
|
||||
}
|
||||
collect := fn(self: ^Self, $A: type): ?A {
|
||||
$if Type(A).kind() != .Array {
|
||||
@error("collecting", Self, "into type", A, "unsupported for now")
|
||||
|
@ -97,3 +120,53 @@ Take := fn($T: type): type return struct {
|
|||
return self.iter.inner.next()
|
||||
}
|
||||
}
|
||||
|
||||
Skip := fn($T: type): type return struct {
|
||||
.iter: ^Iterator(T);
|
||||
.step: uint
|
||||
IterNext := @TypeOf(T.next(idk).val)
|
||||
|
||||
$next := fn(self: ^@CurrentScope()): Next(IterNext) {
|
||||
n := 0
|
||||
loop {
|
||||
x := self.iter.next()
|
||||
if n == self.step | x.finished return x
|
||||
n += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Chain := fn($A: type, $B: type): type {
|
||||
Iter0Next := @TypeOf(A.next(idk).val)
|
||||
Iter1Next := @TypeOf(B.next(idk).val)
|
||||
|
||||
$if Iter0Next != Iter1Next @error(Iter0Next, " != ", Iter1Next)
|
||||
|
||||
return struct {
|
||||
.iter0: ^Iterator(A)
|
||||
/* todo: ^B? */;
|
||||
.iter1: B;
|
||||
.state: enum{.Iter0; .Iter0Finished; .BothFinished}
|
||||
|
||||
next := fn(self: ^@CurrentScope()): Next(Iter0Next) {
|
||||
// todo: replace with Type(T).uninit()
|
||||
x: Next(Iter0Next) = idk
|
||||
match self.state {
|
||||
.Iter0 => {
|
||||
x = self.iter0.inner.next()
|
||||
if x.finished {
|
||||
self.state = .Iter0Finished
|
||||
return self.next()
|
||||
}
|
||||
},
|
||||
.Iter0Finished => {
|
||||
x = self.iter1.inner.next()
|
||||
if x.finished self.state = .BothFinished
|
||||
},
|
||||
_ => {
|
||||
},
|
||||
}
|
||||
return .(self.state == .BothFinished, x.val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
12
src/lib.hb
12
src/lib.hb
|
@ -6,6 +6,18 @@ mem := @use("mem.hb")
|
|||
log := @use("log.hb")
|
||||
fmt := @use("fmt.hb")
|
||||
|
||||
config := struct {
|
||||
$DEBUG := true
|
||||
$MIN_LOGLEVEL := log.LogLevel.Info
|
||||
// sufficent for now.
|
||||
$FMT_BUFFER_SIZE := 256
|
||||
|
||||
$min_loglevel := fn(): log.LogLevel {
|
||||
$if config.DEBUG & config.MIN_LOGLEVEL < .Debug return .Debug
|
||||
return config.MIN_LOGLEVEL
|
||||
}
|
||||
}
|
||||
|
||||
Version := struct {
|
||||
.major: uint;
|
||||
.minor: uint;
|
||||
|
|
18
src/log.hb
18
src/log.hb
|
@ -1,4 +1,4 @@
|
|||
.{target} := @use("lib.hb")
|
||||
.{target, config, fmt} := @use("lib.hb")
|
||||
|
||||
LogLevel := enum {
|
||||
.Error;
|
||||
|
@ -9,6 +9,9 @@ LogLevel := enum {
|
|||
}
|
||||
|
||||
$log := fn(level: LogLevel, str: []u8): void {
|
||||
if level > config.min_loglevel() {
|
||||
return
|
||||
}
|
||||
$match target.current() {
|
||||
.AbleOS => return @ecall(3, 1, target.LogEcall.(level, str.ptr, str.len), @size_of(target.LogEcall)),
|
||||
}
|
||||
|
@ -19,3 +22,16 @@ $warn := fn(message: []u8): void return log(.Warn, message)
|
|||
$info := fn(message: []u8): void return log(.Info, message)
|
||||
$debug := fn(message: []u8): void return log(.Debug, message)
|
||||
$trace := fn(message: []u8): void return log(.Trace, message)
|
||||
|
||||
fmt_buffer: [config.FMT_BUFFER_SIZE]u8 = idk
|
||||
|
||||
print := fn(any: @Any()): void {
|
||||
$if @TypeOf(any) == []u8 {
|
||||
$match target.current() {
|
||||
.AbleOS => info(any),
|
||||
}
|
||||
} else {
|
||||
len := fmt.format(fmt_buffer[..], any)
|
||||
info(fmt_buffer[..len])
|
||||
}
|
||||
}
|
||||
|
|
51
src/mem.hb
51
src/mem.hb
|
@ -1,10 +1,9 @@
|
|||
.{target, iter: .{Iterator, Next}} := @use("lib.hb")
|
||||
.{target, iter: .{Iterator, Next}, config, log, Type} := @use("lib.hb")
|
||||
|
||||
$size := fn($T: type, count: uint): uint {
|
||||
return @size_of(T) * count
|
||||
}
|
||||
|
||||
// ! (compiler) bug: parameter named 'align' causes panic.
|
||||
/// safety: assumes align != 0
|
||||
$forward_align := fn(ptr: ^u8, _align: uint): ^u8 {
|
||||
return @bit_cast((@bit_cast(ptr) + _align - 1) / _align * _align)
|
||||
|
@ -15,16 +14,50 @@ $backward_align := fn(ptr: ^u8, _align: uint): ^u8 {
|
|||
return @bit_cast(@bit_cast(ptr) / _align * _align)
|
||||
}
|
||||
|
||||
$copy := fn(dest: ^u8, src: ^u8, len: uint): void {
|
||||
target.memcopy(dest, src, len)
|
||||
$is_aligned := fn(ptr: ^u8, _align: uint): bool {
|
||||
return @bit_cast(ptr) % _align == 0
|
||||
}
|
||||
|
||||
$move := fn(dest: ^u8, src: ^u8, len: uint): void {
|
||||
target.memmove(dest, src, len)
|
||||
$dangling := fn($T: type): ^T {
|
||||
$if Type(T).kind() == .Optional @error(T, " is an optional pointer. use `null` instead.")
|
||||
return @bit_cast(@align_of(T))
|
||||
}
|
||||
|
||||
$set := fn(dest: ^u8, src: u8, len: uint): void {
|
||||
target.memset(dest, src, len)
|
||||
$as_bytes := fn(v: @Any()): []u8 {
|
||||
$match Type(@TypeOf(v)).kind() {
|
||||
.Pointer => return @as(^u8, @bit_cast(v))[..@size_of(@ChildOf(@TypeOf(v)))],
|
||||
.Slice => return @as(^u8, @bit_cast(v.ptr))[..size(
|
||||
@ChildOf(@TypeOf(v)),
|
||||
v.len,
|
||||
)],
|
||||
_ => @error(@TypeOf(v), " is not a pointer or a slice."),
|
||||
}
|
||||
}
|
||||
|
||||
$overlaps := fn(lhs: []u8, rhs: []u8): bool {
|
||||
return lhs.ptr < rhs.ptr + rhs.len & rhs.ptr < lhs.ptr + lhs.len
|
||||
}
|
||||
|
||||
$copy := fn(dest: []u8, src: []u8): void {
|
||||
$if config.DEBUG {
|
||||
if src.len > dest.len | overlaps(dest, src) {
|
||||
log.error("mem.copy: regions overlap or src bigger than dest")
|
||||
die
|
||||
}
|
||||
}
|
||||
target.memcopy(dest.ptr, src.ptr, src.len)
|
||||
}
|
||||
|
||||
$move := fn(dest: []u8, src: []u8): void {
|
||||
$if config.DEBUG {
|
||||
log.error("mem.move: src bigger than dest")
|
||||
if src.len > dest.len die
|
||||
}
|
||||
target.memmove(dest.ptr, src.ptr, src.len)
|
||||
}
|
||||
|
||||
$set := fn(dest: []u8, src: u8): void {
|
||||
target.memset(dest.ptr, src, dest.len)
|
||||
}
|
||||
|
||||
equals := fn(lhs: []u8, rhs: []u8): bool {
|
||||
|
@ -52,7 +85,7 @@ reverse := fn(slice: []u8): []u8 {
|
|||
} else return slice
|
||||
}
|
||||
|
||||
$bytes := fn(slice: []u8): Iterator(struct {
|
||||
$iter := fn(slice: []u8): Iterator(struct {
|
||||
.slice: []u8
|
||||
|
||||
$next := fn(self: ^@CurrentScope()): Next(u8) {
|
||||
|
|
Loading…
Reference in a new issue