scripting: register and call built-in scripts by name

This commit is contained in:
vindarel 2022-12-28 15:08:00 +01:00
parent 3c371c9a81
commit 0d95880caa
7 changed files with 109 additions and 61 deletions

View file

@ -215,62 +215,26 @@ See more in [[docs/README.md][the documentation]].
NOTE: this is brand new! Expect limitations and changes.
Get the =ciel= binary and call it with a .lisp file:
Get the =ciel= binary and call it with your .lisp script:
#+begin_src bash
$ ciel script.lisp
#+end_src
An example script:
Call built-in scripts:
#+begin_src lisp
;; Start your script with this to access all CIEL goodies:
(in-package :ciel-user)
;; We have access to the STR library:
(print (str:join "-" (list "I" "am" "a" "lisper")))
;; We have access to the DICT notation for hash-tables:
(print "testing dict:")
(print (dict :a 1 :b 2))
;; format! prints on standard output and flushes the streams.
(format! t "cmd?")
;; We can run shell commands:
(cmd:cmd "ls")
(format! t "Let's define an alias to run shell commands with '!'. This gives: ")
(defalias ! #'cmd:cmd)
(! "pwd")
;; In cas of an error, we can ask for a CIEL toplevel REPL:
(handler-case
(error "oh no")
(error (c)
(format! t "An error occured: ~a" c)
(format! t "Here's a CIEL top level REPL: ")
(sbcli::repl :noinform t)))
#+begin_src bash
$ ciel --script simpleHTTPserver 9000
#+end_src
Output:
or
#+begin_src txt
"I-am-a-lisper"
"testing dict:"
(dict
:A 1
:B 2
)
cmd? ABOUT.org ciel ciel-core
bin docs src
[…]
Let's define an alias to run shell commands with '!'. This gives:
/home/vindarel/projets/ciel
ciel-user>
#+begin_src bash
$ ciel -s quicksearch colors
#+end_src
See [[https://ciel-lang.github.io/CIEL/#/scripting][the documentation]].
** Shell REPL
Run =ciel= with no arguments:

View file

@ -139,7 +139,16 @@
:components ((:file "repl")
(:file "scripting")
(:file "shell-utils")
(:file "repl-utils"))
(:file "repl-utils")
;; I define them here, for good practice (for me),
;; but I don't use them.
;; static-file is important, otherwise the scripts would be run.
(:module "src/scripts"
:components
((:static-file "quicksearch")
(:static-file "simpleHTTPserver")))
)
;; Build a binary with Deploy, ship foreign libraries (and ignore libssl).
:defsystem-depends-on (:deploy) ;; need to (ql:quickload "deploy") before building.

View file

@ -2,12 +2,20 @@
> Note: this is brand new! Expect limitations and changes.
Get the `ciel` binary and call it with a .lisp file:
Get the `ciel` binary and call it with your .lisp script:
```
$ ciel script.lisp
```
(or `./script.lisp`, see below)
Call built-in scripts:
```
$ ciel --script simpleHTTPserver 9000
```
An example script:
```lisp
@ -20,7 +28,7 @@ An example script:
(format! t "Hello ~a!~&" name))
;; Access CLI args:
(hello (second (uiop:command-line-arguments)))
(hello (second uiop:*command-line-arguments*))
;; We have access to the DICT notation for hash-tables:
(print "testing dict:")
@ -70,7 +78,9 @@ ciel-user>
## Command line arguments
Access them with `(uiop:command-line-arguments)`.
Access them with `uiop:*command-line-arguments`.
This list of arguments can be modified by us. You can always check the original list with `(uiop:command-line-arguments)`.
## Executable file and shebang line
@ -145,17 +155,23 @@ $ ciel -e "(-> (http:get \"https://fakestoreapi.com/products/1\") (json:read-jso
)
```
## Other scripts
## Built-in scripts
Call built-in scripts with `--script` or `-s`.
### Simple HTTP server
```
$ ciel -s simpleHTTPserver 9000
```
see `src/scripts/simpleHTTPserver.lisp` in the CIEL repository.
~~~lisp
(in-package :ciel-user)
;; CLI args: the script name, an optional port number.
(defparameter *port* (or (parse-integer (second (uiop:command-line-arguments)))
(defparameter *port* (or (ignore-errors (parse-integer (second uiop:*command-line-arguments*)))
8000))
(defvar *acceptor* (make-instance 'hunchentoot:easy-acceptor
@ -169,7 +185,8 @@ see `src/scripts/simpleHTTPserver.lisp` in the CIEL repository.
(sleep most-positive-fixnum)
;; Catch a C-c and quit gracefully.
(sb-sys:interactive-interrupt ()
(format! t "Bye!")))
(format! t "Bye!")
(hunchentoot:stop *acceptor*)))
~~~
Given you have an `index.html` file:
@ -224,7 +241,7 @@ Search for Lisp libraries on Quicklisp, Cliki and Github.
see `src/scripts/quicksearch.lisp`.
```lisp
$ ciel src/scripts/quicksearch.lisp color
$ ciel -s quicksearch color
SEARCH-RESULTS: "color"
=======================

View file

@ -1,6 +1,10 @@
(in-package :ciel)
(defparameter *scripts* (dict)
"Available scripts.
Hash-table: file name (sans extension) -> file content (string).")
(defun maybe-ignore-shebang (in)
"If this file starts with #!, delete the shebang line,
so we can LOAD the file.
@ -40,6 +44,34 @@
(error (c)
(format! *error-output* "~a" c))))
(defun register-builtin-scripts ()
"Find available scripts in src/scripts, register them in *SCRIPTS*.
Call this before creating the CIEL binary."
;; We save the file's content as a string.
;; We will run them with LOAD (and an input stream from the string).
;;
;; Example:
;;
;; (load (make-string-input-stream (str:from-file "src/scripts/simpleHTTPserver.lisp")))
(loop for file in (uiop:directory-files "src/scripts/")
if (equal "lisp" (pathname-type file))
do (format t "~t scripts: registering ~a~&" (pathname-name file))
(setf (gethash (pathname-name file) *scripts*)
(str:from-file file))))
(defun run-script (name)
"If NAME is registered in *SCRIPTS*, run this script."
(bind (((:values content exists) (gethash name *scripts*)))
(cond
((and exists (str:blankp content)
(format *error-output* "uh the script ~s has no content?~&" name)))
((not exists)
(format *error-output* "The script ~s was not found.~&" name))
(t
;; Run it!
(load (make-string-input-stream content))))))
;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ciel-user
;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -118,6 +150,18 @@
(return-from main))
;; --script / -s : run scripts by name.
;; They are registered by name in the binary.
;; Ideas:
;; - look for scripts in specified directories.
((member arg '("--script" "-s") :test #'equal)
(pop args)
(setf arg (first args))
;; ditch the "-s" option, must not be seen by the script.
(pop uiop:*command-line-arguments*)
(run-script arg)
(return-from main))
;; LOAD some file.lisp
;; Originally, the goal of the scripting capabilities. The rest are details.
((and arg
@ -143,3 +187,10 @@
(error (c)
(format! *error-output* "Unexpected error: ~a~&" c)
(return-from main)))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; top-level for binary construction.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(format t "~&Registering built-in scripts in src/scripts/ …~&")
(register-builtin-scripts)

3
src/scripts/README.md Normal file
View file

@ -0,0 +1,3 @@
- it is better to use `uiop:*command-line-arguments*` instead of `(uiop:command-line-arguments)`. The latter always get the original full list, but when calling `ciel -s` we need to pop the arguments list, to ditch the `-s`.

View file

@ -10,9 +10,9 @@
(in-package :ciel-user)
(unless (second (uiop:command-line-arguments))
(unless (second uiop:*command-line-arguments*)
(format! t "Quicksearch: search for Lisp libraries on Quicklisp, Cliki and Github.~&")
(format! t "Usage: ciel quicksearch.lisp keyword~&")
(uiop:quit))
(quicksearch:? (second (uiop:command-line-arguments)) :ud) ;; url and details.
(quicksearch:? (second uiop:*command-line-arguments*) :ud) ;; url and details.

View file

@ -8,12 +8,12 @@
(in-package :ciel-user)
;; CLI args: the script name, an optional port number.
(defparameter *port* (or (parse-integer (second (uiop:command-line-arguments)))
8000))
(defparameter *port* (or (ignore-errors (parse-integer (second uiop:*command-line-arguments*)))
9000))
(defvar *acceptor* (make-instance 'hunchentoot:easy-acceptor
:document-root "./"
:port *port*))
(defparameter *acceptor* (make-instance 'hunchentoot:easy-acceptor
:document-root "./"
:port *port*))
(hunchentoot:start *acceptor*)
;; Serve static assets under a static/ directory (optional).
@ -26,4 +26,8 @@
(handler-case
(sleep most-positive-fixnum)
(sb-sys:interactive-interrupt ()
(format! t "Bye!")))
(format! t "Bye!")
(hunchentoot:stop *acceptor*))
(error ()
(format! t "An error occured. Quitting.")
(hunchentoot:stop *acceptor*)))