literate-lisp
literate-lisp copied to clipboard
Load Common Lisp code blocks from Org files
-- encoding:utf-8 Mode: POLY-ORG; -- ---
#+Startup: noindent #+PROPERTY: literate-lang lisp #+PROPERTY: literate-load yes #+PROPERTY: literate-insert-header no
[[http://quickdocs.org/literate-lisp/][file:http://quickdocs.org/badge/literate-lisp.svg]] [[https://github.com/jingtaozf/literate-lisp/actions][file:https://github.com/jingtaozf/literate-lisp/workflows/Continous%20Integration/badge.svg]]
- Table of Contents :TOC:noexport:
- [[#introduction][Introduction]]
- [[#tutorial][Tutorial]]
- [[#the-restriction-to-the-org-file][The restriction to the Org file]]
- [[#install-polymode-in-emacs][install polymode in Emacs]]
- [[#how-to-insert-code-block-quickly][how to insert code block quickly]]
- [[#a-new-code-block-header-argument-load][a new code block header argument ~load~]]
- [[#reference-named-blocks-as-global-parameters][Reference named blocks as global parameters]]
- [[#how-to-debug-org-file-in-lispworks-ide][How to debug Org file in LispWorks IDE]]
- [[#how-to-integrate-with-namded-readtables][How to integrate with namded-readtables]]
- [[#how-to-write-user-initialization-file-with-literate-programming-style][How to write user initialization file with literate programming style]]
- [[#how-to-include-org-codes-with-asdf-package-inferred-system-extension][how to include Org code with ASDF package-inferred-system extension]]
- [[#how-to-tangle-to-a-bundle-of-lisp-files-from-one-org-file][how to tangle to a bundle of lisp files from one Org file]]
- [[#packages-written-by-literate-lisp][packages written by literate-lisp]]
- [[#a-demo-literate-application][A demo literate application]]
- [[#the-asd-file][The ASD file]]
- [[#a-demo-package-for-this-file][a demo package for this file]]
- [[#test-cases][Test cases]]
- Introduction [[https://github.com/jingtaozf/literate-lisp][literate-lisp]] provides an easy way to use [[http://www.literateprogramming.com/][literal programming]] in Common Lisp language. It extends the Common Lisp [[https://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node187.html][reader syntax]] so a Common Lisp vendor can read Org files as Common Lisp source files.
By using this package ([[https://github.com/jingtaozf/literate-lisp][literate-lisp]]), Emacs [[https://orgmode.org/][Org mode]], and Emacs Lisp library [[https://polymode.github.io/][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 [[https://common-lisp.net/project/slime/][SLIME]].
The implementation detail of [[https://github.com/jingtaozf/literate-lisp][literate-lisp]] is in file [[./literate-lisp.org]] ([[./literate-lisp.pdf][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 [[https://orgmode.org/manual/Triggering-publication.html#Triggering-publication][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.
- [[./.github/workflows/continuous-integration.yml][continuous-integration.yml]] \ The config file used by Web service [[https://github.com/jingtaozf/literate-lisp/actions][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.
- Tutorial ** The restriction to the Org file 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: #+BEGIN_SRC org
-- encoding:utf-8 Mode: POLY-ORG; -- ---
#+END_SRC Which make Emacs open file with utf-8 encoding and [[https://github.com/polymode/poly-org][poly-org-mode]]. ** Installing polymode in Emacs It's better to edit the Org file with [[https://polymode.github.io/][polymode]], which will make code block use its native file mode. The following Emacs Lisp scripts in .emacs will install it. #+BEGIN_SRC elisp (use-package poly-org :ensure t) #+END_SRC ** How to insert code block quickly Please have a look of the section [[https://github.com/jingtaozf/literate-elisp/blob/master/literate-elisp.org#how-to-insert-code-block-in-org-file][How to insert code block in Org file]] in library [[https://github.com/jingtaozf/literate-elisp][literate-elisp]].
Please note that =literate-lisp= now support parsing Org property values ([[https://orgmode.org/manual/Property-Syntax.html][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=. ** The ~load~ code block header argument Please have a look of the section [[./literate-lisp.org#new-defined-header-argument-load][new defined header argument load]] in [[./literate-lisp.org]]. ** Reference named blocks as global parameters If a [[https://orgmode.org/manual/Blocks.html][block]] has a named for it, that is, with a =#+NAME:= before it like this: #+begin_example ,#+NAME: js-demo-code ,#+BEGIN_SRC js document.getElementById("demo").innerHTML = "Hello JavaScript"; ,#+END_SRC #+end_example 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 [[https://orgmode.org/worg/orgcard.html#org11fbe72][org-babel-goto-named-src-block]], or by hacking [[https://github.com/joaotavora/sly][sly]] like this: #+BEGIN_SRC elisp :load no (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)) #+END_SRC You can hack [[https://common-lisp.net/project/slime/][slime]] the same way.
NOTE You have to install literate-lisp to active =readtable= in [[https://github.com/joaotavora/sly][sly]] when using SBCL to make above patch work, because the SBCL backend in =sly= will [[https://github.com/joaotavora/sly/blob/master/slynk/backend/sbcl.lisp#L423][read the source code inside file]] when find definitions. #+BEGIN_SRC lisp :load no (literate-lisp:install-globally) #+END_SRC ** How to debug Org file in LispWorks IDE You have to add the following code in your ~.lispworks~ to enable the debug facility in Lispworks Editor. #+BEGIN_SRC lisp :load no (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) #+END_SRC Thanks for Martin Simmons in [[http://www.lispworks.com/][LispWorks]] to support the above configuration code. ** How to integrate with namded-readtables You may find that [[https://github.com/melisgl/named-readtables][named-readtables]] is friendly to define the syntax for literate-lisp in your code [[https://github.com/jingtaozf/literate-lisp/issues/12#issuecomment-710256276][like this]]: #+BEGIN_SRC lisp :load no (named-readtables:defreadtable literate-lisp (:merge :standard) (:dispatch-macro-char ## #\space #'literate-lisp::sharp-space) (:dispatch-macro-char ## #+ #'literate-lisp::sharp-plus)) #+END_SRC
** How to write user initialization file with literate programming style 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 [[http://www.sbcl.org/manual/#Initialization-Files][~$HOME/.sbclrc~]] for SBCL. #+BEGIN_SRC lisp :load no (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")) #+END_SRC I find it useful for various Lisp vendors so all initialization code for them can be in just one file.
** How to include Org code with ASDF package-inferred-system extension The [[https://common-lisp.net/project/asdf/asdf.html#The-package_002dinferred_002dsystem-extension][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: #+BEGIN_SRC lisp :load no (asdf:defsystem literate-libraries :serial t :defsystem-depends-on (:literate-lisp) :default-component-class :org :class :package-inferred-system) #+END_SRC 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 #+begin_example
-- 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 ... ... #+end_example After loading the above ASD definition file, you can load system literate-libraries/utilities in your REPL. #+BEGIN_SRC lisp :load no (load "/some/path/literate-libraries.asd") (ql:quickload :literate-libraries/utilities) #+END_SRC
Please upgrade to ASDF 3.3.4.5 or later, it is not supported in earlier ASDF versions.
** How to tangle to a bundle of lisp files from one Org file 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 [[./literate-lisp.org#tangle-to-multiple-files-for-one-org-file][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]].
** Packages written by literate-lisp
- [[https://github.com/jingtaozf/s-graphviz][s-graphviz]] an S-expression presentation of GraphViz DOT Language ** A demo literate application *** The ASD file We use the original ASD definition file, and extend the ASDF syntax(The documentation of extended ASDF syntax can be found in [[https://github.com/jingtaozf/literate-lisp/blob/master/literate-lsp.org#make-asdf-handle-org-file-correctly][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 #+BEGIN_SRC elisp :load no (asdf:defsystem literate-demo :author "Xu Jingtao [email protected]" :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"))) #+END_SRC 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]]. *** A demo package for this file #+BEGIN_SRC lisp (defpackage :literate-demo (:use :cl) (:export )) (in-package :literate-demo) #+END_SRC *** Test cases :PROPERTIES: :literate-load: test :END: **** Preparation The [[https://common-lisp.net/project/fiveam/][FiveAM]] library is used to test. #+BEGIN_SRC lisp (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) #+END_SRC **** test case for named block Let's define a named code block for some javascript code: #+NAME: js-demo-code-1 #+begin_src js { console.log("Hello"); } #+end_src Then try to read it in our test case #+BEGIN_SRC lisp (5am:test named-block (5am:is (stringp js-demo-code-1)) (5am:is (not (null (position #" js-demo-code-1 :test #'char=))))) #+END_SRC **** run all tests in this library This function is the entry point to run all tests and return true if all test cases pass. #+BEGIN_SRC lisp (defun run-test () (5am:run! 'literate-demo-suite)) #+END_SRC