mu/mu.arc

1435 lines
55 KiB
Plaintext
Raw Normal View History

2014-10-10 22:09:16 +00:00
;; what happens when our virtual machine starts up
2014-08-19 17:31:58 +00:00
(= initialization-fns* (queue))
(def reset ()
(each f (as cons initialization-fns*)
(f)))
(mac on-init body
`(enq (fn () ,@body)
initialization-fns*))
(mac init-fn (name . body)
2014-11-27 08:34:29 +00:00
`(enq (fn ()
;? (prn ',name)
2014-11-27 16:49:18 +00:00
(= (function* ',name) (convert-names:convert-labels:convert-braces:insert-code ',body ',name)))
initialization-fns*))
2014-10-10 22:09:16 +00:00
;; persisting and checking traces for each test
2014-10-07 17:26:14 +00:00
(= traces* (queue))
(= trace-dir* ".traces/")
(ensure-dir trace-dir*)
(= curr-trace-file* nil)
(on-init
2014-10-07 17:26:14 +00:00
(awhen curr-trace-file*
(tofile (+ trace-dir* it)
(each (label trace) (as cons traces*)
(pr label ": " trace))))
(= curr-trace-file* nil)
(= traces* (queue)))
2014-10-07 17:26:14 +00:00
(def new-trace (filename)
2014-12-14 15:24:42 +00:00
(prn "new-trace " filename)
;? )
2014-10-07 17:26:14 +00:00
(= curr-trace-file* filename))
2014-10-07 20:26:01 +00:00
(= dump-trace* nil)
(def trace (label . args)
(when (or (is dump-trace* t)
2014-11-21 22:40:03 +00:00
(and dump-trace* (is label "-"))
2014-11-06 18:21:15 +00:00
(and dump-trace* (pos label dump-trace*!whitelist))
(and dump-trace* (no dump-trace*!whitelist) (~pos label dump-trace*!blacklist)))
(apply prn label ": " args))
(enq (list label (apply tostring:prn args))
traces*))
2014-11-21 22:40:03 +00:00
(redef tr args ; why am I still returning to prn when debugging? Will this help?
(do1 nil
(apply trace "-" args)))
2014-11-22 02:21:15 +00:00
(def tr2 (msg arg)
(tr msg arg)
arg)
(def check-trace-contents (msg expected-contents)
(unless (trace-contents-match expected-contents)
(prn "F - " msg)
(prn " trace contents")
(print-trace-contents-mismatch expected-contents)))
(def trace-contents-match (expected-contents)
(each (label msg) (as cons traces*)
(when (and expected-contents
(is label expected-contents.0.0)
(posmatch expected-contents.0.1 msg))
(pop expected-contents)))
(no expected-contents))
(def print-trace-contents-mismatch (expected-contents)
(each (label msg) (as cons traces*)
(whenlet (expected-label expected-msg) expected-contents.0
(if (and (is label expected-label)
(posmatch expected-msg msg))
(do (pr " * ")
(pop expected-contents))
(pr " "))
(pr label ": " msg)))
(prn " couldn't find")
(each (expected-label expected-msg) expected-contents
(prn " ! " expected-label ": " expected-msg)))
2014-11-25 05:56:12 +00:00
;; virtual machine state
; things that a future assembler will need separate memory for:
; code; types; args channel
(def clear ()
(= types* (table))
(= memory* (table))
(= function* (table))
2014-11-27 13:24:24 +00:00
)
2014-11-25 05:56:12 +00:00
(enq clear initialization-fns*)
(on-init
(= types* (obj
; Each type must be scalar or array, sum or product or primitive
type (obj size 1) ; implicitly scalar and primitive
type-address (obj size 1 address t elem 'type)
type-array (obj array t elem 'type)
type-array-address (obj size 1 address t elem 'type-array)
location (obj size 1 address t elem 'location) ; assume it points to an atom
integer (obj size 1)
boolean (obj size 1)
boolean-address (obj size 1 address t elem 'boolean)
byte (obj size 1)
2014-11-27 08:34:29 +00:00
byte-address (obj size 1 address t elem 'byte)
string (obj array t elem 'byte) ; inspired by Go
2014-11-27 06:48:48 +00:00
string-address (obj size 1 address t elem 'string)
2014-11-25 05:56:12 +00:00
character (obj size 1) ; int32 like a Go rune
character-address (obj size 1 address t elem 'character)
; isolating function calls
scope (obj array t elem 'location) ; by convention index 0 points to outer scope
scope-address (obj size 1 address t elem 'scope)
; arrays consist of an integer length followed by the right number of elems
integer-array (obj array t elem 'integer)
integer-array-address (obj size 1 address t elem 'integer-array)
2014-11-29 03:28:13 +00:00
integer-array-address-address (obj size 1 address t elem 'integer-array-address)
2014-11-25 05:56:12 +00:00
integer-address (obj size 1 address t elem 'integer) ; pointer to int
2014-11-29 00:55:47 +00:00
integer-address-address (obj size 1 address t elem 'integer-address)
2014-11-25 05:56:12 +00:00
; records consist of a series of elems, corresponding to a list of types
integer-boolean-pair (obj size 2 record t elems '(integer boolean) fields '(int bool))
integer-boolean-pair-address (obj size 1 address t elem 'integer-boolean-pair)
integer-boolean-pair-array (obj array t elem 'integer-boolean-pair)
integer-boolean-pair-array-address (obj size 1 address t elem 'integer-boolean-pair-array)
integer-integer-pair (obj size 2 record t elems '(integer integer))
integer-point-pair (obj size 2 record t elems '(integer integer-integer-pair))
integer-point-pair-address (obj size 1 address t elem 'integer-point-pair)
2014-11-29 02:40:47 +00:00
integer-point-pair-address-address (obj size 1 address t elem 'integer-point-pair-address)
2014-11-25 05:56:12 +00:00
; tagged-values are the foundation of dynamic types
tagged-value (obj size 2 record t elems '(type location) fields '(type payload))
tagged-value-address (obj size 1 address t elem 'tagged-value)
tagged-value-array (obj array t elem 'tagged-value)
tagged-value-array-address (obj size 1 address t elem 'tagged-value-array)
tagged-value-array-address-address (obj size 1 address t elem 'tagged-value-array-address)
; heterogeneous lists
list (obj size 2 record t elems '(tagged-value list-address) fields '(car cdr))
list-address (obj size 1 address t elem 'list)
list-address-address (obj size 1 address t elem 'list-address)
; parallel routines use channels to synchronize
channel (obj size 3 record t elems '(integer integer tagged-value-array-address) fields '(first-full first-free circular-buffer))
channel-address (obj size 1 address t elem 'channel)
; editor
line (obj array t elem 'character)
line-address (obj size 1 address t elem 'line)
line-address-address (obj size 1 address t elem 'line-address)
screen (obj array t elem 'line-address)
screen-address (obj size 1 address t elem 'screen)
)))
2014-07-06 07:07:03 +00:00
2014-11-06 18:22:33 +00:00
;; managing concurrent routines
2014-11-06 23:24:00 +00:00
; routine = runtime state for a serial thread of execution
(def make-routine (fn-name . args)
(annotate 'routine (obj alloc 1000 call-stack (list
(obj fn-name fn-name pc 0 args args caller-arg-idx 0)))))
2014-11-06 23:24:00 +00:00
(defextend empty (x) (isa x 'routine)
(no rep.x!call-stack))
(def stack (routine)
((rep routine) 'call-stack))
(mac push-stack (routine op)
`(push (obj fn-name ,op pc 0 caller-arg-idx 0)
((rep ,routine) 'call-stack)))
(mac pop-stack (routine)
`(pop ((rep ,routine) 'call-stack)))
(def top (routine)
stack.routine.0)
(def body (routine (o idx 0))
(function* stack.routine.idx!fn-name))
(mac pc (routine (o idx 0)) ; assignable
`((((rep ,routine) 'call-stack) ,idx) 'pc))
(mac caller-arg-idx (routine (o idx 0)) ; assignable
`((((rep ,routine) 'call-stack) ,idx) 'caller-arg-idx))
(mac caller-args (routine) ; assignable
`((((rep ,routine) 'call-stack) 0) 'args))
(mac results (routine) ; assignable
`((((rep ,routine) 'call-stack) 0) 'results))
2014-11-22 03:29:37 +00:00
(def waiting-for-exact-cycle? (routine)
2014-11-22 04:20:12 +00:00
(is 'literal rep.routine!sleep.1))
2014-11-22 03:29:37 +00:00
2014-11-22 04:25:41 +00:00
(def ready-to-wake-up (routine)
(assert no.routine*)
(if (is 'literal rep.routine!sleep.1)
(> curr-cycle* rep.routine!sleep.0)
(~is rep.routine!sleep.1 (memory* rep.routine!sleep.0))))
2014-11-22 04:25:41 +00:00
2014-11-06 18:22:33 +00:00
(on-init
(= running-routines* (queue)) ; simple round-robin scheduler
2014-11-06 23:38:00 +00:00
; set of sleeping routines; don't modify routines while they're in this table
(= sleeping-routines* (table))
(= completed-routines* nil) ; audit trail
2014-11-06 18:22:33 +00:00
(= routine* nil)
(= abort-routine* (parameter nil))
2014-11-06 23:24:16 +00:00
(= curr-cycle* 0)
(= scheduling-interval* 500)
(= scheduler-switch-table* nil) ; hook into scheduler for tests
2014-11-06 23:24:16 +00:00
)
2014-11-06 18:22:33 +00:00
; like arc's 'point' but you can also call ((abort-routine*)) in nested calls
(mac routine-mark body
(w/uniq (g p)
`(ccc (fn (,g)
(parameterize abort-routine* (fn ((o ,p)) (,g ,p))
,@body)))))
(def run fn-names
(freeze-functions)
2014-11-27 07:23:44 +00:00
(= traces* (queue))
(each it fn-names
(enq make-routine.it running-routines*))
(while (~empty running-routines*)
(= routine* deq.running-routines*)
(trace "schedule" top.routine*!fn-name)
(routine-mark
(run-for-time-slice scheduling-interval*))
2014-11-22 03:29:37 +00:00
(update-scheduler-state)
;? (tr "after run iter " running-routines*)
;? (tr "after run iter " empty.running-routines*)
))
; prepare next iteration of round-robin scheduler
;
; state before: routine* running-routines* sleeping-routines*
; state after: running-routines* (with next routine to run at head) sleeping-routines*
;
; responsibilities:
; add routine* to either running-routines* or sleeping-routines* or completed-routines*
; wake up any necessary sleeping routines (either by time or on a location)
; detect deadlock: kill all sleeping routines when none can be woken
(def update-scheduler-state ()
;? (trace "schedule" curr-cycle*)
(when routine*
(if
rep.routine*!sleep
(do (trace "schedule" "pushing " top.routine*!fn-name " to sleep queue")
(set sleeping-routines*.routine*))
(~empty routine*)
2014-11-22 03:31:30 +00:00
(do (trace "schedule" "scheduling " top.routine*!fn-name " for further processing")
(enq routine* running-routines*))
:else
(do (trace "schedule" "done with routine")
(push routine* completed-routines*)))
(= routine* nil))
;? (tr 111)
2014-11-22 03:29:37 +00:00
(each (routine _) canon.sleeping-routines*
2014-11-22 04:25:41 +00:00
(when (ready-to-wake-up routine)
(trace "schedule" "waking up " top.routine!fn-name)
(wipe sleeping-routines*.routine) ; do this before modifying routine
(wipe rep.routine!sleep)
(++ pc.routine)
(enq routine running-routines*)))
;? (tr 112)
2014-11-22 03:29:37 +00:00
(when (empty running-routines*)
(whenlet exact-sleeping-routines (keep waiting-for-exact-cycle? keys.sleeping-routines*)
(let next-wakeup-cycle (apply min (map [rep._!sleep 0] exact-sleeping-routines))
(= curr-cycle* (+ 1 next-wakeup-cycle))
(trace "schedule" "skipping to cycle " curr-cycle*)
(update-scheduler-state))))
;? (tr 113)
(detect-deadlock)
;? (tr 114)
)
(def detect-deadlock ()
(when (and (empty running-routines*)
(~empty sleeping-routines*)
(~some 'literal (map (fn(_) rep._!sleep.1)
keys.sleeping-routines*)))
(each (routine _) sleeping-routines*
(wipe sleeping-routines*.routine)
(= rep.routine!error "deadlock detected")
(push routine completed-routines*))))
2014-11-06 18:22:33 +00:00
(def die (msg)
(tr "die: " msg)
2014-11-06 18:22:33 +00:00
(= rep.routine*!error msg)
(= rep.routine*!stack-trace rep.routine*!call-stack)
(wipe rep.routine*!call-stack)
2014-11-29 01:43:33 +00:00
(iflet abort-continuation (abort-routine*)
(abort-continuation)))
2014-11-06 18:22:33 +00:00
;; running a single routine
; operand accessors
2014-11-07 19:56:34 +00:00
(def nondummy (operand) ; precondition for helpers below
(~is '_ operand))
2014-10-29 07:18:58 +00:00
(mac v (operand) ; for value
`((,operand 0) 0))
2014-07-31 08:53:14 +00:00
; routines consist of instrs
; instrs consist of oargs, op and args
(def parse-instr (instr)
(iflet delim (pos '<- instr)
(list (cut instr 0 delim) ; oargs
(v (instr (+ delim 1))) ; op
(cut instr (+ delim 2))) ; args
(list nil (v car.instr) cdr.instr)))
2014-08-21 02:49:05 +00:00
(def metadata (operand)
cdr.operand)
2014-08-21 02:49:05 +00:00
(def ty (operand)
(cdr operand.0))
2014-07-31 08:53:14 +00:00
2014-08-22 02:55:16 +00:00
(def typeinfo (operand)
(or (types* ty.operand.0)
2014-11-07 08:59:23 +00:00
(err "unknown type @(tostring prn.operand)")))
2014-08-22 02:55:16 +00:00
($:require "charterm/main.rkt")
; run instructions from 'routine*' for 'time-slice'
2014-10-29 17:26:25 +00:00
(def run-for-time-slice (time-slice)
2014-08-28 23:40:28 +00:00
(point return
(for ninstrs 0 (< ninstrs time-slice) (++ ninstrs)
(if (empty body.routine*) (err "@stack.routine*.0!fn-name not defined"))
(while (>= pc.routine* (len body.routine*))
(pop-stack routine*)
(if empty.routine* (return ninstrs))
(++ pc.routine*))
(++ curr-cycle*)
2014-11-05 02:33:07 +00:00
(trace "run" "-- " int-canon.memory*)
(trace "run" curr-cycle* " " top.routine*!fn-name " " pc.routine* ": " (body.routine* pc.routine*))
;? (trace "run" routine*)
2014-11-23 06:26:11 +00:00
(when (atom (body.routine* pc.routine*)) ; label
(when (aand scheduler-switch-table*
(alref it (body.routine* pc.routine*)))
(++ pc.routine*)
(trace "run" "context-switch forced " abort-routine*)
((abort-routine*)))
2014-11-23 06:26:11 +00:00
(++ pc.routine*)
(continue))
(let (oarg op arg) (parse-instr (body.routine* pc.routine*))
2014-11-18 18:53:33 +00:00
(let results
(case op
; arithmetic
add
2014-10-07 20:26:01 +00:00
(do (trace "add" (m arg.0) " " (m arg.1))
(+ (m arg.0) (m arg.1))
2014-10-07 20:26:01 +00:00
)
subtract
(- (m arg.0) (m arg.1))
multiply
(* (m arg.0) (m arg.1))
divide
(/ (real (m arg.0)) (m arg.1))
divide-with-remainder
2014-08-27 05:00:23 +00:00
(list (trunc:/ (m arg.0) (m arg.1))
(mod (m arg.0) (m arg.1)))
; boolean
and
(and (m arg.0) (m arg.1))
or
(or (m arg.0) (m arg.1))
not
(not (m arg.0))
; comparison
equal
(is (m arg.0) (m arg.1))
not-equal
2014-10-07 20:26:01 +00:00
(do (trace "neq" (m arg.0) " " (m arg.1))
(~is (m arg.0) (m arg.1))
2014-10-07 20:26:01 +00:00
)
less-than
(< (m arg.0) (m arg.1))
greater-than
(> (m arg.0) (m arg.1))
lesser-or-equal
(<= (m arg.0) (m arg.1))
greater-or-equal
(>= (m arg.0) (m arg.1))
; control flow
2014-10-15 00:51:30 +00:00
jump
(do (= pc.routine* (+ 1 pc.routine* (v arg.0)))
2014-11-07 20:33:12 +00:00
(trace "jump" "jumping to " pc.routine*)
(continue))
2014-10-15 00:51:30 +00:00
jump-if
2014-11-07 20:33:12 +00:00
(let flag (m arg.0)
(trace "jump" "checking that " flag " is t")
(when (is t flag)
(= pc.routine* (+ 1 pc.routine* (v arg.1)))
(trace "jump" "jumping to " pc.routine*)
(continue)))
jump-unless ; convenient helper
2014-11-07 20:33:12 +00:00
(let flag (m arg.0)
(trace "jump" "checking that " flag " is not t")
(unless (is t flag)
(= pc.routine* (+ 1 pc.routine* (v arg.1)))
(trace "jump" "jumping to " pc.routine*)
(continue)))
; data management: scalars, arrays, records
copy
(m arg.0)
2014-08-20 04:33:48 +00:00
get
2014-11-29 04:08:24 +00:00
(with (operand (canonize arg.0)
idx (v arg.1))
(assert (iso '(offset) (ty arg.1)) "record index @arg.1 must have type 'offset'")
2014-11-29 04:08:24 +00:00
(assert (< -1 idx (len typeinfo.operand!elems)) "@idx is out of bounds of record @operand")
(m `((,(apply + v.operand
(map sizeof (firstn idx typeinfo.operand!elems)))
,typeinfo.operand!elems.idx)
(global))))
2014-10-05 18:34:23 +00:00
get-address
2014-11-29 04:08:24 +00:00
(with (operand (canonize arg.0)
idx (v arg.1))
(assert (iso '(offset) (ty arg.1)) "record index @arg.1 must have type 'offset'")
2014-11-29 04:08:24 +00:00
(assert (< -1 idx (len typeinfo.operand!elems)) "@idx is out of bounds of record @operand")
(apply + v.operand
(map sizeof (firstn idx typeinfo.operand!elems))))
index
2014-11-29 04:08:24 +00:00
(withs (operand (canonize arg.0)
elemtype typeinfo.operand!elem
idx (m arg.1))
(unless (< -1 idx array-len.operand)
(die "@idx is out of bounds of array @operand"))
(m `((,(+ v.operand
1 ; for array size
(* idx sizeof.elemtype))
,elemtype)
(global))))
index-address
2014-11-29 04:08:24 +00:00
(withs (operand (canonize arg.0)
elemtype typeinfo.operand!elem
idx (m arg.1))
(unless (< -1 idx array-len.operand)
(die "@idx is out of bounds of array @operand"))
(+ v.operand
1 ; for array size
(* idx sizeof.elemtype)))
2014-08-31 18:20:28 +00:00
new
(if (isa arg.0 'string)
; special-case: allocate space for a literal string
(new-string arg.0)
(let type (v arg.0)
(assert (iso '(literal) (ty arg.0)) "new: second arg @arg.0 must be literal")
(if (no types*.type) (err "no such type @type"))
; todo: initialize memory. currently racket does it for us
(if types*.type!array
(new-array type (m arg.1))
(new-scalar type))))
sizeof
2014-10-12 19:01:04 +00:00
(sizeof (m arg.0))
length
(let base arg.0
2014-11-01 09:16:16 +00:00
(if (or typeinfo.base!array typeinfo.base!address)
array-len.base
-1))
2014-10-05 03:19:12 +00:00
; tagged-values require one primitive
save-type
2014-10-25 09:32:30 +00:00
(annotate 'record `(,(ty arg.0) ,(m arg.0)))
2014-10-05 03:19:12 +00:00
; multiprocessing
run
(run (v arg.0))
fork
(let routine (apply make-routine (v car.arg) (map m cdr.arg))
;? (tr "before: " rep.routine*!alloc)
(= rep.routine!alloc rep.routine*!alloc)
(++ rep.routine*!alloc 1000)
;? (tr "after: " rep.routine*!alloc " " rep.routine!alloc)
(enq routine running-routines*))
2014-10-12 19:01:04 +00:00
; todo: errors should stall a process and let its parent
; inspect it
assert
(assert (m arg.0))
2014-11-06 23:38:00 +00:00
sleep
2014-11-07 03:05:20 +00:00
(let operand arg.0
; store sleep as either (<cycle number> literal) or (<location> <current value>)
(if (is ty.operand.0 'literal)
(let delay v.operand
(trace "run" "sleeping until " (+ curr-cycle* delay))
(= rep.routine*!sleep `(,(+ curr-cycle* delay) literal)))
(do
;? (tr "blocking on " operand " -> " (addr operand))
(= rep.routine*!sleep `(,addr.operand ,m.operand))))
2014-11-07 03:05:20 +00:00
((abort-routine*)))
2014-10-05 03:19:12 +00:00
2014-10-05 06:00:19 +00:00
; text interaction
clear-screen
2014-10-05 06:00:19 +00:00
(do1 nil ($.charterm-clear-screen))
clear-line
2014-10-05 06:00:19 +00:00
(do1 nil ($.charterm-clear-line))
cursor
(do1 nil ($.charterm-cursor (m arg.0) (m arg.1)))
2014-10-15 00:27:10 +00:00
print-primitive
2014-10-05 17:44:14 +00:00
(do1 nil ((if ($.current-charterm) $.charterm-display pr) (m arg.0)))
read-key
(and ($.charterm-byte-ready?) ($.charterm-read-key))
2014-10-05 06:00:19 +00:00
bold-mode
(do1 nil ($.charterm-bold))
non-bold-mode
(do1 nil ($.charterm-normal))
2014-10-05 17:44:14 +00:00
console-on
(do1 nil (if (no ($.current-charterm)) ($.open-charterm)))
2014-10-05 17:44:14 +00:00
console-off
(do1 nil (if ($.current-charterm) ($.close-charterm)))
2014-10-05 06:00:19 +00:00
; user-defined functions
next-input
(let idx caller-arg-idx.routine*
(++ caller-arg-idx.routine*)
(trace "arg" arg " " idx " " caller-args.routine*)
(if (len> caller-args.routine* idx)
(list caller-args.routine*.idx t)
(list nil nil)))
input
(do (assert (iso '(literal) (ty arg.0)))
(= caller-arg-idx.routine* (v arg.0))
(let idx caller-arg-idx.routine*
(++ caller-arg-idx.routine*)
(trace "arg" arg " " idx " " caller-args.routine*)
(if (len> caller-args.routine* idx)
(list caller-args.routine*.idx t)
(list nil nil))))
prepare-reply
2014-10-31 23:24:17 +00:00
(prepare-reply arg)
reply
2014-10-31 23:24:17 +00:00
(do (when arg
(prepare-reply arg))
2014-10-31 23:18:13 +00:00
(let results results.routine*
(pop-stack routine*)
2014-10-31 23:18:13 +00:00
(if empty.routine* (return ninstrs))
(let (caller-oargs _ _) (parse-instr (body.routine* pc.routine*))
(trace "reply" arg " " caller-oargs)
(each (dest val) (zip caller-oargs results)
2014-11-07 19:56:34 +00:00
(when nondummy.dest
(trace "reply" val " => " dest)
(setm dest val))))
2014-10-31 23:18:13 +00:00
(++ pc.routine*)
(while (>= pc.routine* (len body.routine*))
(pop-stack routine*)
(when empty.routine* (return ninstrs))
(++ pc.routine*))
(continue)))
; else try to call as a user-defined function
2014-10-05 18:33:25 +00:00
(do (if function*.op
(let callee-args (accum yield
(each a arg
(yield (m a))))
(push-stack routine* op)
(= caller-args.routine* callee-args))
2014-10-05 18:33:25 +00:00
(err "no such op @op"))
(continue))
)
2014-11-18 18:53:33 +00:00
; opcode generated some 'results'
; copy to output args
2014-11-18 18:53:33 +00:00
(if (acons results)
(each (dest val) (zip oarg results)
2014-11-07 19:57:45 +00:00
(unless (is dest '_)
2014-11-18 19:02:22 +00:00
(trace "run" val " => " dest)
2014-11-07 19:57:45 +00:00
(setm dest val)))
(when oarg ; must be a list
2014-11-18 18:58:51 +00:00
(trace "run" results " => " oarg.0)
2014-11-18 18:53:33 +00:00
(setm oarg.0 results)))
)
(++ pc.routine*)))
(return time-slice)))
2014-07-06 07:07:03 +00:00
2014-10-31 23:24:17 +00:00
(def prepare-reply (args)
(= results.routine*
(accum yield
(each a args
(yield (m a))))))
; helpers for memory access respecting
; immediate addressing - 'literal' and 'offset'
; direct addressing - default
; indirect addressing - 'deref'
; relative addressing - if routine* has 'default-scope'
(def m (loc) ; read memory, respecting metadata
(point return
(if (in ty.loc.0 'literal 'offset)
(return v.loc))
(when (is v.loc 'default-scope)
(return rep.routine*!call-stack.0!default-scope))
(trace "m" loc)
(assert (isa v.loc 'int) "addresses must be numeric (problem in convert-names?) @loc")
(with (n sizeof.loc
addr addr.loc)
;? (trace "m" "reading " n " locations starting at " addr)
(if (is 1 n)
2014-11-29 02:19:09 +00:00
memory*.addr
:else
(annotate 'record
(map memory* (addrs addr n)))))))
(def setm (loc val) ; set memory, respecting metadata
(point return
(when (is v.loc 'default-scope)
(assert (is 1 sizeof.loc) "can't store compounds in default-scope @loc")
(= rep.routine*!call-stack.0!default-scope val)
(return))
(assert (isa v.loc 'int) "can't store to non-numeric address (problem in convert-names?)")
(trace "setm" loc " <= " val)
(with (n (if (isa val 'record) (len rep.val) 1)
addr addr.loc)
(trace "setm" "size of " loc " is " n)
(assert n "setm: can't compute type of @loc")
(assert addr "setm: null pointer @loc")
(if (is 1 n)
(do (assert (~isa val 'record) "setm: record of size 1 @(tostring prn.val)")
(trace "setm" loc ": setting " addr " to " val)
2014-11-29 02:19:09 +00:00
(= memory*.addr val))
2014-11-29 02:58:38 +00:00
(do (if ((types* typeof.loc) 'array)
; size check for arrays
2014-11-29 08:57:06 +00:00
(when (~is n
(+ 1 ; array length
(* rep.val.0 (sizeof ((types* typeof.loc) 'elem)))))
2014-11-29 02:58:38 +00:00
(die "writing invalid array @(tostring prn.val)"))
; size check for non-arrays
(when (~is sizeof.loc n)
(die "writing to incorrect size @(tostring prn.val) => @loc")))
2014-11-29 02:40:47 +00:00
(let addrs (addrs addr n)
(each (dest src) (zip addrs rep.val)
(trace "setm" loc ": setting " dest " to " src)
(= memory*.dest src))))))))
(def typeof (operand)
(let loc absolutize.operand
(while (pos '(deref) metadata.loc)
(zap deref loc))
ty.loc.0))
(def addr (operand)
;? (prn 211 " " operand)
(let loc absolutize.operand
;? (prn 212 " " loc)
(while (pos '(deref) metadata.loc)
;? (prn 213 " " loc)
(zap deref loc))
;? (prn 214 " " loc)
v.loc))
(def addrs (n sz)
(accum yield
(repeat sz
(yield n)
(++ n))))
2014-11-29 04:08:24 +00:00
(def canonize (operand)
(ret operand
(zap absolutize operand)
(while (pos '(deref) metadata.operand)
2014-11-29 04:08:24 +00:00
(zap deref operand))))
(def array-len (operand)
(trace "array-len" operand)
2014-11-29 09:05:39 +00:00
(zap canonize operand)
(if typeinfo.operand!array
(m `((,v.operand integer) ,@metadata.operand))
:else
(err "can't take len of non-array @operand")))
(def sizeof (x)
(trace "sizeof" x)
(point return
2014-11-29 08:57:06 +00:00
(when (acons x)
2014-11-29 09:05:39 +00:00
(zap canonize x)
;? (tr "sizeof 1 @x")
2014-11-29 08:57:06 +00:00
(when typeinfo.x!array
;? (tr "sizeof 2")
2014-11-29 09:44:06 +00:00
(return (+ 1 (* array-len.x (sizeof typeinfo.x!elem))))))
;? (tr "sizeof 3")
(let type (if (and acons.x (pos '(deref) metadata.x))
typeinfo.x!elem ; deref pointer
acons.x
ty.x.0
:else ; naked type
x)
(assert types*.type "sizeof: no such type @type")
(if (~or types*.type!record types*.type!array)
types*.type!size
types*.type!record
(sum idfn
(accum yield
(each elem types*.type!elems
(yield sizeof.elem))))
:else
(err "sizeof can't handle @type (arrays require a specific variable)")))))
2014-11-29 01:43:33 +00:00
(def absolutize (operand)
(if (no routine*)
operand
(pos '(global) metadata.operand)
2014-11-29 01:43:33 +00:00
operand
:else
(iflet base rep.routine*!call-stack.0!default-scope
;? (do (prn 313 " " operand " " base)
2014-11-29 01:43:33 +00:00
(if (< v.operand memory*.base)
`((,(+ v.operand base) ,@(cdr operand.0))
,@metadata.operand
(global))
2014-11-29 01:43:33 +00:00
(die "no room for var @operand in routine of size @memory*.base"))
;? )
2014-11-29 01:43:33 +00:00
operand)))
(def deref (operand)
(assert (pos '(deref) metadata.operand))
(assert typeinfo.operand!address)
(cons `(,(memory* v.operand) ,typeinfo.operand!elem)
(drop-one '(deref) metadata.operand)))
(def drop-one (f x)
(when acons.x ; proper lists only
(if (testify.f car.x)
cdr.x
(cons car.x (drop-one f x)))))
; memory allocation
(def new-scalar (type)
;? (tr "new scalar: @type")
(ret result rep.routine*!alloc
(++ rep.routine*!alloc sizeof.type)))
(def new-array (type size)
;? (tr "new array: @type @size")
(ret result rep.routine*!alloc
(++ rep.routine*!alloc (+ 1 (* (sizeof types*.type!elem) size)))
2014-11-29 02:19:09 +00:00
(= memory*.result size)))
(def new-string (literal-string)
;? (tr "new string: @literal-string")
(ret result rep.routine*!alloc
(= (memory* rep.routine*!alloc) len.literal-string)
(++ rep.routine*!alloc)
(each c literal-string
(= (memory* rep.routine*!alloc) c)
(++ rep.routine*!alloc))))
2014-10-10 22:09:16 +00:00
;; desugar structured assembly based on blocks
(def convert-braces (instrs)
(let locs () ; list of information on each brace: (open/close pc)
(let pc 0
(loop (instrs instrs)
(each instr instrs
;? (tr instr)
2014-11-23 06:26:11 +00:00
(if (or atom.instr (~is 'begin instr.0)) ; label or regular instruction
(do
(trace "c{0" pc " " instr " -- " locs)
(++ pc))
2014-11-27 16:48:38 +00:00
; hack: racket replaces braces with parens, so we need the
2014-11-25 02:52:15 +00:00
; keyword 'begin' to delimit blocks.
2014-11-27 16:48:38 +00:00
; ultimately there'll be no nesting and braces will just be
2014-11-25 02:52:15 +00:00
; in an instr by themselves.
2014-11-23 06:26:11 +00:00
:else ; brace
(do
(push `(open ,pc) locs)
(recur cdr.instr)
(push `(close ,pc) locs))))))
(zap rev locs)
;? (tr "-")
(with (pc 0
stack ()) ; elems are pcs
(accum yield
(loop (instrs instrs)
(each instr instrs
;? (tr "- " instr)
2014-10-07 20:26:01 +00:00
(point continue
2014-11-23 06:26:11 +00:00
(when (atom instr) ; label
(yield instr)
(++ pc)
2014-11-23 06:26:11 +00:00
(continue))
(when (is car.instr 'begin)
(push pc stack)
(recur cdr.instr)
(pop stack)
(continue))
2014-12-14 17:49:57 +00:00
(let (oarg op arg) (parse-instr instr)
(trace "c{1" pc " " op " " oarg)
(case op
break
(do
2014-11-27 16:56:59 +00:00
(assert (is oarg nil) "break: can't take oarg in @instr")
(yield `(((jump)) ((,(close-offset pc locs (and arg (v arg.0))) offset)))))
2014-10-15 00:51:30 +00:00
break-if
(do
2014-11-27 16:56:59 +00:00
(assert (is oarg nil) "break-if: can't take oarg in @instr")
(yield `(((jump-if)) ,arg.0 ((,(close-offset pc locs (and cdr.arg (v arg.1))) offset)))))
break-unless
(do
2014-11-27 16:56:59 +00:00
(assert (is oarg nil) "break-unless: can't take oarg in @instr")
(yield `(((jump-unless)) ,arg.0 ((,(close-offset pc locs (and cdr.arg (v arg.1))) offset)))))
loop
(do
2014-11-27 16:56:59 +00:00
(assert (is oarg nil) "loop: can't take oarg in @instr")
(yield `(((jump)) ((,(open-offset pc stack (and arg (v arg.0))) offset)))))
loop-if
(do
(trace "cvt0" "loop-if: " instr " => " (- stack.0 1))
2014-11-27 16:56:59 +00:00
(assert (is oarg nil) "loop-if: can't take oarg in @instr")
(yield `(((jump-if)) ,arg.0 ((,(open-offset pc stack (and cdr.arg (v arg.1))) offset)))))
loop-unless
(do
(trace "cvt0" "loop-if: " instr " => " (- stack.0 1))
2014-11-27 16:56:59 +00:00
(assert (is oarg nil) "loop-unless: can't take oarg in @instr")
(yield `(((jump-unless)) ,arg.0 ((,(open-offset pc stack (and cdr.arg (v arg.1))) offset)))))
;else
2014-12-14 17:49:57 +00:00
(yield instr)))
2014-10-07 20:26:01 +00:00
(++ pc))))))))
2014-11-27 17:30:43 +00:00
(def close-offset (pc locs nblocks)
(or= nblocks 1)
;? (tr nblocks)
(point return
;? (tr "close " pc " " locs)
(let stacksize 0
(each (state loc) locs
(point continue
;? (tr stacksize "/" done " " state " " loc)
(when (<= loc pc)
(continue))
;? (tr "process " stacksize loc)
(if (is 'open state) (++ stacksize) (-- stacksize))
; last time
;? (tr "process2 " stacksize loc)
2014-11-27 17:30:43 +00:00
(when (is stacksize (* -1 nblocks))
;? (tr "close now " loc)
(return (- loc pc 1))))))))
2014-11-27 17:30:43 +00:00
(def open-offset (pc stack nblocks)
(or= nblocks 1)
(- (stack (- nblocks 1)) 1 pc))
2014-11-27 16:49:18 +00:00
;; convert jump targets to offsets
(def convert-labels (instrs)
;? (tr "convert-labels " instrs)
(let labels (table)
(let pc 0
(each instr instrs
(when (~acons instr)
;? (tr "label " pc)
(= labels.instr pc))
(++ pc)))
(let pc 0
(each instr instrs
(when (and acons.instr
(acons car.instr)
(in (v car.instr) 'jump 'jump-if 'jump-unless))
2014-11-27 16:49:18 +00:00
(each arg cdr.instr
;? (tr "trying " arg " " ty.arg ": " v.arg " => " (labels v.arg))
(when (and (is ty.arg.0 'offset)
2014-11-27 16:49:18 +00:00
(isa v.arg 'sym)
(labels v.arg))
(= v.arg (- (labels v.arg) pc 1)))))
(++ pc))))
instrs)
2014-11-07 19:59:22 +00:00
;; convert symbolic names to raw memory locations
2014-10-29 07:18:58 +00:00
(def convert-names (instrs)
2014-12-13 22:00:14 +00:00
;? (tr "convert-names " instrs)
2014-11-07 19:59:22 +00:00
(with (location (table)
2014-11-04 06:34:58 +00:00
isa-field (table))
2014-10-29 07:18:58 +00:00
(let idx 1
(each instr instrs
2014-11-23 06:26:11 +00:00
(point continue
(when atom.instr
(continue))
2014-11-07 19:59:22 +00:00
(trace "cn0" instr " " canon.location " " canon.isa-field)
2014-10-29 07:18:58 +00:00
(let (oargs op args) (parse-instr instr)
2014-11-04 06:34:58 +00:00
(if (in op 'get 'get-address)
2014-11-04 07:58:06 +00:00
(with (basetype (typeinfo args.0)
2014-11-04 06:34:58 +00:00
field (v args.1))
(assert basetype "no such type @args.0")
(trace "cn0" "field-access " field)
2014-11-04 21:43:57 +00:00
; todo: need to rename args.0 as well?
(when (pos '(deref) (metadata args.0))
(trace "cn0" "field-access deref")
2014-11-04 07:58:06 +00:00
(assert basetype!address "@args.0 requests deref, but it's not an address of a record")
(= basetype (types* basetype!elem)))
2014-11-04 06:34:58 +00:00
(when (isa field 'sym)
2014-11-07 19:59:22 +00:00
(assert (or (~location field) isa-field.field) "field @args.1 is also a variable")
(when (~location field)
(trace "cn0" "new field; computing location")
2014-11-07 08:56:42 +00:00
(assert basetype!fields "no field names available for @instr")
(iflet idx (pos field basetype!fields)
(do (set isa-field.field)
2014-11-07 20:01:20 +00:00
(trace "cn0" "field location @idx")
2014-11-07 19:59:22 +00:00
(= location.field idx))
2014-11-07 08:56:42 +00:00
(assert nil "couldn't find field in @instr")))))
2014-11-04 06:34:58 +00:00
(each arg args
(assert (~isa-field v.arg) "arg @arg is also a field name")
2014-11-27 13:24:24 +00:00
(when (maybe-add arg location idx)
(err "use before set: @arg"))))
2014-10-29 07:18:58 +00:00
(each arg oargs
(trace "cn0" "checking " arg)
(unless (is arg '_)
(assert (~isa-field v.arg) "oarg @arg is also a field name")
2014-11-07 19:59:22 +00:00
(when (maybe-add arg location idx)
(trace "cn0" "location for arg " arg ": " idx)
2014-11-27 08:34:29 +00:00
; todo: can't allocate arrays on the stack
2014-12-14 16:06:32 +00:00
(++ idx (sizeof ty.arg.0)))))))))
2014-11-07 19:59:22 +00:00
(trace "cn1" "update names " canon.location " " canon.isa-field)
2014-10-29 07:18:58 +00:00
(each instr instrs
2014-11-23 06:26:11 +00:00
(when (acons instr)
(let (oargs op args) (parse-instr instr)
(each arg args
(when (and nondummy.arg (location v.arg))
(zap location v.arg)))
(each arg oargs
(when (and nondummy.arg (location v.arg))
(zap location v.arg))))))
2014-10-29 07:18:58 +00:00
instrs))
2014-11-07 19:59:22 +00:00
(def maybe-add (arg location idx)
(trace "maybe-add" arg)
2014-11-07 19:56:34 +00:00
(when (and nondummy.arg
2014-12-14 16:06:32 +00:00
(~in ty.arg.0 'literal 'offset 'fn)
2014-11-07 19:59:22 +00:00
(~location v.arg)
2014-11-04 06:38:28 +00:00
(isa v.arg 'sym)
(~in v.arg 'nil 'default-scope)
2014-12-14 16:06:32 +00:00
(~assoc 'global metadata.arg))
2014-11-07 19:59:22 +00:00
(= (location v.arg) idx)))
2014-10-29 07:18:58 +00:00
;; literate tangling system for reordering code
(def convert-quotes (instrs)
(let deferred (queue)
(each instr instrs
2014-11-25 02:56:15 +00:00
(when (acons instr)
(case instr.0
defer
(let (q qinstrs) instr.1
(assert (is 'make-br-fn q) "defer: first arg must be [quoted]")
(each qinstr qinstrs
(enq qinstr deferred))))))
(accum yield
(each instr instrs
2014-12-04 02:12:44 +00:00
(if atom.instr ; label
(yield instr)
(is instr.0 'defer)
nil ; skip
(is instr.0 'reply)
(do
(when cdr.instr ; return values
(= instr.0 'prepare-reply)
(yield instr))
2014-12-04 02:12:44 +00:00
(each instr (as cons deferred)
(yield instr))
(yield '(reply)))
2014-12-04 02:12:44 +00:00
:else
(yield instr)))
(each instr (as cons deferred)
(yield instr)))))
2014-11-25 03:27:52 +00:00
(on-init
(= before* (table)) ; label -> queue of fragments
(= after* (table))) ; label -> list of fragments
; see add-code below for adding to before* and after*
2014-11-25 03:27:52 +00:00
(def insert-code (instrs (o name))
2014-12-13 22:00:14 +00:00
;? (tr "insert-code " instrs)
(loop (instrs instrs)
(accum yield
(each instr instrs
(if (and (acons instr) (~is 'begin car.instr))
; simple instruction
(yield instr)
(and (acons instr) (is 'begin car.instr))
; block
(yield `{begin ,@(recur cdr.instr)})
(atom instr)
; label
(do
;? (prn "tangling " instr)
(each fragment (as cons (or (and name (before* (sym:string name '/ instr)))
before*.instr))
(each instr fragment
(yield instr)))
(yield instr)
(each fragment (or (and name (after* (sym:string name '/ instr)))
after*.instr)
(each instr fragment
(yield instr)))))))))
2014-11-25 03:27:52 +00:00
;; system software
(section 100
(init-fn maybe-coerce
((default-scope scope-address) <- new (scope literal) (30 literal))
((x tagged-value-address) <- new (tagged-value literal))
((x tagged-value-address deref) <- next-input)
((p type) <- next-input)
((xtype type) <- get (x tagged-value-address deref) (0 offset))
((match? boolean) <- equal (xtype type) (p type))
{ begin
(break-if (match? boolean))
(reply (0 literal) (nil literal))
}
((xvalue location) <- get (x tagged-value-address deref) (1 offset))
(reply (xvalue location) (match? boolean)))
2014-10-12 19:01:04 +00:00
(init-fn new-tagged-value
((default-scope scope-address) <- new (scope literal) (30 literal))
; assert (sizeof arg.0) == 1
((xtype type) <- next-input)
((xtypesize integer) <- sizeof (xtype type))
((xcheck boolean) <- equal (xtypesize integer) (1 literal))
(assert (xcheck boolean))
2014-10-12 19:01:04 +00:00
; todo: check that arg 0 matches the type? or is that for the future typechecker?
((result tagged-value-address) <- new (tagged-value literal))
; result->type = arg 0
((resulttype location) <- get-address (result tagged-value-address deref) (type offset))
((resulttype location deref) <- copy (xtype type))
; result->payload = arg 1
((locaddr location) <- get-address (result tagged-value-address deref) (payload offset))
((locaddr location deref) <- next-input)
(reply (result tagged-value-address)))
2014-10-12 19:01:04 +00:00
2014-10-13 01:04:29 +00:00
(init-fn list-next ; list-address -> list-address
((default-scope scope-address) <- new (scope literal) (30 literal))
((base list-address) <- next-input)
((result list-address) <- get (base list-address deref) (cdr offset))
(reply (result list-address)))
2014-10-13 01:04:29 +00:00
(init-fn list-value-address ; list-address -> tagged-value-address
((default-scope scope-address) <- new (scope literal) (30 literal))
((base list-address) <- next-input)
((result tagged-value-address) <- get-address (base list-address deref) (car offset))
(reply (result tagged-value-address)))
2014-10-25 09:32:30 +00:00
(init-fn new-list
((default-scope scope-address) <- new (scope literal) (30 literal))
; new-list = curr = new list
((new-list-result list-address) <- new (list literal))
((curr list-address) <- copy (new-list-result list-address))
2014-10-25 09:32:30 +00:00
{ begin
; while read curr-value
((curr-value integer) (exists? boolean) <- next-input)
(break-unless (exists? boolean))
; curr.cdr = new list
((next list-address-address) <- get-address (curr list-address deref) (cdr offset))
((next list-address-address deref) <- new (list literal))
; curr = curr.cdr
((curr list-address) <- list-next (curr list-address))
; curr.car = (type curr-value)
((dest tagged-value-address) <- list-value-address (curr list-address))
((dest tagged-value-address deref) <- save-type (curr-value integer))
(loop)
2014-10-25 09:32:30 +00:00
}
; return new-list.cdr
((new-list-result list-address) <- list-next (new-list-result list-address)) ; memory leak
(reply (new-list-result list-address)))
2014-10-25 09:32:30 +00:00
2014-11-04 08:01:57 +00:00
(init-fn new-channel
((default-scope scope-address) <- new (scope literal) (30 literal))
; result = new channel
2014-11-04 08:01:57 +00:00
((result channel-address) <- new (channel literal))
; result.first-full = 0
2014-11-04 08:01:57 +00:00
((full integer-address) <- get-address (result channel-address deref) (first-full offset))
((full integer-address deref) <- copy (0 literal))
; result.first-free = 0
2014-11-04 08:01:57 +00:00
((free integer-address) <- get-address (result channel-address deref) (first-free offset))
((free integer-address deref) <- copy (0 literal))
; result.circular-buffer = new tagged-value[arg+1]
((capacity integer) <- next-input)
((capacity integer) <- add (capacity integer) (1 literal)) ; unused slot for full? below
2014-11-04 08:01:57 +00:00
((channel-buffer-address tagged-value-array-address-address) <- get-address (result channel-address deref) (circular-buffer offset))
((channel-buffer-address tagged-value-array-address-address deref) <- new (tagged-value-array literal) (capacity integer))
2014-11-04 08:01:57 +00:00
(reply (result channel-address)))
2014-11-18 12:39:24 +00:00
(init-fn capacity
((default-scope scope-address) <- new (scope literal) (30 literal))
((chan channel) <- next-input)
2014-11-18 12:39:24 +00:00
((q tagged-value-array-address) <- get (chan channel) (circular-buffer offset))
((qlen integer) <- length (q tagged-value-array-address deref))
2014-11-18 12:39:24 +00:00
(reply (qlen integer)))
2014-11-04 21:46:31 +00:00
(init-fn write
((default-scope scope-address) <- new (scope literal) (30 literal))
((chan channel-address) <- next-input)
((val tagged-value) <- next-input)
{ begin
; block if chan is full
((full boolean) <- full? (chan channel-address deref))
(break-unless (full boolean))
((full-address integer-address) <- get-address (chan channel-address deref) (first-full offset))
(sleep (full-address integer-address deref))
}
2014-11-23 03:50:21 +00:00
; store val
((q tagged-value-array-address) <- get (chan channel-address deref) (circular-buffer offset))
((free integer-address) <- get-address (chan channel-address deref) (first-free offset))
2014-11-04 21:46:31 +00:00
((dest tagged-value-address) <- index-address (q tagged-value-array-address deref) (free integer-address deref))
((dest tagged-value-address deref) <- copy (val tagged-value))
2014-11-23 03:50:21 +00:00
; increment free
2014-11-04 21:46:31 +00:00
((free integer-address deref) <- add (free integer-address deref) (1 literal))
2014-11-07 20:33:12 +00:00
{ begin
2014-11-23 03:50:21 +00:00
; wrap free around to 0 if necessary
((qlen integer) <- length (q tagged-value-array-address deref))
((remaining? boolean) <- less-than (free integer-address deref) (qlen integer))
2014-11-07 20:33:12 +00:00
(break-if (remaining? boolean))
((free integer-address deref) <- copy (0 literal))
}
(reply (chan channel-address deref)))
2014-11-04 21:46:31 +00:00
2014-11-05 02:35:13 +00:00
(init-fn read
((default-scope scope-address) <- new (scope literal) (30 literal))
((chan channel-address) <- next-input)
{ begin
; block if chan is empty
((empty boolean) <- empty? (chan channel-address deref))
(break-unless (empty boolean))
((free-address integer-address) <- get-address (chan channel-address deref) (first-free offset))
(sleep (free-address integer-address deref))
}
2014-11-23 03:50:21 +00:00
; read result
((full integer-address) <- get-address (chan channel-address deref) (first-full offset))
((q tagged-value-array-address) <- get (chan channel-address deref) (circular-buffer offset))
2014-11-05 02:35:13 +00:00
((result tagged-value) <- index (q tagged-value-array-address deref) (full integer-address deref))
2014-11-23 03:50:21 +00:00
; increment full
2014-11-05 02:35:13 +00:00
((full integer-address deref) <- add (full integer-address deref) (1 literal))
2014-11-07 20:33:12 +00:00
{ begin
2014-11-23 03:50:21 +00:00
; wrap full around to 0 if necessary
((qlen integer) <- length (q tagged-value-array-address deref))
((remaining? boolean) <- less-than (full integer-address deref) (qlen integer))
2014-11-07 20:33:12 +00:00
(break-if (remaining? boolean))
((full integer-address deref) <- copy (0 literal))
}
(reply (result tagged-value) (chan channel-address deref)))
2014-11-05 02:35:13 +00:00
; An empty channel has first-empty and first-full both at the same value.
(init-fn empty?
((default-scope scope-address) <- new (scope literal) (30 literal))
; return arg.first-full == arg.first-free
((chan channel) <- next-input)
((full integer) <- get (chan channel) (first-full offset))
((free integer) <- get (chan channel) (first-free offset))
((result boolean) <- equal (full integer) (free integer))
(reply (result boolean)))
; A full channel has first-empty just before first-full, wasting one slot.
; (Other alternatives: https://en.wikipedia.org/wiki/Circular_buffer#Full_.2F_Empty_Buffer_Distinction)
(init-fn full?
((default-scope scope-address) <- new (scope literal) (30 literal))
((chan channel) <- next-input)
; curr = chan.first-free + 1
((curr integer) <- get (chan channel) (first-free offset))
((curr integer) <- add (curr integer) (1 literal))
{ begin
; if (curr == chan.capacity) curr = 0
2014-11-18 12:39:24 +00:00
((qlen integer) <- capacity (chan channel))
((remaining? boolean) <- less-than (curr integer) (qlen integer))
(break-if (remaining? boolean))
((curr integer) <- copy (0 literal))
}
; return chan.first-full == curr
((full integer) <- get (chan channel) (first-full offset))
((result boolean) <- equal (full integer) (curr integer))
(reply (result boolean)))
2014-11-27 08:34:29 +00:00
(init-fn strcat
((default-scope scope-address) <- new (scope literal) (30 literal))
; result = new string[a.length + b.length]
((a string-address) <- next-input)
((a-len integer) <- length (a string-address deref))
((b string-address) <- next-input)
((b-len integer) <- length (b string-address deref))
2014-11-27 08:34:29 +00:00
((result-len integer) <- add (a-len integer) (b-len integer))
((result string-address) <- new (string literal) (result-len integer))
; copy a into result
2014-11-29 23:48:15 +00:00
((result-idx integer) <- copy (0 literal))
2014-11-27 08:34:29 +00:00
((i integer) <- copy (0 literal))
{ begin
; while (i < a.length)
((a-done? boolean) <- less-than (i integer) (a-len integer))
2014-11-27 08:34:29 +00:00
(break-unless (a-done? boolean))
; result[result-idx] = a[i]
((out byte-address) <- index-address (result string-address deref) (result-idx integer))
((in byte) <- index (a string-address deref) (i integer))
((out byte-address deref) <- copy (in byte))
; ++i
((i integer) <- add (i integer) (1 literal))
; ++result-idx
((result-idx integer) <- add (result-idx integer) (1 literal))
(loop)
}
; copy b into result
((i integer) <- copy (0 literal))
{ begin
; while (i < b.length)
((b-done? boolean) <- less-than (i integer) (b-len integer))
2014-11-27 08:34:29 +00:00
(break-unless (b-done? boolean))
; result[result-idx] = a[i]
((out byte-address) <- index-address (result string-address deref) (result-idx integer))
((in byte) <- index (b string-address deref) (i integer))
((out byte-address deref) <- copy (in byte))
; ++i
((i integer) <- add (i integer) (1 literal))
; ++result-idx
((result-idx integer) <- add (result-idx integer) (1 literal))
(loop)
}
(reply (result string-address)))
; replace underscores in first with remaining args
(init-fn interpolate ; string-address template, string-address a..
((default-scope scope-address) <- new (scope literal) (60 literal))
((template string-address) <- next-input)
2014-11-29 09:54:46 +00:00
; compute result-len, space to allocate for result
((tem-len integer) <- length (template string-address deref))
((result-len integer) <- copy (tem-len integer))
{ begin
; while arg received
((a string-address) (arg-received? boolean) <- next-input)
(break-unless (arg-received? boolean))
;? (print-primitive ("arg now: " literal))
;? (print-primitive (a string-address))
;? (print-primitive ("@" literal))
;? (print-primitive (a string-address deref)) ; todo: test (m on scoped array)
;? (print-primitive ("\n" literal))
;? ;? (assert (nil literal))
; result-len = result-len + arg.length - 1 (for the 'underscore' being replaced)
((a-len integer) <- length (a string-address deref))
((result-len integer) <- add (result-len integer) (a-len integer))
((result-len integer) <- subtract (result-len integer) (1 literal))
;? (print-primitive ("result-len now: " literal))
;? (print-primitive (result-len integer))
;? (print-primitive ("\n" literal))
(loop)
}
; rewind to start of non-template args
(_ <- input (0 literal))
; result = new string[result-len]
((result string-address) <- new (string literal) (result-len integer))
2014-11-29 09:54:46 +00:00
; repeatedly copy sections of template and 'holes' into result
((result-idx integer) <- copy (0 literal))
((i integer) <- copy (0 literal))
{ begin
; while arg received
((a string-address) (arg-received? boolean) <- next-input)
(break-unless (arg-received? boolean))
; copy template into result until '_'
{ begin
; while (i < template.length)
((tem-done? boolean) <- less-than (i integer) (tem-len integer))
(break-unless (tem-done? boolean) (2 blocks))
; while template[i] != '_'
((in byte) <- index (template string-address deref) (i integer))
((underscore? boolean) <- equal (in byte) (#\_ literal))
(break-if (underscore? boolean))
; result[result-idx] = template[i]
((out byte-address) <- index-address (result string-address deref) (result-idx integer))
((out byte-address deref) <- copy (in byte))
; ++i
((i integer) <- add (i integer) (1 literal))
; ++result-idx
((result-idx integer) <- add (result-idx integer) (1 literal))
(loop)
}
;? (print-primitive ("i now: " literal))
;? (print-primitive (i integer))
;? (print-primitive ("\n" literal))
; copy 'a' into result
((j integer) <- copy (0 literal))
{ begin
; while (j < a.length)
((arg-done? boolean) <- less-than (j integer) (a-len integer))
(break-unless (arg-done? boolean))
; result[result-idx] = a[j]
((in byte) <- index (a string-address deref) (j integer))
;? (print-primitive ("copying: " literal))
;? (print-primitive (in byte))
;? (print-primitive (" at: " literal))
;? (print-primitive (result-idx integer))
;? (print-primitive ("\n" literal))
((out byte-address) <- index-address (result string-address deref) (result-idx integer))
((out byte-address deref) <- copy (in byte))
; ++j
((j integer) <- add (j integer) (1 literal))
; ++result-idx
((result-idx integer) <- add (result-idx integer) (1 literal))
(loop)
}
; skip '_' in template
((i integer) <- add (i integer) (1 literal))
;? (print-primitive ("i now: " literal))
;? (print-primitive (i integer))
;? (print-primitive ("\n" literal))
(loop) ; interpolate next arg
}
2014-11-29 09:54:46 +00:00
; done with holes; copy rest of template directly into result
{ begin
; while (i < template.length)
((tem-done? boolean) <- less-than (i integer) (tem-len integer))
2014-11-29 09:54:46 +00:00
(break-unless (tem-done? boolean))
; result[result-idx] = template[i]
((in byte) <- index (template string-address deref) (i integer))
;? (print-primitive ("copying: " literal))
;? (print-primitive (in byte))
;? (print-primitive (" at: " literal))
;? (print-primitive (result-idx integer))
;? (print-primitive ("\n" literal))
((out byte-address) <- index-address (result string-address deref) (result-idx integer))
((out byte-address deref) <- copy (in byte))
; ++i
((i integer) <- add (i integer) (1 literal))
; ++result-idx
((result-idx integer) <- add (result-idx integer) (1 literal))
(loop)
}
(reply (result string-address)))
) ; section 100 for system software
2014-11-04 21:34:59 +00:00
(def canon (table)
2014-11-07 05:44:16 +00:00
(sort (compare < [tostring (prn:car _)]) (as cons table)))
2014-11-04 21:34:59 +00:00
2014-11-05 02:33:07 +00:00
(def int-canon (table)
(sort (compare < car) (as cons table)))
2014-11-25 05:56:12 +00:00
;; loading code into the virtual machine
(def add-code (forms)
(each (op . rest) forms
(case op
; syntax: function <name> [ <instructions> ]
; don't apply our lightweight tools just yet
function!
2014-11-25 05:56:12 +00:00
(let (name (_make-br-fn body)) rest
(assert (is 'make-br-fn _make-br-fn))
(= function*.name body))
function
(let (name (_make-br-fn body)) rest
(assert (is 'make-br-fn _make-br-fn))
(= function*.name (join body function*.name)))
2014-11-25 05:59:22 +00:00
; syntax: before <label> [ <instructions> ]
;
2014-11-25 05:56:12 +00:00
; multiple before directives => code in order
before
(let (label (_make-br-fn fragment)) rest
(assert (is 'make-br-fn _make-br-fn))
(or= before*.label (queue))
(enq fragment before*.label))
2014-11-25 05:59:22 +00:00
; syntax: after <label> [ <instructions> ]
;
2014-11-25 05:56:12 +00:00
; multiple after directives => code in *reverse* order
; (if initialization order in a function is A B, corresponding
; finalization order should be B A)
after
(let (label (_make-br-fn fragment)) rest
(assert (is 'make-br-fn _make-br-fn))
(push fragment after*.label))
)))
(def freeze-functions ()
(each (name body) canon.function*
2014-11-27 16:49:18 +00:00
;? (tr name)
;? (prn keys.before* " -- " keys.after*)
2014-11-27 16:49:18 +00:00
;? (= function*.name (convert-names:convert-labels:convert-braces:prn:insert-code body)))
2014-12-13 22:00:14 +00:00
(= function*.name (convert-names:convert-labels:convert-braces:tokenize-args:insert-code body name))))
(def tokenize-arg (arg)
2014-12-14 16:14:13 +00:00
;? (tr "tokenize-arg " arg)
2014-12-14 07:26:15 +00:00
(if (in arg '<- '_)
2014-12-14 16:14:13 +00:00
arg
(isa arg 'sym)
(map [map [fromstring _ (read)] _]
(map [tokens _ #\:]
(tokens string.arg #\/)))
:else
arg))
(def tokenize-args (instrs)
2014-12-13 22:00:14 +00:00
;? (tr "tokenize-args " instrs)
2014-12-13 10:26:53 +00:00
(accum yield
(each instr instrs
(if atom.instr
(yield instr)
(is 'begin instr.0)
(yield `{begin ,@(tokenize-args cdr.instr)})
2014-12-13 10:26:53 +00:00
:else
(yield (map tokenize-arg instr))))))
;; test helpers
(def memory-contains (addr value)
;? (prn "Looking for @value starting at @addr")
(loop (addr addr
idx 0)
;? (prn "@idx vs @addr")
(if (>= idx len.value)
t
(~is memory*.addr value.idx)
(do1 nil
(prn "@addr should contain @value.idx but contains @memory*.addr"))
:else
(recur (+ addr 1) (+ idx 1)))))
(def memory-contains-array (addr value)
;? (prn "Looking for @value starting at @addr, size @memory*.addr vs @len.value")
(and (>= memory*.addr len.value)
(loop (addr (+ addr 1)
idx 0)
;? (prn "comparing @memory*.addr and @value.idx")
(if (>= idx len.value)
t
(~is memory*.addr value.idx)
(do1 nil
(prn "@addr should contain @value.idx but contains @memory*.addr"))
:else
(recur (+ addr 1) (+ idx 1))))))
2014-11-25 06:13:39 +00:00
;; load all provided files and start at 'main'
2014-08-22 18:05:51 +00:00
(reset)
(awhen (pos "--" argv)
(map add-code:readfile (cut argv (+ it 1)))
2014-11-25 09:25:20 +00:00
;? (= dump-trace* (obj whitelist '("run" "schedule" "add")))
;? (freeze-functions)
;? (prn function*!factorial)
(run 'main)
2014-10-05 17:44:14 +00:00
(if ($.current-charterm) ($.close-charterm))
2014-12-13 02:11:10 +00:00
(prn "\nmemory: " memory*)
2014-11-01 23:34:33 +00:00
;? (prn completed-routines*)
)