[Openmcl-devel] How to use lisp program in unix pipeline
gb at clozure.com
Tue May 13 08:29:05 EDT 2003
On Tue, 13 May 2003, Frank Sonnemans wrote:
> Thanks for the exhaustive reply. From what you write I have another scenario
> in mind. Instead of having many multi megabyte images I can create one
> specialized image which does nothing but load a fasl (or lisp) file which is
> the first commandline parameter, call a sort of main function which gets the
> rest of the commandline options as a parameter and exit. This way openmcl
> would behave like a shell executing a shell script on steroids.
I think so, though as soon as I sent my long message yesterday and saw
Sven Van Caekenber's reply, I wondered if it might be simpler in many
ways to use shell scripts. Let's see.
Here's an attempt to define an application class that behaves as you
suggest. One might realistically want a -little- more error handling
than is shown here.
;---- file "filter-app.lisp"
(in-package "CCL") ; Too many of these things are internal symbols in CCL.
;;; A FILTER-APP inherits from APPLICATION, but not from
(defclass filter-app (application) ())
;;; We'll handle the argument list by hand. It may not even be necessary
;;; to specialize this method, but it can't hurt.
(defmethod parse-application-arguments ((app filter-app))
(values nil nil nil))
;;; We don't have any sort of specialized init file.
(defmethod application-init-file ((app filter-app))
;;; All unhandled errors are fatal: we don't want to see a break loop.
(defmethod application-error ((app filter-app) cond frame)
(declare (ignore frame))
(format t "~&Fatal error: ~a~&" cond)
(force-output) ; ensure user sees the error
;;; Define the application class's toplevel function.
(defmethod toplevel-function ((app filter-app) init-file)
(declare (ignore init-file))
;; The CAR of CCL::*COMMAND-LINE-ARGUMENT-LIST* is the
;; pathname of the kernel.
;; Assume that the CADR is the name of a loadable file
;; which defines CL-USER::MAIN. After loading the
;; file, try to call CL-USER::MAIN on the CDDR of the
(load (cadr ccl::*command-line-argument-list*))
(apply #'cl-user::main (cddr ccl::*command-line-argument-list*)))
;---- end of filter-app.lisp
And here's a familiar-looking "application":
;---- file "adder-app.lisp"
;;; Read numbers from *standard-input* until EOF.
;;; Print the result of adding our first argument (default: 1) to
;;; each number. Don't try to handle errors: this is just a silly
(defun main (&optional (num-string "1") &rest ignore)
(declare (ignore ignore))
(let* ((addend (read-from-string num-string))
(eof (cons nil nil)))
(check-type addend number)
(format t "~&number: ")
(let* ((num (read t nil eof)))
(if (eq num eof) (return))
(format t "~& sum of ~d and ~d is ~d." num addend (+ num addend))))))
;---- end of "adder-app.lisp"
If we save an image using the new application class (this happens to be
0.14, but I believe this stuff works the same way in 0.13):
Welcome to OpenMCL Version (Alpha: Darwin) 0.14-030513!
? (load "home:filter-app.lisp")
? (save-application "home:filter-app.image" :application-class 'ccl::filter-app)
We can then invoke the image from the shell, specifying a "main" application
to run and additional arguments:
[~] gb at dervish> openmcl -I filter-app.image adder-app.lisp
sum of 3 and 1 is 4.
sum of 4 and 1 is 5.
sum of 0.5 and 1 is 1.5.
sum of -1 and 1 is 0.
Fatal error: value NOT-A-NUMBER is not of the expected type NUMBER.
[~] gb at dervish> echo '#c(3.0 2.0)' | openmcl -I filter-app.image adder-app.lisp 2
sum of #c(3.0 2.0) and 2 is #c(5.0 2.0).
I'm not sure whether *ERROR-OUTPUT* is really that distinct from
*STANDARD-OUTPUT*, and for filter-like programs it might be nice to
distinguish them better.
You may also find that it's necessary to FORCE-OUTPUT a bit more
often than you would when writing "normal" lisp code; this is probably
more true in 0.14 than 0.13.x.
Openmcl-devel mailing list
Openmcl-devel at clozure.com
More information about the Openmcl-devel