repl.scm 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. ;;; GNU Guix --- Functional package management for GNU
  2. ;;; Copyright © 2018, 2019, 2020 Ludovic Courtès <ludo@gnu.org>
  3. ;;;
  4. ;;; This file is part of GNU Guix.
  5. ;;;
  6. ;;; GNU Guix is free software; you can redistribute it and/or modify it
  7. ;;; under the terms of the GNU General Public License as published by
  8. ;;; the Free Software Foundation; either version 3 of the License, or (at
  9. ;;; your option) any later version.
  10. ;;;
  11. ;;; GNU Guix is distributed in the hope that it will be useful, but
  12. ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
  13. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. ;;; GNU General Public License for more details.
  15. ;;;
  16. ;;; You should have received a copy of the GNU General Public License
  17. ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
  18. (define-module (guix repl)
  19. #:use-module (srfi srfi-1)
  20. #:use-module (srfi srfi-26)
  21. #:use-module (ice-9 match)
  22. #:export (send-repl-response
  23. machine-repl))
  24. ;;; Commentary:
  25. ;;;
  26. ;;; This module implements the "machine-readable" REPL provided by
  27. ;;; 'guix repl -t machine'. It's a lightweight module meant to be
  28. ;;; embedded in any Guile process providing REPL functionality.
  29. ;;;
  30. ;;; Code:
  31. (define (self-quoting? x)
  32. "Return #t if X is self-quoting."
  33. (letrec-syntax ((one-of (syntax-rules ()
  34. ((_) #f)
  35. ((_ pred rest ...)
  36. (or (pred x)
  37. (one-of rest ...))))))
  38. (one-of symbol? string? keyword? pair? null? array?
  39. number? boolean? char?)))
  40. (define repl-prompt
  41. ;; Current REPL prompt or #f.
  42. (make-parameter #f))
  43. (define (stack->frames stack)
  44. "Return STACK's frames as a list."
  45. (unfold (cute >= <> (stack-length stack))
  46. (cut stack-ref stack <>)
  47. 1+
  48. 0))
  49. (define* (send-repl-response exp output
  50. #:key (version '(0 0)))
  51. "Write the response corresponding to the evaluation of EXP to PORT, an
  52. output port. VERSION is the client's protocol version we are targeting."
  53. (define (value->sexp value)
  54. (if (self-quoting? value)
  55. `(value ,value)
  56. `(non-self-quoting ,(object-address value)
  57. ,(object->string value))))
  58. (define (frame->sexp frame)
  59. `(,(frame-procedure-name frame)
  60. ,(match (frame-source frame)
  61. ((_ (? string? file) (? integer? line) . (? integer? column))
  62. (list file line column))
  63. (_
  64. '(#f #f #f)))))
  65. (define (handle-exception key . args)
  66. (define reply
  67. (match version
  68. ((0 1 (? positive?) _ ...)
  69. ;; Protocol (0 1 1) and later.
  70. (let ((stack (if (repl-prompt)
  71. (make-stack #t handle-exception (repl-prompt))
  72. (make-stack #t))))
  73. ;; Note: 'make-stack' returns #f if there's no 'handle-exception'
  74. ;; stack frame, which is the case when this file is being
  75. ;; interpreted as with 'primitive-load'.
  76. `(exception (arguments ,key ,@(map value->sexp args))
  77. (stack ,@(map frame->sexp
  78. (if stack
  79. (stack->frames stack)
  80. '()))))))
  81. (_
  82. ;; Protocol (0 0).
  83. `(exception ,key ,@(map value->sexp args)))))
  84. (write reply output)
  85. (newline output)
  86. (force-output output))
  87. (catch #t
  88. (lambda ()
  89. (let ((results (call-with-values
  90. (lambda ()
  91. (primitive-eval exp))
  92. list)))
  93. (write `(values ,@(map value->sexp results))
  94. output)
  95. (newline output)
  96. (force-output output)))
  97. (const #t)
  98. handle-exception))
  99. (define* (machine-repl #:optional
  100. (input (current-input-port))
  101. (output (current-output-port)))
  102. "Run a machine-usable REPL over ports INPUT and OUTPUT.
  103. The protocol of this REPL is meant to be machine-readable and provides proper
  104. support to represent multiple-value returns, exceptions, objects that lack a
  105. read syntax, and so on. As such it is more convenient and robust than parsing
  106. Guile's REPL prompt."
  107. (define tag
  108. (make-prompt-tag "repl-prompt"))
  109. (define (loop exp version)
  110. (match exp
  111. ((? eof-object?) #t)
  112. (exp
  113. (send-repl-response exp output
  114. #:version version)
  115. (loop (read input) version))))
  116. (write `(repl-version 0 1 1) output)
  117. (newline output)
  118. (force-output output)
  119. ;; In protocol version (0 0), clients would not send their supported
  120. ;; protocol version. Thus, the code below checks for two case: (1) a (0 0)
  121. ;; client that directly sends an expression to evaluate, and (2) a more
  122. ;; recent client that sends (() repl-version ...). This form is chosen to
  123. ;; be unambiguously distinguishable from a regular Scheme expression.
  124. (call-with-prompt tag
  125. (lambda ()
  126. (parameterize ((repl-prompt tag))
  127. (match (read input)
  128. ((() 'repl-version version ...)
  129. (loop (read input) version))
  130. (exp
  131. (loop exp '(0 0))))))
  132. (const #f)))