JHines: you can use defn() to access the body/content of a macro definition, and defn() allows you to access macros with names fitting an arbitrary regexp pattern that won't typically be expanded. So you can create a macro, and if that macro fits a unique pattern then it will be treated a certain way. This way you can implement a sort of pseudo-type.
For example, lets create a boolean type in m4. In terms of how m4 understands macros, the canonical description of a boolean type is already implicit. What I mean by that is ifelse($1,1,1,0) will actually look at the byte value contained at $1 and compare it with the value of 1 (returning 1 if true, and 0 if false). Other m4 macros achieve macro comparisons like regexp() and eval(). So, we can say that the closest thing to a 'false' value in m4 is an empty string, and the closest thing to 'true' in m4 is a non-empty string. CODE: define(getboolVal,`pushdef(_boolVal,defn($))_boolVal($1)popdef(`_boolVal')') define($,`ifelse'($`'1,`true',0,$`'1,`false',`')) getboolVal(true) On Mon, Aug 8, 2011 at 5:51 AM, Johnicholas Hines < [email protected]> wrote: > I'd be interested in an interactive shell also - but rather than a > code generator, I was imagining implementing another language IN m4, > just as you can write a lisp interpreter in C. > > Here's a snippet - I made it as small and simple as possible, possibly > too small and simple, but extending it to have variables that are > looked up in the environment, more control flow, should be > straightforward. > > divert(-1) > # a global counter used for making things unique > define(`_gensym',`0') > define(`gensym',`_gensym`'define(`_gensym',incr(_gensym))') > # exp ::= Num( int ) > define(`_num',`define(`eat_Num'$1,`$'`1_num($2,shift($'`@))')Num$1') > define(`num',`_num(gensym,$1)') > # exp ::= Plus( exp , exp ) > define(`_plus',`define(`eat_Plus'$1,`$'`1_plus($2,$3,shift($'`@))')Plus$1') > define(`plus',`_plus(gensym,$1,$2)') > # define print_exp by cases > # print_exp(Num(int)) => int > define(`_print_exp_num',`$1') > # print_exp(Plus(exp1,exp2)) => print_exp(exp1)+print_exp(exp2) > define(`_print_exp_plus',`print_exp($1)+print_exp($2)') > # actually dispatch > define(`print_exp',`eat_$1(_print_exp)') > # define my_eval by cases > # my_eval(Num(int)) => int > define(`_my_eval_num',`$1') > # my_eval(Plus(exp1,exp2)) => eval(my_eval(exp1)+my_eval(exp2)) > define(`_my_eval_plus',`eval(my_eval($1)+my_eval($2))') > # actually dispatch > define(`my_eval',`eat_$1(_my_eval)') > divert`'dnl > # Now we can use the new "little language" > print_exp(plus(plus(num(1),num(2)),num(3))) > my_eval(plus(num(4),plus(num(5),num(6)))) > > Is this sort of definition-by-cases a standard m4 idiom? > What would the idiomatic way to create a new abstract datatype be? > > Johnicholas > >
