You need JavaScript to see the rest of the doc :( Just look at the GitHub repository. Cheers, EWW user maybe?
49 |
50 |
51 |
52 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
--------------------------------------------------------------------------------
/docs/dependencies.md:
--------------------------------------------------------------------------------
1 |
2 | - access: A library providing functions that unify data-structure access for Common Lisp: access and (setf access)
3 | - alexandria: Alexandria is a collection of portable public domain utilities.
4 | - arrow-macros: arrow-macros provides clojure-like arrow macros and diamond wands.
5 | - bordeaux-threads: Bordeaux Threads makes writing portable multi-threaded apps simple.
6 | - cl-ansi-text: ANSI control string characters, focused on color
7 | - cl-ansi-text: ANSI control string characters, focused on color
8 | - cl-cron: A simple tool that provides cron like facilities directly inside of common lisp. For this to work properly note that your lisp implementation should have support for threads
9 | - cl-csv: Facilities for reading and writing CSV format files
10 | - cl-ftp: send or receive files from FTP.
11 | - cl-ppcre: Perl-compatible regular expression library
12 | - cl-reexport: Reexport external symbols in other packages.
13 | mechanisms for running and composing Unix shell commands and
14 | constructs from Common Lisp.
15 |
16 | Essentially, it provides a '!' syntax that you can use to ...
17 | - clingon: Command-line options parser system for Common Lisp
18 | - closer-mop: Closer to MOP is a compatibility layer that rectifies many of the absent or incorrect CLOS MOP features across a broad range of Common Lisp implementations.
19 | - cmd: A utility for running external programs
20 | - dbi: Database independent interface for Common Lisp
21 | - defstar: defstar: macros allowing easy inline type declarations for
22 | variables and and function return values.
23 | - dexador: Yet another HTTP client for Common Lisp
24 | - dissect: A lib for introspecting the call stack and active restarts.
25 | - easy-routes: Yet another routes handling utility on top of Hunchentoot
26 | - fiveam: A simple regression testing framework
27 |
28 |
29 | - for: An extensible iteration macro library.
30 | - fset: A functional set-theoretic collections library.
31 | See: https://gitlab.common-lisp.net/fset/fset/-/wikis/home
32 | - generic-cl: Standard Common Lisp functions implemented using generic functions.
33 | - hunchentoot: Hunchentoot is a HTTP server based on USOCKET and
34 | BORDEAUX-THREADS. It supports HTTP 1.1, serves static files, has a
35 | simple framework for user-defined handlers and can be extended
36 | through su...
37 | - local-time: A library for manipulating dates and times, based on a paper by Erik Naggum
38 | - log4cl: NIL
39 | - lparallel: Parallelism for Common Lisp
40 | - lquery: A library to allow jQuery-like HTML/DOM manipulation.
41 | - nodgui: Tck-Tk graphical user interfaces. nodgui is a fork of Ltk with a built-in theme and more widgets.
42 | - metabang-bind: Bind is a macro that generalizes multiple-value-bind, let, let*, destructuring-bind, structure and slot accessors, and a whole lot more.
43 | - modf: A SETF like macro for functional programming
44 |
45 |
46 | - named-readtables: Library that creates a namespace for readtables akin
47 | to the namespace of packages.
48 | - parse-float: Parse floating point values in strings.
49 | - parse-number: Number parsing library
50 | - printv: printv: a batteries-included tracing and debug-logging macro
51 | - pythonic-string-reader: A simple and unintrusive read table modification that allows for
52 | simple string literal definition that doesn't require escaping characters.
53 | - quicksearch: Quicksearch searches CL library, and outputs results at REPL.
54 | - quri: Yet another URI library for Common Lisp
55 | - repl-utilities: Ease common tasks at the REPL.
56 | - serapeum: Utilities beyond Alexandria.
57 | - shasht: JSON reading and writing for the Kzinti.
58 | - shlex: Lexical analyzer for simple shell-like syntax.
59 | - spinneret: Common Lisp HTML5 generator.
60 | - secret-values: reduce the risk of accidentally revealing secret values such as passwords.
61 | - str: Modern, consistent and terse Common Lisp string manipulation library.
62 | - sxql: A SQL generator
63 | - trivia: NON-optimized pattern matcher compatible with OPTIMA, with extensible optimizer interface and clean codebase
64 | - trivial-arguments: A simple library to retrieve the lambda-list of a function.
65 | - trivial-do: Looping extensions that follow the style of the core DO functions.
66 | - trivial-monitored-thread: Trivial Monitored Thread offers a very simple (aka trivial) way of spawning threads and being informed when one any of them crash and die.
67 | - trivial-package-local-nicknames: Portability library for package-local nicknames
68 | - trivial-types: Trivial type definitions
69 | - vgplot: Interface to gnuplot
70 | - which: The which UNIX command in Common Lisp.
71 |
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | # CIEL
2 |
3 | CIEL is a ready-to-use collection of Lisp libraries.
4 |
5 | It's Common Lisp, batteries included.
6 |
7 | It comes in 3 forms:
8 |
9 | - a binary, to run CIEL **scripts**: fast start-up times, standalone binary, built-in utilities.
10 | - a simple full-featured **REPL** for the terminal.
11 | - a **Lisp library**.
12 |
13 | Questions, doubts? See the [FAQ](FAQ.md).
14 |
15 | Status: it's a work in progress. I deployed it for client projects.
16 |
17 | ```lisp
18 | #!/usr/bin/env ciel
19 |
20 | (-> "https://fakestoreapi.com/products?limit=5"
21 | http:get
22 | json:read-json
23 | (elt 0)
24 | (access "title"))
25 | ```
26 |
27 | ```bash
28 | $ chmodx +x getproduct.lisp
29 | $ time ./getproduct.lisp
30 | "Fjallraven - Foldsack No…ckpack, Fits 15 Laptops"
31 | ./getproduct.lisp 0.10s user 0.02s system 24% cpu 0.466 total
32 | ```
33 |
34 |
35 |
36 | ## Rationale
37 |
38 | One of our goals is to make Common Lisp useful out of the box for
39 | mundane tasks -by today's standards.
40 |
41 | Consequently, **we ship libraries** to
42 | handle JSON and CSV, as well as others to ease string manipulation,
43 | to have regular expressions out of the box, for threads and
44 | jobs scheduling, for HTTP and URI handling, to create simple GUIs with
45 | nodgui (Tk-based, nice theme), and so on. You can of course do all this without CIEL, but
46 | then you'd have to install the library manager (Quicklisp) first and load these libraries
47 | into your Lisp image every time you start it. Now, you have them at
48 | your fingertips whenever you start CIEL. Some of the libraries we bring in are for extending the language
49 | syntax a bit. For example, we include the `Trivia` library for
50 | pattern matching.
51 |
52 | We also aim to **soften the irritating parts of standard Common Lisp**.
53 | A famous one, puzzling for beginners and non-optimal for seasoned
54 | lispers, is the creation of hash-tables. We include the `dict` function
55 | from the Serapeum library (which we enhanced further with a pull request):
56 |
57 |
58 | ~~~lisp
59 | CIEL-USER> (dict :a 1 :b 2 :c 3)
60 | ~~~
61 |
62 | which prints:
63 |
64 | ~~~lisp
65 | (dict
66 | :A 1
67 | :B 2
68 | :C 3
69 | )
70 | ~~~
71 |
72 | In standard Common Lisp, the equivalent is more convoluted:
73 |
74 | ~~~lisp
75 | (let ((ht (make-hash-table :test 'equal)))
76 | (setf (gethash :a ht) 1)
77 | (setf (gethash :b ht) 2)
78 | (setf (gethash :c ht) 3)
79 | ht)
80 | ;; #
81 | ;; (and we don't get a readable representation, so our example is not even equivalent)
82 | ~~~
83 |
84 | We **add missing functions**. For example, after you used `parse-integer`, you are probably looking for a `parse-float` function… but you can't find it, because it isn't defined by the standard. You must install a third-party library. Now you have it. Or, you know `append` and you want is non-consing (destructive) counterpart. In pure CL, it's `nconc`, we ship an `nappend` alias.
85 |
86 | We **enhance the docstrings** of built-in functions and macros with more
87 | explanations and examples, so you don't have to reach to external
88 | documentation just yet in order to understand and try out things like
89 | `mapcar` or `loop` (look 'ma, LOOP has no docstring by default).
90 |
91 | Moreover, we bring a **user friendly REPL on the terminal**,
92 | with bells and whistles useful to the developer and people living in a
93 | terminal window. For example, our [repl for the terminal](repl.md) has readline support, multi-line editing, optional syntax highlighting, a shell passthrough, and more goodies.
94 |
95 | We bring **scripting capabilities**. Just run `ciel myscript.lisp`, and use all the high-level CIEL libraries and Common Lisp features in your script. It starts up fast.
96 |
97 |
98 | *We are only started. You can sponsor us [on GitHub sponsors](https://github.com/sponsors/vindarel/), thank you!*
99 |
100 | [](https://ko-fi.com/K3K828W0V)
101 |
102 | *If you need to learn Common Lisp the efficient way, I have [a course on videos](https://www.udemy.com/course/common-lisp-programming/?referralCode=2F3D698BBC4326F94358), with many real-world practical stuff in and still growing. If you are a student, drop me a line for a coupon. Thank you for your support!*
103 |
104 |
105 | # Install
106 |
107 | Let's get started. See the [installation instructions](install.md).
108 |
109 | # Scripting & libraries
110 |
111 | Then, have a look at CIEL's [scripting capabilies](scripting.md), and on the [libraries](libraries.md) we ship.
112 |
113 |
114 | # Final words
115 |
116 | That was your life in CL:
117 |
118 |
119 | and now:
120 |
121 |
122 |
--------------------------------------------------------------------------------
/docs/repl.md:
--------------------------------------------------------------------------------
1 | # CIEL's custom REPL
2 |
3 | CIEL's REPL is more user friendly than the default SBCL one. In particular:
4 |
5 | - it has readline capabilities, meaning that the arrow keys work by default (wouhou!) and there is a persistent history, like in any shell.
6 | - it has **multiline input**.
7 | - it has **TAB completion**.
8 | - including for files (after a bracket) and binaries in the PATH.
9 | - it handles errors gracefully: you are not dropped into the debugger and its sub-REPL, you simply see the error message.
10 | - it has optional **syntax highlighting**.
11 | - it has a **shell pass-through**: try `!ls` (also available in Slime)
12 | - it runs **interactive commands**: try `!htop`, `!vim test.lisp`, `!emacs -nw test.lisp` or `!env FOO=BAR sudo -i powertop`.
13 | - it has a quick **edit and load file** command: calling `%edit file.lisp` will open the file with the editor of the EDITOR environment variable. When you close it, the file is loaded and evaluated.
14 | - it has an optional **lisp critic** that scans the code you enter at
15 | the REPL for instances of bad practices.
16 |
17 | - it defines more **helper commands**:
18 |
19 | ``` txt
20 | %help => Prints this general help message
21 | %doc => Prints the available documentation for this symbol
22 | %? => Gets help on a symbol : :? str
23 | %w => Writes the current session to a file
24 | %d => Dumps the disassembly of a symbol
25 | %t => Prints the type of an expression
26 | %q => Ends the session.
27 | ```
28 |
29 | Our REPL is adapted from [sbcli](https://github.com/hellerve/sbcli). See also [cl-repl](https://github.com/koji-kojiro/cl-repl/), that has an interactive debugger.
30 |
31 | > Note: a shell interface doesn't replace a good development environment. See this [list of editors for Common Lisp](https://lispcookbook.github.io/cl-cookbook/editor-support.html): Emacs, Vim, Atom, VSCode, Intellij, SublimeText, Jupyter Notebooks and more.
32 |
33 | ## Quick documentation lookup
34 |
35 | The documentation for a symbol is available with `%doc` and also by
36 | appending a "?" after a function name:
37 |
38 | ```
39 | ciel-user> %doc dict
40 | ;; or:
41 | ciel-user> (dict ?
42 | ```
43 |
44 | ## Shell pass-through with "!"
45 |
46 | Use `!` to send a shell command. All shell commands are run interactively, so you can run `htop`, `sudo`, `emacs -nw` etc.
47 |
48 | ```
49 | !ls
50 | !sudo emacs -nw /etc/
51 | ```
52 |
53 | We provide TAB completion for shell commands that are in your PATH.
54 |
55 | See [Lish](https://github.com/nibbula/lish/) and [SHCL](https://github.com/bradleyjensen/shcl) for more unholy union of (posix) shells and Common Lisp.
56 |
57 |
58 | ## Syntax highlighting
59 |
60 | Syntax highlighting is off by default. To enable it, install [pygments](https://pygments.org/) and add this in your `~/.cielrc`:
61 |
62 | ```lisp
63 | (setf sbcli:*syntax-highlighting* t)
64 |
65 | ;; and, optionally:
66 | ;; (setf sbcli::*pygmentize* "/path/to/pygmentize")
67 | ;; (setf sbcli::*pygmentize-options* (list "-s" "-l" "lisp"))
68 | ```
69 |
70 | You can also switch it on and off from the REPL:
71 |
72 | ```lisp
73 | (setf sbcli:*syntax-highlighting* t)
74 | ```
75 |
76 | ## Friendly lisp-critic
77 |
78 | The `%lisp-critic` helper command toggles on and off the
79 | [lisp-critic](https://github.com/g000001/lisp-critic). The Lisp Critic
80 | scans your code for instances of bad Lisp programming practice. For
81 | example, when it sees the following function:
82 |
83 |
84 | ~~~lisp
85 | (critique
86 | (defun count-a (lst)
87 | (setq n 0)
88 | (dolist (x lst)
89 | (if (equal x 'a)
90 | (setq n (+ n 1))))
91 | n))
92 | ~~~
93 |
94 | the lisp-critic gives you these advices:
95 |
96 | ```
97 | ----------------------------------------------------------------------
98 |
99 | SETS-GLOBALS: GLOBALS!! Don't use global variables, i.e., N
100 | ----------------------------------------------------------------------
101 |
102 | DOLIST-SETF: Don't use SETQ inside DOLIST to accumulate values for N.
103 | Use DO. Make N a DO variable and don't use SETQ etc at all.
104 | ----------------------------------------------------------------------
105 |
106 | USE-EQL: Unless something special is going on, use EQL, not EQUAL.
107 | ----------------------------------------------------------------------
108 |
109 | X-PLUS-1: Don't use (+ N 1), use (1+ N) for its value or (INCF N) to
110 | change N, whichever is appropriate here.
111 | ----------------------------------------------------------------------
112 | ; in: DEFUN COUNT-A
113 | ; (SETQ CIEL-USER::N 0)
114 | ;
115 | ; caught WARNING:
116 | ; undefined variable: N
117 | ;
118 | ; compilation unit finished
119 | ; Undefined variable:
120 | ; N
121 | ; caught 1 WARNING condition
122 | => COUNT-A
123 | ```
124 |
125 | ## Quick edit & load a file
126 |
127 | Use `%edit file.lisp`.
128 |
129 | This will open the file with the editor of the EDITOR environment variable. When you
130 | close it, the file is loaded and evaluated. If you defined functions, you can try them in the REPL.
131 |
132 | It is a quick way to write lisp code and have fast feedback. It's nice
133 | to use to tinker with code, to write small throw-away
134 | programs. However, this doesn't replace a true editor setup!
135 |
136 | We use [magic-ed](https://github.com/sanel/magic-ed). You can also call it manually with `(magic-ed "file.lisp")`, and give it a couple arguments:
137 |
138 | - `:eval nil`: don't evaluate the file when you close it.
139 | - `:output :string`: output the file content as a string.
140 |
--------------------------------------------------------------------------------
/src/scripts/simpleHTTPserver.lisp:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ciel
2 | ;;;
3 | ;;; Run with:
4 | ;;; $ ciel -s simpleHTTPserver 4242
5 | ;;;
6 | ;;; or
7 | ;;;
8 | ;;; $ ./simpleHTTPserver.lisp
9 | ;;;
10 | ;;; Use -b to open the web browser.
11 |
12 | (in-package :ciel-user)
13 |
14 | (use-package :spinneret)
15 |
16 | ;; ;;;;;;;;;;;;;;;;;;;;;;,
17 | ;; CLI args.
18 | ;; ;;;;;;;;;;;;;;;;;;;;;;,
19 | (defparameter *my-cli-args* *script-args*
20 | "Copy of the CLI options given to this script.
21 | When we call
22 |
23 | $ ciel -v simpleHTTPserver.lisp -b 4444
24 |
25 | then the list '(simpleHTTPserver.lisp -b 4444) is given in *script-args*.")
26 |
27 | ;; CLI args: the script name, an optional port number.
28 | (defparameter *port* 9000
29 | "Default port. Change it with a free argument on the command-line.")
30 |
31 | (defparameter *acceptor* nil
32 | "Hunchentoot's server instance. Create it with `make-acceptor'.")
33 |
34 | (defun make-acceptor (&key (port *port*))
35 | "Return the existing acceptor or create one."
36 | ;; I prefer to put this in a function to develop interactively.
37 | (or *acceptor*
38 | (setf *acceptor* (make-instance 'hunchentoot:easy-acceptor
39 | :document-root "./"
40 | :port port))))
41 |
42 | ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
43 | ;; "Templates" (with s-expressions and Spinneret)
44 | ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
45 |
46 | (defmacro with-page ((&key title) &body body)
47 | `(with-html-string
48 | (:doctype)
49 | (:html
50 | (:head
51 | (:title ,title))
52 | (:body ,@body))))
53 |
54 | (defun file-or-directory-namestring (path)
55 | "Return the name of this file or of this directory.
56 | XXX: we want the name of the directory, not the full path."
57 | (unless (fboundp 'str:ensure-suffix)
58 | (print "WARN: str:ensure-suffix is in the latest cl-str. You'll need to clone it into ~/quicklisp/local-projects/"))
59 | (if (uiop:file-pathname-p path)
60 | (file-namestring path)
61 | ;; How to simply get the directory name, not full path?
62 | ;; pathname-directory -> (:relative "path" "to" "dir")
63 | ;; INFO: this is merged in cl-str but not in Quicklisp yet.
64 | (str:ensure-suffix "/"
65 | (first (last (pathname-directory path))))))
66 |
67 | (defun show-file-list (file-list &key title)
68 | "List files in a HTML list."
69 | (with-page (:title title)
70 | (:header
71 | (:h2 title))
72 | (:ol (dolist (item file-list)
73 | (:li (:a :href
74 | (format nil "~a" (file-or-directory-namestring item))
75 | (format nil "~a" (file-or-directory-namestring item))))))
76 | (:br)
77 | (:footer :style "color: dimgrey" ("Powered by CIEL Is an Extended Lisp" ))))
78 |
79 | (defun file-list ()
80 | (show-file-list (append
81 | ;; This is how to list directories,
82 | ;; but we have to serve their content now.
83 | ;; (uiop:subdirectories (uiop:getcwd))
84 | (uiop:directory-files (uiop:getcwd)))
85 | :title (format nil "Files for ~a" (uiop:getcwd))))
86 |
87 | ;; ;;;;;;;;;;;;;;;;;;;;;;
88 | ;; web-server config.
89 | ;;;;;;;;;;;;;;;;;;;;;;;;,
90 |
91 | ;; On the root URL "/" show the listing, but when clicking on a file let the server serve it.
92 | (defun serve-root ()
93 | (push
94 | (hunchentoot:create-regex-dispatcher "/$" #'file-list)
95 | hunchentoot:*dispatch-table*))
96 |
97 | #+(or)
98 | (setf hunchentoot:*dispatch-table* nil)
99 |
100 | (defun serve-static-assets ()
101 | ;; Serve static assets under a static/ directory (optional).
102 | (push (hunchentoot:create-folder-dispatcher-and-handler
103 | "/static/" "static/" ;; starts without a /
104 | )
105 | hunchentoot:*dispatch-table*))
106 |
107 | ;; main function:
108 | (defun simplehttpserver (&key browse (port *port*))
109 | "Create a Hunchentoot dispatcher on our root directory,
110 | serve static assets,
111 | start Hunchentoot and keep it on the foreground."
112 | (serve-root)
113 | (serve-static-assets)
114 | (handler-case
115 | (progn
116 | ;; Start the webserver.
117 | (hunchentoot:start (make-acceptor :port port))
118 | (format! t "~&Serving files on port ~a…~&" port)
119 | (format! t "~&~&~t ⤷ http://127.0.0.1:~a ~&~&" port)
120 |
121 | #+unix
122 | (when browse
123 | (uiop:format! t "Open web browser…~&")
124 | (uiop:run-program (list
125 | "xdg-open"
126 | (format nil "http://localhost:~a" port))))
127 |
128 | ;; Wait in the foreground.
129 | (sleep most-positive-fixnum))
130 |
131 | (usocket:address-in-use-error ()
132 | (format! *error-output* "This port is already in use. Quitting.~&"))
133 | (sb-sys:interactive-interrupt ()
134 | (format! t "Bye!")
135 | (hunchentoot:stop *acceptor*))
136 | (error ()
137 | (format! t "An error occured. Quitting.")
138 | (hunchentoot:stop *acceptor*))))
139 |
140 |
141 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
142 | ;; Parse CLI arguments with Clingon.
143 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
144 | (defparameter cli/options
145 | (list
146 | (clingon:make-option
147 | :flag
148 | :description "Show help"
149 | :short-name #\h
150 | :key :help)
151 | (clingon:make-option
152 | :flag
153 | :description "Open browser"
154 | :short-name #\b
155 | :long-name "browse"
156 | :key :browse))
157 | "Our script's options.")
158 |
159 | (defun cli/handler (cmd)
160 | "Look at our CLI args and eventually start the web server.
161 |
162 | cmd: a Clingon command built with cli/command."
163 | (let* ((help (clingon:getopt cmd :help))
164 | (browse (clingon:getopt cmd :browse))
165 | ;; freeargs always have the script name??
166 | (freeargs (rest (clingon:command-arguments cmd)))
167 | (port *port*))
168 | (when help
169 | ;; This funcall is to avoid a style warning: the cli/command function
170 | ;; is not yet defined.
171 | (clingon:print-usage (funcall 'cli/command) t)
172 | (return-from cli/handler))
173 | (when freeargs
174 | (setf port (or (ignore-errors
175 | (parse-integer (first freeargs)))
176 | *port*)))
177 | (simplehttpserver :browse browse :port port)
178 | ))
179 |
180 | (defun cli/command ()
181 | "Create a top-level Clingon command."
182 | (clingon:make-command
183 | :name "simpleHTTPserver.lisp"
184 | :description "Serve the local directory with a simple web server."
185 | :usage "[-h] [-b] [PORT]"
186 | :version "0.1"
187 | :license "todo"
188 | :authors '("vindarel")
189 | :options cli/options
190 | :handler #'cli/handler))
191 |
192 | #|
193 | A note on using Clingon's free arguments in scripts.
194 | (we get them with (clingon:command-arguments))
195 |
196 | $ ciel -s simplehttpserver 4242
197 | only has 4242 as free argument, but
198 |
199 | $ ./simpleHTTPserver.lisp 4242
200 | under the hood equals to
201 | $ ciel simpleHTTPserver.lisp 4242
202 | and this has two free arguments: simplehttpserver.lisp and 4242.
203 |
204 | To fix this, to make it always coherent so we can run this script with
205 | -s or with the shebang, CIEL sets *script-args* to always have
206 | the script name.
207 |
208 | |#
209 |
210 | ;; Call the main function only when running this as a script,
211 | ;; not when developing on the REPL.
212 | #+ciel
213 | (clingon:run (cli/command) *script-args*)
214 |
--------------------------------------------------------------------------------
/ciel.asd:
--------------------------------------------------------------------------------
1 | #|
2 | This file is a part of ciel project.
3 | |#
4 |
5 | (require "asdf") ;; for CI
6 |
7 | (asdf:defsystem "ciel"
8 | :description "CIEL Is an Extended Lisp (Common Lisp, batteries included)."
9 | :version "0.2.1"
10 | :author "vindarel"
11 | :license "MIT"
12 | :homepage "https://github.com/ciel-lang/CIEL/"
13 | :source-control (:git "https://github.com/ciel-lang/CIEL/")
14 | :bug-tracker "https://github.com/ciel-lang/CIEL/issues/"
15 |
16 | :depends-on (
17 | :cl-reexport ;; for us
18 | :cl-ansi-text
19 |
20 | :access
21 | :alexandria
22 | :arrow-macros
23 |
24 | ;; CSV
25 | :cl-csv
26 | :cl-csv-data-table
27 | :data-table
28 |
29 | ;; Previously, we had dependencies that depended on Osicat (fof, moira),
30 | ;; hence complicating deployment of binaries.
31 | ;; Check with (ql:who-depends-on "osicat") and ditch Osicat.
32 | ;;
33 | :file-finder ;; file-object finder
34 |
35 | ;; threads
36 | :bordeaux-threads
37 | :trivial-monitored-thread
38 | :lparallel
39 | :moira/light ;; monitor background threads
40 | :cl-cron
41 |
42 | :closer-mop
43 | :cl-ansi-text
44 | :cl-csv
45 | :shasht ;; json
46 | :cl-json-pointer/synonyms
47 | :dissect
48 | :fset
49 | :file-notify ;; needs inotify (linux) or fsevent (macos)
50 | :generic-cl
51 |
52 | ;; web
53 | :dexador
54 | :hunchentoot
55 | :easy-routes ;; better route definition for Hunchentoot.
56 | :quri
57 | :lquery
58 | :spinneret ;; lispy templates. Used in simpleHTTPserver.lisp
59 |
60 | ;; other networking:
61 | :cl-ftp ;; depends on only: split-sequence and usocket.
62 |
63 | ;; GUI
64 | ;; We remove nodgui as of <2024-08-30>
65 | ;; because it was too heavy in dependencies, see
66 | ;; https://github.com/ciel-lang/CIEL/issues/56
67 | ;; We'll test again with its lightweight nodgui-lite system.
68 | ;; :nodgui ;; ltk fork with built-in themes and more widgets.
69 | ;; to test:
70 | ;; :nodgui-lite
71 |
72 | ;; CLI
73 | :clingon ;; args parsing
74 |
75 | :local-time
76 | :modf
77 |
78 | ;; number parsing
79 | :parse-float
80 | :parse-number
81 |
82 | ;; database
83 | :dbi ; connects and executes queries.
84 | ;; dbi users must reference the driver's dependency
85 | ;; when building a binary.
86 | ;; If not, dbi wants to install a system on the fly,
87 | ;; calls to ASDF, which fails with a useless message.
88 | ;;
89 | ;; Can we suppose sqlite3 is ubiquitous?
90 | ;; This would require libsqlite3 (libsqlite3-dev on Debian).
91 | ;; :dbd-sqlite3
92 | ;; With those:
93 | ;; :dbd-mysql ;; requires libmysqlclient
94 | ;; :dbd-postgres
95 |
96 | :sxql ;; SQL generator from lispy syntax.
97 | ;; I recently removed Mito. Why? lol.
98 |
99 | ;; numerical
100 | :vgplot
101 |
102 | ;; regexp
103 | :cl-ppcre
104 |
105 | ;; string manipulation
106 | :str
107 |
108 | ;; security
109 | :secret-values
110 |
111 | ;; other utilities
112 | :progressons ;; no deps. Simple progress bar. Not in Quicklisp as of <2024-08-30>.
113 | :termp ;; no deps. Are we in a dumb terminal like Slime's REPL?
114 |
115 | ;;;
116 | ;;; Language extensions.
117 | ;;;
118 | ;; triple quotes
119 | :pythonic-string-reader
120 |
121 | ;; pattern matching
122 | :trivia
123 | :trivial-arguments
124 | :trivial-package-local-nicknames
125 | :trivial-types
126 |
127 | ;; extended let
128 | :metabang-bind
129 |
130 | ;; type declarations
131 | :defstar
132 |
133 | ;; iteration
134 | :for
135 | :trivial-do
136 |
137 | :cmd
138 | :serapeum
139 | :shlex
140 |
141 | :function-cache ;; memoization
142 |
143 | ;; tests
144 | :fiveam
145 |
146 | :which
147 |
148 | ;;;
149 | ;;; Debugging, developer utilities.
150 | ;;;
151 | :log4cl
152 | :printv
153 | :repl-utilities ;; see readme, summary, doc, package-apropos, trace-package etc
154 |
155 | ;;;
156 | ;;; User helpers.
157 | ;;; ;TODO: we don't want these dependencies when we build a binary.
158 | ;;;
159 | :named-readtables
160 | :quicksearch ;; search on GitHub, Cliki, Quickdocs.
161 | )
162 | :components ((:module "src/more-common-names"
163 | :components
164 | ((:file "names")))
165 | (:module "src"
166 | :components
167 | ((:file "packages")
168 | (:file "json-pointer-minus")
169 | (:file "ciel")
170 | (:file "csv")
171 | (:file "gui")))
172 | (:file "utils")
173 | (:module "src/more-docstrings"
174 | :components
175 | ((:file "docstrings"))))
176 | )
177 |
178 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
179 | ;;; Sub-system for the terminal REPL.
180 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
181 | (asdf:defsystem "ciel/repl"
182 | :description "readline REPL for CIEL with quality of life improvements."
183 | :depends-on (;; :ciel ;; let's avoid, it could run side effects twice (like a defparameter set then reset).
184 | ;; deps
185 | :cl-readline
186 | :lisp-critic ;; it would be nice to integrate it with Slime.
187 | :magic-ed)
188 | :components ((:file "repl")
189 | (:file "utils")
190 | (:file "scripting")
191 | (:file "repl-utils")
192 |
193 | ;; I define them here, for good practice (for me),
194 | ;; but I don't use them.
195 | ;; static-file is important, otherwise the scripts would be run.
196 | (:module "src/scripts"
197 | :components
198 | ((:static-file "quicksearch")
199 | (:static-file "simpleHTTPserver")))
200 | )
201 |
202 | :build-operation "program-op"
203 | :build-operation "program-op"
204 | :build-pathname "ciel"
205 | :entry-point "ciel::main")
206 |
207 | ;;; This defines ciel.asd. It is enough to quickload CIEL.
208 | ;;; But to build a binary,
209 | ;;; see build-config.lisp for extra config.
210 |
211 | ;; build a smaller executable with SBCL's core compression:
212 | ;; from 119MB to 28MB, however startup time increases from 0.02 to 0.35s (noticeable).
213 | #+sb-core-compression
214 | (defmethod asdf:perform ((o asdf:image-op) (c asdf:system))
215 | (uiop:dump-image (asdf:output-file o c) :executable t :compression t))
216 |
--------------------------------------------------------------------------------
/docs/FAQ.md:
--------------------------------------------------------------------------------
1 | About CIEL
2 | ==========
3 |
4 | Is CIEL yet another language re-design?
5 | ---------------------------------------
6 |
7 | Absolutely not. CIEL is plain Common Lisp. We don't redefine the semantics of the language. CIEL is a collection of useful libraries, shipped as one Quicklisp meta-library, a core image and an executable.
8 |
9 | Is CIEL a standard library?
10 | ---------------------------
11 |
12 | No, we can't say that. We ship useful libraries written by a variety of people, including ourselves, and we make them available to you so you don't have to spend time looking for them, choosing them, installing them and importing them. We use the same libraries that every other lisper can find on Quicklisp. We provide a core image and a ready-to-use REPL to make on-boarding even easier.
13 |
14 | If I use CIEL, do I learn Common Lisp?
15 | --------------------------------------
16 |
17 | Yes you do. And in addition you'll be acquainted to useful and often popular third-party libraries and utilities. They are all referenced on our website.
18 |
19 | How do I switch from CIEL to plain Common Lisp?
20 | -----------------------------------------------
21 |
22 | You will have to know what external libraries you are using. Mainly, by reading CIEL's documentation, or by using your editor's "go to definition" feature (`M-.` in Slime) to find out. You'll have to use the `cl` or `cl-user` package instead of `ciel-user`, and you'll have to declare your dependencies yourself in your project's `.asd` file.
23 |
24 | Eventually, there could be a script that does that for you.
25 |
26 | Is CIEL as fast as Common Lisp?
27 | -------------------------------
28 |
29 | In general, yes. Only some functions are more generic, thus slower, than default Common Lisp. For example, `access`. That is more the case if you use `generic-ciel`.
30 |
31 | Is CIEL stable?
32 | ---------------
33 |
34 | No. At least not in the Common Lisp sense of stability, which means "very stable". CIEL is as stable as the set of the libraries it includes. We have solutions to improve stability, so this is an open question (using our own Quicklisp distribution, redifining a symbol in case of an upstream change for backwards compatibility,…).
35 |
36 | Generally though, the ecosystem is quite conservative. We saw deprecation warnings staying for 12 years.
37 |
38 | Who is CIEL for?
39 | ----------------
40 |
41 | CIEL is for everybody who wants to discover Common Lisp, or for more experienced lispers who want to have a batteries-included Common Lisp distribution at hand.
42 |
43 | I am a seasoned lisper, why should I care?
44 | ------------------------------------------
45 |
46 | You must regularly hear that "getting started with Common Lisp is hard", "Common Lisp is full of quirks", "finding what one needs is difficult", etc. CIEL is an attempt to ease on-boarding, and getting rid off these (legitimate) complaints.
47 |
48 | You can test and discover new libraries.
49 |
50 | You can show CIEL to your non-lispers friends and colleagues, without saying embarrassing things like: "Just install rlwrap". "To join strings, use format's ~{ @ : }". "Yeah, there's parse-integer but not parse-float, just install it". "To see what's in a hash-table, I'll give you a snippet". etc.
51 |
52 | CIEL is bloated. How can I build a lighter one?
53 | -----------------------------------------------
54 |
55 | First and foremost, if you think there is room for a lighter CIEL
56 | package, come discuss in the Github issues. We can maybe create and
57 | independent package with lighter dependencies.
58 |
59 | Otherwise, you can have a look to this [core-dumper](https://gitlab.com/ambrevar/lisp-repl-core-dumper) tool.
60 |
61 |
62 | What is CIEL for?
63 | -----------------
64 |
65 | Please see the project's homepage, and write to us if it is not clear enough!
66 |
67 | ## About CIEL scripting
68 |
69 | ### Is CIEL like Babashka for Clojure?
70 |
71 | Babashka is a popular Clojure tool that is:
72 |
73 | - a fast-starting scripting environment
74 | - a standalone binary
75 | - a collection of useful built-in libraries
76 |
77 | So, it looks like it is.
78 |
79 | Babashka was made possible thanks to the GraalVM Native Image, a
80 | technical breakthrough on the JVM world. Without it, they wouldn't
81 | have a fast-starting scripting environment. Common Lispers on the
82 | contrary always could build standalone binaries, with Lisp sources
83 | compiled to machine code. So these "scripting" capabilities are not a
84 | surprise. CIEL scripting only makes it very easy to run and share
85 | Common Lisp scripts (with batteries included).
86 |
87 | ### Does CIEL scripting replace Roswell?
88 |
89 | Roswell does a lot more than scripting, especially it allows to easily
90 | install various Common Lisp implementations.
91 |
92 | It makes it easy to share programs, we just have to run
93 | `ros install github-handle/software-name`.
94 |
95 | However we find easier and faster to install and run a CIEL script,
96 | since CIEL avoids compilation times, thanks to it including various
97 | libraries out of the box.
98 |
99 | At the time of writing, Roswell does something (or many things) more. It allows to
100 | [build images and executables](https://github.com/roswell/roswell/wiki/Building-images-and-executables),
101 | and it even provides a few interesting options, like options to reduce the binary size.
102 |
103 | ### What about cl-launch?
104 |
105 | [cl-launch](https://www.cliki.net/cl-launch) is supposed to help for
106 | scripting. Because of its bad documentation, I have difficulties
107 | seeing what it does and how to use it. It can maybe be helpful, but it
108 | won't give you batteries included like CIEL does.
109 |
110 | ### Is that all the scripting options available for Common Lisp?
111 |
112 | Of course not. For one, implementations like SBCL have the `--script` and `--load` flags.
113 |
114 | You can use a shebang line with them too. Here's one:
115 |
116 | ```
117 | #|
118 | exec sbcl --load "$0" --eval "(main)" --quit --end-toplevel-options "${@:1}"
119 | |#
120 | ....
121 | (defun main ()
122 | ...)
123 | ```
124 |
125 | You can make the script executable with `chmod +x` and run it.
126 |
127 | Here's how it works. Lines starting by a `#` are shell comments, so
128 | the first line is ignored. Then `sbcl` is run and the Lisp process
129 | takes over. The remaining lines are not read by the shell. `sbcl`
130 | loads this same file (`$0`) and calls the `(main)` function. In Lisp,
131 | lines in-between `#| … |#` are comments, so the `exec` line is
132 | ignored. Command line options are passed on to the Lisp
133 | process. Thanks to the main function, we can also load this file
134 | interactively on our editor, we don't have top-level code that will be
135 | run and have side effects.
136 |
137 | This works fine, but everytime you'll want to use third-party
138 | libraries, you'll notice the time it takes to load them. In CIEL, many
139 | are built-in, so your script starts up (very) fast.
140 |
141 | Also, our shebang line is easier to type:
142 |
143 | ```
144 | #!/usr/bin/env ciel
145 | ```
146 |
147 | See other solutions and attempts for scripting in CL on [awesome-cl#scripting](https://github.com/CodyReichert/awesome-cl#scripting).
148 |
149 | ### But writing Lisp code on the terminal is not fun :(
150 |
151 | I find it fun, but don't write big one-liners to feed to `--eval` ;)
152 | You can write your CIEL scripts using your favourite editor setup.
153 |
154 | Also, Common Lisp strings only accept double quotes, so use single quotes for the outter eval expression, and double quotes inside.
155 |
156 | Anyways, CIEL scripting doesn't replace a good editor setup, where you
157 | can have all the praised [image-based interactivity](https://www.youtube.com/watch?v=jBBS4FeY7XM)
158 | a good Lisp provides.
159 |
160 | Also, once you have your Common Lisp development environment in place,
161 | you can build your own standalone binaries, with or without relying on
162 | the `:ciel` library.
163 |
164 |
165 |
166 |
167 | About Common Lisp
168 | =================
169 |
170 | What is Common Lisp good for, really?
171 | -------------------------------------
172 |
173 | We have a famous quote for this question:
174 |
175 | > Please don't assume Lisp is only useful for Animation and Graphics, AI, Bioinformatics, B2B and Ecommerce, Data Mining, EDA/Semiconductor applications, Expert Systems, Finance, Intelligent Agents, Knowledge Management, Mechanical CAD, Modeling and Simulation, Natural Language, Optimization, Research, Risk Analysis, Scheduling, Telecom, and Web Authoring just because these are the only things they happened to list.
176 | >
177 | > Kent Pitman
178 |
179 | Will I get hit by the Lisp curse?
180 | ---------------------------------
181 |
182 | The only, very serious Lisp curse we know, is that once you taste Lisp, all other languages become insipid. CIEL brings you higher in the sky, at a higher risk. Sorry!
183 |
--------------------------------------------------------------------------------
/src/more-docstrings/docstrings.lisp:
--------------------------------------------------------------------------------
1 | (uiop:define-package :more-docstrings
2 | (:use :cl))
3 |
4 | (in-package :more-docstrings)
5 |
6 | #|
7 | Add more documentation and add examples to the docstrings.
8 |
9 | Now, when we read a function documentation from our editor or with the
10 | built-in `documentation` function, we can learn more about it, and see
11 | examples. We have less the need to reach for external resources.
12 |
13 | The goal is still to ease the first contact of newcomers with CL.
14 | For example, give examples on how to use MAP, MAPCAR, MAPCAN.
15 |
16 | XXX: gotchas
17 |
18 | - if we quickload :ciel twice, the docstrings
19 | are appended twice too :S The hash-table cache doesn't help in that
20 | case.
21 | - we are modifying the symbols in the :cl package, not the ones in :ciel.
22 |
23 | |#
24 |
25 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
26 | ;;; One function to do the job:
27 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
28 |
29 | (defvar *docstrings-cache* (make-hash-table)
30 | "Cache the original docstring of functions and macros we are augmenting.
31 | Mainly to ease our tests at the REPL.")
32 |
33 | (defun documentation-with-cache (symbol &optional (doc-type 'function))
34 | (let ((cached (gethash symbol *docstrings-cache*)))
35 | (if cached
36 | cached
37 |
38 | (let ((doc (documentation symbol doc-type)))
39 | (setf (gethash symbol *docstrings-cache*)
40 | ;; don't store a NIL docstring.
41 | (or doc ""))
42 | doc))))
43 |
44 | (defun docstring-append (symbol s &optional (doc-type 'function))
45 | "Add S to the docstring of SYMBOL (to designate a function or a macro).
46 | DOC-TYPE is the required argument of DOCUMENTATION, by default 'function (for functions and macros), otherwise use 'variable."
47 | (let ((doc (documentation-with-cache symbol doc-type)))
48 | (setf (documentation symbol doc-type)
49 | (str:concat doc s))))
50 |
51 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
52 | ;;; Now use it.
53 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
54 |
55 | ;;; Variables
56 |
57 | (docstring-append '*default-pathname-defaults* "
58 |
59 | An implementation-dependent pathname, typically in the working directory that was current when Common Lisp was started up.
60 |
61 | Read more:
62 |
63 | - https://cl-community-spec.github.io/pages/002adefault_002dpathname_002ddefaults_002a.html"
64 | 'variable)
65 |
66 | ;;; mapcar
67 | (docstring-append 'mapcar "
68 |
69 | For example:
70 |
71 | (mapcar #'+ '(1 2 3) '(10 20 30)) ;; => (11 22 33)
72 |
73 | (mapcar (lambda (x)
74 | (format t \"~a is ~R~&\" x x))
75 | '(1 2 3))
76 | ;; =>
77 | 1 is one
78 | 2 is two
79 | 3 is three
80 | (NIL NIL NIL)
81 | ")
82 |
83 |
84 | ;;; mapcan
85 | (docstring-append 'mapcan "
86 |
87 | NCONC concatenates lists destructively.")
88 |
89 |
90 | ;;; sort
91 | (docstring-append 'sort "
92 |
93 | Since SORT is destructive, use COPY-LIST:
94 |
95 | (setq mylist (list 1 3 2))
96 | (sort (copy-list mylist) #'<)
97 |
98 | See also STABLE-SORT.")
99 |
100 |
101 | ;;; loop
102 | (docstring-append 'loop "
103 | The basic LOOP structure is
104 |
105 | (loop for x in (list x y z)
106 | do …)
107 |
108 | \"do\" is for side effects.
109 |
110 | Use \"collect\" to return results:
111 |
112 | (loop for x in (list 1 2 3)
113 | collect (* x 10))
114 |
115 | To iterate over arrays, use \"across\" instead of \"in\".
116 |
117 | To iterate over hash-tables… try MAPHASH first :D
118 |
119 | For many examples, see the CL Cookbook:
120 | https://lispcookbook.github.io/cl-cookbook/iteration.html")
121 |
122 |
123 | ;;; maphash
124 | (docstring-append 'maphash "
125 |
126 | Example:
127 |
128 | (maphash (lambda (key value)
129 | (format t \"key is: ~a, value is: ~a~&\" key value))
130 | (dict :a 'one))
131 | ;; => key is: A, value is: ONE")
132 |
133 |
134 | ;;; defun
135 | (docstring-append 'defun "
136 |
137 | Example:
138 |
139 | (defun hello (name)
140 | \"Say \\\"hello\\\" to NAME.\"
141 | (format t \"Hello ~a!\" name))
142 |
143 | Define named parameters with &key:
144 |
145 | (defun hello (name &key lisper)
146 | ...)
147 |
148 | and use it like so: (hello \"you\" :lisper t)
149 |
150 | Key parameters are NIL by default. Give them another default value like this:
151 |
152 | (defun hello (name &key (lisper t))
153 | ...)
154 |
155 | Read more:
156 | https://gigamonkeys.com/book/functions.html
157 | https://lispcookbook.github.io/cl-cookbook/functions.html")
158 |
159 |
160 | ;;; defmacro
161 | (docstring-append 'defmacro "Macros operate on code, which they see as lists of lists of symbols.
162 |
163 | Macros, unlike functions, do not evaluate their arguments. They
164 | expand (at compile time) into another piece of code, that will
165 | eventually be evaluated.
166 |
167 | First rule for macros: don't write a macro when a function can do.
168 |
169 | Example macros: DEFUN LOOP SETF WITH-OPEN-FILE
170 |
171 | See also: QUOTE BACKQUOTE GENSYM MACROEXPAND
172 |
173 | Read more:
174 | https://lispcookbook.github.io/cl-cookbook/macros.html
175 | https://gigamonkeys.com/book/macros-standard-control-constructs.html
176 | https://www.youtube.com/watch?v=ygKXeLKhiTI Little bits of Lisp video
177 | ")
178 |
179 | ;;; defclass
180 | (docstring-append 'defclass "The macro defclass defines a new named class. It returns the new class object as its result.
181 |
182 | Example:
183 |
184 | (defclass living-being () ())
185 |
186 | (defclass person (living-being)
187 | ((name
188 | :initarg :name
189 | :initform \"\"
190 | :accessor name)
191 | (lisper
192 | :initarg :lisper
193 | :initform nil
194 | :accessor lisper
195 | :documentation \"Set to non-nil if this person fancies Lisp.\")))
196 |
197 | Slots are unbound by default, here we prefer them to be the empty string and nil.
198 |
199 | An :accessor creates a generic method. You can have the same accessor name in different classes.
200 |
201 | Create an instance of that class with MAKE-INSTANCE:
202 |
203 | (make-instance 'person :name \"Alice\" :lisper t)
204 |
205 | Define how to pretty-print an object with PRINT-OBJECT.
206 |
207 | After we change a class definition (slots are modified, added or removed), we can control how an object is updated with UPDATE-INSTANCE-FOR-REDEFINED-CLASS.
208 |
209 | Read more:
210 | https://lispcookbook.github.io/cl-cookbook/clos.html
211 | https://cl-community-spec.github.io/pages/defclass.html
212 | ")
213 |
214 | ;;; to be continued.
215 |
216 | (docstring-append 'print-object "The generic function print-object writes the printed representation of object to stream. The function print-object is called by the Lisp printer; it should not be called by the user.
217 |
218 | Example:
219 |
220 | (defmethod print-object ((obj person) stream)
221 | (print-unreadable-object (obj stream :type t :identity t)
222 | (with-slots (name lisper) obj
223 | (format stream \"~a, lisper: ~a\" name lisper))))
224 |
225 | (make-instance 'person :name \"Alice\")
226 | ;; =>
227 | #
228 | (1) (2) (3)
229 | 1 tells the reader that this object can't be read back in
230 | 2 is the object type
231 | 3 is the object identity (address).
232 |
233 | Read more:
234 | https://cl-community-spec.github.io/pages/print_002dobject.html
235 | https://lispcookbook.github.io/cl-cookbook/clos.html#pretty-printing
236 | ")
237 |
238 | (docstring-append 'defstruct "
239 |
240 | Example:
241 |
242 | (defstruct person
243 | name age)
244 |
245 | Creates the `make-person' constructor function, the `person-p' predicate as well as the `person-name' and `person-age' setf-able functions:
246 |
247 | (person-name (make-person :name \"lisper\"))
248 | ;; => \"lisper\"
249 |
250 | Read more:
251 |
252 | - https://lispcookbook.github.io/cl-cookbook/data-structures.html#structures
253 | - https://cl-community-spec.github.io/pages/defstruct.html")
254 |
255 | (docstring-append 'defgeneric "
256 |
257 | A generic function is a lisp function which is associated
258 | with a set of methods and dispatches them when it's invoked. All
259 | the methods with the same function name belong to the same generic
260 | function.
261 |
262 | The `defgeneric` form defines the generic function. If we write a
263 | `defmethod` without a corresponding `defgeneric`, a generic function
264 | is automatically created.
265 |
266 | Example:
267 |
268 | (defgeneric greet (obj)
269 | (:documentation \"says hi\")
270 | (:method (obj)
271 | (format t \"Hi\")))
272 |
273 | ")
274 |
275 | (docstring-append 'find "
276 | Search for ITEM in SEQUENCE, return ITEM.
277 |
278 | Example:
279 |
280 | (find 20 '(10 20 30)) ;; => 20
281 | (find \"foo\" '(\"abc\" \"foo\") :test #'string-equal) ;; => \"foo\"
282 |
283 | See also: `find-if', `position', `search', `index', `elt'…
284 |
285 | Read more:
286 |
287 | - https://cl-community-spec.github.io/pages/find.html
288 | - https://lispcookbook.github.io/cl-cookbook/data-structures.html")
289 |
290 | (docstring-append 'with-open-file "
291 | Example:
292 |
293 | write to a file:
294 |
295 | (with-open-file (f \"/path/to/file.txt\" :direction :output
296 | :if-exists :supersede
297 | :if-does-not-exist :create)
298 | (write-sequence \"hello file\" f))
299 |
300 | This binds a stream to the `f' variable and we write content to it.
301 |
302 | You can read files with :direction :input as well as UIOP: uiop:read-file-string, uiop:read-file-lines etc.
303 |
304 | :if-exists options are :error :supersede :append :rename and NIL (this binds the stream variable to NIL).
305 |
306 | :if-does-not-exist options are :error :create and NIL (this binds the stream variable to NIL).
307 |
308 |
309 | Read more:
310 |
311 | - https://lispcookbook.github.io/cl-cookbook/files.html
312 | - https://cl-community-spec.github.io/pages/with_002dopen_002dfile.html
313 | ")
314 |
315 | (docstring-append 'round "
316 |
317 | See also:
318 |
319 | - `fround', that returns the rounded value as a float
320 | - `ceiling', `floor' and `truncate' (and their f… equivalent).
321 |
322 | Read more:
323 |
324 | - https://lispcookbook.github.io/cl-cookbook/numbers.html
325 | - https://cl-community-spec.github.io/pages/floor.html")
326 |
327 | #+ciel
328 | (docstring-append 'function-cache:defcached "
329 |
330 | Example:
331 |
332 | (defcached (foo :timeout 10) (arg)
333 | (sleep 3)
334 | arg)
335 |
336 | The functions's result is cached for 10 seconds, for the given argument. A second call returns immediately.")
337 |
--------------------------------------------------------------------------------
/docs/install.md:
--------------------------------------------------------------------------------
1 |
2 | # Install
3 |
4 | CIEL can be used in two different roles:
5 |
6 | - As a library that you can load into any Common Lisp implementation.
7 | - As a binary based on `sbcl`, which is a command-line tool for use in the terminal or in shell scripts. It provides a more feature-rich REPL than standard `sbcl`, and a much faster startup time than starting `sbcl` and then loading the CIEL library.
8 |
9 | If you use a Lisp development environment, such as Emacs with Slime, you should opt for the library rather than the binary. To get the same fast startup time, you can use a prebuilt core image, as we will explain below.
10 |
11 | In the following, we will explain how to install the library, the binary, and the prebuilt core image, for various common SBCL setups.
12 |
13 | ## Download a prebuilt binary.
14 |
15 | To download a CIEL binary:
16 |
17 | - check our releases on https://github.com/ciel-lang/CIEL/releases/
18 | - we provide a binary from a CI for some systems: go to
19 | , download the latest
20 | artifacts, unzip the `ciel-v0-{platform}.zip` archive and run `ciel-v0-{platform}/ciel`.
21 | - if you use the [Guix](https://guix.gnu.org/) package manager, install package `sbcl-ciel-repl`.
22 |
23 | CIEL is currently built for the following platforms:
24 |
25 | | Platform | System Version (release date) |
26 | |----------|-------------------------------|
27 | | debian | Debian Buster (2019) |
28 | | void | Void Linux glibc (2023-05), using [cinerion's Docker image](https://github.com/cinerion/sbcl-voidlinux-docker) |
29 |
30 | Start it with `./ciel` (adapt the path if you put the binary somewhere else).
31 |
32 | With no arguments, you enter CIEL's terminal REPL.
33 |
34 | You can give a CIEL script as first argument, or call a built-in one. See the scripting section.
35 |
36 | ## Run the binary in a Docker container
37 |
38 | We have a Dockerfile.
39 |
40 | Build your CIEL image:
41 |
42 | docker build -t ciel .
43 |
44 | The executable is built in `/usr/local/bin/ciel` of the Docker image.
45 |
46 | Get a CIEL REPL:
47 |
48 | docker run --rm -it ciel /usr/local/bin/ciel
49 |
50 | Run a script on your filesystem:
51 |
52 | docker run --rm -it ciel /usr/local/bin/ciel path/to/your/lisp/script.lisp
53 |
54 | Run a built-in script:
55 |
56 | docker run --rm -it ciel /usr/local/bin/ciel -s simpleHTTPserver
57 |
58 | So, save you some typing with a shell alias:
59 |
60 | alias ciel="sudo docker run --rm -it ciel /usr/local/bin/ciel"
61 |
62 | ## Install CIEL as a library
63 |
64 | You can install and CIEL like any other Common Lisp library, but you must make sure to also get all of its dependencies, a task that is best left to a package manager.
65 |
66 | ### Quicklisp
67 |
68 | CIEL is not on Quicklisp yet, but it is on [Ultralisp](https://ultralisp.org).
69 |
70 | So, either clone this repository:
71 |
72 | git clone https://github.com/ciel-lang/CIEL ~/quicklisp/local-projects/CIEL
73 |
74 | or install the Ultralisp distribution and pull the library from there:
75 |
76 | ```lisp
77 | (ql-dist:install-dist "http://dist.ultralisp.org/" :prompt nil)
78 | ```
79 |
80 | #### Install our Lisp dependencies [MANDATORY]
81 |
82 | Even if you have a Lisp setup with Quicklisp installed, the current
83 | distribution of Quicklisp is quite old (as of August, 2024) and you
84 | need to pull recent dependencies.
85 |
86 | We'll clone the required ones into your `~/quicklisp/local-projects/`.
87 |
88 | make ql-deps
89 |
90 | Other tools exist for this (Qlot, ocicl…), we are just not using them yet.
91 |
92 | #### Loading CIEL with Quicklisp
93 |
94 | Now, in both cases, you can load the `ciel.asd` file (with `asdf:load-asd`
95 | or `C-c C-k` in Slime) or quickload "ciel":
96 |
97 | ```lisp
98 | (ql:quickload "ciel")
99 | ```
100 |
101 | be sure to enter the `ciel-user` package:
102 |
103 | ```lisp
104 | (in-package :ciel-user)
105 | ```
106 | you now have access to all CIEL's packages and functions.
107 |
108 | ### Guix
109 |
110 | CIEL is available via the [Guix](https://guix.gnu.org/) package manager, as a source code package (`cl-ciel`) or precompiled for SBCL (`sbcl-ciel`) and ECL (`ecl-ciel`). You have to add Lisp itself (package `sbcl` or `ecl`), and any other Lisp library you may want to use.
111 |
112 | In Lisp, do
113 | ```lisp
114 | (require "asdf")
115 | (asdf:load-system :ciel)
116 | (in-package :ciel-user)
117 | ```
118 |
119 | Alternatively, or in addition, you can install `sbcl-ciel:image`, which contains a prebuilt core image under `bin/ciel.image`. It is executable, so you can run it in place of `sbcl`, or you can load it from the `sbcl` command line:
120 |
121 | ```
122 | sbcl --core $(GUIX_PROFILE)/bin/ciel.image
123 | ```
124 |
125 | In either case, you get a Lisp environment with CIEL preloaded, so all you have to do is
126 |
127 | ```lisp
128 | (in-package :ciel-user)
129 | ```
130 |
131 |
132 | ## Using CIEL as a library in your Lisp code
133 |
134 | To use it in your project, create a package and "use" `ciel` in addition
135 | to `cl`:
136 |
137 | ```lisp
138 | (defpackage yourpackage
139 | (:use :cl :ciel))
140 | ```
141 |
142 | Alternatively, you can use `generic-ciel`, based on
143 | [generic-cl](https://github.com/alex-gutev/generic-cl/) (warn:
144 | generic-ciel is less tested at the moment).
145 |
146 | ```lisp
147 | (defpackage yourpackage
148 | (:use :cl :generic-ciel))
149 | ```
150 |
151 | `generic-cl` allows you to define `+` or `equalp` methods for your own
152 | objects (and more).
153 |
154 | # Building CIEL binaries and core images
155 |
156 | To build CIEL, both the binary and the core image, you need a couple
157 | system dependencies and you have to check a couple things on the side
158 | of Lisp before proceeding.
159 |
160 | ## Dependencies
161 |
162 | ### System dependencies
163 |
164 | You will probably need the following system dependencies (names for a
165 | Debian Bullseye system):
166 |
167 | zlib1g-dev # from deploy for SBCL < 2.2.6
168 |
169 | If your SBCL version is >= 2.2.6 you might want to use the more
170 | performant `libzstd-dev` library instead of `zlib1g-dev`.
171 |
172 | libzstd-dev # from deploy for SBCL >= 2.2.6
173 |
174 | On Linux:
175 |
176 | inotify-tools
177 |
178 | On MacOS:
179 |
180 | fsevent
181 |
182 | You can run: `make debian-deps` or `make macos-deps`.
183 |
184 |
185 | ### ASDF >= 3.3.4 (local-nicknames)
186 |
187 | ASDF is the de-facto system definition facility of Common Lisp, that
188 | lets you define your system's metadata (author, dependencies, sources,
189 | modules…).
190 |
191 | Please ensure that you have ASDF >= 3.3.4.
192 |
193 | It is for instance not the case with SBCL 2.2.9.
194 |
195 | Ask the version with `(asdf:asdf-version)` on a Lisp REPL, or with
196 | this one-liner from a terminal:
197 |
198 | $ sbcl --eval '(and (print (asdf:asdf-version)) (quit))'
199 |
200 | Here's a one-liner to update ASDF:
201 |
202 | $ mkdir ~/common-lisp/
203 | $ ( cd ~/common-lisp/ && wget https://asdf.common-lisp.dev/archives/asdf-3.3.5.tar.gz && tar -xvf asdf-3.3.5.tar.gz && mv asdf-3.3.5 asdf )
204 |
205 |
206 | ### Install Quicklisp
207 |
208 | To build CIEL on your machine, you need the [Quicklisp library
209 | manager](https://quicklisp.org/beta/). Quicklisp downloads and
210 | installs a library and its dependencies on your machine. It's very
211 | slick, we can install everything from the REPL without restarting our
212 | Lisp process. It follows a "distrubution" approach, think Debian
213 | releases, where libraries are tested to load.
214 |
215 | It isn't the only library manager nowadays. See [https://github.com/CodyReichert/awesome-cl#library-manager](https://github.com/CodyReichert/awesome-cl#library-manager).
216 |
217 | Install it:
218 |
219 | ```sh
220 | curl -O https://beta.quicklisp.org/quicklisp.lisp
221 | sbcl --load quicklisp.lisp --eval "(quicklisp-quickstart:install)" --quit
222 | sbcl --load ~/quicklisp/setup.lisp --eval "(ql:add-to-init-file)" --quit
223 | ```
224 |
225 | It creates a `~/quicklisp/` directory. Read its installation instructions to know more.
226 |
227 | See the section above for loading CIEL via Quicklisp for how to make sure you have all the required dependencies.
228 |
229 | ### Run the build procedure
230 |
231 | You need the dependencies above: Quicklisp, a good ASDF version, our up-to-date Lisp dependencies.
232 |
233 | To build CIEL's binary, use:
234 |
235 | $ make build
236 |
237 | This creates a `ciel` binary in the current directory.
238 |
239 | To create a Lisp image:
240 |
241 | $ make image
242 | # or
243 | $ sbcl --load build-image.lisp
244 |
245 | This creates the `ciel-core` Lisp image.
246 |
247 | Unlike binaries, we cannot distribute core images. They depend on the machine they was were built on.
248 |
249 | ### Using the core image
250 |
251 | The way we use a core image is to load it at startup like this:
252 |
253 | sbcl --core ciel-core --eval '(in-package :ciel-user)'
254 |
255 | It loads fast and you have all CIEL libraries and goodies at your disposal.
256 |
257 | Then you have to configure your editor, like Slime, to have the choice of the Lisp image to
258 | start. See below.
259 |
260 | ### Core image: configure your editor
261 |
262 | The advantage of a core image is that it loads instantly, faster than
263 | a `(ql:quickload "ciel")`. We'll ask our editor to start SBCL with our
264 | CIEL core image.
265 |
266 | We'll configure SLIME for [multiple Lisps](https://common-lisp.net/project/slime/doc/html/Multiple-Lisps.html#Multiple-Lisps).
267 |
268 | You need to add this to your Emacs init file:
269 |
270 | ```lisp
271 | (setq slime-lisp-implementations
272 | `((sbcl ("sbcl" "--dynamic-space-size" "2000")) ;; default. Adapt if needed.
273 | (ciel-sbcl ("sbcl" "--core" "/path/to/ciel/ciel-core" "--eval" "(in-package :ciel-user)"))))
274 | (setq slime-default-lisp 'ciel-sbcl)
275 | ```
276 |
277 | and start a Lisp process with `M-x slime`.
278 |
279 | If you didn't set `ciel-sbcl` as the default, then start the Lisp
280 | process with `M-- M-x slime` (alt-minus prefix), and choose
281 | `ciel-sbcl`. You can start more than one Lisp process from SLIME.
282 |
283 | The Lisp process should start instantly, as fast as the default SBCL,
284 | you won't wait for the quicklisp libraries to load.
285 |
286 | ## Post-installation tips
287 |
288 | ### Zsh completion
289 |
290 | zsh users can add a one-liner to their .zshrc to add TAB-completion for CIEL arguments:
291 |
292 | compdef _gnu_generic ciel
293 |
294 | this `_gnu_generic` zsh helper scrapes the output of `ciel --help` to
295 | offer you the completion. It shows like this, after a `ciel -` (with a dash):
296 |
297 | ```sh
298 | $ ciel -
299 | --eval -e -- eval a lisp form
300 | --help -- display usage information and exit
301 | --noinform -- Don't print the welcome banner.
302 | --no-userinit -- Don't load the ~/.cielrc init file at start-up (for the CIEL terminal
303 | --script -s -- run a lisp file
304 | --scripts -z -- list available scripts.
305 | --verbose -v -- verbosity level (default- 0)
306 | --version -- display version and exit
307 | ```
308 |
309 | If anyone got to get this *and* the completion of ciel scripts, please ping us.
310 |
--------------------------------------------------------------------------------
/docs/serapeum.md:
--------------------------------------------------------------------------------
1 | # Symbols imported from SERAPEUM for sequences and hashtables
2 |
3 |
4 | ## ASSORT
5 |
6 | ARGLIST: `(seq &key (key #'identity) (test #'eql) (start 0) end hash &aux
7 | (orig-test test))`
8 |
9 | FUNCTION: Return SEQ assorted by KEY.
10 |
11 | (assort (iota 10)
12 | :key (lambda (n) (mod n 3)))
13 | => '((0 3 6 9) (1 4 7) (2 5 8))
14 |
15 | Groups are ordered as encountered. This property means you could, in
16 | principle, use `assort' to implement `remove-duplicates' by taking the
17 | first element of each group:
18 |
19 | (mapcar #'first (assort list))
20 | ≡ (remove-duplicates list :from-end t)
21 |
22 | However, if TEST is ambiguous (a partial order), and an element could
23 | qualify as a member of more than one group, then it is not guaranteed
24 | that it will end up in the leftmost group that it could be a member
25 | of.
26 |
27 | (assort '(1 2 1 2 1 2) :test #'<=)
28 | => '((1 1) (2 2 1 2))
29 |
30 | The default algorithm used by `assort' is, in the worst case, O(n) in
31 | the number of groups. If HASH is specified, then a hash table is used
32 | instead. However TEST must be acceptable as the `:test' argument to
33 | `make-hash-table'.
34 |
35 | ## BATCHES
36 |
37 | ARGLIST: `(seq n &key (start 0) end even)`
38 |
39 | FUNCTION: Return SEQ in batches of N elements.
40 |
41 | (batches (iota 11) 2)
42 | => ((0 1) (2 3) (4 5) (6 7) (8 9) (10))
43 |
44 | If EVEN is non-nil, then SEQ must be evenly divisible into batches of
45 | size N, with no leftovers.
46 |
47 | ## IOTA
48 |
49 | ARGLIST: `(n &key (start 0) (step 1))`
50 |
51 | FUNCTION: Return a list of n numbers, starting from START (with numeric contagion
52 | from STEP applied), each consequtive number being the sum of the previous one
53 | and STEP. START defaults to 0 and STEP to 1.
54 |
55 | Examples:
56 |
57 | (iota 4) => (0 1 2 3)
58 | (iota 3 :start 1 :step 1.0) => (1.0 2.0 3.0)
59 | (iota 3 :start -1 :step -1/2) => (-1 -3/2 -2)
60 |
61 | ## RUNS
62 |
63 | ARGLIST: `(seq &key (start 0) end (key #'identity) (test #'eql)
64 | (count most-positive-fixnum))`
65 |
66 | FUNCTION: Return a list of runs of similar elements in SEQ.
67 | The arguments START, END, and KEY are as for `reduce'.
68 |
69 | (runs '(head tail head head tail))
70 | => '((head) (tail) (head head) (tail))
71 |
72 | The function TEST is called with the first element of the run as its
73 | first argument.
74 |
75 | (runs '(1 2 3 1 2 3) :test #'<)
76 | => ((1 2 3) (1 2 3))
77 |
78 | The COUNT argument limits how many runs are returned.
79 |
80 | (runs '(head tail tail head head tail) :count 2)
81 | => '((head) (tail tail))
82 |
83 | ## PARTITION
84 |
85 | ARGLIST: `(pred seq &key (start 0) end (key #'identity))`
86 |
87 | FUNCTION: Partition elements of SEQ into those for which PRED returns true
88 | and false.
89 |
90 | Return two values, one with each sequence.
91 |
92 | Exactly equivalent to:
93 | (values (remove-if-not predicate seq) (remove-if predicate seq))
94 | except it visits each element only once.
95 |
96 | Note that `partition` is not just `assort` with an up-or-down
97 | predicate. `assort` returns its groupings in the order they occur in
98 | the sequence; `partition` always returns the “true” elements first.
99 |
100 | (assort '(1 2 3) :key #'evenp) => ((1 3) (2))
101 | (partition #'evenp '(1 2 3)) => (2), (1 3)
102 |
103 | ## PARTITIONS
104 |
105 | ARGLIST: `(preds seq &key (start 0) end (key #'identity))`
106 |
107 | FUNCTION: Generalized version of PARTITION.
108 |
109 | PREDS is a list of predicates. For each predicate, `partitions'
110 | returns a filtered copy of SEQ. As a second value, it returns an extra
111 | sequence of the items that do not match any predicate.
112 |
113 | Items are assigned to the first predicate they match.
114 |
115 | ## SPLIT-SEQUENCE
116 |
117 | ARGLIST: `(delimiter sequence &key (start 0) (end nil) (from-end nil) (count nil)
118 | (remove-empty-subseqs nil) (test #'eql test-p) (test-not nil test-not-p)
119 | (key #'identity))`
120 |
121 | FUNCTION: Return a list of subsequences in seq delimited by delimiter.
122 | If :remove-empty-subseqs is NIL, empty subsequences will be included
123 | in the result; otherwise they will be discarded. All other keywords
124 | work analogously to those for CL:SUBSTITUTE. In particular, the
125 | behaviour of :from-end is possibly different from other versions of
126 | this function; :from-end values of NIL and T are equivalent unless
127 | :count is supplied. :count limits the number of subseqs in the main
128 | resulting list. The second return value is an index suitable as an
129 | argument to CL:SUBSEQ into the sequence indicating where processing
130 | stopped.
131 |
132 | ## COUNT-CPUS
133 |
134 | ARGLIST: `(&key (default 2) online)`
135 |
136 | FUNCTION: Try very hard to return a meaningful count of CPUs.
137 | If ONLINE is non-nil, try to return only the active CPUs.
138 |
139 | The second value is T if the number of processors could be queried,
140 | `nil' otherwise.
141 |
142 | ## DICT
143 |
144 | ARGLIST: `(&rest keys-and-values)`
145 |
146 | FUNCTION: A concise constructor for hash tables.
147 |
148 | (gethash :c (dict :a 1 :b 2 :c 3)) => 3, T
149 |
150 | By default, return an 'equal hash table containing each successive
151 | pair of keys and values from KEYS-AND-VALUES.
152 |
153 | If the number of KEYS-AND-VALUES is odd, then the first argument is
154 | understood as the test.
155 |
156 | (gethash "string" (dict "string" t)) => t
157 | (gethash "string" (dict 'eq "string" t)) => nil
158 |
159 | Note that `dict' can also be used for destructuring (with Trivia).
160 |
161 | (match (dict :x 1)
162 | ((dict :x x) x))
163 | => 1
164 |
165 | ## DO-HASH-TABLE
166 |
167 | ARGLIST: `((key value table &optional return) &body body)`
168 |
169 | FUNCTION: Iterate over hash table TABLE, in no particular order.
170 |
171 | At each iteration, a key from TABLE is bound to KEY, and the value of
172 | that key in TABLE is bound to VALUE.
173 |
174 | ## DICT*
175 |
176 | ARGLIST: `(dict &rest args)`
177 |
178 | FUNCTION: Merge new bindings into DICT.
179 | Roughly equivalent to `(merge-tables DICT (dict args...))'.
180 |
181 | ## DICTQ
182 |
183 | ARGLIST: `(&rest keys-and-values)`
184 |
185 | FUNCTION: A literal hash table.
186 | Like `dict', but the keys and values are implicitly quoted, and the
187 | hash table is inlined as a literal object.
188 |
189 | ## POPHASH
190 |
191 | ARGLIST: `(key hash-table)`
192 |
193 | FUNCTION: Lookup KEY in HASH-TABLE, return its value, and remove it.
194 |
195 | This is only a shorthand. It is not in itself thread-safe.
196 |
197 | From Zetalisp.
198 |
199 | ## SWAPHASH
200 |
201 | ARGLIST: `(key value hash-table)`
202 |
203 | FUNCTION: Set KEY and VALUE in HASH-TABLE, returning the old values of KEY.
204 |
205 | This is only a shorthand. It is not in itself thread-safe.
206 |
207 | From Zetalisp.
208 |
209 | ## HASH-FOLD
210 |
211 | ARGLIST: `(fn init hash-table)`
212 |
213 | FUNCTION: Reduce TABLE by calling FN with three values: a key from the hash
214 | table, its value, and the return value of the last call to FN. On the
215 | first call, INIT is supplied in place of the previous value.
216 |
217 | From Guile.
218 |
219 | ## MAPHASH-RETURN
220 |
221 | ARGLIST: `(fn hash-table)`
222 |
223 | FUNCTION: Like MAPHASH, but collect and return the values from FN.
224 | From Zetalisp.
225 |
226 | ## MERGE-TABLES
227 |
228 | ARGLIST: `(&rest tables)`
229 |
230 | FUNCTION: Merge TABLES, working from left to right.
231 | The resulting hash table has the same parameters as the first table.
232 |
233 | If no tables are given, an new, empty hash table is returned.
234 |
235 | If a single table is given, a copy of it is returned.
236 |
237 | If the same key is present in two tables, the value from the rightmost
238 | table is used.
239 |
240 | All of the tables being merged must have the same value for
241 | `hash-table-test'.
242 |
243 | Clojure's `merge'.
244 |
245 | ## FLIP-HASH-TABLE
246 |
247 | ARGLIST: `(table &key (test (constantly t)) (key #'identity))`
248 |
249 | FUNCTION: Return a table like TABLE, but with keys and values flipped.
250 |
251 | (gethash :y (flip-hash-table (dict :x :y)))
252 | => :x, t
253 |
254 | TEST allows you to filter which keys to set.
255 |
256 | (def number-names (dictq 1 one 2 two 3 three))
257 |
258 | (def name-numbers (flip-hash-table number-names))
259 | (def name-odd-numbers (flip-hash-table number-names :filter #'oddp))
260 |
261 | (gethash 'two name-numbers) => 2, t
262 | (gethash 'two name-odd-numbers) => nil, nil
263 |
264 | KEY allows you to transform the keys in the old hash table.
265 |
266 | (def negative-number-names (flip-hash-table number-names :key #'-))
267 | (gethash 'one negative-number-names) => -1, nil
268 |
269 | KEY defaults to `identity'.
270 |
271 | ## SET-HASH-TABLE
272 |
273 | ARGLIST: `(set &rest hash-table-args &key (test #'eql) (key #'identity) (strict t)
274 | &allow-other-keys)`
275 |
276 | FUNCTION: Return SET, a list considered as a set, as a hash table.
277 | This is the equivalent of Alexandria's `alist-hash-table' and
278 | `plist-hash-table' for a list that denotes a set.
279 |
280 | STRICT determines whether to check that the list actually is a set.
281 |
282 | The resulting hash table has the elements of SET for both its keys and
283 | values. That is, each element of SET is stored as if by
284 | (setf (gethash (key element) table) element)
285 |
286 | ## HASH-TABLE-PREDICATE
287 |
288 | ARGLIST: `(hash-table)`
289 |
290 | FUNCTION: Return a predicate for membership in HASH-TABLE.
291 | The predicate returns the same two values as `gethash', but in the
292 | opposite order.
293 |
294 | ## HASH-TABLE-FUNCTION
295 |
296 | ARGLIST: `(hash-table &key read-only strict (key-type 't) (value-type 't) strict-types)`
297 |
298 | FUNCTION: Return a function for accessing HASH-TABLE.
299 |
300 | Calling the function with a single argument is equivalent to `gethash'
301 | against a copy of HASH-TABLE at the time HASH-TABLE-FUNCTION was
302 | called.
303 |
304 | (def x (make-hash-table))
305 |
306 | (funcall (hash-table-function x) y)
307 | ≡ (gethash y x)
308 |
309 | If READ-ONLY is nil, then calling the function with two arguments is
310 | equivalent to `(setf (gethash ...))' against HASH-TABLE.
311 |
312 | If STRICT is non-nil, then the function signals an error if it is
313 | called with a key that is not present in HASH-TABLE. This applies to
314 | setting keys, as well as looking them up.
315 |
316 | The function is able to restrict what types are permitted as keys and
317 | values. If KEY-TYPE is specified, an error will be signaled if an
318 | attempt is made to get or set a key that does not satisfy KEY-TYPE. If
319 | VALUE-TYPE is specified, an error will be signaled if an attempt is
320 | made to set a value that does not satisfy VALUE-TYPE. However, the
321 | hash table provided is *not* checked to ensure that the existing
322 | pairings KEY-TYPE and VALUE-TYPE -- not unless STRICT-TYPES is also
323 | specified.
324 |
325 | ## MAKE-HASH-TABLE-FUNCTION
326 |
327 | ARGLIST: `(&rest args &key &allow-other-keys)`
328 |
329 | FUNCTION: Call `hash-table-function' on a fresh hash table.
330 | ARGS can be args to `hash-table-function' or args to
331 | `make-hash-table', as they are disjoint.
332 |
333 | ## DELETE-FROM-HASH-TABLE
334 |
335 | ARGLIST: `(table &rest keys)`
336 |
337 | FUNCTION: Return TABLE with KEYS removed (as with `remhash').
338 | Cf. `delete-from-plist' in Alexandria.
339 |
340 | ## PAIRHASH
341 |
342 | ARGLIST: `(keys data &optional hash-table)`
343 |
344 | FUNCTION: Like `pairlis', but for a hash table.
345 |
346 | Unlike `pairlis', KEYS and DATA are only required to be sequences (of
347 | the same length), not lists.
348 |
349 | By default, the hash table returned uses `eql' as its tests. If you
350 | want a different test, make the table yourself and pass it as the
351 | HASH-TABLE argument.
352 |
--------------------------------------------------------------------------------
/scripting.lisp:
--------------------------------------------------------------------------------
1 |
2 | (in-package :ciel)
3 |
4 | (defparameter *ciel-version* #.(asdf:component-version (asdf:find-system :ciel))
5 | "CIEL's version, read from the .asd.")
6 |
7 | (defparameter *scripts* (dict 'equalp)
8 | "Available scripts.
9 | Hash-table: file name (sans extension) -> file content (string).
10 | The name is case-insensitive (it's easier for typing things in the terminal).")
11 |
12 | ;; eval
13 | (defun wrap-user-code (s)
14 | "Wrap this user code to handle common conditions, such as a C-c C-c to quit gracefully."
15 | ;; But is it enough when we run a shell command?
16 | `(handler-case
17 | ,s ;; --eval takes one form only so no need of ,@
18 | (sb-sys:interactive-interrupt (c)
19 | (declare (ignore c))
20 | (format! *error-output* "Bye!~%"))
21 | (error (c)
22 | (format! *error-output* "~a" c))))
23 |
24 |
25 | (defun register-builtin-scripts ()
26 | "Find available scripts in src/scripts, register them in *SCRIPTS*.
27 | Call this before creating the CIEL binary."
28 | ;; We save the file's content as a string.
29 | ;; We will run them with LOAD (and an input stream from the string).
30 | ;;
31 | ;; Example:
32 | ;;
33 | ;; (load (make-string-input-stream (str:from-file "src/scripts/simpleHTTPserver.lisp")))
34 | (loop for file in (uiop:directory-files "src/scripts/")
35 | if (equal "lisp" (pathname-type file))
36 | do (format t "~t scripts: registering ~a~&" (pathname-name file))
37 | (setf (gethash (pathname-name file) *scripts*)
38 | (str:from-file file))))
39 |
40 | (defun run-script (name)
41 | "If NAME is registered in *SCRIPTS*, run this script."
42 | (bind (((:values content exists) (gethash name *scripts*)))
43 | (cond
44 | ((and exists (str:blankp content)
45 | (format *error-output* "uh the script ~s has no content?~&" name)))
46 | ((not exists)
47 | (format *error-output* "The script ~s was not found.~&" name))
48 | (t
49 | ;; Run it!
50 | ;; We first add a symbol in the feature list, so a script nows when it is being executed.
51 | (push :ciel ciel-user::*features*)
52 | ;; We ignore the shebang line, if there is one.
53 | ;; We can call scripts either with ciel -s or with ./script
54 | (load (maybe-ignore-shebang
55 | (make-string-input-stream content)))))))
56 |
57 | (defun top-level/command ()
58 | "Creates and returns the top-level command"
59 | (clingon:make-command
60 | :name "ciel"
61 | :description "CIEL Is an Extended Lisp. It's Common Lisp, batteries included."
62 | :version *ciel-version*
63 | :license "todo"
64 | :authors '("vindarel ")
65 | :usage (format nil "accepts optional command-line arguments.~%
66 | ~t~tWith no arguments, run the CIEL readline REPL.~%
67 | ~t~tWith a file as argument, run it as a script.~%
68 | ~t~tWith --eval / -e