macros
Download
Skip this Video
Download Presentation
Macros

Loading in 2 Seconds...

play fullscreen
1 / 15

Macros - PowerPoint PPT Presentation


  • 173 Views
  • Uploaded on

Macros. “How can you get anything done in [other languages], I think, without macros?” - Paul Graham, 2003. Writing Programs…. … that write programs! Macros work via transformation Macros translate a given LISP expression into another expression Allows for efficient execution.

loader
I am the owner, or an agent authorized to act on behalf of the owner, of the copyrighted work described.
capcha
Download Presentation

PowerPoint Slideshow about ' Macros' - trista


An Image/Link below is provided (as is) to download presentation

Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author.While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server.


- - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - -
Presentation Transcript
macros

Macros

“How can you get anything done in [other languages], I think, without macros?”

- Paul Graham, 2003

writing programs
Writing Programs…
  • … that write programs!
  • Macros work via transformation
    • Macros translate a given LISP expression into another expression
  • Allows for efficient execution
two similar functions
Two Similar Functions

(defun print-file (file)

(with-open-file (str file)

(do ((line (read-line str nil nil)

(read-line str nil nil)))

((null line) nil)

(format t "~A~%" line) )))

(defun find-first-comment (file)

(with-open-file (str file)

(do ((line (read-line str nil nil)

(read-line str nil nil)))

((null line) nil)

(when (eql (char line 0) #\;)

(return line)) )))

desire to generalize
Desire to Generalize
  • If this pattern reoccurs often, we would like to do something simpler like

(defun print-file (file)

(dofile line file

(format t "~A~%" line) ))

(defun find-first-comment (file)

(dofile line file

(when (eql (char line 0) #\;)

(return line)) ))

  • But how would you do this with a function? line is being passed as a variable name…
solution is macros
Solution is Macros
  • What a function does
    • 1. Evaluate each argument
    • 2. Evaluate the body of the function
  • What a macro does
    • 1. Evaluate body of macro
    • 2. Evaluate resulting body of expanded macro
defining macros
Defining Macros
  • Similar to defun

(defmacro macro-name (argument*) S-expr*)

(defmacro dofile (var file &rest body)

(list \'with-open-file

(list \'str file)

(list* \'do

(list (list var

(list \'read-line

\'str \'nil \'nil)

(list \'read-line

\'str \'nil \'nil)))

(list (list \'null var) \'nil)

body)))

  • But this is impossibly ugly
backquote
Backquote
  • The backquote (`), like single quote (\'), can be used to inhibit evaluation

>`(a b c d)

(A B C D)

>`(setf x (* 3 7))

(SETF X (* 3 7))

  • Unlike single quote, can specify partial evaluation by using a comma (,)

>`(setf x ,(* 3 7))

(SETF X 21)

simplified macro with backquote
Simplified Macro with Backquote
  • We can make our macro definition more readable with the backquote

(defmacro dofile (var file &rest body)

`(with-open-file (str ,file)

,(list* \'do `((,var

(read-line str nil nil)

(read-line str nil nil)))

`((null ,var) nil)

body)))

  • Still kind of ugly. Problem is that body can be a list of commands, so we need list*
splice
Splice
  • The splicing operator (,@) allows the outer parentheses of an expression to be removed

>`(+ ,@(cdr \'(2 4 6)))

(+ 4 6)

  • Now we can fix our macro

(defmacro dofile (var file &rest body)

`(with-open-file (str ,file)

(do ((,var (read-line str nil nil)

(read-line str nil nil)))

((null ,var) nil)

,@body) ))

macroexpand
Macroexpand
  • A tool for debugging macros, it expands a macro and suppresses second evaluation, returning the expanded expression

>(defmacro front (some-list)

(list \'car some-list))

FRONT

>(macroexpand \'(front \'(1 2 3)))

(CAR \'(1 2 3))

T

  • If your macro definition contains macros in it, then they will also be expanded, which may result in code you don’t recognize
specializing the input
Specializing the Input
  • Currently we call our macro using

(dofile var file body*)

  • But we can group our arguments like so

(dofile (var file) body*)

  • If we parenthesize them in the macro definition
  • Also, in macros, &body is preferred in place of &rest, for formatting reasons. So now we have:

(defmacro dofile ((var file)&body body)

`(with-open-file (str ,file)

(do ((,var (read-line str nil nil)

(read-line str nil nil)))

((null ,var) nil)

,@body) ))

problems 1
Problems (1)
  • This code works:

(defun copy (source out)

(with-open-file (outstr out :direction :output)

(dofile (line source)

(format outstr "~A~%" line))))

  • But this almost identical code doesn’t

(defun copy (source out)

(with-open-file (str out :direction :output)

(dofile (line source)

(format str "~A~%" line))))

problems 2
Problems (2)
  • If we expand the macro definition, we see why

(defun copy (source out)

(with-open-file (str out :direction :output)

(with-open-file (str source)

(do ((line (read-line str nil nil)

(read-line str nil nil)))

((null line) nil)

(format str "~A~%" line))) ))

  • There are name collisions between the variables in the macro and those in the function definition.
  • The only way to avoid this is by knowing the internals of the macro, which is unacceptable. We need to fix the macro again
gensym
Gensym
  • gensym generates a new symbol name that is guaranteed to be unique
  • We use it to get unique variable names for our macro (changes in red)

(defmacro dofile ((var file) &body body)

(let ((str (gensym)))

`(with-open-file (,str ,file)

(do ((,var (read-line ,str nil nil)

(read-line ,str nil nil)))

((null ,var) nil)

,@body) )))

macroexpand after gensym
Macroexpand After Gensym

>(macroexpand \'(dofile (line "file.txt") nil))

(LET ((#:G1478 (OPEN "file.txt")))

(UNWIND-PROTECT

(PROGN

(BLOCK ()

(LET ((LINE (READ-LINE #:G1478 NIL NIL)))

(TAGBODY

#:G1479

(IF (NULL LINE) (RETURN (PROGN NIL)))

(TAGBODY NIL)

(PSETQ LINE (READ-LINE #:G1478 NIL NIL))

(GO #:G1479)))))

(IF #:G1478 (CLOSE #:G1478))))

ad