about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorKartik K. Agaram <vc@akkartik.com>2015-01-17 15:41:24 -0800
committerKartik K. Agaram <vc@akkartik.com>2015-01-17 15:41:24 -0800
commit877b4fae0436bdf22e7c0f911ce8f7030038a04c (patch)
treead84b545ec8d1c1575edcbdcd43075cce1e76967
parenta1e8f8d8b41fa9f3ad381ebc7868900a266545c0 (diff)
downloadmu-877b4fae0436bdf22e7c0f911ce8f7030038a04c.tar.gz
576 - helper for printing integers
This requires creating a new data structure called buffer, because
strings are too inefficient for appending to, and we need to know how
long they need to be before we clear them.

But I'm not gonna bother to write tests for all the new primitives I
just introduced, because that's not expedient.

One test for mu is how nicely it handles situations like this without
requiring perfect test hygiene. In this case, I can imagine tools that
will extract tests for a particular function out of all known tests.
Especially if it's a pure function that should be easy. Then just show
each test to the programmer and ask him to give it a reasonable name.
-rw-r--r--horizon4
-rw-r--r--mu.arc125
-rw-r--r--mu.arc.t35
3 files changed, 164 insertions, 0 deletions
diff --git a/horizon b/horizon
index a8869da4..f46e56ec 100644
--- a/horizon
+++ b/horizon
@@ -23,3 +23,7 @@ Trace should contain [
   typed "b"
   typed "c"
 ]
+
+---
+
+Extract unit tests for helper out of all existing tests.
diff --git a/mu.arc b/mu.arc
index cc267850..2e1d8fd6 100644
--- a/mu.arc
+++ b/mu.arc
@@ -156,6 +156,9 @@
               string-address-array-address (obj size 1  address t  elem '(string-address-array))
               character (obj size 1)  ; int32 like a Go rune
               character-address (obj size 1  address t  elem '(character))
+              ; a buffer makes it easy to append to a string
+              buffer (obj size 2  and-record t  elems '((integer) (string-address))  fields '(length data))
+              buffer-address (obj size 1  address t  elem '(buffer))
               ; isolating function calls
               space (obj array t  elem '(location))  ; by convention index 0 points to outer space
               space-address (obj size 1  address t  elem '(space))
@@ -1116,6 +1119,7 @@
   (replace-names-with-location instrs name))
 
 (def assign-names-to-location (instrs name)
+;?   (tr name)
   (ret location (table)
     (with (isa-field  (table)
            idx  1)  ; 0 always reserved for next space
@@ -1944,6 +1948,126 @@
   (print-primitive-to-host c:location)
 )
 
+(init-fn init-buffer
+  (default-space:space-address <- new space:literal 30:literal)
+  (result:buffer-address <- new buffer:literal)
+  (len:integer-address <- get-address result:buffer-address/deref length:offset)
+  (len:integer-address/deref <- copy 0:literal)
+  (s:string-address-address <- get-address result:buffer-address/deref data:offset)
+  (capacity:integer <- next-input)
+  (s:string-address-address/deref <- new string:literal capacity:integer)
+  (reply result:buffer-address)
+)
+
+(init-fn grow-buffer
+  (default-space:space-address <- new space:literal 30:literal)
+  (in:buffer-address <- next-input)
+  ; double buffer size
+  (x:string-address-address <- get-address in:buffer-address/deref data:offset)
+  (oldlen:integer <- length x:string-address-address/deref/deref)
+  (newlen:integer <- multiply oldlen:integer 2:literal)
+  (olddata:string-address <- copy x:string-address-address/deref)
+  (x:string-address-address/deref <- new string:literal newlen:integer)
+  ; copy old contents
+  (i:integer <- copy 0:literal)
+  { begin
+    (done?:boolean <- greater-or-equal i:integer oldlen:integer)
+    (break-if done?:boolean)
+    (src:byte <- index olddata:string-address/deref i:integer)
+    (dest:byte-address <- index-address x:string-address-address/deref/deref i:integer)
+    (dest:byte-address <- copy src:byte)
+    (loop)
+  }
+  (reply in:buffer-address)
+)
+
+(init-fn buffer-full?
+  (default-space:space-address <- new space:literal 30:literal)
+  (in:buffer-address <- next-input)
+  (len:integer <- get in:buffer-address/deref length:offset)
+  (s:string-address <- get in:buffer-address/deref data:offset)
+  (capacity:integer <- length s:string-address/deref)
+  (result:boolean <- greater-or-equal len:integer capacity:integer)
+  (reply result:boolean)
+)
+
+(init-fn append
+  (default-space:space-address <- new space:literal 30:literal)
+  (in:buffer-address <- next-input)
+  (c:character <- next-input)
+  { begin
+    ; grow buffer if necessary
+    (full?:boolean <- buffer-full? in:buffer-address)
+    (break-unless full?:boolean)
+    (in:buffer-address <- grow-buffer in:buffer-address)
+  }
+  (len:integer-address <- get-address in:buffer-address/deref length:offset)
+  (s:string-address <- get in:buffer-address/deref data:offset)
+  (dest:byte-address <- index-address s:string-address/deref len:integer-address/deref)
+  (dest:byte-address/deref <- copy c:character)  ; todo: unicode
+  (len:integer-address/deref <- add len:integer-address/deref 1:literal)
+  (reply in:buffer-address)
+)
+
+(init-fn integer-to-decimal-string
+  (n:integer <- next-input)
+  ; is it zero?
+  { begin
+    (zero?:boolean <- equal n:integer 0:literal)
+    (break-unless zero?:boolean)
+    (s:string-address <- new "0")
+    (reply s:string-address)
+  }
+  ; save sign
+  (negate-result:boolean <- copy nil:literal)
+  { begin
+    (negative?:boolean <- less-than n:integer 0:literal)
+    (break-unless negative?:boolean)
+;?     (print-primitive-to-host (("is negative " literal)))
+    (negate-result:boolean <- copy t:literal)
+    (n:integer <- multiply n:integer -1:literal)
+  }
+  ; add digits from right to left into intermediate buffer
+  (tmp:buffer-address <- init-buffer 30:literal)
+  (zero:character <- copy ((#\0 literal)))
+  (digit-base:integer <- character-to-integer zero:character)
+  { begin
+    (done?:boolean <- equal n:integer 0:literal)
+    (break-if done?:boolean)
+    (n:integer digit:integer <- divide-with-remainder n:integer 10:literal)
+    (digit-codepoint:integer <- add digit-base:integer digit:integer)
+    (c:character <- integer-to-character digit-codepoint:integer)
+    (tmp:buffer-address <- append tmp:buffer-address c:character)
+    (loop)
+  }
+  ; add sign
+  { begin
+    (break-unless negate-result:boolean)
+    (tmp:buffer-address <- append tmp:buffer-address ((#\- literal)))
+  }
+  ; reverse buffer into string result
+  (len:integer <- get tmp:buffer-address/deref length:offset)
+  (buf:string-address <- get tmp:buffer-address/deref data:offset)
+  (result:string-address <- new string:literal len:integer)
+  (i:integer <- subtract len:integer 1:literal)
+  (j:integer <- copy 0:literal)
+  { begin
+    ; while (i >= 0)
+    (done?:boolean <- less-than i:integer 0:literal)
+    (break-if done?:boolean)
+    ; result[j] = tmp[i]
+    (src:byte <- index buf:string-address/deref i:integer)
+    (dest:byte-address <- index-address result:string-address/deref j:integer)
+    (dest:byte-address/deref <- copy src:byte)
+    ; ++i
+    (i:integer <- subtract i:integer 1:literal)
+    ; --j
+    (j:integer <- add j:integer 1:literal)
+    (loop)
+  }
+  (reply result:string-address)
+)
+
 (init-fn send-prints-to-stdout
   (default-space:space-address <- new space:literal 30:literal)
   (stdout:channel-address <- next-input)
@@ -1960,6 +2084,7 @@
 )
 
 ; after all system software is loaded:
+;? (= dump-trace* (obj whitelist '("cn0" "cn1")))
 (freeze system-function*)
 )  ; section 100 for system software
 
diff --git a/mu.arc.t b/mu.arc.t
index 1b378c5a..dcb38e0f 100644
--- a/mu.arc.t
+++ b/mu.arc.t
@@ -4034,6 +4034,41 @@
             (~memory-contains-array (memory* (+ base 4)) "c"))
     (prn "F - 'split' cuts string at two delimiters")))
 
+(reset)
+(new-trace "integer-to-decimal-string")
+(add-code
+  '((function main [
+      (1:string-address/raw <- integer-to-decimal-string 34:literal)
+    ])))
+;? (set dump-trace*)
+;? (= dump-trace* (obj whitelist '("run")))
+(run 'main)
+(let base memory*.1
+  (when (~memory-contains-array base "34")
+    (prn "F - converting integer to decimal string")))
+
+(reset)
+(new-trace "integer-to-decimal-string-zero")
+(add-code
+  '((function main [
+      (1:string-address/raw <- integer-to-decimal-string 0:literal)
+    ])))
+(run 'main)
+(let base memory*.1
+  (when (~memory-contains-array base "0")
+    (prn "F - converting zero to decimal string")))
+
+(reset)
+(new-trace "integer-to-decimal-string-negative")
+(add-code
+  '((function main [
+      (1:string-address/raw <- integer-to-decimal-string -237:literal)
+    ])))
+(run 'main)
+(let base memory*.1
+  (when (~memory-contains-array base "-237")
+    (prn "F - converting negative integer to decimal string")))
+
 )  ; section 100 for string utilities
 
 (reset)