Phase 7b: null literal + x == null (retire ptr_null/ptr_is_null)
`null` is now a real pointer literal and null-tests are comparisons, instead of `ptr_null()` and `ptr_is_null(x)`: ptr_null() -> null ptr_is_null(x) -> (x == null) not ptr_is_null(x) -> (x != null) Mechanics: a new E_NULL primary (`null`, like true/false) lowers to the `null` pointer; emit_bin's comparison path now picks `ptr` vs `i32` from operand type (via llty), so `==`/`!=` work on any pointer/record/slice. The two intrinsics are deleted. Two reseeds: (A) add the literal + ptr comparison keeping the intrinsics; (B) migrate all 182 call sites (compiler + runtime, via a balanced-paren script that skips string-literal args and rewrites `not ptr_is_null` to `!= null`) and delete the intrinsics. Node/Val/Buf/Tok field defaults now read `ptr = null`. Vocabulary drops the two from LUDIC_INTRINSICS; `null` joins true/false as a language constant (grammar + ludic_syntax.h). LANGUAGE.md notes the literal. Reseeded (21664 lines); C-free fixpoint holds; goldens identical; 17/17; vocab + doc-fences clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
ddb1af3008
commit
70ab79a4e9
34 changed files with 5064 additions and 5124 deletions
|
|
@ -62,6 +62,7 @@ fn p_primary() -> Node {
|
|||
if t.kind == TK_ID {
|
||||
if streq(t.text, "true") { let n = node(E_BOOL); n.ival = 1; pi = pi + 1; return n }
|
||||
if streq(t.text, "false") { let n = node(E_BOOL); n.ival = 0; pi = pi + 1; return n }
|
||||
if streq(t.text, "null") { pi = pi + 1; return node(E_NULL) }
|
||||
if streq(t.text, "new") { pi = pi + 1; let n = node(E_NEW); n.s = ptype(); return n }
|
||||
let n = node(E_ID); n.s = t.text; pi = pi + 1; return n
|
||||
}
|
||||
|
|
@ -275,13 +276,13 @@ fn already_loaded(full: ptr) -> bool {
|
|||
# `@pure`, `@deterministic`, … — one channel, not a zoo of prefix keywords.
|
||||
fn parse_one_decl() -> void {
|
||||
var is_export = false
|
||||
var qspec: Node = ptr_null()
|
||||
var onspawn_model: ptr = ptr_null()
|
||||
var ondespawn_model: ptr = ptr_null()
|
||||
var onattach_prop: ptr = ptr_null()
|
||||
var onenable_prop: ptr = ptr_null()
|
||||
var ondisable_prop: ptr = ptr_null()
|
||||
var hook_phase: ptr = ptr_null() # @OnStart / @OnQuit override the phase
|
||||
var qspec: Node = null
|
||||
var onspawn_model: ptr = null
|
||||
var ondespawn_model: ptr = null
|
||||
var onattach_prop: ptr = null
|
||||
var onenable_prop: ptr = null
|
||||
var ondisable_prop: ptr = null
|
||||
var hook_phase: ptr = null # @OnStart / @OnQuit override the phase
|
||||
while is_op("@") {
|
||||
pi = pi + 1; let a = eat_id() # collect a leading @annotation
|
||||
if streq(a, "export") { is_export = true }
|
||||
|
|
@ -309,13 +310,13 @@ fn parse_one_decl() -> void {
|
|||
if is_id("model") { push(prog, parse_archetype()); return }
|
||||
if is_id("handler") {
|
||||
let h = parse_system()
|
||||
if not ptr_is_null(onspawn_model) { register_onspawn(onspawn_model, h.a); return } # spawn hook
|
||||
if not ptr_is_null(ondespawn_model) { register_ondespawn(ondespawn_model, h.a); return } # despawn hook
|
||||
if not ptr_is_null(onattach_prop) { register_onattach(onattach_prop, h.a); return } # attach hook
|
||||
if not ptr_is_null(onenable_prop) { register_onenable(onenable_prop, h.a); return } # enable hook
|
||||
if not ptr_is_null(ondisable_prop) { register_ondisable(ondisable_prop, h.a); return } # disable hook
|
||||
if not ptr_is_null(hook_phase) { h.ty = hook_phase } # @OnStart/@OnQuit
|
||||
if not ptr_is_null(qspec) { # @Queries wraps the body in its S_QUERY
|
||||
if (onspawn_model != null) { register_onspawn(onspawn_model, h.a); return } # spawn hook
|
||||
if (ondespawn_model != null) { register_ondespawn(ondespawn_model, h.a); return } # despawn hook
|
||||
if (onattach_prop != null) { register_onattach(onattach_prop, h.a); return } # attach hook
|
||||
if (onenable_prop != null) { register_onenable(onenable_prop, h.a); return } # enable hook
|
||||
if (ondisable_prop != null) { register_ondisable(ondisable_prop, h.a); return } # disable hook
|
||||
if (hook_phase != null) { h.ty = hook_phase } # @OnStart/@OnQuit
|
||||
if (qspec != null) { # @Queries wraps the body in its S_QUERY
|
||||
qspec.a = h.a
|
||||
let wrap = node(N_BLOCK); push(wrap.kids, qspec); h.a = wrap
|
||||
}
|
||||
|
|
@ -336,7 +337,7 @@ fn do_import(rel: ptr) -> void {
|
|||
if already_loaded(full) { return }
|
||||
push(loaded_paths, full)
|
||||
let src = read_file(full)
|
||||
if ptr_is_null(src) { perr(sconcat("cannot open import ", full)) }
|
||||
if (src == null) { perr(sconcat("cannot open import ", full)) }
|
||||
let saved_toks = toks; let saved_pi = pi; let saved_dir = cur_dir
|
||||
cur_dir = dir_of(full)
|
||||
lex(src) # resets the global token stream
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue