Clemson University -- CPSC 231 -- Spring 2010 macro processor simple idea of textual substitution define(name,definition) place definition in symbol table and replace name with definition wherever it appears quoted string protects from replacement example define and replace sequence given by program /home/mark/231/sim/mp (must be on Sun SPARC system to run it) m4 macro processor a programming language with a quite different feel than C, Java, etc.: * textual substitution (replacement) rather than numeric data processing * function evaluation rather than variety of control structures * recursion rather than iteration (you can build iteration out of recursion) define(name,definition) -- macro definition - instructs m4 to replace each occurrence of "name" by its definition (textual substitution, a.k.a. expansion). You can define a macro once and use it several times. Leading unquoted blanks, tabs, and newlines are ignored. $1,$2,... inside "definition" -- macro parameters (based on position in call) for parameterized expansion ($0 = macro name, $* = all arguments $1 and above) name(arg1,arg2) -- macro call with two parameters (arguments) include(file) -- read in text of "file" `text' -- delay expansion of text but strip quotes (note open quote and close quote difference) changequote(^,!) -- change quote characters to other characters eval(expression) -- evaluate an arithmetic expression incr(arg) / decr(arg) -- returns the value incremented or decremented [ shortcuts for eval(arg+1), eval(arg-1) ] ifdef(arg1,arg2,arg3) -- if the first parameter is defined, return the second, otherwise return the third ifelse(arg1,arg2,arg3,arg4) -- if the first parameter is the same string as the second, return the third parameter, otherwise return the fourth (with a provision for nested ifelse evaluation using parameters 4,5,6, and 7) index(arg1,arg2) -- returns position within the first parameter where the second parameter begins (0-origin), or -1 if the second parameter is not a substring within the first parameter len(arg) -- returns the number of characters in the first parameter (i.e., the string length) substr(arg1,arg2,arg3) -- substring(string,start,length) where start is 0-origin position translit(arg1,arg2,arg3) -- transliterate first string using match characters of second parameter with substitute characters of third parameter divert(2) -- send output to second stream ... undivert -- print all streams dnl -- delete rest of line, including newline debugmode(V) -- turns on tracing and debugging output ---------- typical use in HLL of "magic number" substitution ---------- define(N,10) for(i=0;i') show(a) show( b ) define(`fn',`eval($1*$2)') fn(3) m4: Bad expression in eval: 3* fn(3 4) m4: Bad expression in eval (excess input): 3 4* fn (3,4) m4: Bad expression in eval: * (3,4) fn(3,4) 12 fn(3,4,5) 12 ---------- counting in m4, for use in location counting for assembly ---------- ------ note use of quoted lines on left to provide annotation of actions ------ % cat m4.script `define(loc,1) ' define(loc,1) `loc ' loc `define(loc,2) ' define(loc,2) `loc ' loc `define(`loc',3) ' define(`loc',3) `loc ' loc `loc+1 ' loc+1 `eval(loc+1) ' eval(loc+1) `define(loc,loc+1) ' define(loc,loc+1) `loc ' loc `define(`loc',loc+1) ' define(`loc',loc+1) `loc ' loc `define(`loc',eval(loc+1))' define(`loc',eval(loc+1)) `loc ' loc `define(`loc',eval(loc+1))' define(`loc',eval(loc+1)) `loc ' loc % % % m4 m4.script define(loc,1) loc 1 define(loc,2) loc 1 define(`loc',3) loc 3 loc+1 3+1 eval(loc+1) 4 define(loc,loc+1) loc 3 define(`loc',loc+1) loc 3+1 define(`loc',eval(loc+1)) loc 5 define(`loc',eval(loc+1)) loc 6 Note: There are different versions of m4 and they produce different results on errors. Specifically, you may or may not get an error message when you attempt the third line above, i.e., define(loc,2): /usr/ccs/bin/m4 gives bad macro name define(1,2) /usr/local/bin/m4 silently ignores the define ------ accumulator machine lacks a mac (multiply-accumulate) instruction ----- ---- demonstrates typical use of macros for abbreviation of code segments ---- -- (i.e., in-line functions w/o subroutine-call/reg.-save-restore overhead) -- define(mac,`load($2) mul($3) add($1)') mac(a,b,c) load(b) mul(c) add(a) ----- immediate values would be convenient for the accumulator machine ------- -------- this macro places the value in-line and then jumps over it ---------- define(`_xc',0) define(`add_imm',`comment(``expansion of $0($1)'') add(`_immediate_value_'_xc) ba(`_done_'_xc) word(`_immediate_value_'_xc,$1) label(`_done_'_xc) define(`_xc',incr(_xc)) ') add_imm(10) add_imm(25) ---------------- use of macro library file (SPARC assembly code) ------------ % cat parms.m define(`setmov',`ifelse(substr($1,0,1),%,mov,set)')dnl define(`send', `! call to `$0' with parameters $1 $2 $3 $4 $5 $6' `! place these parameters into %o0-%o5 registers' `ifelse(len($1),0,,` setmov($1) $1,%o0')' `ifelse(len($2),0,,` setmov($2) $2,%o1')' `ifelse(len($3),0,,` setmov($3) $3,%o2')' `ifelse(len($4),0,,` setmov($4) $4,%o3')' `ifelse(len($5),0,,` setmov($5) $5,%o4')' `ifelse(len($6),0,,` setmov($6) $6,%o5')' )dnl define(`multiply', `! call to `$0' with parameters $1 $2 $3' `ifelse($1,%o0,,` mov $1,%o0')' `ifelse($2,%o1,,` mov $2,%o1')' ` call .mul' ` nop' `ifelse($3,%o0,,` mov %o0,$3')' )dnl % m4 include(parms.m) send(a,b,c) ! call to send with parameters a b c ! place these parameters into %o0-%o5 registers set a,%o0 set b,%o1 set c,%o2 send(3,%l3) ! call to send with parameters 3 %l3 ! place these parameters into %o0-%o5 registers set 3,%o0 mov %l3,%o1 multiply(a,b,c) ! call to multiply with parameters a b c mov a,%o0 mov b,%o1 call .mul nop mov %o0,c multiply(%o0,%o1,%o0) ! call to multiply with parameters %o0 %o1 %o0 call .mul nop ---------------------- macro call to prtf (x86 wasm) ------------------------ ------- demonstrates use of expansion counter to create unique labels ------- define(prt, ``; macro expansion of $0 with parms $*'' `pushdef(`i',0)' `ifelse(len($6),0,,` push $6 define(`i',eval(i+2))')' `ifelse(len($5),0,,` push $5 define(`i',eval(i+2))')' `ifelse(len($4),0,,` push $4 define(`i',eval(i+2))')' `ifelse(len($3),0,,` push $3 define(`i',eval(i+2))')' `ifelse(len($2),0,,` push $2 define(`i',eval(i+2))')' `` lea ax,fmt'macro_exp_cntr define(`i',eval(i+2))' ` push ax' ` call prtf' ` add sp,i' `` jmps next'macro_exp_cntr' ``fmt'macro_exp_cntr` db '$1`,13,10'' ``next'macro_exp_cntr' `define(`macro_exp_cntr',eval(macro_exp_cntr+1))dnl' `popdef(`i')') define(macro_exp_cntr,0)dnl prt('%x',a) ; macro expansion of prt with parms '%x',a push a lea ax,fmt0 push ax call prtf add sp,4 jmps next0 fmt0 db '%x',13,10 next0 prt('%d %d %d',d,e,f) ; macro expansion of prt with parms '%d %d %d',d,e,f push f push e push d lea ax,fmt1 push ax call prtf add sp,8 jmps next1 fmt1 db '%d %d %d',13,10 next1 prt('title') ; macro expansion of prt with parms 'title' lea ax,fmt2 push ax call prtf add sp,2 jmps next2 fmt2 db 'title',13,10 next2 ---------- standard macro for iteration ----------- --- note save and restore of iteration variable --- define(`forloop', `pushdef(`$1', `$2')_forloop(`$1', `$2', `$3', `$4')popdef(`$1')')dnl define(`_forloop', `$4`'ifelse($1, `$3', , `define(`$1', incr($1))_forloop(`$1', `$2', `$3', `$4')')')dnl define(i,4) `i' is i forloop(`i', 1, 8, `this is iteration i ') `i' is i i is 4 this is iteration 1 this is iteration 2 this is iteration 3 this is iteration 4 this is iteration 5 this is iteration 6 this is iteration 7 this is iteration 8 i is 4 forloop(`j',0,10,`eval(2**j) = eval(2**j,2) ') 1 = 1 2 = 10 4 = 100 8 = 1000 16 = 10000 32 = 100000 64 = 1000000 128 = 10000000 256 = 100000000 512 = 1000000000 1024 = 10000000000 ---------- standard macro for iteration on embedded parameter list ---------- divert(-1) # foreach(x, (item_1, item_2, ..., item_n), stmt) define(`foreach', `pushdef(`$1', `')_foreach(`$1', `$2', `$3')popdef(`$1')') define(`_arg1', `$1') define(`_foreach', `ifelse(`$2', `()', , `define(`$1', _arg1$2)$3`'_foreach(`$1', (shift$2), `$3')')') divert foreach(`i',`(a,b,c)',`this is letter i ') this is letter a this is letter b this is letter c ------------------------ example macro library usage ------------------------ 1. place useful macros into a file, e.g., name it "macro_defs.m" 2. use "include(macro_defs.m)" in pass1.m and pass2.m before the inclusion of source.m ---------- end ----------