Converting a Scheme expression to a string
Asked Answered
K

6

6

Given an expression '(lambda (x) x) How can I translate this into a string. I thought symbol->string will do the job but no it cant not a symbol.

e.g for a macro to-string: (to-string (lambda(x) x)) this should return >> "(lambda (x) x)"

Any ideas folks Thanks

Kelseykelsi answered 2/11, 2009 at 0:34 Comment(2)
Just a clarification I am dealing with quoted list. I would walk over the list and do string-concats but then I dont know how to deal with variadics e,g '(lambda (x . y) (display x)(display y))Kelseykelsi
Which variant of scheme are you using? Is it R6RS-compatible, or only R5RS-compatible?Conchiolin
L
8

Standard Scheme (at least in the R5RS sense) has no way of doing this, so if you want portable code, you need to walk the structure yourself. Tedious, but not too complicated (even for dotted lists). But if you just want some working version, then you should look in your implementation's manual and search for the way to do this. The answer will almost always be simple, for example, in PLT Scheme you'd use (format "~s" '(lambda ...))

Lip answered 2/11, 2009 at 1:30 Comment(2)
That answer is correct, though with implementations supporting SRFI 6, you can just make an output string port, and write to it.Hildegard
The point is that doing so directly can in some cases be more efficient. If you're going the srfi route, then srfi-28 formalizes format. (And yes, the reference implementation uses srfi-6, but implementations will often use whatever builtins they have -- the mzscheme implementation of srfi-26 just uses its own format.)Lip
H
5

The expression '(lambda (x) x) is a quoted list.

The expression (lambda (x) x) is some kind of compiled, opaque, executable, internal object of the runtime.

symbol->string simply converts a symbol to a string, which is a sequence of characters.

If you're working with a list, you can simply walk the list and print the individual components out. In fact (write '(lambda (x) x)) will simply print the list out.

Many schemes have something akin to (with-output-to-string ... ) that returns a string of all the output written to the standard port.

However, if you do (write (lambda (x) x)), you'll get who knows what. You'll get whatever the implementation provides when dumping an executable function type. Some may print a "disassembly" showing the source code. Others may simply print #function or something equally useless.

In short, if you just want to print out a list, there are all sorts of mechanisms for that.

If you want to print out the source code of a compiled function, that's a completely different problem, very implementation dependent, and may well be impossible.

Heather answered 2/11, 2009 at 0:50 Comment(0)
C
2

If you are using an R6RS-compatible scheme, you can use the write function combined with call-with-string-output-port.

#!r6rs
(import (rnrs base)
        (rnrs io ports)   ;for call-with-string-output-port
        (rnrs io simple)) ;for write

;; Produces a string representation of the S-Expression e
(define (expr->string e)
  (call-with-string-output-port
   (lambda (out) (write e out))))

Some examples:

> (expr->string '(lambda (x) (+ x 1)))
"(lambda (x) (+ x 1))"
> (expr->string '(lambda (x) (string-append x " and cheese!")))
"(lambda (x) (string-append x \" and cheese!\"))"

By the way, it's usually possible to go in the other direction as well, using read combined with open-string-input-port.

;; Produces the S-Expression represented by the string.
;; In most cases this will be a left-inverse of expr->string,
;; so that (string->expr (expr->string e)) = e.
;; If the string has multiple S-Expressions represented
;; in it, this only returns the first one.
(define (string->expr s)
  (read (open-string-input-port s)))

Some examples:

> (string->expr "(lambda (x) (+ x 1))")
(lambda (x) (+ x 1))
> (equal? (string->expr "(lambda (x) (+ x 1))")
          '(lambda (x) (+ x 1)))
#t
> (equal? (string->expr (expr->string '(lambda (x) (+ x 1))))
          '(lambda (x) (+ x 1)))
#t
Conchiolin answered 17/9, 2018 at 11:58 Comment(0)
C
1

You should walk through the conses. When a new cons starts write "(", when it ends write ")" and use symbol->string for symbols inside the conses.

You can extend this with type dispatching. May be pretty print exists in scheme too?

Chronic answered 2/11, 2009 at 0:39 Comment(2)
Thanks Trickster for the quick response. I thought of this solution too but then I dont know how to deal with cases of variadic functions e.g '(lambda (x . y) (+ x y)) how to actually know the . between x and y and output "(lambda (x . y) (+ x y)) "Kelseykelsi
well, (x y) is (cons x (cons y nil)) and (x . y) is (cons x y)Chronic
E
1

Use pretty-format:

(pretty-format v [columns]) → string?
Endlong answered 2/11, 2009 at 0:42 Comment(1)
unfortunately the Scheme implementation I am using doesnot implement pretty-format :( Any other alternative?Kelseykelsi
M
0

This is a simple code that, I think, does what you want.
It uses no implementation specific stuff and is quite straight forward.

Note, however, that it doesn't correctly handle special characters in strings (e.g. "a\"b\"c" will turn to "a"b"c").

(define join
    (lambda (l delim to-str)
      (fold-left
        (lambda (str elem)
          (string-append str delim (to-str elem)))
        (to-str (car l))
        (cdr l))))
(define sexpr->string 
      (lambda (sexpr)
        (cond 
          ((number? sexpr) (number->string sexpr))
          ((symbol? sexpr) (symbol->string sexpr))
          ((boolean? sexpr) (if sexpr "#t" "#f"))
          ((string? sexpr) (string-append "\"" sexpr "\""))
          ((char? sexpr) (string-append "#\\" (string sexpr)))
          ((vector? sexpr) 
           (let ((s-vec (join (vector->list sexpr) " " sexpr->string)))
             (string-append "#(" s-vec ")")))
          ((null? sexpr) "()")
          ((list? sexpr) (string-append "(" (join sexpr " " sexpr->string) ")"))

          ((pair? sexpr) 
           (let ((s-car (sexpr->string (car sexpr)))
                 (s-cdr (sexpr->string (cdr sexpr))))
             (string-append "(" s-car " . " s-cdr ")"))))))
Macaroon answered 17/9, 2018 at 11:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.