Adds the Http.* namespace and its transport, the HTTP client from #6. The JSON companion the proposal called for already shipped as Json.* (#44). - runtime/native/http.ll: the macOS transport — NSURLConnection driven through the objc runtime C ABI (no ObjC/C source), run on a detached pthread so the frame never blocks; TLS is the system's, on by default. A fixed slot pool holds each in-flight request; the worker publishes status/body/response behind an atomic done flag (release/acquire). Spliced + Foundation linked only when a program uses Http.*. - runtime/native/http.ludic: the Http.* runtime — get/post/request, the open/set/body/send builder, poll/status/ok/text/body_len/header/free, plus a pure-Ludic response parser (Http.parse + case-insensitive header lookup) that is transport-independent and portable. - compiler: Http.* dispatch, g_uses_http splice, hs_* transport intrinsics + declarations, the conditional Foundation link, and a new \r string/char escape the protocol needs. - docs: a full docs/language/http section (16 pages); check-impl/check-docs green. - test: examples/library/http.ludic self-asserts the parser offline (Darwin-gated build via the canonical path, since it links Foundation). Verified end to end against real endpoints: HTTPS GET (200 + headers + body) and POST (body + custom header). HTTP is out-of-band and never feeds the deterministic sim. Reseeded; suites green (81 + 29). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
90 lines
7.2 KiB
Text
90 lines
7.2 KiB
Text
# emit_intrin2.ludic — the rest of the low-level intrinsics: raw memory, fixed
|
|
# memory access, pointer arithmetic, stdio, time, and the windowing hooks.
|
|
# Matches compiler/back/ir_intrin.c. Reached from emit_intrinsic's fall-through.
|
|
|
|
var g_windowed: bool = false # headless by default (games read stdin / dump PPM)
|
|
|
|
function is_intrinsic2(name: pointer) -> bool {
|
|
if (name == "free") or (name == "fill") { return true }
|
|
if (name == "offset") or (name == "read_char") { return true }
|
|
if (name == "as_fixed") or (name == "as_int") { return true }
|
|
if (name == "is_windowed") or (name == "game_title") { return true }
|
|
if (name == "win_open") or (name == "win_poll") or (name == "win_present") { return true }
|
|
if (name == "win_running") or (name == "win_close") { return true }
|
|
if (name == "win_held") or (name == "win_mouse") { return true } # #50 device layer
|
|
if (name == "win_pad") or (name == "win_touch") { return true } # #51 gamepad / touch
|
|
if (name == "snd_load") or (name == "snd_play") or (name == "snd_stop") { return true } # #22 audio
|
|
if (name == "snd_playing") or (name == "snd_set_volume") or (name == "snd_set_rate") { return true }
|
|
if (name == "hs_req_new") or (name == "hs_req_header") or (name == "hs_req_body") { return true } # #6 HTTP
|
|
if (name == "hs_send") or (name == "hs_done") or (name == "hs_status") or (name == "hs_body") { return true }
|
|
if (name == "hs_blen") or (name == "hs_header") or (name == "hs_free") { return true }
|
|
return false
|
|
}
|
|
|
|
function emit_intrinsic2(name: pointer, e: Node) -> Val {
|
|
if (name == "free") {
|
|
let p = arg_code(e, 0); emit(" call void @free(ptr "); emit(p); emit(")\n"); return val("0", "void")
|
|
}
|
|
if (name == "fill") {
|
|
let p = arg_code(e, 0); let v = arg_code(e, 1); let n = arg_code(e, 2)
|
|
let w = emit_bind(`zext i32 {n} to i64`)
|
|
emit(" call ptr @memset(ptr "); emit(p); emit(", i32 "); emit(v); emit(", i64 "); emit(w); emit(")\n")
|
|
return val("0", "void")
|
|
}
|
|
if (name == "offset") {
|
|
let p = arg_code(e, 0); let n = arg_code(e, 1)
|
|
let g = nreg()
|
|
emit(" "); emit(g); emit(" = getelementptr inbounds i8, ptr "); emit(p); emit(", i32 "); emit(n); emit("\n")
|
|
return val(g, "pointer")
|
|
}
|
|
if (name == "read_char") { return val(emit_bind("call i32 @getchar()"), "int") }
|
|
if (name == "as_fixed") { let a = emit_expr(e.kids[0]); return val(a.code, "fixed") }
|
|
if (name == "as_int") { let a = emit_expr(e.kids[0]); return val(a.code, "int") }
|
|
if (name == "is_windowed") { if g_windowed { return val("1", "bool") }; return val("0", "bool") }
|
|
if (name == "game_title") { return val("@.gametitle", "string") }
|
|
# windowing hooks — declared external; only reached on the is_windowed() branch
|
|
if (name == "win_poll") { return val(emit_bind("call i32 @win_poll()"), "int") }
|
|
if (name == "win_running") { return val(emit_bind("call i32 @win_running()"), "bool") }
|
|
if (name == "win_open") {
|
|
let a = arg_code(e, 0); let b = arg_code(e, 1); let c = arg_code(e, 2); let d = arg_code(e, 3)
|
|
emit(" call void @win_open(i32 "); emit(a); emit(", i32 "); emit(b); emit(", i32 "); emit(c); emit(", ptr "); emit(d); emit(")\n")
|
|
return val("0", "void")
|
|
}
|
|
if (name == "win_present") {
|
|
let a = arg_code(e, 0); let b = arg_code(e, 1); let c = arg_code(e, 2)
|
|
emit(" call void @win_present(ptr "); emit(a); emit(", i32 "); emit(b); emit(", i32 "); emit(c); emit(")\n")
|
|
return val("0", "void")
|
|
}
|
|
if (name == "win_close") { emit(" call void @win_close()\n"); return val("0", "void") }
|
|
# #50 — fill a caller buffer with the platform device state (windowed only,
|
|
# DCE'd headless). win_held: an 8-word held-key bitset; win_mouse: [x, y,
|
|
# button-mask, wheel-delta].
|
|
if (name == "win_held") { let a = arg_code(e, 0); emit(" call void @win_held(ptr "); emit(a); emit(")\n"); return val("0", "void") }
|
|
if (name == "win_mouse") { let a = arg_code(e, 0); emit(" call void @win_mouse(ptr "); emit(a); emit(")\n"); return val("0", "void") }
|
|
# #51 — win_pad: 6 words/pad [connected, mask, lx, ly, rx, ry]; win_touch: 3
|
|
# words/point [active, x, y]. Both windowed-only, DCE'd headless.
|
|
if (name == "win_pad") { let a = arg_code(e, 0); emit(" call void @win_pad(ptr "); emit(a); emit(")\n"); return val("0", "void") }
|
|
if (name == "win_touch") { let a = arg_code(e, 0); emit(" call void @win_touch(ptr "); emit(a); emit(")\n"); return val("0", "void") }
|
|
# #22 audio backend — AVAudioPlayer via audio.ll; windowed-only, DCE'd headless.
|
|
if (name == "snd_load") { let a = arg_code(e, 0); return val(emit_bind(`call ptr @snd_load(ptr {a})`), "pointer") }
|
|
if (name == "snd_play") {
|
|
let a = arg_code(e, 0); let b = arg_code(e, 1); let c = arg_code(e, 2); let d = arg_code(e, 3)
|
|
emit(" call void @snd_play(ptr "); emit(a); emit(", i32 "); emit(b); emit(", i32 "); emit(c); emit(", i32 "); emit(d); emit(")\n"); return val("0", "void")
|
|
}
|
|
if (name == "snd_stop") { let a = arg_code(e, 0); emit(" call void @snd_stop(ptr "); emit(a); emit(")\n"); return val("0", "void") }
|
|
if (name == "snd_playing") { let a = arg_code(e, 0); return val(emit_bind(`call i32 @snd_playing(ptr {a})`), "int") }
|
|
if (name == "snd_set_volume") { let a = arg_code(e, 0); let b = arg_code(e, 1); emit(" call void @snd_set_volume(ptr "); emit(a); emit(", i32 "); emit(b); emit(")\n"); return val("0", "void") }
|
|
if (name == "snd_set_rate") { let a = arg_code(e, 0); let b = arg_code(e, 1); emit(" call void @snd_set_rate(ptr "); emit(a); emit(", i32 "); emit(b); emit(")\n"); return val("0", "void") }
|
|
# #6 HTTP transport — NSURLConnection on a background thread (http.ll).
|
|
if (name == "hs_req_new") { let a = arg_code(e, 0); let b = arg_code(e, 1); return val(emit_bind(`call ptr @hs_req_new(ptr {a}, ptr {b})`), "pointer") }
|
|
if (name == "hs_req_header") { let a = arg_code(e, 0); let b = arg_code(e, 1); let c = arg_code(e, 2); emit(" call void @hs_req_header(ptr "); emit(a); emit(", ptr "); emit(b); emit(", ptr "); emit(c); emit(")\n"); return val("0", "void") }
|
|
if (name == "hs_req_body") { let a = arg_code(e, 0); let b = arg_code(e, 1); let c = arg_code(e, 2); emit(" call void @hs_req_body(ptr "); emit(a); emit(", ptr "); emit(b); emit(", i32 "); emit(c); emit(")\n"); return val("0", "void") }
|
|
if (name == "hs_send") { let a = arg_code(e, 0); let b = arg_code(e, 1); emit(" call void @hs_send(i32 "); emit(a); emit(", ptr "); emit(b); emit(")\n"); return val("0", "void") }
|
|
if (name == "hs_done") { let a = arg_code(e, 0); return val(emit_bind(`call i32 @hs_done(i32 {a})`), "int") }
|
|
if (name == "hs_status") { let a = arg_code(e, 0); return val(emit_bind(`call i32 @hs_status(i32 {a})`), "int") }
|
|
if (name == "hs_body") { let a = arg_code(e, 0); return val(emit_bind(`call ptr @hs_body(i32 {a})`), "pointer") }
|
|
if (name == "hs_blen") { let a = arg_code(e, 0); return val(emit_bind(`call i32 @hs_blen(i32 {a})`), "int") }
|
|
if (name == "hs_header") { let a = arg_code(e, 0); let b = arg_code(e, 1); return val(emit_bind(`call ptr @hs_header(i32 {a}, ptr {b})`), "pointer") }
|
|
if (name == "hs_free") { let a = arg_code(e, 0); emit(" call void @hs_free(i32 "); emit(a); emit(")\n"); return val("0", "void") }
|
|
return val("0", "void")
|
|
}
|