me_tcl.tcl

Go to the documentation of this file.
00001 /*  -*- tcl -*-*/
00002 /*  ### ### ### ######### ######### #########*/
00003 /*  Package description*/
00004 
00005 /*  Implementation of the ME virtual machine as a singleton, tied to*/
00006 /*  Tcl for control flow and stack handling (except the AST stack).*/
00007 
00008 /*  ### ### ### ######### ######### #########*/
00009 /*  Requisites*/
00010 
00011 /*  ### ### ### ######### ######### #########*/
00012 /*  Implementation*/
00013 
00014 namespace ::grammar::me::tcl {
00015     namespace export \
00016     init lc tok sv tokens ast \
00017     astall ctok nc next ord \
00018     \
00019     isv_clear              ict_advance        inc_save    \
00020     isv_terminal           ict_match_token    inc_restore \
00021     isv_nonterminal_leaf   ict_match_tokrange icl_get     \
00022     isv_nonterminal_range  ict_match_tokclass icl_rewind  \
00023     isv_nonterminal_reduce iok_ok      \
00024     ier_clear              iok_fail    \
00025     ier_get                iok_negate  \
00026     ier_expected           ias_push    \
00027     ier_nonterminal        ias_mark    \
00028     ier_merge              ias_pop2mark
00029 
00030     variable ok
00031 }
00032 
00033 /*  ### ### ### ######### ######### #########*/
00034 /*  Implementation, API. Ensemble command.*/
00035 
00036 ret  ::grammar::me::tcl (type cmd , type args) {
00037     # Dispatcher for the ensemble command.
00038     variable tcl::cmds
00039     return [uplevel 1 [linsert $args 0 $cmds($cmd)]]
00040 }
00041 
00042 namespace grammar::me::tcl {
00043     variable cmds
00044 
00045     /*  Mapping from cmd names to procedures for quick dispatch. The*/
00046     /*  objects will shimmer into resolved command references.*/
00047 
00048     array  cmds =  {
00049     init   ::grammar::me::tcl::init
00050     lc     ::grammar::me::tcl::lc
00051     tok    ::grammar::me::tcl::tok
00052     sv     ::grammar::me::tcl::sv
00053     tokens ::grammar::me::tcl::tokens
00054     ast    ::grammar::me::tcl::ast
00055     astall ::grammar::me::tcl::astall
00056     ctok   ::grammar::me::tcl::ctok
00057     nc     ::grammar::me::tcl::nc
00058     next   ::grammar::me::tcl::next
00059     ord    ::grammar::me::tcl::ord
00060     }
00061 }
00062 
00063 /*  ### ### ### ######### ######### #########*/
00064 /*  API Implementation.*/
00065 
00066 ret  ::grammar::me::tcl::init (type nxcmd , optional tokmap ={)} {
00067     variable next  $nxcmd
00068     variable as    {}
00069     variable ok    0
00070     variable error {}
00071     variable sv    {}
00072     variable loc  -1
00073     variable ct    {}
00074     variable tc    {}
00075     variable nc
00076     variable tokOrd
00077     variable tokUseOrd 0
00078 
00079     array un nc =      *
00080     array un tokOrd =  *
00081 
00082     if {[llength $tokmap]} {
00083     if {[llength $tokmap] % 2 == 1} {
00084         return -code error \
00085             "Bad token order map, not a dictionary"
00086     }
00087     array  tokOrd =  $tokmap
00088      tokUseOrd =  1
00089     }
00090     return
00091 }
00092 
00093 ret  ::grammar::me::tcl::lc (type pos) {
00094     variable tc
00095     return [lrange [lindex $tc $pos] 2 3]
00096 }
00097 
00098 ret  ::grammar::me::tcl::tok (type from , optional to ={)} {
00099     variable tc
00100     if {$to == {}} { to =  $from}
00101     return [lrange $tc $from $to]
00102 }
00103 
00104 ret  ::grammar::me::tcl::tokens () {
00105     variable tc
00106     return [llength $tc]
00107 }
00108 
00109 ret  ::grammar::me::tcl::sv () {
00110     variable sv
00111     return  $sv
00112 }
00113 
00114 ret  ::grammar::me::tcl::ast () {
00115     variable as
00116     return [lindex $as end]
00117 }
00118 
00119 ret  ::grammar::me::tcl::astall () {
00120     variable as
00121     return $as
00122 }
00123 
00124 ret  ::grammar::me::tcl::ctok () {
00125     variable ct
00126     return  $ct
00127 }
00128 
00129 ret  ::grammar::me::tcl::nc () {
00130     variable nc
00131     return [array get nc]
00132 }
00133 
00134 ret  ::grammar::me::tcl::next () {
00135     variable next
00136     return  $next
00137 }
00138 
00139 ret  ::grammar::me::tcl::ord () {
00140     variable tokOrd
00141     return [array get tokOrd]
00142 }
00143 
00144 /*  ### ### ### ######### ######### #########*/
00145 /*  Terminal matching*/
00146 
00147 ret  ::grammar::me::tcl::ict_advance (type msg) {
00148     # Inlined: Getch, Expected, ClearErrors
00149 
00150     variable ok
00151     variable error
00152     # ------------------------
00153     variable tc
00154     variable loc
00155     variable ct
00156     # ------------------------
00157     variable next
00158     # ------------------------
00159 
00160     # Satisfy from input cache if possible.
00161     incr loc
00162     if {$loc < [llength $tc]} {
00163     set ct [lindex $tc $loc 0]
00164     set ok 1
00165     set error {}
00166     return
00167     }
00168 
00169     # Actually read from the input, and remember
00170     # the information.
00171 
00172     # Read from buffer, and remember.
00173     # Note: loc is the instance variable.
00174     # This implicitly increments the location!
00175 
00176     set tokdata [uplevel \#0 $next]
00177     if {![llength $tokdata]} {
00178     set ok 0
00179     set error [list $loc [list $msg]]
00180     return
00181     } elseif {[llength $tokdata] != 4} {
00182     return -code error "Bad callback result, expected 4 elements"
00183     }
00184 
00185     lappend tc $tokdata
00186     set ct [lindex $tokdata 0]
00187     set ok    1
00188     set error {}
00189     return
00190 }
00191 
00192 ret  ::grammar::me::tcl::ict_match_token (type tok , type msg) {
00193     variable ct
00194     variable ok
00195 
00196     set ok [expr {$tok eq $ct}]
00197 
00198     OkFail $msg
00199     return
00200 }
00201 
00202 ret  ::grammar::me::tcl::ict_match_tokrange (type toks , type toke , type msg) {
00203     variable ct
00204     variable ok
00205     variable tokUseOrd
00206     variable tokOrd
00207 
00208     if {$tokUseOrd} {
00209     set ord $tokOrd($ct)
00210     set ok [expr {
00211         ($toks <= $ord) &&
00212         ($ord <= $toke)
00213     }] ; # {}
00214     } else {
00215     set ok [expr {
00216         ([string compare $toks   $ct] <= 0) &&
00217         ([string compare $ct   $toke] <= 0)
00218     }] ; # {}
00219     }
00220 
00221     OkFail $msg
00222     return
00223 }
00224 
00225 ret  ::grammar::me::tcl::ict_match_tokclass (type code , type msg) {
00226     variable ct
00227     variable ok
00228 
00229     set ok [string is $code -strict $ct]
00230 
00231     OkFail $msg
00232     return
00233 }
00234 
00235 ret  ::grammar::me::tcl::OkFail (type msg) {
00236     variable ok
00237     variable error
00238     variable loc
00239 
00240     # Inlined: Expected, Unget, ClearErrors
00241 
00242     if {!$ok} {
00243     set error [list $loc [list $msg]]
00244     incr loc -1
00245     } else {
00246     set error {}
00247     }
00248     return
00249 }
00250 
00251 /*  ### ### ### ######### ######### #########*/
00252 /*  Nonterminal cache*/
00253 
00254 ret  ::grammar::me::tcl::inc_restore (type symbol) {
00255     variable loc
00256     variable nc
00257     variable ok
00258     variable error
00259     variable sv
00260 
00261     # Satisfy from cache if possible.
00262     if {[info exists nc($loc,$symbol)]} {
00263     foreach {go ok error sv} $nc($loc,$symbol) break
00264 
00265     # Go forward, as the nonterminal matches (or not).
00266     set loc $go
00267     return 1
00268     }
00269     return 0
00270 }
00271 
00272 ret  ::grammar::me::tcl::inc_save (type symbol , type at) {
00273     variable loc
00274     variable nc
00275     variable ok
00276     variable error
00277     variable sv
00278 
00279     if 0 {
00280     if {[info exists nc($at,$symbol)]} {
00281         return -code error "Cannot overwrite\
00282             existing data @ ($at, $symbol)"
00283     }
00284     }
00285 
00286     # FIXME - end location should be argument.
00287 
00288     # Store not only the value, but also how far
00289     # the match went (if it was a match).
00290 
00291     set nc($at,$symbol) [list $loc $ok $error $sv]
00292     return
00293 }
00294 
00295 /*  ### ### ### ######### ######### #########*/
00296 /*  Unconditional matching.*/
00297 
00298 ret  ::grammar::me::tcl::iok_ok () {
00299     variable ok 1
00300     return
00301 }
00302 
00303 ret  ::grammar::me::tcl::iok_fail () {
00304     variable ok 0
00305     return
00306 }
00307 
00308 ret  ::grammar::me::tcl::iok_negate () {
00309     variable ok
00310     set ok [expr {!$ok}]
00311     return
00312 }
00313 
00314 /*  ### ### ### ######### ######### #########*/
00315 /*  Basic input handling and tracking*/
00316 
00317 ret  ::grammar::me::tcl::icl_get () {
00318     variable loc
00319     return  $loc
00320 }
00321 
00322 ret  ::grammar::me::tcl::icl_rewind (type oldloc) {
00323     variable loc
00324 
00325     if 0 {
00326     if {($oldloc < -1) || ($oldloc > $loc)} {
00327         return -code error "Bad location \"$oldloc\" (vs $loc)"
00328     }
00329     }
00330     set loc $oldloc
00331     return
00332 }
00333 
00334 /*  ### ### ### ######### ######### #########*/
00335 /*  Error handling.*/
00336 
00337 ret  ::grammar::me::tcl::ier_get () {
00338     variable error
00339     return  $error
00340 }
00341 
00342 ret  ::grammar::me::tcl::ier_clear () {
00343     variable error {}
00344     return
00345 }
00346 
00347 ret  ::grammar::me::tcl::ier_nonterminal (type msg , type pos) {
00348     # Inlined: Errors, Expected.
00349 
00350     variable error
00351 
00352     if {[llength $error]} {
00353     foreach {l m} $error break
00354     incr pos
00355     if {$l == $pos} {
00356         set error [list $l [list $msg]]
00357     }
00358     }
00359 }
00360 
00361 ret  ::grammar::me::tcl::ier_merge (type new) {
00362     variable error
00363 
00364     # We have either old or new error data, keep it.
00365 
00366     if {![llength $error]} {set error $new ; return}
00367     if {![llength $new]}   {return}
00368 
00369     # If one of the errors is further on in the input choose that as
00370     # the information to propagate.
00371 
00372     foreach {loe msgse} $error break
00373     foreach {lon msgsn} $new   break
00374 
00375     if {$lon > $loe} {set error $new ; return}
00376     if {$loe > $lon} {return}
00377 
00378     # Equal locations, merge the message lists.
00379 
00380     foreach m $msgsn {lappend msgse $m}
00381     set error [list $loe [lsort -uniq $msgse]]
00382     return
00383 }
00384 
00385 /*  ### ### ### ######### ######### #########*/
00386 /*  Operations for the construction of the*/
00387 /*  abstract syntax tree (AST).*/
00388 
00389 ret  ::grammar::me::tcl::isv_clear () {
00390     variable sv {}
00391     return
00392 }
00393 
00394 ret  ::grammar::me::tcl::isv_terminal () {
00395     variable loc
00396     variable sv
00397     variable as
00398 
00399     set sv [list {} $loc $loc]
00400     lappend as $sv
00401     return
00402 }
00403 
00404 ret  ::grammar::me::tcl::isv_nonterminal_leaf (type nt , type pos) {
00405     # Inlined clear, reduce, and optimized.
00406     variable ok
00407     variable loc
00408     variable sv {}
00409 
00410     # Clear ; if {$ok} {Reduce $nt}
00411 
00412     if {$ok} {
00413     incr pos
00414     set sv [list $nt $pos $loc]
00415     }
00416     return
00417 }
00418 
00419 ret  ::grammar::me::tcl::isv_nonterminal_range (type nt , type pos) {
00420     variable ok
00421     variable loc
00422     variable sv {}
00423 
00424     if {$ok} {
00425     # TerminalString $pos
00426     # Get all characters after 'pos' to current location as terminal data.
00427 
00428     incr pos
00429     set sv [list $nt $pos $loc [list {} $pos $loc]]
00430 
00431     #set sv [linsert $sv 0 $nt] ;#Reduce $nt
00432     }
00433     return
00434 }
00435 
00436 ret  ::grammar::me::tcl::isv_nonterminal_reduce (type nt , type pos , optional mrk =0) {
00437     variable ok
00438     variable as
00439     variable loc
00440     variable sv {}
00441 
00442     if {$ok} {
00443     incr pos
00444     set sv [lrange $as $mrk end]         ;#SaveToMark $mrk
00445     set sv [linsert $sv 0 $nt $pos $loc] ;#Reduce $nt
00446     }
00447     return
00448 }
00449 
00450 /*  ### ### ### ######### ######### #########*/
00451 /*  AST stack handling*/
00452 
00453 ret  ::grammar::me::tcl::ias_push () {
00454     variable as
00455     variable sv
00456     lappend as $sv
00457     return
00458 }
00459 
00460 ret  ::grammar::me::tcl::ias_mark () {
00461     variable as
00462     return [llength $as]
00463 }
00464 
00465 ret  ::grammar::me::tcl::ias_pop2mark (type mark) {
00466     variable as
00467     if {[llength $as] <= $mark} return
00468     incr mark -1
00469     set as [lrange $as 0 $mark]
00470     return
00471 }
00472 
00473 /*  ### ### ### ######### ######### #########*/
00474 /*  Data structures.*/
00475 
00476 namespace ::grammar::me::tcl {
00477     /*  ### ### ### ######### ######### #########*/
00478     /*  Public State of MVM (Matching Virtual Machine)*/
00479 
00480     variable ok   0  ; /*  Boolean: Ok/Fail of last match operation.*/
00481 
00482     /*  ### ### ### ######### ######### #########*/
00483     /*  Internal state.*/
00484 
00485     variable ct   {}  ; /*  Current token.*/
00486     variable loc  0   ; /*  Location of 'ct' as offset in input.*/
00487 
00488     variable error {} ; /*  Error data for last match.*/
00489     /*                  ; # == List (loc, list of strings)*/
00490     /*                  ; # or empty list*/
00491     variable sv   {}  ; /*  Semantic value for last match.*/
00492 
00493     /*  ### ### ### ######### ######### #########*/
00494     /*  Data structures for AST construction*/
00495 
00496     variable as {} ; /*  Stack of values for AST*/
00497 
00498     /*  ### ### ### ######### ######### #########*/
00499     /*  Memo data structures for tokens and match results.*/
00500 
00501     variable tc {}
00502     variable nc ; array  nc =  {}
00503 
00504     /*  ### ### ### ######### ######### #########*/
00505     /*  Input buffer, location of next character to read.*/
00506     /*  ASSERT (loc <= cloc)*/
00507 
00508     variable next   ; /*  Callback to get next character.*/
00509 
00510     /*  Token ordering for range checks. Optional*/
00511 
00512     variable tokOrd ; array  tokOrd =  {}
00513     variable tokUseOrd 0
00514 
00515     /*  ### ### ### ######### ######### #########*/
00516 }
00517 
00518 /*  ### ### ### ######### ######### #########*/
00519 /*  Package Management*/
00520 
00521 package provide grammar::me::tcl 0.1
00522 

Generated on 21 Sep 2010 for Gui by  doxygen 1.6.1