# # tmg.tcl - a very small macroprocessor in TCL # # The syntax is a bit stricter than the older TMG, in particular # @$! must occur in the first column. See the wiki for a description. # # performance is not too bad, e.g. # to process 5712 lines, 174819 bytes using tkCanvas.c is around 24ms # on an old laptop. # # Copyright (c) 2020, Phil Maker # # No rights reserved. # package provide tmg 0.1 proc tmg {s filename} { set prog {} set ::_out {} set lines [split $s \n] set nlines [llength $lines] set lineno 0 set default_first "=" foreach line $lines { incr lineno if {$lineno < $nlines} { set sep "\\n" } else { set sep "" } set first [string index $line 0] ;# a command character or not set rest [string range $line 1 end] if {$first eq "="} { set default_first [string range $line 1 1] } elseif {$default_first eq "@"} { ;# plain old code append prog $line \n } elseif {$default_first eq "$"} { ;# $ only append prog "append _out \[subst -nocommands -nobackslashes [list $line]\] $sep" \n } elseif {$default_first eq "!"} { append prog "append _out \[subst [list $rest]\] $sep" \n } elseif {$first eq "@"} { append prog $rest \n } elseif {$first eq "$" || $default_first eq "$"} { ;# $ subst only append prog "append _out \[subst -nocommands -nobackslashes [list $rest]\] $sep" \n } elseif {$first eq "!" || $default_first eq "!"} { ;# $ and commands append prog "append _out \[subst [list $rest]\] $sep" \n } else { ;# literal append prog "append _out [list $line] $sep" \n } } # TODO: maybe add in frink style checking or tclCheck if {[catch [list uplevel #0 $prog] r opt]} { array set a $opt set lineno $a(-errorline) if {0} { puts stderr $prog puts stderr "$$$" } %failed "tmg failed with error $r at $filename:$lineno [_file_line $filename $lineno]" return "" } else { return $r } }