guile-user
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

syntax-case identifier name predicate in guard expression


From: Zelphir Kaltstahl
Subject: syntax-case identifier name predicate in guard expression
Date: Tue, 23 Jul 2019 01:01:17 +0200
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Thunderbird/60.7.2

Hi Guile Users!

In previous e-mails I inquired about macros defining procedures.

Afterwards I noticed, that I need my macro to check whether some regular
expression is inside the name of an identifier.

For example I have an two identifiers (not strings and no values bound
to them yet):

--------8<--------8<--------8<--------
/containers/json
/containers/<some string in here>/json
-------->8-------->8-------->8--------

I seem unable to find a way using syntax-rules to distinguish between
these two, when they are only given as identifiers. So I looked into
syntax-case, which has guards and thought, that I might be able to use
guard clauses to check for substrings in the name of the identifier
there. However, so far my attempts have failed to get it right and I am
having difficulties finding any example for such a thing. I believe in
principle it should be possible, however. It is just that I do not know how.

Here is my current code, simplified:

--------8<--------8<--------8<--------
(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-match "<[^>]+>" (identifier-name->string (syntax route)))
       (syntax (quote aaa))])))
-------->8-------->8-------->8--------

When calling that I get an error:

--------8<--------8<--------8<--------
(define-api-route /containers/<id>/json GET
application/x-www-form-urlencoded)

While compiling expression:
In procedure symbol->string: Wrong type argument in position 1
(expecting symbol): #<syntax /containers/<id>/json>
-------->8-------->8-------->8--------

I have to wrap `route` into (syntax …) though, because otherwise it
complains about a reference to a pattern variable outside of a syntax
form. I do not really understand this, but in all examples I have seen
for guard clauses, pattern variables are wrapped in a call to syntax.

However, identifier-name->string does not work with syntax objects. I am
not sure how I can get the name of the identifier inside the guard
clause as string, so that I can check for occurences of anything in
angle braces <>.

I've also tried multiple other approaches over the last 2 days, but
always there was some error that made it seem like it was the wrong
approach and fixing the errors led to other errors. For example the
following also does not work (whole transcript):

--------8<--------8<--------8<--------
GNU Guile 2.2.4
Copyright (C) 1995-2017 Free Software Foundation, Inc.

Guile comes with ABSOLUTELY NO WARRANTY; for details type `,show w'.
This program is free software, and you are welcome to redistribute it
under certain conditions; type `,show c' for details.

Enter `,help' for help.
scheme@(guile-user)> (use-modules (web uri)
             (web client)
             (json)
             (ice-9 iconv)
             (ice-9 regex))
scheme@(guile-user)> (define-syntax identifier-name->string
  (lambda (stx)
    (syntax-case stx ()
      ((_ id)
       (identifier? #'id)
       (datum->syntax #'id (symbol->string (syntax->datum #'id)))))))
scheme@(guile-user)> (define-syntax define-api-route-with-template-variables
  (syntax-rules (GET HEAD POST PUT DELETE CONNECT OPTIONS TRACE PATH)
    [(_ route GET my-content-type)
     (syntax
      (define* (route url-temp-vars docker-socket #:key (data #f))
        (let* ([route-as-string (identifier-name->string route)]
               [match-count
                (fold-matches "<[^>]+>" route-as-string 0 (λ (curr-match
count) (+ count 1)))])
          (cond
           [(= match-count (length url-template-vars))
            ;; build the final request url
            ;; loop over available url template variables
            (let ([request-url
                   (let loop ([constructed-route-string route-as-string]
                              [remaining-url-template-vars url-temp-vars])
                     (cond
                      ;; return the whole constructed request url
                      [(null? remaining-url-template-vars)
constructed-route-string]
                      [else
                       ;; replace another url template var and recur
                       (loop (regexp-substitute #f
                                                (string-match "<[^>]+>"
constructed-route-string)
                                                'pre (car
remaining-url-template-vars) 'post)
                             (cdr remaining-url-template-vars))]))])
              ;; do the call with the built request url
              (send-request-to-docker-socket request-url
                                             docker-socket
                                             my-content-type
                                             #:data data))]
           [else
            (error "wrong number of URL template variables"
                   route
                   route-as-string
                   url-template-vars)]))))]))
scheme@(guile-user)> (define-syntax define-api-route
  (lambda (stx)
  ;; https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods
  ;; All HTTP methods are literals.
    (syntax-case stx (GET HEAD POST PUT DELETE CONNECT OPTIONS TRACE PATH)

      [(_ route GET my-content-type)
       (let ([route-as-string (identifier-name->string route)])
         (string-match "<[^>]+>" route-as-string))
       (define-api-route-with-template-variables route GET my-content-type)]

      [(_ route GET my-content-type)
       ;; optional guard clause, already implied by the position of this
case of syntax
       (not (string-match "<[^>]+>" (identifier-name->string route)))
       (syntax
        (define* (route docker-socket #:key (data #f))
          (call-with-values
              (lambda ()
                (http-get (identifier-name->string route)
                          #:port docker-socket
                          #:version '(1 . 1)
                          #:keep-alive? #f
                          #:headers `((host . ("localhost" . #f))
                                      (content-type . (my-content-type
(charset . "utf-8"))))
                          #:body (scm->json-string data)
                          #:decode-body? #t
                          #:streaming? #f))
            (lambda (response response-text)
              (let ([resp-text-as-string (bytevector->string
response-text "utf-8")])
                (cons response resp-text-as-string))))))])))
scheme@(guile-user)> (define-api-route /containers/json GET
application/x-www-form-urlencoded)
scheme@(guile-user)> (define-api-route /containers/<id>/json GET
application/x-www-form-urlencoded)
scheme@(guile-user)> /containers/<id>/json
$2 = #<procedure /containers/<id>/json (docker-socket #:key data)>
scheme@(guile-user)>
-------->8-------->8-------->8--------

As you can see, the /containers/<id>/json does not take template
variable after it is defined, which means, that the syntax-case macro
always goes into the other case and the check for <id> does not work.
Apparently the regex is in this code checked for literally the string
"route", which is not what I want, as route is only supposed to stand
for whatever the user gives as identifier when calling the macro and I
want to check inside that.

My question now is:

How can I check for the regular expression inside a name of an
identifier inside a guard clause?

If that is impossible, what other ways are there to distinguish between
two macro expansions, where one has some <> part in the identifier and
the other does not and get 2 different expansions? How is this usually done?

I know I already took up some time of you before with my questions. I
just cannot find examples for such things on my searches and do not have
a lot experience writing macros. So thank you, if you can help me out
again with this problem.

I will also include a link to my repository, which should be publicly
available, where a few versions of my attempts are in the commits:
https://gitlab.com/ZelphirKaltstahl/guile-scheme-macros/tree/dev/procedure-defining

Regards,

Zelphir




reply via email to

[Prev in Thread] Current Thread [Next in Thread]