go: Phase 8 first slice — stdlib strings/strconv, 41 tests, +40 cleared [shapes-static-types-bidirectional]
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 31s
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 31s
New :go-package NAME ENTRIES value type with field lookup via
extended go-eval-select. New :go-builtin-fn callable for closure-
based stdlib functions. lib/go/std/strings.sx ships 12 functions
(Contains, HasPrefix, HasSuffix, Index, Count, Repeat, Join,
ToUpper, ToLower, TrimSpace, Split, Replace) + lib/go/std/strconv.sx
ships Itoa/Atoi.
Pre-existing bug fixed: parser was emitting (:literal V) for both
`42` and `"42"`, relying on first-char heuristic in eval/types.
Now emits :literal-string for string/rune literals so Atoi("42")
correctly receives the string. 3 parse tests + 2 in-composite-key
tests updated to new shape.
Total 597/597. Stdlib 41/41 — +40 acceptance bar cleared. Sister
diary documents the 11 value-type kinds (struct/slice/map/chan/
fn/method/builtin/builtin-fn/package/panic/defer) all sharing the
"(:KIND PAYLOAD...)" shape, alongside AST nodes and sentinel signals
as the kit's three orthogonal first-class-tag axes.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
71
lib/go/std/strconv.sx
Normal file
71
lib/go/std/strconv.sx
Normal file
@@ -0,0 +1,71 @@
|
||||
;; lib/go/std/strconv.sx — Go's `strconv` package, v0 subset.
|
||||
|
||||
(define
|
||||
go-strconv-itoa
|
||||
;; Itoa(n) → string. Real Go returns the decimal representation.
|
||||
(fn (args)
|
||||
(cond
|
||||
(not (= (len args) 1))
|
||||
(list :eval-error :strconv-itoa-arity (len args))
|
||||
:else
|
||||
(let ((n (first args)))
|
||||
(cond
|
||||
(not (number? n)) (list :eval-error :strconv-itoa-not-number n)
|
||||
:else (str n))))))
|
||||
|
||||
(define
|
||||
go-strconv-atoi
|
||||
;; Atoi(s) → (int, error). v0 returns just the int on success or
|
||||
;; an :eval-error on failure (multi-return is a later refinement).
|
||||
(fn (args)
|
||||
(cond
|
||||
(not (= (len args) 1))
|
||||
(list :eval-error :strconv-atoi-arity (len args))
|
||||
:else
|
||||
(let ((s (first args)))
|
||||
(cond
|
||||
(not (string? s)) (list :eval-error :strconv-atoi-not-string s)
|
||||
(= (len s) 0) (list :eval-error :strconv-atoi-empty)
|
||||
:else (go-strconv-parse-int s 0 (= (nth s 0) "-") 0))))))
|
||||
|
||||
(define
|
||||
go-strconv-parse-int
|
||||
;; Parse a (possibly signed) base-10 integer literal. Stops on the
|
||||
;; first non-digit char and returns the parsed prefix, or :eval-error
|
||||
;; if no digits were consumed.
|
||||
(fn (s start neg acc)
|
||||
(let ((i (cond (= start 0) (cond neg 1 :else 0) :else start)))
|
||||
(cond
|
||||
(>= i (len s))
|
||||
(cond
|
||||
(= (cond neg (- i 1) :else i) 0)
|
||||
(list :eval-error :strconv-atoi-no-digits s)
|
||||
:else
|
||||
(cond neg (- 0 acc) :else acc))
|
||||
:else
|
||||
(let ((d (go-strconv-digit (nth s i))))
|
||||
(cond
|
||||
(< d 0)
|
||||
(cond
|
||||
(= (cond neg (- i 1) :else i) 0)
|
||||
(list :eval-error :strconv-atoi-no-digits s)
|
||||
:else
|
||||
(cond neg (- 0 acc) :else acc))
|
||||
:else
|
||||
(go-strconv-parse-int s (+ i 1) neg (+ (* acc 10) d))))))))
|
||||
|
||||
(define
|
||||
go-strconv-digit
|
||||
(fn (c)
|
||||
(cond
|
||||
(= c "0") 0 (= c "1") 1 (= c "2") 2 (= c "3") 3
|
||||
(= c "4") 4 (= c "5") 5 (= c "6") 6 (= c "7") 7
|
||||
(= c "8") 8 (= c "9") 9
|
||||
:else -1)))
|
||||
|
||||
(define
|
||||
go-std-strconv
|
||||
(list :go-package "strconv"
|
||||
(list
|
||||
(list "Itoa" (list :go-builtin-fn go-strconv-itoa))
|
||||
(list "Atoi" (list :go-builtin-fn go-strconv-atoi)))))
|
||||
386
lib/go/std/strings.sx
Normal file
386
lib/go/std/strings.sx
Normal file
@@ -0,0 +1,386 @@
|
||||
;; lib/go/std/strings.sx — Go's `strings` package, v0 subset.
|
||||
;;
|
||||
;; Exposed as `go-std-strings`, a (:go-package "strings" ENTRIES) value.
|
||||
;; Register with `(go-env-extend env "strings" go-std-strings)` to make
|
||||
;; `strings.X(...)` call sites work in evaluated Go code.
|
||||
;;
|
||||
;; Each entry is (FIELD-NAME (list :go-fn PARAMS BODY)) — the same
|
||||
;; shape user-defined Go functions get. Bodies are written in SX
|
||||
;; directly via go-builtin closures wrapping host-level string ops
|
||||
;; for speed, OR as parsed Go source for fidelity. v0 uses
|
||||
;; go-builtin wrappers — simpler and fast.
|
||||
|
||||
;; ── helpers: implement go-std-strings entries as builtins ────────
|
||||
|
||||
(define
|
||||
go-strings-contains
|
||||
(fn (args)
|
||||
(cond
|
||||
(not (= (len args) 2))
|
||||
(list :eval-error :strings-contains-arity (len args))
|
||||
:else
|
||||
(let ((s (first args)) (sub (nth args 1)))
|
||||
(cond
|
||||
(not (string? s)) (list :eval-error :strings-not-string s)
|
||||
(not (string? sub)) (list :eval-error :strings-not-string sub)
|
||||
:else
|
||||
(go-strings-index-of s sub 0))))))
|
||||
|
||||
(define
|
||||
go-strings-index-of
|
||||
;; Returns true if SUB appears in S at or after START, else false.
|
||||
(fn (s sub start)
|
||||
(let ((slen (len s)) (sublen (len sub)))
|
||||
(cond
|
||||
(= sublen 0) true
|
||||
(> (+ start sublen) slen) false
|
||||
(go-strings-match-at s sub start 0) true
|
||||
:else (go-strings-index-of s sub (+ start 1))))))
|
||||
|
||||
(define
|
||||
go-strings-match-at
|
||||
(fn (s sub start k)
|
||||
(cond
|
||||
(>= k (len sub)) true
|
||||
(= (nth s (+ start k)) (nth sub k))
|
||||
(go-strings-match-at s sub start (+ k 1))
|
||||
:else false)))
|
||||
|
||||
(define
|
||||
go-strings-has-prefix
|
||||
(fn (args)
|
||||
(cond
|
||||
(not (= (len args) 2))
|
||||
(list :eval-error :strings-hasprefix-arity (len args))
|
||||
:else
|
||||
(let ((s (first args)) (p (nth args 1)))
|
||||
(cond
|
||||
(not (string? s)) (list :eval-error :strings-not-string s)
|
||||
(not (string? p)) (list :eval-error :strings-not-string p)
|
||||
(> (len p) (len s)) false
|
||||
:else (go-strings-match-at s p 0 0))))))
|
||||
|
||||
(define
|
||||
go-strings-has-suffix
|
||||
(fn (args)
|
||||
(cond
|
||||
(not (= (len args) 2))
|
||||
(list :eval-error :strings-hassuffix-arity (len args))
|
||||
:else
|
||||
(let ((s (first args)) (suf (nth args 1)))
|
||||
(cond
|
||||
(not (string? s)) (list :eval-error :strings-not-string s)
|
||||
(not (string? suf)) (list :eval-error :strings-not-string suf)
|
||||
(> (len suf) (len s)) false
|
||||
:else
|
||||
(go-strings-match-at s suf (- (len s) (len suf)) 0))))))
|
||||
|
||||
(define
|
||||
go-strings-index
|
||||
(fn (args)
|
||||
(cond
|
||||
(not (= (len args) 2))
|
||||
(list :eval-error :strings-index-arity (len args))
|
||||
:else
|
||||
(let ((s (first args)) (sub (nth args 1)))
|
||||
(cond
|
||||
(not (string? s)) (list :eval-error :strings-not-string s)
|
||||
(not (string? sub)) (list :eval-error :strings-not-string sub)
|
||||
:else (go-strings-index-loop s sub 0))))))
|
||||
|
||||
(define
|
||||
go-strings-index-loop
|
||||
(fn (s sub start)
|
||||
(let ((slen (len s)) (sublen (len sub)))
|
||||
(cond
|
||||
(= sublen 0) 0
|
||||
(> (+ start sublen) slen) -1
|
||||
(go-strings-match-at s sub start 0) start
|
||||
:else (go-strings-index-loop s sub (+ start 1))))))
|
||||
|
||||
(define
|
||||
go-strings-repeat
|
||||
(fn (args)
|
||||
(cond
|
||||
(not (= (len args) 2))
|
||||
(list :eval-error :strings-repeat-arity (len args))
|
||||
:else
|
||||
(let ((s (first args)) (n (nth args 1)))
|
||||
(cond
|
||||
(not (string? s)) (list :eval-error :strings-not-string s)
|
||||
(< n 0) (list :eval-error :strings-repeat-negative n)
|
||||
:else (go-strings-repeat-loop s n ""))))))
|
||||
|
||||
(define
|
||||
go-strings-repeat-loop
|
||||
(fn (s n acc)
|
||||
(cond
|
||||
(<= n 0) acc
|
||||
:else (go-strings-repeat-loop s (- n 1) (str acc s)))))
|
||||
|
||||
(define
|
||||
go-strings-count
|
||||
(fn (args)
|
||||
(cond
|
||||
(not (= (len args) 2))
|
||||
(list :eval-error :strings-count-arity (len args))
|
||||
:else
|
||||
(let ((s (first args)) (sub (nth args 1)))
|
||||
(cond
|
||||
(not (string? s)) (list :eval-error :strings-not-string s)
|
||||
(not (string? sub)) (list :eval-error :strings-not-string sub)
|
||||
:else (go-strings-count-loop s sub 0 0))))))
|
||||
|
||||
(define
|
||||
go-strings-count-loop
|
||||
(fn (s sub start acc)
|
||||
(let ((idx (go-strings-index-loop s sub start)))
|
||||
(cond
|
||||
(< idx 0) acc
|
||||
:else
|
||||
(go-strings-count-loop s sub (+ idx (max 1 (len sub))) (+ acc 1))))))
|
||||
|
||||
(define
|
||||
go-strings-join
|
||||
(fn (args)
|
||||
(cond
|
||||
(not (= (len args) 2))
|
||||
(list :eval-error :strings-join-arity (len args))
|
||||
:else
|
||||
(let ((sep (nth args 1)) (xs (first args)))
|
||||
(cond
|
||||
(not (string? sep)) (list :eval-error :strings-not-string sep)
|
||||
(not (and (list? xs) (= (first xs) :go-slice)))
|
||||
(list :eval-error :strings-join-not-slice xs)
|
||||
:else (go-strings-join-loop (nth xs 1) sep ""))))))
|
||||
|
||||
(define
|
||||
go-strings-join-loop
|
||||
(fn (xs sep acc)
|
||||
(cond
|
||||
(= (len xs) 0) acc
|
||||
(= (len acc) 0) (go-strings-join-loop (rest xs) sep (first xs))
|
||||
:else
|
||||
(go-strings-join-loop (rest xs) sep (str acc sep (first xs))))))
|
||||
|
||||
;; ── case conversion ──────────────────────────────────────────────
|
||||
|
||||
(define
|
||||
go-strings-char-to-upper
|
||||
(fn (c)
|
||||
(cond
|
||||
(and (>= c "a") (<= c "z"))
|
||||
;; ASCII uppercase shift: 'a' is 0x61, 'A' is 0x41 → diff 0x20.
|
||||
;; SX has no charcode primitive, so use a char-pair table.
|
||||
(go-strings-letter-toggle c true)
|
||||
:else c)))
|
||||
|
||||
(define
|
||||
go-strings-char-to-lower
|
||||
(fn (c)
|
||||
(cond
|
||||
(and (>= c "A") (<= c "Z"))
|
||||
(go-strings-letter-toggle c false)
|
||||
:else c)))
|
||||
|
||||
(define
|
||||
go-strings-letter-toggle
|
||||
;; Toggle a single ASCII letter's case via direct mapping.
|
||||
;; `to-upper?` true means input is lowercase, output uppercase.
|
||||
(fn (c to-upper?)
|
||||
(cond
|
||||
to-upper?
|
||||
(cond
|
||||
(= c "a") "A" (= c "b") "B" (= c "c") "C" (= c "d") "D"
|
||||
(= c "e") "E" (= c "f") "F" (= c "g") "G" (= c "h") "H"
|
||||
(= c "i") "I" (= c "j") "J" (= c "k") "K" (= c "l") "L"
|
||||
(= c "m") "M" (= c "n") "N" (= c "o") "O" (= c "p") "P"
|
||||
(= c "q") "Q" (= c "r") "R" (= c "s") "S" (= c "t") "T"
|
||||
(= c "u") "U" (= c "v") "V" (= c "w") "W" (= c "x") "X"
|
||||
(= c "y") "Y" (= c "z") "Z" :else c)
|
||||
:else
|
||||
(cond
|
||||
(= c "A") "a" (= c "B") "b" (= c "C") "c" (= c "D") "d"
|
||||
(= c "E") "e" (= c "F") "f" (= c "G") "g" (= c "H") "h"
|
||||
(= c "I") "i" (= c "J") "j" (= c "K") "k" (= c "L") "l"
|
||||
(= c "M") "m" (= c "N") "n" (= c "O") "o" (= c "P") "p"
|
||||
(= c "Q") "q" (= c "R") "r" (= c "S") "s" (= c "T") "t"
|
||||
(= c "U") "u" (= c "V") "v" (= c "W") "w" (= c "X") "x"
|
||||
(= c "Y") "y" (= c "Z") "z" :else c))))
|
||||
|
||||
(define
|
||||
go-strings-map-chars
|
||||
(fn (s i acc char-fn)
|
||||
(cond
|
||||
(>= i (len s)) acc
|
||||
:else
|
||||
(go-strings-map-chars s (+ i 1) (str acc (char-fn (nth s i))) char-fn))))
|
||||
|
||||
(define
|
||||
go-strings-to-upper
|
||||
(fn (args)
|
||||
(cond
|
||||
(not (= (len args) 1))
|
||||
(list :eval-error :strings-toupper-arity (len args))
|
||||
:else
|
||||
(let ((s (first args)))
|
||||
(cond
|
||||
(not (string? s)) (list :eval-error :strings-not-string s)
|
||||
:else (go-strings-map-chars s 0 "" go-strings-char-to-upper))))))
|
||||
|
||||
(define
|
||||
go-strings-to-lower
|
||||
(fn (args)
|
||||
(cond
|
||||
(not (= (len args) 1))
|
||||
(list :eval-error :strings-tolower-arity (len args))
|
||||
:else
|
||||
(let ((s (first args)))
|
||||
(cond
|
||||
(not (string? s)) (list :eval-error :strings-not-string s)
|
||||
:else (go-strings-map-chars s 0 "" go-strings-char-to-lower))))))
|
||||
|
||||
;; ── TrimSpace ────────────────────────────────────────────────────
|
||||
|
||||
(define
|
||||
go-strings-is-space?
|
||||
(fn (c)
|
||||
(or (= c " ") (= c "\t") (= c "\n") (= c "\r"))))
|
||||
|
||||
(define
|
||||
go-strings-trim-left
|
||||
(fn (s i)
|
||||
(cond
|
||||
(>= i (len s)) i
|
||||
(go-strings-is-space? (nth s i)) (go-strings-trim-left s (+ i 1))
|
||||
:else i)))
|
||||
|
||||
(define
|
||||
go-strings-trim-right
|
||||
(fn (s end)
|
||||
(cond
|
||||
(<= end 0) 0
|
||||
(go-strings-is-space? (nth s (- end 1))) (go-strings-trim-right s (- end 1))
|
||||
:else end)))
|
||||
|
||||
(define
|
||||
go-strings-substr
|
||||
;; Substring [lo, hi) — naive but predictable.
|
||||
(fn (s lo hi)
|
||||
(cond
|
||||
(>= lo hi) ""
|
||||
:else
|
||||
(go-strings-substr-loop s lo hi ""))))
|
||||
|
||||
(define
|
||||
go-strings-substr-loop
|
||||
(fn (s i hi acc)
|
||||
(cond
|
||||
(>= i hi) acc
|
||||
:else (go-strings-substr-loop s (+ i 1) hi (str acc (nth s i))))))
|
||||
|
||||
(define
|
||||
go-strings-trim-space
|
||||
(fn (args)
|
||||
(cond
|
||||
(not (= (len args) 1))
|
||||
(list :eval-error :strings-trimspace-arity (len args))
|
||||
:else
|
||||
(let ((s (first args)))
|
||||
(cond
|
||||
(not (string? s)) (list :eval-error :strings-not-string s)
|
||||
:else
|
||||
(let ((lo (go-strings-trim-left s 0)))
|
||||
(let ((hi (go-strings-trim-right s (len s))))
|
||||
(go-strings-substr s lo hi))))))))
|
||||
|
||||
;; ── Split ────────────────────────────────────────────────────────
|
||||
|
||||
(define
|
||||
go-strings-split
|
||||
(fn (args)
|
||||
(cond
|
||||
(not (= (len args) 2))
|
||||
(list :eval-error :strings-split-arity (len args))
|
||||
:else
|
||||
(let ((s (first args)) (sep (nth args 1)))
|
||||
(cond
|
||||
(not (string? s)) (list :eval-error :strings-not-string s)
|
||||
(not (string? sep)) (list :eval-error :strings-not-string sep)
|
||||
(= (len sep) 0)
|
||||
;; Empty separator: real Go splits to all chars; v0 keeps
|
||||
;; behaviour simple — single-element slice.
|
||||
(list :go-slice (list s))
|
||||
:else
|
||||
(list :go-slice (go-strings-split-loop s sep 0 (list))))))))
|
||||
|
||||
(define
|
||||
go-strings-split-loop
|
||||
(fn (s sep start acc)
|
||||
(let ((idx (go-strings-index-loop s sep start)))
|
||||
(cond
|
||||
(< idx 0)
|
||||
(go-strings-split-finalize acc (go-strings-substr s start (len s)))
|
||||
:else
|
||||
(go-strings-split-loop s sep (+ idx (len sep))
|
||||
(go-strings-split-finalize acc
|
||||
(go-strings-substr s start idx)))))))
|
||||
|
||||
(define
|
||||
go-strings-split-finalize
|
||||
;; Append a piece to acc, growing the list in order.
|
||||
(fn (acc piece)
|
||||
(cond
|
||||
(= (len acc) 0) (list piece)
|
||||
:else (go-name-concat acc (list piece)))))
|
||||
|
||||
;; ── Replace ──────────────────────────────────────────────────────
|
||||
|
||||
(define
|
||||
go-strings-replace
|
||||
;; Replace(s, old, new, n). n < 0 = all.
|
||||
(fn (args)
|
||||
(cond
|
||||
(not (= (len args) 4))
|
||||
(list :eval-error :strings-replace-arity (len args))
|
||||
:else
|
||||
(let ((s (first args)) (old (nth args 1))
|
||||
(newv (nth args 2)) (n (nth args 3)))
|
||||
(cond
|
||||
(not (string? s)) (list :eval-error :strings-not-string s)
|
||||
(not (string? old)) (list :eval-error :strings-not-string old)
|
||||
(not (string? newv)) (list :eval-error :strings-not-string newv)
|
||||
(= (len old) 0) s
|
||||
:else (go-strings-replace-loop s old newv n 0 ""))))))
|
||||
|
||||
(define
|
||||
go-strings-replace-loop
|
||||
(fn (s old newv n start acc)
|
||||
(let ((idx (go-strings-index-loop s old start)))
|
||||
(cond
|
||||
(or (< idx 0) (= n 0))
|
||||
(str acc (go-strings-substr s start (len s)))
|
||||
:else
|
||||
(go-strings-replace-loop s old newv
|
||||
(cond (< n 0) -1 :else (- n 1))
|
||||
(+ idx (len old))
|
||||
(str acc (go-strings-substr s start idx) newv))))))
|
||||
|
||||
;; ── go-std-strings package value ─────────────────────────────────
|
||||
|
||||
(define
|
||||
go-std-strings
|
||||
(list :go-package "strings"
|
||||
(list
|
||||
(list "Contains" (list :go-builtin-fn go-strings-contains))
|
||||
(list "HasPrefix" (list :go-builtin-fn go-strings-has-prefix))
|
||||
(list "HasSuffix" (list :go-builtin-fn go-strings-has-suffix))
|
||||
(list "Index" (list :go-builtin-fn go-strings-index))
|
||||
(list "Count" (list :go-builtin-fn go-strings-count))
|
||||
(list "Repeat" (list :go-builtin-fn go-strings-repeat))
|
||||
(list "Join" (list :go-builtin-fn go-strings-join))
|
||||
(list "ToUpper" (list :go-builtin-fn go-strings-to-upper))
|
||||
(list "ToLower" (list :go-builtin-fn go-strings-to-lower))
|
||||
(list "TrimSpace" (list :go-builtin-fn go-strings-trim-space))
|
||||
(list "Split" (list :go-builtin-fn go-strings-split))
|
||||
(list "Replace" (list :go-builtin-fn go-strings-replace)))))
|
||||
Reference in New Issue
Block a user