- Introduction
- Tutorial
- The restriction to the Org file
- install polymode in Emacs
- how to insert code block quickly
- a new code block header argument ~load~
- Reference named blocks as global parameters
- How to debug Org file in LispWorks IDE
- How to integrate with namded-readtables
- How to write user initialization file with literate programming style
- how to include Org code with ASDF package-inferred-system extension
- how to tangle to a bundle of lisp files from one Org file
- packages written by literate-lisp
- A demo literate application
literate-lisp provides an easy way to use literal programming in Common Lisp language. It extends the Common Lisp reader syntax so a Common Lisp vendor can read Org files as Common Lisp source files.
By using this package (literate-lisp), Emacs Org mode, and Emacs Lisp library polymode, literate programming can be very easy, with one Org file containing both documentation and source code, and this Org file can interact well with SLIME.
The implementation detail of literate-lisp is in file ./literate-lisp.org (pdf version).
This library contains the following files:
- ./literate-lisp.org
The implementation and documentation of literate lisp reader. - ./lisp
This directory contains the tangled code of literate lisp reader, generated from ./literate-lisp.org - ./literate-lisp.pdf
The weaved documentation, generated from ./literate-lisp.org by Org mode’s publish feature. - ./readme.org
This file contains introduction and demo code for how to do literate lisp in an Org file. - ./puzzle.org
This file contains a puzzle solver to show how to do literate lisp in an Org file. - continuous-integration.yml
The config file used by Web service GitHub actions to test this library. - ./literate-lisp.asd
The ASDF definition for literate-lisp project. - ./literate-demo.asd
The ASDF definition for literate demo project.
The Org file should start with a comment character and a space character(“# “), to drive lisp reader into Org syntax. Actually it can be a convenient way for us to specify some local variables, for example I often put them in the first line of an Org file:
# -*- encoding:utf-8 Mode: POLY-ORG; -*- ---
Which make Emacs open file with utf-8 encoding and poly-org-mode.
It’s better to edit the Org file with polymode, which will make code block use its native file mode. The following Emacs Lisp scripts in .emacs will install it.
(use-package poly-org
:ensure t)
Please have a look of the section How to insert code block in Org file in library literate-elisp.
Please note that literate-lisp
now support parsing Org property values (property syntax),
so there is no need to insert :load no
in header argument now, you can set them as
Org properties.
To disable insertion of unnecessary header argument,
you can set Org property literate-insert-header
to no
.
Please have a look of the section new defined header argument load in ./literate-lisp.org.
If a block has a named for it, that is, with a #+NAME:
before it like this:
#+NAME: js-demo-code #+BEGIN_SRC js document.getElementById("demo").innerHTML = "Hello JavaScript"; #+END_SRC
Then after loading, a global parameter js-demo-code
will contain the string in above block.
It is more friendly than write this string in lisp directly,
because org-mode
can provide syntax for it and poly-mode
can even enable us edit this code block in js-mode
.
You can visit these named blocks by Emacs Lisp function org-babel-goto-named-src-block, or by hacking sly like this:
(defun sly-edit-definition-of-named-block (&optional name method)
(when (string-prefix-p "#+END_" (string-trim (buffer-substring (line-beginning-position) (line-end-position))) t)
(let ((case-fold-search t))
(search-forward-regexp (format "#\\+NAME:\s+%s" name))
(forward-line 2)
(goto-char (line-beginning-position)))))
(eval-after-load "sly"
'(advice-add 'sly-edit-definition :after #'sly-edit-definition-of-named-block))
You can hack slime the same way.
NOTE You have to install literate-lisp to active *readtable*
in sly when using SBCL to make above patch work, because
the SBCL backend in sly
will read the source code inside file when find definitions.
(literate-lisp:install-globally)
You have to add the following code in your .lispworks
to enable the debug facility in Lispworks Editor.
(defun check-org-mode (buffer truename)
(when (and truename (equal (pathname-type truename) "org"))
(setf (editor:buffer-major-mode buffer) "Lisp")))
(editor:add-global-hook editor::read-file-hook 'check-org-mode)
Thanks for Martin Simmons in LispWorks to support the above configuration code.
You may find that named-readtables is friendly to define the syntax for literate-lisp in your code like this:
(named-readtables:defreadtable literate-lisp
(:merge :standard)
(:dispatch-macro-char #\# #\space #'literate-lisp::sharp-space)
(:dispatch-macro-char #\# #\+ #'literate-lisp::sharp-plus))
You can put all initialization code in an Org source file, all you need is to load literate-lisp
firstly.
For example, you can put the following code in file ~$HOME/.sbclrc~ for SBCL.
(require :asdf)
#-quicklisp
(let ((quicklisp-init "~/quicklisp/setup.lisp")
(quicklisp-install "~/quicklisp.lisp"))
(cond ((probe-file quicklisp-init)
(format *terminal-io* "loading quicklisp...~%")
(load quicklisp-init)
(format *terminal-io* "loading quicklisp...done~%"))
((probe-file quicklisp-install)
(load quicklisp-install)
(funcall (intern "INSTALL" :quicklisp-quickstart)))))
(load "~/projects/common-lisp/literate-lisp/literate-lisp.asd")
(ql:quickload :literate-lisp)
(literate-lisp:with-literate-syntax
(load "~/projects/common-lisp/config/init-lisp.org"))
I find it useful for various Lisp vendors so all initialization code for them can be in just one file.
The ASDF package-inferred-system extension is wonderful, in which each file is its own system, and dependencies are deduced from the defpackage form or its variant, uiop:define-package. You can also use literate-lisp to make a package inferred system by writing an ASD definition like this:
(asdf:defsystem literate-libraries
:serial t
:defsystem-depends-on (:literate-lisp)
:default-component-class :org
:class :package-inferred-system)
Here *:class :package-inferred-system*
enables the package-inferred-system extension, and *:default-component-class :org*
means
that ASDF will look for all Org files to find out a system and load it.
For example, you can create an Org file in the same directory of above ASD definition file named as utilities.org and contains the following code
# -*- encoding:utf-8 Mode: POLY-ORG; -*- --- * Create a package for this package inferred system #+BEGIN_SRC lisp (defpackage literate-libraries/utilities (:use :cl) (:import-from :flexi-streams :octet :make-flexi-stream) (:import-from :log4cl :log-config) (:documentation "a utility module.")) #+END_SRC * implementation ... ...
After loading the above ASD definition file, you can load system literate-libraries/utilities in your REPL.
(load "/some/path/literate-libraries.asd")
(ql:quickload :literate-libraries/utilities)
Please upgrade to ASDF 3.3.4.5 or later, it is not supported in earlier ASDF versions.
Yes, now you can tangle one Org file to a bundle of lisp files, so to share it to team members with more clear interface.
Please have a look of tangle to multiple files for one Org file
or the usage of Org property LITERATE_EXPORT_PACKAGE
and LITERATE_EXPORT_NAME
in file ./literate-lisp.org.
- s-graphviz an S-expression presentation of GraphViz DOT Language
We use the original ASD definition file, and extend the ASDF syntax(The documentation of extended ASDF syntax can be found in literate-lisp.org).
In a short word, we should load literate-lisp
by ASDF keyword :defsystem-depends-on
and
declare the Org source file with new ASDF keyword :org
.
Now let’s define the ASDF system file ./literate-demo.asd for this demo package
(asdf:defsystem literate-demo
:author "Xu Jingtao <jingtaozf@gmail.com>"
:version "0.1"
:licence "MIT"
:serial t
:description "a demo project of literate-lisp"
:defsystem-depends-on ("literate-lisp")
:depends-on (:iterate #+dev :clgplot)
:components ((:module :demo :pathname "./"
:components ((:org "puzzle")
(:org "readme"))))
:properties ((version "0.1")))
Which will load ./puzzle.org and this file directly as a lisp source file.
The whole content of ASDF definition file is in ./literate-demo.asd.
(defpackage :literate-demo
(:use :cl)
(:export ))
(in-package :literate-demo)
The FiveAM library is used to test.
(eval-when (:compile-toplevel :load-toplevel :execute)
(unless (find-package :fiveam)
#+quicklisp (ql:quickload :fiveam)
#-quicklisp (asdf:load-system :fiveam)))
(5am:def-suite literate-demo-suite :description "The test suite of literate-demo.")
(5am:in-suite literate-demo-suite)
Let’s define a named code block for some javascript code:
{
console.log("Hello");
}
Then try to read it in our test case
(5am:test named-block
(5am:is (stringp js-demo-code-1))
(5am:is (not (null (position #\" js-demo-code-1 :test #'char=)))))
This function is the entry point to run all tests and return true if all test cases pass.
(defun run-test ()
(5am:run! 'literate-demo-suite))