+
This page is meant to provide an introduction to using Emacs as a Lisp IDE.
-
Using Emacs as an IDE
+
We divided it roughly into 2 sections: how to use Slime or Sly, and complementary information on built-in Emacs commands to work with Lisp code.
-
This page is meant to provide an introduction to using Emacs as a Lisp IDE.
-
-
Portacle is a portable and
-multi-platform CL development environment shipping Emacs, SBCL and
+
We want to bring Portacle to your attention. It is a portable and
+multi-platform CL development environment shipping Emacs, Slime, SBCL, git and
necessary extensions. It is a straightforward way to get going.

-
+
-
Why Use Emacs?
+
Why Use Emacs?
- Emacs has fantastic support for working with Lisp code
@@ -99,116 +102,29 @@ necessary extensions. It is a straightforward way to get going.
- Vast number of extensions: awesome-emacs.
-
-
-
Emacs Lisp vs Common Lisp
-
-
It isn’t necessary to use Emacs Lisp to use Emacs with Slime or Sly for Common Lisp
-
-
However learning Emacs Lisp can be useful and is similar (but different) from CL:
-
- - Dynamic scope is everywhere
- - There are no reader (or reader-related) functions
- - Does not support all the types that are supported in CL
- - Incomplete implementation of CLOS (with the add-on EIEIO package)
- - No numerical tower support
-
-
-
Some good Emacs Lisp learning resources:
-
-
-
-
-
SLIME: Superior Lisp Interaction Mode for Emacs
+
SLIME: Superior Lisp Interaction Mode for Emacs
SLIME is the goto major mode
-for CL programming.
+for CL programming. It has a lot of features that make it a powerful, integrated and very interactive development environment.
- - Pros:
-
- - Provides REPL which is hooked to implementation directly in Emacs
- - Has integrated Common Lisp debugger with Emacs interface
- - Interactive object-inspector in Emacs buffer
- - Has its own minor mode which enhances lisp-mode in many ways
- - Supports every common Common Lisp implementation
- - Readily available from MELPA
- - Actively maintained
- - Symbol completion
- - Cross-referencing
- - Can perform macroexpansions
-
-
- - Setup:
-
- - Installing it from MELPA is straightforward. Search package-list-packages for ‘slime’ and click to install. It will install itself and all dependencies.
- - Enable the desired contribs (SLIME does very little by defaults), e.g.
(slime-setup '(slime-fancy slime-quicklisp slime-asdf))
.
- - Run SLIME with
M-x slime
.
- - See also your GNU/Linux distribution for a “slime” package.
-
-
+ - it provides a REPL which is hooked to the running image, directly in Emacs,
+ - it integrates the Common Lisp debugger with an Emacs interface
+ - it provides symbol completion,
+ - code evaluation, compilation, macroexpansion
+ - cross-referencing,
+ - breaking, stepping, tracing,
+ - go to definition,
+ - online documentation,
+ - fuzzy searching functions and symbols, system names, documentation,
+ - an interactive object inspector,
+ - it supports every common Common Lisp implementation,
+ - multiple connections and multiple listener buffers (mrepl)
+ - it is readily available from MELPA
+ - it is actively maintained.
-
Check out this video tutorial ! (and the author’s channel, full of great stuff)
-
-
SLIME fancy, contrib packages and other extensions
-
-
SLIME’s functionalities live in packages and so-called contrib
-modules
-must be loaded to add further functionalities. The default
-slime-fancy
includes:
-
-
- - slime-autodoc
- - slime-c-p-c
- - slime-editing-commands
- - slime-fancy-inspector
- - slime-fancy-trace
- - slime-fontifying-fu
- - slime-fuzzy
- - slime-mdot-fu
- - slime-macrostep
- - slime-presentations
- - slime-references
- - slime-repl
- - slime-scratch
- - slime-package-fu
- - slime-trace-dialog
-
-
-
SLIME also has some nice extensions like
-Helm-SLIME which features, among
-others:
-
-
- - Fuzzy completion,
- - REPL and connection listing,
- - Fuzzy-search of the REPL history,
- - Fuzzy-search of the apropos documentation.
-
-
-
REPL interactions
-
-
From the SLIME REPL, press ,
to prompt for commands. There is completion
-over the available systems and packages. Examples:
-
-
- ,load-system
- ,reload-system
- ,in-package
- ,restart-inferior-lisp
-
-
-
and many more.
-
-
With the slime-quicklisp
contrib, you can also ,ql
to list all systems
-available for installation.
-
-
SLY: Sylvester the Cat’s Common Lisp IDE
+
SLY: Sylvester the Cat’s Common Lisp IDE
SLY is a SLIME fork that contains
the following improvements:
@@ -231,242 +147,120 @@ the following improvements:
-
Finding one’s way into Emacs’ built-in documentation
+
Sly is shipped by default in Doom Emacs.
-
Emacs comes with built-in tutorials and documentation. Moreover, it is
-a self-documented and self-discoverable editor, capable of introspection to let you
-know about the current keybindings, to let you search about function documentation,
-available variables,source code, tutorials, etc. Whenever you ask yourself questions like
-“what are the available shortcuts to do x” or “what does this
-keybinding really do”, the answer is most probably a keystroke away,
-right inside Emacs. You should learn a few keybindings to be able to
-discover Emacs with Emacs flawlessly.
+
Installing SLIME or SLY
-
The help on the topic is here:
+
Manually
+
+
On Ubuntu, SLIME is easily installed alongside Emacs and SBCL:
+
+
sudo apt install emacs slime sbcl
+
+
+
Otherwise, install SLIME by adding this code to your ~/.emacs.d/init.el
file:
+
+
(require 'package)
+(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t)
+(dolist (package '(slime))
+ (unless (package-installed-p package)
+ (package-install package)))
+(require 'slime)
+
+
+
assuming you’ve also instealled Emacs and SBCL.
+
+
Since SLIME is heavily modular and the defaults only do the bare minimum (not
+even the SLIME REPL), you might want to enable more features with
+
+
(require 'slime)
+(slime-setup '(slime-fancy slime-quicklisp slime-asdf slime-mrepl))
+
+
+
After this you can press Alt-X on your keyboard and type slime
and try Common Lisp!
+
+
(Alt-X is often written M-x
in Emacs-world.)
+
+
For more details, consult the
+documentation (also available
+as an Info page).
+
+
Now you can run SLIME with, as mentioned, M-x slime
and/or M-x slime-connect
.
+
+
See also:
-
The help keybindings start with either C-h
or F1
. Important ones are:
+
Portacle
+
+
Portacle is a portable and
+multi-platform CL development environment with which you can start
+developping in Lisp in a few clicks. Portacle includes Emacs, SBCL, Slime, git, and useful Emacs extensions (which-key, treeview…).
+
+
It is a straightforward way to get going.
+
+
Doom Emacs
+
+
Doom Emacs is a popular Emacs configuration. You can easily enable its Sly integration.
+
+
SLIME fancy and contrib packages
+
+
SLIME’s functionalities live in packages and so-called contrib
+modules
+must be loaded to add further functionalities. The afored mentioned
+slime-fancy
includes:
- C-h k <keybinding>
: what function does this keybinding call?
- C-h f <function name>
: what keybinding is linked to this function?
- C-h a <topic>
: show a list of commands whose name match the given topic. It accepts a keyword, a list of keywords or a regular expression.
- C-h i
: show the Info page, a menu of major topics.
+ - slime-autodoc
+ - slime-c-p-c
+ - slime-editing-commands
+ - slime-fancy-inspector
+ - slime-fancy-trace
+ - slime-fontifying-fu
+ - slime-fuzzy
+ - slime-mdot-fu
+ - slime-macrostep
+ - slime-presentations
+ - slime-references
+ - slime-repl
+ - slime-scratch
+ - slime-package-fu
+ - slime-trace-dialog
+ - slime-mrepl (multiple REPLs)
-
Some Emacs packages give even more help.
-
-
More help and discoverability packages
-
-
Sometimes, you start typing a key sequence but you can’t remember it
-completely. Or, you wonder what other keybindings are related. Comes
-which-key-mode. This
-packages will display all possible keybindings starting with the key(s) you just typed.
-
-
For example, I know there are useful keybindings under C-x
but I don’t remember which ones… I just type C-x
, I wait for half a second, and which-key shows all the ones available.
-
-

-
-
Just try it with C-h
too!
-
-
See also Helpful, an alternative to the built-in Emacs help that provides much more contextual information.
-
-

-
-
Learn Emacs with the built-in tutorial
-
-
Emacs ships its own tutorial. You should give it a look to learn the most important keybindings and concepts.
-
-
Call it with M-x help-with-tutorial
(where M-x
is alt-x
).
-
-
-
-
Working with Lisp Code
-
-
In this short tutorial we’ll see how to:
+
SLIME also has some nice extensions like
+Helm-SLIME which features, among
+others:
- - edit Lisp code
- - evaluate and compile Lisp code
- - search Lisp code
+ - Fuzzy completion,
+ - REPL and connection listing,
+ - Fuzzy-search of the REPL history,
+ - Fuzzy-search of the apropos documentation.
-
Packages for structured editing
+
Working with SLIME (or SLY)
-
In addition to the built-in Emacs commands, you have several packages at your disposal
-that will help to keep the parens and/or the indentation balanced.
-The list below is somewhat sorted by age of the
-extension, according to the
-history of Lisp editing:
+
One of the first things you might want to do is to compile and load some Lisp code. Use C-c C-c
on a function or C-c C-k
to compile a whole file. But that’s not all, read on.
-
- - Paredit - Paredit is a
-classic. It defines the must-have commands (move, kill, split, join
-a sexp,…).
-(visual tutorial)
- - Smartparens - Smartparens
-not only deals with parens but with everything that comes in pairs
-(html tags,…) and thus has features for non-lispy languages.
- - Lispy - Lispy reimagines Paredit
-with the goal to have the shortest bindings (mostly one key) that
-only act depending on the point position.
- - Paxedit - Paxedit adds
-commands based on the context (in a symbol, a sexp,… ) and puts
-efforts on whitespace cleanup and context refactoring.
- - Parinfer - Parinfer
-automatically fixes the parens depending on the indentation, or the
-other way round (or both !).
-
+
Note that we give function names for SLIME. They are most of the time similar with SLY.
-
We personally advice to try Parinfer and the famous Paredit, then to
-go up the list. See explanations and even more on
-Wikemacs.
-
-
-
-
Editing
-
-
Emacs has, of course, built-in commands to deal with s-expressions.
-
-
Forward/Backward/Up/Down movement and selection by s-expressions
-
-
Use C-M-f
and C-M-b
(forward-sexp
and backward-sexp
) to move
-in units of s-expressions.
-
-
Use C-M-t
to swap
-the first addition sexp and the second one. Put the cursor on the open
-parens of “(+ x” in defun c and press
-
-
Use C-M-@
to highlight an entire sexp. Then press C-M-u
to expand
-the selection “upwards” and C-M-d
to move forward down one level of
-parentheses.
-
-
Deleting s-expressions
-
-
Use C-M-k
(kill-sexp
) and C-M-backspace
(backward-kill-sexp
) (but caution: this keybinding may restart the system on GNU/Linux).
-
-
For example, if point is before (progn
(I’ll use [] as an indication where the cursor is):
-
-
(defun d ()
- (if t
- (+ 3 3)
- [](progn
- (+ 1 1)
- (if t
- (+ 2 2)
- (+ 3 3)))
- (+ 4 4)))
-
-
-
and you press C-M-k
, you get:
-
-
(defun d ()
- (if t
- (+ 3 3)
- []
- (+ 4 4)))
-
-
-
Indenting s-expressions
-
-
Indentation is automatic for Lisp forms.
-
-
Pressing TAB will indent incorrectly indented code. For example, put
-the point at the beginning of the (+ 3 3)
form and press TAB:
-
-
(progn
-(+ 3 3))
-
-
-
you correctly get
-
-
(progn
- (+ 3 3))
-
-
-
Use C-M-q
(slime-reindent-defun
) to indent the current function definition:
-
-
;; Put the cursor on the open parens of "(defun ..."
-;; and press "C-M-q" to indent the code:
-(defun e ()
-"A badly indented function."
-(let ((x 20))
-(loop for i from 0 to x
-do (loop for j from 0 below 10
-do (print j))
-(if (< i 10)
-(let ((z nil) )
-(setq z (format t "x=~d" i))
-(print z))))))
-
-;; This is the result:
-
-(defun e ()
- "A badly indented function (now correctly indented)."
- (let ((x 20))
- (loop for i from 0 to x
- do (loop for j from 0 below 10
- do (print j))
- (if (< i 10)
- (let ((z nil) )
- (setq z (format t "x=~d" i))
- (print z))))))
-
-
-
You can also select a region and call M-x indent-region
.
-
-
Support for parenthesis
-
-
Use M-(
to insert a pair of parenthesis (()
) and the same
-keybinding with a prefix argument, C-u M-(
, to enclose the
-expression in front of the cursor with a pair of parens.
-
-
For example, we start with the cursor before the first paren:
-
-
CL-USER> |(- 2 2)
-
-
-
Press C-u M-(
to enclose it with parens:
-
-
CL-USER> (|(- 2 2))
-;; now write anything.
-CL-USER> (zerop (- 2 2))
-
-
-
With a numbered prefix argument (C-u 2 M-(
), wrap around this number of s-expressions.
-
-
Additionnaly, use M-x check-parens
to spot malformed s-exps and C-c
-C-]
(slime-close-all-parens-in-sexp
) to insert the required number
-of closing parenthesis.
-
-
Code completion
+
Code completion
Use the built-in C-c TAB
to complete symbols in SLIME. You can get tooltips
with company-mode.
-

+

-
In the REPL, it’s simply TAB.
+
In the REPL, it’s simply TAB.
-
Use Emacs’ hippie-expand, bound to M-/
, to complete any string
-present in other open buffers.
+
Use Emacs’ hippie-expand, bound to M-/
, to complete any string present in other open buffers.
-
Hiding/showing code
-
-
Use C-x n n
(narrow-to-region) and C-x n w
to widen back.
-
-
See also code folding.
-
-
-
-
Insert a comment, comment a region with M-;
, adjust text with M-q
.
-
-
-
-
Evaluating and Compiling Lisp in SLIME
+
Evaluating and Compiling Lisp in SLIME
Compile the entire buffer by pressing C-c C-k
(slime-compile-and-load-file
).
@@ -474,13 +268,16 @@ present in other open buffers.
Compile a defun by putting the cursor inside it and pressing C-c C-c
(slime-compile-defun
).
+
Once you compiled some code, you can try it, for example on the REPL.
+
To evaluate rather than compile:
- evaluate the sexp before the point by putting the cursor after
its closing paren and pressing
C-x C-e
(slime-eval-last-expression
). The result is printed in the minibuffer.
- - similarly, use
C-c C-p
(slime-pprint-eval-last-expression
) to eval and pretty-print the expression before point. It shows the result in a new “slime-description” window.
+ - similarly, use
C-c C-p
(slime-pprint-eval-last-expression
) to eval and pretty-print the expression before point. It shows the result in a new “slime-description” buffer.
+ - use
M-x slime-eval-print-last-expression
(unbound by default) to print the result in the same file, under the cursor.
- evaluate a region with
C-c C-r
,
- evaluate a defun with
C-M-x
,
- type
C-c C-e
(slime-interactive-eval
) to get a prompt that asks for code to eval in the current context. It prints the result in the minibuffer. With a prefix argument, insert the result into the current buffer.
@@ -489,9 +286,9 @@ its closing paren and pressing C-x C-e
See also other commands in the menu.
-
+But what’s the difference between evaluating and compiling some code?
-EVALUATION VS COMPILATION
+evaluation VS compilation
There are a couple of pragmatic differences when choosing between compiling or evaluating.
In general, it is better to compile top-level forms, for two reasons:
@@ -524,89 +321,52 @@ In general, it is better to compile top-level forms, for two reasons:
See also eval-in-repl to send any form to the repl.
-
+Debugging
-
+We cover debugging commands in its own debugging chapter.
-Searching Lisp Code
-
-Standard Emacs text search (isearch forward/backward, regexp searches, search/replace)
-
-C-s
does an incremental search forward (e.g. - as each key is
-the search string is entered, the source file is searched for the
-first match. This can make finding specific text much quicker as
-you only need to type in the unique characters. Repeat searches
-(using the same search characters) can be done by repeatedly
-pressing C-s
-
-C-r
does an incremental search backward
-
-C-s RET
and C-r RET
both do conventional string searches
-(forward and backward respectively)
-
-C-M-s
and C-M-r
both do regular expression searches (forward
-and backward respectively)
-
-M-%
does a search/replace while C-M-%
does a regular
-expression search/replace
-
-Finding occurrences (occur, grep)
-
-Use M-x grep
, rgrep
, occur
…
-
-See also interactive versions with
-helm-swoop, helm-occur,
-ag.el.
-
-Go to definition
+Go to definition
Put the cursor on any symbol and press M-.
(slime-edit-definition
) to go to its
definition. Press M-,
to come back.
-Go to symbol, list symbols in current source
+Go to any symbol, list symbols in current source
-Use C-u M-.
(slime-edit-definition
with a prefix argument, also available as M-- M-.
) to autocomplete the symbol and navigate to it. This command always asks for a symbol even if the cursor is on one. It works with any loaded definition. Here’s a little demonstration video.
+Use C-u M-.
(slime-edit-definition
with a prefix argument, also available as M-- M-.
) to autocomplete the symbol and navigate to it.
+
+This command always asks for a symbol even if the cursor is on one. It works with any loaded definition. Here’s a little demonstration video.
You can think of it as a imenu
completion that always work for any Lisp symbol. Add in Slime’s fuzzy completion for maximum powerness!
-Crossreferencing: find who’s calling, referencing, setting a symbol
-
-Slime has nice cross-referencing facilities. For example, you can ask
-what calls a particular function, what expands a macro, or where a global variable is being used.
-
-Results are presented in a new buffer, listing the places which reference a particular entity.
-From there, we can press Enter to go to the corresponding source line,
-or more interestingly we can recompile the place at point by pressing C-c C-c on that
-line. Likewise, C-c C-k will recompile all the references. This is useful when
-modifying macros, inline functions, or constants.
-
-The bindings are the following (they are also shown in Slime’s menu):
-
-
- - C-c C-w c (
slime-who-calls
) callers of a function
- - C-c C-w m (
slime-who-macroexpands
) places where a macro is expanded
- - C-c C-w r (
slime-who-references
) global variable references
- - C-c C-w b (
slime-who-bind
) global variable bindings
- - C-c C-w s (
slime-who-sets
) global variable setters
- - C-c C-w a (
slime-who-specializes
) methods specialized on a symbol
-
-
-And when the slime-asdf
contrib is enabled,
-C-c C-w d (slime-who-depends-on
) lists dependent ASDF systems
-
-And a general binding: M-? or M-_ (slime-edit-uses
) combines all
-of the above, it lists every kind of references.
-
-
-
-Lisp Documentation in Emacs - Learning About Lisp Symbols
-
-Argument lists
+Argument lists
When you put the cursor on a function, SLIME will show its signature
in the minibuffer.
-Documentation lookup
+If you want to see them better, try C-c C-s
after a function name.
+
+For example, you forgot how to use with-open-file
. Write it:
+
+
(with-open-file
+
+
+
now press C-c C-s
(slime-complete-form
) and you’ll get:
+
+
(with-open-file (stream filespec :direction direction
+ :element-type element-type
+ :if-exists if-exists
+ :if-does-not-exist if-does-not-exist
+ :external-format external-format
+ :class class
+ )
+ body...)
+
+
+
written in your source file (or in the REPL).
+
+
The minibuffer will show you the default values of the arguments.
+
+
Documentation lookup
The main shortcut to know is:
@@ -634,15 +394,172 @@ in the minibuffer.

-
Inspect
+
Inspector
You can call (inspect 'symbol)
from the REPL or call it with C-c I
from a source file.
-
Macroexpand
+
Learn to use with its documentation: use l
to come back to the previous object, *
to copy the object at point… and more.
+
+
Macroexpand
Use C-c M-m
to macroexpand a macro call
-
Consult the Hyper Spec (CLHS) offline
+
Crossreferencing: find who’s calling, referencing, setting a symbol
+
+
Slime has nice cross-referencing facilities. For example, you can ask
+what calls a particular function, what expands a macro, or where a global variable is being used.
+
+
Results are presented in a new buffer, listing the places which reference a particular entity.
+From there, we can press Enter to go to the corresponding source line,
+or more interestingly we can recompile the place at point by pressing C-c C-c on that
+line. Likewise, C-c C-k will recompile all the references. This is useful when
+modifying macros, inline functions, or constants.
+
+
The bindings are the following (they are also shown in Slime’s menu):
+
+
+ - C-c C-w c (
slime-who-calls
) callers of a function
+ - C-c C-w m (
slime-who-macroexpands
) places where a macro is expanded
+ - C-c C-w r (
slime-who-references
) global variable references
+ - C-c C-w b (
slime-who-bind
) global variable bindings
+ - C-c C-w s (
slime-who-sets
) global variable setters
+ - C-c C-w a (
slime-who-specializes
) methods specialized on a symbol
+
+
+
And when the slime-asdf
contrib is enabled,
+C-c C-w d (slime-who-depends-on
) lists dependent ASDF systems
+
+
And a general binding: M-? or M-_ (slime-edit-uses
) combines all
+of the above, it lists every kind of references.
+
+
Systems interactions
+
+
In Slime, you can use the usual C-c C-k
in an .asd file to compile and load it, then ql:quickload
(or asdf:load-system
) to effectively load the system. SLIME offers more interactive commands to interact with Lisp systems:
+
+
+ M-x slime-load-system
: offers a prompt to select an ASDF system, with autocompletion of projects collected from where ASDF sees Common Lisp projects, then compile and load the system. The default system name is taken from the first file matching *.asd in the current buffer’s working directory.
+
+ - note that the system name is inferred from the .asd file name. The real system name defined inside may be different.
+ - to understand where ASDF looks for Lisp systems, read the getting started page, section “How to load an existing project”.
+
+
+ M-x slime-open-system
: this opens a new buffer for all source files of a given system.
+ M-x slime-browse-system
: this command opens a Dired buffer to browse the files of a system.
+ M-x slime-rgrep-system
: run rgrep
on the base directory of a system.
+ M-x slime-isearch-system
: run isearch
on the files of a system.
+ M-x slime-query-replace-system
: run query-replace
on an ASDF system.
+ M-x slime-save-system
: save all files belongign to a system.
+ M-x slime-delete-system-fasls
: this deletes the cached .fasl files for this system.
+
+
+
Sly users have a more featureful sly-load-system
command that will search the .asd file on the current directory and in parent directories.
+
+
REPL interactions
+
+
From the SLIME REPL, press ,
to prompt for commands. There is completion
+over the available systems and packages. Examples:
+
+
+ ,load-system
+ ,reload-system
+ ,in-package
(also C-c M-p
in a .lisp file)
+ ,restart-inferior-lisp
+
+
+
and many more. Usually the interactive commands given in the previous section have a REPL shortcut.
+
+
With the slime-quicklisp
contrib, we can use ,ql
to
+autocomplete a system to install, from all systems available for
+installation.
+
+
In addition, we can use the Quicklisp-systems Slime extension to search, browse and load Quicklisp systems from Emacs.
+
+
Sending code to the REPL
+
+
You can write code in the REPL, but you can also interact with code directly from the source file.
+
+
We saw C-c C-j, that sends the expression at point to the REPL and evaluates it.
+
+
C-c C-y (slime-call-defun
): send code to the REPL.
+
+
When the point is inside a defun and C-c C-y is pressed (below I’ll use [] as an indication where the cursor is)
+
+
(defun foo ()
+ nil[])
+
+
+
then (foo [])
will be inserted into the REPL, so that you can write
+additional arguments and run it.
+
+
If foo
was in a different package than the package of the REPL,
+(package:foo )
or (package::foo )
will be inserted.
+
+
This feature is very useful for testing a function you just wrote.
+
+
That works not only for a defun
, but also for defgeneric
, defmethod
,
+defmacro
, and define-compiler-macro
in the same fashion as for defun.
+
+
For defvar
, defparameter
, defconstant
: [] *foo*
will be inserted
+(the cursor is positioned before the symbol so that you can easily
+wrap it into a function call).
+
+
For defclass: (make-instance ‘class-name )
.
+
+
Inserting calls to frames in the debugger
+
+
C-y in SLDB on a frame will insert a call to that frame into the REPL, e.g.,
+
+
(/ 0) =>
+…
+1: (CCL::INTEGER-/-INTEGER 1 0)
+…
+
+
+
C-y will insert (CCL::INTEGER-/-INTEGER 1 0)
.
+
+
(thanks to Slime tips)
+
+
Synchronizing packages
+
+
C-c ~ (slime-sync-package-and-default-directory
): When run in a
+buffer with a lisp file it will change the current package of the REPL
+to the package of that file and also set the current directory of the REPL
+to the parent directory of the file.
+
+
Exporting symbols
+
+
Slime provides a shortcut to add export declarations to your package, effectively exporting one or many symbol(s), or on the contrary un-exporting it.
+
+
C-c x (slime-export-symbol-at-point) from the slime-package-fu
+contrib: takes the symbol at point and modifies the :export
clause of
+the corresponding defpackage form. It also exports the symbol. When
+called with a negative argument (C-u C-c x) it will remove the symbol
+from :export
and unexport it.
+
+
M-x slime-export-class does the same but with symbols defined
+by a structure or a class, like accessors, constructors, and so on.
+It works on structures only on SBCL and Clozure CL so far.
+Classes should work everywhere with MOP.
+
+
Customization
+
+
There are different styles of how symbols are presented in
+defpackage
, the default is to use uninterned symbols (#:foo
).
+This can be changed:
+
+
to use keywords, add this to your Emacs init file:
+
+
(setq slime-export-symbol-representation-function
+ (lambda (n) (format ":%s" n)))
+
+
+
or strings:
+
+
(setq slime-export-symbol-representation-function
+ (lambda (n) (format "\"%s\"" (upcase n))))
+
+
+
(optional) Consult the Hyper Spec (CLHS) offline
The Common Lisp Hyper Spec is the
official online version of the ANSI Common Lisp standard. We can start
@@ -699,106 +616,301 @@ common-lisp-hyperspec-format, bound to C-c C-d ~
.
you can also look-up glossary terms (for example, you can look-up “function” instead of “defun”), use M-x common-lisp-hyperspec-glossary-term
, bound to C-c C-d g
.
-
Miscellaneous
+
Working with Emacs
-
Synchronizing packages
+
In this section we’ll learn the most useful Emacs commands to work with Lisp code in general, or to perform common actions.
-
C-c ~ (slime-sync-package-and-default-directory
): When run in a
-buffer with a lisp file it will change the current package of the REPL
-to the package of that file and also set the current directory of the REPL
-to the parent directory of the file.
+
We’ll start by how to find your way into Emacs’ built-in documentation. If there is a skill you should learn, that is the one.
-
Calling code
+
Don’t forget that Emacs both GUI and terminal interfaces have menus, they help in discovering all available commands. If you don’t see one, ensure that your emacs configuration doesn’t hide it. Display the menu with M-x menu-bar-mode
.
-
C-c C-y (slime-call-defun
): When the point is inside a defun and
-C-c C-y is pressed,
+
Built-in documentation
-
(I’ll use [] as an indication where the cursor is)
+
Emacs comes with built-in tutorials and documentation. Moreover, it is
+a self-documented and self-discoverable editor, capable of introspection to let you
+know about the current keybindings, to let you search about function documentation,
+available variables,source code, tutorials, etc. Whenever you ask yourself questions like
+“what are the available shortcuts to do x” or “what does this
+keybinding really do”, the answer is most probably a keystroke away,
+right inside Emacs. You should learn a few keybindings to be able to
+discover Emacs with Emacs flawlessly.
-
(defun foo ()
- nil[])
-
-
-
then (foo [])
will be inserted into the REPL, so that you can write
-additional arguments and run it.
-
-
If foo
was in a different package than the package of the REPL,
-(package:foo )
or (package::foo )
will be inserted.
-
-
This feature is very useful for testing a function you just wrote.
-
-
That works not only for defun, but also for defgeneric, defmethod,
-defmacro, and define-compiler-macro in the same fashion as for defun.
-
-
For defvar, defparameter, defconstant: [] *foo*
will be inserted
-(the cursor is positioned before the symbol so that you can easily
-wrap it into a function call).
-
-
For defclass: (make-instance ‘class-name )
.
-
-
Inserting calls to frames in the debugger
-
-
C-y in SLDB on a frame will insert a call to that frame into the REPL, e.g.,
-
-
(/ 0) =>
-…
-1: (CCL::INTEGER-/-INTEGER 1 0)
-…
-
-
-
C-y will insert (CCL::INTEGER-/-INTEGER 1 0)
.
-
-
(thanks to Slime tips)
-
-
Exporting symbols
-
-
C-c x (slime-export-symbol-at-point) from the slime-package-fu
-contrib: takes the symbol at point and modifies the :export
clause of
-the corresponding defpackage form. It also exports the symbol. When
-called with a negative argument (C-u C-c x) it will remove the symbol
-from :export
and unexport it.
-
-
M-x slime-export-class does the same but with symbols defined
-by a structure or a class, like accessors, constructors, and so on.
-It works on structures only on SBCL and Clozure CL so far.
-Classes should work everywhere with MOP.
-
-
Customization
-
-
There are different styles of how symbols are presented in
-defpackage
, the default is to use uninterned symbols (#:foo
).
-This can be changed:
-
-
to use keywords:
-
-
(setq slime-export-symbol-representation-function
- (lambda (n) (format ":%s" n)))
-
-
-
or strings:
-
-
(setq slime-export-symbol-representation-function
- (lambda (n) (format "\"%s\"" (upcase n))))
-
-
-
Project Management
-
-
ASDF is the de-facto build facility. It is shipped in most Common Lisp implementations.
+
The help on the topic is here:
-
Searching Quicklisp libraries
+
The help keybindings start with either C-h
or F1
. Important ones are:
-
From the REPL, we can use ,ql
to install a package known by name already.
+
+ C-h k <keybinding>
: what function does this keybinding call?
+ C-h f <function name>
: what keybinding is linked to this function?
+ C-h a <topic>
: show a list of commands whose name match the given topic. It accepts a keyword, a list of keywords or a regular expression.
+ C-h i
: show the Info page, a menu of major topics.
+
-
In addition, we can use the Quicklisp-systems Slime extension to search, browse and load Quicklisp systems from Emacs.
+
Some Emacs packages give even more help.
-
Questions/Answers
+
More help and discoverability packages
-
utf-8 encoding
+
Sometimes, you start typing a key sequence but you can’t remember it
+completely. Or, you wonder what other keybindings are related. Comes
+which-key-mode. This
+packages will display all possible keybindings starting with the key(s) you just typed.
+
+
For example, I know there are useful keybindings under C-x
but I don’t remember which ones… I just type C-x
, I wait for half a second, and which-key shows all the ones available.
+
+

+
+
Just try it with C-h
too!
+
+
See also Helpful, an alternative to the built-in Emacs help that provides much more contextual information.
+
+

+
+
Built-in tutorial
+
+
Emacs ships its own tutorial. You should give it a look to learn the most important keybindings and concepts.
+
+
Call it with M-x help-with-tutorial
(where M-x
is alt-x
).
+
+
Editing
+
+
Emacs has, of course, built-in commands to deal with s-expressions.
+
+
Forward/Backward/Up/Down movement and selection by s-expressions
+
+
Use C-M-f
and C-M-b
(forward-sexp
and backward-sexp
) to move
+in units of s-expressions.
+
+
Use C-M-t
to swap
+the first addition sexp and the second one. Put the cursor on the open
+parens of “(+ x” in defun c and press
+
+
Use C-M-@
to highlight an entire sexp. Then press C-M-u
to expand
+the selection “upwards” and C-M-d
to move forward down one level of
+parentheses.
+
+
Deleting s-expressions
+
+
Use C-M-k
(kill-sexp
) and C-M-backspace
(backward-kill-sexp
) (but caution: this keybinding may restart the system on GNU/Linux).
+
+
For example, if point is before (progn
(I’ll use [] as an indication where the cursor is):
+
+
(defun d ()
+ (if t
+ (+ 3 3)
+ [](progn
+ (+ 1 1)
+ (if t
+ (+ 2 2)
+ (+ 3 3)))
+ (+ 4 4)))
+
+
+
and you press C-M-k
, you get:
+
+
(defun d ()
+ (if t
+ (+ 3 3)
+ []
+ (+ 4 4)))
+
+
+
Indenting s-expressions
+
+
Indentation is automatic for Lisp forms.
+
+
Pressing TAB will indent incorrectly indented code. For example, put
+the point at the beginning of the (+ 3 3)
form and press TAB:
+
+
(progn
+(+ 3 3))
+
+
+
you correctly get
+
+
(progn
+ (+ 3 3))
+
+
+
Use C-M-q
(slime-reindent-defun
) to indent the current function definition:
+
+
;; Put the cursor on the open parens of "(defun ..."
+;; and press "C-M-q" to indent the code:
+(defun e ()
+"A badly indented function."
+(let ((x 20))
+(loop for i from 0 to x
+do (loop for j from 0 below 10
+do (print j))
+(if (< i 10)
+(let ((z nil) )
+(setq z (format t "x=~d" i))
+(print z))))))
+
+;; This is the result:
+
+(defun e ()
+ "A badly indented function (now correctly indented)."
+ (let ((x 20))
+ (loop for i from 0 to x
+ do (loop for j from 0 below 10
+ do (print j))
+ (if (< i 10)
+ (let ((z nil) )
+ (setq z (format t "x=~d" i))
+ (print z))))))
+
+
+
You can also select a region and call M-x indent-region
.
+
+
Open and close parenthesis
+
+
When you are in a Slime REPL, you can use C-return
or M-return
+(slime-repl-closing-return
) to close the remaining parenthesis and
+evaluate your input string.
+
+
In files, use M-(
to insert a pair of parenthesis (()
) and the same
+keybinding with a prefix argument, C-u M-(
, to enclose the
+expression in front of the cursor with a pair of parens.
+
+
For example, we start with the cursor before the first paren:
+
+
CL-USER> |(- 2 2)
+
+
+
Press C-u M-(
to enclose it with parens:
+
+
CL-USER> (|(- 2 2))
+;; now write anything.
+CL-USER> (zerop (- 2 2))
+
+
+
With a numbered prefix argument (C-u 2 M-(
), wrap around this number of s-expressions.
+
+
Additionnaly, use M-x check-parens
to spot malformed s-exps and C-c
+C-]
(slime-close-all-parens-in-sexp
) to insert the required number
+of closing parenthesis.
+
+
There are additional packages that can make your use of parens easier:
+
+
+ M-x show-paren-mode
, a built-in Emacs mode: it toggles the
+visualization of matching parenthesis. When enabled, place the
+cursor on a paren and you’ll see the other paren it matches
+with. You can initialize it in your Emacs init file with
+(show-paren-mode t)
. It is a global minor mode (it will work for
+all buffers, all languages).
+ - when evil-mode (the vim layer) is enabled, you can use the
%
key to go to the matchin paren.
+ M-x electric-pair-mode
, a built-in Emacs mode: when enabled,
+typing an open parenthesis automatically inserts the corresponding
+closing parenthesis, and vice versa. (Likewise for brackets, etc.).
+If the region is active, the parentheses (brackets, etc.) are inserted
+around the region instead.
+ - you could use Paredit (animated guide) to automatically insert parentheses in pairs,
+ - or lispy-mode, like Paredit, but a key triggers an action when the cursor is placed right before or right after a parentheses.
+
+
+
Hiding/showing code
+
+
Use C-x n n
(narrow-to-region) and C-x n w
to widen back.
+
+
See also code folding with external packages.
+
+
+
+
Insert a comment or comment a region with M-;
, adjust text with M-q
.
+
+
(optional) Packages for structured editing
+
+
In addition to the built-in Emacs commands, you have several packages at your disposal
+that will help to keep the parens and/or the indentation balanced.
+The list below is somewhat sorted by age of the
+extension, according to the
+history of Lisp editing:
+
+
+ - Paredit - Paredit is a
+classic. It defines the must-have commands (move, kill, split, join
+a sexp,…).
+(visual tutorial)
+ - Smartparens - Smartparens
+not only deals with parens but with everything that comes in pairs
+(html tags,…) and thus has features for non-lispy languages.
+ - Lispy - Lispy reimagines Paredit
+with the goal to have the shortest bindings (mostly one key) that
+only act depending on the point position.
+ - Paxedit - Paxedit adds
+commands based on the context (in a symbol, a sexp,… ) and puts
+efforts on whitespace cleanup and context refactoring.
+ - Parinfer - Parinfer
+automatically fixes the parens depending on the indentation, or the
+other way round (or both !).
+
+
+
We personally advice to try Parinfer and the famous Paredit, then to
+go up the list. See explanations and even more on
+Wikemacs.
+
+
Search and replace
+
+
isearch forward/backward, regexp searches, search/replace
+
+
C-s
does an incremental search forward (e.g. - as each key is
+the search string is entered, the source file is searched for the
+first match. This can make finding specific text much quicker as
+you only need to type in the unique characters. Repeat searches
+(using the same search characters) can be done by repeatedly
+pressing C-s
+
+
C-r
does an incremental search backward
+
+
C-s RET
and C-r RET
both do conventional string searches
+(forward and backward respectively)
+
+
C-M-s
and C-M-r
both do regular expression searches (forward
+and backward respectively)
+
+
M-%
does a search/replace while C-M-%
does a regular
+expression search/replace
+
+
Finding occurrences (occur, grep)
+
+
Use M-x grep
, rgrep
, occur
…
+
+
See also interactive versions with
+helm-swoop, helm-occur,
+ag.el.
+
+
Questions/Answers
+
+
Emacs Lisp vs Common Lisp
+
+
It isn’t necessary to write Emacs Lisp in order to use Emacs with Slime or Sly for Common Lisp.
+
+
However learning Emacs Lisp can be useful and is similar (but different) from CL:
+
+ - Dynamic scope is everywhere
+ - There are no reader (or reader-related) functions
+ - Does not support all the types that are supported in CL
+ - Incomplete implementation of CLOS (with the add-on EIEIO package)
+ - No numerical tower support
+
+
+
Some good Emacs Lisp learning resources:
+
+
+
What about LSP (Language Server Protocol)?
+
+
LSP server and client ports for Common Lisp exist, but we don’t need them to have a high quality IDE integration. In fact, Slime/Swank follow a client/server architecture, like LSP, but Slime predates LSP by decades, and still offers much more features for lispers than LSP.
+
+
utf-8 encoding
You might want to set this to your init file:
@@ -816,7 +928,7 @@ This can be changed:
This will avoid getting ascii stream decoding error
s when you have
non-ascii characters in files you evaluate with SLIME.
-
Default cut/copy/paste keybindings
+
Default cut/copy/paste keybindings
I am so used to C-c, C-v and friends to copy and paste text that
the default Emacs shortcuts don’t make any sense to me.
@@ -827,9 +939,9 @@ the default Emacs shortcuts don’t make any sense to me.
(require 'cua) (CUA-mode t)
-
Appendix
+
Appendix
-
All Slime REPL shortcuts
+
All Slime REPL shortcuts
Here is the reference of all Slime shortcuts that work in the REPL.
@@ -849,8 +961,8 @@ SPC slime-space
(that binding is currently shadowed by another mode)
, slime-handle-repl-shortcut
DEL backward-delete-char-untabify
-<C-down> slime-repl-forward-input
<C-return> slime-repl-closing-return
+<C-down> slime-repl-forward-input
<C-up> slime-repl-backward-input
<return> slime-repl-return
@@ -887,7 +999,9 @@ C-c M-i slime-fuzzy-complete-symbol
C-c M-o slime-repl-clear-buffer
-
All other Slime shortcuts
+
All other Slime shortcuts
+
+
There is more to what we showed! Slime has shortcuts to disassemble the function definition of the symbol at point, learn how to navigate the inspector, toggle functions profiling, learn its indentation or completion strategies, use multiple Lisp connections, learn how to manipulate presentations…
Here are all the default keybindings defined by Slime mode.
@@ -1053,10 +1167,28 @@ C-x 4 . slime-edit-definition-other-window
C-c C-v M-o slime-clear-presentations
-
See also
+
See also
- - Common Lisp REPL exploration guide - a concise and curated set of highlights to find one’s way in the REPL.
+ - SLIME’s documentation
+ - Slime video tutorial (and the author’s channel, full of great stuff)
+ - Marco Baringer’s Slime tutorial
+ - Common Lisp REPL exploration guide, a concise and curated set of highlights to find one’s way in the REPL.
+ - Emacs4CL, a tiny DIY kit to set up vanilla Emacs for Common Lisp programming.
+ - slime-star, a collection of extensions for SLIME:
+
+ - doc contribs: richer slime-help and slime-info buffers to display documentation.
+ - Quicklisp systems: autocompletion to load Quicklisp systems from the REPL.
+ - quicksearch integration: search for Common Lisp repositories on Quicklisp, Github and Cliki.
+ - Slime breakpoints: set breakpoints visually without code annotation, get buttons to step through code.
+ - Quicklisp apropos: “apropos” across Quicklisp libraries.
+ - Slime critic: get the Slime critic gently critique your code.
+ - interactive print and trace buffers
+ - dedicated Emacs buffers for output streams
+ - access to the ANSICL specification in Emacs’ Info format.
+ - Lisp system browser: a (work in progress) Smalltalk-like system browser for Common Lisp, where one can get different panes to browse available packages and their functions, variables, macros, classes, generic functions.
+
+
@@ -1089,7 +1221,7 @@ C-c C-v M-o slime-clear-presentations
© 2002–2023 the Common Lisp Cookbook Project
diff --git a/lispcookbook.github.io/cl-cookbook/error_handling.html b/lispcookbook.github.io/cl-cookbook/error_handling.html
index dff6b47..37570df 100644
--- a/lispcookbook.github.io/cl-cookbook/error_handling.html
+++ b/lispcookbook.github.io/cl-cookbook/error_handling.html
@@ -53,14 +53,19 @@
-
- 📢 🤶 ⭐
- Discover our contributor's Lisp course with this Christmas coupon.
-
- Recently added: 18 videos on MACROS.
-
- Learn more.
-
+
+
+
+
+
+
+
+
+
+
+ 📢 New videos: web dev demo part 1, dynamic page with HTMX, Weblocks demo
+
+
📕 Get the EPUB and PDF
@@ -657,7 +662,7 @@ anything), but our message is printed afterwards anyway.
© 2002–2023 the Common Lisp Cookbook Project
diff --git a/lispcookbook.github.io/cl-cookbook/ffi.html b/lispcookbook.github.io/cl-cookbook/ffi.html
index 49ab4d6..031d234 100644
--- a/lispcookbook.github.io/cl-cookbook/ffi.html
+++ b/lispcookbook.github.io/cl-cookbook/ffi.html
@@ -53,125 +53,95 @@
-
- 📢 🤶 ⭐
- Discover our contributor's Lisp course with this Christmas coupon.
-
- Recently added: 18 videos on MACROS.
-
- Learn more.
-
+
+
+
+
+
+
+
+
+
+
+ 📢 New videos: web dev demo part 1, dynamic page with HTMX, Weblocks demo
+
+
📕 Get the EPUB and PDF
The ANSI Common Lisp standard doesn’t mention this topic. So almost everything that can be said here depends on your OS and your implementation.
+
The ANSI Common Lisp standard doesn’t mention this topic. So almost everything that can be said here depends on your OS and your implementation. However these days, we can use the CFFI library, a portable and easy-to-use C foreign function interface.
-
+
+ CFFI, the Common Foreign Function Interface, purports to be a portable FFI for Common Lisp. It abstracts away the differences between the API of the native FFI’s of the various Common Lisp implementations.
+
-
Example: Calling ‘gethostname’ from CLISP
+
We’ll see an example right now.
-
Note: You should read the relevant chapter from the CLISP implementation notes before you proceed.
+
-
int gethostname(char *name, int len)
follows a typical pattern of C “out”-parameter convention - it expects a pointer to a buffer it’s going to fill. So you must view this parameter as either :OUT
or :IN-OUT
. Additionally, one must tell the function the size of the buffer. Here len
is just an :IN
parameter. Sometimes this will be an :IN-OUT
parameter, returning the number of bytes actually filled in.
+
Let’s use defcfun
to interface with the foreign ceil C function from math.h
.
-
So name
is actually a pointer to an array of up to len
characters, regardless of what the poor “char *
” C prototype says, to be used like a C string (0-termination). How many elements are in the array? Luckily, in our case, you can find it out without calculating the sizeof()
a C structure. It’s a hostname that will be returned. The Solaris 2.x manpage says “Host names are limited to MAXHOSTNAMELEN characters, currently 256.”
+
defcfun is a macro in the cffi library that generates a function with the name you give it.
-
Also, in the present example, you can use allocation :ALLOCA
, like you’d do in C: stack-allocate a temporary. Why make things worse when using Lisp than when using C?
-
-
This yields the following useful signature for your foreign function:
-
-
(ffi:def-c-call-out gethostname
- (:arguments (name (ffi:c-ptr (ffi:c-array-max ffi:char 256))
- :out :alloca)
- (len ffi:int))
- ;; (:return-type BOOLEAN) could have been used here
- ;; (Solaris says it's either 0 or -1).
- (:return-type ffi:int))
-
- (defun myhostname ()
- (multiple-value-bind (success name)
- ;; :OUT or :IN-OUT parameters are returned via multiple values
- (gethostname 256)
- (if (zerop success)
- (subseq name 0 (position #\null name))
- (error ... ; errno may be set
- ...))))
- (defvar hostname (myhostname))
+CL-USER> (cffi:defcfund ("ceil" c-ceil) :double (number :double))
-Possibly SUBSEQ
and POSITION
are superfluous, thanks to C-ARRAY-MAX
as opposed to C-ARRAY
:
+We say that the “ceil” C function will be called “c-ceil” on our Lisp side, it takes one argument that is a double float, and it returns a number that is also a double float.
-(defun myhostname ()
- (multiple-value-bind (success name)
- ;; :out or :in-out parameters are returned via multiple values
- (gethostname 256)
- (if (zerop success) name
- (error ... ; errno may be set
- ...))))
+Here is the above function macroexpanded with macrostep-expand
:
+
+(progn
+ nil
+ (defun c-ceil (number)
+ (let ((#:g312 number))
+ (cffi-sys:%foreign-funcall "ceil" (:double #:g312 :double) :convention
+ :cdecl :library :default))))
-
+The reason we called it c-ceil
and not ceil
is only for the example, so we know this is a wrapper around C. You can name it “ceil”, since it doesn’t designate a built-in Common Lisp function or macro.
-Example: Calling ‘gethostname’ from Allegro CL
+Now that we have a c-ceil function from math.h
, let’s use it! We must give it double float.
-This is how the same example above would be written in Allegro Common Lisp version 6 and above. ACL doesn’t explicitly distinguish between input
and output
arguments. The way to declare an argument as output
(i.e., modifiable by C) is to use an array, since arrays are passed by reference and C therefore receives a pointer to a memory location (which is what it expects). In this case things are made even easier by the fact that gethostname()
expects an array of char, and a SIMPLE-ARRAY
of CHARACTER
represents essentially the same thing in Lisp. The foreign function definition is therefore the following:
-
-(def-foreign-call (c-get-hostname "gethostname")
- ((name (* :char) (simple-array 'character (*)))
- (len :int integer))
- :returning :int)
+CL-USER> (c-ceil 5.4d0)
+6.0d0
-Let’s read this line by line: this form defines a Lisp function called C-GET-HOSTNAME
that calls the C function gethostname()
. It takes two arguments: the first one, called NAME
, is a pointer to a char (*char
in C), and a SIMPLE-ARRAY
of characters in Lisp; the second one is called LEN
, and is an integer. The function returns an integer value.
+As you can see, it works! The double gets rounded up to 6.0d0
as expected.
-And now the Lisp side:
+Let’s try another one! This time, we’ll use floor, and we couldn’t name it “floor” because this Common Lisp function exists.
-(defun get-hostname ()
- (let* ((name (make-array 256 :element-type 'character))
- (result (c-get-hostname name 256)))
- (if (zerop result)
- (let ((pos (position #\null name)))
- (subseq name 0 pos))
- (error "gethostname() failed."))))
+CL-USER> (cffi:defcfun ("floor" c-floor) :double (number :double))
+C-FLOOR
+CL-USER> (c-floor 5d0)
+5.0d0
+CL-USER> (c-floor 5.4d0)
+5.0d0
-This function creates the NAME
array, calls C-GET-HOSTNAME
to fill it and then checks the returned value. If the value is zero, then the call was successful, and we return the contents of NAME
up to the first 0 character (the string terminator in C), otherwise we signal an error. Note that, unlike the previous example, we allocate the string in Lisp, and we rely on the Lisp garbage collector to get rid of it after the function terminates. Here is a usage example:
+Great!
-* (get-hostname)
- "terminus"
+One more, let’s try sqrt
from math.h, still with double floats:
+
+CL-USER> (cffi:defcfun ("sqrt" c-sqrt) :double (number :double))
+C-SQRT
+CL-USER> (c-sqrt 36.50d0)
+6.041522986797286d0
-Working with strings is, in general, easier than the previous example showed. Let’s say you want to call getenv()
from Lisp to access the value of an environment variable. getenv()
takes a string argument (the variable name) and returns another string (the variable value). To be more precise, the argument is a pointer to a sequence of characters that should have been allocated by the caller, and the return value is a pointer to an already-existing sequence of chars (in the environment). Here is the definition of C-GETENV
:
+We can do arithmetic with our new c-sqrt
:
-(def-foreign-call (c-getenv "getenv")
- ((var (* :char) string))
- :returning :int
- :strings-convert t)
+CL-USER> (+ 2 (c-sqrt 3d0))
+3.732050807568877d0
-The argument in this case is still a pointer to char in C, but we can declare it a STRING
to Lisp. The return value is a pointer, so we declare it as integer. Finally, the :STRINGS-CONVERT
keyword argument specifies that ACL should automatically translate the Lisp string passed as the first argument into a C string. Here is how it’s used:
+We can even use our new shiny c-sqrt
to map over a list of doubles and take the square root of all of them!
-* (c-getenv "SHELL")
- -1073742215
-
-
-If you are surprised by the return value, just remember that C-GETENV
returns a pointer, and we must tell Lisp how to interpret the contents of the memory location pointed to by it. Since in this case we know that it will point to a C string, we can use the FF:NATIVE-TO-STRING
function to convert it to a Lisp string:
-
-* (native-to-string (c-getenv "SHELL"))
- "/bin/tcsh"
- 9
- 9
-
-
-(The second and third values are the number of characters and bytes copied, respectively). One caveat: if you ask for the value of a non-existent variable, C-GETENV
will return 0, and NATIVE-TO-STRING
will fail. So a safer example would be:
-
-* (let ((ptr (c-getenv "NOSUCHVAR")))
- (unless (zerop ptr)
- (native-to-string ptr)))
- NIL
+CL-USER> (mapcar #'c-sqrt '(3d0 4d0 5d0 6d0 7.5d0 12.75d0))
+(1.7320508075688772d0 2.0d0 2.23606797749979d0 2.449489742783178d0
+ 2.7386127875258306d0 3.570714214271425d0)
@@ -204,7 +174,7 @@
© 2002–2023 the Common Lisp Cookbook Project
diff --git a/lispcookbook.github.io/cl-cookbook/files.html b/lispcookbook.github.io/cl-cookbook/files.html
index 6f39478..9d941cf 100644
--- a/lispcookbook.github.io/cl-cookbook/files.html
+++ b/lispcookbook.github.io/cl-cookbook/files.html
@@ -53,14 +53,19 @@
-
- 📢 🤶 ⭐
- Discover our contributor's Lisp course with this Christmas coupon.
-
- Recently added: 18 videos on MACROS.
-
- Learn more.
-
+
+
+
+
+
+
+
+
+
+
+ 📢 New videos: web dev demo part 1, dynamic page with HTMX, Weblocks demo
+
+
📕 Get the EPUB and PDF
@@ -872,7 +877,7 @@ operations.
© 2002–2023 the Common Lisp Cookbook Project
diff --git a/lispcookbook.github.io/cl-cookbook/functions.html b/lispcookbook.github.io/cl-cookbook/functions.html
index 920a2c2..4ed703d 100644
--- a/lispcookbook.github.io/cl-cookbook/functions.html
+++ b/lispcookbook.github.io/cl-cookbook/functions.html
@@ -53,14 +53,19 @@
-
- 📢 🤶 ⭐
- Discover our contributor's Lisp course with this Christmas coupon.
-
- Recently added: 18 videos on MACROS.
-
- Learn more.
-
+
+
+
+
+
+
+
+
+
+
+ 📢 New videos: web dev demo part 1, dynamic page with HTMX, Weblocks demo
+
+
📕 Get the EPUB and PDF
@@ -761,7 +766,7 @@ library (in Quicklisp).
© 2002–2023 the Common Lisp Cookbook Project
diff --git a/lispcookbook.github.io/cl-cookbook/getting-started.html b/lispcookbook.github.io/cl-cookbook/getting-started.html
index 19fe5fe..3776fc1 100644
--- a/lispcookbook.github.io/cl-cookbook/getting-started.html
+++ b/lispcookbook.github.io/cl-cookbook/getting-started.html
@@ -53,23 +53,28 @@
-
- 📢 🤶 ⭐
- Discover our contributor's Lisp course with this Christmas coupon.
-
- Recently added: 18 videos on MACROS.
-
- Learn more.
-
+
+
+
+
+
+
+
+
+
+
+ 📢 New videos: web dev demo part 1, dynamic page with HTMX, Weblocks demo
+
+
📕 Get the EPUB and PDF
We’ll begin with presenting easy steps to install a development environment and to start a new Common Lisp project.
+
We’ll begin by presenting easy steps to install a development environment and to start a new Common Lisp project.
-
Want a 2-clicks install? Then get
+
Want a 2-click install? Then get
Portacle, a portable and
multi-platform Common Lisp environment. It ships Emacs, SBCL (the
implementation), Quicklisp (package manager), SLIME (IDE) and
@@ -84,9 +89,9 @@ Git. It’s the most straightforward way to get going!
apt-get install sbcl
-
Common Lisp has been standardized via an ANSI document, so it can be
-implemented in different ways. See
-Wikipedia’s list of implementations.
+
Common Lisp is an ANSI standard but implementations can vary greatly in what
+they provide in addition to the standard. See Wikipedia’s list of
+implementations.
The following implementations are packaged for Debian and most other popular Linux distributions:
@@ -100,7 +105,7 @@ implemented in different ways. See
- ABCL, to interface with the JVM,
- - ClozureCL, a good implementation with very fast build times (see this Debian package for Clozure CL),
+ - ClozureCL, a good implementation with very fast build times,
- CLASP, that interoperates with C++ libraries using LLVM for compilation to native code,
- AllegroCL (proprietary)
- LispWorks (proprietary)
@@ -386,7 +391,7 @@ Debian. The package names usually begin with the cl- prefix (use
Advanced dependencies management
-You can drop Common Lisp projects into any of those folders:
+You can drop Common Lisp projects into any of these folders:
~/quicklisp/local-projects
@@ -420,10 +425,10 @@ available right-away:
(ql:quickload "system")
-
The practical different between the two is that ql:quickload
first tries to
+
The practical difference between the two is that ql:quickload
first tries to
fetch the system from the Internet if it is not already installed.
-
Note that symlinks in local-projects to another location of your liking works too.
+
Note that symlinks in local-projects to another location of your liking work too.
How to work with local versions of libraries
@@ -445,15 +450,15 @@ to help us build
dists.
Now that we have Quicklisp and our editor ready, we can start writing
Lisp code in a file and interacting with the REPL.
-
But what if we want to work with an existing project or create a new
-one, how do we proceed, what’s the right sequence of defpackage
,
-what to put in the .asd
file, how to load the project into the REPL ?
+
But if we want to work with an existing project or create a new
+one, how do we proceed? What’s the right sequence of defpackage
?
+What should we put in the .asd
file? How do we load the project into the REPL ?
Creating a new project
Some project builders help to scaffold the project structure. We like
-cl-project that also sets
-up a tests skeleton.
+
cl-project, which also sets
+up a test skeleton.
In short:
@@ -461,7 +466,7 @@ up a tests skeleton.
(cl-project:make-project #P"./path-to-project/root/")
-
it will create a directory structure like this:
+
will create a directory structure like this:
|-- my-project.asd
|-- my-project-test.asd
@@ -473,7 +478,7 @@ up a tests skeleton.
`-- my-project.lisp
-
Where my-project.asd
resembles this:
+
where my-project.asd
resembles this:
(asdf:defsystem "my-project"
:version "0.1.0"
@@ -504,8 +509,8 @@ up a tests skeleton.
How to load an existing project
You have created a new project, or you have an existing one, and you
-want to work with it on the REPL, but Quicklisp doesn’t know it. How
-can you do ?
+want to work with it in the REPL, but Quicklisp doesn’t know about it. What
+do you do?
Well first, if you create it or clone it into
one of ~/common-lisp
, ~/.local/share/common-lisp/source/
or
@@ -547,14 +552,14 @@ result in the REPL.
You can add this to your ~/.sbclrc
.
-If you dislike the REPL to print all symbols upcase, add this:
+If you dislike the REPL printing all symbols uppercase, add this:
(setf *print-case* :downcase)
-
Warning: This might break the behaviour of some packages like it happened with
+
Warning: This might break the behaviour of some packages like happened with
Mito.
Avoid doing this in production.
@@ -603,7 +608,7 @@ Avoid doing this in production.
© 2002–2023 the Common Lisp Cookbook Project
diff --git a/lispcookbook.github.io/cl-cookbook/gui.html b/lispcookbook.github.io/cl-cookbook/gui.html
index d08ed72..f850280 100644
--- a/lispcookbook.github.io/cl-cookbook/gui.html
+++ b/lispcookbook.github.io/cl-cookbook/gui.html
@@ -53,14 +53,19 @@
-
- 📢 🤶 ⭐
- Discover our contributor's Lisp course with this Christmas coupon.
-
- Recently added: 18 videos on MACROS.
-
- Learn more.
-
+
+
+
+
+
+
+
+
+
+
+ 📢 New videos: web dev demo part 1, dynamic page with HTMX, Weblocks demo
+
+
📕 Get the EPUB and PDF
@@ -1165,7 +1170,7 @@ be immediately applied while the application is running!
© 2002–2023 the Common Lisp Cookbook Project
diff --git a/lispcookbook.github.io/cl-cookbook/index.html b/lispcookbook.github.io/cl-cookbook/index.html
index 05d90f8..d94cc24 100644
--- a/lispcookbook.github.io/cl-cookbook/index.html
+++ b/lispcookbook.github.io/cl-cookbook/index.html
@@ -53,14 +53,19 @@
-
- 📢 🤶 ⭐
- Discover our contributor's Lisp course with this Christmas coupon.
-
- Recently added: 18 videos on MACROS.
-
- Learn more.
-
+
+
+
+
+
+
+
+
+
+
+ 📢 New videos: web dev demo part 1, dynamic page with HTMX, Weblocks demo
+
+
📕 Get the EPUB and PDF
@@ -149,6 +154,7 @@ a book containing recipes and other information about the preparation and cookin
Interfacing with your OS
Databases
Foreign Function Interfaces
+ NEW! ⭐ Building Dynamic Libraries
GUI programming
Sockets
WebSockets
@@ -295,7 +301,7 @@ later.
© 2002–2023 the Common Lisp Cookbook Project
diff --git a/lispcookbook.github.io/cl-cookbook/io.html b/lispcookbook.github.io/cl-cookbook/io.html
index 655e82e..843dba7 100644
--- a/lispcookbook.github.io/cl-cookbook/io.html
+++ b/lispcookbook.github.io/cl-cookbook/io.html
@@ -53,14 +53,19 @@
-
- 📢 🤶 ⭐
- Discover our contributor's Lisp course with this Christmas coupon.
-
- Recently added: 18 videos on MACROS.
-
- Learn more.
-
+
+
+
+
+
+
+
+
+
+
+ 📢 New videos: web dev demo part 1, dynamic page with HTMX, Weblocks demo
+
+
📕 Get the EPUB and PDF
@@ -277,7 +282,7 @@ and
© 2002–2023 the Common Lisp Cookbook Project
diff --git a/lispcookbook.github.io/cl-cookbook/iteration.html b/lispcookbook.github.io/cl-cookbook/iteration.html
index 2450fab..3258bcd 100644
--- a/lispcookbook.github.io/cl-cookbook/iteration.html
+++ b/lispcookbook.github.io/cl-cookbook/iteration.html
@@ -53,14 +53,19 @@
-
- 📢 🤶 ⭐
- Discover our contributor's Lisp course with this Christmas coupon.
-
- Recently added: 18 videos on MACROS.
-
- Learn more.
-
+
+
+
+
+
+
+
+
+
+
+ 📢 New videos: web dev demo part 1, dynamic page with HTMX, Weblocks demo
+
+
📕 Get the EPUB and PDF
@@ -264,8 +269,10 @@ needed for the final result are actually created.
reducers (i.e. functions like +
) to traverse data streams in any way they
wish, all while being very memory efficient.
-See its README or its
-API for more information.
+See its README, its
+API, or the original
+Transducers document for more
+information.
Recipes
@@ -347,7 +354,7 @@ infinitely. Here we show how to loop on a list forever.
We can build an infinite list by setting its last element to the list itself:
-(loop with list-a = '(1 2 3)
+(loop with list-a = (list 1 2 3)
with infinite-list = (setf (cdr (last list-a)) list-a)
for item in infinite-list
repeat 8
@@ -355,7 +362,7 @@ infinitely. Here we show how to loop on a list forever.
;; (1 2 3 1 2 3 1 2)
-Illustration: (last '(1 2 3))
is (3)
, a list, or rather a cons cell, whose car
is 3 and cdr
is NIL. See the data-structures chapter for a reminder. This is the representation of (list 3)
:
+Illustration: (last (list 1 2 3))
is (3)
, a list, or rather a cons cell, whose car
is 3 and cdr
is NIL. See the data-structures chapter for a reminder. This is the representation of (list 3)
:
[o|/]
|
@@ -1157,7 +1164,7 @@ NIL
;; (X Y Z)
-Iterating 2 by 2 over a list
+Iterating over a plist or 2 by 2 over a list
To iterate over a list, 2 items at a time we use a combination of on
, by
and destructuring.
@@ -1536,7 +1543,7 @@ external-symbols fixnum float t nil of-type
© 2002–2023 the Common Lisp Cookbook Project
diff --git a/lispcookbook.github.io/cl-cookbook/license.html b/lispcookbook.github.io/cl-cookbook/license.html
index f81917e..2adc9da 100644
--- a/lispcookbook.github.io/cl-cookbook/license.html
+++ b/lispcookbook.github.io/cl-cookbook/license.html
@@ -53,14 +53,19 @@
-
- 📢 🤶 ⭐
- Discover our contributor's Lisp course with this Christmas coupon.
-
- Recently added: 18 videos on MACROS.
-
- Learn more.
-
+
+
+
+
+
+
+
+
+
+
+ 📢 New videos: web dev demo part 1, dynamic page with HTMX, Weblocks demo
+
+
📕 Get the EPUB and PDF
@@ -124,7 +129,7 @@ documentation, even if advised of the possibility of such damage.
© 2002–2023 the Common Lisp Cookbook Project
diff --git a/lispcookbook.github.io/cl-cookbook/lispworks.html b/lispcookbook.github.io/cl-cookbook/lispworks.html
index 2b0fe51..451f4cb 100644
--- a/lispcookbook.github.io/cl-cookbook/lispworks.html
+++ b/lispcookbook.github.io/cl-cookbook/lispworks.html
@@ -53,14 +53,19 @@
-
- 📢 🤶 ⭐
- Discover our contributor's Lisp course with this Christmas coupon.
-
- Recently added: 18 videos on MACROS.
-
- Learn more.
-
+
+
+
+
+
+
+
+
+
+
+ 📢 New videos: web dev demo part 1, dynamic page with HTMX, Weblocks demo
+
+
📕 Get the EPUB and PDF
@@ -640,6 +645,33 @@ create a so-called console
image with multiprocessing enabled:
See LispWorks’ documentation.
+Delivering applications
+
+LispWorks’ delivery method revolves around its delivery
function. It has good documentation: https://www.lispworks.com/documentation/lw80/deliv/deliv.htm.
+
+Unlike other open-source Lisps, LispWorks provides a tree-shaker that
+can strip-off packages from the delivered application, allowing to
+build small binaries, around 7MB.
+
+Delivery limitations
+
+LispWorks’s delivery doesn’t include compile-file
into the delivered
+application (nor save-image
, deliver
and the IDE). As such, it isn’t possible to change code on the fly on a
+delivered image. No Swank server, no possibility to use
+ql:quickload
.
+
+To allow remote debugging, LW however provides its own debugger client. On the backend, do:
+
+(require "remote-debugger-client")
+(dbg:start-client-remote-debugging-server :announce t)
+
+
+and on the IDE, do:
+
+(require "remote-debugger-full")
+(dbg:ide-connect-remote-debugging "host" :open-a-listener t)
+
+
See also
@@ -679,7 +712,7 @@ create a so-called console
image with multiprocessing enabled:
© 2002–2023 the Common Lisp Cookbook Project
diff --git a/lispcookbook.github.io/cl-cookbook/macros.html b/lispcookbook.github.io/cl-cookbook/macros.html
index fc450a7..c09367a 100644
--- a/lispcookbook.github.io/cl-cookbook/macros.html
+++ b/lispcookbook.github.io/cl-cookbook/macros.html
@@ -53,14 +53,19 @@
-
- 📢 🤶 ⭐
- Discover our contributor's Lisp course with this Christmas coupon.
-
- Recently added: 18 videos on MACROS.
-
- Learn more.
-
+
+
+
+
+
+
+
+
+
+
+ 📢 New videos: web dev demo part 1, dynamic page with HTMX, Weblocks demo
+
+
📕 Get the EPUB and PDF
@@ -774,7 +779,7 @@ It also shows how to manipulate macros (and their expansion) in Emacs.
© 2002–2023 the Common Lisp Cookbook Project
diff --git a/lispcookbook.github.io/cl-cookbook/misc.html b/lispcookbook.github.io/cl-cookbook/misc.html
index 77f21c4..8aa4ae1 100644
--- a/lispcookbook.github.io/cl-cookbook/misc.html
+++ b/lispcookbook.github.io/cl-cookbook/misc.html
@@ -53,14 +53,19 @@
-
- 📢 🤶 ⭐
- Discover our contributor's Lisp course with this Christmas coupon.
-
- Recently added: 18 videos on MACROS.
-
- Learn more.
-
+
+
+
+
+
+
+
+
+
+
+ 📢 New videos: web dev demo part 1, dynamic page with HTMX, Weblocks demo
+
+
📕 Get the EPUB and PDF
@@ -189,7 +194,7 @@ T
© 2002–2023 the Common Lisp Cookbook Project
diff --git a/lispcookbook.github.io/cl-cookbook/numbers.html b/lispcookbook.github.io/cl-cookbook/numbers.html
index c599e8a..2bdb0e9 100644
--- a/lispcookbook.github.io/cl-cookbook/numbers.html
+++ b/lispcookbook.github.io/cl-cookbook/numbers.html
@@ -53,14 +53,19 @@
-
- 📢 🤶 ⭐
- Discover our contributor's Lisp course with this Christmas coupon.
-
- Recently added: 18 videos on MACROS.
-
- Learn more.
-
+
+
+
+
+
+
+
+
+
+
+ 📢 New videos: web dev demo part 1, dynamic page with HTMX, Weblocks demo
+
+
📕 Get the EPUB and PDF
@@ -699,7 +704,7 @@ or other bit-wise functions.
© 2002–2023 the Common Lisp Cookbook Project
diff --git a/lispcookbook.github.io/cl-cookbook/os.html b/lispcookbook.github.io/cl-cookbook/os.html
index 0a9641a..e95b6d3 100644
--- a/lispcookbook.github.io/cl-cookbook/os.html
+++ b/lispcookbook.github.io/cl-cookbook/os.html
@@ -53,14 +53,19 @@
-
- 📢 🤶 ⭐
- Discover our contributor's Lisp course with this Christmas coupon.
-
- Recently added: 18 videos on MACROS.
-
- Learn more.
-
+
+
+
+
+
+
+
+
+
+
+ 📢 New videos: web dev demo part 1, dynamic page with HTMX, Weblocks demo
+
+
📕 Get the EPUB and PDF
@@ -114,6 +119,8 @@ NIL
You should also note that some of these implementations also provide the ability to set these variables. These include ECL (si:setenv
) and AllegroCL, LispWorks, and CLISP where you can use the functions from above together with setf
. This feature might be important if you want to start subprocesses from your Lisp environment.
+To set an envionmental variable, you can setf
with (uiop:getenv "lisp")
in a implementation-independent way.
+
Also note that the
Osicat
library has the method (environment-variable "name")
, on POSIX-like
@@ -156,7 +163,8 @@ libraries (see next section) make it portable.
(defun my-command-line ()
(or
#+SBCL *posix-argv*
- #+LISPWORKS system:*line-arguments-list*))
+ #+LISPWORKS system:*line-arguments-list*)
+ #+CLISP *args*)
Now it would be handy to access them in a portable way and to parse
@@ -495,7 +503,7 @@ with the returned exit-code
. 0 is success.
(format t "error output is: ~a" error-output)))
-Running visual commands (htop)
+Running interactive and visual commands (htop)
Use uiop:run-program
and set both :input
and :output
to :interactive
:
@@ -506,8 +514,7 @@ with the returned exit-code
. 0 is success.
This will spawn htop
in full screen, as it should.
-It works for more commands (sudo
, vim
…), however not for all interactive
-programs, such as less
or fzf
.
+It works for more commands (sudo
, vim
, less
…).
Piping
@@ -585,7 +592,7 @@ SB-POSIX:WAITPID (fbound)
© 2002–2023 the Common Lisp Cookbook Project
diff --git a/lispcookbook.github.io/cl-cookbook/packages.html b/lispcookbook.github.io/cl-cookbook/packages.html
index ce903c7..d4b02c9 100644
--- a/lispcookbook.github.io/cl-cookbook/packages.html
+++ b/lispcookbook.github.io/cl-cookbook/packages.html
@@ -53,14 +53,19 @@
-
- 📢 🤶 ⭐
- Discover our contributor's Lisp course with this Christmas coupon.
-
- Recently added: 18 videos on MACROS.
-
- Learn more.
-
+
+
+
+
+
+
+
+
+
+
+ 📢 New videos: web dev demo part 1, dynamic page with HTMX, Weblocks demo
+
+
📕 Get the EPUB and PDF
@@ -430,7 +435,7 @@ example:
© 2002–2023 the Common Lisp Cookbook Project
diff --git a/lispcookbook.github.io/cl-cookbook/pattern_matching.html b/lispcookbook.github.io/cl-cookbook/pattern_matching.html
index d4e16e0..b2c18f7 100644
--- a/lispcookbook.github.io/cl-cookbook/pattern_matching.html
+++ b/lispcookbook.github.io/cl-cookbook/pattern_matching.html
@@ -53,14 +53,19 @@
-
- 📢 🤶 ⭐
- Discover our contributor's Lisp course with this Christmas coupon.
-
- Recently added: 18 videos on MACROS.
-
- Learn more.
-
+
+
+
+
+
+
+
+
+
+
+ 📢 New videos: web dev demo part 1, dynamic page with HTMX, Weblocks demo
+
+
📕 Get the EPUB and PDF
@@ -294,7 +299,7 @@ true it is matched against subpattern1.
© 2002–2023 the Common Lisp Cookbook Project
diff --git a/lispcookbook.github.io/cl-cookbook/performance.html b/lispcookbook.github.io/cl-cookbook/performance.html
index c0f9d44..109fe8d 100644
--- a/lispcookbook.github.io/cl-cookbook/performance.html
+++ b/lispcookbook.github.io/cl-cookbook/performance.html
@@ -53,14 +53,19 @@
-
- 📢 🤶 ⭐
- Discover our contributor's Lisp course with this Christmas coupon.
-
- Recently added: 18 videos on MACROS.
-
- Learn more.
-
+
+
+
+
+
+
+
+
+
+
+ 📢 New videos: web dev demo part 1, dynamic page with HTMX, Weblocks demo
+
+
📕 Get the EPUB and PDF
@@ -749,7 +754,7 @@ unless it is declared notinline
.
© 2002–2023 the Common Lisp Cookbook Project
diff --git a/lispcookbook.github.io/cl-cookbook/process.html b/lispcookbook.github.io/cl-cookbook/process.html
index b67cbdf..25113c4 100644
--- a/lispcookbook.github.io/cl-cookbook/process.html
+++ b/lispcookbook.github.io/cl-cookbook/process.html
@@ -53,14 +53,19 @@
-
- 📢 🤶 ⭐
- Discover our contributor's Lisp course with this Christmas coupon.
-
- Recently added: 18 videos on MACROS.
-
- Learn more.
-
+
+
+
+
+
+
+
+
+
+
+ 📢 New videos: web dev demo part 1, dynamic page with HTMX, Weblocks demo
+
+
📕 Get the EPUB and PDF
@@ -2373,7 +2378,7 @@ manual. For SBCL, here is a link to the of
© 2002–2023 the Common Lisp Cookbook Project
diff --git a/lispcookbook.github.io/cl-cookbook/regexp.html b/lispcookbook.github.io/cl-cookbook/regexp.html
index 1fb3891..17a63e4 100644
--- a/lispcookbook.github.io/cl-cookbook/regexp.html
+++ b/lispcookbook.github.io/cl-cookbook/regexp.html
@@ -53,14 +53,19 @@
-
- 📢 🤶 ⭐
- Discover our contributor's Lisp course with this Christmas coupon.
-
- Recently added: 18 videos on MACROS.
-
- Learn more.
-
+
+
+
+
+
+
+
+
+
+
+ 📢 New videos: web dev demo part 1, dynamic page with HTMX, Weblocks demo
+
+
📕 Get the EPUB and PDF
@@ -164,6 +169,17 @@ CL-USER> (mapcar #'parse-integer *)
If SHAREDP is true, the substrings may share structure with TARGET-STRING.
+count-matches (new in 2.1.2, April 2024)
+
+(count-matches regex target-string)
returns a count of all matches of regex
against target-string
:
+
+CL-USER> (ppcre:count-matches "a" "foo bar baz")
+2
+
+CL-USER> (ppcre:count-matches "\\w*" "foo bar baz")
+6
+
+
scan-to-strings, register-groups-bind
The scan-to-strings
function is similar to scan
but returns
@@ -238,7 +254,7 @@ assigning the matching fragment to the variable:
© 2002–2023 the Common Lisp Cookbook Project
diff --git a/lispcookbook.github.io/cl-cookbook/scripting.html b/lispcookbook.github.io/cl-cookbook/scripting.html
index ad8a47b..c67990f 100644
--- a/lispcookbook.github.io/cl-cookbook/scripting.html
+++ b/lispcookbook.github.io/cl-cookbook/scripting.html
@@ -53,14 +53,19 @@
The Common Lisp Cookbook – Scripting. Command line arguments. Executables.
-
- 📢 🤶 ⭐
- Discover our contributor's Lisp course with this Christmas coupon.
-
- Recently added: 18 videos on MACROS.
-
- Learn more.
-
+
+
+
+
+
+
+
+
+
+
+ 📢 New videos: web dev demo part 1, dynamic page with HTMX, Weblocks demo
+
+
📕 Get the EPUB and PDF
@@ -888,7 +893,7 @@ whichever other policy.
© 2002–2023 the Common Lisp Cookbook Project
diff --git a/lispcookbook.github.io/cl-cookbook/sockets.html b/lispcookbook.github.io/cl-cookbook/sockets.html
index 577cf7c..1ebae1c 100644
--- a/lispcookbook.github.io/cl-cookbook/sockets.html
+++ b/lispcookbook.github.io/cl-cookbook/sockets.html
@@ -53,14 +53,19 @@
-
- 📢 🤶 ⭐
- Discover our contributor's Lisp course with this Christmas coupon.
-
- Recently added: 18 videos on MACROS.
-
- Learn more.
-
+
+
+
+
+
+
+
+
+
+
+ 📢 New videos: web dev demo part 1, dynamic page with HTMX, Weblocks demo
+
+
📕 Get the EPUB and PDF
@@ -253,7 +258,7 @@ and #(8 7 6 5 4 3 2 1)
on the second one.
© 2002–2023 the Common Lisp Cookbook Project
diff --git a/lispcookbook.github.io/cl-cookbook/strings.html b/lispcookbook.github.io/cl-cookbook/strings.html
index 22b51e8..c7b69a8 100644
--- a/lispcookbook.github.io/cl-cookbook/strings.html
+++ b/lispcookbook.github.io/cl-cookbook/strings.html
@@ -53,14 +53,19 @@
-
- 📢 🤶 ⭐
- Discover our contributor's Lisp course with this Christmas coupon.
-
- Recently added: 18 videos on MACROS.
-
- Learn more.
-
+
+
+
+
+
+
+
+
+
+
+ 📢 New videos: web dev demo part 1, dynamic page with HTMX, Weblocks demo
+
+
📕 Get the EPUB and PDF
@@ -801,7 +806,7 @@ in this case.
;; (42 41 1)
-
To any number: read-from-string
+
To any number: read-from-string
Be aware that the full reader is in effect if you’re using this
function. This can lead to vulnerability issues. You should use a
@@ -834,6 +839,23 @@ SYMBOL
"gotcha"
+
Protecting read-from-string
+
+
At the very least, if you are reading data coming from the outside, use this:
+
+
(let ((cl:*read-eval* nil))
+ (read-from-string "…"))
+
+
+
This prevents code to be evaluated at read-time. That way our last example, using the #.
reader macro, would not work. You’ll get the error “can’t read #. while *READ-EVAL* is NIL”.
+
+
And better yet, for more protection from a possibly custom readtable that would introduce another reader macro:
+
+
(with-standard-io-syntax
+ (let ((cl:*read-eval* nil))
+ (read-from-string "…")))
+
+
To a float: the parse-float library
There is no built-in function similar to parse-integer
to parse
@@ -1275,7 +1297,7 @@ _ - Conditional Newline
© 2002–2023 the Common Lisp Cookbook Project
diff --git a/lispcookbook.github.io/cl-cookbook/systems.html b/lispcookbook.github.io/cl-cookbook/systems.html
index d666bc8..3e14a3b 100644
--- a/lispcookbook.github.io/cl-cookbook/systems.html
+++ b/lispcookbook.github.io/cl-cookbook/systems.html
@@ -53,14 +53,19 @@
-
- 📢 🤶 ⭐
- Discover our contributor's Lisp course with this Christmas coupon.
-
- Recently added: 18 videos on MACROS.
-
- Learn more.
-
+
+
+
+
+
+
+
+
+
+
+ 📢 New videos: web dev demo part 1, dynamic page with HTMX, Weblocks demo
+
+
📕 Get the EPUB and PDF
@@ -298,7 +303,7 @@ generate a project skeleton. It will create a default ASDF definition,
© 2002–2023 the Common Lisp Cookbook Project
diff --git a/lispcookbook.github.io/cl-cookbook/testing.html b/lispcookbook.github.io/cl-cookbook/testing.html
index 1c04871..a9d3c3a 100644
--- a/lispcookbook.github.io/cl-cookbook/testing.html
+++ b/lispcookbook.github.io/cl-cookbook/testing.html
@@ -53,14 +53,19 @@
-
- 📢 🤶 ⭐
- Discover our contributor's Lisp course with this Christmas coupon.
-
- Recently added: 18 videos on MACROS.
-
- Learn more.
-
+
+
+
+
+
+
+
+
+
+
+ 📢 New videos: web dev demo part 1, dynamic page with HTMX, Weblocks demo
+
+
📕 Get the EPUB and PDF
@@ -932,7 +937,7 @@ a problem.
© 2002–2023 the Common Lisp Cookbook Project
diff --git a/lispcookbook.github.io/cl-cookbook/trace-dialog.png b/lispcookbook.github.io/cl-cookbook/trace-dialog.png
new file mode 100644
index 0000000000000000000000000000000000000000..805bca8f7b4e7b2de1ed76fbeb005f7cd0b1b7d6
GIT binary patch
literal 65573
zcmd43Ra9Hu+qb*0LQByiZE+}WE$&t*#oZ+oiWh>rwYV2AE`{RmPH}g4w-ADR;G@s;
zfBkvKKG}PZ?|_jp*T?~DX0AKG>%Qhpu%f&q#
5Df3h485>*Mn}HpAU
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