wip.tcl
Go to the documentation of this file.00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025 package require Tcl 8.4
00026
00027
00028 package require snit 1.3
00029
00030
00031 package require struct::
00032
00033 # ### ### ### ######### ######### #########
00034 ## API = & Implementation
00035
00036 snit::type wip {
00037
00038
00039
00040
00041 constructor {e} {} ;
00042
00043
00044 ret def (type name , optional cp ={)} {} ; # Define a DSL command.
00045 method defl {names} {} ; # Def many, simple names (cp = name)
00046 method defd {dict} {} ; # s.a. name/cp dict
00047 method deflva {args} {} ; # s.a. defl, var arg form
00048 method defdva {args} {} ; # s.a. defd, var arg form
00049
00050 # Execution of word lists.
00051 method runl {alist} {} ; # execute list of words
00052 method run {args} {} ; # ditto, words as varargs
00053 method run_next {} {} ; # run the next command in the input.
00054 method run_next_while {accept} {} ; # s.a., while acceptable command
00055 method run_next_until {reject} {} ; # s.a., until rejectable command
00056
00057 # Manipulation of the input word list.
00058 method peek {} {} ; # peek at next word in input
00059 method next {} {} ; # pull next word from input
00060 method insert {at args} {} ; # insert words back into the input
00061 method push {args} {} ; # ditto, at == 0
00062
00063 # ### ### ### ######### ######### #########
00064 ## Processor construction.
00065
00066 constructor {e args} {
00067 if {$e eq ""} {
00068 return -code error "No engine specified"
00069 }
00070 engine = $e
00071 $self Definitions $args
00072 return
00073 }
00074
00075 ret Definitions (type alist) {
00076 # args = series of 'def name' and 'def name cp' statements.
00077 # The code to handle them is in essence a WIP too, just
00078 # hardcoded, as state machine.
00079
00080 set state expect-def
00081 set n {}
00082 set cp {}
00083 foreach a $alist {
00084 if {$state eq "expect-def"} {
00085 if {$a ne "def"} {
00086 return -code error "Expected \"def\", got \"$a\""
00087 }
00088 set state get-name
00089 } elseif {$state eq "get-name"} {
00090 set name $a
00091 set state get-cp-or-def
00092 } elseif {$state eq "get-cp-or-def"} {
00093 # This means that 'def' cannot be a command prefix for
00094 # DSL command.
00095 if {$a eq "def"} {
00096 # Short definition, name only, completed.
00097 $self def $name
00098 # We already have the first word of the next
00099 # definition here, name is coming up next.
00100 set state get-name
00101 } else {
00102 # Long definition, name + cp, completed.
00103 $self def $name $a
00104 # Must be followed by the next definition.
00105 set state expect-def
00106 }
00107 }
00108 }
00109 if {$state eq "get-cp-or-def"} {
00110 # Had a short definition last, now complete.
00111 $self def $name
00112 } elseif {$state eq "get-name"} {
00113 # Incomplete definition at the end, bogus
00114 return -code error "Incomplete definition at end, name missing."
00115 }
00116 return
00117 }
00118
00119
00120
00121
00122
00123
00124 variable engine {} ;
00125 variable program {} ;
00126 variable arity -array {} ;
00127 variable cmd -array {} ;
00128
00129
00130
00131
00132
00133
00134
00135
00136
00137 ret def (type name , optional mp ={)} {
00138 if {$mp eq {}} {
00139
00140 mp = [list $name]
00141 m = $name
00142 n = 0
00143
00144 } else {
00145
00146
00147
00148 m = [lindex $mp 0]
00149 n = [expr {[llength $mp]-1}]
00150 }
00151
00152
00153 a = [$engine info args $m]
00154 if {[lindex $a end] eq "args"} {
00155 return -code error "Unable to handle Tcl varargs"
00156 }
00157
00158
00159
00160
00161
00162 cmd = ($name) [linsert $mp 0 $engine]
00163 arity = ($name) [expr {[llength $a] - $n}]
00164 return
00165 }
00166
00167 ret deflva (type args) { $self defl $args ; return }
00168 ret defdva (type args) { $self defd $args ; return }
00169 ret defl (type names) { foreach n $names { $self def $n } ; return }
00170 ret defd (type dict) {
00171 if {[llength $dict]%2==1} {
00172 return -code error "Expected a dictionary, got \"$dict\""
00173 }
00174 foreach {name mp} $dict {
00175 $self def $name $mp
00176 }
00177 return
00178 }
00179
00180
00181
00182
00183
00184
00185
00186 ret run (type args) {
00187 return [$self runl $args]
00188 }
00189
00190 ret runl (type alist) {
00191 # Note: We are saving the current program and restore it
00192 # afterwards, this handles the possibility that this is a
00193 # recursive call into the dispatcher.
00194 set saved $program
00195 set program $alist
00196 set r {}
00197 while {[llength $program]} {
00198 set r [$self run_next]
00199 }
00200 set program $saved
00201 return $r
00202 }
00203
00204 ret run_next_while (type accept) {
00205 set r {}
00206 while {[struct::set contains $accept [$self peek]]} {
00207 set r [$self run_next]
00208 }
00209 return $r
00210 }
00211
00212 ret run_next_until (type reject) {
00213 set r {}
00214 while {![struct::set contains $reject [$self peek]]} {
00215 set r [$self run_next]
00216 }
00217 return $r
00218 }
00219
00220 ret run_next () {
00221 # The first word in the list is the current command. Determine
00222 # the number of its fixed arguments. This also checks command
00223 # validity in general.
00224
00225 set c [lindex $program 0]
00226 if {![info exists arity($c)]} {
00227 return -code error \
00228 "Unknown command \"$c\""
00229 }
00230
00231 set n $arity($c)
00232 set m $cmd($c)
00233
00234 # Take the fixed arguments from the input as well.
00235
00236 set cargs [lrange $program 1 $n]
00237 incr n
00238
00239 # Remove the command to dispatch, and its fixed arguments from
00240 # the program. This is done before the dispatch so that the
00241 # command has access to the true current state of the input.
00242
00243 set program [lrange $program $n end]
00244
00245 # Now run the command with its arguments. Commands needing
00246 # more than the declared fixed number of arguments are
00247 # responsible for reading them from input via the method
00248 # 'next' provided by the processor core.
00249
00250 # Note: m already has the engine at the front, it was stored
00251 # that way, see 'def'.
00252
00253 if {![llength $cargs]} {
00254 return [eval $m]
00255 } else {
00256 # Explanation: First linsert constructs 'linsert $m end {*}$cargs',
00257 # which the inner eval transforms into '{*}$m {*}$cargs', which at
00258 # last is run by the outer eval.
00259 return [eval [eval [linsert $cargs 0 linsert $m end]]]
00260 }
00261 }
00262
00263
00264
00265
00266
00267 ret next () {
00268 set w [lindex $program 0]
00269 set program [lrange $program 1 end]
00270 return $w
00271 }
00272
00273
00274 ret peek () {
00275 return [lindex $program 0]
00276 }
00277
00278
00279 ret peekall () {
00280 return $program
00281 }
00282
00283
00284 ret replace (type args) {
00285 set program $args
00286 return
00287 }
00288 ret replacel (type alist) {
00289 set program $alist
00290 return
00291 }
00292
00293
00294 ret insert (type at , type args) {
00295 set program [eval [linsert $args end linsert $program $at]]
00296 return
00297 }
00298 ret insertl (type at , type alist) {
00299 set program [eval [linsert $alist end linsert $program $at]]
00300 return
00301 }
00302
00303
00304 ret push (type args) {
00305 set program [eval [linsert $args end linsert $program 0]]
00306 return
00307 }
00308 ret pushl (type alist) {
00309 set program [eval [linsert $alist end linsert $program 0]]
00310 return
00311 }
00312
00313
00314 ret add (type args) {
00315 set program [eval [linsert $args end linsert $program end]]
00316 return
00317 }
00318 ret addl (type alist) {
00319 set program [eval [linsert $alist end linsert $program end]]
00320 return
00321 }
00322
00323
00324
00325
00326 }
00327
00328
00329
00330
00331
00332
00333
00334
00335
00336
00337
00338
00339 snit::macro wip::ret asproc (type var , type method , type suffix) {
00340 proc $method$suffix {args} [string map [list @v@ $var @m@ $method] {
00341 upvar 1 {@v@} dst
00342 return [eval [linsert $args 0 $dst {@m@}]]
00343 }]
00344 }
00345
00346
00347
00348
00349
00350
00351
00352
00353
00354
00355
00356
00357
00358 snit::macro wip::dsl {{suffix {}}} {
00359 if {$suffix ne ""} { suffix = _$suffix}
00360
00361
00362 component wip$suffix
00363
00364
00365
00366
00367 ret wip$(type suffix)_setup {} [string map [list @@ $suffix] {
00368 install {wip@@} using wip "${selfns}::wip@@" $self
00369 }]
00370
00371
00372
00373
00374 foreach {p} {
00375 add addl def
00376 defd defdva defl deflva
00377 insert insertl replace replacel
00378 push pushl run runl
00379 next peek peekall run_next
00380 run_next_until run_next_while
00381 } {
00382 wip::ret asproc wip$suffix $p $suffix
00383 }
00384 return
00385 }
00386
00387 # ### ### ### ######### ######### #########
00388 ## Ready
00389
00390 package provide wip 1.0
00391