token-value: build with one native list->string (fixes non-ASCII crash); 0.16.3
THE MINIMAL SHAPE, chosen by re-benching the held perf/tokenizer-linear rewrite against the sigil 0.17.15 runtime (native char-iteration: ASCII string-ref fast path, native string->vector/make-string sweep):
8000 lines (min of 3, alternating, loaded box):
old scan (CLI-embedded v0.16.2): 1852-2287ms
full rewrite (perf/tokenizer-linear): 2583-2969ms <- now a REGRESSION
this branch (old scan + new token-value): 1785ms <- old speed + fix
5000 lines: old 1083ms / rewrite 1882ms / this 1436ms (load noise band)On 0.17.14 the two scans were wall-clock neutral; 0.17.15's native lift lands HARDER on the old vectorized scan (its dominant cost was the per-token make-string+string-set! extraction, now natively fast) than on the rewrite's class-table machinery. So the scan rewrite no longer earns its keep and is NOT taken.
What IS taken: the token-value crash fix. make-string + per-char string-set! vm-errors on any token containing a non-ASCII char (the byte-width constraint is permanent by design -- the 0.17.15 string cursor depends on it), which broke Slate's highlighter and the formatter's emit path on unicode sources, and crashed tokenize itself on "+lambda"-style tokens mid-scan. token-value now collects chars and converts with ONE native list->string -- also ~2x faster over full token streams.
Differential harness (cherry-picked from the held branch): ALL PASSED against the inlined v0.16.2 (byte-identical on ASCII; unicode diverges only where the old implementation crashed). sigil test 23 green (NB vacuous for local code -- CLI-embedded module shadowing; bench-diff is the real evidence).
package.sgl | 2 +-
src/sigil/format/tokenize.sgl | 26 ++++++++++++++++----------
2 files changed, 17 insertions(+), 11 deletions(-)package.sglmodified
(package name: "sigil-format" version: "0.16.2" version: "0.16.3" sigil: "^0.17" description: "Sigil code formatter (paren-inference + AST-aware reflow)" url: "https://codeberg.org/sigil/sigil-format"src/sigil/format/tokenize.sglmodified
(define (token-indent tok) (vector-ref tok 5)) ;;; Extract the text value of a token from the source chars vector ;; Build the token's text with ONE native list->string call. The previous ;; make-string + per-char string-set! implementation VM-ERRORED on any ;; token containing a non-ASCII char ("string-set!: character byte width ;; change not supported" -- make-string pre-sizes 1-byte cells and the ;; byte-width constraint is permanent by design, the string cursor depends ;; on it). That crash broke Slate's highlighter (token-value per symbol ;; token) and the formatter's emit path on unicode Sigil sources, and made ;; tokenize itself crash on tokens like "+\u03bb" (looks-like-number? ;; extracts the text mid-scan). Collect-and-convert is also ~2x faster ;; over full token streams (measured on the 0.17.x bytecode VM). (define (token-value tok chars) (let* ((s (vector-ref tok 1)) (e (vector-ref tok 2)) (n (- e s)) (v (make-string n))) (let loop ((i 0)) (if (>= i n) v (begin (string-set! v i (vector-ref chars (+ s i))) (loop (+ i 1))))))) (let ((s (vector-ref tok 1)) (e (vector-ref tok 2))) (let loop ((i (- e 1)) (acc '())) (if (< i s) (list->string acc) (loop (- i 1) (cons (vector-ref chars i) acc)))))) ;;; Get the chars vector from a tokenize result (define (tokenize-result-chars result) (car result))