Zarutian 3. july 2007: Capicol is an variant of
picol, which in turn is an variant of
Tcl. Capicol stands for Capability
picol and is my investigation into capability-based security and asynchronous message passing in concurrent environment. It is not complete yet and probably very slow. (I want to get it right before fast, thank you)
Zarutian 11. july 2007: So how does Capicol (or intend to) implement capabilities? Well, simply through a dictionary that the code running in the capicol interp doesn't have access to.
That dictionary maps handles to addresses of other Capicolets (and simple i/o adaptors to the world outside the Capicolets), Capicolet being the snapshot state of an capicol interp, which might themselves be running or stored on other machines. Back to that dictionary. A Capicolet can only send an message to an address it has a handle for.
So how does a Capicolet get an handle for a yet unknown address?
Via the addresses field of a message that the capicol interp code replaces with handles (making new ones when coming across currently unknown addresses) upon receipt of a message and vice verse on sending.
Zarutian 20.. july 2007: for an introduction to capability based security see
http://www.skyhunter.com/marcs/capabilityIntro/index.html and on capability based security in general see erights.org New and improved version of the following code coming soon.
Zarutian 23. july 2007: Needs a bit wikignoming that I dont have time to do right now. (Needs a space before each line of code) Turns out I have a bit time.
Zarutian 19. september 2007: I am thinking about simplifing the capability list saved with each capicol state.
# This code is hereby released in the public domain.
# Any infriging software patents will be disregarded and
# propably made invalid because of obviouseness.
# v 0.6
package require Tcl 8.5
package provide capicol 0.6.0
# state:
# my_address
# <capicol address>
# number_of_children
# <number>
# running
# <boolean>
# quota
# <number>
# capabilities
# <address>*
# in-queue
# <in_message>*
# out_queue
# <out_message>*
# <type>
# commands
# <command name>*
# <type> <contents>
# returnstack
# frame*
# frame
# pointer
# <number>
# code
# <call template>*
# results
# <number>
# <result>
# type
# macro | micro
# einungis fyrir macro frames:
# variables
# <name>
# <value>
# arguments
# <string>
# [break-goto]
# <number>
# [continue-goto]
# <number>
# [catcher]
# <name of a variable>
# [save-to]
# dest
# <number>
# variables
# <name>
# <value>
# <in_message> := <addresses> <data> <quota>
# <out_message> := <out_message_type> <out_message_contents>
# <out_message_type> := "beget" | "gain" | "message"
# <out_message_contents> := <startup script> <addresses> <quota>; for "beget"
# <out_message_contents> := <in_message> ; for "message"
# <out_message_contents> := <cert> ; for "gain"
# <addresses> := <address>*
# <call template> := a string where [<index into the result table>] must be replace with that result
# <type> := "prim" | "execlist" | "script"
#
# decided to upvar state from all that stuff that [advance $state] invokes
# þarf að breyta áköll á exec_prim_set úr öðrum exec_prims yfir í call set <varname> <value>
# þarf að breyta exec_prim_set þannig að það finni macro frame og breyti breytum þar
namespace eval capicol {}
proc capicol::log args {
# override with another proc to get all kind of debugging data.
}
namespace eval capicol::interp {}
proc capicol::interp::state_check {} {
upvar state state
if {![dict exists $state commands]} { error "commands missing" }
if {![dict exists $state frame code]} { error "code missing" }
if {![dict exists $state my_address]} {
error "an capicol state cannot be without an address!"
}
if {![dict exists $state capabilities]} {
error "an capicol without capabilities: why?"
}
if {![dict exists $state frame pointer]} { dict set state frame pointer 0 }
if {![dict exists $state frame results]} { dict set state frame results {} }
if {![dict exists $state frame variables]} { dict set state frame variables {} }
if {![dict exists $state frame args]} { dict set state frame args {} }
if {![dict exists $state returnstack]} { dict set state returnstack {} }
}
proc capicol::interp::prepare_command_to_be_invoked {} {
upvar state state
set cmd&args [lindex [dict get $state frame code] [dict get $state frame pointer]]
set cmd&args [interpolate [dict get $state frame results] [set cmd&args]]
return [set cmd&args]
}
proc capicol::interp::new_callframe_for_execlist {code args} {
upvar state state
dict set state frame code $code
dict set state frame pointer -1; # þarf að vera -1 út af autoincr
dict set state frame variables {}
dict set state frame results {}
dict set state frame args $args
}
proc capicol::interp::advance {state} {
capicol::log invoked [info level] [info level 0]
state_check
set args [lassign [prepare_command_to_be_invoked] cmd]
if {[dict exists $state commands $cmd]} {
if {[llength [dict get $state commands $cmd]] > 1} { error "malformed command record for $cmd" }
set rest [lassign [dict get $state commands $cmd] type contents]
switch -exact -- $type {
"execlist" {
push_continuation $state
new_callframe_for_execlist $contents $args
}
"prim" {
set pointer [dict get $state frame pointer]
dict set state frame results $pointer [exec_prim [dict get $state commands $cmd contents] $args]
}
"script" {
dict set state commands $cmd [list execlist [translate $contents] $contents]
return [advance $state]
}
default {
error "unknown command type $type"
}
}
} else {
# the unknown command handling
if {![dict exists $state commands unknown]} {
call error "unknown command $cmd"
return
} else {
# invoke the unknown command
dict set state frame results \[[dict get $state pointer]\] [call unknown [set cmd&args]]
}
}
if {[llength [dict get $state frame code]] < [dict get $state frame pointer]} {
# execution fell off the end of an execlist
set state [lindex [exec_prim_return {} $state] end]
}
dict set state frame pointer [expr [dict get $state frame pointer] + 1]; # autoincr
return $state
}
proc capicol::interp::translate {script {offset 0}} {
upvar state state; # here only for [call error] in this procedure
capicol::log invoked [info level] [info level 0]
# todo: refactor this mess of a procedure
# translates scripts into execlists
set code [list]
set counter $offset
set level 0
dict set stack $level {}
set index 0
set length [string length $script]
set braced 0
set quoted no
while {$index < $length} {
set char [string index $code $index]
incr index
if {[string equal "#" $char] && [string is space [dict get $stack $level]]} {
# handle comments
# deviates from the 11 syntax rules in the way that comments are until end of line
while true {
set char [string index $code $index]
incr index
if {[string equal "\n" $char]} { break }
}
} elseif {[string equal "\$" $char] && !$braced} {
# translate $varname into [get varname]
set varname ""
while true {
set char [string index $script $index]
incr index
if {[string is space $char] || [string equal $char "\""]} {
break
} else {
append varname $char
}
}
lappend code "get $varname"
dict append stack $level "\[[set counter]\]"
incr counter
} elseif {[string equal $char "\""] && !$braced} {
# handle quotes
if {$quoted} {
set quoted no
} else {
set quoted yes
}
} elseif {[string equal $char "\\"]} {
# handle escaped characters
dict append stack $level "\\"
dict append stack $level [string index $script $index]
incr index
} elseif {[string equal $char "\["] && !$braced} {
# handle opening bracket
incr level +1
dict set stack $level {}
} elseif {[string equal $char "\]"] && !$braced} {
# handle closeing bracket
lappend code [dict get $stack $level]
dict unset stack $level
incr level -1
if {$level < 0} { call error "too many \[ or too few \]" }
dict append stack $level \[[set counter]\]
incr counter
} elseif {([string equal $char "\n"] || [string equal $char ";"]) && !$braced} {
# handle newline and semicolon
if {$level != 0} { call error "unquoted \\n inside an command" }
if {![string is space [dict get $stack 0]]} {
lappend result [dict get $stack 0]
incr counter
dict set stack 0 {}
}
} elseif {[string equal "\{" $char]} {
if {!$braced} {
set braced 1
} else {
incr braced +1
}
dict append stack $level $char
} elseif {[string equal "\}" $char]} {
if {!$braced} {
call error "missing \{ somewhere or too many \}"
} else {
incr braced -1
}
dict append stack $level $char
} else {
dict append stack $level $char
}
}
return $code
}
proc capicol::interp::interpolate {map template} {
# mig grunar að þessi procedure hafi einhver vandkvæði
capicol::log invoked [info level] [info level 0]
set out {}
set i 0
while {$i < [string length $template]} {
set char [string index $template $i]
incr i
if {[string equal $char "\["]} {
set tag {}
while true {
set char [string index $template $i]
incr i
if {[string equal $char "\]"]} {
break
} elseif {[string equal $char "\["]} {
error "only one bracket level allowed in interpolation"
} else {
append tag $char
}
if {$i >= [string length $template]} {
error "where is the closing bracket?"
}
}
if {![dict exists $map $tag]} { error "tag not found in map" }
append out [dict get $map $tag]
# finnst eins og ég sé að gleyma einhverju hér
} elseif {[string equal $char "\{"]} {
append out $char
set level 1
while true {
set char [string index $template $i]
incr i
if {[string equal $char "\{"]} {
incr level +1
} elseif {[string equal $char "\}"]} {
incr level -1
}
append out $char
if {$level == 0} { break }
if {$i >= [string length $template]} {
error "missing closing brace some where"
}
}
} elseif {[string equal $char "\\"]} {
append out "\\"
append out [string index $template $i]; incr i
} else {
append out $char
}
}
return $out
}
proc capicol::interp::space_quota_check {} {
upvar state state
if {([string length [dict get $state commands]] + \
[string length [dict get $state returnstack]] + \
[string length [dict get $state frame]]) > [dict get $state quota]} {
call error "not enaugh quota to run!"
return
}
}
proc capicol::interp::push_continuation {continuation} {
upvar state state
capicol::log invoked [info level] [info level 0]
set temp [dict create]
dict lappend state returnstack [dict get $state frame]
space_quota_check
}
proc capicol::interp::call {args} {
upvar state state
capicol::log invoked [info level] [info level 0]
push_continuation $state
dict set state frame code [list [set args]]
dict set state frame pointer -1
#return -code return
}
# primitives (or built in commands)
proc capicol::interp::exec_prim {primid arguments} {
upvar state state
capicol::log invoked [info level] [info level 0]
# giant despatching switch
# I rather use jump tables but cant have them easily in higher level languages
# I wonder about the speed of this thing
switch -exact -- $primid {
"+" -
"-" -
"*" -
"/" -
"%" -
"&" -
"|" -
"^" -
"<<" -
">>" { return [exec_prim_math $primid $arguments] }
"<" -
"<=" -
"==" -
"!=" { return [exec_prim_compare $primid $arguments] }
"and" { return [exec_prim_logical_and $arguments] }
"any_messages?" { return [exec_prim_any_messages? $arguments] }
"args" { return [exec_prim_args $arguments] }
"beget" { return [exec_beget $arguments] }
"break" { return [exec_prim_break $arguments] }
"catch" { return [exec_prim_catch $arguments] }
"capabilities" { return [exec_prim_capabilites $arguments] }
"continue" { return [exec_prim_continue $arguments] }
"command_exists?" { return [exec_prim_command_exists? $arguments] }
"dict" { return [exec_prim_dict $arguments] }
"die" { return [exec_prim_die $arguments] }
"drop_capability" { return [exec_prim_drop_capability $arguments] }
"error" { return [exec_prim_error $arguments] }
"gain" { return [exec_prim_gain $arguments] }
"get" { return [exec_prim_get $arguments] }
"if" { return [exec_prim_if $arguments] }
"lappend" { return [exec_prim_lappend $arguments] }
"lassign" { return [exec_prim_lassign $arguments] }
"lindex" { return [exec_prim_lindex $arguments] }
"linsert" { return [exec_prim_linsert $arguments] }
"list" { return [exec_prim_list $arguments] }
"llength" { return [exec_prim_llength $arguments] }
"lrange" { return [exec_prim_lrange $arguments] }
"lrepeat" { return [exec_prim_lrepeat $arguments] }
"lsearch" { return [exec_prim_lsearch $arguments] }
"lset" { return [exec_prim_lset $arguments] }
"lsort" { return [exec_prim_lsort $arguments] }
"next_message" { return [exec_prim_next_message $arguments] }
"not" { return [exec_prim_logical_not $arguments] }
"or" { return [exec_prim_logical_or $arguments] }
"rename" { return [exec_prim_rename $arguments] }
"return" { return [exec_prim_return $arguments] }
"routine" { return [exec_prim_routine $arguments] }
"send_message" { return [exec_prim_send_message $arguments] }
"set" { return [exec_prim_set $arguments] }
"string" { return [exec_prim_string $arguments] }
"unset" { return [exec_prim_unset $arguments] }
"uplevel" { return [exec_prim_uplevel $arguments] }
"var_exists?" { return [exec_prim_var_exists? $arguments] }
"while" { return [exec_prim_while $arguments] }
"__branch" { return [exec_prim___branch $arguments] }
"__jump" { return [exec_prim___jump $arguments] }
default { error "unknown capicol primitive $primid" }
}
}
proc capicol::interp::exec_prim_math {op arguments} {
capicol::log invoked [info level] [info level 0]
set result [lindex $arguments 0]
foreach item [lrange $arguments 1 end] {
set result [expr $result $op $item]
}
return $result
}
proc capicol::interp::exec_prim_compare {op arguments} {
capicol::log invoked [info level] [info level 0]
if {[llength $arguments] != 2} {
upvar state state
call error "wrong # args: should be \"$op number number\""
return
}
return [expr [lindex $arguments 0] $op [lindex $arguments 1]]
}
proc capicol::interp::exec_prim_logical_and {arguments} {
log invoked [info level] [info level 0]
set result [lindex $arguments 0]
foreach item [lrange $arguments 1 end] {
set result [expr $result && $item]
}
return $result
}
proc capicol::interp::exec_prim_any_messages? {arguments} {
upvar state state
capicol::log invoked [info level] [info level 0]
if {![dict exists $state in_queue]} {
dict set state in_queue {}
}
return [expr [llength [dict get $state in_queue]] != 0]
}
proc capicol::interp::exec_prim_args {arguments} {
upvar state state
capicol::log invoked [info level] [info level 0]
if {![dict exists $state frame args]} {
dict set state frame args {}
}
return [dict get $state frame args]
}
proc capicol::interp::exec_prim_beget {arguments} {
upvar state state
capicol::log invoked [info level] [info level 0]
if {[llength $arguments] != 3} {
call error "wrong # args: should be \"beget startup_script capabilities quota\""
return
}
set startup_script [lindex $arguments 0]
set addresses [lindex $arguments 1]
set quota [lindex $arguments 2]
foreach address $addresses {
if {[lsearch -exact [dict get $state capabilities] $address] == -1} {
call error "this capicol has no such address in capabilities list: $address"
return
}
}
if {[dict get $state quota] < $quota} {
call error "this capicol has not enaugh quota for giving to child"
return
}
if {$quota < [string length $startup_script]} {
call error "not enaugh quota allotted to child for the startup script!"
return
}
if {![dict exists $state my_address]} {
error "an capicol state cannot be without an address!"
}
if {![dict exists $state number_of_children]} {
dict set state number_of_children 0
}
# make new address for the "child" using the replicator serial scheme
set child "[dict get $state my_address].[dict incr state number_of_children]"
::capicol::runtime::beget $child $startup_script $addresses $quota
# add the child to the states capabilities list
dict lappend state capabilities $child
return $child
}
proc capicol::interp::exec_prim_break {arguments} {
# depends on the implementation of exec_prim_while
upvar state state
capicol::log invoked [info level] [info level 0]
# search up the invocation stack for break-goto
set level [llength [dict get $state returnstack]]
incr level -1
while true {
set frame [lindex [dict get $state returnstack] $level]
if {[dict exists $frame break-goto]} {
incr level -1
dict set state returnstack [lrange [dict get $state returnstack] 0 $level]
dict set state frame $frame
dict set state frame pointer [expr [dict get $frame break-goto] - 1]
return
}
if {$level == -1} {
call error "break invoked outside an loop"
return
}
incr level -1
}
}
proc capicol::interp::exec_prim_catch {arguments} {
# depends on the implementation of exec_prim_error
upvar state state
capicol::log invoked [info level] [info level 0]
# catch <script> [<var>]
if {([llength $arguments] < 1) || ([llength $arguments] > 2)} {
call error "wrong # args: should be \"catch script ?var?\""
return
}
dict set state frame catcher [lindex $arguments 1]
exec_prim_upevel [list 0 [lindex $arguments 0]]
}
proc capicol::interp::exec_prim_capabilities {arguments} {
upvar state state
capicol::log invoked [info level] [info level 0]
if {![dict exists $state capabilities]} {
error "an capicol without capabilities: why?"
}
return [dict get $state capabilities]
}
proc capicol::interp::exec_prim_continue {arguments} {
# depends on the implementation of exec_prim_while
upvar state state
capicol::log invoked [info level] [info level 0]
# search up the invocation stack for continue-goto
set level [llength [dict get $state returnstack]]
incr level -1
while true {
set frame [lindex [dict get $state returnstack] $level]
if {[dict exists $frame continue-goto]} {
incr level -1
dict set state returnstack [lrange [dict get $state returnstack] 0 $level]
dict set state frame $frame
dict set state frame pointer [expr [dict get $frame continue-goto] - 1]
return
}
if {$level == -1} {
call error "continue invoked outside an loop"
return
}
incr level -1
}
}
proc capicol::interp::exec_prim_dict {arguments} {
upvar state state
capicol::log invoked [info level] [info level 0]
if {[llength $arguments] < 1} {
call error "wrong # args: should be \"dict subcommand ?arg ...?\""
return
}
# simple dispatcher
set subcommand [lindex $arguments 0]
set arguments [lrange $arguments 1 end]
switch -exact -- $subcommand {
"append" { return [exec_prim_dict_append $arguments] }
"create" { return [exec_prim_dict_create $arguments] }
"exists" { return [exec_prim_dict_exists $arguments] }
"filter" { return [exec_prim_dict_filter $arguments] }
"for" { return [exec_prim_dict_for $arguments] }
"get" { return [exec_prim_dict_get $arguments] }
"incr" { return [exec_prim_dict_incr $arguments] }
"info" { return [exec_prim_dict_info $arguments] }
"keys" { return [exec_prim_dict_keys $arguments] }
"lappend" { return [exec_prim_dict_lappend $arguments] }
"merge" { return [exec_prim_dict_merge $arguments] }
"remove" { return [exec_prim_dict_remove $arguments] }
"replace" { return [exec_prim_dict_replace $arguments] }
"set" { return [exec_prim_dict_set $arguments] }
"size" { return [exec_prim_dict_size $arguments] }
"unset" { return [exec_prim_dict_unset $arguments] }
"update" { return [exec_prim_dict_update $arguments] }
"values" { return [exec_prim_dict_values $arguments] }
"with" { return [exec_prim_dict_remove $arguments] }
}
call error "bad subcommand \"[lindex $arguments 0]\": must be append, create, exists, filter, for, get, incr, info, keys, lappend, merge, remove, replace, set, size, unset, update, values or with"
}
proc capicol::interp::exec_prim_dict_append {arguments} {
upvar state state
capicol::log invoked [info level] [info level 0]
if {[llength $arguments] < 3} {
call error "wrong # args: should be \"dict append varName key ?key ...? value\""
return
}
set varname [lindex $arguments 0]
set keys [lrange $arguments 1 end-1]
set value [lindex $arguments 0]
set dict [exec_prim_get [list $varname]]
set prevValue [exec_prim_dict_get [list $dict {*}$keys]]
set value "[set prevValue][set value]"
set dict [exec_prim_dict_replace [list $dict {*}$keys $value]]
exec_prim_dict_set [list $varname $dict]
return $value
}
proc capicol::interp::exec_prim_dict_create {arguments} {
upvar state state
capicol::log invoked [info level] [info level 0]
if {([llength $arguments] % 2) != 0} {
call error "wrong # args: should be \"dict create ?key value ...?\""
return
}
return $arguments
}
proc capicol::interp::exec_prim_dict_exists {arguments} {
upvar state state
capicol::log invoked [info level] [info level 0]
if {[llength $arguments] < 2} {
call error "wrong # args: should be \"dict exists dictionary key ?key ...?\""
return
}
set dict [lindex $arguments 0]
set keys [lrange $arguments 1 end]
set found no
while {[llength $keys] > 0} {
set found no
foreach {k v} $dict {
if {[string equal $k [lindex $keys 0]]} {
set found yes
set value $v
}
}
if {!$found} { break }
set dict $value
set keys [lrange $keys 1 end]
}
return $found
}
proc capicol::interp::exec_prim_dict_filter {arguments} {
upvar state state
capicol::log invoked [info level] [info level 0]
call error {not yet implemented: use this idiom instead:
set results {}
foreach {key value} $dictionary {
if $condition {
lappend result $key
lappend result $value
}
}; # end of error message
}
proc capicol::interp::exec_prim_dict_for {arguments} {
upvar state state
capicol::log invoked [info level] [info level 0]
call error {not yet implemented: ude this idiom instead:
foreach {keyVar valueVar} dictionary script
}; # end of error message
}
proc capicol::interp::exec_prim_dict_get {arguments} {
upvar state state
capicol::log invoked [info level] [info level 0]
if {[llength $arguments] < 1} {
call error "wrong # args: should be \"dict get dictionary ?key ...?\""
return
}
set dict [lindex $arguments 0]
set keys [lrange $arguments 1 end]
while {[llength $keys] > 0} {
set found no
foreach {k v} $dict {
if {[string equal $k [lindex $keys 0]]} {
set found yes
set value $v
}
}
if {!$found} {
call error "key \"[lindex $keys 0]\" not known in dictionary"
return
}
set dict $value
set keys [lrange $keys 1 end]
}
return $value
}
proc capicol::interp::exec_prim_dict_incr {arguments} {
upvar state state
capicol::log invoked [info level] [info level 0]
if {[llength $arguments] < 3} {
call error "wrong # args: should be \"dict append varName key ?key ...? increment\""
return
}
set varname [lindex $arguments 0]
set keys [lrange $arguments 1 end-1]
set value [lindex $arguments 0]
set dict [exec_prim_get [list $varname]]
set prevValue [exec_prim_dict_get [list $dict {*}$keys]]
set value "[set prevValue][set value]"
set dict [exec_prim_dict_replace [list $dict {*}$keys $value]]
exec_prim_dict_set [list $varname $dict]
return $value
}
proc capicol::interp::exec_prim_dict_info {arguments} {
capicol::log invoked [info level] [info level 0]
return "no info"
}
proc capicol::interp::exec_prim_dict_keys {arguments} {
upvar state state
capicol::log invoked [info level] [info level 0]
if {[llength $arguments] < 1} {
call error "wrong # args: should be \"dict keys dictionary ?pattern?\""
return
}
set result {}
set pattern *
if {[llength $arguments] == 2} { set pattern [lindex $arguments 1] }
foreach {key value} [lindex $arguments 0] {
if {[string match $pattern $key]} {
lappend result $key
}
}
return $result
}
proc capicol::interp::exec_prim_dict_lappend {arguments} {
upvar state state
capicol::log invoked [info level] [info level 0]
# use replace
call error "not yet implemented!"
}
proc capicol::interp::exec_prim_dict_merge {arguments} {
upvar state state
capicol::log invoked [info level] [info level 0]
set out {}
foreach dict $arguments {
if {([llength $dict] % 2) != 0} {
call error "missing value to go with key"
return
}
foreach key [dict keys $dict] {
dict set out $key [dict get $dict $key]
}
}
return $out
}
proc capicol::interp::exec_prim_dict_remove {arguments} {
upvar state state
capicol::log invoked [info level] [info level 0]
if {[llength $arguments] < 1} {
call error "wrong # args: should be \"dict remove dictionary ?key ...?\""
return
}
set dict [lindex $arguments 0]
set keys [lrange $arguments 1 end]
set vstack [list $dict]
if {[llength $keys] > 1} {
foreach key [lrange $keys 0 end-1] {
set dict [exec_prim_dict_get [list $dict [lindex $keys 0]]]
lappend vstack $dict
}
set key [lindex $keys 0]
} else {
set key $keys
}
set out {}
foreach {k v} $dict {
if {![string equal $k $key]} {
lappend out $k
lappend out $v
}
}
if {[llength $keys] > 1} {
set out [exec_prim_dict_replace [list $out {*}[lrange $keys 0 end-1] $out]]
}
return $out
}
proc capicol::interp::exec_prim_dict_replace {arguments} {
upvar state state
capicol::log invoked [info level] [info level 0]
if {[llength $arguments] < 3} {
call error "wrong # args: should be \"dict replace dictionary key ?key ...? value\""
return
}
set dict [lindex $arguments 0]
set keys [lrange $arguments 1 end-1]
set value [lindex $arguments end]
set kstack [lrange $keys 0 end-1]
set vstack {}
set d $dict
while {[llength $kstack] > 0} {
set v2 {}
foreach {k v} $d {
if {[string equal $k [lindex $kstack 0]]} { set v2 $v }
}
lappend vstack $v2
set d $v2
set kstack [lrange $kstack 1 end]
}
lappend vstack $value
while {[llength $vstack] > 0} {
set temp [lindex $vstack end-1]
lappend temp [lindex $keys end]
lappend temp [lindex $vstack end]
lset vstack end-1 $temp
set keys [lrange $keys 0 end-1]
set vstack [lrange $vstack 0 end-1]
}
set dict $vstack
return $dict
}
proc capicol::interp::exec_prim_dict_set {arguments} {
upvar state state
capicol::log invoked [info level] [info level 0]
if {[llength $arguments] < 3} {
call error "wrong # args: should be \"dict set varName key ?key ...? value\""
return
}
set varname [lindex $arguments 0]
set keys [lrange $arguments 1 end-1]
set value [lindex $arguments end]
set bool [exec_prim_var_exists? $varname]
if {$bool} {
set dict [exec_prim_get $varname]
}
lset arguments 0 $dict
set dict [exec_prim_dict_replace $arguments]
return [exec_prim_set [list $varname $dict]]
}
proc capicol::interp::exec_prim_dict_size {arguments} {
upvar state state
capicol::log invoked [info level] [info level 0]
if {[llength $arguments] != 1} {
call error "wrong # args: should be \"dict size dictionary\""
return
}
return [expr {[length $arguments] / 2}]
}
proc capicol::interp::exec_prim_dict_unset {arguments} {
upvar state state
capicol::log invoked [info level] [info level 0]
# use dict remove
call error "not yet implemented!"
}
proc capicol::interp::exec_prim_dict_update {arguments} {
upvar state state
capicol::log invoked [info level] [info level 0]
call error "not yet implemented!"
}
proc capicol::interp::exec_prim_dict_values {arguments} {
upvar state state
capicol::log invoked [info level] [info level 0]
if {[llength $arguments] < 1} {
call error "wrong # args: should be \"dict values dictionary ?pattern?\""
return
}
set result {}
set pattern *
if {[llength $arguments] == 3} { set pattern [lindex $arguments 2] }
foreach {key value} [lindex $arguments 1] {
if {[string match $pattern $value]} {
lappend result $value
}
}
return $result
}
proc capicol::interp::exec_prim_dict_with {arguments} {
upvar state state
capicol::log invoked [info level] [info level 0]
call error "not yet implemented!"
}
# prim dict -end-
proc capicol::interp::exec_prim_die {arguments} {
upvar state state
capicol::log invoked [info level] [info level 0]
capicol::runtime::died $state $arguments
}
proc capicol::interp::exec_prim_drop_capability {arguments} {
upvar state state
capicol::log invoked [info level] [info level 0]
if {[llength $arguments] != 1} {
call error "wrong # args: should be \"drop_capability <address>\""
return
}
if {![dict exists $state capabilities]} {
error "an capicol without an capabilities: why?"
}
if {[set r [lsearch -exact [dict get $state capabilities] $arguments]] == -1} {
call error "this capicol doesnt have address $arguments on its capabilities list"
return
}
dict set state capabilities [lreplace [dict get $state capabilities] $r $r]
}
proc capicol::interp::exec_prim_error {arguments} {
# depends on the implementation of exec_prim_catch
upvar state state
capicol::log invoked [info level] [info level 0]
while true {
if {[dict exists $state frame catcher]} break
if {[string equal [dict get $state returnstack] ""]} {
call die error $arguments
return
}
exec_prim_return {}
}
set catcher [dict get $state frame catcher]
dict unset state frame catcher
exec_prim_set [list $catcher $arguments]
return true
}
proc capicol::interp::exec_prim_gain {arguments} {
upvar state state
capicol::log invoked [info level] [info level 0]
call error "not yet implemented!"
}
proc capicol::interp::exec_prim_get {arguments} {
upvar state state
capicol::log invoked [info level] [info level 0]
if {[llength $arguments] != 1} {
call error "wrong # args: should be \"get varName\"
return
}
if {![dict exists $state frame variables $arguments]} {
call error "can't read \"[set arguments]\": no such variable"
return
}
return [dict get $state frame variables $arguments]
}
proc capicol::interp::exec_prim_if {arguments} {
upvar state state
capicol::log invoked [info level] [info level 0]
# only primitive if supported:
# if <test> <yes-command> [else <no-command>]
if {([llength $arguments] < 2) || (4 < [llength $arguments])} {
call error "wrong # args: should be \"if test yes-body \[else no-body\]\""
return
}
if {([llength $arguments] == 4) && ![string equal "else" [lindex $arguments 2]]} {
call error "else keyword missing"
return
}
set test [lindex $arguments 0]
set true [lindex $arguments 1]
set false {}
if {[llength $arguments] == 4} { set false [lindex $arguments 3] }
set code [list uplevel 1 $test]
lappend code [list __branch "\[0\]" 4]
lappend code [list uplevel 1 $false]
lappend code [list __jump 5]
lappend code [list uplevel 1 $true]
lappend code [list]
push_continuation $state
dict set state frame code $code
dict set state frame pointer -1
return
}
proc capicol::interp::exec_prim_lappend {arguments} {
upvar state state
capicol::log invoked [info level] [info level 0]
if {[llength $arguments] < 1} {
call error "wrong # args: should be \"lappend varname ?value ...?\""
return
}
set result [exec_prim_get [lindex $arguments 0]]
foreach item [lrange $arguments 1 end] {
lappend result $item
}
exec_prim_set [list [lindex $arguments 0] $result]
return $result
}
proc capicol::interp::exec_prim_lassign {arguments} {
upvar state state
log invoked [info level] [info level 0]
if {[llength $arguments] < 2} {
call error "wrong # args: should be \"lassign list varname ?varname ...?\""
return
}
set list [lindex $arguments 0]
set vars [lrange $arguments 1 end]
set counter 0
foreach var $vars {
exec_prim_set [list $var [lindex $list $counter]]
incr counter
}
return [lrange $list $counter end]
}
proc capicol::interp::exec_prim_lindex {arguments} {
upvar state state
capicol::log invoked [info level] [info level 0]
if {[llength $arguments] < 1} {
call error "wrong # args: should be \"lindex list ?index ...?\""
return
}
set list [lindex $arguments 0]
set indexes [lrange $arguments 1 end]
foreach item $indexes {
if {![string is digit $item] && \
![string equal -length 3 "end" $item] && \
![string equal -length 4 "end-" $item]} {
call error "bad index \"[set item]\": must be integer or end?-integer?"
return
}
set list [lindex $list $item]
}
return [list $list $state]
}
proc capicol::interp::exec_prim_linsert {arguments} {
upvar state state
capicol::log invoked [info level] [info level 0]
if {[llength $arguments] < 3} {
call error "wrong # args: should be \"linsert list index element ?element ...?\""
return
}
set list [lindex $arguments 0]
set index [lindex $arguments 1]
if {![string is digit $index] && \
![string equal -length 3 "end" $index] && \
![string equal -length 4 "end-" $index]} {
call error "bad index \"[set index]\": must be integer or end?-integer?"
return
}
if {[string equal -length "end-" $index]} {
set index [expr {[llength $list] - [string range $index 4 end]}
}
set elements [lrange $arguments 2 end]
foreach item $elements {
set list [linsert $list $index $item]
incr index +1
}
return $list
}
proc capicol::interp::exec_prim_list {arguments} {
capicol::log invoked [info level] [info level 0]
return $arguments
}
proc capicol::exec_prim_llength {arguments} {
upvar state state
capicol::log invoked [info level] [info level 0]
if {[llength $arguments != 1} {
call error "wrong # args: should be \"llength list\""
return
}
return [llength [lindex $arguments 0]]
}
# var hér þann 26. október 2007 kl 01:48
proc exec_prim_lrange {arguments} {
upvar state state
log invoked [info level] [info level 0]
if {[llength $arguments != 3} {
call error "wrong # args: should be \"lrange list first last\""
return
}
set list [lindex $arguments 0]
set first [lindex $arguments 1]
if {![string is digit $first] && \
![string equal -length 3 "end" $first] && \
![string equal -length 4 "end-" $first]} {
call error "bad index \"[set first]\": must be integer or end?-integer?"
return
}
set last [lindex $arguments 2]
if {![string is digit $last] && \
![string equal -length 3 "end" $last] && \
![string equal -length 4 "end-" $last]} {
call error "bad index \"[set last]\": must be integer or end?-integer?"
return
}
return [list [lrange $list $first $last] $state]
}
proc exec_prim_lrepeat {arguments} {
upvar state state
log invoked [info level] [info level 0]
if {[llength $arguments] < 2} {
call error "wrong # args: should be \"lrepeat positiveCount value ?value ...?\""
return
}
set counter [lindex $arguments 0]
if {![string is digit $counter]} {
call error "expected integer but got \"[set counter]\""
return
}
if {$counter < 1} {
call error "must have a count of at least 1"
return
}
set values [lrange $arguments 1 end]
set result {}
while {$counter > 0} {
foreach value $values {
lappend result $value
}
incr counter -1
}
return $result
}
proc exec_prim_lsearch {arguments} {
upvar state state
log invoked [info level] [info level 0]
if {[llength $arguments] < 2} {
call error "wrong # args: should be \"lsearch ?options? list pattern\""
return
}
set list [lindex $arguments end-1]
set pattern [lindex $arguments end]
set options [lrange $arguments 0 end-2]
set option-all no
set option-ascii no
set option-decreasing no
set option-dictionary no
set option-exact no
set option-glob no
set option-increasing no
set option-index ""
set option-inline no
set option-integer no
set option-not no
set option-real no
set option-regexp no
set option-sorted no
set option-start ""
set option-subindices no
set index 0
while {$index < [llength $options]} {
set item [lindex $options $index]
incr index
if {[string equal $item "-all"]} {
set option-all yes
} elseif {[string equal $item "-ascii"]} {
set option-ascii yes
} elseif {[string equal $item "-decreasing"]} {
set option-decreasing yes
} elseif {[string equal $item "-dictionary"]} {
set option-dictionary yes
} elseif {[string equal $item "-exact"]} {
set option-exact yes
if {$option-glob || $option-regexp} {
call error "make up your damn mind about the options to lsearch will ya!"
return
}
} elseif {[string equal $item "-glob"]} {
set option-glob yes
if {$option-exact || $option-regexp} {
call error "make up your damn mind about the options to lsearch will ya!"
return
}
} elseif {[string equal $item "-increasing"]} {
set option-increasing yes
} elseif {[string equal $item "-index"]} {
set option-index [lindex $options $index]
incr index
if {![string is digit $option-index] && \
![string equal -length 3 "end" $option-index] && \
![string equal -length 4 "end-" $option-index]} {
call error "bad index \"[set option-index]\": must be integer or end?-integer?"
return
}
} elseif {[string equal $item "-inline"]} {
set option-inline yes
} elseif {[string equal $item "-not"]} {
set option-not yes
} elseif {[string equal $item "-real"]} {
set option-real yes
} elseif {[string equal $item "-regexp"]} {
set option-regexp yes
if {$option-glob || $option-exact} {
call error "make up your damn mind about the options to lsearch will ya!"
return
}
} elseif {[string equal $item "-sorted"]} {
set sorted yes
} elseif {[string equal $item "-start"]} {
set option-start [lindex $options $index]
incr index
if {![string is digit $option-start] && \
![string equal -length 3 "end" $option-start] && \
![string equal -length 4 "end-" $option-start]} {
call error "bad index \"[set option-start]\": must be integer or end?-integer?"
return
}
} elseif {[string equal $item "-subindices"]} {
set subindices yes
if {[string equal $option-index ""]} {
call error "-subindices cannot be used without -index option"
return
}
} else {
call error "bad option \"[set item]\": must be -all, -ascii, -decreasing, -dictionary, -exact, -glob, -increasing, -index, -inline, -integer, -not, -real, -regexp, -sorted, -start or -subindices"
return
}
}
set tmp "lsearch"
if {$option-all} { lappend tmp -all }
if {$option-ascii} { lappend tmp -ascii }
if {$option-decreasing} { lappend tmp -decreasing }
if {$option-dictionary} { lappend tmp -dictionary }
if {$option-exact} { lappend tmp -exact }
if {$option-glob} { lappend tmp -glob }
if {$option-increasing} { lappend tmp -increasing }
if {![string equal $option-index ""]} { lappend tmp -index $option-index }
if {$option-inline} { lappend tmp -inline }
if {$option-integer} { lappend tmp -integer }
if {$option-not} { lappend tmp -not }
if {$option-real} { lappend tmp -real }
if {$option-regexp} { lappend tmp -regexp }
if {$option-sorted} { lappend tmp -sorted }
if {![string equal $option-start ""]} { lappend tmp -start $option-start }
if {$option-subindices} { lappend tmp -subindices }
lappend tmp $list
lappend tmp $pattern
if {[catch $tmp result]} {
call error $result
return
}
return $result
}
proc exec_prim_lset {arguments} {
upvar state state
log invoked [info level] [info level 0]
if {[llength $arguments] < 3} {
call error "lset listVar index ?index...? value"
return
}
set listvar [lindex $arguments 0]
set indexes [lrange $arguments 1 end-1]
set value [lindex $arguments end]
if {![exec_prim_var_exists? $listvar]} {
call error "can't read \"$listvar\": no such variable"
return
}
set listval [exec_prim_get $listvar]
set stack ""
set counter -1
foreach index $indexes {
if {![string is digit $index] && \
![string equal -length 3 "end" $index] && \
![string equal -length 4 "end-" $index]} {
call error "bad index \"[set index]\": must be integer or end?-integer?"
}
lappend stack $listval
set listval [lindex $listval $index]
incr counter
}
lappend stack $value
while {$counter > -1} {
set listval [lreplace [lindex $stack $counter] [lindex $indexes $counter] [lindex $indexes $counter]]
}
exec_prim_set [list $listvar $listval]
return $listval
}
proc exec_prim_lsort {arguments} {
upvar state state
log invoked [info level] [info level 0]
if {[llength $arguments] < 1} {
call error "wrong # args: should be \"lsort ?options? list\""
}
set list [lindex $arguments end]
set options [lrange $arguments 0 end-1]
set option-ascii no
set option-creasing in
set option-dictionary no
set option-index ""
set option-indices no
set option-integer no
set option-real no
set option-unique no
set index 0
while {$index < [llenght $options]} {
set item [lindex $options $index]
incr index
if {[string equal $item "-ascii"]} {
set option-ascii yes
} elseif {[string equal $item "-command"]} {
set option-command [lindex $options $index]
incr index
call error "sorry not yet implemented! too tricky as it is!"
return
} elseif {[string equal $item "-decreasing"]} {
set option-creasing de
} elseif {[string equal $item "-dictionary"]} {
set option-dictionary yes
} elseif {[string equal $item "-index"]} {
set option-index [lindex $options $index]
incr index
if {![string is digit $option-index] && \
![string equal -length 3 "end" $option-index] && \
![string equal -length 4 "end-" $option-index]} {
call error "bad index \"[set option-index]\": must be integer or end?-integer?"
return
}
} elseif {[string equal $item "-indices"]} {
set option-indices yes
} elseif {[string equal $item "-integer"]} {
set option-integer yes
} elseif {[string equal $item "-real"]} {
set option-real yes
} elseif {[string equal $item "-unique"]} {
set option-unique yes
} else {
call error "bad option \"[set item]\": must be -ascii, -command, -decreasing, -dictionary, -increasing, -index, -indices, -integer, -real, or -unique"
return
}
}
set tmp "lsort"
if {$option-ascii} { lappend tmp -ascii }
if {[string equal "de" $option-creasing]} { lappend tmp -decreasing }
if {$option-dictionary} { lappend tmp -dictionary }
if {![string equal "" $option-index]} { lappend tmp -index $option-index }
if {$option-indices} { lappend tmp -indices }
if {$option-integer} { lappend tmp -integer }
if {$option-real} { lappend tmp -real }
if {$option-unique} { lappend tmp -unique }
lappend tmp $list
if {[catch $tmp result]} {
call [list error $result]
return
}
return $resul
}
proc exe_prim_next_message {arguments} {
upvar state state
log invoked [info level] [info level 0]
# no pattern matching or anything fancy
if {[llength [dict get $state in-queue]] == 0} {
# suspend the capicol state for a retry later
# because the in-queue is empty
dict set state running no
dict incr state pointer -1
return [list <?promise?> $state]
}
set message [lindex [dict get $state in-queue] 0]
dict set state in-queue [lrange [dict get $state in-queue] 1 end]
lassign $message addresses data quota
set tmp [dict get $state capabilities]
foreach address $addresses {
lappend tmp $address
}
dict set state capabilities $tmp
dict incr state quota $quota
return $message
}
proc exec_prim_logical_or {arguments} {
log invoked [info level] [info level 0]
set result [lindex $arguments 0]
foreach item [lrange $arguments 1 end] {
set result [expr $result || $item]
}
return $result
}
proc exec_prim_rename {arguments} {
upvar state state
log invoked [info level] [info level 0]
if {[llength $arguments] != 2} {
call error "wrong # args: should be \"rename oldName newName\""
return
}
set old [lindex $arguments 0]
set new [lindex $arguments 1]
if {![dict exists $state commands $old]} {
call error "no such command: $old"
return
}
if {[dict exists $state commands $new]} {
call error "$new exists already"
return
}
if {![string equal $new ""]} {
dict set state commands $new [dict get $state commands $old]
}
dict unset state commands $old
return
}
proc exec_prim_return {arguments} {
upvar state state
log invoked [info level] [info level 0]
# return from a frame command
if {[llength $arguments] == 1} {
set result [lindex $arguments 0]
} else {
set last_result_index [lindex [lsort [dict keys [dict get $state frame results] *]] end]
set result [dict get $state frame results $last_result_index]
}
if {0 == [llength [dict get $state returnstack]]} {
exec_prim_die "end of program"
}
# related to uplevel -begin-
if {[dict exists $state frame saveto]} {
dict set state frame saveto variables [dict get $state frame variables]
}
# related to uplevel -end-
dict set state frame [lindex [dict get $state returnstack] end]
dict set state returnstack [lrange [dict get $state returnstack] 0 end-1]
# related to uplevel -begin-
if {[dict exists $state frame saveto]} {
set t1 [dict get $state frame saveto dest]
set t2 [dict get $state frame saveto variables]
set t3 [lindex [dict get $state returnstack] $t1]
set t4 [dict merge $t3 [list variables $t2]]
set t5 [lreplace [dict get $state returnstack] $t1 $t1 $t4]
dict set state returnstack $t5
dict unset state frame saveto
}
# related to uplevel -end-
dict set state results \[[dict get $state frame pointer]\] $result
return $result
}; # var hér
proc exec_prim_routine {arguments} {
upvar state state
log invoked [info level] [info level 0]
if {[llength $arguments] != 2} {
call error "wrong # args: should be \"routine name body\"
return
}
set name [lindex $arguments 0]
set body [lindex $arguments 1]
if {[dict exists $state commands $name]} {
call error "command already exists!"
return
}
dict set state commands $name type script
dict set state commands $name contents $body
space_quota_check
return $name
}
proc exec_prim_command_exists? {arguments} {
upvar state state
log invoked [info level] [info level 0]
if {[llength $arguments] != 1} {
call error "wrong # args: should be \"command_exists? name\"
return
}
set name [lindex $arguments 0]
return [dict exists $state commands $name]
}
proc exec_prim_send_message {arguments} {
upvar state state
log invoked [info level] [info level 0]
if {[llength $arguments] < 2} {
call error "wrong # args: should be \"send_message addresses data ?quota?\"
}
set addresses [lindex $arguments 0]
set data [lindex $arguments 1]
set quota [lindex $arguments 2]
if {[string equal $quota ""]} { set quota [string length $data] }
if {$quota < [string length $data]} {
call error "not enaugh quota alotted for data to be sent"
return
}
if {[dict get $state quota] < $quota} {
call error "not enaugh quota to send message"
return
}
foreach address $addresses {
if {[lsearch -exact [dict get $state capabilities] $address] == -1} {
call "this capicol has not address $address in its capabilities list"
return
}
}
::capicol::runtime::send_message [dict get $state my_address [list $addresses $data $quota]]
}; # var hér
proc exec_prim_set {arguments state} {
log invoked [info level] [info level 0]
if {[llength $arguments] != 2} {
return [call [list error "wrong # args: should be \"set varName value\""] $state]
}
set varname [lindex $arguments 0]
set value [lindex $arguments 1]
dict set state variables $varname $value
return [list $value $state]
}
proc exec_prim_string {arguments state} {
log invoked [info level] [info level 0]
if {[llength $arguments] < 1} {
return [call [list error "wrong # args: should be \"string option arg ?arg ...?\""] $state]
}
set subcommand [lindex $arguments 0]
set rest [lrange $arguments 1 end]
switch -exact -- $subcommand {
"bytelength" { return [exec_prim_string_bytelength $rest $state] }
"compare" { return [exec_prim_string_compare $rest $state] }
"equal" { return [exec_prim_string_equal $rest $state] }
"first" { return [exec_prim_string_first $rest $state] }
"index" { return [exec_prim_string_index $rest $state] }
"is" { return [exec_prim_string_is $rest $state] }
"last" { return [exec_prim_string_last $rest $state] }
"length" { return [exec_prim_string_length $rest $state] }
"map" { return [exec_prim_string_map $rest $state] }
"match" { return [exec_prim_string_match $rest $state] }
"range" { return [exec_prim_string_range $rest $state] }
"repeat" { return [exec_prim_string_repeat $rest $state] }
"replace" { return [exec_prim_string_replace $rest $state] }
"tolower" { return [exec_prim_string_tolower $rest $state] }
"toupper" { return [exec_prim_string_toupper $rest $state] }
"totitle" { return [exec_prim_string_totitle $rest $state] }
"trim" { return [exec_prim_string_trim $rest $state] }
"trimleft" { return [exec_prim_string_trimleft $rest $state] }
"trimright" { return [exec_prim_string_trimright $rest $state] }
"wordend" { return [exec_prim_string_wordend $rest $state] }
"wordstart" { return [exec_prim_string_wordstart $rest $state] }
default { return [call [list error "bad option \"[set subcommand]\": must be bytelength, compare, equal, first, index, is, last, length, map, match, range, repeat, replace, tolower, toupper, totitle, trim, trimleft, trimright, wordend, or wordstart"] $state] }
}
}
proc exec_prim_string_bytelength {arguments state} {
log invoked [info level] [info level 0]
if {[llength $arguments] != 1} {
return [call [list error "wrong # args: should be \"string bytelength string\""] $state]
}
return [list [string bytelength [lindex $arguments 0]] $state]
}
proc exec_prim_string_compare {arguments state} {
log invoked [info level] [info level 0]
if {[llength $arguments] < 2} {
return [call [list error "wrong # args: should be \"string compare ?-nocase? ?-length int? string1 string2\""] $state]
}
set string1 [lindex $arguments end-1]
set string2 [lindex $arguments end]
set options [lrange $arguments 0 end-2]
set option-nocase no
set option-length ""
set index 0
while {$index < [llength $options]} {
set item [lindex $options $index]
incr index
if {[string equal $item "-nocase"]} {
set option-nocase yes
} elseif {[string equal $item "-length"]} {
set option-length [lindex $options $index]
incr index
} else {
return [call [list error "bad option \"[set item]\": must be -nocase or -length"] $state]
}
}
set tmp "string"
lappend tmp "compare"
if {$option-nocase} { lappend tmp -nocase }
if {![string equal $option-length ""]} {
lappend tmp -length
lappend tmp $option-length
}
lappend tmp $string1
lappend tmp $string2
catch $tmp result
return [list $result $state]
}
proc exec_prim_string_equal {arguments state} {
log invoked [info level] [info level 0]
if {[llength $arguments] < 2} {
return [call [list error "wrong # args: should be \"string equal ?-nocase? ?-length int? string1 string2\""] $state]
}
set string1 [lindex $arguments end-1]
set string2 [lindex $arguments end]
set options [lrange $arguments 0 end-2]
set option-nocase no
set option-length ""
set index 0
while {$index < [llength $options]} {
set item [lindex $options $index]
incr index
if {[string equal $item "-nocase"]} {
set option-nocase yes
} elseif {[string equal $item "-length"]} {
set option-length [lindex $options $index]
incr index
} else {
return [call [list error "bad option \"[set item]\": must be -nocase or -length"] $state]
}
}
set tmp "string"
lappend tmp "equal"
if {$option-nocase} { lappend tmp -nocase }
if {![string equal $option-length ""]} {
lappend tmp -length
lappend tmp $option-length
}
lappend tmp $string1
lappend tmp $string2
catch $tmp result
return [list $result $state]
}
proc exec_prim_string_first {arguments state} {
log invoked [info level] [info level 0]
if {([llength $arguments] < 2) || ([llength $arguments] > 3)} {
return [call [list error "wrong # args: should be \"string first subString string ?startIndex?\""] $state]
}
set substring [lindex $arguments 0]
set string [lindex $arguments 1]
set startIndex [lindex $argumnets 2]
if {[string equal $startIndex ""]} {
set startIndex 0
}
if {![string is digit $startIndex] && \
![string equal -length 3 $startIndex "end"] && \
![string equal -length 4 $startIndex "end-"]} {
return [call [list error "bad index \"[set startIndex]\": must be integer or end?-integer?"] $state]
}
return [list [string first $substring $string $startIndex] $state]
}
proc exec_prim_string_index {arguments state} {
log invoked [info level] [info level 0]
if {[llength $arguments] != 2} {
return [call [list error "wrong # args: should be \"string index string charIndex\""] $state]
}
set string [lindex $arguments 0]
set index [lindex $arguments 1]
if {![string is digit $index] && \
![string equal -length 3 $index "end"] && \
![string equal -length 4 $index "end-"]} {
return [call [list error "bad index \"[set index]\": must be integer or end?-integer?"] $state]
}
return [list [string index $string $index] $state]
}
proc exec_prim_string_is {arguments state} {
log invoked [info level] [info level 0]
if {[llength $arguments] < 2} {
return [call [list error "wrong # args: should be \"string is class ?-strict? string\""] $state]
}
set class [lindex $arguments 0]
set string [lindex $arguments end]
set option-strict [expr {([string equal "-strict" [lindex $arguments 1]] && ([llength $arguments] == 3)}]
if {[lsearch -exact {alnum alpha ascii control boolean digit double false graph integer lower print punct space true upper wordchar xdigit} $class] == -1} {
return [call [list error "bad class \"[set class]\": must be alnum, alpha, ascii, control, boolean, digit, double, false, graph, integer, lower, print, space, true, upper, wordchar or xdigit"] $state]
}
set tmp "string"
lappend tmp "is"
lappend tmp $class
if {$option-strict} { lappend tmp -strict }
lappend tmp $string
catch $tmp result
return [list $result $state]
}
proc exec_prim_string_last {arguments state} {
log invoked [info level] [info level 0]
if {([llength $arguments] < 2) || ([llength $arguments] > 3)} {
return [call [list error "wrong # args: should be \"string last subString string ?startIndex?\""] $state]
}
set substring [lindex $arguments 0]
set string [lindex $arguments 1]
set startIndex [lindex $argumnets 2]
if {[string equal $startIndex ""]} {
set startIndex 0
}
if {![string is digit $startIndex] && \
![string equal -length 3 $startIndex "end"] && \
![string equal -length 4 $startIndex "end-"]} {
return [call [list error "bad index \"[set startIndex]\": must be integer or end?-integer?"] $state]
}
return [list [string last $substring $string $startIndex] $state]
}
proc exec_prim_string_length {arguments state} {
log invoked [info level] [info level 0]
if {[llength $arguments] != 1} {
return [call [list error "wrong # args: should be \"string length string\""] $state]
}
return [list [string length [lindex $arguments 0]] $state]
}
proc exec_prim_string_map {arguments state} {
log invoked [info level] [info level 0]
}
proc exec_prim_string_match {arguments state} {}
proc exec_prim_string_range {arguments state} {}
proc exec_prim_string_repeat {arguments state} {}
proc exec_prim_string_tolower {arguments state} {}
proc exec_prim_string_toupper {arguments state} {}
proc exec_prim_string_totitle {arguments state} {}
proc exec_prim_string_trim {arguments state} {}
proc exec_prim_string_trimleft {arguments state} {}
proc exec_prim_string_trimright {arguments state} {}
proc exec_prim_string_wordend {arguments state} {}
proc exec_prim_string_wordstart {arguments state} {}
proc exec_prim_unset {arguments state} {
log invoked [info level] [info level 0]
if {[llength $arguments] != 1} {
return [call [list error "wrong # args: should be \"unset varname\""] $state]
}
dict unset state frame variables $arguments
return [list "" $state]
}
proc exec_prim_uplevel {arguments state} {
log invoked [info level] [info level 0]
if {[llength $arguments] != 2} {
return [call [list error "wrong # args: should be \"uplevel level script\""] $state]
}
set level [lindex $arguments 0]
set script [lindex $arguments 1]
if {[string equal [string index $level 0] "#"]} {
set relative {}
set level [string range $level 1 end]
} else {
set relative "end-"
}
if {![string is digit $level]} {
return [call [list error "level must be an number optionaly preceded with #"] $state]
}
set state [push_continuation $state]
set frame [lindex [dict get $state returnstack] [set relative][set level]]
dict set state frame variables [dict get $frame variables]
dict set state frame args [dict get $frame args]
dict set state frame saveto dest [set relative][set level]
dict set state frame pointer -1
dict set state frame code [translate [lindex $arguments 1]]
return [list {} $state]
}
proc exec_prim_var_exists? {arguments state} {
log invoked [info level] [info level 0]
if {[llength $arguments] != 1} {
return [call [list error "wrong # args: should be \"var_exists? varName\""] $state]
}
return [list [dict exists $state variables $arguments] $state]
}
proc exec_prim_while {arguments state} {
log invoked [info level] [info level 0]
# not done
if {[llength $arguments] != 2} {
return [call [list error "wrong # args: should be \"while test script\""] $state]
}
set code [list error "<empty jump slot>"]
# script body:
lappend code [list uplevel 1 [lindex $arguments 1]]
lset code 0 [list __jump [llength $code]]
# here I use the picol way: test is an script
lappend code [list uplevel 1 [lindex $arguments 0]]
lappend code [list __branch "\[[llength $code]\]" 1]
return [call $code $state]
}
proc exec_prim___branch {arguments state} {
log invoked [info level] [info level 0]
if {[llength $arguments] != 2} {
return [call [list error "wrong # args: should be \"__branch condition destination\""] $state]
}
if {![string is bool [lindex $arguments 0]]} {
return [call [list error "condition must be an boolean value"] $state]
}
if {![string is digit [lindex $arguments 1]]} {
return [call [list error "destination must be numerical"] $state]
}
if {[string is true [lindex $arguements 0]]} {
dict set state frame pointer [expr $arguments - 1]
}
return [list {} $state]
}
proc exec_prim___jump {arguments state} {
log invoked [info level] [info level 0]
if {[llength $arguments] != 1} {
return [call [list error "wrong # args: should be \"__jump destination\""] $state]
}
if {![string is digit $arguments]} {
return [call [list error "destination must be numerical"] $state]
}
dict set state frame pointer [expr $arguments - 1]
return [list {} $state]
}
proc new_state {address} {
log invoked [info level] [info level 0]
dict set c my_address $address
dict set c frame args {}
dict set c frame pointer 0
dict set c frame results {}
dict set c frame variables {}
dict set c frame code [list [list error "capicol::new_state doesnt supply the code! you do!"]]
dict set c returnstack {}
set alist [list]
foreach content {+ - * / % & | ^ < << >> <= == != and any_messages? args beget break catch capabilities continue dict die drop_capability error gain get
if lappend lassign lindex linsert list llength lrange lrepeat lreplace lsearch lset lsort next_message or rename return routine send_message
set string uplevel var_exists? while __branch __jump
} {
lappend alist $content [list type prim contents $content]
}
dict set c commands $alist
dict set c quota [expr [string length [dict get $state commands]] + \
[string length [dict get $state returnstack]] + \
[string length [dict get $state frame]]]
return $c
}
}
namespace eval capicol::runtime {
variable capicols {}
variable runlist {}
proc run_one_slice {} {
log invoked [info level] [info level 0]
variable capicols
variable runlist
# round robin scheduling of run slices.
set name [lindex $runlist 0]
set runlist [join [list [lrange $runlist 1 end] [list $name]]]
set state [dict get $capicols $name]
if {![dict exists $state run_slice_size]} {
dict set state run_slice_size 8
}
set counter [dict get $state run_slize_size]
while {[dict get $state running]} {
set state [::capicol::interp::advance $state]
if {$counter == 0} { break }
incr counter -1
if {![dict get $state running]} {
deschedule $name
break
}
}
dict set capicols $name $state
}
proc died {state reason} {
variable capicols
set name [dict get $state my_address]
set message [list capicol-death $name $reason $state]
deschedule $name
dict unset capicols $name
set creator [join [lrange [split $name "."] 0 end-1]"."]
send_message [list $creator $message [string length $message]]
}
proc beget {child_name startup_code addresses quota} {
set child [::capicol::interp::new_state $child_name]
dict incr child quota $quota
lassign [caphandles_from_adddresses $addresses $child] dummy child
dict set child code [translate $startup_code]
variable capicols
dict set capicols $child_name $child
schedule $child_name
}
proc schedule {name} {
log invoked [info level] [info level 0]
variable capicols
variable runlist
dict set capicols $name running yes
if {![dict exists $capicols $name run_slice_size]} {
dict set capicols $name run_slice_size 8
}
if {[lsearch -exact $runlist $name] == -1} {
lappend runlist $name
}
return
}
proc deschedule {name} {
log invoked [info level] [info level 0]
variable capicols
variable runlist
dict set capicols $name running no
set t [lsearch -exact $runlist $name]
set runlist [lreplace $runlist $t $t]
return
}
proc send_message {sender message} {
log invoked [info level] [info level 0]
variable capicols
lassign $message addresses
lassign $addresses destination
if {[dict exists $capicols $destination]} {
# internal (between capicols on same machine/runtime)
schedule $destination
set t [dict get $capicols $destination in_queue]
lappend t $message
dict set $capicols $destination in_queue $t
} else {
# external (to external objects and between capicols on diffrent machines/runtimes)
variable external_handlers
foreach {pattern command} $external_handlers {
if {[string match $patter $destination]} {
append command " "
append command [list $destination]
append command " "
append command [list $sender]
append command " "
append command [list $message]
catch $command
}
}
}
return
}
proc register_external_handler {pattern command} {
variable external_handlers
set external_handlers "[list $command] [set external handlers]"
set external_handlers "[list $pattern] [set external handlers]"
return
}
proc unregister_external_handler {pattern command} {
variable external_handlers
set index 0
foreach {p c} $external_handlers {
if {[string equal $p $pattern] && [string equal $c $command]} {
set external_handlers [lreplace $external_handlers $index [expr $index +1]]
return
}
incr index 2
}
}
proc store_snapshot {filename} {
set fd [open $filename w]
fconfigure $fd -encoding utf-8
variable capicols
variable runlist
dict set tmp runlist $runlist
dict set tmp capicols $capicols
puts $fd $tmp
close $fd
}
proc load_snapshot {filename} {
set fd [open $filename r]
fconfigure $fd -encoding utf-8
set tmp [read $fd]
close $fd
variable capicols [dict get $tmp capicols]
variable runlist [dict get $tmp runlist]
}
proc looper {} {
run_one_slice
after idle [list ::capicol::runtime::looper]
}
proc start {} {
after idle [list ::capicol::runtime::looper]
}
}