Step 17b: Pretext — DOM-free text layout with otfm font measurement
Pure SX text layout library with one IO boundary (text-measure perform). Knuth-Plass optimal line breaking, Liang's hyphenation, position calculation. Library (lib/text-layout.sx): - break-lines: Knuth-Plass DP over word widths - break-lines-greedy: simple word-wrap for comparison - hyphenate-word: Liang's trie algorithm - position-line/position-lines: running x/y sums - measure-text: single perform (text-measure IO) Server font measurement (otfm): - Reads OpenType cmap + hmtx tables from .ttf files - DejaVu Serif/Sans bundled in shared/static/fonts/ - _cek_io_resolver hook: perform works inside aser/eval_expr - JIT VM suspension inline resolution for IO in compiled code ~font component (shared/sx/templates/font.sx): - Works like ~tw: emits @font-face CSS via cssx scope - Sets font-family on parent via spread - Deduplicates font declarations Infrastructure fixes: - stdin load command: per-expression error handling (was aborting on first error) - cek_run IO hook: _cek_io_resolver in sx_types.ml - JIT VmSuspended: inline IO resolution when resolver installed - ListRef handling in IO resolver (perform creates ListRef, not List) Demo page at /sx/(applications.(pretext)): - Hero: justified paragraph with otfm-measured proportional widths - Greedy vs Knuth-Plass side-by-side comparison - Badness scoring visualization - Hyphenation syllable decomposition 25 new tests (spec/tests/test-text-layout.sx), 3201/3201 passing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -585,7 +585,18 @@ and cek_step_loop state =
|
||||
|
||||
(* cek-run *)
|
||||
and cek_run state =
|
||||
(let final = (cek_step_loop (state)) in (if sx_truthy ((cek_suspended_p (final))) then (raise (Eval_error (value_to_str (String "IO suspension in non-IO context")))) else (cek_value (final))))
|
||||
let rec run s =
|
||||
let final = cek_step_loop s in
|
||||
if sx_truthy (cek_suspended_p final) then
|
||||
match !Sx_types._cek_io_resolver with
|
||||
| Some resolver ->
|
||||
let request = cek_io_request final in
|
||||
let result = resolver request final in
|
||||
run (cek_resume final result)
|
||||
| None ->
|
||||
raise (Eval_error (value_to_str (String "IO suspension in non-IO context")))
|
||||
else cek_value final
|
||||
in run state
|
||||
|
||||
(* cek-resume *)
|
||||
and cek_resume suspended_state result' =
|
||||
|
||||
@@ -246,6 +246,12 @@ exception Parse_error of string
|
||||
to avoid a dependency cycle between sx_runtime and sx_vm. *)
|
||||
exception CekPerformRequest of value
|
||||
|
||||
(** Hook: resolve IO suspension inline in cek_run.
|
||||
When set, cek_run calls this instead of raising "IO suspension in non-IO context".
|
||||
The function receives the suspended state and returns the resolved value.
|
||||
Used by the HTTP server to handle perform (text-measure) during aser. *)
|
||||
let _cek_io_resolver : (value -> value -> value) option ref = ref None
|
||||
|
||||
(** Hook: convert VM suspension exceptions to CekPerformRequest.
|
||||
Set by sx_vm after it defines VmSuspended. Called by sx_runtime.sx_apply_cek. *)
|
||||
let _convert_vm_suspension : (exn -> unit) ref = ref (fun _ -> ())
|
||||
|
||||
Reference in New Issue
Block a user