lots of work
improve build script reorganise stuff fix some bugs improve Type struct start working on string, fmt, rand null terminator no longer required on strings better document bugs minor opts
This commit is contained in:
parent
2f74ac8cc0
commit
fb67c9b382
24
README.md
24
README.md
|
@ -5,19 +5,33 @@ use `./build -h` to see available arguments. supports:
|
|||
- custom linker (for `c_native`)
|
||||
- running the executable (for `c_native`)
|
||||
- setting output path
|
||||
- only recompiling if either source or environment change
|
||||
|
||||
> [!IMPORTANT]
|
||||
> all features, targets, etc, are provisional and subject to change
|
||||
|
||||
### To change build target
|
||||
create the file `src/lib/target/target.hb` with the contents `target := @use("c_native.hb")` (example)<br>
|
||||
manually:
|
||||
> create the file `src/lib/target/target.hb` with the contents `target := @use("c_native.hb")` (example)
|
||||
|
||||
automatically:
|
||||
> use the `-t` flag supplied in `./build`
|
||||
|
||||
currently available targets:
|
||||
- `c_native`
|
||||
- `hbvm_ableos`
|
||||
|
||||
alternatively, use `./build -t c_native` (example)
|
||||
|
||||
## Currently "working" features
|
||||
- `std.vec.Vec`
|
||||
- `std.alloc.{SimpleAllocator, RawAllocator}`
|
||||
- `std.{Type, Kind, exit}`
|
||||
### Currently "working" features
|
||||
- `lily.{Type, TypeOf, Kind, exit, panic, memcpy, memmove, memset}`
|
||||
- `lily.log.{log, info, error, warn, debug, trace}`
|
||||
- `lily.result.Result`
|
||||
|
||||
### Currently "in progress" features
|
||||
- `lily.rand.SimpleRandom`
|
||||
- `lily.log.{print, printf}`
|
||||
|
||||
### Currently "broken due to compiler" features
|
||||
- `lily.collections.SparseVec`
|
||||
- `lily.alloc.{SimpleAllocator, RawAllocator}`
|
||||
|
|
308
build
308
build
|
@ -1,197 +1,167 @@
|
|||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
build_dir=$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd)
|
||||
check_for_update=0
|
||||
SCRIPT_DIR="$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd)"
|
||||
readonly SCRIPT_DIR
|
||||
readonly HBC_COMMIT="18e8a831"
|
||||
readonly SRC_DIR="$SCRIPT_DIR/src"
|
||||
readonly DEFAULT_OUT="$SCRIPT_DIR/target/lily"
|
||||
readonly BUILD_PATH="$SCRIPT_DIR/target/build"
|
||||
readonly CHECKSUM_FILE="$BUILD_PATH/checksum"
|
||||
readonly TARGET_FILE="$SRC_DIR/lib/target/target.hb"
|
||||
|
||||
linker=""
|
||||
target="c_native"
|
||||
run=0
|
||||
out="$build_dir/target/lily"
|
||||
out="$DEFAULT_OUT"
|
||||
|
||||
make_build_dir() {
|
||||
if ! [ -e "$build_dir/target/build" ]; then
|
||||
mkdir -p "$build_dir/target/build"
|
||||
elif ! [ -w "$build_dir/target/build" ]; then
|
||||
echo "$build_dir/target/build is not writable." >&2
|
||||
exit 1
|
||||
fi
|
||||
die() {
|
||||
printf "ERROR: %s\n" "$1" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
log() {
|
||||
printf "INFO: %s\n" "$1" >&2
|
||||
}
|
||||
|
||||
check_hbc() {
|
||||
has_hbc=1
|
||||
if ! command -v hbc >/dev/null 2>&1; then
|
||||
printf "Warning: hblang compiler not found in \$PATH\n"
|
||||
has_hbc=0
|
||||
fi
|
||||
if command -v cargo >/dev/null 2>&1; then
|
||||
cargo_output=$(cargo install --list | grep 'holey-bytes#' | awk -F'#' '{print substr($2, 1, 8)}')
|
||||
command -v hbc >/dev/null 2>&1 || {
|
||||
log "hblang compiler not found in \$PATH"
|
||||
command -v cargo >/dev/null 2>&1 || die "hblang compiler is missing, and could not use cargo to install"
|
||||
}
|
||||
|
||||
if [ -z "$cargo_output" ] && [ $has_hbc = 0 ]; then
|
||||
printf "Note: Installing hblang compiler using cargo\n"
|
||||
if ! cargo +nightly install --git https://git.ablecorp.us/ableos/holey-bytes hbc >/dev/null 2>&1; then
|
||||
printf "Failed to install hblang compiler for whatever reason\n"
|
||||
exit 1
|
||||
fi
|
||||
printf "Done installing\n"
|
||||
return
|
||||
fi
|
||||
if [ $check_for_update = 1 ]; then
|
||||
short_commit_hash=""
|
||||
response=$(curl -s "https://git.ablecorp.us/api/v1/repos/ableos/holey-bytes/commits?limit=1")
|
||||
if [ -n "$response" ]; then
|
||||
commit_hash=$(printf '%s' "$response" | jq -r '.[0].sha' 2>/dev/null)
|
||||
if [ -n "$commit_hash" ] && [ ! "$commit_hash" = "null" ]; then
|
||||
short_commit_hash="$(printf '%s' "$commit_hash" | cut -c 1-8)"
|
||||
fi
|
||||
fi
|
||||
if [ -n "$short_commit_hash" ]; then
|
||||
if [ ! "$short_commit_hash" = "$cargo_output" ] && [ $has_hbc = 1 ]; then
|
||||
printf "hblang compiler is out of date"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
elif [ $has_hbc = 0 ]; then
|
||||
printf "Error: hblang compiler is missing, and could not use cargo to install hblang compiler."
|
||||
exit 1
|
||||
fi
|
||||
command -v cargo >/dev/null 2>&1 && {
|
||||
cargo_ver=$(cargo install --list | awk '/holey-bytes/ {match($0, /[0-9a-f]{8}/); if (RSTART) print substr($0, RSTART, 8)}')
|
||||
[ "${cargo_ver:-}" = "$HBC_COMMIT" ] && return 0
|
||||
|
||||
log "Local hbc version: ${cargo_ver:-None}, Required: $HBC_COMMIT. Installing correct version..."
|
||||
cargo +nightly install --git https://git.ablecorp.us/ableos/holey-bytes hbc --debug --rev "$HBC_COMMIT" >/dev/null 2>&1 ||
|
||||
die "Failed to install hblang compiler"
|
||||
log "hblang compiler installed successfully"
|
||||
}
|
||||
}
|
||||
|
||||
check_out_dir() {
|
||||
if ! [ -w "$(dirname "${out}")" ]; then
|
||||
printf "ERROR: Out directory does not exist or is not writeable"
|
||||
exit 1
|
||||
fi
|
||||
check_changes() {
|
||||
[ -r "$CHECKSUM_FILE" ] && previous_checksum=$(cat "$CHECKSUM_FILE")
|
||||
current_checksum=$(printf "%s%s%s%s" \
|
||||
"$(find "$SRC_DIR" -type f -exec md5sum {} + | sort | md5sum)" \
|
||||
"$linker" "$out" "$target" | md5sum)
|
||||
echo "$current_checksum" >"$CHECKSUM_FILE"
|
||||
[ "${previous_checksum:-}" != "$current_checksum" ]
|
||||
}
|
||||
|
||||
set_target() {
|
||||
echo "target := @use(\"./$target.hb\")" >"$build_dir/src/lib/target/target.hb"
|
||||
}
|
||||
|
||||
while :; do
|
||||
case ${1:-""} in
|
||||
-h | --help)
|
||||
printf "build script
|
||||
-r/--run: run (Available only on target=c_native)
|
||||
-l/--linker: set linker (Available only on target=c_native)
|
||||
-t/--target: set target (One of: c_native, hbvm_ableos)
|
||||
-o/--out: set output path
|
||||
-h/--help: show help
|
||||
examples
|
||||
./build -t hbvm_ableos
|
||||
./build -r -l 'zig cc' -t c_native"
|
||||
exit
|
||||
;;
|
||||
-r | --run)
|
||||
run=1
|
||||
;;
|
||||
-t | --target)
|
||||
if [ -n "${2:-}" ]; then
|
||||
target="${2:-}"
|
||||
parse_args() {
|
||||
while [ $# -gt 0 ]; do
|
||||
case ${1:-""} in
|
||||
-h | --help)
|
||||
cat <<EOF
|
||||
Build Script
|
||||
Usage: $0 [options]
|
||||
-r, --run Run the compiled output (Available only on target=c_native)
|
||||
-l, --linker <linker> Specify linker (Available only on target=c_native)
|
||||
-t, --target <target> Specify target (One of: c_native, hbvm_ableos)
|
||||
-o, --out <path> Set output path
|
||||
-h, --help Show this help message
|
||||
Examples:
|
||||
$0 -t hbvm_ableos
|
||||
$0 -r -l 'zig cc' -t c_native
|
||||
$0 -r
|
||||
EOF
|
||||
exit
|
||||
;;
|
||||
-r | --run) run=1 ;;
|
||||
-t | --target)
|
||||
[ -z "${2:-}" ] && die "'--target' requires a non-empty option argument"
|
||||
target=$2
|
||||
shift
|
||||
else
|
||||
printf "ERROR: '--target' requires a non-empty option argument." >&2
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
--target=?*)
|
||||
target=${1#*=}
|
||||
;;
|
||||
--target=)
|
||||
printf 'ERROR: "--target" requires a non-empty option argument.\n' >&2
|
||||
exit 1
|
||||
;;
|
||||
-l | --linker)
|
||||
if [ -n "${2:-}" ]; then
|
||||
linker="${2:-}"
|
||||
;;
|
||||
--target=?*) target=${1#*=} ;;
|
||||
--target=) die "'--target' requires a non-empty option argument" ;;
|
||||
-l | --linker)
|
||||
[ -z "${2:-}" ] && die "'--linker' requires a non-empty option argument"
|
||||
linker=$2
|
||||
shift
|
||||
else
|
||||
printf "ERROR: '--linker' requires a non-empty option argument." >&2
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
--linker=?*)
|
||||
linker=${1#*=}
|
||||
;;
|
||||
--linker=)
|
||||
printf 'ERROR: "--linker" requires a non-empty option argument.\n' >&2
|
||||
exit 1
|
||||
;;
|
||||
-o | --out)
|
||||
if [ -n "${2:-}" ]; then
|
||||
out="${2:-}"
|
||||
;;
|
||||
--linker=?*) linker=${1#*=} ;;
|
||||
--linker=) die "'--linker' requires a non-empty option argument" ;;
|
||||
-o | --out)
|
||||
[ -z "${2:-}" ] && die "'--out' requires a non-empty option argument"
|
||||
out=$2
|
||||
shift
|
||||
else
|
||||
printf "ERROR: '--out' requires a non-empty option argument." >&2
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
--out=?*)
|
||||
out=${1#*=}
|
||||
;;
|
||||
--out=)
|
||||
printf 'ERROR: "--out" requires a non-empty option argument.\n' >&2
|
||||
exit 1
|
||||
;;
|
||||
--)
|
||||
;;
|
||||
--out=?*) out=${1#*=} ;;
|
||||
--out=) die "'--out' requires a non-empty option argument" ;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
-?*) die "Unknown option $1" ;;
|
||||
*) break ;;
|
||||
esac
|
||||
shift
|
||||
break
|
||||
;;
|
||||
-?*)
|
||||
printf "ERROR: Unknown option %s\n" "$1" >&2
|
||||
;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
done
|
||||
|
||||
if [ -n "$linker" ] && [ "$target" = "hbvm_ableos" ]; then
|
||||
printf "ERROR: Linker cannot be set with target 'hbvm_ableos'"
|
||||
exit 1
|
||||
fi
|
||||
if [ $run = 1 ] && [ "$target" = "hbvm_ableos" ]; then
|
||||
printf "ERROR: ./build does not support running the executable with target 'hbvm_ableos'"
|
||||
exit 1
|
||||
fi
|
||||
if [ -n "$linker" ] && [ "$target" = "hbvm_ableos" ]; then
|
||||
die "Linker cannot be set with target 'hbvm_ableos'"
|
||||
fi
|
||||
if [ "$run" = 1 ] && [ "$target" = "hbvm_ableos" ]; then
|
||||
die "./build does not support running the executable with target 'hbvm_ableos'"
|
||||
fi
|
||||
}
|
||||
|
||||
check_hbc
|
||||
|
||||
if [ "$target" = "c_native" ]; then
|
||||
build_c_native() {
|
||||
if [ -z "$linker" ]; then
|
||||
set -- "zig cc" "clang" "gcc"
|
||||
for linker; do
|
||||
cmd=$(echo "$linker" | awk '{print $1}')
|
||||
if command -v "$cmd" >/dev/null 2>&1; then break; fi
|
||||
for cmd in "zig cc" clang gcc; do
|
||||
if command -v "${cmd%% *}" >/dev/null 2>&1; then
|
||||
linker=$cmd
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -z "$linker" ]; then
|
||||
echo "Could not find any of the following:" "$@" ". please install any, or add to PATH." >&2
|
||||
exit 1
|
||||
fi
|
||||
[ -z "$linker" ] && die "Could not find zig, clang, or gcc. Please install any, or add to PATH."
|
||||
fi
|
||||
|
||||
make_build_dir
|
||||
check_out_dir
|
||||
set_target
|
||||
if check_changes || [ ! -e "$out" ]; then
|
||||
hbc "$SCRIPT_DIR/src/main.hb" --backend-flags=opt_level=speed >"$BUILD_PATH/out.o" ||
|
||||
{
|
||||
rm -f "$BUILD_PATH/out.o"
|
||||
rm -f "$CHECKSUM_FILE"
|
||||
die "Compilation failed"
|
||||
}
|
||||
|
||||
if hbc "$build_dir/src/main.hb" --backend-flags=opt_level=speed >"$build_dir/target/build/lily.o"; then
|
||||
$linker "$build_dir/target/build/lily.o" -o "$out"
|
||||
strip -s "$out"
|
||||
else
|
||||
echo "compilation failed :(" >&2
|
||||
exit 1
|
||||
$linker "$BUILD_PATH/out.o" -o "$out"
|
||||
strip -s "$out" || die "Stripping failed"
|
||||
fi
|
||||
if [ $run = 1 ] && [ ! "$target" = "hbvm_ableos" ]; then
|
||||
"$build_dir"/target/lily
|
||||
|
||||
if [ "$run" = 1 ]; then exec "$out"; fi
|
||||
}
|
||||
|
||||
build_hbvm() {
|
||||
[ "$out" = "$DEFAULT_OUT" ] && out="$SCRIPT_DIR/target/lily.hbf"
|
||||
if check_changes || [ ! -e "$out" ]; then
|
||||
hbc "$SCRIPT_DIR/src/main.hb" --target=unknown-virt-unknown --backend-flags=opt_level=speed >"$out" ||
|
||||
{
|
||||
rm -f "$CHECKSUM_FILE"
|
||||
die "Compilation failed"
|
||||
}
|
||||
fi
|
||||
elif [ "$target" = "hbvm_ableos" ]; then
|
||||
make_build_dir
|
||||
check_out_dir
|
||||
set_target
|
||||
if [ "$out" = "$build_dir/target/lily" ]; then out="$build_dir/target/lily.hbf"; fi
|
||||
if ! hbc "$build_dir/src/main.hb" --target=unknown-virt-unknown >"$out"; then
|
||||
echo "compilation failed :(" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
main() {
|
||||
parse_args "$@"
|
||||
|
||||
mkdir -p "$BUILD_PATH" || die "Failed to create build directory"
|
||||
[ -w "$BUILD_PATH" ] || die "$BUILD_PATH is not writable"
|
||||
[ -w "$(dirname "$out")" ] || die "Out directory does not exist or is not writeable"
|
||||
|
||||
echo "target := @use(\"./$target.hb\")" >"$TARGET_FILE"
|
||||
check_hbc
|
||||
|
||||
case $target in
|
||||
c_native) build_c_native ;;
|
||||
hbvm_ableos) build_hbvm ;;
|
||||
*) die "Invalid target: $target" ;;
|
||||
esac
|
||||
|
||||
}
|
||||
|
||||
main "$@"
|
||||
|
|
BIN
src/assets/zeroed
Normal file
BIN
src/assets/zeroed
Normal file
Binary file not shown.
|
@ -1,4 +1,4 @@
|
|||
.{collections: .{Vec}, target, target_c_native, target_hbvm_ableos, null_pointer} := @use("lib.hb")
|
||||
.{collections: .{SparseVec}, target, target_c_native, target_hbvm_ableos, Type} := @use("lib.hb")
|
||||
|
||||
Allocation := struct {
|
||||
ptr: ^void,
|
||||
|
@ -7,12 +7,12 @@ Allocation := struct {
|
|||
|
||||
// ! THIS ALLOCATOR IS TEMPORARY
|
||||
SimpleAllocator := struct {
|
||||
allocations: Vec(Allocation, RawAllocator),
|
||||
allocations: SparseVec(Allocation, RawAllocator),
|
||||
raw: RawAllocator,
|
||||
|
||||
$new := fn(): Self {
|
||||
raw := RawAllocator.new()
|
||||
return .(Vec(Allocation, RawAllocator).new(&raw), raw)
|
||||
return .(SparseVec(Allocation, RawAllocator).new(&raw), raw)
|
||||
}
|
||||
deinit := fn(self: ^Self): void {
|
||||
loop if self.allocations.len == 0 break else {
|
||||
|
@ -66,16 +66,18 @@ RawAllocator := struct {
|
|||
old_ptr: ^void,
|
||||
size: uint,
|
||||
old_size: uint,
|
||||
$new := fn(): Self return .(null_pointer(void), null_pointer(void), 0, 0)
|
||||
$new := fn(): Self return .(Type(^void).uninit(), Type(^void).uninit(), 0, 0)
|
||||
deinit := fn(self: ^Self): void {
|
||||
if target == target_c_native {
|
||||
target.free(self.ptr)
|
||||
target.free(self.old_ptr)
|
||||
// ! (compiler) bug: comparing `self.ptr != Type(^void).uninit()` rather than `self.size == 0`
|
||||
// causes condition to never be true (even though it is)
|
||||
if self.size != 0 target.free(self.ptr)
|
||||
if self.old_size != 0 target.free(self.old_ptr)
|
||||
} else if target == target_hbvm_ableos {
|
||||
target.free(self.ptr, self.size)
|
||||
target.free(self.old_ptr, self.old_size)
|
||||
if self.size != 0 target.free(self.ptr, self.size)
|
||||
if self.old_size != 0 target.free(self.old_ptr, self.old_size)
|
||||
};
|
||||
*self = Self.new()
|
||||
// *self = Self.new()
|
||||
}
|
||||
alloc := fn(self: ^Self, $T: type, count: uint): ?^T {
|
||||
ptr := target.malloc(count * @sizeof(T))
|
||||
|
|
|
@ -1,8 +1 @@
|
|||
.{Vec} := @use("vec.hb")
|
||||
|
||||
// mostly placeholder error error type
|
||||
Error := enum {
|
||||
KeyNotFound,
|
||||
OutOfRange,
|
||||
Full,
|
||||
}
|
||||
.{SparseVec} := @use("vec.hb")
|
|
@ -1,18 +1,19 @@
|
|||
.{target, log, target_c_native, target_hbvm_ableos, result: .{Result}, null_pointer} := @use("../lib.hb");
|
||||
.{Error} := @use("lib.hb")
|
||||
.{target, target_c_native, target_hbvm_ableos, memmove, Type} := @use("../lib.hb");
|
||||
|
||||
Vec := fn($T: type, $A: type): type return struct {
|
||||
// self.slice.len tracks non-null elements
|
||||
SparseVec := fn($T: type, $A: type): type return struct {
|
||||
// slice.len tracks null and non-null elements
|
||||
slice: []?T,
|
||||
allocator: ^A,
|
||||
// len tracks non-null elements
|
||||
len: uint,
|
||||
// capacity tracks maximum slice.len before reallocating
|
||||
capacity: uint,
|
||||
|
||||
$new := fn(allocator: ^A): Self return .{slice: null_pointer(?T)[0..0], allocator, capacity: 0, len: 0}
|
||||
$new := fn(allocator: ^A): Self return .{slice: Type([]?T).uninit(), allocator, capacity: 0, len: 0}
|
||||
deinit := fn(self: ^Self): void {
|
||||
// currently does not handle deinit of T if T allocates memory
|
||||
if self.capacity != 0 self.allocator.free(?T, self.slice.ptr)
|
||||
self.slice = null_pointer(?T)[0..0]
|
||||
self.slice = Type([]?T).uninit()
|
||||
self.capacity = 0
|
||||
}
|
||||
push := fn(self: ^Self, value: T): void {
|
||||
|
@ -26,8 +27,11 @@ Vec := fn($T: type, $A: type): type return struct {
|
|||
// ! (c_native) (compiler) bug: null check broken, so unwrapping (unsafe!)
|
||||
new_alloc := @unwrap(self.allocator.alloc(?T, self.capacity))
|
||||
|
||||
if self.len > 0 {
|
||||
memmove(new_alloc, self.slice.ptr, self.slice.len * @sizeof(?T))
|
||||
}
|
||||
|
||||
if self.slice.len > 0 {
|
||||
target.memmove(@bitcast(new_alloc), @bitcast(self.slice.ptr), self.slice.len * @sizeof(?T))
|
||||
self.allocator.free(?T, self.slice.ptr)
|
||||
}
|
||||
self.slice.ptr = new_alloc
|
||||
|
@ -55,6 +59,7 @@ Vec := fn($T: type, $A: type): type return struct {
|
|||
a := self.slice[n]
|
||||
if a != null {
|
||||
self.slice[n] = null
|
||||
self.slice.len -= 1
|
||||
self.len -= 1
|
||||
return a
|
||||
}
|
||||
|
@ -69,12 +74,12 @@ Vec := fn($T: type, $A: type): type return struct {
|
|||
a := self.slice[m]
|
||||
if a != null {
|
||||
// ! (compiler) bug: This print causes compiler panic
|
||||
printf("%d\n\0".ptr, a)
|
||||
if !@target("*-virt-unknown") printf("%d\n\0".ptr, a)
|
||||
if j == n {
|
||||
self.slice[m] = null
|
||||
self.len -= 1
|
||||
// ! (compiler) bug: This print happens but we never see the "zub zub {a}" in main.hb?????
|
||||
printf("here: %d\n\0".ptr, a)
|
||||
// ! (compiler) bug: This print happens but we never see the "zub zub" in main.hb
|
||||
if !@target("*-virt-unknown") printf("here: %d\n\0".ptr, a)
|
||||
return a
|
||||
}
|
||||
j += 1
|
||||
|
|
71
src/lib/fmt.hb
Normal file
71
src/lib/fmt.hb
Normal file
|
@ -0,0 +1,71 @@
|
|||
.{target, Type, TypeOf, string, memcpy} := @use("lib.hb")
|
||||
|
||||
// ! (c_native) (compiler) bug: caused by: `lily.log.print(100)`
|
||||
fmt_int := fn(v: @Any(), str: []u8, radix: @TypeOf(v)): uint {
|
||||
is_negative := TypeOf(v).is_signed_int() & v < 0
|
||||
prefix_len := 0
|
||||
if is_negative {
|
||||
v = -v
|
||||
str[0] = '-'
|
||||
prefix_len += 1
|
||||
}
|
||||
|
||||
if radix == 16 {
|
||||
*@as(^[2]u8, @bitcast(str.ptr + prefix_len)) = *@bitcast("0x".ptr)
|
||||
prefix_len += 2
|
||||
} else if radix == 2 {
|
||||
*@as(^[2]u8, @bitcast(str.ptr + prefix_len)) = *@bitcast("0b".ptr)
|
||||
prefix_len += 2
|
||||
} else if radix == 8 {
|
||||
*@as(^[2]u8, @bitcast(str.ptr + prefix_len)) = *@bitcast("0o".ptr)
|
||||
prefix_len += 2
|
||||
}
|
||||
|
||||
if v == 0 {
|
||||
str[prefix_len] = '0'
|
||||
return prefix_len + 1
|
||||
}
|
||||
|
||||
i := prefix_len
|
||||
loop if v <= 0 break else {
|
||||
remainder := v % radix
|
||||
v /= radix
|
||||
if remainder > 9 {
|
||||
str[i] = @intcast(remainder - 10 + 'A')
|
||||
} else {
|
||||
str[i] = @intcast(remainder + '0')
|
||||
}
|
||||
i += 1
|
||||
}
|
||||
|
||||
string.reverse(str[prefix_len..i])
|
||||
return i
|
||||
}
|
||||
|
||||
fmt_bool := fn(v: bool, str: []u8): uint {
|
||||
if v {
|
||||
memcpy(str.ptr, "true".ptr, 4)
|
||||
return 4
|
||||
} else {
|
||||
memcpy(str.ptr, "false".ptr, 5)
|
||||
return 5
|
||||
}
|
||||
}
|
||||
|
||||
format := fn(buf: []u8, v: @Any()): uint {
|
||||
T := TypeOf(v)
|
||||
match T.kind() {
|
||||
.Pointer => return fmt_int(@as(uint, @bitcast(v)), buf, 16),
|
||||
.Builtin => {
|
||||
if T.is_int() return fmt_int(v, buf, 10)
|
||||
if T.is_bool() return fmt_bool(v, buf)
|
||||
},
|
||||
_ => @error("format(", T, ") is not supported."),
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
format_with_str := fn(str: []u8, buf: []u8, v: @Any()): uint {
|
||||
memcpy(buf.ptr, str.ptr, str.len)
|
||||
return str.len
|
||||
}
|
101
src/lib/lib.hb
101
src/lib/lib.hb
|
@ -4,16 +4,18 @@ Version := struct {
|
|||
patch: uint,
|
||||
}
|
||||
|
||||
$VERSION := Version(0, 0, 2)
|
||||
$VERSION := Version(0, 0, 3)
|
||||
|
||||
collections := @use("collections/lib.hb")
|
||||
result := @use("result.hb")
|
||||
string := @use("string.hb")
|
||||
alloc := @use("alloc.hb")
|
||||
rand := @use("rand.hb")
|
||||
log := @use("log.hb")
|
||||
fmt := @use("fmt.hb")
|
||||
|
||||
target_c_native := @use("target/c_native.hb")
|
||||
target_hbvm_ableos := @use("target/hbvm_ableos.hb")
|
||||
// target := target_hbvm_ableos
|
||||
target_c_native := @use("target/c_native.hb")
|
||||
target := @use("target/target.hb").target
|
||||
|
||||
$exit := fn(code: int): void {
|
||||
|
@ -21,6 +23,39 @@ $exit := fn(code: int): void {
|
|||
die
|
||||
}
|
||||
|
||||
$panic := fn(message: ?[]u8): void {
|
||||
if message != null log.error(message) else log.error("The program called panic.")
|
||||
exit(1)
|
||||
}
|
||||
|
||||
$memcpy := fn(dest: @Any(), src: @Any(), size: uint): void {
|
||||
return target.memcpy(@bitcast(dest), @bitcast(src), size)
|
||||
}
|
||||
|
||||
$memmove := fn(dest: @Any(), src: @Any(), size: uint): void {
|
||||
return target.memmove(@bitcast(dest), @bitcast(src), size)
|
||||
}
|
||||
|
||||
$memset := fn(dest: @Any(), src: u8, size: uint): void {
|
||||
return target.memset(@bitcast(dest), src, size)
|
||||
}
|
||||
|
||||
RawKind := enum {
|
||||
Builtin,
|
||||
Struct,
|
||||
Tuple,
|
||||
Enum,
|
||||
Union,
|
||||
Pointer,
|
||||
Slice,
|
||||
Optional,
|
||||
Function,
|
||||
Template,
|
||||
Global,
|
||||
Constant,
|
||||
Module,
|
||||
}
|
||||
|
||||
Kind := enum {
|
||||
Builtin,
|
||||
Struct,
|
||||
|
@ -29,17 +64,31 @@ Kind := enum {
|
|||
Union,
|
||||
Pointer,
|
||||
Slice,
|
||||
Opt,
|
||||
Array,
|
||||
Optional,
|
||||
Function,
|
||||
Template,
|
||||
Global,
|
||||
Const,
|
||||
Constant,
|
||||
Module,
|
||||
}
|
||||
|
||||
$null_pointer := fn($T: type): ^T return @bitcast(0)
|
||||
TypeOf := fn(T: @Any()): type return Type(@TypeOf(T))
|
||||
|
||||
Type := fn($T: type): type return struct {
|
||||
// ! no way of representing arbitrary size integers yet
|
||||
USize := fn(): type {
|
||||
if @sizeof(T) == 1 return u8 else if @sizeof(T) == 2 return u16 else if @sizeof(T) == 4 return u32 else return uint
|
||||
}
|
||||
Child := fn(): type {
|
||||
return Type(@ChildOf(T))
|
||||
}
|
||||
This := fn(): type {
|
||||
return T
|
||||
}
|
||||
$is_bool := fn(): bool {
|
||||
return T == bool
|
||||
}
|
||||
$is_unsigned_int := fn(): bool {
|
||||
return T == uint | T == u8 | T == u16 | T == u32
|
||||
}
|
||||
|
@ -47,21 +96,49 @@ Type := fn($T: type): type return struct {
|
|||
return T == int | T == i8 | T == i16 | T == i32
|
||||
}
|
||||
$is_int := fn(): bool {
|
||||
return Self.unsigned_int(T) | Self.signed_int(T)
|
||||
return Self.is_unsigned_int() | Self.is_signed_int()
|
||||
}
|
||||
$is_float := fn(): bool {
|
||||
return T == f32 | T == f64
|
||||
}
|
||||
$usize := fn(): type {
|
||||
if @sizeof(T) == 1 return u8 else if @sizeof(T) == 2 return u16 else if @sizeof(T) == 4 return u32 else return uint
|
||||
$len := fn(): uint {
|
||||
return @lenof(T)
|
||||
}
|
||||
$bits := fn(): Self.usize() {
|
||||
$align := fn(): uint {
|
||||
return @alignof(T)
|
||||
}
|
||||
$size := fn(): uint {
|
||||
return @sizeof(T)
|
||||
}
|
||||
$bits := fn(): uint {
|
||||
return @sizeof(T) << 3
|
||||
}
|
||||
$bitmask := fn(): Self.usize() {
|
||||
$bitmask := fn(): Self.Usize() {
|
||||
return -1
|
||||
}
|
||||
$kind := fn(): Kind {
|
||||
$raw_kind := fn(): RawKind {
|
||||
return @bitcast(@kindof(T))
|
||||
}
|
||||
$kind := fn(): Kind {
|
||||
if Self.raw_kind() == .Slice {
|
||||
if []@ChildOf(T) == T return .Slice else return .Array
|
||||
} else if @kindof(T) > @bitcast(Kind.Slice) {
|
||||
return @bitcast(@kindof(T) + 1)
|
||||
} else return @bitcast(Self.raw_kind())
|
||||
}
|
||||
/// ! There are no guarantees that this value is zeroed for builtins, enums, unions, structs, arrays, or tuples.
|
||||
$uninit := fn(): T {
|
||||
match Self.kind() {
|
||||
.Pointer => return @bitcast(0),
|
||||
.Slice => return @bitcast(@as(^void, @bitcast(0))[0..0]),
|
||||
.Array => return idk,
|
||||
.Builtin => return idk,
|
||||
.Struct => return idk,
|
||||
.Tuple => return idk,
|
||||
.Union => return idk,
|
||||
.Enum => return idk,
|
||||
.Optional => return null,
|
||||
_ => @error("Type(", T, ").uninit() does not make sense."),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
.{target, target_c_native, target_hbvm_ableos} := @use("lib.hb")
|
||||
.{target, target_c_native, target_hbvm_ableos, fmt} := @use("lib.hb")
|
||||
|
||||
LogLevel := enum {
|
||||
Error,
|
||||
|
@ -13,23 +13,40 @@ log := fn(level: LogLevel, str: []u8): void {
|
|||
return @eca(3, 1, target.LogMsg.(level, str.ptr, str.len), @sizeof(target.LogMsg))
|
||||
} else if target == target_c_native {
|
||||
match level {
|
||||
.Error => target.printf_str("\{1b}[31mERROR\{1b}[0m: %s\n\0".ptr, str.ptr),
|
||||
.Warn => target.printf_str("\{1b}[33mWARN\{1b}[0m: %s\n\0".ptr, str.ptr),
|
||||
.Info => target.printf_str("\{1b}[32mINFO\{1b}[0m: %s\n\0".ptr, str.ptr),
|
||||
.Debug => target.printf_str("\{1b}[34mDEBUG\{1b}[0m: %s\n\0".ptr, str.ptr),
|
||||
.Trace => target.printf_str("\{1b}[35mTRACE\{1b}[0m: %s\n\0".ptr, str.ptr),
|
||||
.Error => target.printf_str("\{1b}[31mERROR\{1b}[0m: %.*s\n\0".ptr, str.len, str.ptr),
|
||||
.Warn => target.printf_str("\{1b}[33mWARN\{1b}[0m: %.*s\n\0".ptr, str.len, str.ptr),
|
||||
.Info => target.printf_str("\{1b}[32mINFO\{1b}[0m: %.*s\n\0".ptr, str.len, str.ptr),
|
||||
.Debug => target.printf_str("\{1b}[34mDEBUG\{1b}[0m: %.*s\n\0".ptr, str.len, str.ptr),
|
||||
.Trace => target.printf_str("\{1b}[35mTRACE\{1b}[0m: %.*s\n\0".ptr, str.len, str.ptr),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$print := fn(str: []u8): void {
|
||||
if target == target_c_native {
|
||||
target.puts(str.ptr)
|
||||
} else if target == target_hbvm_ableos {
|
||||
info(str)
|
||||
// it's good enough i guess. dont write more than 4096 chars or you will explode.
|
||||
print_buffer := @embed("../assets/zeroed")
|
||||
|
||||
print := fn(any: @Any()): void {
|
||||
if @TypeOf(any) == []u8 {
|
||||
if target == target_c_native {
|
||||
target.printf_str("%.*s\n\0".ptr, any.len, any.ptr)
|
||||
} else if target == target_hbvm_ableos {
|
||||
info(any)
|
||||
}
|
||||
} else {
|
||||
// limits len to size of buffer - 1
|
||||
len := fmt.format(print_buffer[0..@sizeof(@TypeOf(print_buffer)) - 1], any)
|
||||
print_buffer[len] = 0
|
||||
// ! (compiler) bug: not inlining here causes compiler panic
|
||||
@inline(print, print_buffer[0..len])
|
||||
}
|
||||
}
|
||||
|
||||
printf := fn(str: []u8, any: @Any()): void {
|
||||
len := fmt.format_with_str(str, print_buffer[0..@sizeof(@TypeOf(print_buffer)) - 1], any)
|
||||
print_buffer[len] = 0
|
||||
print(print_buffer[0..len])
|
||||
}
|
||||
|
||||
$error := fn(message: []u8): void return log(LogLevel.Error, message)
|
||||
$warn := fn(message: []u8): void return log(LogLevel.Warn, message)
|
||||
$info := fn(message: []u8): void return log(LogLevel.Info, message)
|
||||
|
|
64
src/lib/rand.hb
Normal file
64
src/lib/rand.hb
Normal file
|
@ -0,0 +1,64 @@
|
|||
.{target, TypeOf, Type} := @use("lib.hb")
|
||||
|
||||
// ! NON CRYPTOGRAPHIC, TEMPORARY
|
||||
SimpleRandom := struct {
|
||||
seed: uint,
|
||||
$new := fn(): Self return Self.(0)
|
||||
$deinit := fn(self: ^Self): void {
|
||||
self.seed = 0
|
||||
}
|
||||
$any := fn(self: ^Self, $A: type): A {
|
||||
T := Type(A)
|
||||
match T.kind() {
|
||||
.Slice => @error("Use SimpleRandom.fill or SimpleRandom.fill_values instead."),
|
||||
.Array => @error("Use SimpleRandom.fill or SimpleRandom.fill_values instead."),
|
||||
_ => {
|
||||
},
|
||||
}
|
||||
if T.is_bool() {
|
||||
a: A = idk
|
||||
target.getrandom(@bitcast(&a), 1)
|
||||
return @bitcast(a & @as(u8, 1))
|
||||
}
|
||||
a: A = idk
|
||||
target.getrandom(@bitcast(&a), T.size())
|
||||
return a
|
||||
}
|
||||
/// Accepts any type for min and max (as long as it is the same for both).
|
||||
$range := fn(self: ^Self, min: @Any(), max: @TypeOf(min)): @TypeOf(min) {
|
||||
return self.any(@TypeOf(min)) % (max - min) + min
|
||||
}
|
||||
/// Fills an array or slice with random bytes
|
||||
$fill := fn(self: ^Self, buf: @Any()): void {
|
||||
T := TypeOf(buf)
|
||||
match T.kind() {
|
||||
.Slice => target.getrandom(@bitcast(buf.ptr), buf.len * T.Child().size()),
|
||||
.Array => target.getrandom(@bitcast(&buf), T.size()),
|
||||
_ => @error("Can only fill bytes of Slice or Array."),
|
||||
}
|
||||
}
|
||||
/// Fills an array or slice with random values
|
||||
// ! (compiler) bug: `buf[i]` causing compiler panic here
|
||||
fill_values := fn(self: ^Self, buf: @Any()): void {
|
||||
T := TypeOf(buf)
|
||||
match T.kind() {
|
||||
.Slice => {
|
||||
len := buf.len * T.Child().size()
|
||||
i := 0
|
||||
loop if i == len break else {
|
||||
buf[i] = self.any(T.Child().This())
|
||||
i += 1
|
||||
}
|
||||
},
|
||||
.Array => {
|
||||
len := T.size()
|
||||
i := 0
|
||||
$loop if i == len break else {
|
||||
buf[i] = self.any(T.Child().This())
|
||||
i += 1
|
||||
}
|
||||
},
|
||||
_ => @error("Can only fill values of Slice or Array."),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
.{exit} := @use("lib.hb")
|
||||
.{panic} := @use("lib.hb")
|
||||
|
||||
ResultInner := fn($T: type, $E: type): type return union {ok: T, err: E}
|
||||
|
||||
|
@ -8,9 +8,9 @@ Result := fn($T: type, $E: type): type return struct {
|
|||
|
||||
$ok := fn(k: T): Self return .(.{ok: k}, true)
|
||||
$err := fn(k: E): Self return .(.{err: k}, false)
|
||||
$unwrap := fn(self: Self): T return self.expect("Panic: Unwrap on Error Variant.\n\0")
|
||||
$unwrap := fn(self: Self): T return self.expect("Unwrap on error variant.")
|
||||
$unwrap_unchecked := fn(self: Self): T return self.inner.ok
|
||||
unwrap_or := fn(self: Self, v: T): T if self.is_ok return self.inner.ok else return v
|
||||
unwrap_or_else := fn(self: Self, $F: type): T if self.is_ok return self.inner.ok else return F(self.inner.err)
|
||||
expect := fn(self: Self, msg: []u8): T if self.is_ok return self.inner.ok else exit(1)
|
||||
expect := fn(self: Self, msg: []u8): T if self.is_ok return self.inner.ok else panic(msg)
|
||||
}
|
170
src/lib/string.hb
Normal file
170
src/lib/string.hb
Normal file
|
@ -0,0 +1,170 @@
|
|||
reverse := fn(str: []u8): void {
|
||||
if str.len == 0 return;
|
||||
j := str.len - 1
|
||||
i := 0
|
||||
temp := @as(u8, 0)
|
||||
loop if i < j {
|
||||
temp = str[i]
|
||||
str[i] = str[j]
|
||||
str[j] = temp
|
||||
i += 1
|
||||
j -= 1
|
||||
} else return
|
||||
}
|
||||
|
||||
equals := fn(lhs: []u8, rhs: []u8): bool {
|
||||
if lhs.len != rhs.len return false
|
||||
if lhs.ptr == rhs.ptr return true
|
||||
i := 0
|
||||
loop if i == lhs.len break else {
|
||||
if lhs[i] != rhs[i] return false
|
||||
i += 1
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
clear := fn(str: []u8): void {
|
||||
i := 0
|
||||
loop if i == str.len break else {
|
||||
str[i] = 0
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
|
||||
split_once := fn(haystack: []u8, needle: @Any()): ?struct {left: []u8, right: []u8} {
|
||||
T := @TypeOf(needle)
|
||||
i := 0
|
||||
if T == []u8 {
|
||||
if needle.len == 0 return null
|
||||
loop {
|
||||
if i + needle.len > haystack.len return null
|
||||
if haystack[i] == needle[0] {
|
||||
matches := true
|
||||
n := 1
|
||||
loop {
|
||||
if n == needle.len break
|
||||
if haystack[i + n] != needle[n] {
|
||||
matches = false
|
||||
break
|
||||
}
|
||||
n += 1
|
||||
}
|
||||
|
||||
if matches return .(haystack[0..i], haystack[i + needle.len..])
|
||||
}
|
||||
i += 1
|
||||
}
|
||||
} else if T == u8 {
|
||||
loop {
|
||||
if haystack[i] == needle {
|
||||
return .(haystack[0..i], haystack[i + 1..])
|
||||
} else if i == haystack.len return null
|
||||
i += 1
|
||||
}
|
||||
} else {
|
||||
@error("Type of needle must be []u8 or u8.")
|
||||
}
|
||||
}
|
||||
|
||||
split := fn(iter: []u8, needle: @Any()): struct {
|
||||
str: []u8,
|
||||
needle: @TypeOf(needle),
|
||||
done: bool,
|
||||
|
||||
next := fn(self: ^Self): ?[]u8 {
|
||||
if self.done return null;
|
||||
|
||||
splits := split_once(self.str, self.needle)
|
||||
if splits != null {
|
||||
self.str = splits.right
|
||||
return splits.left
|
||||
} else {
|
||||
self.done = true
|
||||
return self.str
|
||||
}
|
||||
}
|
||||
} {
|
||||
T := @TypeOf(needle)
|
||||
if T != []u8 & T != u8 {
|
||||
@error("Type of needle must be []u8 or u8.")
|
||||
}
|
||||
return .(iter, needle, false)
|
||||
}
|
||||
|
||||
chars := fn(iter: []u8): struct {
|
||||
str: []u8,
|
||||
|
||||
next := fn(self: ^Self): ?u8 {
|
||||
if self.str.len == 0 return null
|
||||
self.str = self.str[1..]
|
||||
return self.str[0]
|
||||
}
|
||||
} {
|
||||
return .(iter)
|
||||
}
|
||||
|
||||
count := fn(haystack: []u8, needle: @Any()): uint {
|
||||
T := @TypeOf(needle)
|
||||
i := 0
|
||||
c := 0
|
||||
if T == []u8 {
|
||||
if needle.len == 0 return null
|
||||
loop {
|
||||
if i + needle.len > haystack.len return c
|
||||
if haystack[i] == needle[0] {
|
||||
matches := true
|
||||
n := 1
|
||||
loop {
|
||||
if n == needle.len break
|
||||
if haystack[i + n] != needle[n] {
|
||||
matches = false
|
||||
break
|
||||
}
|
||||
n += 1
|
||||
}
|
||||
|
||||
if matches c += 1
|
||||
}
|
||||
i += 1
|
||||
}
|
||||
} else if T == u8 {
|
||||
loop {
|
||||
if haystack[i] == needle c += 1 else if i == haystack.len return c
|
||||
i += 1
|
||||
}
|
||||
} else {
|
||||
@error("Type of needle must be []u8 or u8.")
|
||||
}
|
||||
}
|
||||
|
||||
left_trim := fn(str: []u8, sub: []u8): []u8 {
|
||||
i := 0
|
||||
if str[0] == sub[0] {
|
||||
loop if i == sub.len {
|
||||
return str[i..str.len]
|
||||
} else if str[i] != sub[i] | i == str.len {
|
||||
break
|
||||
} else {
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
right_trim := fn(str: []u8, sub: []u8): []u8 {
|
||||
i := 0
|
||||
if str[str.len - 1] == sub[sub.len - 1] {
|
||||
loop if i == sub.len {
|
||||
return str[0..str.len - i]
|
||||
} else if str[str.len - i - 1] != sub[sub.len - i - 1] | i == str.len {
|
||||
break
|
||||
} else {
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
trim := fn(str: []u8, sub: []u8): []u8 {
|
||||
return right_trim(left_trim(str, sub), sub)
|
||||
}
|
|
@ -4,6 +4,5 @@ memmove := fn(dest: ^void, src: ^void, size: uint): void @import()
|
|||
memcpy := fn(dest: ^void, src: ^void, size: uint): void @import()
|
||||
memset := fn(dest: ^void, src: u8, size: uint): void @import()
|
||||
exit := fn(code: int): void @import()
|
||||
puts := fn(str: ^u8): void @import()
|
||||
printf := fn(str: ^u8): void @import()
|
||||
printf_str := fn(str0: ^u8, str1: ^u8): void @import("printf")
|
||||
printf_str := fn(str0: ^u8, strlen: uint, str1: ^u8): void @import("printf")
|
||||
getrandom := fn(dest: ^void, size: uint): void @import()
|
|
@ -1,8 +1,6 @@
|
|||
.{log: .{LogLevel}, null_pointer} := @use("../lib.hb")
|
||||
.{log: .{LogLevel}} := @use("../lib.hb")
|
||||
|
||||
$PAGE_SIZE := 4096
|
||||
$MAX_ALLOC := 0xFF
|
||||
$MAX_FREE := 0xFF
|
||||
|
||||
LogMsg := packed struct {level: LogLevel, string: ^u8, strlen: uint}
|
||||
|
||||
|
@ -20,16 +18,12 @@ $free_pages := fn(ptr: ^void, count: uint): void {
|
|||
return @eca(3, 2, &FreePageMsg.(1, count, ptr), @sizeof(FreePageMsg))
|
||||
}
|
||||
|
||||
malloc := fn(size: uint): ?^void {
|
||||
if size == 0 return null
|
||||
pages := calculate_pages(size)
|
||||
return request_pages(pages)
|
||||
$malloc := fn(size: uint): ?^void {
|
||||
return request_pages(calculate_pages(size))
|
||||
}
|
||||
|
||||
free := fn(ptr: ^void, size: uint): void {
|
||||
if size == 0 | ptr == null_pointer(void) return;
|
||||
pages := calculate_pages(size)
|
||||
free_pages(ptr, pages)
|
||||
$free := fn(ptr: ^void, size: uint): void {
|
||||
free_pages(ptr, calculate_pages(size))
|
||||
}
|
||||
|
||||
CopyMsg := packed struct {a: u8, count: uint, src: ^void, dest: ^void}
|
||||
|
@ -47,5 +41,7 @@ memmove := fn(dest: ^void, src: ^void, size: uint): void {
|
|||
memset(src, 0, size)
|
||||
}
|
||||
|
||||
$getrandom := fn(dest: ^void, size: uint): void return @eca(3, 4, dest, size)
|
||||
|
||||
$exit := fn(code: int): void {
|
||||
}
|
46
src/main.hb
46
src/main.hb
|
@ -1,33 +1,49 @@
|
|||
lily := @use("lib/lib.hb");
|
||||
|
||||
Allocator := lily.alloc.SimpleAllocator
|
||||
Vec := lily.collections.Vec
|
||||
Vec := lily.collections.SparseVec
|
||||
Random := lily.rand.SimpleRandom
|
||||
Result := lily.result.Result
|
||||
|
||||
printf := fn(str: ^u8, u: uint): void @import()
|
||||
|
||||
// ! (runtime) (target_hbvm_ableos?) bug: leaking memory (1 page in this function)
|
||||
// ! (runtime) bug: leaking memory (waiting on compiler bugfixes)
|
||||
// ! can't run on target_hbvm_ableos due to unresolved compiler bugs
|
||||
main := fn(argc: uint, argv: []^void): uint {
|
||||
allocator := Allocator.new()
|
||||
defer allocator.deinit()
|
||||
vec := Vec(uint, Allocator).new(&allocator)
|
||||
defer vec.deinit()
|
||||
rand := Random.new()
|
||||
defer rand.deinit()
|
||||
|
||||
i := 0
|
||||
// ! (compiler) bug: using `if i < 5 {}` rather than `if i >= 5 break else {}` causes
|
||||
// the program to halt after the loop
|
||||
// ! (compiler) bug: checking against `vec.len` rather than `i` causes the loop to go forever
|
||||
// despite the fact that vec.len is incremented in vec.push
|
||||
loop if i == 5 break else {
|
||||
defer i += 1
|
||||
vec.push(i)
|
||||
lily.log.info("pushed to vec\0")
|
||||
vec.push(rand.any(uint))
|
||||
lily.log.info("pushed to vec")
|
||||
}
|
||||
|
||||
z := vec.remove(1)
|
||||
z = vec.remove(3)
|
||||
if z != null {
|
||||
// should print `zub zub 4`
|
||||
// fails to print anything
|
||||
// search "(compiler) bug:" in the whole codebase to find bugs
|
||||
// additional bugs tbd
|
||||
printf("zub zub %d\n\0".ptr, z)
|
||||
}
|
||||
// ! (c_native) this causes a compiler bug in lily.fmt.fmt_int
|
||||
// lily.log.print(100)
|
||||
|
||||
lily.log.print(true)
|
||||
|
||||
// ! (c_native) (compiler) bug: compiler panic from vec.remove()
|
||||
// z := vec.remove(1)
|
||||
// lily.log.info("removed from vec")
|
||||
// z = vec.remove(3)
|
||||
// lily.log.info("removed from vec")
|
||||
// if z != null {
|
||||
// ! (compiler) bug: this never happens (even though it should)
|
||||
// lily.log.info("zub zub")
|
||||
// }
|
||||
|
||||
lily.log.info("the following should panic:")
|
||||
a := Result(bool, bool).err(false)
|
||||
_ = a.unwrap()
|
||||
|
||||
return 0
|
||||
}
|
Loading…
Reference in a new issue