-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
from syntax-objects/Summer2021#23 cc @shhyou
- Loading branch information
Showing
4 changed files
with
118 additions
and
0 deletions.
There are no files selected for viewing
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
#lang racket/base | ||
(module+ test | ||
(require rackunit syntax/macro-testing | ||
(for-syntax racket/base syntax/parse syntax/transformer syntax-parse-example/fnarg/fnarg)) | ||
|
||
;; macro for defining functions that cannot mutate their parameters and do | ||
;; not accept keyword arguments | ||
(define-syntax (define/immutable-parameter stx) | ||
(syntax-parse stx | ||
[(_ (name:id (~var arg (fnarg #:keyword? #f)) ...) body:expr ...+) | ||
;; The `~@` form _splices_ the keyword at (A), if it exists, into the | ||
;; function header. | ||
;; The `~?` form at (B) chooses the first form if both the arg.fresh-var | ||
;; and arg.default attributes have values. Otherwise, `~?` chooses the | ||
;; second form. | ||
#'(define (name (~@ (~? arg.keyword) ;; (A) | ||
(~? [arg.fresh-var arg.default] ;; (B) | ||
arg.fresh-var)) | ||
...) | ||
;; Disable set! for arg | ||
(define-syntax arg.name | ||
(make-variable-like-transformer #'arg.fresh-var #f)) | ||
... | ||
body ...)])) | ||
|
||
(check-exn #rx"set!: cannot mutate identifier" | ||
(lambda () | ||
(convert-compile-time-error | ||
(let () | ||
(define/immutable-parameter (bad-fn x) | ||
(set! x 'oops) | ||
(void)) | ||
(void))))) | ||
|
||
(check-exn #rx"keyword argument is not allowed" | ||
(lambda () | ||
(convert-compile-time-error | ||
(define/immutable-parameter (has-kw a #:cache cache?) | ||
(void))))) | ||
|
||
(test-case "fib/cache" | ||
(define fib-cache (make-hash)) | ||
(define/immutable-parameter (fib n [cache #f]) | ||
(cond | ||
[(<= n 1) n] | ||
[(and cache (hash-has-key? cache n)) | ||
(hash-ref cache n)] | ||
[else | ||
(define result | ||
(+ (fib (- n 1) cache) | ||
(fib (- n 2) cache))) | ||
(when cache | ||
(hash-set! cache n result)) | ||
result])) | ||
|
||
(check-equal? (fib 8) 21) | ||
(check-equal? (fib 40 fib-cache) 102334155)) | ||
) |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
#lang racket/base | ||
|
||
(provide fnarg) | ||
|
||
(require racket/syntax syntax/parse) | ||
|
||
;; Use _splicing_ to parse a sequence of elements for keyword args. | ||
(define-splicing-syntax-class (fnarg [allow-optional? #t] | ||
#:keyword? [allow-keyword? #t]) | ||
#:attributes (name fresh-var keyword default) | ||
#:commit | ||
(pattern name:id | ||
#:attr keyword #f | ||
#:attr default #f | ||
#:with fresh-var (generate-temporary #'name)) | ||
(pattern [name:id default:expr] | ||
#:fail-unless allow-optional? "optional argument is not allowed" | ||
#:attr keyword #f | ||
#:with fresh-var (generate-temporary #'name)) | ||
;; The ~seq pattern describes a _sequence_ of elements | ||
(pattern (~seq keyword:keyword name:id) | ||
#:fail-unless allow-keyword? "keyword argument is not allowed" | ||
#:attr default #f | ||
#:with fresh-var (generate-temporary #'name)) | ||
(pattern (~seq keyword:keyword [name:id default:expr]) | ||
#:fail-unless allow-optional? "optional argument is not allowed" | ||
#:fail-unless allow-keyword? "keyword argument is not allowed" | ||
#:with fresh-var (generate-temporary #'name))) |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
#lang syntax-parse-example | ||
@require[ | ||
(for-label racket/base syntax/parse syntax-parse-example/fnarg/fnarg)] | ||
|
||
@title{Function Parameter Syntax Class} | ||
@stxbee2021["shhyou" 23] | ||
|
||
@; ============================================================================= | ||
|
||
@defmodule[syntax-parse-example/fnarg/fnarg]{} | ||
|
||
Syntax classes offer a powerful mechanism for abstracting over classes of | ||
conceptually related patterns. Moreover, syntax classes can be parameterized, | ||
with @racket[expr/c] being one prominent example. | ||
|
||
In this example, we define a syntax class that roughly captures the grammar of | ||
formal parameters in function headers. It parses the name of the formal | ||
parameter, the default expressions of optional arguments and the keywords of | ||
keyworded arguments. | ||
|
||
@defidform[fnarg]{ | ||
The @racket[fnarg] syntax class matches the basic argument patterns that | ||
can appear in function headers: mandatory, optional, and keyword arguments. | ||
The class optionally accepts two parameters to toggle whether optional and/or | ||
keyword arguments are allowed. | ||
|
||
Refer to the source file @tt{fnarg-test.rkt} for an example use. | ||
|
||
@racketfile{fnarg.rkt} | ||
|
||
} |
This file contains 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