On 8/3/19 10:48 AM, Zelphir Kaltstahl wrote:
Hi!
I am trying to write a macro, which checks the name of an argument for
presence a substring. This is not the main purpose of the macro, but I
want to do different things depending on the substring being contained
or not contained.
Here is what I've got so far:
~~~~~
;; A macro to get the identifier name as string, shamelessly copied from
StackOverflow and renamed:
(define-syntax identifier-name->string
(lambda (stx)
(syntax-case stx ()
((_ id)
(identifier? #'id)
(datum->syntax #'id (symbol->string (syntax->datum #'id)))))))
;; And the actual macro I want to write:
(define-syntax define-api-route
(lambda (stx)
(syntax-case stx (GET HEAD POST PUT DELETE CONNECT OPTIONS TRACE PATH)
[(_ route GET my-content-type)
(string-contains? (identifier-name->string (syntax route)) "abc")
(println "abc is in the identifier name")])))
~~~~~
With this, Racket will complain, that I am referencing an identifier
before its definition:
~~~~~
> (define-syntax identifier-name->string
(lambda (stx)
(syntax-case stx ()
((_ id)
(identifier? #'id)
(datum->syntax #'id (symbol->string (syntax->datum #'id)))))))
>
(define-syntax define-api-route
(lambda (stx)
(syntax-case stx (GET HEAD POST PUT DELETE CONNECT OPTIONS TRACE
PATH)
[(_ route GET my-content-type)
(string-contains? (identifier-name->string (syntax route)) "abc")
(println "abc is in the identifier name")])))
> (define-api-route abc/abc/abc GET
application/json)
; string-contains?: undefined;
; cannot reference an identifier before its definition
; in module: top-level
; internal name: string-contains?
; [,bt for context]
There are two things going wrong here:
1. The undefined identifier is `string-contains?`. You are using it in
the macro body, so you must require the module that provides it for-syntax:
(require (for-syntax racket/string))
The reason you have to do that for `string-contains?` but not for
`identifier?`, `syntax`, `lambda`, and so on is that the racket language
implicitly does a `(require (for-syntax racket/base))` for you, and all
of those other things are provided by racket/base. (Technically, racket
just provides racket/base for-syntax.)
2. Your `identifier-name->string` macro needs to be a phase-1 function
instead.
The implementation of a macro is an expression at a phase 1 higher than
where the macro itself is defined. The top-level starts at phase 0, so
the right-hand side of the `define-api-route` is a phase-1 expression.
If you want to *use* (as opposed to *produce syntax referring to*)
`identifier-name->string` in the macro body, it must also be defined at
phase 1. Since you want to use it on a phase-1 *identifier value* and
get a phase-1 *string value*, it should be a function.
So replace the definition of `identifier-name->string` with this:
(begin-for-syntax
;; identifier-name->string : Identifier -> String
(define (identifier-name->string id)
(symbol->string (syntax->datum id))))
If you don't want to use `begin-for-syntax`, there are two other ways to
define this helper function and make it available to the macro body. You
can put it in a new module and require that module for-syntax (that is,
at phase 1). Or you can make the definition local to the
`define-api-route` macro by moving it just inside that macro's `(lambda
(stx) ___)`. In both cases, drop the `begin-for-syntax`.
Ryan
~~~~~
If I take away the (syntax ...) in the guard expression however, it will
also not work, as template variables may only occur in syntax:
~~~~~
> (define-syntax
define-api-route
(lambda
(stx)
(syntax-case stx (GET HEAD POST PUT DELETE CONNECT OPTIONS TRACE
PATH) [(_ route GET
my-content-type)
(string-contains? (identifier-name->string route)
"abc")
(println "abc is in the identifier name")])))
; readline-input:19:52: route: pattern variable cannot be used outside of a
; template
; in: route
; [,bt for context]
~~~~~
I have also tried loads of other stuff, but I cannot find a way to:
1. get the identifier name of route, whatever the user has as route (not
a string yet!)
2. check inside the guard expression, whether the identifier name
contains a certain substring
3. based on that substring call other macros to do the actual job of
defining an API route
Can you help me writing this macro?
It would also be great, if the macro was portable, meaning that it is
usable from any Scheme.
--
You received this message because you are subscribed to the Google
Groups "Racket Users" group.
To unsubscribe from this group and stop receiving emails from it, send
an email to racket-users+unsubscr...@googlegroups.com
<mailto:racket-users+unsubscr...@googlegroups.com>.
To view this discussion on the web visit
https://groups.google.com/d/msgid/racket-users/8b706c81-2587-4dc8-aaaa-a900813571f1%40googlegroups.com
<https://groups.google.com/d/msgid/racket-users/8b706c81-2587-4dc8-aaaa-a900813571f1%40googlegroups.com?utm_medium=email&utm_source=footer>.
--
You received this message because you are subscribed to the Google Groups "Racket
Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to racket-users+unsubscr...@googlegroups.com.
To view this discussion on the web visit
https://groups.google.com/d/msgid/racket-users/334dde59-5d2f-aee4-e46a-6f155221f565%40ccs.neu.edu.