Comparative Macrology
wilfred.me.uk
wilfred.me.uk
"Text based fexpr":
gen_swap_code()
{
echo "local tmp=\$$1;"
echo "$1=\$$2;"
echo "$2=\$tmp;"
}
fun()
{
local a=3
local b=4
eval $(gen_swap_code a b) # sugar needed here
echo $a $b
}
$ fun
4 3Tcl doesn't have comparable macros, though some writers say in Tcl coding, the whole thing is like writing a bunch of macros anyway, but I'm not sure I buy that.
I'm also not entirely clear what you meant by "user-defined interpretive operators ...". Current Tcl versions provide many facilities for specifying semantics of expressions (proc, ensembles, interpreters, lambdas, etc.) and the ability to redefine most any operator or built-in function if one so desires.
The philosophy of "it means what I say it means" can lead to strange constructions, but that is a basis for describing Tcl as "macro-like" in its abundant, highly flexible features. The many ways it reflects Lisp/Scheme-like traits is truly part of its appeal.
Using fresh symbols is not enough. GENSYM creates uninterned symbols - those are not interned in a package. Thus referencing them in user code is not possible.
Shadowing 'SETF' in Common Lisp would be non-conforming. SBCL for example detects it and gives an error. The ANSI CL Standard, Section 11.1.2.1.2. http://www.lispworks.com/documentation/HyperSpec/Body/11_aba... Still for user code in macro expansions it can be a problem.
If the SWAP macro really should work with places in a semantic compatible way, then it needs to do more. Compare with the built-in ROTATEF macro. It makes sure that place subforms are only evaluated once.
? (let ((a (vector 1 2)))
(swap (aref a (print 0)) (aref a (print 1)))
a)
0
0
1
1
#(2 1)
Note that it prints 0 and 1 twice.The built-in ROTATEF does it better: it prints them only once. This shows that the subforms are only evaluated once.
? (let ((a (vector 1 2)))
(rotatef (aref a (print 0)) (aref a (print 1)))
a)
0
1
#(2 1)
The EACH-IT macro has a bug: the body needs to be put into a PROGN. Otherwise the LOOP macro might think that parts of the body are LOOP clauses. (defmacro each-it (list &rest body)
`(loop for it in ,list
do (progn ,@body)))
For the purposes as an example, DOTIMES would be more idiomatic, since it provides the basic stuff like declarations and goto tags. It is also the traditional iteration macro for lists.An alternative to PROGN would be LOCALLY, which also allows declarations. Thus one could declare IT to be of a certain type, for example.
#define SWAP(x, y) { \
typeof (x) x##y = x; \
x = y; \
y = x##y; \
}
Also, you can be tricky and "co-recurse" in macros, which is explained here: https://github.com/pfultz2/Cloak/wiki/C-Preprocessor-tricks,...Regarding each-it, I think its pretty doable in C. Right off the top of my head, this seems to work:
#define eachIt(myArray) typeof(myArray[0]) it; for (int i = 0; i < sizeof(myArray)/sizeof(myArray[0]) && (it = myArray[i], 1); ++i)
This can now be used just as in the example: struct test_ { int one; double two; };
typedef test_ test;
test array[17];
eachIt(array) {
it.one = 1;
it.two = 0.2;
}
However, I believe(?) that it depends on the version of the C standard whether this is hygienic or not. When I compile in GCC it certainly works fine because the i is counted as internal to the for loops scope, but I'm not sure that's always the case. There is also the separate question of whether "it" should be hygienic or not.> However, it prevents you from writing generic macros that use any l-value – we can’t write swap!(x[0], x[1]) as we could in Common Lisp.
As long as `macro_rules!` has existed (I could be wrong about before 0.8 though), you can wrote a macro that takes any expression, and will error from the assignment when the expression is not an lvalue:
macro_rules! swap {
($x:expr, $y:expr) => ({
let tmp = $x;
$x = $y;
$y = tmp;
})
}Dylan was one of the original languages to have an infix-syntax with a Lisp / Scheme style macro system. (It is fairly similar to Scheme's syntax rules, but there's an implementation of something more powerful that is used within the compiler but isn't currently available to users.)
The examples that he is demonstrating in the other languages would like this in Dylan:
define macro swap!
{ swap! (?place1:expression, ?place2:expression) }
=>
{ let value = ?place1;
?place1 := ?place2;
?place2 := value; }
end;
and: define macro each-it
{ each-it (?collection:expression)
?:body
end }
=>
{ for (?=it in ?collection)
?body
end };
end;
Dylan's macros are hygienic by default, and as can be seen in the ``each-it`` macro, ``?=it`` is a way to violate hygiene without much difficulty, when needed.My full post as linked above contains links to documentation and other minor details.
Here's the GCC manual page: https://gcc.gnu.org/onlinedocs/gcc/Typeof.html, note that you can go "Up" to the main Extensions section where you'll find the sub-page for typeof.
Also, the OP does the usual "everything is a function in C" error, which is weird in a text about macros in programming languages. The typeof extension, like sizeof, is not a function, it's a unary prefix operator. But nobody seems to care about that for (the standard) sizeof operator, so I guess I shouldn't be surprised.
Also, of course the local temporary variable should be marked as const.
proc swap {_a _b} {
upvar 1 $_a a
upvar 1 $_b b
foreach a $b b $a {}
}
Foreach is a builtin, but is trivially defined as: proc each {ls script} {
for {set i 0} {$i<[llength $ls]} {incr i} {
uplevel 1 $script
}
}
Perhaps more interesting: proc until {cond script} {
tailcall while "!($cond)" $script
}