finish reorganisation

implement better comptime configuration & target handling
adjust build script
This commit is contained in:
koniifer 2025-01-08 00:16:03 +00:00
parent 7d678d8c0f
commit 70a81a3dd9
17 changed files with 372 additions and 334 deletions

View file

@ -1,36 +1,49 @@
# Lily
an attempt at a cross-platform standard library for hblang.<br>
use `./build -h` to see available arguments. supports:
- changing target
- custom linker (for `c_native`)
- running the executable (for `c_native`)
- setting output path
- dumping assembly of `c_native` or `hbvm_ableos` programs
- only recompiling if either source or environment change
an attempt at a cross-platform standard library for hblang.
> [!IMPORTANT]
> all features, targets, etc, are provisional and subject to change
> all features, targets, modules, etc, are provisional and may be subject to change or deletion
use `./build -h` to see available arguments.
### supports:
- changing target
- custom linker (for native targets)
- running the executable (for native targets)
- setting output path
- dumping assembly representation
- only recompiling if either source or environment change
### To change build target
manually:
> create the file `src/lib/target/target.hb` with the contents `target := @use("my_target.hb")`
use the `-t` flag supplied in `./build`
automatically:
> use the `-t` flag supplied in `./build`
use a target triple (i.e. `x86_64-unknown-linux-gnu`), or pick from one of these aliases:
>- hbvm
>- ableos
>- libc (links to system libc)
currently available targets:
- `c_native`
- `hbvm_ableos`
>[!NOTE]
`hbvm == ableos` (for now)
### Currently "working" features
### Modifying build config
compiler flags are in: `./build` (at top of file)
> used for NON-CODE configuration
compile-time configuration is in: `./src/lily/lib.hb`
> used for things like toggling debug assertions, setting minimum log level, etc
## Features
### Working
- `lily.{Type, TypeOf, Kind, exit, panic, memcpy, memmove, memset}`
- `lily.log.{log, info, error, warn, debug, trace}`
- `lily.result.Result`
- `lily.alloc.{SimpleAllocator, RawAllocator}`
- `lily.collections.Vec`
### Currently "in progress" features
### In progress
- `lily.rand.SimpleRandom`
### Currently "broken due to compiler" features
- `lily.log.{print, printf}`
- `lily.collections.SparseVec`
- `lily.alloc.{SimpleAllocator, RawAllocator}`
### Partially broken due to compiler
- `lily.log.print`
### Completely broken due to compiler
- `lily.log.printf`

151
build
View file

@ -5,18 +5,14 @@ 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 CONFIG_FILE="$SRC_DIR/lib/target/config.hb"
hbc_flags="--backend-flags=opt_level=speed"
linker=""
target="c_native"
target="default"
run=0
debug="false"
dump_asm=0
out="$DEFAULT_OUT"
die() {
printf "ERROR: %s\n" "$1" >&2
@ -46,9 +42,9 @@ check_hbc() {
check_changes() {
[ -r "$CHECKSUM_FILE" ] && previous_checksum=$(cat "$CHECKSUM_FILE")
current_checksum=$(printf "%s%s%s%s%s" \
current_checksum=$(printf "%s%s%s" \
"$(find "$SRC_DIR" -type f -exec md5sum {} + | sort | md5sum)" \
"$linker" "$out" "$target" "$debug" | md5sum)
"$linker" "$target" | md5sum)
echo "$current_checksum" >"$CHECKSUM_FILE"
[ "${previous_checksum:-}" != "$current_checksum" ]
}
@ -59,45 +55,28 @@ parse_args() {
-h | --help)
cat <<EOF
Usage: $0 [options]
-r, --run Run the compiled output (Available only on target=c_native)
-d, --debug Enable debug mode in compilation
-a, --dump-asm Dump the assembly to stdout (Available on all targets)
-l, --linker <linker> Specify linker (Available only on target=c_native)
-t, --target <target> Specify target (Run without <target> to see options)
-o, --out <path> Set output path
-r, --run Run the output binary
-a, --dump-asm Dump the assembly to stdout
-l, --linker <linker> Specify linker
-t, --target <target> Specify target triple (or an alias: 'libc' 'ableos' 'hbvm')
-h, --help Show this help message
Examples:
$0 -t hbvm_ableos
$0 -r -l 'zig cc' -t c_native
$0 -r --debug
$0 -a -t hbvm_ableos > out.hbf
$0 -t ableos
$0 -r -l 'zig cc' -t libc
$0 -a -t ableos > out.hbf
EOF
exit
;;
-r | --run) run=1 ;;
-a | --dump-asm) dump_asm=1 ;;
-d | --debug) debug="true" ;;
-t | --target)
[ -z "${2:-}" ] &&
{
cat <<EOF
Usage: $0 --target <target>
hbvm_ableos Build for AbleOS
c_native Build for your machine
EOF
exit
}
[ -z "${2:-}" ] && die "'--target' requires a non-empty option argument"
target=$2
shift
;;
--target=?*) target=${1#*=} ;;
--target=)
cat <<EOF
Usage: $0 --target <target>
hbvm_ableos Build for AbleOS
c_native Build for your machine
EOF
exit
die "'--target' requires a non-empty option argument"
;;
-l | --linker)
[ -z "${2:-}" ] && die "'--linker' requires a non-empty option argument"
@ -122,58 +101,58 @@ EOF
esac
shift
done
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
if [ "$run" = 1 ] && [ "$dump_asm" = 1 ]; then
die "Argument '--run' is incompatible with argument '--dump-asm'"
fi
case $target in
hbvm | ableos)
if [ $run = 1 ]; then die "'--run' cannot be set with '--target $target'"; fi
target="unknown-virt-unknown"
;;
libc) target="default" ;;
esac
}
build_c_native() {
if [ -z "$linker" ]; then
for cmd in "zig cc" clang gcc; do
if command -v "${cmd%% *}" >/dev/null 2>&1; then
linker=$cmd
break
build() {
if check_changes || [ ! -e "$BUILD_PATH/$target/lily" ]; then
if [ "$target" = "unknown-virt-unknown" ]; then
out="$BUILD_PATH/$target/lily.axe"
if [ $dump_asm = 1 ]; then
hbc_flags="$hbc_flags --dump-asm"
out="/dev/stdout"
fi
done
[ -z "$linker" ] && die "Could not find zig, clang, or gcc. Please install any, or add to PATH."
mkdir -p "$BUILD_PATH/$target" || die "Failed to create build directory"
[ -w "$BUILD_PATH/$target" ] || die "$BUILD_PATH/$target is not writable"
hbc "$SCRIPT_DIR/src/main.hb" $hbc_flags "--target=unknown-virt-unknown" >"$out" ||
{
rm -f "$BUILD_PATH/$target/lily"
rm -f "$CHECKSUM_FILE"
die "Compilation failed"
}
else
if [ -z "$linker" ]; then
for cmd in "zig cc" clang gcc; do
if command -v "${cmd%% *}" >/dev/null 2>&1; then
linker=$cmd
break
fi
done
[ -z "$linker" ] && die "Could not find zig, clang, or gcc. Please install any, or add to PATH."
fi
if [ ! "$target" = "default" ]; then
hbc_flags="$hbc_flags --target=$target"
fi
mkdir -p "$BUILD_PATH/$target" || die "Failed to create build directory"
[ -w "$BUILD_PATH/$target" ] || die "$BUILD_PATH/$target is not writable"
hbc "$SCRIPT_DIR/src/main.hb" $hbc_flags >"$BUILD_PATH/$target/lily.o" ||
{
rm -f "$BUILD_PATH/$target/lily.o"
rm -f "$CHECKSUM_FILE"
die "Compilation failed"
}
$linker "$BUILD_PATH/$target/lily.o" -o "$BUILD_PATH/$target/lily"
fi
fi
if check_changes || [ ! -e "$out" ]; then
hbc "$SCRIPT_DIR/src/main.hb" $hbc_flags >"$BUILD_PATH/out.o" ||
{
rm -f "$BUILD_PATH/out.o"
rm -f "$CHECKSUM_FILE"
die "Compilation failed"
}
$linker "$BUILD_PATH/out.o" -o "$out"
strip -s "$out" || die "Stripping failed"
fi
if [ "$run" = 1 ]; then exec "$out"; fi
if [ "$dump_asm" = 1 ]; then objdump -d "$BUILD_PATH/out.o"; fi
}
build_hbvm() {
[ "$out" = "$DEFAULT_OUT" ] && out="$SCRIPT_DIR/target/lily.axe"
hbc_flags="$hbc_flags --target=unknown-virt-unknown"
if [ "$dump_asm" = 1 ]; then
hbc_flags="$hbc_flags --dump-asm"
out=/dev/stdout
fi
if check_changes || [ ! -w "$out" ]; then
hbc "$SCRIPT_DIR/src/main.hb" $hbc_flags >"$out" ||
{
rm -f "$CHECKSUM_FILE"
die "Compilation failed"
}
if [ ! "$target" = "unknown-virt-unknown" ]; then
if [ "$run" = 1 ]; then exec "$BUILD_PATH/$target/lily"; fi
if [ "$dump_asm" = 1 ]; then objdump -d "$BUILD_PATH/$target/lily.o"; fi
fi
}
@ -182,17 +161,9 @@ main() {
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"
printf "target := @use(\"./%s.hb\")\n\$DEBUG := %s" "$target" "$debug" >"$CONFIG_FILE"
check_hbc
case $target in
c_native) build_c_native ;;
hbvm_ableos) build_hbvm ;;
*) die "Invalid target: $target" ;;
esac
build
}
main "$@"

View file

@ -1,147 +0,0 @@
.{collections: .{SparseVec, Vec}, target, target_c_native, target_hbvm_ableos, DEBUG, Type, log} := @use("lib.hb")
Allocation := struct {
ptr: ^void,
len: uint,
}
// ! THIS ALLOCATOR IS TEMPORARY
SimpleAllocator := struct {
allocations: Vec(Allocation, RawAllocator),
raw: RawAllocator,
$new := fn(): Self {
raw := RawAllocator.new()
return .(Vec(Allocation, RawAllocator).new(&raw), raw)
}
deinit := fn(self: ^Self): void {
loop if self.allocations.len() == 0 break else {
alloced := self.allocations.pop()
if alloced == null continue
if target == target_c_native {
target.free(alloced.ptr)
} else if target == target_hbvm_ableos {
target.free(alloced.ptr, alloced.len)
}
}
self.allocations.deinit()
self.raw.deinit()
}
alloc := fn(self: ^Self, $T: type, count: uint): ?^T {
ptr := target.malloc(count * @sizeof(T))
if target == target_hbvm_ableos {
if ptr != null self.allocations.push(.(ptr, count * @sizeof(T)))
}
if DEBUG {
log.debug("allocated")
}
return @bitcast(ptr)
}
alloc_zeroed := fn(self: ^Self, $T: type, count: uint): ?^T {
ptr := target.calloc(count * @sizeof(T))
if target == target_hbvm_ableos {
if ptr != null self.allocations.push(.(ptr, count * @sizeof(T)))
}
return @bitcast(ptr)
}
realloc := fn(self: ^Self, $T: type, ptr: ^T, count: uint): ?^T {
if target == target_hbvm_ableos {
// temporary optimisation, ableos only gives whole pages.
// this prevents reallocating 1 page over and over
if count * @sizeof(T) < target.PAGE_SIZE return ptr
alloced := self._find_and_remove(@bitcast(ptr))
if alloced == null return null
new_ptr := target.realloc(@bitcast(ptr), alloced.len, count * @sizeof(T))
if new_ptr != null self.allocations.push(.(new_ptr, count * @sizeof(T))) else self.allocations.push(alloced)
return @bitcast(new_ptr)
} else if target == target_c_native {
new_ptr := target.realloc(@bitcast(ptr), count * @sizeof(T))
if DEBUG {
log.debug("reallocated")
}
return @bitcast(new_ptr)
}
}
free := fn(self: ^Self, $T: type, ptr: ^T): void {
if target == target_c_native {
target.free(@bitcast(ptr))
if DEBUG {
log.debug("freed")
}
} else if target == target_hbvm_ableos {
alloced := self._find_and_remove(@bitcast(ptr))
if alloced == null return;
target.free(@bitcast(ptr), alloced.len)
}
}
_find_and_remove := fn(self: ^Self, ptr: ^void): ?Allocation {
i := 0
loop if i == self.allocations.len() break else {
defer i += 1
alloced := self.allocations.get(i)
if alloced == null return null
if alloced.ptr == ptr {
_ = self.allocations.remove(i)
return alloced
}
}
return null
}
}
// ! THIS ALLOCATOR IS *ALSO* TEMPORARY
RawAllocator := struct {
ptr: ^void,
size: uint,
$new := fn(): Self return .(Type(^void).uninit(), 0)
deinit := fn(self: ^Self): void {
if target == target_c_native {
// ! (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)
} else if target == target_hbvm_ableos {
if self.size != 0 target.free(self.ptr, self.size)
};
*self = Self.new()
}
alloc := fn(self: ^Self, $T: type, count: uint): ?^T {
ptr := target.malloc(count * @sizeof(T))
if ptr != null {
self.ptr = ptr
self.size = count * @sizeof(T)
}
return @bitcast(ptr)
}
alloc_zeroed := fn(self: ^Self, $T: type, count: uint): ?^T {
ptr := target.calloc(count * @sizeof(T))
if ptr != null {
self.ptr = ptr
self.size = count * @sizeof(T)
}
return @bitcast(ptr)
}
realloc := fn(self: ^Self, $T: type, ptr: ^T, count: uint): ?^T {
if target == target_hbvm_ableos {
new_ptr := target.realloc(self.ptr, self.size, count * @sizeof(T))
if new_ptr != null {
self.ptr = new_ptr
self.size = count * @sizeof(T)
}
return @bitcast(new_ptr)
} else if target == target_c_native {
new_ptr := target.realloc(self.ptr, count * @sizeof(T))
if new_ptr != null {
self.ptr = new_ptr
self.size = count * @sizeof(T)
}
return @bitcast(new_ptr)
}
}
free := fn(self: ^Self, $T: type, ptr: ^T): void {
if target == target_c_native {
target.free(self.ptr)
} else if target == target_hbvm_ableos {
target.free(self.ptr, self.size)
}
}
}

157
src/lily/alloc.hb Normal file
View file

@ -0,0 +1,157 @@
std := @use("lib.hb");
.{Config, Target, Type, log, collections} := std;
.{Vec} := collections
Allocation := struct {
ptr: ^void,
len: uint,
}
// ! THIS ALLOCATOR IS TEMPORARY
SimpleAllocator := struct {
allocations: Vec(Allocation, RawAllocator),
raw: RawAllocator,
$new := fn(): Self {
raw := RawAllocator.new()
return .(Vec(Allocation, RawAllocator).new(&raw), raw)
}
deinit := fn(self: ^Self): void {
loop if self.allocations.len() == 0 break else {
alloced := self.allocations.pop()
if alloced == null continue
match Target.current() {
.LibC => Target.free(alloced.ptr),
.AbleOS => Target.free(alloced.ptr, alloced.len),
}
}
self.allocations.deinit()
self.raw.deinit()
log.debug("deinit: allocator")
}
alloc := fn(self: ^Self, $T: type, count: uint): ?^T {
ptr := Target.malloc(count * @sizeof(T))
if Target.current() == .AbleOS {
if ptr != null self.allocations.push(.(ptr, count * @sizeof(T)))
}
log.debug("allocated")
return @bitcast(ptr)
}
alloc_zeroed := fn(self: ^Self, $T: type, count: uint): ?^T {
ptr := Target.calloc(count * @sizeof(T))
if Target.current() == .AbleOS {
if ptr != null self.allocations.push(.(ptr, count * @sizeof(T)))
}
return @bitcast(ptr)
}
realloc := fn(self: ^Self, $T: type, ptr: ^T, count: uint): ?^T {
match Target.current() {
.AbleOS => {
// temporary optimisation, ableos only gives whole pages.
// this prevents reallocating 1 page over and over
if count * @sizeof(T) < Target.PAGE_SIZE return ptr
alloced := self._find_and_remove(@bitcast(ptr))
if alloced == null return null
new_ptr := Target.realloc(@bitcast(ptr), alloced.len, count * @sizeof(T))
if new_ptr != null {
self.allocations.push(.(new_ptr, count * @sizeof(T)))
} else {
self.allocations.push(alloced)
}
return @bitcast(new_ptr)
},
.LibC => {
new_ptr := Target.realloc(@bitcast(ptr), count * @sizeof(T))
log.debug("reallocated")
return @bitcast(new_ptr)
},
}
}
free := fn(self: ^Self, $T: type, ptr: ^T): void {
match Target.current() {
.AbleOS => {
alloced := self._find_and_remove(@bitcast(ptr))
if alloced != null Target.free(@bitcast(ptr), alloced.len)
},
.LibC => {
Target.free(@bitcast(ptr))
},
}
log.debug("freed")
}
_find_and_remove := fn(self: ^Self, ptr: ^void): ?Allocation {
i := 0
loop if i == self.allocations.len() break else {
defer i += 1
alloced := self.allocations.get(i)
if alloced == null return null
if alloced.ptr == ptr {
_ = self.allocations.remove(i)
return alloced
}
}
return null
}
}
// ! THIS ALLOCATOR IS *ALSO* TEMPORARY
RawAllocator := struct {
ptr: ^void,
size: uint,
$new := fn(): Self return .(Type(^void).uninit(), 0)
deinit := fn(self: ^Self): void {
self.free(void, Type(^void).uninit());
*self = Self.new()
log.debug("deinit: raw allocator")
}
alloc := fn(self: ^Self, $T: type, count: uint): ?^T {
ptr := Target.malloc(count * @sizeof(T))
if ptr != null {
self.ptr = ptr
self.size = count * @sizeof(T)
}
log.debug("allocated raw")
return @bitcast(ptr)
}
alloc_zeroed := fn(self: ^Self, $T: type, count: uint): ?^T {
ptr := Target.calloc(count * @sizeof(T))
if ptr != null {
self.ptr = ptr
self.size = count * @sizeof(T)
}
return @bitcast(ptr)
}
realloc := fn(self: ^Self, $T: type, ptr: ^T, count: uint): ?^T {
if self.size == 0 return null
log.debug("reallocated raw")
match Target.current() {
.LibC => {
new_ptr := Target.realloc(self.ptr, count * @sizeof(T))
if new_ptr != null {
self.ptr = new_ptr
self.size = count * @sizeof(T)
}
return @bitcast(new_ptr)
},
.AbleOS => {
new_ptr := Target.realloc(self.ptr, self.size, count * @sizeof(T))
if new_ptr != null {
self.ptr = new_ptr
self.size = count * @sizeof(T)
}
return @bitcast(new_ptr)
},
}
}
// ! INLINING THIS FUNCTION CAUSES MISCOMPILATION!! DO NOT INLINE IT!! :) :) :)
free := fn(self: ^Self, $T: type, ptr: ^T): void {
if self.size == 0 return;
match Target.current() {
.LibC => Target.free(self.ptr),
.AbleOS => Target.free(self.ptr, self.size),
}
log.debug("freed raw")
}
}

View file

@ -1,4 +1,4 @@
.{target, target_c_native, target_hbvm_ableos, memmove, Type} := @use("../lib.hb");
.{memmove, Type, log, alloc} := @use("../lib.hb");
Vec := fn($T: type, $A: type): type return struct {
slice: []T,
@ -10,6 +10,11 @@ Vec := fn($T: type, $A: type): type return struct {
if self.cap > 0 self.allocator.free(T, self.slice.ptr)
self.slice = Type([]T).uninit()
self.cap = 0
if A == alloc.RawAllocator {
log.debug("deinit: vec (w/ raw allocator)")
} else {
log.debug("deinit: vec (w/ allocator)")
}
}
push := fn(self: ^Self, value: T): void {
if self.slice.len == self.cap {

89
src/lily/lib.hb Normal file
View file

@ -0,0 +1,89 @@
Version := struct {
major: uint,
minor: uint,
patch: uint,
}
$VERSION := Version(0, 0, 4)
target_ableos := @use("targets/ableos.hb")
target_libc := @use("targets/libc.hb")
Config := struct {
$DEBUG := false
$DEBUG_ASSERTIONS := false
$MIN_LOGLEVEL := log.LogLevel.Info
$debug := fn(): bool return Config.DEBUG
$debug_assertions := fn(): bool return Config.DEBUG | Config.DEBUG_ASSERTIONS
$min_loglevel := fn(): log.LogLevel {
if Config.debug() return .Debug
return Config.MIN_LOGLEVEL
}
}
Target := enum {
LibC,
AbleOS,
$current := fn(): Self {
// This captures all HBVM targets, but for now only AbleOS is supported
if @target("*-virt-unknown") {
return .AbleOS
}
// Assume that unknown targets have libc
return .LibC
}
$Lib := fn(): type {
match Self.current() {
.LibC => return target_libc,
.AbleOS => return target_ableos,
}
}
/* ! memmove, memcpy, memset, exit, currently suffixed with `_w` to distinguish them from the wrapper functions */;
.{malloc, calloc, realloc, free, memmove: memmove_w, memcpy: memcpy_w, memset: memset_w, exit: exit_w, getrandom} := Target.Lib();
.{printf_str} := Target.Lib();
.{PAGE_SIZE, LogMsg} := Target.Lib()
}
// ----------------------------------------------------
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");
.{print, printf} := log;
.{Type, TypeOf} := @use("type.hb")
// ! (compiler) bug: inlining here crashes the parser. nice.
// ! (c_native) (compiler) bug: NOT inlining here makes it sometimes not work
$panic := fn(message: ?[]u8): never {
if message != null log.error(message) else log.error("The program called panic.")
exit(1)
}
// ! exit, memcpy, memmove, and memset are all temporary wrapper functions
$exit := fn(code: int): never {
Target.exit_w(code)
die
}
$memcpy := fn(dest: @Any(), src: @Any(), size: uint): void {
if TypeOf(dest).kind() != .Pointer | TypeOf(src).kind() != .Pointer @error("memcpy requires a pointer")
Target.memcpy_w(@bitcast(dest), @bitcast(src), size)
}
$memmove := fn(dest: @Any(), src: @Any(), size: uint): void {
if TypeOf(dest).kind() != .Pointer | TypeOf(src).kind() != .Pointer @error("memmove requires a pointer")
Target.memmove_w(@bitcast(dest), @bitcast(src), size)
}
$memset := fn(dest: @Any(), src: u8, size: uint): void {
if TypeOf(dest).kind() != .Pointer @error("memset requires a pointer")
Target.memset_w(@bitcast(dest), src, size)
}

View file

@ -1,4 +1,4 @@
.{target, target_c_native, target_hbvm_ableos, fmt} := @use("lib.hb")
.{Config, Target, fmt} := @use("lib.hb")
LogLevel := enum {
Error,
@ -9,28 +9,27 @@ LogLevel := enum {
}
log := fn(level: LogLevel, str: []u8): void {
if target == target_hbvm_ableos {
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.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),
}
if level > Config.min_loglevel() return;
match Target.current() {
.LibC => match level {
.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),
},
.AbleOS => return @eca(3, 1, Target.LogMsg.(level, str.ptr, str.len), @sizeof(Target.LogMsg)),
}
}
// it's good enough i guess. dont write more than 4096 chars or you will explode.
print_buffer := @embed("../assets/zeroed")
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)
match Target.current() {
.LibC => Target.printf_str("%.*s\n\0".ptr, any.len, any.ptr),
.AbleOS => info(any),
}
} else {
// limits len to size of buffer - 1

View file

@ -1,4 +1,4 @@
.{target, TypeOf, Type} := @use("lib.hb")
.{Target, TypeOf, Type} := @use("lib.hb")
// ! NON CRYPTOGRAPHIC, TEMPORARY
SimpleRandom := struct {
@ -17,11 +17,11 @@ SimpleRandom := struct {
}
if T.is_bool() {
a: A = idk
target.getrandom(@bitcast(&a), 1)
Target.getrandom(@bitcast(&a), 1)
return @bitcast(a & @as(u8, 1))
}
a: A = idk
target.getrandom(@bitcast(&a), T.size())
Target.getrandom(@bitcast(&a), T.size())
return a
}
/// Accepts any type for min and max (as long as it is the same for both).
@ -32,8 +32,8 @@ SimpleRandom := struct {
$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()),
.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."),
}
}

View file

@ -1,4 +1,4 @@
.{log: .{LogLevel}} := @use("../lib.hb")
.{LogLevel} := @use("../lib.hb").std.log
$PAGE_SIZE := 4096

View file

@ -1,52 +1,3 @@
Version := struct {
major: uint,
minor: uint,
patch: uint,
}
$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_hbvm_ableos := @use("target/hbvm_ableos.hb")
target_c_native := @use("target/c_native.hb");
.{DEBUG, target} := @use("target/config.hb")
// ! exit, memcpy, memmove, and memset are all temporary wrapper functions
$exit := fn(code: int): never {
target.exit(code)
die
}
// ! (compiler) bug: inlining here crashes the parser. nice.
// ! (c_native) (compiler) bug: NOT inlining here makes it sometimes not work
$panic := fn(message: ?[]u8): never {
if message != null log.error(message) else log.error("The program called panic.")
exit(1)
}
$memcpy := fn(dest: @Any(), src: @Any(), size: uint): void {
if TypeOf(dest).kind() != .Pointer | TypeOf(src).kind() != .Pointer @error("memcpy requires a pointer")
target.memcpy(@bitcast(dest), @bitcast(src), size)
}
$memmove := fn(dest: @Any(), src: @Any(), size: uint): void {
if TypeOf(dest).kind() != .Pointer | TypeOf(src).kind() != .Pointer @error("memmove requires a pointer")
target.memmove(@bitcast(dest), @bitcast(src), size)
}
$memset := fn(dest: @Any(), src: u8, size: uint): void {
if TypeOf(dest).kind() != .Pointer @error("memset requires a pointer")
target.memset(@bitcast(dest), src, size)
}
RawKind := enum {
Builtin,
Struct,

View file

@ -1,4 +1,4 @@
lily := @use("lib/lib.hb");
lily := @use("lily/lib.hb")
Allocator := lily.alloc.SimpleAllocator
Vec := lily.collections.Vec
@ -18,13 +18,13 @@ main := fn(argc: uint, argv: []^void): uint {
// 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 {
loop if i == 10 break else {
defer i += 1
vec.push(rand.any(uint))
lily.log.info("pushed to vec")
}
// ! (c_native) this causes a compiler bug in lily.fmt.fmt_int
// ! (libc) this causes a compiler bug in lily.fmt.fmt_int
// lily.log.print(100)
lily.log.print(true)