diff options
author | Kartik K. Agaram <vc@akkartik.com> | 2015-01-25 16:40:11 -0800 |
---|---|---|
committer | Kartik K. Agaram <vc@akkartik.com> | 2015-01-25 16:40:11 -0800 |
commit | 4f686be1ebb6713933298531531fbef0898c6b2b (patch) | |
tree | 3a46478c288a8b6fa835c16882363f41a5c891f4 | |
parent | d1f57fa1ab95a36a562d2b75a064bc387bd2d5d3 (diff) | |
download | mu-4f686be1ebb6713933298531531fbef0898c6b2b.tar.gz |
623 - 'nochange' to guard against race conditions
I dunno, this may all be a wild goose chase. I haven't been disciplined in tagging in-out arguments in 'read-move' and its helpers. Maybe I should just drop those 'nochange' oargs in 'read' and 'write'. Maybe I should reserve output args only for return values that callers might actually care about, and use more conventional metadata like 'const' or 'unique' or 'inout' on other args.
-rw-r--r-- | blocking.arc.t | 2 | ||||
-rw-r--r-- | buffered-stdin.mu | 2 | ||||
-rw-r--r-- | channel.mu | 4 | ||||
-rw-r--r-- | chessboard.arc.t | 30 | ||||
-rw-r--r-- | chessboard.mu | 6 | ||||
-rw-r--r-- | mu.arc | 26 | ||||
-rw-r--r-- | mu.arc.t | 76 | ||||
-rw-r--r-- | stdin.mu | 2 |
8 files changed, 93 insertions, 55 deletions
diff --git a/blocking.arc.t b/blocking.arc.t index e7535506..3cffb462 100644 --- a/blocking.arc.t +++ b/blocking.arc.t @@ -5,7 +5,7 @@ (add-code '((function reader [ (default-space:space-address <- new space:literal 30:literal/capacity) - (x:tagged-value 1:channel-address/space:global <- read 1:channel-address/space:global) + (x:tagged-value 1:channel-address/space:global/nochange <- read 1:channel-address/space:global) ]) (function main [ (default-space:space-address <- new space:literal 30:literal/capacity) diff --git a/buffered-stdin.mu b/buffered-stdin.mu index 3f4030b9..2e437b4e 100644 --- a/buffered-stdin.mu +++ b/buffered-stdin.mu @@ -15,7 +15,7 @@ (s:string-address <- new "? ") (print-string nil:literal/terminal s:string-address) { begin - (x:tagged-value buffered-stdin:channel-address/deref <- read buffered-stdin:channel-address) + (x:tagged-value buffered-stdin:channel-address/deref/nochange <- read buffered-stdin:channel-address) (c:character <- maybe-coerce x:tagged-value character:literal) ;? (print-primitive-to-host (("AAA " literal))) ;? 1 ;? (print-primitive-to-host c:character) ;? 1 diff --git a/channel.mu b/channel.mu index 88055398..0e23db46 100644 --- a/channel.mu +++ b/channel.mu @@ -15,7 +15,7 @@ ; channels take (n2:integer <- copy n:integer) (n3:tagged-value-address <- init-tagged-value integer:literal n2:integer) - (chan:channel-address/deref <- write chan:channel-address n3:tagged-value-address/deref) + (chan:channel-address/deref/nochange <- write chan:channel-address n3:tagged-value-address/deref) (n:integer <- add n:integer 1:literal) (loop) } @@ -27,7 +27,7 @@ (chan:channel-address <- next-input) { begin ; read a tagged value from the channel - (x:tagged-value chan:channel-address/deref <- read chan:channel-address) + (x:tagged-value chan:channel-address/deref/nochange <- read chan:channel-address) ; unbox the tagged value into an integer (n2:integer <- maybe-coerce x:tagged-value integer:literal) ; other threads might get between these prints diff --git a/chessboard.arc.t b/chessboard.arc.t index 78f9531a..1f93e905 100644 --- a/chessboard.arc.t +++ b/chessboard.arc.t @@ -10,22 +10,22 @@ (r:integer/routine <- fork read-move:fn nil:literal/globals 2000:literal/limit stdin:channel-address) (c:character <- copy ((#\a literal))) (x:tagged-value <- save-type c:character) - (stdin:channel-address/deref <- write stdin:channel-address x:tagged-value) + (stdin:channel-address/deref/nochange <- write stdin:channel-address x:tagged-value) (c:character <- copy ((#\2 literal))) (x:tagged-value <- save-type c:character) - (stdin:channel-address/deref <- write stdin:channel-address x:tagged-value) + (stdin:channel-address/deref/nochange <- write stdin:channel-address x:tagged-value) (c:character <- copy ((#\- literal))) (x:tagged-value <- save-type c:character) - (stdin:channel-address/deref <- write stdin:channel-address x:tagged-value) + (stdin:channel-address/deref/nochange <- write stdin:channel-address x:tagged-value) (c:character <- copy ((#\a literal))) (x:tagged-value <- save-type c:character) - (stdin:channel-address/deref <- write stdin:channel-address x:tagged-value) + (stdin:channel-address/deref/nochange <- write stdin:channel-address x:tagged-value) (c:character <- copy ((#\4 literal))) (x:tagged-value <- save-type c:character) - (stdin:channel-address/deref <- write stdin:channel-address x:tagged-value) + (stdin:channel-address/deref/nochange <- write stdin:channel-address x:tagged-value) (c:character <- copy ((#\newline literal))) (x:tagged-value <- save-type c:character) - (stdin:channel-address/deref <- write stdin:channel-address x:tagged-value) + (stdin:channel-address/deref/nochange <- write stdin:channel-address x:tagged-value) (sleep until-routine-done:literal r:integer/routine) ]))) ;? (set dump-trace*) @@ -59,7 +59,7 @@ (default-space:space-address <- new space:literal 30:literal/capacity) (c:character <- copy ((#\a literal))) (x:tagged-value <- save-type c:character) - (1:channel-address/raw/deref <- write 1:channel-address/raw x:tagged-value)) + (1:channel-address/raw/deref/nochange <- write 1:channel-address/raw x:tagged-value)) (wipe completed-routines*) ; check that read-move consumes it and then goes to sleep (enq read-move-routine running-routines*) @@ -74,16 +74,16 @@ (default-space:space-address <- new space:literal 30:literal/capacity) (c:character <- copy ((#\2 literal))) (x:tagged-value <- save-type c:character) - (1:channel-address/raw/deref <- write 1:channel-address/raw x:tagged-value) + (1:channel-address/raw/deref/nochange <- write 1:channel-address/raw x:tagged-value) (c:character <- copy ((#\- literal))) (x:tagged-value <- save-type c:character) - (1:channel-address/raw/deref <- write 1:channel-address/raw x:tagged-value) + (1:channel-address/raw/deref/nochange <- write 1:channel-address/raw x:tagged-value) (c:character <- copy ((#\a literal))) (x:tagged-value <- save-type c:character) - (1:channel-address/raw/deref <- write 1:channel-address/raw x:tagged-value) + (1:channel-address/raw/deref/nochange <- write 1:channel-address/raw x:tagged-value) (c:character <- copy ((#\4 literal))) (x:tagged-value <- save-type c:character) - (1:channel-address/raw/deref <- write 1:channel-address/raw x:tagged-value)) + (1:channel-address/raw/deref/nochange <- write 1:channel-address/raw x:tagged-value)) ; check that read-move consumes them and then goes to sleep (when (ran-to-completion 'read-move) (prn "F - chessboard waits after each subsequent letter of move until the last")) @@ -96,7 +96,7 @@ (default-space:space-address <- new space:literal 30:literal/capacity) (c:character <- copy ((#\newline literal))) (x:tagged-value <- save-type c:character) - (1:channel-address/raw/deref <- write 1:channel-address/raw x:tagged-value)) + (1:channel-address/raw/deref/nochange <- write 1:channel-address/raw x:tagged-value)) ; check that read-move consumes it and -- this time -- returns (when (~ran-to-completion 'read-move) (prn "F - 'read-move' completes after final letter of move")) @@ -113,7 +113,7 @@ (r:integer/routine <- fork-helper read-move:fn nil:literal/globals 2000:literal/limit stdin:channel-address dummy:terminal-address) (c:character <- copy ((#\q literal))) (x:tagged-value <- save-type c:character) - (stdin:channel-address/deref <- write stdin:channel-address x:tagged-value) + (stdin:channel-address/deref/nochange <- write stdin:channel-address x:tagged-value) (sleep until-routine-done:literal r:integer/routine) ]))) (run 'main) @@ -131,7 +131,7 @@ (r:integer/routine <- fork-helper read-file:fn nil:literal/globals 2000:literal/limit stdin:channel-address dummy:terminal-address) (c:character <- copy ((#\i literal))) (x:tagged-value <- save-type c:character) - (stdin:channel-address/deref <- write stdin:channel-address x:tagged-value) + (stdin:channel-address/deref/nochange <- write stdin:channel-address x:tagged-value) (sleep until-routine-done:literal r:integer/routine) ]))) ;? (= dump-trace* (obj whitelist '("schedule"))) @@ -154,7 +154,7 @@ (r:integer/routine <- fork-helper read-rank:fn nil:literal/globals 2000:literal/limit stdin:channel-address dummy:terminal-address) (c:character <- copy ((#\9 literal))) (x:tagged-value <- save-type c:character) - (stdin:channel-address/deref <- write stdin:channel-address x:tagged-value) + (stdin:channel-address/deref/nochange <- write stdin:channel-address x:tagged-value) (sleep until-routine-done:literal r:integer/routine) ]))) (run 'main) diff --git a/chessboard.mu b/chessboard.mu index b26401ab..8177a403 100644 --- a/chessboard.mu +++ b/chessboard.mu @@ -127,7 +127,7 @@ (function read-file [ (default-space:space-address <- new space:literal 30:literal) (stdin:channel-address <- next-input) - (x:tagged-value stdin:channel-address/deref <- read stdin:channel-address) + (x:tagged-value stdin:channel-address/deref/nochange <- read stdin:channel-address) ;? (print-primitive-to-host x:tagged-value) ;? 1 ;? (print-primitive-to-host (("\n" literal))) ;? 1 (a:character <- copy ((#\a literal))) @@ -154,7 +154,7 @@ (function read-rank [ (default-space:space-address <- new space:literal 30:literal) (stdin:channel-address <- next-input) - (x:tagged-value stdin:channel-address/deref <- read stdin:channel-address) + (x:tagged-value stdin:channel-address/deref/nochange <- read stdin:channel-address) (c:character <- maybe-coerce x:tagged-value character:literal) ;? (print-primitive-to-host (("BBB " literal))) ;? 1 ;? (print-primitive-to-host c:character) ;? 1 @@ -180,7 +180,7 @@ (function expect-stdin [ (default-space:space-address <- new space:literal 30:literal) (stdin:channel-address <- next-input) - (x:tagged-value stdin:channel-address/deref <- read stdin:channel-address) + (x:tagged-value stdin:channel-address/deref/nochange <- read stdin:channel-address) (c:character <- maybe-coerce x:tagged-value character:literal) (expected:character <- next-input) (match?:boolean <- equal c:character expected:character) diff --git a/mu.arc b/mu.arc index b06d402f..ca4b0216 100644 --- a/mu.arc +++ b/mu.arc @@ -267,6 +267,8 @@ (mac results (routine) ; assignable `((((rep ,routine) 'call-stack) 0) 'results)) +(mac reply-args(routine) ; assignable + `((((rep ,routine) 'call-stack) 0) 'reply-args)) (def waiting-for-exact-cycle? (routine) (is 'until rep.routine!sleep.0)) @@ -794,13 +796,18 @@ reply (do (when arg (prepare-reply arg)) - (let results results.routine* + (with (results results.routine* + reply-args reply-args.routine*) (pop-stack routine*) (if empty.routine* (return ninstrs)) (let (caller-oargs _ _) (parse-instr (body.routine* pc.routine*)) (trace "reply" repr.arg " " repr.caller-oargs) - (each (dest val) (zip caller-oargs results) + (each (dest reply-arg val) (zip caller-oargs reply-args results) +;? (prn dest " / " reply-arg " => " val) ;? 1 (when nondummy.dest + (when (pos '(nochange) metadata.reply-arg) + (unless (is dest (m reply-arg)) + (die "'nochange' arg in @repr.reply-args can't bind to @repr.caller-oargs"))) (trace "reply" repr.val " => " dest) (setm dest val)))) (++ pc.routine*) @@ -845,7 +852,8 @@ (= results.routine* (accum yield (each a args - (yield (m a)))))) + (yield (m a))))) + (= reply-args.routine* args)) ; helpers for memory access respecting ; immediate addressing - 'literal' and 'offset' @@ -2038,15 +2046,15 @@ (stdin:channel-address <- next-input) ;? (c:character <- copy ((#\a literal))) ;? 1 ;? (curr:tagged-value <- save-type c:character) ;? 1 -;? (stdin:channel-address/deref <- write stdin:channel-address curr:tagged-value) ;? 1 +;? (stdin:channel-address/deref/nochange <- write stdin:channel-address curr:tagged-value) ;? 1 ;? (c:character <- copy ((#\newline literal))) ;? 1 ;? (curr:tagged-value <- save-type c:character) ;? 1 -;? (stdin:channel-address/deref <- write stdin:channel-address curr:tagged-value) ;? 1 +;? (stdin:channel-address/deref/nochange <- write stdin:channel-address curr:tagged-value) ;? 1 { begin ;? 1 (c:character <- read-key k:keyboard-address) ;? 1 (loop-unless c:character) ;? 1 (curr:tagged-value <- save-type c:character) ;? 1 - (stdin:channel-address/deref <- write stdin:channel-address curr:tagged-value) ;? 1 + (stdin:channel-address/deref/nochange <- write stdin:channel-address curr:tagged-value) ;? 1 (eof?:boolean <- equal c:character ((#\null literal))) ;? 1 (break-if eof?:boolean) ;? 1 (loop) ;? 1 @@ -2063,7 +2071,7 @@ ;? ($dump-channel 1093:literal) ;? 1 ; read characters from stdin until newline, copy into line { begin - (x:tagged-value stdin:channel-address/deref <- read stdin:channel-address) + (x:tagged-value stdin:channel-address/deref/nochange <- read stdin:channel-address) (c:character <- maybe-coerce x:tagged-value character:literal) (assert c:character) ;? (print-primitive-to-host line:buffer-address) ;? 1 @@ -2095,7 +2103,7 @@ (curr:tagged-value <- save-type c:character) ;? ($dump-channel 1093:literal) ;? 1 ;? ($start-tracing) ;? 1 - (buffered-stdin:channel-address/deref <- write buffered-stdin:channel-address curr:tagged-value) + (buffered-stdin:channel-address/deref/nochange <- write buffered-stdin:channel-address curr:tagged-value) ;? ($stop-tracing) ;? 1 ;? ($dump-channel 1093:literal) ;? 1 ;? ($quit) ;? 1 @@ -2345,7 +2353,7 @@ (screen:terminal-address <- next-input) (stdout:channel-address <- next-input) { begin - (x:tagged-value stdout:channel-address/deref <- read stdout:channel-address) + (x:tagged-value stdout:channel-address/deref/nochange <- read stdout:channel-address) (c:character <- maybe-coerce x:tagged-value character:literal) (done?:boolean <- equal c:character ((#\null literal))) (break-if done?:boolean) diff --git a/mu.arc.t b/mu.arc.t index 9c321af6..f6ba0534 100644 --- a/mu.arc.t +++ b/mu.arc.t @@ -1321,6 +1321,7 @@ 4 1 5 3 6 4)) (prn "F - 'reply' permits a function to return multiple values at once")) +; 'prepare-reply' is useful for doing cleanup before exiting a function (reset) (new-trace "new-fn-prepare-reply") (add-code @@ -1344,6 +1345,35 @@ 4 1 5 3 6 4)) (prn "F - without args, 'reply' returns values from previous 'prepare-reply'.")) +; When you have arguments that are both read from and written to, include them +; redundantly in both ingredients and results. That'll help tools track what +; changed. + +; To enforce that the result and ingredient must always match, use the +; 'nochange' property. Results with 'nochange' properties should only be +; copied to themselves. +(reset) +(new-trace "new-fn-nochange") +(add-code + '((function test1 [ + ; increment the contents of an address + (default-space:space-address <- new space:literal 2:literal) + (x:integer-address <- next-input) + (x:integer-address/deref <- add x:integer-address/deref 1:literal) + (reply x:integer-address/nochange) + ]) + (function main [ + (1:integer-address <- new integer:literal) + (1:integer-address/deref <- copy 0:literal) + (2:integer-address <- test1 1:integer-address) + ]))) +(run 'main) +(let routine (car completed-routines*) +;? (prn rep.routine!error) ;? 1 + (when (no rep.routine!error) + (prn "F - 'nochange' results are never copied to other locations"))) +;? (quit) ;? 1 + ) ; section 20 (section 11 @@ -3039,7 +3069,7 @@ (1:channel-address <- init-channel 3:literal) (2:integer <- copy 34:literal) (3:tagged-value <- save-type 2:integer) - (1:channel-address/deref <- write 1:channel-address 3:tagged-value) + (1:channel-address/deref/nochange <- write 1:channel-address 3:tagged-value) (5:integer <- get 1:channel-address/deref first-full:offset) (6:integer <- get 1:channel-address/deref first-free:offset) ]))) @@ -3064,8 +3094,8 @@ (1:channel-address <- init-channel 3:literal) (2:integer <- copy 34:literal) (3:tagged-value <- save-type 2:integer) - (1:channel-address/deref <- write 1:channel-address 3:tagged-value) - (5:tagged-value 1:channel-address/deref <- read 1:channel-address) + (1:channel-address/deref/nochange <- write 1:channel-address 3:tagged-value) + (5:tagged-value 1:channel-address/deref/nochange <- read 1:channel-address) (7:integer <- maybe-coerce 5:tagged-value integer:literal) (8:integer <- get 1:channel-address/deref first-full:offset) (9:integer <- get 1:channel-address/deref first-free:offset) @@ -3089,13 +3119,13 @@ ; write a value (2:integer <- copy 34:literal) (3:tagged-value <- save-type 2:integer) - (1:channel-address/deref <- write 1:channel-address 3:tagged-value) + (1:channel-address/deref/nochange <- write 1:channel-address 3:tagged-value) ; first-free will now be 1 (5:integer <- get 1:channel-address/deref first-free:offset) ; read one value - (_ 1:channel-address/deref <- read 1:channel-address) + (_ 1:channel-address/deref/nochange <- read 1:channel-address) ; write a second value; verify that first-free wraps around to 0. - (1:channel-address/deref <- write 1:channel-address 3:tagged-value) + (1:channel-address/deref/nochange <- write 1:channel-address 3:tagged-value) (6:integer <- get 1:channel-address/deref first-free:offset) ]))) ;? (set dump-trace*) @@ -3115,15 +3145,15 @@ ; write a value (2:integer <- copy 34:literal) (3:tagged-value <- save-type 2:integer) - (1:channel-address/deref <- write 1:channel-address 3:tagged-value) + (1:channel-address/deref/nochange <- write 1:channel-address 3:tagged-value) ; read one value - (_ 1:channel-address/deref <- read 1:channel-address) + (_ 1:channel-address/deref/nochange <- read 1:channel-address) ; first-full will now be 1 (5:integer <- get 1:channel-address/deref first-full:offset) ; write a second value - (1:channel-address/deref <- write 1:channel-address 3:tagged-value) + (1:channel-address/deref/nochange <- write 1:channel-address 3:tagged-value) ; read second value; verify that first-full wraps around to 0. - (_ 1:channel-address/deref <- read 1:channel-address) + (_ 1:channel-address/deref/nochange <- read 1:channel-address) (6:integer <- get 1:channel-address/deref first-full:offset) ]))) ;? (set dump-trace*) @@ -3156,7 +3186,7 @@ (1:channel-address <- init-channel 3:literal) (2:integer <- copy 34:literal) (3:tagged-value <- save-type 2:integer) - (1:channel-address/deref <- write 1:channel-address 3:tagged-value) + (1:channel-address/deref/nochange <- write 1:channel-address 3:tagged-value) (5:boolean <- empty? 1:channel-address/deref) (6:boolean <- full? 1:channel-address/deref) ]))) @@ -3174,7 +3204,7 @@ (1:channel-address <- init-channel 1:literal) (2:integer <- copy 34:literal) (3:tagged-value <- save-type 2:integer) - (1:channel-address/deref <- write 1:channel-address 3:tagged-value) + (1:channel-address/deref/nochange <- write 1:channel-address 3:tagged-value) (5:boolean <- empty? 1:channel-address/deref) (6:boolean <- full? 1:channel-address/deref) ]))) @@ -3192,9 +3222,9 @@ (1:channel-address <- init-channel 3:literal) (2:integer <- copy 34:literal) (3:tagged-value <- save-type 2:integer) - (1:channel-address/deref <- write 1:channel-address 3:tagged-value) - (1:channel-address/deref <- write 1:channel-address 3:tagged-value) - (_ 1:channel-address/deref <- read 1:channel-address) + (1:channel-address/deref/nochange <- write 1:channel-address 3:tagged-value) + (1:channel-address/deref/nochange <- write 1:channel-address 3:tagged-value) + (_ 1:channel-address/deref/nochange <- read 1:channel-address) (5:boolean <- empty? 1:channel-address/deref) (6:boolean <- full? 1:channel-address/deref) ]))) @@ -3213,7 +3243,7 @@ (2:integer <- copy 34:literal) (3:tagged-value <- save-type 2:integer) (1:channel-address/deref <- write 1:channel-address 3:tagged-value) - (_ 1:channel-address/deref <- read 1:channel-address) + (_ 1:channel-address/deref/nochange <- read 1:channel-address) (5:boolean <- empty? 1:channel-address/deref) (6:boolean <- full? 1:channel-address/deref) ]))) @@ -3233,7 +3263,7 @@ '((function main [ (1:channel-address <- init-channel 3:literal) ; channel is empty, but receives a read - (2:tagged-value 1:channel-address/deref <- read 1:channel-address) + (2:tagged-value 1:channel-address/deref/nochange <- read 1:channel-address) ]))) ;? (set dump-trace*) ;? (= dump-trace* (obj whitelist '("run" "schedule"))) @@ -3257,9 +3287,9 @@ (1:channel-address <- init-channel 1:literal) (2:integer <- copy 34:literal) (3:tagged-value <- save-type 2:integer) - (1:channel-address/deref <- write 1:channel-address 3:tagged-value) + (1:channel-address/deref/nochange <- write 1:channel-address 3:tagged-value) ; channel has capacity 1, but receives a second write - (1:channel-address/deref <- write 1:channel-address 3:tagged-value) + (1:channel-address/deref/nochange <- write 1:channel-address 3:tagged-value) ]))) ;? (set dump-trace*) ;? (= dump-trace* (obj whitelist '("run" "schedule" "addr"))) @@ -3284,14 +3314,14 @@ (default-space:space-address <- new space:literal 30:literal) (chan:channel-address <- init-channel 3:literal) ; create a channel (fork producer:fn nil:literal/globals nil:literal/limit chan:channel-address) ; fork a routine to produce a value in it - (1:tagged-value/raw <- read chan:channel-address) ; wait for input on channel + (1:tagged-value/raw/nochange <- read chan:channel-address) ; wait for input on channel ]) (function producer [ (default-space:space-address <- new space:literal 30:literal) (n:integer <- copy 24:literal) (ochan:channel-address <- next-input) (x:tagged-value <- save-type n:integer) - (ochan:channel-address/deref <- write ochan:channel-address x:tagged-value) + (ochan:channel-address/deref/nochange <- write ochan:channel-address x:tagged-value) ]))) ;? (set dump-trace*) ;? (= dump-trace* (obj whitelist '("schedule" "run" "addr"))) @@ -3311,13 +3341,13 @@ (default-space:space-address <- new space:literal 30:literal) (1:channel-address <- init-channel 3:literal) ; create a channel (fork producer:fn default-space:space-address/globals nil:literal/limit) ; pass it as a global to another routine - (1:tagged-value/raw <- read 1:channel-address) ; wait for input on channel + (1:tagged-value/raw/nochange <- read 1:channel-address) ; wait for input on channel ]) (function producer [ (default-space:space-address <- new space:literal 30:literal) (n:integer <- copy 24:literal) (x:tagged-value <- save-type n:integer) - (1:channel-address/space:global/deref <- write 1:channel-address/space:global x:tagged-value) + (1:channel-address/space:global/deref/nochange <- write 1:channel-address/space:global x:tagged-value) ]))) (run 'consumer) (each routine completed-routines* diff --git a/stdin.mu b/stdin.mu index 4ffded52..a8c41182 100644 --- a/stdin.mu +++ b/stdin.mu @@ -14,7 +14,7 @@ ; now read characters from stdin until a 'q' is typed (print-primitive-to-host (("? " literal))) { begin - (x:tagged-value stdin:channel-address/deref <- read stdin:channel-address) + (x:tagged-value stdin:channel-address/deref/nochange <- read stdin:channel-address) (c:character <- maybe-coerce x:tagged-value character:literal) ;? (print-primitive-to-host (("main: stdin is " literal))) ;? (print-primitive-to-host stdin:channel-address) |