Rainb
Rainb

Reputation: 2465

How to Customize REPL Output Format in Guile Scheme?

The existing question How to have objects print nicely in Guile scheme repl focuses on customizing the printing of specific user-defined objects (e.g., GOOPS classes). In contrast, I want to globally override the REPL's printer to format all objects in a specific way, regardless of their type or origin.

I’m trying to customize the output format of the Guile REPL so that it displays additional information about the evaluated expressions. For example, when I evaluate 1, I want the REPL to display:

1 (number)

Similarly, for a list like '(guix build gnu-build-system), I’d like the output to be formatted as:

#<list>
  Item: guix (symbol)
  Item: build (symbol)
  Item: gnu-build-system (symbol)

I’ve written a function called repl-inspect that formats objects in the desired way. Here’s a simplified version:

(use-modules (ice-9 pretty-print)) ; For pretty-printing
(use-modules (srfi srfi-1))        ; For list utilities
(use-modules (system repl repl))   ; For repl-print

;; ANSI color codes
(define *color-red* "\x1b[31m")
(define *color-green* "\x1b[32m")
(define *color-yellow* "\x1b[33m")
(define *color-blue* "\x1b[34m")
(define *color-magenta* "\x1b[35m")
(define *color-cyan* "\x1b[36m")
(define *color-reset* "\x1b[0m")

;; Custom type-of function
(define (type-of obj)
  (cond
    ((struct? obj) 'struct)
    ((hash-table? obj) 'hash-table)
    ((variable? obj) 'variable)
    ((list? obj) 'list)
    ((pair? obj) 'pair)
    ((string? obj) 'string)
    ((symbol? obj) 'symbol)
    ((number? obj) 'number)
    ((boolean? obj) 'boolean)
    ((procedure? obj) 'procedure)
    ((char? obj) 'char)
    ((null? obj) 'null)
    (else 'unknown)))

;; Helper to convert value to string and trim trailing whitespace
(define (value->string value)
  (string-trim-right
   (call-with-output-string
    (lambda (port)
      (pretty-print value port)))
   #\newline))

;; Your inspect function
(define (repl-inspect obj depth)
  (define (indent)
    (display (make-string (* depth 2) #\space)))

  (define (color-print color text)
    (display color)
    (display text)
    (display *color-reset*))

  (define (print-with-type value)
    (display " (")
    (color-print *color-yellow* (symbol->string (type-of value)))
    (display ")"))

  (cond
    ;; Handle structs
    ((struct? obj)
     (indent)
     (color-print *color-cyan* "#<struct>\n")
     (let ((vtable (struct-vtable obj)))
       (indent)
       (color-print *color-yellow* "Vtable: ")
       (display (value->string vtable))
       (print-with-type vtable)
       (newline)
       (indent)
       (color-print *color-yellow* "Fields:\n")
       (catch #t
         (lambda ()
           (let loop ((index 0))
             (catch #t
               (lambda ()
                 (let ((field-value (struct-ref obj index)))
                   (indent)
                   (color-print *color-green* "  Field ")
                   (display index)
                   (color-print *color-green* ": ")
                   (repl-inspect field-value (+ depth 1)) ; Recursively inspect each field
                   (loop (+ index 1))))
               (lambda (key . args)
                 (indent)
                 (color-print *color-red* "  (End of fields)\n")
                 (indent)
                 (color-print *color-red* "  Error details: ")
                 (pretty-print (cons key args))))))
         (lambda (key . args)
           (indent)
           (color-print *color-red* "  (Cannot access struct fields)\n")
           (indent)
           (color-print *color-red* "  Error details: ")
           (pretty-print (cons key args))))))
    ;; Handle hash tables
    ((hash-table? obj)
     (indent)
     (color-print *color-magenta* "#<hash-table>\n")
     (hash-for-each (lambda (key value)
                      (indent)
                      (color-print *color-green* "  Key: ")
                      (display (value->string key))
                      (print-with-type key)
                      (newline)
                      (indent)
                      (color-print *color-green* "  Value: ")
                      (repl-inspect value (+ depth 1))) ; Recursively inspect the value
                    obj))
    ;; Handle variables
    ((variable? obj)
     (indent)
     (color-print *color-blue* "#<variable>\n")
     (indent)
     (color-print *color-green* "  Value: ")
     (repl-inspect (variable-ref obj) (+ depth 1))) ; Inspect the value stored in the variable
    ;; Handle lists
    ((list? obj)
     (indent)
     (color-print *color-yellow* "#<list>\n")
     (for-each (lambda (item)
                 (indent)
                 (color-print *color-green* "  Item: ")
                 (repl-inspect item (+ depth 1))) ; Recursively inspect each item
               obj))
    ;; Handle pairs (e.g., cons cells)
    ((pair? obj)
     (indent)
     (color-print *color-cyan* "#<pair>\n")
     (indent)
     (color-print *color-green* "  Car: ")
     (repl-inspect (car obj) (+ depth 1))
     (indent)
     (color-print *color-green* "  Cdr: ")
     (repl-inspect (cdr obj) (+ depth 1)))
    ;; Handle other types (base case)
    (else
     (indent)
     (display (value->string obj))
     (print-with-type obj)
     (newline))))
     
;; Example usage
;(define interface (resolve-interface '(guix build gnu-build-system)))
;(repl-inspect interface 0)

How can I configure the Guile REPL to use my repl-inspect function for displaying the results of evaluated expressions? Is there a way to override the default REPL printer or hook into the REPL’s output mechanism?

Upvotes: 0

Views: 45

Answers (1)

Rainb
Rainb

Reputation: 2465

I found out! Thanks to this old git repository. https://github.com/NalaGinrut/nala-repl/blob/master/nala/colorized.scm Their purpose was adding a bit of color, to outputs of course nowadays you'd use guile-colorized, but this gave me enough hints to find out

Assuming repl-inspect is already defined, you'd simply use

(use-modules (system repl common))

(define (repl-inspect-printer repl val)
  (repl-inspect val 0))

(define (activate-repl-inspect-printer)
  (let ((rs (fluid-ref *repl-stack*)))
    (if (null? rs)
        (repl-default-option-set! 'print repl-inspect-printer) 
        (repl-option-set! (car rs) 'print repl-inspect-printer)))) 


(activate-repl-inspect-printer)

In my case repl-inspect is what I wanted to use but you could use anything! I have to thank whoever Nala is!

Eye candy: Pretty print repl

Upvotes: 0

Related Questions