cc98b007129c — Leonard Ritter 28 days ago
* console based tukan interpreter is functional
3 files changed, 399 insertions(+), 384 deletions(-)

M lib/tukan/uvm.sc
A => testing/test_echo.tuk
M testing/test_node5.sc => testing/tuk_interpreter.sc
M lib/tukan/uvm.sc +203 -45
@@ 4,6 4,7 @@ using import Map
 using import Set
 using import Array
 using import String
+using import Option
 using import Rc
 
 using import .libbf

          
@@ 105,7 106,7 @@ type Number :: (storageof bf_t)
                         \ 'sign 'expn 'len
                     f (self.tab as rawstring) ((sizeof (self.tab @ 0)) * self.len) ...
                     return;
-            local index : i32 = (- (index as i32))
+            local index : i32 = (~ (index as i32))
             f (&index as rawstring) (sizeof index) ...
             ;
 

          
@@ 115,7 116,7 @@ type Number :: (storageof bf_t)
             local index : i32
             f (&index as rawstring) (sizeof index) ...
             if (index < 0)
-                return (this-type -index)
+                return (this-type (~ index))
             local source : bf_t
             source.sign = index
             va-map

          
@@ 490,6 491,28 @@ struct Cell
                     f key value ...
             recur table.keys table.values ...
 
+    fn debug-dump (self)
+        fn recur (key value depth i)
+            returning void
+            print
+                .. depth (tostring i) ": " (repr key) "=" (repr value)
+            dispatch key
+            case CellLimb (klimb)
+                dispatch value
+                case CellLimb (vlimb)
+                    let kl = klimb.cells
+                    let vl = vlimb.cells
+                    for i in (range ArrayCellCount)
+                        let k v =
+                            kl @ i
+                            vl @ i
+                        this-function k v (.. depth "    ") i
+                default
+                    assert false "table structure: internal error"
+                    unreachable;
+            default;
+        recur self.keys self.values "" -1
+
     fn __repr (self) "<Cell>"
 
     fn capacity (self)

          
@@ 670,14 693,14 @@ struct Cell
         fn recur (keylimb valuelimb key value depth)
             returning (uniqueof Atom -1) (uniqueof Atom -2)
             assert (depth <= 56)
-            let mask = (((('hashbits key) >> (depth * IndexBits)) as u32) & IndexMask)
             if (('kind keylimb) == Atom.Kind.CellLimb) # branch
+                let mask = (((('hashbits key) >> (depth * IndexBits)) as u32) & IndexMask)
                 assert (('kind valuelimb) == Atom.Kind.CellLimb)
                 local newkl = (copy (keylimb as CellLimb))
                 local newvl = (copy (valuelimb as CellLimb))
-                let currentkey = (copy (newkl.cells @ mask))
                 let subkeylimb subvaluelimb =
-                    this-function currentkey (newvl.cells @ mask) key value (depth + 1)
+                    this-function (newkl.cells @ mask) (newvl.cells @ mask) key value (depth + 1)
+                let skkind = (copy ('kind subkeylimb))
                 flag := 1:u64 << mask
                 if ('none? subkeylimb)
                     flag := (~ flag)

          
@@ 689,7 712,7 @@ struct Cell
                 newkl.cells @ mask = subkeylimb
                 newvl.cells @ mask = subvaluelimb
                 assert (newkl.mask != 0)
-                if ((bitcount newkl.mask) == 1)
+                if (((bitcount newkl.mask) == 1) & (skkind != Atom.Kind.CellLimb))
                     let index = (findmsb newkl.mask)
                     assert (index < ArrayCellCount)
                     let node = (newkl.cells @ index)

          
@@ 706,14 729,14 @@ struct Cell
                 if ('none? value)
                     # we're removing this value anyway
                     return (copy keylimb) (copy valuelimb)
-                let oldmask = (((('hashbits keylimb) >> (depth * IndexBits)) as u32) & IndexMask)
+                let newmask = (((('hashbits keylimb) >> (depth * IndexBits)) as u32) & IndexMask)
                 local limb : CellLimb
                 for i in (range ArrayCellCount)
                     limb.cells @ i = (Atom)
-                limb.mask = 1:u64 << oldmask
-                limb.cells @ oldmask = (copy keylimb)
+                limb.mask = 1:u64 << newmask
+                limb.cells @ newmask = (copy keylimb)
                 let kref = (Atom (copy limb))
-                limb.cells @ oldmask = (copy valuelimb)
+                limb.cells @ newmask = (copy valuelimb)
                 let vref = (Atom limb)
                 return
                     this-function kref vref key value depth

          
@@ 1217,6 1240,27 @@ type+ Atom
             String
                 repr self
 
+    fn totext (self)
+        dispatch self
+        case None ()
+            return (Atom "none")
+        case False ()
+            return (Atom "false")
+        case True ()
+            return (Atom "true")
+        case Number (num)
+            return (Atom ('tostring num))
+        case Text (str)
+            return self
+        case Blob (str)
+            return (Atom "<blob>")
+        case Symbol (str)
+            return (Atom (copy str))
+        case Cell (table)
+            return (Atom "<cell>")
+        default
+            return (Atom "<?>")
+
     fn __copy (self)
         viewing self
         let ptr = (topointer self)

          
@@ 1256,7 1300,8 @@ sugar uquote (expr...)
 
 let builtins global-env =
     fold (scope env = (Scope) (Cell)) for name in
-        sugar-quote + - * / let fn quote set get nextindex _
+        sugar-quote + - * / let fn quote set get nextindex _ == != not all
+            \ any dump totext .. cond
         sym := (Atom (name as Symbol))
         code := ('hashbits sym)
         _

          
@@ 1277,6 1322,10 @@ fn global-environment ()
 fn... ueval (env : Atom, expr : Atom)
     let ueval = this-function
 
+    inline errormsg (args...)
+        print "in" ('tostring expr)
+        print "error:" args...
+
     assert (('kind env) == Atom.Kind.Cell)
     let envtable = (env as Cell)
     switch ('kind expr)

          
@@ 1312,6 1361,17 @@ fn... ueval (env : Atom, expr : Atom)
             # (quote value)
             case builtins.quote
                 return ('get-index args 1)
+            case builtins.cond
+                let cond = (ueval env ('get-index args 1))
+                switch ('kind cond)
+                case Atom.Kind.True
+                    return (ueval env ('get-index args 2))
+                case Atom.Kind.False
+                    return (ueval env ('get-index args 3))
+                default
+                    if (not ('none? cond))
+                        errormsg "condition must be bool, but is" ('tostring cond)
+                    return (Atom)
             default;
         default;
 

          
@@ 1322,19 1382,22 @@ fn... ueval (env : Atom, expr : Atom)
                 let table = (expr as Cell)
                 call
                     Cell.gen-each-index
-                        inline (index node result env)
+                        inline (index node result env anynone?)
                             if (index > 0)
+                                let outval = (ueval env node)
                                 result =
-                                    'set-index result (index - 1) (ueval env node)
+                                    'set-index result (index - 1) outval
                     \ table args env
                 call
                     Cell.gen-each-pair
-                        inline (key value result env)
+                        inline (key value result env anynone?)
+                            let outk = (ueval env key)
+                            let outv = (ueval env value)
                             result =
                                 'set result
                                     if (('kind key) == Atom.Kind.Symbol) (copy key)
-                                    else (ueval env key)
-                                    ueval env value
+                                    else outk
+                                    outv
                     \ table args env
                 deref args
 

          
@@ 1367,55 1430,150 @@ fn... ueval (env : Atom, expr : Atom)
         case Atom.Kind.Symbol
             let table = (expr as Cell)
 
-            fn verify (val K)
-                if (('kind val) != K)
-                    print K "expected, got" ('tostring val)
-            fn verify-bool (val)
-                switch ('kind val)
-                pass Atom.Kind.False
-                pass Atom.Kind.True
-                do;
-                default
-                    print "boolean expected, got" ('tostring val)
+            #
+                fn verify-same (a b)
+                    let A B = ('kind a) ('kind b)
+                    if (A != B)
+                        print "same kind expected, but" A "!=" B
+                fn verify (val K)
+                    if (('kind val) != K)
+                        print K "expected, got" ('tostring val)
+                fn verify-bool (val)
+                    switch ('kind val)
+                    pass Atom.Kind.False
+                    pass Atom.Kind.True
+                    do;
+                    default
+                        print "boolean expected, got" ('tostring val)
+
+            inline kindstrs (x...)
+                va-map (inline (x) ('tostring x)) x...
+
+            inline any-kinds? (K x...)
+                va-lfold false
+                    inline (k v result)
+                        result | (('kind v) == K)
+                    x...
+
+            inline any-none? (x...)
+                any-kinds? Atom.Kind.None x...
+
+            inline all-kinds? (K x...)
+                va-lfold true
+                    inline (k v result)
+                        result & (('kind v) == K)
+                    x...
 
             inline binop (f)
                 let a = ('get-index args 0)
-                verify a Atom.Kind.Number
+                let b = ('get-index args 1)
+                if (all-kinds? Atom.Kind.Number a b)
+                    return (Atom (f (a as Number) (b as Number)))
+                else
+                    if (not (any-none? a b))
+                        errormsg "numbers expected, got " (kindstrs a b)
+                    return (Atom)
+
+            inline eval-eq ()
+                let a = ('get-index args 0)
                 let b = ('get-index args 1)
-                verify b Atom.Kind.Number
-                return (Atom (f (a as Number) (b as Number)))
+                if (any-none? a b) (Atom)
+                else (Atom (a == b))
+
+            inline eval-neq ()
+                let a = ('get-index args 0)
+                let b = ('get-index args 1)
+                if (any-none? a b) (Atom)
+                else (Atom (a != b))
+
+            inline eval-not ()
+                let x = ('get-index args 0)
+                switch ('kind x)
+                case Atom.Kind.None x
+                case Atom.Kind.False (Atom true)
+                case Atom.Kind.True (Atom false)
+                default
+                    errormsg "boolean expected, got " (kindstrs x)
+                    (Atom)
 
             inline eval-set ()
                 let source = ('get-index args 0)
-                verify source Atom.Kind.Cell
-                source as:= Cell
-                let key = ('get-index args 1)
-                let value = ('get-index args 2)
-                Atom ('set source key value)
+                if (all-kinds? Atom.Kind.Cell source)
+                    let _source = source
+                    source as:= Cell
+                    let key = ('get-index args 1)
+                    let value = ('get-index args 2)
+                    if (any-none? key) _source
+                    else
+                        Atom ('set source key value)
+                else
+                    if (not (any-none? source))
+                        errormsg "cell expected, got " (kindstrs source)
+                    (Atom)
 
             inline eval-get ()
                 let source = ('get-index args 0)
-                verify source Atom.Kind.Cell
-                source as:= Cell
-                let key = ('get-index args 1)
-                let value = ('get-index args 2)
-                let result = ('get source key)
-                if ('none? result)
-                    value
+                if (all-kinds? Atom.Kind.Cell source)
+                    source as:= Cell
+                    let key = ('get-index args 1)
+                    let value = ('get-index args 2)
+                    if (any-kinds? Atom.Kind.None key) (Atom)
+                    else
+                        let result = ('get source key)
+                        if ('none? result)
+                            value
+                        else
+                            result
                 else
-                    result
+                    if (not (any-none? source))
+                        errormsg "cell expected, got " (kindstrs source)
+                    (Atom)
 
             inline eval-countof ()
                 let source = ('get-index args 0)
-                verify source Atom.Kind.Cell
-                source as:= Cell
-                Atom ('next-index source)
+                if (all-kinds? Atom.Kind.Cell source)
+                    source as:= Cell
+                    Atom ('next-index source)
+                else
+                    if (not (any-none? source))
+                        errormsg "cell expected, got " (kindstrs source)
+                    (Atom)
 
             switch ('hashbits head)
             case builtins.+ (binop +)
             case builtins.- (binop -)
             case builtins.* (binop *)
             case builtins./ (binop /)
+            case builtins.== (eval-eq)
+            case builtins.!= (eval-neq)
+            case (getattr builtins '..)
+                let a = ('get-index args 0)
+                let b = ('get-index args 1)
+                if (all-kinds? Atom.Kind.Text a b)
+                    return (Atom (.. (a as String) (b as String)))
+                else
+                    if (not (any-none? a b))
+                        errormsg "texts expected, got " (kindstrs a b)
+                    return (Atom)
+            case builtins.all
+                let a = ('get-index args 0)
+                let b = ('get-index args 1)
+                if ('none? a) a
+                else b
+            case builtins.any
+                let a = ('get-index args 0)
+                let b = ('get-index args 1)
+                if ('none? a) b
+                else a
+            case builtins.dump
+                let x = ('get-index args 0)
+                #if (not ('none? x))
+                print ('tostring x) "<-" ('tostring expr)
+                x
+            case builtins.totext
+                let x = ('get-index args 0)
+                'totext x
+            case builtins.not (eval-not)
             # (set table key value)
             case builtins.set (eval-set)
             # (get table key default)

          
A => testing/test_echo.tuk +23 -0
@@ 0,0 1,23 @@ 
+: exit?
+    == "\n" (get io 'readline)
+: state (get io 'state (_ (: index 0)))
+cond (any exit? false)
+    _
+        : exit 0
+        : stdout "exiting...\n"
+        : state state
+    let
+        : state
+            any
+                all (get io 'readline)
+                    set state 'index (+ (get state 'index) 1)
+                state
+        _
+            : block-break true
+            : prompt
+                .. (totext (get state 'index)) "> "
+            : stdout
+                any
+                    all (get io 'break) "enter empty line to exit\n"
+                    get io 'readline
+            : state state

          
M testing/test_node5.sc => testing/tuk_interpreter.sc +173 -339
@@ 1,358 1,192 @@ 
-
+using import struct
 using import enum
-using import struct
-using import Rc
-using import Array
 using import Map
 using import Set
+using import Array
 using import String
-using import testing
-using import itertools
-using import glm
-using import UTF-8
+using import Rc
 
 import ..lib.tukan.use
-using import tukan.SHA1
-
-#
-    definite datatypes:
-
-        core types (64):
-            * 2 bit: atomic type
-            * 2 bit: vector size (1, 2, 3, 4)
-            * 2 bit: image dimensions (0, 1, 2, 3)
-            elemental values (16):
-                atomic values (4):
-                    word size is always 4 bytes
-                    word types: bool, unsigned, signed, float
-                vectors of atomics (3*4): fixed size arrays of 2 to 4 words
-            n-dim images (16*3):
-                1d images of elementals of dynamic size
-                2d images of elementals of dynamic size, requires x-stride
-                3d images of elementals of dynamic size, requires x-stride and x*y-stride
-        composites:
-            arrays of composites and core types, of dynamic size (...)
-            tuples of composites and core types, of static size (...)
-
-unlet Value
-
-let Word = u32
-let Word4 = (vector Word 4)
-type TypeRef <: Word
-
-# type bits
-let ElementalTypeMask = 0xf:u32
-let AtomicTypeMask = 0x3:u32
-let Bool = (bitcast 0:u32 TypeRef)
-let Int = (bitcast 1:u32 TypeRef)
-let UInt = (bitcast 2:u32 TypeRef)
-let Float = (bitcast 3:u32 TypeRef)
-let VectorSizeOffset = 2:u32
-let VectorSizeMask = (0x3:u32 << VectorSizeOffset)
-let NoVec = (0:u32 << VectorSizeOffset)
-let Vec2 = (1:u32 << VectorSizeOffset)
-let Vec3 = (2:u32 << VectorSizeOffset)
-let Vec4 = (3:u32 << VectorSizeOffset)
-let CompositeOffset = 31:u32
-let IsComposite = (1:u32 << CompositeOffset) # ~x is address
-
-# composite type header bits
-let CompositeHeaderOffset = 16:u32
-let CompositeHeaderMask = (0xf:u32 << CompositeHeaderOffset)
-let IsImage1D     = (1:u32 << CompositeHeaderOffset) # bottom bits: elemental-type; width
-let IsImage2D     = (2:u32 << CompositeHeaderOffset) # bottom bits: elemental-type; width, height
-let IsImage3D     = (3:u32 << CompositeHeaderOffset) # bottom bits: elemental-type; width, height, depth
-let IsArray       = (8:u32 << CompositeHeaderOffset) # count, element-type
-let IsTuple       = (9:u32 << CompositeHeaderOffset) # count, element-type, ...
-
-global memory : (Array Word)
-'append memory 0:u32
+using import tukan.uvm
+using import tukan.module
+using import tukan.pickle
+using import tukan.File
 
-fn... alloc (wordsize : usize)
-    let offset = (countof memory)
-    let nextoffset = (offset + wordsize)
-    assert (nextoffset < (1:usize << 31)) "out of memory"
-    'resize memory nextoffset
-    copy (offset as Word)
-
-fn... vectorT (atomic-type : Word, size : Word)
-    assert ((atomic-type | AtomicTypeMask) == AtomicTypeMask) "type must be atomic"
-    assert ((size >= 1) & (size <= 4)) "vector size must be in range 2..4"
-    bitcast (atomic-type | ((size - 1) << VectorSizeOffset)) TypeRef
-
-global dedup2mem :
-    Map SHA1.DigestType Word
-        fn (x)
-            local k = x
-            let k = (bitcast &k @u32)
-            ((k @ 0) as u64 | ((k @ 1) as u64 << 32)) as hash
-
-fn... getaddr (data : @Word, count : Word)
-    let digest = (sha1 (bitcast data rawstring) (count * (sizeof Word)))
-    try
-        copy ('get dedup2mem digest)
-    else
-        let offset = (alloc count)
-        for i in (range count)
-            memory @ (offset + i) = data @ i
-        'set dedup2mem digest offset
-        offset
+##############################################################################
 
-fn... imageT (elemental-type : TypeRef, width : Word, height : Word = 0, depth : Word = 0)
-    local tmp =
-        arrayof Word
-            storagecast
-                | elemental-type
-                    if (height == 0) IsImage1D
-                    elseif (depth == 0) IsImage2D
-                    else IsImage3D
-            \ width height depth
-    bitcast (~ (getaddr &tmp (countof tmp))) TypeRef
-
-fn... arrayT (element-type : TypeRef, count : Word)
-    local tmp =
-        arrayof Word IsArray count element-type
-    bitcast (~ (getaddr &tmp (countof tmp))) TypeRef
-
-fn... indirect-tupleT (element-types : @TypeRef, count : Word)
-    let outcount = (count + 2)
-    let ptr = (alloca-array TypeRef outcount)
-    ptr @ 0 = IsTuple
-    ptr @ 1 = count
-    for i in (range count)
-        ptr @ (i + 2) = element-types @ i
-    bitcast (~ (getaddr (bitcast ptr @Word) outcount)) TypeRef
-
-inline tupleT (...)
-    local tmp =
-        arrayof TypeRef ...
-    indirect-tupleT &tmp (countof tmp)
+struct Options
+    debug = false
+    statepath : string = ""
 
-type+ TypeRef
-    inline embedded? (self)
-        (self & IsComposite) != IsComposite
-
-    fn wordcount (self)
-        returning usize
-        if ((self & IsComposite) == IsComposite)
-            let addr = (~ (storagecast self))
-            let head = (memory @ addr)
-            switch (head & CompositeHeaderMask)
-            case IsImage1D
-                * (this-function (bitcast (head & ElementalTypeMask) TypeRef))
-                    copy ((memory @ (addr + 1)) as usize)
-            case IsImage2D
-                * (this-function (bitcast (head & ElementalTypeMask) TypeRef))
-                    copy ((memory @ (addr + 1)) as usize)
-                    copy ((memory @ (addr + 2)) as usize)
-            case IsImage3D
-                * (this-function (bitcast (head & ElementalTypeMask) TypeRef))
-                    copy ((memory @ (addr + 1)) as usize)
-                    copy ((memory @ (addr + 2)) as usize)
-                    copy ((memory @ (addr + 3)) as usize)
-            case IsArray
-                * (this-function (bitcast (memory @ (addr + 2)) TypeRef))
-                    copy ((memory @ (addr + 1)) as usize)
-            case IsTuple
-                count := memory @ (addr + 1)
-                fold (s = 0:usize) for i in (range count)
-                    + s
-                        this-function (bitcast (memory @ (addr + 2 + i)) TypeRef)
-            default 0:usize
-        else
-            switch (self & VectorSizeMask)
-            case NoVec 1:usize
-            default
-                + 1:usize
-                    copy (((self & VectorSizeMask) >> VectorSizeOffset) as usize)
+fn run (argc argv program opts)
+    #
+        inputs:
+            setup : ?
+                sent for the first frame of the application.
+            readline : text
+                sent after a `prompt` request.
+            break : ?
+                sent when the user presses Ctrl+D on the prompt. if the program
+                doesn't subsequently set `block-break`, it is aborted with
+                error code 255.
+            state : ?
+                as set by `state`. When previously saved to disk, it will be
+                provided along with `setup`.
 
-    fn __repr (self)
-        returning string
-        if ((self & IsComposite) == IsComposite)
-            let addr = (~ (storagecast self))
-            let head = (memory @ addr)
-            switch (head & CompositeHeaderMask)
-            case IsImage1D
-                let str = (tostring (bitcast (head & ElementalTypeMask) TypeRef))
-                .. str
-                    \ "*" (tostring ((memory @ (addr + 1)) as i32))
-            case IsImage2D
-                let str = (tostring (bitcast (head & ElementalTypeMask) TypeRef))
-                .. str
-                    \ "*" (tostring ((memory @ (addr + 1)) as i32))
-                    \ "*" (tostring ((memory @ (addr + 2)) as i32))
-            case IsImage3D
-                let str = (tostring (bitcast (head & ElementalTypeMask) TypeRef))
-                .. str
-                    \ "*" (tostring ((memory @ (addr + 1)) as i32))
-                    \ "*" (tostring ((memory @ (addr + 2)) as i32))
-                    \ "*" (tostring ((memory @ (addr + 3)) as i32))
-            case IsArray
-                .. (tostring (bitcast (memory @ (addr + 2)) TypeRef))
-                    \ "@" (tostring ((memory @ (addr + 1)) as i32))
-            case IsTuple
-                count := memory @ (addr + 1)
-                ..
-                    fold (s = "(") for i in (range count)
-                        .. s
-                            if (i == 0) ""
-                            else " "
-                            tostring (bitcast (memory @ (addr + 2 + i)) TypeRef)
-                    ")"
-            default
-                "?"
-        else
-            switch (self & VectorSizeMask)
-            case NoVec
-                switch (self & AtomicTypeMask)
-                case Bool "bool"
-                case Int "int"
-                case UInt "uint"
-                case Float "float"
-                default "?"
-            default
-                ..
-                    switch (self & AtomicTypeMask)
-                    case Bool "b"
-                    case Int "i"
-                    case UInt "u"
-                    case Float ""
-                    default "?"
-                    "vec"
-                    tostring
-                        (((self & VectorSizeMask) >> VectorSizeOffset) as i32) + 1
-
-fn... stringT (count : usize)
-    arrayT UInt ((count as Word + (sizeof Word)) // (sizeof Word))
-
-struct Value plain
-    type : TypeRef
-    value : uvec4
-
-    fn __@ (self index)
-        let T = self.type
-        let value = self.value
-        if ((T & IsComposite) == IsComposite)
-            let addr = (~ (storagecast T))
-            let head = (memory @ addr)
-            switch (head & CompositeHeaderMask)
-            case IsArray
-                let count = ((memory @ (addr + 1)) as usize)
-                assert (index < count) "index out of bounds"
-                let ET = (bitcast (memory @ (addr + 2)) TypeRef)
-                let stride = ('wordcount ET)
-                let offset = (value @ 0)
-                Value ET
-                    if ('embedded? ET)
-                        @ (bitcast (& (memory @ offset)) @uvec4)
-                    else offset
-            #case IsTuple
-                count := memory @ (addr + 1)
-                assert (index < count) "index out of bounds"
-                let ET = (bitcast (memory @ (addr + 2 + index)) TypeRef)
+        outputs:
+            exit : number
+                when set, exits the program with the provided errorcode.
+            prompt : text
+                when set, sends a prompt to the user and sends the users input
+                via `readline` back into the program. if the user presses
+                Ctrl+D, `break` is sent.
+            block-break : ?
+                if set in a frame where `break` is set, prevents the break from
+                prompt to be executed.
+            state : ?
+                when set, is feedback as `state`. the application can maintain
+                stateful variables this way and simulate mutation. if provided
+                after receiving a `break` or when sending an `exit`, its contents
+                 will be serialized to disk.
+            stdout : text
+                when set, prints text to stdout.
 
 
-                fold (s = 0:usize) for i in (range count)
-                    + s
-                        this-function (bitcast (memory @ (addr + 2 + i)) TypeRef)
-            default
-                assert false "composite value is not indexable"
-                unreachable;
-        else
-            switch (T & VectorSizeMask)
-            case NoVec
-                assert false "atomic value is not indexable"
-                unreachable;
-            default
-                let ET = ((T & AtomicTypeMask) as TypeRef)
-                this-type ET (value @ index)
+    let env = ((global-environment) as Cell)
 
-fn... alloc (T : TypeRef)
-    this-function ('wordcount T)
-case using alloc
+    let io = (Cell)
 
-inline vec-constructor (T TT f)
-    fn... (x : T)
-        let x = (f x)
-        Value TT x
-    case (x : T, y : T)
-        let x y = (f x) (f y)
-        Value (vectorT TT 2)
-            uvec4 x y x y
-    case (x : T, y : T, z : T)
-        let x y z = (f x) (f y) (f z)
-        Value (vectorT TT 3)
-            uvec4 x y z 1
-    case (x : T, y : T, z : T, w : T)
-        let x y z w = (f x) (f y) (f z) (f w)
-        Value (vectorT TT 4)
-            uvec4 x y z w
-
-let boolconst = (vec-constructor bool Bool _)
-let intconst = (vec-constructor i32 Int _)
-let uintconst = (vec-constructor u32 UInt _)
-let floatconst = (vec-constructor f32 Float (inline (x) (bitcast x Word)))
-
-fn... stringconst (s : String)
-    count := (countof s)
-    let T = (stringT count)
-    let addr = (alloc T)
-    ptr := (& (memory @ addr)) as (mutable rawstring)
-    for i in (range count)
-        ptr @ i = s @ i
-    Value T addr
+    inline debugprint (...)
+        if opts.debug
+            print ...
 
-print
-    @
-        floatconst 1 2 3
-        0
-
-print
-    stringT 4
-    Float
-    vectorT Float 4
-    imageT (vectorT Float 3) 16
-    imageT Float 16 16
-    imageT Float 16 32 16
-    imageT Float 16 16
-    tupleT UInt Int Float
-    arrayT (vectorT Float 3) 4
-    tupleT UInt (tupleT Float Float) Int
-    tupleT;
-
-print
-    stringconst "hi"
-
+    vvv bind init
+    do
+        let io = ('set io 'setup true)
+        let io =
+            if (sc_is_file opts.statepath)
+                debugprint "loading state from" opts.statepath
+                let file =
+                    try (File.open opts.statepath "rb")
+                    else
+                        error
+                            .. "opening " opts.statepath " failed"
+                'set io 'state (unpickle file)
+            else io
+        let env = ('set env 'io io)
+        ueval env program
+    struct System
+        break? = false
+    if ('none? init)
+        print "error: setup unhandled"
+        return 255
+    local sys : System
+    vvv bind result state
+    loop (state = init)
+        if (('kind state) != Atom.Kind.Cell)
+            print "error: main function must return cell"
+            break 255 (Atom)
+        debugprint "IO <-" ('tostring (Atom state))
+        let cstate = (state as Cell)
+        let stateval = ('get cstate 'state)
+        if sys.break?
+            sys.break? = false
+            let blockbreak = ('get cstate 'block-break)
+            if ('none? blockbreak)
+                debugprint "breaking"
+                break 255 stateval
+        let stdoutval = ('get cstate 'stdout)
+        if (not ('none? stdoutval))
+            io-write! (stdoutval as String as string)
+        let exitval = ('get cstate 'exit)
+        if (not ('none? exitval))
+            break (exitval as Number as integer) stateval
+        let promptval = ('get cstate 'prompt)
+        let io =
+            if (not ('none? promptval))
+                let ok? line = (sc_prompt (promptval as String as string) "")
+                if ok?
+                    'set io 'readline (.. line "\n")
+                else
+                    sys.break? = true
+                    'set io 'break true
+            else (copy io)
+        let io = ('set io 'state stateval)
+        debugprint "IO ->" ('tostring (Atom (copy io)))
+        let env = ('set (copy env) 'io io)
+        ueval (copy env) (copy program)
+    if ((not (empty? opts.statepath)) and (not ('none? state)))
+        using import tukan.File
+        let testfilepath =
+            .. module-dir "/test.uvm"
+        do
+            let file =
+                try (File.open opts.statepath "wb")
+                else
+                    error
+                        .. "creating " opts.statepath " failed"
+            debugprint "saving state to" opts.statepath
+            pickle file state
+            drop file
+    debugprint "exiting with code" result
+    return result
 
-#
-    let readline =
-        input string Input.Readline
-    let setup =
-        input Input.Setup
-    let stdout =
-        output string Output.Stdout
-    let exit =
-        output inttype Output.Exit
-    let prompt =
-        output string Output.Prompt
+fn print-help (exename)
+    print "usage:" exename
+        """"[option [...]] [filename]
 
-#fn build-code (expr)
+            Options:
+            -h, --help                  print this text and exit.
+            -d, --debug                 print debug information during execution.
+            --                          terminate option list.
+    exit 0
 
+fn print-version ()
+    print "Executable path:" compiler-path
+    exit 0
 
-#build-code
-    sugar-quote
-        let exit? (equal "\n" readline)
-
-        bind exit
-            then exit? 0
-
-        bind prompt
-            then (merge setup (not exit?)) "> "
-
-        bind stdout
-            merge
-                then exit? "exiting...\n"
-                else exit? readline
-
-
-;
  No newline at end of file
+if main-module?
+    let minus-char = 45:i8 # "-"
+    let interpreterpath argc argv = (script-launch-args)
+    let exename = (string (argv @ 0))
+    local sourcepath = ""
+    local parse-options = true
+    local opts : Options
+    let start-offset =
+        for i in (range argc)
+            let k = (i + 1)
+            let arg = (string (argv @ i))
+            if ((deref parse-options) and ((arg @ 0) == minus-char))
+                if ((arg == "--help") or (arg == "-h"))
+                    print-help exename
+                elseif ((== arg "--version") or (== arg "-v"))
+                    print-version;
+                elseif ((== arg "--debug") or (== arg "-d"))
+                    opts.debug = true
+                elseif (== arg "--")
+                    parse-options = false
+                else
+                    print
+                        .. "unrecognized option: " arg
+                            \ ". Try --help for help."
+                    exit 1
+            elseif (sourcepath == "")
+                sourcepath = arg
+                # remainder is passed on to script
+                break k
+        else argc
+    if (sourcepath != "")
+        #let sourcepath =
+            if (sourcepath == "")
+                .. compiler-dir "/lib/scopes/console.sc"
+            else (deref sourcepath)
+        let argc = (argc - start-offset)
+        let argv = (& (argv @ start-offset))
+        let sourcepath = (sc_realpath sourcepath)
+        opts.statepath = (.. sourcepath ".state")
+        let expr = ((list-load sourcepath) as list)
+        let expr = (cons 'let expr)
+        exit
+            run argc argv (Atom.from-value expr) opts
+    else
+        print "missing script name"