refactor: index preprocessed process arguments directly

This commit is contained in:
2026-10-23 12:07:23 +02:00
parent c1e03632ed
commit 0821b84bc3
5 changed files with 10 additions and 54 deletions
+2 -2
View File
@@ -29,9 +29,9 @@ main proc() int {
this prints `42`. interpreted `main` must be a top-level function with no parameters; nonvoid results are printed, not used as process exit statuses. this prints `42`. interpreted `main` must be a top-level function with no parameters; nonvoid results are printed, not used as process exit statuses.
the native bootstrap accepts either a no-argument `main` or `main proc(init process.Init)`. it supplies `init.io` and `init.args` directly from native startup `argc`/`argv`. `process.Args` borrows the argument pointer array and zero-terminated strings without allocation or copying; native startup storage lasts for the process lifetime. `process.argument_count(init.args)` includes the executable name, and `process.argument(init.args, index)` returns a borrowed `[;0]u8` or `null` when out of range. callers can supply their own `process.Args { values = pointers[..] }` for testing. the native bootstrap accepts either a no-argument `main` or `main proc(init process.Init)`. it supplies `init.io` and `init.args` from native startup `argc`/`argv`. startup scans each argument once into a heap-allocated array of borrowed zero-terminated slices (`process.Args.values: [][;0]u8`), without copying strings. the descriptor array and borrowed strings remain valid until `main` returns; startup then frees the descriptors, including on a fallible return. allocation failure exits with status `2`. `init.args.values.len` includes the executable name, and `init.args.values[index]` directly accesses an argument after a length guard. callers can supply their own `process.Args { values = slices[..] }` for testing.
source loading uses the supplied `io.Io` for opening, reading, and closing files. `io.open` accepts a borrowed zero-terminated slice (`[;0]u8`), including arguments returned by `process.argument` without copying their storage. the bootstrap supports checked sentinel-slice construction with `pointer[..length;0]`. `io.read_all(allocator, reader, maximum)` reads to EOF into a caller-owned `ArrayList(u8)`; release it with `arraylist.deinit`. it frees partial storage on error and consumes one extra byte to distinguish an exact-size input from a limit violation. the driver maps `limit_exceeded` to `source_too_large` at its `u32` source-size limit. source loading uses the supplied `io.Io` for opening, reading, and closing files. `io.open` accepts a borrowed zero-terminated slice (`[;0]u8`), including `init.args.values[index]` without copying its storage. the bootstrap supports checked sentinel-slice construction with `pointer[..length;0]`. `io.read_all(allocator, reader, maximum)` reads to EOF into a caller-owned `ArrayList(u8)`; release it with `arraylist.deinit`. it frees partial storage on error and consumes one extra byte to distinguish an exact-size input from a limit violation. the driver maps `limit_exceeded` to `source_too_large` at its `u32` source-size limit.
the parser supports arithmetic expressions with nested parenthesized groups, such as `a + (b - c) * 2 / 3`. groups control precedence without adding AST nodes; inner expression spans exclude the grouping parentheses. parsing accepts integer division syntax, but type checking requires float operands for `/`. the parser supports arithmetic expressions with nested parenthesized groups, such as `a + (b - c) * 2 / 3`. groups control precedence without adding AST nodes; inner expression spans exclude the grouping parentheses. parsing accepts integer division syntax, but type checking requires float operands for `/`.
+6 -11
View File
@@ -46,9 +46,9 @@ DriverError :: alias (
main proc(init process.Init) i32 { main proc(init process.Init) i32 {
return command(mem.c_allocator, init.io, init.args) catch |err| { return command(mem.c_allocator, init.io, init.args) catch |err| {
if (process.argument(init.args, 2)) |path| { if (init.args.values.len > 2) {
io.print("{s}: infrastructure error: {s}\n", io.stderr(init.io), { io.print("{s}: infrastructure error: {s}\n", io.stderr(init.io), {
path, infrastructure_message(err), init.args.values[2], infrastructure_message(err),
}) catch |_| {} }) catch |_| {}
} else { } else {
io.print("honey: infrastructure error: {s}\n", io.stderr(init.io), { io.print("honey: infrastructure error: {s}\n", io.stderr(init.io), {
@@ -78,20 +78,15 @@ infrastructure_message proc(error DriverError) []u8 {
@hide:file @hide:file
command proc(allocator mem.Allocator, runtime io.Io, args process.Args) i32 ! DriverError { command proc(allocator mem.Allocator, runtime io.Io, args process.Args) i32 ! DriverError {
if (process.argument_count(args) != 3) { if (args.values.len != 3) {
try io.print("usage: honey run <file>\n", io.stderr(runtime), {}) try io.print("usage: honey run <file>\n", io.stderr(runtime), {})
return 2 return 2
} }
if (process.argument(args, 1)) |operation| { if (!mem.eql(args.values[1], "run")) {
if (!mem.eql(operation, "run")) { try io.print("usage: honey run <file>\n", io.stderr(runtime), {})
try io.print("usage: honey run <file>\n", io.stderr(runtime), {})
return 2
}
} else {
return 2 return 2
} }
if (process.argument(args, 2)) |path| return try run_file(allocator, runtime, path) return try run_file(allocator, runtime, args.values[2])
return 2
} }
@hide:file @hide:file
-12
View File
@@ -1,12 +0,0 @@
argument_count proc(args Args) usize {
return args.values.len
}
#! returns a borrowed, zero-terminated slice, valid while the supplied argument storage lives.
argument proc(args Args, index usize) ?[;0]u8 {
if (index >= argument_count(args)) return null
pointer :: args.values[index]
length := usize(0)
while (pointer[length] != 0) length += 1
return pointer[..length;0]
}
-27
View File
@@ -1,27 +0,0 @@
import "@std/testing"
import "@std/mem"
borrows_supplied_arguments test {
first [3;0]mut u8 := ['r', 'u', 'n']
empty [0;0]u8 := []
spaced [9;0]u8 := ['t', 'w', 'o', ' ', 'w', 'o', 'r', 'd', 's']
pointers [3][*;0]u8 :: [(&first).ptr, (&empty).ptr, (&spaced).ptr]
args :: Args{ values = pointers[..] }
try testing.expect_equal(usize(3), argument_count(args))
if (argument(args, 0)) |value| {
first[0] = 'f'
try testing.expect(mem.eql(value, "fun"))
} else { try testing.expect(false) }
if (argument(args, 1)) |value| {
try testing.expect_equal(usize(0), value.len)
} else { try testing.expect(false) }
if (argument(args, 2)) |value| {
try testing.expect(mem.eql(value, "two words"))
} else { try testing.expect(false) }
try testing.expect(argument(args, 3) == null)
try testing.expect(argument(args, maxval!(usize)) == null)
none :: Args{ values = pointers[0..0] }
try testing.expect_equal(usize(0), argument_count(none))
try testing.expect(argument(none, 0) == null)
}
+2 -2
View File
@@ -5,7 +5,7 @@ Init :: struct {
args Args args Args
} }
#! borrows the argument pointer array and its zero-terminated strings; owns no storage. #! borrows argument slices and their zero-terminated strings, valid until main returns.
Args :: struct { Args :: struct {
values [][*;0]u8 values [][;0]u8
} }