Lower typed Int helpers to Kotlin

ober

ad51de9795bb640e445deeb223f052672c80581c

diff --git a/lib/jerboa/typed/checker.ss b/lib/jerboa/typed/checker.ss
index 84b360f..708a932 100644
--- a/lib/jerboa/typed/checker.ss
+++ b/lib/jerboa/typed/checker.ss
@@ -403,6 +403,9 @@
       (cons 'int32->string
             (make-typed-call-sig (list 'Int32) 'String '()
               'jvm-to-string '()))
+      (cons 'int->string
+            (make-typed-call-sig (list 'Int) 'String '()
+              'jvm-to-string '()))
       (cons 'string-take
             (make-typed-call-sig (list 'String 'Int32) 'String '()
               'jvm-string-take '()))
@@ -1430,6 +1433,25 @@
                    'jvm-to-int32 'int32 (list arg-ir) '()))
             (append errors operand-errors))))))
 
+  (def (infer-to-int args env type-names expr)
+    ;; JVM/Android interop: explicitly widen or normalize a numeric value to
+    ;; Kotlin Long, represented by Typed Jerboa's Int.
+    (if (not (= (length args) 1))
+      (values #f
+        (list (error-at expr 'bad-primitive-arity
+                "int needs exactly one operand"
+                expr)))
+      (let-values ([(arg-ir errors) (infer-expression (car args) env type-names)])
+        (let* ([operand-errors
+                (operand-type-errors 'numeric (list (ir-type arg-ir))
+                  (list expr) expr)]
+               [ok? (and arg-ir (null? errors) (null? operand-errors))])
+          (values
+            (and ok?
+                 (make-typed-ir-call 'Int (expr-source expr)
+                   'jvm-to-int 'int (list arg-ir) '()))
+            (append errors operand-errors))))))
+
   (def (infer-float-array args env type-names expr)
     (let-values ([(arg-irs errors) (infer-args args env type-names)])
       (let* ([types (ir-list-types arg-irs)]
@@ -1849,7 +1871,7 @@
               (infer-if args env type-names expr)]
              [(match)
               (infer-match args env type-names expr)]
-             [(+ - * /)
+             [(+ - * / mod)
               (infer-arithmetic head args env type-names expr)]
              [(= < <= > >=)
               (infer-comparison head args env type-names expr)]
@@ -1873,6 +1895,8 @@
               (infer-to-float32 args env type-names expr)]
              [(int32)
               (infer-to-int32 args env type-names expr)]
+             [(int)
+              (infer-to-int args env type-names expr)]
              [(float-array)
               (infer-float-array args env type-names expr)]
              [(bytes-build)
diff --git a/lib/jerboa/typed/kotlin/lower.ss b/lib/jerboa/typed/kotlin/lower.ss
index 23d8740..a105339 100644
--- a/lib/jerboa/typed/kotlin/lower.ss
+++ b/lib/jerboa/typed/kotlin/lower.ss
@@ -149,6 +149,7 @@
       [(-) "-"]
       [(*) "*"]
       [(/) "/"]
+      [(mod) "%"]
       [else (error 'arith-op "unsupported arithmetic op" op)]))
 
   (def (cmp-op op)
@@ -220,6 +221,11 @@
                   (memq (kt-lit-type (car args)) '(Nat Int Int32)))
            (make-kt-lit 'Int32 (kt-lit-value (car args)))
            (make-kt-member-call (car args) 'toInt '()))]
+        [(jvm-to-int)
+         (if (and (kt-lit? (car args))
+                  (memq (kt-lit-type (car args)) '(Nat Int Int32)))
+           (make-kt-lit 'Int (kt-lit-value (car args)))
+           (make-kt-member-call (car args) 'toLong '()))]
         [(jvm-to-string)
          (make-kt-member-call (car args) 'toString '())]
         [(string-length)
diff --git a/tests/test-typed-kotlin.ss b/tests/test-typed-kotlin.ss
index e49a78f..958ed30 100644
--- a/tests/test-typed-kotlin.ss
+++ b/tests/test-typed-kotlin.ss
@@ -278,6 +278,28 @@
 (test-contains "String length lowers to Kotlin length" string-length-kotlin
   "text.length.toULong()")
 
+(define duration-form
+  '(typed-library (sample typed duration)
+     (export formatActivityDuration)
+     (def (formatActivityDuration (ms : Int)) : String
+       (let ((safe (if (< ms (int 0)) (int 0) ms)))
+         (if (< safe (int 1000))
+           (string-append (int->string safe) " ms")
+           (let ((seconds (/ safe (int 1000)))
+                 (tenths (mod (/ safe (int 100)) (int 10))))
+             (string-append
+               (int->string seconds)
+               (string-append "." (string-append (int->string tenths) " s")))))))))
+
+(define duration-kotlin (typed-library-form->kotlin-string duration-form))
+
+(test-contains "Int conversion lowers to Kotlin Long literal" duration-kotlin
+  "if ((ms < 0L)) 0L else ms")
+(test-contains "Int to string lowers to Kotlin toString" duration-kotlin
+  "safe.toString()")
+(test-contains "mod lowers to Kotlin remainder operator" duration-kotlin
+  "((safe / 100L) % 10L)")
+
 (define note-form
   '(typed-library (sample typed notes)
      (export appendNote)