Richard Suchenwirth 2013-11-26 - No matter how much you give to people, they will always want more.

Take me for example. Just yesterday I found out that Tcl 8.6 comes with an experimental tcl::unsupported::assemble command (see TAL (Tcl Assembly Language) for many details), and after playing with it for a while, I already wanted to change things.

For example: in TAL, you put a constant to the top of stack with

 push myconst

Fair enough. But to push the value of a local variable, you are supposed to use

 load myvar

Also simple, but somehow not in the simple spirit of Tcl...

 % set myvar 42
 % asm {push $myvar}
 assembly code may not contain substitutions

But if everything is a string, can't I just do the substitutions in the string myself? After some experimentation, here comes..

the smallest macro-assembler in the world. First, just to reduce typing,

 # interp alias {} asm    {} ::tcl::unsupported::assemble ;# doesn't work! The following does (thanks dkf):
 namespace eval   tcl::unsupported {namespace export assemble}
 namespace import tcl::unsupported::assemble
 rename assemble asm

Then, I thought macro definition and substitution should be in separate places. Makes two lines of code.

 set masm_subs {"push $" "load "}

 proc masm code {uplevel 1 [list asm [string map $::masm_subs $code]]}

 #-- Testing... (equivalent to: proc lempty lst {expr {[llength $lst] == 0}} )
 proc lempty lst {masm {push $lst;listLength;push 0;eq}}
 puts [lempty {}]
 puts [lempty a] 

returns 1 resp. 0, as desired.

Now as I continue learning TAL, I may run into more useful substitutions.. and just lappend them to masm_subs ...

RFox - 2013-11-27 10:55:41

Given that you want assembly time substitution can't you just quote the assembly with "'s rather than {} and let the interpreter do the rest? e.g. not:

   asm {push $myvar}


    asm "push $myvar"

In the end that's what you are doing but with much more work.

RS 2013-11-27: I admit that the example is too simple. The real use case would be proc bodies done in asm or masm, where the value to be substituted is an argument, whose value is not known at assembly time - push takes a constant, load obtains the value of a local variable.

And "much more work" for two lines of code (could have done it in one, even)? My idea was an initial proof of concept that "macro assembly" can be preprocessed in to be in the language that the TAL assembler accepts. Once I have more time, say next weekend, more will come... one idea I have in mind, for instance, is to substitute

 jumpgt label

to the TAL two-instruction sequence

 jumpTrue label

RS 2013-11-30 - Easy enough...

set  masm_subs {"push $" "load " jumpgt "gt;jumpTrue"}
proc masm code {uplevel 1 [list asm [string map $::masm_subs $code]]}
proc mgt {x y} {masm {
    load x;load y;jumpgt GT;push no;jump Done;
    label GT; push yes
    label Done

See also dis2asm