package require struct namespace eval optimize { ::struct::stack ::optimize::clear ::struct::stack ::optimize::save ::struct::stack ::optimize::touches variable newcode variable slave set relative 0 # Passthrough creates a proc to pass through # a command to the next pass. proc Passthrough {name arglist} { set code "proc $name \{$arglist\} \{variable newcode;append newcode \"$name" foreach i $arglist { append code " \$[set i]" } append code "\\n\"\}" eval $code } # This proc makes sure the compiler doesn't make unsafe assumptions in relative addressing. proc Unsafe {} { variable relative set tostack "" if {$relative} { for {set i [clear size];clear clear} {$i != 0} {incr i -1} { clear push "" } } } # Returns 0 if the cell isn't clear. proc Cleared {var} { if {[string match "* $var *" [if {[clear size]} {clear peek}]]} { return 1 } else { return 0 } } # This binds our commands into the slave interpreter. proc binder {interp} { variable slave set slave $interp foreach i "[$slave aliases]" { $slave alias $i {} } foreach i {left right at @ set add subtract in out is0 isnot0 inline forceinline while set goto be0 lang sameval} { $slave alias $i ::optimize::$i } foreach i {while set} { $slave alias $i ::optimize::_$i } $slave alias \# \# } # The following commands aren't passed through. eval { # Append to the top of the clear-stack $args. proc is0 {args} { set args [regsub \{ $args ""] set args [regsub \} $args ""] foreach i $args { if {$i ne "current" && ![string match "* $i *" [expr {[touches size]?[touches peek]:{}}]]} { clear push "[if {[clear size]} {clear pop}] $i " } } } # Remove $args from everywhere in the clear-stack. proc isnot0 {args} { set args [regsub \{ $args ""] set args [regsub \} $args ""] foreach i $args { if {![string match "* $i *" [expr {[touches size]?[touches peek]:{}}]]} { for {set n [clear size]} {$n != 0} {incr n -1} { save push [clear pop] } for {set n [save size]} {$n != 0} {incr n -1} { clear push [regsub -all " $i " [save pop] ""] } } } } # Inform the programmer about the internal cleared status of a cell. proc be0 {args} { set args [regsub \{ $args ""] set args [regsub \} $args ""] foreach i $args { if {[string match "* $i *" [clear peek]]} { puts stderr "$i yes." } else { puts stderr "$i no." } } puts stderr [clear peek] } # If vara is 0, make varb 0. If vara isn't 0, make varb not 0. proc sameval {vara varb} { if {[string match "* $vara *" [clear peek]]} { is0 $varb } else { isnot0 $varb } } } # The following commands are passed through, but handled specially. eval { # Manual "left" calls use relative addressing. proc left {num} { variable newcode variable relative if {$num != 0} { append newcode "left $num\n" } set relative 1 } # Manual "right" calls use relative addressing. proc right {num} { variable newcode variable relative if {$num != 0} { append newcode "right $num\n" } set relative 1 } # "at" calls end relative addressing. proc at {var} { variable newcode variable relative append newcode "at $var\n" set relative 0 } # add makes a cell non-zero; is a non-safe operation in relative addressing. proc add {var num} { variable newcode isnot0 $var append newcode "add $var $num\n" Unsafe } # subtract makes a cell non-zero; is a non-safe operation in relative addressing. proc subtract {var num} { variable newcode isnot0 $var append newcode "subtract $var $num\n" Unsafe } # in makes a cell non-zero; is a non-safe operation in relative addressing. proc in {var} { variable newcode isnot0 $var append newcode "in $var\n" Unsafe } # Only pass a while loop through if it'll actually run. proc _while {var code {touches {}} {varlist {}}} { variable newcode variable slave append newcode "while $var \{\n" touches push $varlist clear push [if {[clear size]} {clear peek}] # set code [interp invokehidden slave subst $code] $slave eval $code clear pop touches pop is0 $var append newcode "\}\n" } # Only pass a set through if it'll run. proc _set {var num} { variable newcode Unsafe foreach i $var { if {![Cleared $var]} { append newcode "set $var 0\n" } is0 $i if {$num > 0} { add $i $num } elseif {$num < 0} { subtract $i [regsub -- - $num ""] } } } # New variables should be 0. proc @ {var num} { variable newcode append newcode "@ $var $num\n" is0 $var } # Can't be passed through normally. proc lang {code} { variable newcode append newcode "lang \{$code\}\n" } } # Raw passthrough commands. foreach {name args} {goto {var num} out {var} inline {args} forceinline {args}} { Passthrough $name $args } }