Critical fixes

This commit is contained in:
allexanderbergmns
2026-08-25 17:34:47 +02:00
parent 415dec40a2
commit 3e1e9bd08a
12 changed files with 361 additions and 19 deletions
+60
View File
@@ -0,0 +1,60 @@
// cli_tool.vr — a miniature daily-driver CLI tool demonstrating the pieces a
// real command-line program needs: flag parsing, interactive input, and typed
// error handling.
//
// import cli
// require_name() # (see below)
//
// vr run examples/cli_tool.vr --name alice --verbose
// vr run examples/cli_tool.vr --name=alice file1 file2
//
// Exit codes: 0 on success, 1 on a runtime (bad input) error, 2 on usage/flag
// errors.
import cli
fn main() {
// --- flag parsing -------------------------------------------------------
let name = cli.flag("name", "world") // match --name/-n and --name=value
let verbose = cli.has("verbose") // match --verbose and -v
let files = cli.positional()
if verbose {
println("files=" + str(files))
}
// --- typed throw / catchable errors -----------------------------------
try {
if len(name) == 0 {
throw { kind: "usage", detail: "name must not be empty" }
}
} catch err {
println("error: " + err)
exit(2)
}
println("hello, " + name + "!")
// --- interactive input -------------------------------------------------
println("-- a quick questionnaire (pipe answers, or run interactively) --")
let age = input("your age? ")
if age == "" {
println("(no answer supplied)")
} else if int_age(age) > 0 {
println("noted (born ~" + str(current_year() - int_age(age)) + ")")
} else {
println("noted")
}
println("done.")
}fn int_age(s) {
try {
return int(s)
} catch e {
return 0
}
}
fn current_year() {
// 2026 — keep in sync; or used as a stand-in for now().year
return 2026
}
+38 -1
View File
@@ -319,7 +319,7 @@ fn test_module_imports() {
}
assert os.base("a/b/c.vr") == "c.vr"
assert os.dir("a/b/c.vr") == "a/b"
assert len(os.glob("lib/*.vr")) == 9
assert len(os.glob("lib/*.vr")) == 10
let tmp = os.join(os.cwd(), "tmp_test.txt")
os.write_lines(tmp, ["x", "y"])
assert len(os.read_lines(tmp)) == 2
@@ -621,6 +621,43 @@ fn worker_boom() {
return x
}
fn test_string_coercion() {
// String + any value stringifies, so error messages "just work"
let e = { code: 403, msg: "forbidden" }
assert "err: " + e == "err: {code: 403, msg: forbidden}"
assert "n=" + 42 == "n=42"
assert "f=" + 3.5 == "f=3.5"
assert "a=" + [1, 2] == "a=[1, 2]"
assert "b=" + none == "b=none"
}
fn test_typed_throw() {
// throw and catch arbitrary values (structs, ints, floats)
mut got = ""
try {
throw { reason: "boom" }
} catch err {
got = "" + err
}
assert got == "{reason: boom}"
mut gotn = -1
try {
throw 99
} catch err {
gotn = int(err)
}
assert gotn == 99
}
fn test_flag_parsing() {
// flags are read from args() at runtime; here we check the lookup helpers
// behave with no flags (defaults) — the actual parse is exercised in CI.
assert flag_val("missing") == ""
assert flag_has("missing") == 0
assert len(flag_positional()) == 0
}
fn test_failing() {
// this one is meant to fail — shows up in `vr test` output
assert 1 == 2