forked from GaloisInc/saw-script
-
Notifications
You must be signed in to change notification settings - Fork 0
/
inf-saw.el
235 lines (202 loc) · 9.49 KB
/
inf-saw.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
;;; inf-saw.el --- an inferior-saw mode
;;;
;;; Derived from inf-lisp that ships with Emacs; that file is GPL version 3
;;; so I suppose this file is also GPL.
;;;
;;; This allows a saw-script interpreter to be run as a subprocess
;;; with inputs and outputs in a buffer, typically *inferior-saw*.
;;; Besides the usual comint bindings for command-line history an so
;;; on, bindings are added to saw-script-mode-map that allow items
;;; from buffers in SawScript mode to be sent to the saw process.
;;;
(require 'comint)
(require 'saw-script)
(defgroup inferior-saw nil
"Run saw in an Emacs buffer."
:group 'saw-script
:version "1")
(defcustom inferior-saw-program "saw"
"Program name for invoking saw in Inferior Saw mode."
:type 'string
:group 'inferior-saw)
(defcustom inferior-saw-prompt "^sawscript> *"
"Regexp to recognize prompts in the Inferior Saw mode.
Defaults to \"^sawscript> *\".
This variable is used to initialize `comint-prompt-regexp' in the
inferior saw buffer.
This variable is only used if the variable
`comint-use-prompt-regexp' is non-nil."
:type 'regexp
:group 'inferior-saw)
(defvar inferior-saw-buffer nil "The current inferior-saw process buffer.")
(defvar inferior-saw-mode-map
(let ((map (copy-keymap comint-mode-map)))
;(define-key map "\C-x\C-e" 'saw-eval-last-thing)
(define-key map "\C-c\C-l" 'saw-load-file)
;(define-key map "\C-c\C-a" 'lisp-show-arglist)
;(define-key map "\C-c\C-d" 'lisp-describe-sym)
;(define-key map "\C-c\C-f" 'lisp-show-function-documentation)
;(define-key map "\C-c\C-v" 'lisp-show-variable-documentation)
map))
;;; These commands augment sawscript mode, so you can process SAWscript in
;;; the source files.
(define-key saw-script-mode-map "\M-\C-x" 'saw-eval-defun) ; GNU convention
(define-key saw-script-mode-map "\C-x\C-e" 'saw-eval-last-sexp) ; GNU convention
(define-key saw-script-mode-map "\C-c\C-e" 'saw-eval-defun)
(define-key saw-script-mode-map "\C-c\C-r" 'saw-eval-region)
;; (define-key saw-script-mode-map "\C-c\C-n" 'saw-eval-form-and-next)
(define-key saw-script-mode-map "\C-c\C-z" 'switch-to-saw)
(define-key saw-script-mode-map "\C-c\C-l" 'saw-load-file)
;(define-key saw-script-mode-map "\C-c\C-a" 'lisp-show-arglist)
;(define-key saw-script-mode-map "\C-c\C-d" 'lisp-describe-sym)
;(define-key saw-script-mode-map "\C-c\C-f" 'lisp-show-function-documentation)
;(define-key saw-script-mode-map "\C-c\C-v" 'lisp-show-variable-documentation)
(define-key saw-script-mode-map "\C-\M-a" 'beginning-of-saw-form)
(define-key saw-script-mode-map "\C-\M-e" 'end-of-saw-form)
(defvar inferior-saw-mode-hook '()
"Hook for customizing Inferior Saw mode.")
(define-derived-mode inferior-saw-mode comint-mode "Inferior Saw"
"Major mode for interacting with an inferior saw process.
Runs a saw interpreter as a subprocess of Emacs, with I/O through an
Emacs buffer. Variable `inferior-saw-program' controls which Saw interpreter
is run.
For information on running multiple processes in multiple buffers, see
documentation for variable `inferior-saw-buffer'.
\\{inferior-saw-mode-map}
Customization: Entry to this mode runs the hooks on `comint-mode-hook' and
`inferior-saw-mode-hook' (in that order).
You can send text to the inferior Saw process from other buffers containing
SAWscript source, if they are in SawScript mode.
`switch-to-saw' switches the current buffer to the Saw process buffer.
`saw-eval-defun' sends the current SAWscript form to the Saw process.
`saw-eval-region' sends the current region to the Saw process (but the
saw interpreter only accepts one SAW form at a time, so the region can only
include one form).
Prefixing the eval commands with a \\[universal-argument] causes
switch to the saw process buffer after sending the text.
A \"SAWscript form\" is a block of text starting with an alphabetic character
at the start of a line, up to its closing semicolon, however many lines that may be.
Commands:\\<inferior-saw-mode-map>
\\[comint-send-input] after the end of the process' output sends the text from the
end of process to point.
\\[comint-send-input] before the end of the process' output copies the sexp ending at point
to the end of the process' output, and sends it.
\\[comint-copy-old-input] copies the sexp ending at point to the end of the process' output,
allowing you to edit it before sending it.
If `comint-use-prompt-regexp' is nil (the default), \\[comint-insert-input] on old input
copies the entire old input to the end of the process' output, allowing
you to edit it before sending it. When not used on old input, or if
`comint-use-prompt-regexp' is non-nil, \\[comint-insert-input] behaves according to
its global binding.
\\[backward-delete-char-untabify] converts tabs to spaces as it moves back.
\\[lisp-indent-line] indents for Lisp; with argument, shifts rest
of expression rigidly with the current line.
\\[indent-sexp] does \\[lisp-indent-line] on each line starting within following expression.
Paragraphs are separated only by blank lines.
If you accidentally suspend your process, use \\[comint-continue-subjob]
to continue it."
(setq comint-prompt-regexp inferior-saw-prompt)
(setq mode-line-process '(":%s"))
;; (saw-mode-variables t)
;; (setq comint-get-old-input (function saw-get-old-input))
;; (setq comint-input-filter (function saw-input-filter))
)
(defun saw-eval-region (start end &optional and-go)
"Send the current region to the inferior Saw process.
Prefix argument means switch to the Saw buffer afterwards.
Note that Saw only allows for one form to be entered at a time,
so the region should consist of only one SawScript form."
(interactive "r\nP")
;; we need to put continuation marks '\' at the end of each line but the last
(let ((raw-str (buffer-substring-no-properties start end)))
(comint-send-string (inferior-saw-proc)
(replace-regexp-in-string (regexp-quote "\n") " \\\n" raw-str nil 'literal)))
;; (comint-send-string (inferior-saw-proc) "; print \"OK\"; \n")
(comint-send-string (inferior-saw-proc) "\n")
(if and-go (switch-to-saw t)))
(defun saw-eval-defun (&optional and-go)
"Send the current SAWscript form to the inferior Saw process.
Prefix argument means switch to the Saw buffer afterwards.
A SAWscript form is defined to start with an alphabetic character at the start of a line,
continuing through balanced parentheses to a semicolon."
(interactive "P")
(save-excursion
(beginning-of-saw-form)
(let ((start (point)))
(end-of-saw-form)
(saw-eval-region start (point))))
(if and-go (switch-to-saw t))) ;; outside save-excursion, in case that matters
(defvar start-of-saw-form-re "^[a-zA-Z]") ;; alphabetic at start of line
(defun beginning-of-saw-form ()
(interactive)
(unless (looking-at start-of-saw-form-re)
(re-search-backward start-of-saw-form-re)))
(defun end-of-saw-form ()
(interactive)
(while (not (looking-at "[[:space:]]*;"))
(forward-sexp))
(re-search-forward "[[:space:]]*;")
)
;; (defun end-of-saw-form ()
;; (interactive)
;; (unless (looking-at "[[:space:]]*;")
;; (forward-sexp)
;; (end-of-saw-form))
;; ;; (re-search-forward "[[:space:]]*;")
;; )
(defun switch-to-saw (eob-p)
"Switch to the inferior Saw process buffer.
With argument, positions cursor at end of buffer."
(interactive "P")
(if (get-buffer-process inferior-saw-buffer)
(let ((pop-up-frames
;; Be willing to use another frame
;; that already has the window in it.
(or pop-up-frames
(get-buffer-window inferior-saw-buffer t))))
(pop-to-buffer inferior-saw-buffer))
(run-saw inferior-saw-program))
(when eob-p
(push-mark)
(goto-char (point-max))))
(defalias 'run-saw 'inferior-saw)
(defun inferior-saw (cmd)
"Run an inferior saw process, input and output via buffer `*inferior-saw*'.
If there is a process already running in `*inferior-saw*', just switch
to that buffer.
With argument, allows you to edit the command line (default is value
of `inferior-saw-program'). Runs the hooks from
`inferior-saw-mode-hook' (after the `comint-mode-hook' is run).
\(Type \\[describe-mode] in the process buffer for a list of commands.)"
(interactive (list (if current-prefix-arg
(read-string "Run saw: " inferior-saw-program)
inferior-saw-program)))
(if (not (comint-check-proc "*inferior-saw*"))
(let ((cmdlist (split-string cmd)))
(set-buffer (apply (function make-comint)
"inferior-saw" (car cmdlist) nil (cdr cmdlist)))
(inferior-saw-mode)))
(setq inferior-saw-buffer "*inferior-saw*")
(pop-to-buffer-same-window "*inferior-saw*"))
;; "Returns the current inferior Saw process.
;; See variable `inferior-saw-buffer'."
(defun inferior-saw-proc ()
(let ((proc (get-buffer-process (if (derived-mode-p 'inferior-saw-mode)
(current-buffer)
inferior-saw-buffer))))
(or proc
(error "No Saw subprocess; see variable `inferior-saw-buffer'"))))
(defvar saw-prev-l/c-dir/file nil
"Record last directory and file used in loading.
This holds a cons cell of the form `(DIRECTORY . FILE)'
describing the last `saw-load-file' command.")
(defun saw-load-file (file-name)
"Load a Saw file into the inferior Saw process."
(interactive (comint-get-source "Load SAWscript file: " saw-prev-l/c-dir/file
'(saw-script-mode) t))
(comint-check-source file-name) ; Check to see if buffer needs saved.
(setq saw-prev-l/c-dir/file (cons (file-name-directory file-name)
(file-name-nondirectory file-name)))
(comint-send-string (inferior-saw-proc)
(format "include \"%s\";\n" file-name))
(switch-to-saw t))