changelog shortlog graph tags branches changeset files revisions annotate raw help

Mercurial > core / lisp/lib/cli/shell.lisp

changeset 698: 96958d3eb5b0
parent: 5540a3e32ba1
author: Richard Westhaver <ellis@rwest.io>
date: Fri, 04 Oct 2024 22:04:59 -0400
permissions: -rw-r--r--
description: fixes
1 ;;; lib/cli/shell.lisp --- shell utils
2 
3 ;; utils for working with shells in different environments
4 
5 ;;; Commentary:
6 
7 ;;; #$ Read Macro
8 
9 ;; A read macro is accessible in the named readtable :SHELL. It has
10 ;; three modes of operation: read, compile, and eval. In read mode,
11 ;; input is parsed and embedded lisp forms are expanded. The string is
12 ;; returned as is. In eval mode, embedded lisp forms are expanded and
13 ;; the resulting string is wrapped in a call to
14 ;; SB-EXT:RUN-PROGRAM. Finally, in eval mode the compiled function is
15 ;; called with default arguments and the result of that call is
16 ;; returned.
17 
18 ;;; Code:
19 (in-package :cli/shell)
20 (in-readtable :std)
21 
22 (defparameter *shell* "/bin/bash")
23 (defparameter *shell-directory* nil)
24 (defparameter *shell-input* nil)
25 
26 (deftype %shell-state () '(member :sh :dolla :pound))
27 
28 (defun plain-shell-reader (stream)
29  (let (out (state :sh))
30  (declare (type %shell-state state))
31  (loop for c = (read-char stream)
32  do (cond
33  ((eq state :sh)
34  (case c
35  (#\$ (setq state :dolla))
36  (#\# (setq state :pound))
37  (t (push c out))))
38  ((eq state :pound)
39  (if (char= c #\,)
40  ;; slow
41  (push (coerce (format nil "~A" (eval (read stream nil nil))) 'list) out)
42  (progn
43  (push #\# out)
44  (push c out)))
45  (setq state :sh))
46  ((eq state :dolla)
47  (if (char= c #\#)
48  (return)
49  (progn
50  (setq state :sh)
51  (push #\$ out)
52  (push c out))))))
53  (concatenate 'string
54  (flatten (nreverse out)))))
55 
56 (defmacro define-process-output-handler (type &body body)
57  "Define a new function which handles the result of a SB-EXT:PROCESS in
58 the context of the $#-reader macro."
59  (declare (ignore type body)))
60 
61 (defun |#$-reader| (stream sub-char numarg)
62  "Switch on the shell reader, parsing STREAM and returning a
63 shell program or executing it. In other words, this is an
64 implementation of the lazy version of SHCL's #$-reader.
65 
66 Similar to shcl, we add some reader extensions to enable embedding
67 lisp forms and other goodies.
68 
69 #0$ x=#,(* 2 2)
70 echo $x
71 $#
72 ;; => 4"
73  (declare (ignore sub-char) ((or (integer 0 9) null) numarg))
74  (let ((str (plain-shell-reader stream)))
75  (if numarg
76  (progn
77  (cond
78  ((= numarg 0)
79  (string-right-trim '(#\Newline)
80  (with-output-to-string (s)
81  (sb-ext:run-program *shell*
82  (list "-c" (format nil "~a" str))
83  :directory (or *shell-directory* *default-pathname-defaults*)
84  :output s
85  :input *shell-input*))))
86  (t (nyi!))))
87  (let ((args (list "-c" (format nil "~a" str)))
88  (directory (or *shell-directory* *default-pathname-defaults*)))
89  (lambda (&key (output *standard-output*) (wait t))
90  (case output
91  (:string (string-right-trim
92  '(#\Newline)
93  (with-output-to-string (s)
94  (sb-ext:run-program *shell* args
95  :directory directory
96  :output s
97  :input *shell-input*
98  :wait wait))))
99  (:integer (parse-integer
100  (string-right-trim
101  '(#\Newline)
102  (with-output-to-string (s)
103  (sb-ext:run-program *shell* args
104  :directory directory
105  :output s
106  :input *shell-input*
107  :wait wait)))))
108  (t (sb-ext:run-program *shell*
109  args
110  :directory directory
111  :output output
112  :input *shell-input*
113  :wait wait))))))))
114 
115 (defreadtable :shell
116  "The shell readtable"
117  (:merge :std)
118  (:dispatch-macro-char #\# #\$ #'|#$-reader|))
119