Created
April 5, 2022 22:47
-
-
Save wilbowma/b177234545e2532d596828922a1dc8f8 to your computer and use it in GitHub Desktop.
Macros lecture live code
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #lang racket | |
| ;; ------------------------------------------------------------------------ | |
| ;; Template helpers | |
| ;; ------------------------------------------------------------------------ | |
| (define-syntax (.... stx) | |
| (syntax-case stx () | |
| [_ | |
| #`(error "Incomplete template at line" #,(syntax-line stx))])) | |
| (begin-for-syntax | |
| (require (for-syntax racket)) | |
| (define-syntax (.... stx) | |
| (syntax-case stx () | |
| [_ | |
| #`#'(error "Incomplete template at line" #,(syntax-line stx))]))) | |
| ;; ------------------------------------------------------------------------ | |
| ;; Syntactic sugar | |
| ;; ------------------------------------------------------------------------ | |
| #| | |
| What is syntactic sugar? | |
| |# | |
| ;; Proposed definitions: | |
| ;; 1. Nice ways to do something you can already. | |
| ;; 2. A transformation from one syntactic representation to another. | |
| ;; My definition: A mere extension of a language, expressed in terms of existing abstractions. | |
| ;; Bool-lang: | |
| ;; e ::= #t | #f | (if e e e) (let ([x e]) e) | |
| ;; Extension: | |
| ;; (or) -> #t | |
| ;; (or e) -> e | |
| ;; (or e es ...) -> (if e e (or es ...)) | |
| ;; (or e es ...) -> (let ([x e]) (if x x (or es ...))) | |
| ;; EXERCISE 1: | |
| ;; Design and implement a compiler that compiles Bool-lang extended with `or` to | |
| ;; Bool-lang | |
| (require racket/trace) | |
| (trace-define (expand-bool-lang e) | |
| (match e | |
| [(? symbol?) | |
| e] | |
| [(? boolean?) | |
| e] | |
| [`(if ,es ...) | |
| (cons 'if (map expand-bool-lang es))] | |
| [`(let ([,x ,e1]) ,e2) | |
| `(let ([,x ,(expand-bool-lang e1)]) | |
| ,(expand-bool-lang e2))] | |
| [`(or ,es ...) | |
| (match e | |
| [`(or) #t] | |
| [`(or ,e) e] | |
| [`(or ,e ,es ...) | |
| (define x 'tmp) | |
| (expand-bool-lang | |
| `(let ([,x ,e]) | |
| (if ,x | |
| ,x | |
| (or ,@es))))])] | |
| #;[`(and ,es ...) | |
| ....])) | |
| ;; ------------------------------------------------------------------------ | |
| ;; Macros | |
| ;; ------------------------------------------------------------------------ | |
| #| | |
| What is a macro?? | |
| |# | |
| ;; Proposed definitions: | |
| ;; 1. User-definable syntactic sugar. | |
| ;; A macro is ... | |
| ;; A procedure that transforms "code" | |
| ;; A procedure that outputs ... the language. | |
| ;; A procedure that transforms syntax. | |
| ;; i.e. it takes in syntax and outputs syntax. | |
| ;; EXERCISE 2: | |
| ;; Design and implement a macro that compiles any `or` *expression* to a Bool-lang | |
| ;; *expression* | |
| ;; sexpr ?? -> ?? bool-lang | |
| ;; 1. What is the signature for this?: | |
| ; `(or ,es ...) -> | |
| ;; 2. How does we replace what was a recursive call to expand-bool-lang? | |
| (define (or-macro e) | |
| (match e | |
| [`(or) #t] | |
| [`(or ,e) e] | |
| [`(or ,e ,es ...) | |
| (define x 'tmp) | |
| #;(or-macro | |
| `(let ([,x ,e]) | |
| (if ,x | |
| ,x | |
| (or ,@es)))) | |
| `(let ([,x ,e]) | |
| (if ,x | |
| ,x | |
| (or ,@es)))])) | |
| (require (for-syntax racket racket/trace)) | |
| #| | |
| (dict-set! 'or '(lambda (stx) ....)) | |
| |# | |
| (define-syntax or | |
| (lambda (stx) | |
| (define (or-macro e) | |
| (match e | |
| [`(or) #t] | |
| [`(or ,e) e] | |
| [`(or ,e ,es ...) | |
| (define x 'tmp) | |
| #;(or-macro | |
| `(let ([,x ,e]) | |
| (if ,x | |
| ,x | |
| (or ,@es)))) | |
| `(let ([,x ,e]) | |
| (if ,x | |
| ,x | |
| (or ,@es)))])) | |
| (datum->syntax stx (or-macro (syntax->datum stx))))) | |
| (define-syntax and | |
| (lambda (stx) | |
| (define (and-macro e) | |
| (match e | |
| [`(and ,e1 ,e2) | |
| `(if ,e1 | |
| ,e2 | |
| #f)])) | |
| (datum->syntax stx (and-macro (syntax->datum stx))))) | |
| (module+ test | |
| (require rackunit) | |
| (check-equal? (or 5) 5) | |
| (check-equal? (or) #t) | |
| (check-equal? (or #f 6) 6) | |
| (check-equal? (and (or #f #t) #t))) | |
| ;; ------------------------------------------------------ | |
| ;; Exogenous macro expansion | |
| ;; ------------------------------------------------------ | |
| ;; Let's create a design for Bool-lang that enables separating the macro | |
| ;; definition from the macro expansion. | |
| ;; EXERCISE 3: | |
| ;; Design and implement a macro expander for Bool-lang. | |
| ;; | |
| ;; Macros should be defined in a dictionary mapping their name or symbol to some | |
| ;; definition that can be interpreted by the expander. | |
| (define (expand-bool-lang-ex stx [macros-env '()]) | |
| (define (expand stx) | |
| (match stx | |
| [`(,macro ,args ...) | |
| #:when (dict-has-key? macros-env macro) | |
| (expand ((dict-ref macros-env macro) stx))] | |
| [`(,stuff ...) | |
| (map expand stuff)] | |
| [_ stx])) | |
| (expand stx)) | |
| (expand-bool-lang-ex | |
| '(or) | |
| `((or . ,or-macro))) | |
| ;; ------------------------------------------------------ | |
| ;; Endogenous macro expansion | |
| ;; ------------------------------------------------------ | |
| ;; Let's create a design for Bool-lang that enables macro | |
| ;; definition inside the object language. | |
| ;; EXERCISE 4: | |
| ;; Design and implement an extension to Bool-lang that enables macros to be | |
| ;; defined inside the Bool-lang itself, and then used by the macro expander | |
| ;; after their definition. | |
| ;; | |
| ;; In principle, you should be able to reuse expand-bool-lang-ex, although you | |
| ;; may choose not to. | |
| ;; Bool-lang: | |
| ;; e ::= #t | #f | (if e e e) (let ([x e]) e) | |
| ;; (let-syntax ([x (lambda (stx) ...)]) e) | |
| (define (expand-bool-lang-en stx) | |
| (define (expand stx macros-env) | |
| (match stx | |
| [`(let-syntax ([,x (lambda (,stx) ,body)]) | |
| ,e) | |
| (expand e (dict-set macros-env x | |
| (eval `(lambda (,stx) ,body) | |
| (module->namespace 'racket))))] | |
| [`(,macro ,args ...) | |
| #:when (dict-has-key? macros-env macro) | |
| (expand ((dict-ref macros-env macro) stx) macros-env)] | |
| [`(,stuff ...) | |
| (map (curryr expand macros-env) stuff)] | |
| [_ stx])) | |
| (expand stx '())) | |
| (expand-bool-lang-en | |
| '(let-syntax ([or (lambda (stx) (match stx [`(or) #t]))]) | |
| (or))) | |
| ;; Doesn't work; this expand doesn't allow shadowing | |
| #;(expand-bool-lang-en | |
| '(let-syntax ([or (lambda (stx) (match stx [`(or) #t]))]) | |
| (let ([or 5]) or))) | |
| (let-syntax ([or (lambda (stx) | |
| (syntax-case stx () | |
| [(_ e) | |
| #'e]))]) | |
| (let ([or 6]) | |
| or)) | |
| ;; macro generating macros are possible with endogenous macro expanders | |
| #;(expand-bool-lang-en | |
| '(let-syntax ([or (lambda (stx) | |
| (match stx [`(or) | |
| '(let-syntax ([and ()]) | |
| ... | |
| )]))]) | |
| (or))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment