Add typed Kotlin JSON array helpers

ober

a6ed194d8d170dcc338394383344430930b5c070

diff --git a/lib/jerboa/typed/checker.ss b/lib/jerboa/typed/checker.ss
index ff7a3e1..ab9ee59 100644
--- a/lib/jerboa/typed/checker.ss
+++ b/lib/jerboa/typed/checker.ss
@@ -478,6 +478,18 @@
       (cons 'url-encode-utf8
             (make-typed-call-sig (list 'String) 'String '()
               'jvm-url-encode-utf8 '()))
+      (cons 'json-array-empty
+            (make-typed-call-sig '() 'JSONArray '()
+              'jvm-json-array-empty '()))
+      (cons 'json-array-put-float32!
+            (make-typed-call-sig (list 'JSONArray 'Float32) 'Unit '()
+              'jvm-json-array-put-float32 '()))
+      (cons 'json-array-length
+            (make-typed-call-sig (list 'JSONArray) 'Int32 '()
+              'jvm-json-array-length '()))
+      (cons 'json-array-opt-float32
+            (make-typed-call-sig (list 'JSONArray 'Int32 'Float32) 'Float32 '()
+              'jvm-json-array-opt-float32 '()))
       ;; crypto primitives — FFI to vetted RustCrypto crates, never reimplemented.
       ;; Each carries kind 'crypto-prim and an info `prim` the emitter dispatches
       ;; on; the Rust block they emit references the crate (sha2/hmac/hkdf) that
diff --git a/lib/jerboa/typed/core.ss b/lib/jerboa/typed/core.ss
index fa2a57a..b438429 100644
--- a/lib/jerboa/typed/core.ss
+++ b/lib/jerboa/typed/core.ss
@@ -137,6 +137,10 @@
       jvm-mutable-list-add
       jvm-mutable-list-remove
       jvm-mutable-list-set
+      jvm-json-array-empty
+      jvm-json-array-put-float32
+      jvm-json-array-length
+      jvm-json-array-opt-float32
       record-ctor
       record-pred
       record-accessor
diff --git a/lib/jerboa/typed/kotlin/lower.ss b/lib/jerboa/typed/kotlin/lower.ss
index c2ba1b2..34db2aa 100644
--- a/lib/jerboa/typed/kotlin/lower.ss
+++ b/lib/jerboa/typed/kotlin/lower.ss
@@ -350,6 +350,30 @@
              (make-kt-assign (make-kt-index-get (car args) (cadr args))
                              (caddr args)))
            (make-kt-lit 'Unit '()))]
+        [(jvm-json-array-empty)
+         (make-kt-new (make-kt-type 'JSONArray #f '()) '())]
+        [(jvm-json-array-put-float32)
+         (make-kt-block
+           (list
+             (make-kt-expr-stmt
+               (make-kt-member-call
+                 (car args)
+                 'put
+                 (list
+                   (make-kt-member-call (cadr args) 'toDouble '())))))
+           (make-kt-lit 'Unit '()))]
+        [(jvm-json-array-length)
+         (make-kt-member-call (car args) 'length '())]
+        [(jvm-json-array-opt-float32)
+         (make-kt-member-call
+           (make-kt-member-call
+             (car args)
+             'optDouble
+             (list
+               (cadr args)
+               (make-kt-member-call (caddr args) 'toDouble '())))
+           'toFloat
+           '())]
         [(jvm-boolean-array-ref)
          (make-kt-index-get (car args) (cadr args))]
         [(jvm-boolean-array-set)
diff --git a/tests/test-typed-checker.ss b/tests/test-typed-checker.ss
index 707a1eb..169a8b5 100644
--- a/tests/test-typed-checker.ss
+++ b/tests/test-typed-checker.ss
@@ -891,6 +891,39 @@
          (mutable-list-remove! cells name))))
   '(argument-type-mismatch))
 
+(test "json-array primitives accept declared JSONArray"
+  (error-kinds
+    '(typed-library (body json-array-ok)
+       (export make-json)
+       (type JSONArray)
+       (type Float32)
+       (def (make-json (x : Float32)) : JSONArray
+         (let ((items (json-array-empty)))
+           (begin
+             (json-array-put-float32! items x)
+             items)))))
+  '())
+
+(test "json-array put rejects non-JSONArray receiver"
+  (error-kinds
+    '(typed-library (body json-array-receiver)
+       (export f)
+       (type JSONArray)
+       (type Float32)
+       (def (f (s : String) (x : Float32)) : Unit
+         (json-array-put-float32! s x))))
+  '(argument-type-mismatch))
+
+(test "json-array opt rejects non-Int32 index"
+  (error-kinds
+    '(typed-library (body json-array-index)
+       (export f)
+       (type JSONArray)
+       (type Float32)
+       (def (f (items : JSONArray) (index : String)) : Float32
+         (json-array-opt-float32 items index (float32 0.0)))))
+  '(argument-type-mismatch))
+
 (test "variant constructor and predicate calls"
   (error-kinds
     '(typed-library (body variant-ok)
diff --git a/tests/test-typed-kotlin.ss b/tests/test-typed-kotlin.ss
index 6d14afe..ae184a1 100644
--- a/tests/test-typed-kotlin.ss
+++ b/tests/test-typed-kotlin.ss
@@ -596,6 +596,38 @@
   list-kotlin
   "cells[0] = cell")
 
+(define json-array-form
+  '(typed-library (sample typed json-array)
+     (export makeNumbers firstNumber)
+     (type JSONArray)
+     (type Int32)
+     (type Float32)
+     (def (makeNumbers (a : Float32) (b : Float32)) : JSONArray
+       (let ((items (json-array-empty)))
+         (begin
+           (json-array-put-float32! items a)
+           (json-array-put-float32! items b)
+           items)))
+     (def (firstNumber (items : JSONArray)) : Float32
+       (if (< (json-array-length items) (int32 1))
+         (float32 0.0)
+         (json-array-opt-float32 items (int32 0) (float32 0.0))))))
+
+(define json-array-kotlin (typed-library-form->kotlin-string json-array-form))
+
+(test-contains "JSONArray empty lowers to constructor"
+  json-array-kotlin
+  "val items: JSONArray = JSONArray()")
+(test-contains "JSONArray put Float32 lowers through Double"
+  json-array-kotlin
+  "items.put(a.toDouble())")
+(test-contains "JSONArray length lowers to method call"
+  json-array-kotlin
+  "items.length()")
+(test-contains "JSONArray opt Float32 lowers through optDouble"
+  json-array-kotlin
+  "items.optDouble(0, 0.0f.toDouble()).toFloat()")
+
 (define fold-form
   '(typed-library (sample typed fold)
      (export sumTo)