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