Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Next revision
Previous revision
tutorials:advanced:cram-macros [2022/04/04 10:04] – created vanessatutorials:advanced:cram-macros [2022/04/10 23:34] (current) luca
Line 1: Line 1:
-===== Defining new macros =====+====== Defining new macros======
  
-Disclamer+Disclaimer
 A general rule of thumb is to try and not define new macros if they are not needed, or if they do not serve a specific purpose, since, while they do make the code more readable in some cases, they also make it way harder to debug the code. A general rule of thumb is to try and not define new macros if they are not needed, or if they do not serve a specific purpose, since, while they do make the code more readable in some cases, they also make it way harder to debug the code.
  
-==== Syntax ==== +===== Syntax ===== 
  
 <code lisp> <code lisp>
Line 13: Line 13:
                                                        
 Macros are used to extend the syntax of standard LISP by generating/transforming code and adding new notations. Macros are used to extend the syntax of standard LISP by generating/transforming code and adding new notations.
-One important thing to note is that the computation of the body is already done at compile time and that it is possible to even quote LISP expressions, which enables us to generate code during compile-time. +One important thing to note is that the computation of the body is already done at compile time and that it is possible to even quote LISP expressions, which enables us to generate code through macro-expansion, which happens during compile-time. 
 For example: For example:
  
Line 30: Line 30:
 Now (forty-two) is equivalent to (* 2 21), which is evaluated to 42 during runtime. Now (forty-two) is equivalent to (* 2 21), which is evaluated to 42 during runtime.
  
-=== Backquote/Comma ===+===== Parameter List ===== 
 +Similarly to functions, you can use &optional, &key, and &rest in parameter lists for macros, but additionally you can also use &body instead of &rest. This not only helps human readers, but also some development environments to interpret how the macro is intended to be used.  
 + 
 +===== Backquote/Comma =====
 To make it easier to read/write complex expressions, we use backquotes (`) and commas (,) as follows: To make it easier to read/write complex expressions, we use backquotes (`) and commas (,) as follows:
  
Line 50: Line 53:
 Note that in this example, we want the function calculate-time to be quoted in our code, since we told the macro to evaluate ccase during compile-time but want to evaluate calculate-time during runtime. Note that in this example, we want the function calculate-time to be quoted in our code, since we told the macro to evaluate ccase during compile-time but want to evaluate calculate-time during runtime.
  
 +==== Splicing ====
 +If we need to remove the outermost parenthesis, for example, to put the code contained into a (progn ...) block, then we can use ,@ to splice the list. You can find an example of how to use this in line 4 of the measure-time macro.
 +
 +===== Example of an CRAM-macro =====
 +<code lisp>
 +CL-USER> (defmacro with-simulated-robot (&body body)
 +          `(let ((results
 +                   (proj:with-projection-environment urdf-bullet-projection-environment
 +                     (cpl-impl::named-top-level (:name :top-level)
 +                       ,@body))))
 +             (car (cram-projection::projection-environment-result-result results))))
 +</code>
 +
 +When analyzing this macro the first thing we can notice is that we only have (&body body) as our parameter list. This means, that this macro will be used as a prefix to code that we want to run on our robot. For example:
 +
 +<code lisp>
 +CL-USER> (with-simulated-robot
 +            code that does awesome stuff)
 +</code>
 +
 +Here, "body" would now contain "(code that does awesome stuff)"
 +So now let's go through what the macro actually does. The first thing that is done, is to signal to LISP that a backquoted expression is about to start. Here, a variable "results" is created, which contains two nested, quoted functions/macros, as well as our spliced, evaluated body. The code contained by our variable "result" will look roughly like this:
 +
 +<code lisp>
 +'(proj:with-projection-environment ... 
 +   '(cpl-impl::named-top-level ...
 +      evaluation of our code that does awesome stuff))
 +</code>
 +
 +Those two functions/macros will only be evaluated at runtime, as we can see from the quotes (').
 +After defining result, let's see roughly how the body of our macro will expand. Since non of the expressions have a comma (,) in front of them, every function/macro will be treated as if they were quoted ('):
 +
 +<code lisp>
 +'(car '(cram-projection::projection-environment-result-result ...))
 +</code>
  
 +To conclude: without any prior knowledge of what the other functions/macros are doing we can tell, that this macro will try to evaluate the body as far as it can during macro expansion. The results will be passed onto other functions/macros, which are all evaluated during runtime, which makes sense, given that the macro name is "with-simulated-robot", which gives the impression that the code will be run while a robot is being simulated, which can obviously only happen during runtime, and not during macro expansion.