Quasistring and string interpolation #307

Open
opened 2019-09-19 16:11:46 +00:00 by belmarca · 4 comments
belmarca commented 2019-09-19 16:11:46 +00:00 (Migrated from github.com)

quasistring and string interpolation have been discussed on IRC. The following is an implementation for quasistring based on parsing a string (during macro expansion) for template variables and capturing lexical scope:

;; quasistring.ss

(import :std/format
        :std/net/request)

;; variant: return procedure of n args?
(defsyntax (quasistring stx)
  (syntax-case stx ()
    ((macro s)
     (stx-string? #'s)
     (let (port (open-input-string (stx-e #'s)))
       (let lp ((c (read-char port))
                (sexps [])
                (str []))
         (cond
          ((eq? c #\#) ;; template variable?
           (let ((c+1 (read-char port)))
             (cond
              ((eq? c+1 #\{) ;; yes, read sexp
               (let (ssexp (read port))
                 (if (eq? (read-char port) #\}) ;; closed template variable?
                   (lp (read-char port) (cons ssexp sexps) (cons #\a (cons #\~ str)))
                   (error "Bad quasistring formatting."))))
              (else ;; no, start again
               (lp (read-char port) sexps str)))))
          ((eq? c #!eof) ;; eof so return syntax
           (close-input-port port)
           (with-syntax (((vars ...) (datum->syntax #'macro (reverse sexps)))
                         (str (list->string (reverse str))))
             #'(apply format str [vars ...])))
          (else ;; no, start again
           (lp (read-char port) sexps (cons c str)))))))))

;; > (let ((a 1) (b 2)) (quasistring "a: #{a}~nb: #{b}"))
;; "a: 1\nb: 2"
;; > (let ((a 1) (b 2)) (quasistring "a: #{b}~nb: #{b}"))
;; "a: 2\nb: 2"
;; > (let (name "world") (quasistring "hello, #{name}!"))
;; "hello, world!"
;; > (quasistring "hello, #{name}!")
;; *** ERROR -- Unbound variable: name
;; > (quasistring "2 + 2 is #{(+ 2 2)}")                             
;; "2 + 2 is 4"
;; > (quasistring "Your IP address is #{(let (req (http-get \"ipinfo.io/json\")) (hash-ref (request-json req) 'ip))}")
;; "Your IP address is x.x.x.x"

(see https://gist.github.com/belmarca/e79685b751950920f5da00461d3d8818).

Some issues have been raised by @drewc and I think this warrants a GitHub issue for further discussion.

`quasistring` and string interpolation have been discussed on IRC. The following is an implementation for `quasistring` based on parsing a string (during macro expansion) for template variables and capturing lexical scope: ```scheme ;; quasistring.ss (import :std/format :std/net/request) ;; variant: return procedure of n args? (defsyntax (quasistring stx) (syntax-case stx () ((macro s) (stx-string? #'s) (let (port (open-input-string (stx-e #'s))) (let lp ((c (read-char port)) (sexps []) (str [])) (cond ((eq? c #\#) ;; template variable? (let ((c+1 (read-char port))) (cond ((eq? c+1 #\{) ;; yes, read sexp (let (ssexp (read port)) (if (eq? (read-char port) #\}) ;; closed template variable? (lp (read-char port) (cons ssexp sexps) (cons #\a (cons #\~ str))) (error "Bad quasistring formatting.")))) (else ;; no, start again (lp (read-char port) sexps str))))) ((eq? c #!eof) ;; eof so return syntax (close-input-port port) (with-syntax (((vars ...) (datum->syntax #'macro (reverse sexps))) (str (list->string (reverse str)))) #'(apply format str [vars ...]))) (else ;; no, start again (lp (read-char port) sexps (cons c str))))))))) ;; > (let ((a 1) (b 2)) (quasistring "a: #{a}~nb: #{b}")) ;; "a: 1\nb: 2" ;; > (let ((a 1) (b 2)) (quasistring "a: #{b}~nb: #{b}")) ;; "a: 2\nb: 2" ;; > (let (name "world") (quasistring "hello, #{name}!")) ;; "hello, world!" ;; > (quasistring "hello, #{name}!") ;; *** ERROR -- Unbound variable: name ;; > (quasistring "2 + 2 is #{(+ 2 2)}") ;; "2 + 2 is 4" ;; > (quasistring "Your IP address is #{(let (req (http-get \"ipinfo.io/json\")) (hash-ref (request-json req) 'ip))}") ;; "Your IP address is x.x.x.x" ``` (see https://gist.github.com/belmarca/e79685b751950920f5da00461d3d8818). Some issues have been raised by @drewc and I think this warrants a GitHub issue for further discussion.
vyzo commented 2019-09-19 17:07:36 +00:00 (Migrated from github.com)

So, for quasistring we want it in :std/misc/text; we probably also want to have include-text (or include-quasistring) for template inclusion from a file.

So, for quasistring we want it in `:std/misc/text`; we probably also want to have `include-text` (or `include-quasistring`) for template inclusion from a file.
belmarca commented 2019-09-19 17:17:38 +00:00 (Migrated from github.com)

OK so quasistring captures lexical scope, but we might need to allow to pass it an expression that builds a string. This works

(let (name "world") (quasistring "hello, #{name}!"))

but this doesn't

(let (name "world") (quasistring ((lambda () "hello, #{name}!"))))

.

OK so `quasistring` captures lexical scope, but we might need to allow to pass it an expression that builds a string. This works ```scheme (let (name "world") (quasistring "hello, #{name}!")) ``` but this doesn't ```scheme (let (name "world") (quasistring ((lambda () "hello, #{name}!")))) ``` .
belmarca commented 2019-11-03 01:20:53 +00:00 (Migrated from github.com)

See https://github.com/vyzo/gerbil/pull/373 for more discussion and implementation.

See https://github.com/vyzo/gerbil/pull/373 for more discussion and implementation.
vyzo commented 2023-09-22 12:08:55 +00:00 (Migrated from github.com)

We have :std/misc/template now, so we are mostly there. I don't think we need quasistring, templates give us 90% of what we need.

@belmarca thoughts?

We have `:std/misc/template` now, so we are mostly there. I don't think we need quasistring, templates give us 90% of what we need. @belmarca thoughts?
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
mighty-gerbils/gerbil#307
No description provided.