feat(errors): panic(msg) + assert(cond, msg) with file:line — no raw crashes (#8)
RFC decision (the split the issue recommended): programmer bugs abort loud and located; recoverable failures become values. This ships the first half. panic(msg) prints `file:line: panic: <msg>` to stderr and aborts the process with exit code 1 — a clear, located error instead of a segfault or a silent wrong result. assert(cond, msg) is the guarded form: it aborts with `file:line: assertion failed: <msg>` only when cond is false, otherwise execution continues. The location is baked in at compile time (the call node carries its source line, g_src_name carries the file); the message is any string. Both lower in emit_call to an fprintf-to-stderr + exit(1) + unreachable tail (assert branches on the condition first). @fprintf and the format constant are declared on demand (g_uses_panic), so a program that never panics is unchanged — and the compiler's own source uses neither, so the C-free bootstrap fixpoint holds. - panic/assert registered as builtins across the vocabulary (ludic_syntax.h, the JetBrains lexer, the TextMate grammar) and documented (docs/language/builtins/) - examples/library/errors.ludic covers the success path (asserts hold, program runs to the end); a panic_case in the suite covers the failure path (non-zero exit + the located stderr message). x test is now 69 checks. Deferred: recoverable failures as `try`/`else` values (needs the tagged-union type system, #1) and a top-level `recover` for the dev game loop. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
ea2c6ab246
commit
c7c8e2779c
13 changed files with 5102 additions and 4778 deletions
28
examples/library/errors.ludic
Normal file
28
examples/library/errors.ludic
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
# errors.ludic — error handling (issue #8): assert(cond, msg) guards an invariant
|
||||
# and aborts with a located message when it is broken; panic(msg) aborts outright.
|
||||
# This is the SUCCESS path — every assertion here holds, so nothing aborts and the
|
||||
# program runs to the end, printing:
|
||||
# 5 10 0 7 1
|
||||
# The failure path (a panic exits non-zero with `file:line: panic: …` on stderr)
|
||||
# is exercised separately in the test runner.
|
||||
program Errors {
|
||||
function checked_div(a: int, b: int) -> int {
|
||||
assert(b != 0, "division by zero")
|
||||
return a / b
|
||||
}
|
||||
|
||||
function clampi(v: int, lo: int, hi: int) -> int {
|
||||
assert(lo <= hi, "lo must not exceed hi")
|
||||
if v < lo { return lo }
|
||||
if v > hi { return hi }
|
||||
return v
|
||||
}
|
||||
|
||||
entry {
|
||||
print(checked_div(10, 2)) # 5
|
||||
print(clampi(15, 0, 10)) # 10 (above hi)
|
||||
print(clampi(0 - 3, 0, 10)) # 0 (below lo)
|
||||
print(clampi(7, 0, 10)) # 7 (in range)
|
||||
print(1) # reached: every assertion held
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue