This is a quotation on the topic "compilation and macro expansion", from the book Common Lisp: A gentle introduction to symbolic computation.
(defmacro bad-announce-macro ()
(format t "~%Hi mom!"))
(defun say-hi ()
(bad-announce-macro))
> (compile 'say-hi)
Hi, mom!
SAY-HI
> (say-hi)
NIL
In the above example the macro was expanded as part of the process of
compiling SAY-HI. So the compiler said ‘‘Hi, mom!’’ The result of the
macro was NIL, so that’s what got compiled into the body of SAY-HI. When
we call the compiled SAY-HI function, it says nothing because the macro has
been replaced with its expansion.
In this quotation, the author says that
the macro has been replaced with its expansion
Okay, so shouldn't it atleast show the printed "Hi mom!" ? Because though the macro doesn't return anything, but it still replaced by something (it's expansion). Based on the code, I hypothesized that
when a function is called after it is compiled, all the macros, that are called in it's body, are expanded into the result they return , not into whatever they have in their own bodies.
I'm not sure if this is right. And the reason for doing this is also not clear.
How often a macro is expanded is unspecified.
Interpreted Lisp in LispWorks:
CL-USER 49 > (say-hi)
Hi mom!
Hi mom!
NIL
The macro expansion is done twice at runtime here.
In compiled code we expect that no macro expansion is necessary at runtime. Thus in your example nothing will be printed, since your generated code does nothing:
CL-USER 50 > (compile 'say-hi)
Hi mom!
SAY-HI
NIL
NIL
CL-USER 51 > (say-hi)
NIL
The macro expansion is NIL.
CL-USER 52 > (macroexpand '(bad-announce-macro))
Hi mom!
NIL ; <- the macro expansion
T
Related
I was wondering if somebody could please explain to me what the dash place holder does?
def remove_every_other(arr)
arr.select.with_index { |_,idx| idx.even? }
end
In some sense, this is just another parameter like any other. There is no difference between the parameter named _ and the parameter named idx. They are both parameters like any other parameter.
So, in some sense, there is nothing special about _: it's a name like any other name and could just as well be named foo or bar or baz.
However, that is not quite true: _ is treated special by the Ruby language. More precisely, any local variable name or parameter name starting with _ is treated special by the Ruby language.
In many communities, the identifier _ or identifiers starting with _ are used to signify that something is being deliberately ignored. And this is also the usage in the Ruby community.
In order to support this usage of _ for ignored variables or parameters, the Ruby language treats local variables and parameters starting with _ special in two ways:
Unused local variable warnings
Ruby will generate warnings for unused local variables:
a = 42
b = 42
b
will generate "warning: assigned but unused variable - a".
However, if I rename the variable to _a or _, the warning will be suppressed:
_a = 42
_ = 42
b = 42
b
Duplicate parameter SyntaxError
Using the same name twice in the same parameter list (in a method parameter list, block parameter list, and lambda literal parameter list) is a SyntaxError:
def foo(a, a) end
# duplicated argument name (SyntaxError)
foo {|a, a|}
# duplicated argument name (SyntaxError)
-> (a, a) {}
# duplicated argument name (SyntaxError)
but _a or _ are valid:
def foo(_a, _a, _, _) end
foo {|_a, _a, _, _|}
-> (_a, _a, _, _) {}
Last result in IRb
There is a third usage of _ specifically in IRb which has nothing to do with the above: in IRb, the local variable _ is automatically assigned the value of the last expression that was evaluated in the current IRb session. But this is purely a feature of IRb and has nothing to do with Ruby.
Treatment in linters / style-checkers / static analyzers
The default rulesets in many linters, style-checkers, and static analyzers forbid unused parameters and local variables, on the assumption that this usually indicates a typo, a programming error, or leftover code from a refactoring. Similar to Ruby itself, they usually do allow this for variables and parameters starting with _.
This has nothing to do with the Ruby language itself, though, it is a matter of the Ruby community, more precisely, the developers of those tools following the majority usage within the community.
In this case, underscore will allow you to skip elements you do not need.
In other cases _ equals last output.
$ irb
>> 2*3
=> 6
>> _ + 7
=> 13
>> _
=> 13
If I have the following at the top of a gnu makefile:
$(if _,a:=1)
$(info a=$a)
Compilation fails (make-3.81: *** No rule to make target '=1', needed by 'a'. Stop., or *** missing separator. Stop. if I exclude the colon). I know I can get around this by using an eval, but I'm not understanding why it's required. Shouldn't this expand to a:=1, be parsed, and set a to 1?
I'm not understanding why it's required
Well, such is the design.
Evaluation procedure always performs a (recursive) expansion (except for "the initial evaluation" where subsequent expansion can be effectively stopped by =, i.e. "lazy assignment"), but expansion procedure never does evaluation, unless it's explicitly told so (basically, $(eval ...) is such an order to switch from expansion to evaluation).
Consider this:
x := 0
y := 1
# z is set to "y<space><equal><space><dollar>x"
z = y = $x
# [0] [1] [y = $x]
$(info [$x] [$y] [$(value z)])
# error as "y = $x" will not be re-evaluated
#$z
# effectively sets y to "0"
$(eval $z)
# [0] [0] [y = $x]
$(info [$x] [$y] [$(value z)])
From make's point of view $(if cond,x=y) does not differ much from $z --- it's expanded but not evaluated. No matter where it stands. Just think anything of a form $(...) to be "data", not "code".
In your case the if function evaluates as its second argument (a:=1) but this is not evaluated in turn as a make variable assignment. With your version of make it becomes a rule. This is probably due to the way make syntactically parses the makefiles. It does not consider that this if statement can be a variable assignment because (before expansion) it looks like none of the valid make variable assignments. So the if statement gets expanded but it is too late for make to consider the result as a variable assignment...
Instead you can use:
a := $(if _,1)
or:
ifneq (_,)
a := 1
endif
or:
$(if _,$(eval a := 1))
or:
$(eval $(if _,a := 1))
The two last forms work because by definition the result of the expansion of the argument of the eval function is processed as plain make statements.
I've found what seems to be an incompatibility between using C-style macros and using the new unified list-initialization form introduced in C++11, but it seems incredible that this sort of thing would be absolutely impossible to write, so I assume I'm missing something.
Here's the issue: curly brackets seem to be ignored when the preprocessor looks to find a macros arguments. A call like MACR(Range{2,4}) is misinterpreted as having two arguments, Range{2 and 4. In the following code, it's all good (well, poor style, but it works) until the marked line:
#include <iostream>
using namespace std;
struct Range { int st, fn; };
ostream& operator << (ostream& out, const Range& r)
{ return out << "(" << r.st << "," << r.fn << ")"; }
#define COUT(X) (cout << (X) << endl)
int main()
{
COUT(3);
Range r {3,5};
COUT(r);
COUT(Range{3,5}); //this line won't compile
}
It gives the following error message:
badmacro.cpp:16:18: error: macro "COUT" passed 2 arguments, but takes just 1
COUT(Range{3,5});
^
compilation terminated due to -Wfatal-errors.
Especially when working with older libraries, it's sometimes unavoidable to use macro calls; surely we're not supposed to forgo the new syntax in those cases? Is there an official workaround for this?
If you need to pass an expression to an existing macro, and an expression contains unshielded commas, just enclose the whole expression in parentheses.
COUT((Range{3,5}));
Ugly? Yes, but that's what happens when you are using macros. Don't do that.
If it's not an expression and can't take extra parentheses, then you simply can't use that macro.
If you are writing a macro, which you shouldn't, sometimes you can write a variadic macro (if your compiler supports that):
#define COUT(...) cout << (__VA_ARGS__) << endl;
Preprocessor macros are just fancy text replacements prior to compiling. When calling a macro, the preprocessor does very little parsing of the parameter list. There is some logic to differentiate between commas inside of nested parenthesis versus outside, so it knows which commas belong to the parameter list of the macro itself versus commas for the parameter list of a nested function call. For example:
macro(param1, param2, func(param1, param2) )
The parameters are interpreted as
param1
param2
func(param1, param2)
Rather than
param1
param2
func(param1
param2)
In your case, your comma is not inside of nested parenthesis, so the preprocessor ends up splitting the parameter list Range{3,5} into two parameter values
Range{3
5}
Hence the error because your macro only accepts one parameter. The preprocessor does not have any context information to know that Range{3,5} should be treated as one parameter value. It just sees the comma and splits on it.
So, to solve your problem, try adding an extra pair of parenthesis:
COUT((Range{3,5}));
The preprocessor should then interpret one parameter value:
(Range{3,5})
Which will create the following statement for the compiler to consume:
(cout << ((Range{3,5})) << endl);
What do &form and &env do in this example (taken from core.clj)?
(def
^{:macro true
:added "1.0"}
let (fn* let [&form &env & decl] (cons 'let* decl)))
Do symbols beginning with &... (other than just plain &) have special meaning in any contexts?
Why aren't &form and &env used in the body of the fn* form?
Yes, &form and &env are special variables within a macro. The names begin with '&' to avoid name clashes with normal user-defined symbols.
The value of &form is the form of the original macro call before macro expansion.
The value of &env is a map of lexical bindings. The keys of &env are the lexically bound symbols.
[adapted from stevenminer's comment]
I am using SMIE to parse a language that doesn't always require ; to terminate a statement.
If the end of a line is outside of a brace construct ({},(),[]) and the last non-comment token was not an operator, then the \n acts as a statement terminator. Otherwise, if the end of the line is within a brace construct or the last token was an operator, then the \n acts as a continuation.
For example,
variable := 172 + 92;
variable := 172 + 92
variable :=
172 + 92;
variable :=
172 + 92
variable := (172 +
92)
are all valid statements. But,
variable
:= 172 + 92
is not.
How can I encode this is the BNF grammar for SMIE (or any BNF for starters)? Or, is that not possible?
I understand how I might put this into the lexer and add ; tokens as appropriate, but I would rather put it into the grammar if possible.
No, you can't encode it in the BNF (because SMIE only accepts very weak BNFs which can't express that). Look at how I did it for Octave mode: the tokenizer is made to return ";" when it encounters a \n that is outside of a brace/bracket/paren (which you can check with (zerop (car (syntax-ppss)))).