summaryrefslogtreecommitdiff
path: root/command-interface/command.scm
blob: 3b98991230d91cdae3615c3f8bc29b638ed05be7 (about) (plain)
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
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
;;; Globals used by the command interpreter

(define *current-string* "")
(define *current-mod* '|Main|)
(define *current-command* '())
(define *remembered-file* "Foo")
(define *fragment-status* '())
(define *temp-counter* 0)
(define *last-compiled* "")
(define *abort-command* '())
(define *command-dispatch* '())
(define *extension-module* '())
(define *extension-file-name* "interactive")

(define (prompt mod)
  (format '#f "~A> " mod))

(define-local-syntax (define-command name&args helpstr . body)
  (let* ((str (car name&args))
	 (args (cdr name&args))
	 (fname (string->symbol (string-append "CMD-" str))))
    `(begin
       (define (,fname arguments)
	 (verify-command-args ',args arguments ',helpstr)
	 (apply (lambda ,args ,@body) arguments))
       (setf *command-dispatch*
	     (nconc *command-dispatch*
		    (list (cons ',str (function ,fname)))))
       ',fname)))
		     
(define (heval)
  (initialize-haskell-system)
  (setf *current-string* "")
  (setf *fragment-status* 'Building)
  (say "~&Yale Haskell ~A~A   ~A~%Type :? for help.~%"
       *haskell-compiler-version* *haskell-compiler-update* (identify-system))
  (read-commands))


;;; This loop reads commands until a quit 

(define (read-commands)
  (do ((cmd-status (read-command) (read-command)))
      ((eq? cmd-status 'quit-command-loop) (exit))))

;;; This processes a single line of input.

(define (read-command)
  (let/cc abort-command
    (setf *abort-command* (lambda () (funcall abort-command 'error)))
    (setf *abort-compilation* *abort-command*)
    (setf *phase* 'command-interface)
    (setf *in-error-handler?* '#f)
    (ready-for-input-line)
    (let ((ch (peek-char)))
      (cond ((eof-object? ch)
	     'quit-command-loop)
	    ((char=? ch '#\:)
	     (read-char)
	     (execute-command))
	    ((and (char=? ch '#\newline)
		  (not (eq? *fragment-status* 'Building)))
	     (read-char)
	     'Ignored)
	    (else
	     (when (not (eq? *fragment-status* 'Building))
	       (setf *fragment-status* 'Building)
	       (setf *current-string* ""))
	     (cond ((eqv? ch '#\=)
		    (read-char)
		    (append-to-current-string (expand-print-abbr (read-line))))
		   ((eqv? ch '#\@)	
		    (read-char)
		    (append-to-current-string (expand-exec-abbr (read-line))))
		   (else
		    (append-to-current-string (read-line))))
	     'OK)
	    ))))

(define (append-to-current-string string)
  (setf *current-string*
	(string-append *current-string*
		       string
		       (string #\newline))))


(define (expand-print-abbr string)
  (incf *temp-counter*)
  (format '#f "temp_~a = print temp1_~a where temp1_~a = ~a"
	  *temp-counter* *temp-counter* *temp-counter* string))

(define (expand-exec-abbr string)
  (incf *temp-counter*)
  (format '#f "temp_~a :: Dialogue~%temp_~a = ~a"
	  *temp-counter* *temp-counter* string))


(define (ready-for-input-line)
  (when (not *emacs-mode*)
     (fresh-line (current-output-port))
     (write-string (prompt *current-mod*) (current-output-port))
     (force-output (current-output-port)))
  (notify-ready))

(define (execute-command)
  (if (char=? (peek-char) '#\() ;this is the escape to the lisp evaluator
      (let ((form (read)))
	(eval form)
	'OK)
      (let* ((string    (read-line))
	     (length    (string-length string))
	     (cmd+args  (parse-command-args string 0 0 length)))
	(cond ((null? cmd+args)
	       (say "~&Eh?~%")
	       'OK)
	      (else
	       (let ((fn (assoc/test (function string-starts?)
				     (car cmd+args)
				     *command-dispatch*)))
		 (cond ((eq? fn '#f)
			(say "~&~A: unknown command.  Use :? for help.~%"
			     (car cmd+args))
			'OK)
		       (else
			(funcall (cdr fn) (cdr cmd+args))))))))))


;;; This parses the command into a list of substrings.  
;;; Args are separated by spaces.

(define (parse-command-args string start next end)
  (declare (type fixnum start next end)
	   (type string string))
  (cond ((eqv? next end)
	 (if (eqv? start next)
	     '()
	     (list (substring string start next))))
	((char=? (string-ref string next) '#\space)
	 (let ((next-next  (+ next 1)))
	   (if (eqv? start next)
	       (parse-command-args string next-next next-next end)
	       (cons (substring string start next)
		     (parse-command-args string next-next next-next end)))))
	(else
	 (parse-command-args string start (+ next 1) end))))

(define (verify-command-args template args help)
  (cond ((and (null? template) (null? args))
	 '#t)
	((symbol? template)
	 '#t)
	((or (null? template) (null? args))
	 (say "~&Command error.~%~A~%" help)
	 (funcall *abort-command*))
	(else
	 (verify-command-args (car template) (car args) help)
	 (verify-command-args (cdr template) (cdr args) help))))

(define-command ("?")
  ":?            Print the help file."
  (print-file "$HASKELL/command-interface-help"))

(define-command ("eval")
  ":eval            Evaluate current extension."
  (eval-fragment '#t)
  'OK)

(define-command ("save")
  ":save     Save current extension"
  (eval-fragment '#f)
  (cond ((eq? *fragment-status* 'Error)
	 (say/ne "~&Cannot save: errors encountered.~%"))  
	((eq? *fragment-status* 'Compiled)
	 (extend-module *current-mod* *extension-module*)
	 (setf *fragment-status* 'Saved)))
  'OK)

(define-command ("quit")
  ":quit        Quit the Haskell evaluator."
  'quit-command-loop)

(define-command ("module" mod)
  ":module module-name    Select module for incremental evaluation."
  (setf *current-mod* (string->symbol mod))
  (when (not (cm-available?))
      (say/ne "~&Warning: module ~A is not currently loaded.~%" *current-mod*))
  'OK)

(define-command ("run" . file)
  ":run <file>   Compile, load, and run a file."
  (set-current-file file)
  (clear-extended-modules)
  (let ((mods (compile/load *remembered-file*)))
    (when (pair? mods)
      (dolist (m mods)
	 (eval-module (table-entry *modules* m)))))
  'OK)

(define-command ("compile" . file)
  ":compile <file> Compile and load a file."
  (set-current-file file)
  (clear-extended-modules)
  (select-current-mod (compile/compile *remembered-file*))
  'OK)

(define-command ("load" . file)
  ":load <file>      Load a file."
  (set-current-file file)
  (clear-extended-modules)
  (select-current-mod (compile/load *remembered-file*))
  'OK)

(define-command ("Main")
  ":Main           Switch to an empty Main module."
  (make-empty-main)
  'OK)

(define-command ("clear")
  ":clear   Clear saved definitions from current module."
  (remove-extended-modules *current-mod*)
  (setf *current-string* "")
  (setf *fragment-status* 'Building))

(define-command ("list")
  ":list          List current extension."
  (say "~&Current Haskell extension:~%~a" *current-string*)
  (cond ((eq? *fragment-status* 'Error)
	 (say "Extension contains errors.~%"))  
	((eq? *fragment-status* 'Compiled)
	 (say "Extension is compiled and ready.~%")))
  'OK)

(define-command ("kill")
  ":kill      Clear the current fragment."
  (when (eq? *fragment-status* 'Building)
    (setf *current-string* ""))
  'OK)

(define-command ("p?")
  ":p?            Show available printers."
  (if *emacs-mode*
      (notify-printers (dynamic *printers*))
      (begin
	(print-file "$HASKELL/emacs-tools/printer-help.txt")
	(say "~&Active printers: ~A~%" (dynamic *printers*)))
    ))

(define-command ("p=" . passes)
  ":p= pass1 pass2 ...  Set printers."
  (setf *printers* (set-printers passes '=))
  (say/ne "~&Setting printers: ~A~%" *printers*))

(define-command ("p+" . passes)
  ":p+ pass1 pass2 ...  Add printers."
  (setf *printers* (set-printers passes '+))
  (say/ne "~&Setting printers: ~A~%" *printers*))

(define-command ("p-" . passes)
  ":p- pass1 pass2 ...  Turn off printers."
  (setf *printers* (set-printers passes '-))
  (say/ne "~&Setting printers: ~A~%" *printers*))



(define-command ("o?")
  ":o?            Show available optimizers."
  (if *emacs-mode*
      (notify-optimizers (dynamic *optimizers*))
      (begin
	(print-file "$HASKELL/emacs-tools/optimizer-help.txt")
	(say "~&Active optimizers: ~A~%" (dynamic *optimizers*)))
    ))

(define-command ("o=" . optimizers)
  ":o= optimizer1 optimizer2 ...  Set optimizers."
  (setf *optimizers* (set-optimizers optimizers '=))
  (say/ne "~&Setting optimizers: ~A~%" *optimizers*))

(define-command ("o+" . optimizers)
  ":o+ optimizer1 optimizer2 ...  Add optimizers."
  (setf *optimizers* (set-optimizers optimizers '+))
  (say/ne "~&Setting optimizers: ~A~%" *optimizers*))

(define-command ("o-" . optimizers)
  ":o- optimizer1 optimizer2 ...  Turn off optimizers."
  (setf *optimizers* (set-optimizers optimizers '-))
  (say/ne "~&Setting optimizers: ~A~%" *optimizers*))


(define-command ("cd" d)
  ":cd directory   Change the current directory."
  (cd d)
  'OK)

(define-command ("Emacs" mode)
  ":Emacs on/off   Turn on or off emacs mode."
  (cond ((string=? mode "on")
	 (setf *emacs-mode* '#t))
	((string=? mode "off")
	 (setf *emacs-mode* '#f))
	(else
	 (say "~&Use on or off.~%"))))

(define-command ("file" name)
  ":file name"
  (setf *extension-file-name* name)
  'OK)