2049 lines
73 KiB
Org Mode
2049 lines
73 KiB
Org Mode
|
#+title: From Emacs Lisp to Common Lisp
|
|||
|
#+author: Gerard Vermeulen
|
|||
|
#+macro: kbd (eval (by-backend-kbd-org-macro $1))
|
|||
|
#+property: header-args:emacs-lisp :exports code :results silent :tangle keu.el
|
|||
|
#+property: header-args:lisp :eval never-export :tangle keu.lisp
|
|||
|
#+property: tangle-dir ./site-lisp/
|
|||
|
#+startup: showeverything
|
|||
|
#+time-stamp-file: -*- LaTeX -*-
|
|||
|
#+cite_export: biblatex phys,biblabel=brackets,doi=true
|
|||
|
#+latex_class: article
|
|||
|
#+latex_class_options: [10pt,english,svgnames]
|
|||
|
#+begin_src latex :noweb yes :results raw
|
|||
|
,#+latex_header: <<latex-header>>
|
|||
|
#+end_src
|
|||
|
#+name: latex-header
|
|||
|
#+begin_src latex :exports none
|
|||
|
% -*- LaTeX -*-
|
|||
|
% LaTeX PREAMBLE:
|
|||
|
% See: https://list.orgmode.org/87o807r7fr.fsf@posteo.net/
|
|||
|
% From: "Juan Manuel Macías" <maciaschain@posteo.net>
|
|||
|
% To: orgmode <emacs-orgmode@gnu.org>
|
|||
|
% Subject: [tip] Insert arbitrary LaTeX code at the beginning of any float environment
|
|||
|
% Date: Sun, 08 May 2022 22:22:16 +0000
|
|||
|
% Message-ID: <87o807r7fr.fsf@posteo.net>
|
|||
|
% LANGUAGE:
|
|||
|
\usepackage{babel}
|
|||
|
\usepackage{fvextra}
|
|||
|
\usepackage{csquotes}
|
|||
|
% LISTS:
|
|||
|
\usepackage{enumitem}
|
|||
|
\setlist{noitemsep}
|
|||
|
% CAPTIONS, LISTINGS, and SUBCAPTIONS
|
|||
|
% Section 2.6 of caption-eng.pdf (texdoc caption) explains that the sign
|
|||
|
% of "skip" depends on the assumption "position=above" or "position=below".
|
|||
|
% The assumption should match the real caption position in the LaTeX code.
|
|||
|
% Use the "DejaVu Sans Mono" as typewriter font for engrave-faces listings.
|
|||
|
\usepackage{caption}
|
|||
|
\captionsetup[listing]{position=below,skip=0em}
|
|||
|
\setmonofont{DejaVu Sans Mono}[Scale=MatchLowercase]
|
|||
|
\usepackage{subcaption}
|
|||
|
% TABLES:
|
|||
|
% https://tex.stackexchange.com/a/341254 answers:
|
|||
|
% "How differ the environments tabular, tabular*, and tabularx?"
|
|||
|
% https://emacs.stackexchange.com/a/28903 answers:
|
|||
|
% "How to tweak org-mode table colors for latex export only?"
|
|||
|
% https://tex.stackexchange.com/a/468596 answers:
|
|||
|
% "How to format LaTeX using siunitx?"
|
|||
|
% https://tex.stackexchange.com/a/355396 answers:
|
|||
|
% "How to load colortbl and xcolor for maximal interoperability?"
|
|||
|
\usepackage{booktabs}
|
|||
|
\usepackage[table]{xcolor}
|
|||
|
\usepackage{tabularx} % DANGER: beware of Org table :width and :align options!
|
|||
|
% STANDALONE FIGURES AND TABLES:
|
|||
|
% https://xuc.me/blog/reuse-tikz-figures-in-articles-and-slides/ explains that
|
|||
|
% making fake beamer commands for standalone pictures is less error-prone than
|
|||
|
% using beamerarticle.
|
|||
|
\usepackage{standalone}
|
|||
|
\usepackage{xparse}
|
|||
|
\NewDocumentCommand{\onslide}{s t+ d<>}{}
|
|||
|
\NewDocumentCommand{\only}{d<>}{}
|
|||
|
\NewDocumentCommand{\uncover}{d<>}{}
|
|||
|
\NewDocumentCommand{\visible}{d<>}{}
|
|||
|
\NewDocumentCommand{\invisible}{d<>}{}
|
|||
|
% PAGE LAYOUT:
|
|||
|
\usepackage[headheight=20mm,top=40mm,bottom=20mm,left=0.1\paperwidth,right=0.1\paperwidth,heightrounded,verbose]{geometry}
|
|||
|
% SI UNITS:
|
|||
|
\usepackage[load-configurations=abbreviations]{siunitx}
|
|||
|
\sisetup{
|
|||
|
range-phrase = \ensuremath{\text{\,\textendash\,}},
|
|||
|
range-units = brackets,
|
|||
|
separate-uncertainty,
|
|||
|
}
|
|||
|
\DeclareSIUnit\dollar{\$}
|
|||
|
\DeclareSIUnit\euro{€}
|
|||
|
\DeclareSIUnit\mK{\milli\kelvin}
|
|||
|
\DeclareSIUnit\mbar{\milli\bar}
|
|||
|
\DeclareSIUnit\micron{\micro\meter}
|
|||
|
\DeclareSIUnit\nW{\nano\watt}
|
|||
|
% TIKZ AND PGFPLOTS:
|
|||
|
\usepackage{tikz}
|
|||
|
\usetikzlibrary{3d,arrows,backgrounds,calc,plotmarks}
|
|||
|
\usepackage{pgfplots}
|
|||
|
\pgfplotsset{compat=newest}
|
|||
|
\def\axisdefaultwidth{90mm}
|
|||
|
\def\axisdefaultheight{75mm}
|
|||
|
% SYMBOLS FOR FLUID MECHANICS
|
|||
|
\newcommand{\Nusselt}{\mbox{\textit{Nu}}}
|
|||
|
\newcommand{\Reynolds}{\mbox{\textit{Re}}}
|
|||
|
% FLOAT BARRIERS:
|
|||
|
% https://tex.stackexchange.com/a/118667 answers:
|
|||
|
% "How to make float barriers for subsections as placeins does for sections?"
|
|||
|
% 1. Make section an implicit float barrier:
|
|||
|
\usepackage[section]{placeins}
|
|||
|
% 2. Make subsection an implicit float barrier:
|
|||
|
\makeatletter
|
|||
|
\AtBeginDocument{%
|
|||
|
\expandafter\renewcommand\expandafter\subsection\expandafter{%
|
|||
|
\expandafter\@fb@secFB\subsection
|
|||
|
}%
|
|||
|
}
|
|||
|
\makeatother
|
|||
|
% 3. Make subsubsection an implicit float barrier:
|
|||
|
\makeatletter
|
|||
|
\AtBeginDocument{%
|
|||
|
\expandafter\renewcommand\expandafter\subsubsection\expandafter{%
|
|||
|
\expandafter\@fb@secFB\subsubsection
|
|||
|
}%
|
|||
|
}
|
|||
|
\makeatother
|
|||
|
% HEADERS AND FOOTERS
|
|||
|
\usepackage{fancyhdr}
|
|||
|
\usepackage{lastpage}
|
|||
|
|
|||
|
\pagestyle{fancy}
|
|||
|
\fancyhf{}
|
|||
|
\renewcommand{\footrulewidth}{0.4pt}
|
|||
|
\fancyfoot[C]{\emph{
|
|||
|
FIFO in Emacs Lisp and Common Lisp -- Gerard Vermeulen
|
|||
|
}
|
|||
|
}
|
|||
|
\renewcommand{\headrulewidth}{0.4pt}
|
|||
|
\fancyhead[L]{\includegraphics[height=1.8cm]{Org-mode-unicorn.png}}
|
|||
|
\fancyhead[C]{
|
|||
|
Page: \thepage/\pageref{LastPage} \\
|
|||
|
\text{ } \\
|
|||
|
\text{ } \\
|
|||
|
DRAFT
|
|||
|
}
|
|||
|
\fancyhead[R]{\includegraphics[height=1.8cm]{Emacs-logo.png}}
|
|||
|
#+end_src
|
|||
|
|
|||
|
#+name: lst:emacs-lisp-setup
|
|||
|
#+begin_src emacs-lisp :exports none :results silent :tangle no
|
|||
|
(with-eval-after-load 'ox-latex
|
|||
|
(make-variable-buffer-local 'org-export-filter-src-block-functions)
|
|||
|
(add-to-list 'org-export-filter-src-block-functions
|
|||
|
'org-latex-engraved-source-block-filter)
|
|||
|
|
|||
|
(when (fboundp 'smart-latex-engrave-org-source-blocks)
|
|||
|
(smart-latex-engrave-org-source-blocks))
|
|||
|
|
|||
|
(make-variable-buffer-local 'org-latex-compiler-file-string)
|
|||
|
(setq org-latex-compiler-file-string
|
|||
|
"%% -*- LaTeX -*-\n%% Intended LaTeX compiler: %s\n")
|
|||
|
(make-variable-buffer-local 'org-latex-title-command)
|
|||
|
(setq org-latex-title-command "\\maketitle\\thispagestyle{fancy}")
|
|||
|
|
|||
|
(make-variable-buffer-local 'org-latex-toc-command)
|
|||
|
(setq org-latex-toc-command "
|
|||
|
\\tableofcontents\\label{toc}
|
|||
|
% \\listoffigures
|
|||
|
\\listoflistings
|
|||
|
% \\listoftables
|
|||
|
\\newpage
|
|||
|
"))
|
|||
|
#+end_src
|
|||
|
|
|||
|
\clearpage
|
|||
|
|
|||
|
* Introduction
|
|||
|
:PROPERTIES:
|
|||
|
:CUSTOM_ID: sec:introduction
|
|||
|
:END:
|
|||
|
|
|||
|
The Emacs Lisp ~queue~ library (src_emacs-lisp{(describe-package 'queue)})
|
|||
|
implements a ~QUEUE~ by means of a ~cl-defstruct~ holding the ~head~ and the
|
|||
|
~rear~ of the ~QUEUE~. Here, I re-implement a ~QUEUE~ in by translating the
|
|||
|
Emacs Lisp ~queue.el~ file to the Common Lisp ~queue.lisp~ file.
|
|||
|
|
|||
|
Note: I am trying to implement similar functionality starting from the forms
|
|||
|
~(cons nil nil)~ or ~(list nil nil)~ instead of the ~queue~ structure. Those
|
|||
|
unsuccessful attempts are named ~FIFO~ or ~KEU~.
|
|||
|
|
|||
|
Note: the words ~FIFO~ and ~QUEUE~ name identical objects in computer science
|
|||
|
and ~KEU~ is a Dutch translation of the French word ~QUEUE~.
|
|||
|
|
|||
|
* Elisp Regression Testing of "queue.el"
|
|||
|
:PROPERTIES:
|
|||
|
:CUSTOM_ID: sec:test-queue-el
|
|||
|
:END:
|
|||
|
|
|||
|
The file [[./test-queue.el][test-queue.el]] contains an implementation of Elisp Regression Testing
|
|||
|
for ~queue.el~. Implementing [[./test-queue.el][test-queue.el]] clarifies the ~queue.el~ client
|
|||
|
contract which is useful for any re-implementation of ~queue.el~ in Emacs Lisp
|
|||
|
or Common Lisp. The file [[./test-queue.el][test-queue.el]] has been tangled from listings
|
|||
|
[[lst:1st-part-test-queue]], [[lst:2nd-part-test-queue]], and [[lst:3rd-part-test-queue]].
|
|||
|
|
|||
|
#+caption[Elisp Regression Testing queue: 1st part of test-queue.el]:
|
|||
|
#+caption: Elisp Regression Testing ~queue~: 1st part of ~test-queue.el~.
|
|||
|
#+name: lst:1st-part-test-queue
|
|||
|
#+begin_src emacs-lisp -n :tangle test-queue.el
|
|||
|
;;; test-queue.el --- ERT for queue.el -*- lexical-binding: t; -*-
|
|||
|
|
|||
|
(require 'ert)
|
|||
|
(require 'queue)
|
|||
|
|
|||
|
(ert-deftest queue-create-test ()
|
|||
|
(let ((queue (queue-create)))
|
|||
|
(should (equal t (and (equal (queue-head queue) nil)
|
|||
|
(equal (queue-tail queue) nil))))))
|
|||
|
|
|||
|
(ert-deftest queue-enqueue-test ()
|
|||
|
(let ((data '((nil) (nil a) (nil a nil) (nil a nil b) (nil a nil b nil)
|
|||
|
(a) (a nil) (a nil b) (a nil b nil) (a nil b nil c))))
|
|||
|
(dolist (datum data)
|
|||
|
(let ((queue (queue-create)))
|
|||
|
(dolist (item datum)
|
|||
|
(queue-enqueue queue item))
|
|||
|
(should (equal t (and (equal datum (queue-head queue))
|
|||
|
(equal (list (car (reverse datum)))
|
|||
|
(queue-tail queue)))))))))
|
|||
|
|
|||
|
(ert-deftest queue-dequeue-test ()
|
|||
|
(let ((data '((nil) (nil a) (nil a nil) (nil a nil b) (nil a nil b nil)
|
|||
|
(a) (a nil) (a nil b) (a nil b nil) (a nil b nil c))))
|
|||
|
(dolist (datum data)
|
|||
|
(let ((queue (queue-create)))
|
|||
|
(dolist (item datum)
|
|||
|
(queue-enqueue queue item))
|
|||
|
(let (mutad)
|
|||
|
(dotimes (n (length datum))
|
|||
|
(push (queue-dequeue queue) mutad))
|
|||
|
(should (equal datum (reverse mutad))))))))
|
|||
|
<
|
|||
|
(ert-deftest queue-prepend-test ()
|
|||
|
(let ((data '((nil) (nil a) (nil a nil) (nil a nil b) (nil a nil b nil)
|
|||
|
(a) (a nil) (a nil b) (a nil b nil) (a nil b nil c))))
|
|||
|
(dolist (datum data)
|
|||
|
(let ((queue (queue-create)) enqueue prepend)
|
|||
|
;; Enqueue/prepend items at even/odd positions:
|
|||
|
(dotimes (n (length datum))
|
|||
|
(let ((item (nth n datum)))
|
|||
|
(if (= 0 (mod n 2))
|
|||
|
(progn
|
|||
|
(push item enqueue)
|
|||
|
(queue-enqueue queue item))
|
|||
|
(push item prepend)
|
|||
|
(queue-prepend queue item))))
|
|||
|
(should (equal (append prepend (reverse enqueue))
|
|||
|
(queue-all queue)))))))
|
|||
|
#+end_src
|
|||
|
|
|||
|
#+caption[Elisp Regression Testing queue: 2nd part of test-queue.el]:
|
|||
|
#+caption: Elisp Regression Testing ~queue~: 2nd part of ~test-queue.el~.
|
|||
|
#+name: lst:2nd-part-test-queue
|
|||
|
#+begin_src emacs-lisp -n :tangle test-queue.el
|
|||
|
(ert-deftest queue-empty-test ()
|
|||
|
(let ((data '((nil) (nil a) (nil a nil) (nil a nil b) (nil a nil b nil)
|
|||
|
(a) (a nil) (a nil b) (a nil b nil) (a nil b nil c))))
|
|||
|
(dolist (datum data)
|
|||
|
(let ((queue (queue-create)))
|
|||
|
(queue-clear queue)
|
|||
|
(dolist (item datum)
|
|||
|
(queue-enqueue queue item))
|
|||
|
(dotimes (count (1- (length datum)))
|
|||
|
(queue-dequeue queue))
|
|||
|
;; Check that QUEUE is not empty:
|
|||
|
(should (equal nil (queue-empty queue)))
|
|||
|
(queue-dequeue queue)
|
|||
|
;; Check that QUEUE is empty:
|
|||
|
(should (equal t (queue-empty queue)))))))
|
|||
|
|
|||
|
(ert-deftest queue-copy-test ()
|
|||
|
(let ((data '((nil) (nil a) (nil a nil) (nil a nil b) (nil a nil b nil)
|
|||
|
(a) (a nil) (a nil b) (a nil b nil) (a nil b nil c))))
|
|||
|
(dolist (datum data)
|
|||
|
(let ((one (queue-create)) two)
|
|||
|
(dolist (item datum)
|
|||
|
(queue-enqueue one item))
|
|||
|
;; Check that the queues have identical contents.
|
|||
|
(setq two (queue-copy one))
|
|||
|
(should (equal t (and (equal (queue-head one) (queue-head two))
|
|||
|
(equal (queue-tail one) (queue-tail two)))))
|
|||
|
;; Check that changing queue ONE does not change queue TWO.
|
|||
|
(dotimes (count (length datum))
|
|||
|
(queue-dequeue one))
|
|||
|
(should (equal t (and (equal nil (queue-head one))
|
|||
|
(equal nil (queue-tail one))
|
|||
|
(equal datum (queue-head two))
|
|||
|
(equal (list (car (reverse datum)))
|
|||
|
(queue-tail two)))))
|
|||
|
;; Check that changing queue TWO does not change queue ONE.
|
|||
|
(setq one (queue-copy two))
|
|||
|
(setq two (queue-copy one))
|
|||
|
(queue-clear two)
|
|||
|
(should (equal t (and (equal nil (queue-head two))
|
|||
|
(equal nil (queue-tail two))
|
|||
|
(equal datum (queue-head one))
|
|||
|
(equal (list (car (reverse datum)))
|
|||
|
(queue-tail one)))))))))
|
|||
|
#+end_src
|
|||
|
|
|||
|
#+caption[Elisp Regression Testing queue: 3rd part of test-queue.el]:
|
|||
|
#+caption: Elisp Regression Testing ~queue~: 3rd part of ~test-queue.el~.
|
|||
|
#+name: lst:3rd-part-test-queue
|
|||
|
#+begin_src emacs-lisp -n :tangle test-queue.el
|
|||
|
(ert-deftest queue-iterator-test ()
|
|||
|
(let ((data '((nil) (nil a) (nil a nil) (nil a nil b) (nil a nil b nil)
|
|||
|
(a) (a nil) (a nil b) (a nil b nil) (a nil b nil c))))
|
|||
|
(dolist (datum data)
|
|||
|
(let ((queue (queue-create)))
|
|||
|
(dolist (item datum)
|
|||
|
(queue-enqueue queue item))
|
|||
|
(let ((queue-iterator (queue-iter queue)))
|
|||
|
(dotimes (n (length datum))
|
|||
|
(should (equal (nth n datum) (iter-next queue-iterator)))))))))
|
|||
|
|
|||
|
;;; test-queue.el ends here
|
|||
|
#+end_src
|
|||
|
|
|||
|
* Common Lisp "How do you DO?" do-loop example
|
|||
|
:PROPERTIES:
|
|||
|
:CUSTOM_ID: sec:how-do-you-do
|
|||
|
:END:
|
|||
|
|
|||
|
Listing [[lst:how-do-you-do]] is an complete ~do~-loop example with variable
|
|||
|
clauses, exit clauses, and a body. I have gleaned listing [[lst:how-do-you-do]]
|
|||
|
from:
|
|||
|
- [[https://courses.cs.northwestern.edu/325/readings/do.html][How do you DO?]] by Chris Riesbeck.
|
|||
|
- [[https://ebixio.com/online_docs/SuccessfulLisp.pdf][Successful Lisp: How to understand and use Common Lisp]] by David B. Lamkins.
|
|||
|
|
|||
|
#+caption["How do you DO?" do-loop example code]:
|
|||
|
#+caption: "How do you DO?" ~do~-loop example code
|
|||
|
#+caption: with variable clauses, exit clauses, and a body.
|
|||
|
#+header: :wrap "src text -n"
|
|||
|
#+name: lst:how-do-you-do
|
|||
|
#+begin_src lisp -n :exports both :results output :package cl-user :tangle no
|
|||
|
(defun how-do-you-do? (items)
|
|||
|
(let ((result))
|
|||
|
;; do loop:
|
|||
|
(do (;; variable clauses:
|
|||
|
(i 1 (1+ i))
|
|||
|
(items items (cdr items)))
|
|||
|
(;; exit clauses:
|
|||
|
(null items) 'done)
|
|||
|
;; body:
|
|||
|
(format t "~&Item ~D is ~S~%" i (car items))
|
|||
|
(push (car items) result))
|
|||
|
;; rest of `how-do-you-do?':
|
|||
|
(format t "~&~A~%" (reverse result))))
|
|||
|
|
|||
|
(let ((items '(how do you do \?)))
|
|||
|
(how-do-you-do? items))
|
|||
|
#+end_src
|
|||
|
|
|||
|
#+caption["How do you DO?" do-loop example result]:
|
|||
|
#+caption: "How do you DO?" ~do~-loop example result.
|
|||
|
#+name: lst:how-do-you-do-result
|
|||
|
#+RESULTS: lst:how-do-you-do
|
|||
|
#+begin_src text -n
|
|||
|
Item 1 is HOW
|
|||
|
Item 2 is DO
|
|||
|
Item 3 is YOU
|
|||
|
Item 4 is DO
|
|||
|
Item 5 is ?
|
|||
|
(HOW DO YOU DO ?)
|
|||
|
#+end_src
|
|||
|
|
|||
|
* Translate "queue.el" to "queue.lisp"
|
|||
|
:PROPERTIES:
|
|||
|
:CUSTOM_ID: sec:queue-el-to-queue-lisp
|
|||
|
:END:
|
|||
|
|
|||
|
The [[./queue.lisp][queue.lisp]] file is a translation to Common Lisp of ~queue.el~. The
|
|||
|
translation is wrapped in a Common Lisp package definition named ~queue~. The
|
|||
|
design of the ~queue~ package offers its client code to use the exported symbols
|
|||
|
~queue:all~, ~queue:clear~, ~queue:copy~, ~queue:create~, ~queue:dequeue~,
|
|||
|
~queue:empty~, ~queue:enqueue~, ~queue:first~, ~queue:head~, ~queue:last~,
|
|||
|
~queue:length~, ~queue:nth~, ~queue:p~, ~queue:prepend~, and ~queue:tail~.
|
|||
|
Limit access to the private ~queue::queue~ structure to reading documentation
|
|||
|
with for instance ~(describe-object 'queue::queue t)~.
|
|||
|
|
|||
|
The [[./queue.lisp][queue.lisp]] code has a package header section, a code section which contains
|
|||
|
the translation of ~queue.el~ to Common Lisp, and package footer section. The
|
|||
|
package footer section defines a kind of short function aliases that the package
|
|||
|
header section exports. See [[https://lispcookbook.github.io/cl-cookbook/packages.html][The Common Lisp Cookbook: Packages]] for additional
|
|||
|
information.
|
|||
|
|
|||
|
The translation takes into account the following points:
|
|||
|
1. ~(setcar place content)~ must be replaced with ~(setf (car place content))~.
|
|||
|
2. ~(setcdr place content)~ must be replaced with ~(setf (cdr place content))~.
|
|||
|
3. The ~while~ loop in the Emacs Lisp version of ~queue-copy~ is no valid Common
|
|||
|
Lisp, where it has been replaced with a ~dotimes~ loop in listing
|
|||
|
[[lst:2nd-part-queue.lisp]].
|
|||
|
4. Chris Riesbeck's [[https://courses.cs.northwestern.edu/325/exercises/critic.html][code critique]] recommends to replace recursively ~(cons item
|
|||
|
nil)~ with ~(list item)~.
|
|||
|
Section [[#sec:queue-code-critiques]] shows that only the Common Lisp implementation
|
|||
|
of ~queue-nth~ fails to pass the code critique implying that client code should
|
|||
|
not use ~queue:nth~. In order to ensure that the [[./queue.lisp][queue.lisp]] code is robust:
|
|||
|
1. Validate all compiler message by loading [[./queue.lisp][queue.lisp]] as shown in section
|
|||
|
[[#sec:queue-loading-check]]
|
|||
|
2. Validate all external symbols of [[./queue.lisp][queue.lisp]] as shown in section
|
|||
|
[[#sec:queue-externals-check]].
|
|||
|
3. Write a test suite as client code of [[./queue.lisp][queue.lisp]] as shown in section
|
|||
|
[[#sec:queue-client-testing]].
|
|||
|
Note: ensure that there is an operational ~sly-mrepl-mode~ buffer by means of
|
|||
|
executing for instance src_emacs-lisp{(call-interactively 'sly)} and execute
|
|||
|
src_emacs-lisp{(sly-hyperspec-lookup "fdefinition")} to read the ~fdefinition~
|
|||
|
documentation; ~fdefinition~ is used in listing [[lst:3rd-part-queue.lisp]] to
|
|||
|
define the short function aliases for export in listing [[lst:1st-part-queue.lisp]].
|
|||
|
|
|||
|
#+caption[Translate queue.el to Common Lisp: 1st part]:
|
|||
|
#+caption: Translate ~queue.el~ to Common Lisp: 1st part.
|
|||
|
#+name: lst:1st-part-queue.lisp
|
|||
|
#+begin_src lisp -n :eval never :tangle queue.lisp
|
|||
|
;;; queue.lisp --- a translation of queue.el to Common Lisp
|
|||
|
|
|||
|
;;; Package header:
|
|||
|
(defpackage queue
|
|||
|
(:use :cl)
|
|||
|
;; Beware of shadowing!
|
|||
|
(:shadow :first :last :length :nth)
|
|||
|
;; Use the short function names defined near the end of this file!
|
|||
|
;; Export all 15 functions!
|
|||
|
;; No `queue' structure export because of package name symbol conflicts!
|
|||
|
(:export #:all #:clear #:copy #:create #:dequeue #:empty #:enqueue
|
|||
|
#:first #:head #:last #:length #:nth #:p #:prepend #:tail))
|
|||
|
|
|||
|
(in-package :queue)
|
|||
|
|
|||
|
;;; Code:
|
|||
|
(defstruct queue
|
|||
|
"QUEUE structure holding the HEAD and the TAIL of QUEUE.
|
|||
|
HEAD is a list of all items and TAIL is a list of the last item.
|
|||
|
QUEUE is empty when HEAD and TAIL are nil."
|
|||
|
(head) (tail))
|
|||
|
|
|||
|
(defun queue-enqueue (queue item)
|
|||
|
"Add ITEM to the end of QUEUE."
|
|||
|
(if (queue-head queue)
|
|||
|
(setf (cdr (queue-tail queue))
|
|||
|
(setf (queue-tail queue) (list item)))
|
|||
|
(setf (queue-head queue)
|
|||
|
(setf (queue-tail queue) (list item)))))
|
|||
|
|
|||
|
(defun queue-prepend (queue item)
|
|||
|
"Add ITEM before the front of QUEUE."
|
|||
|
(if (queue-head queue)
|
|||
|
(push item (queue-head queue))
|
|||
|
(setf (queue-head queue)
|
|||
|
(setf (queue-tail queue) (list item)))))
|
|||
|
|
|||
|
(defun queue-dequeue (queue)
|
|||
|
"Remove the first item of QUEUE and return it.
|
|||
|
Return nil if QUEUE is empty."
|
|||
|
(unless (cdr (queue-head queue))
|
|||
|
(setf (queue-tail queue) nil))
|
|||
|
(pop (queue-head queue)))
|
|||
|
|
|||
|
(defun queue-empty (queue)
|
|||
|
"Return t if QUEUE is empty, otherwise return nil."
|
|||
|
(null (queue-head queue)))
|
|||
|
#+end_src
|
|||
|
|
|||
|
#+caption[Translate queue.el to Common Lisp: 2nd part]:
|
|||
|
#+caption: Translate ~queue.el~ to Common Lisp: 2nd part.
|
|||
|
#+name: lst:2nd-part-queue.lisp
|
|||
|
#+begin_src lisp -n :eval never :tangle queue.lisp
|
|||
|
(defun queue-first (queue)
|
|||
|
"Return the first item in QUEUE, without removing it.
|
|||
|
Return nil if QUEUE is empty."
|
|||
|
(car (queue-head queue)))
|
|||
|
|
|||
|
(defun queue-nth (queue n)
|
|||
|
"Return the item at index N in QUEUE, without removing it.
|
|||
|
Return nil if the length of QUEUE is less than N.
|
|||
|
The first item in QUEUE has index 0."
|
|||
|
(cl:nth n (queue-head queue)))
|
|||
|
|
|||
|
(defun queue-last (queue)
|
|||
|
"Return the last item in QUEUE, without removing it.
|
|||
|
Return nil if QUEUE is empty."
|
|||
|
(car (queue-tail queue)))
|
|||
|
|
|||
|
(defun queue-all (queue)
|
|||
|
"Return a list of all items in QUEUE or nil if QUEUE is empty.
|
|||
|
The oldest item in QUEUE is the first in the list."
|
|||
|
(queue-head queue))
|
|||
|
|
|||
|
(defun queue-copy (queue)
|
|||
|
"Return a copy of QUEUE.
|
|||
|
The new queue contains all items of QUEUE in the same order.
|
|||
|
The items themselves are *not* copied."
|
|||
|
(let ((q (make-queue))
|
|||
|
(items (queue-head queue)))
|
|||
|
(when (queue-head queue)
|
|||
|
(setf (queue-head q) (list (car (queue-head queue)))
|
|||
|
(queue-tail q) (queue-head q))
|
|||
|
(dotimes (count (1- (cl:length items)))
|
|||
|
(setq items (cdr items))
|
|||
|
(setf (queue-tail q)
|
|||
|
(setf (cdr (queue-tail q)) (list (car items))))))
|
|||
|
q))
|
|||
|
|
|||
|
(defun queue-length (queue)
|
|||
|
"Return the number of items in QUEUE."
|
|||
|
(cl:length (queue-head queue)))
|
|||
|
|
|||
|
(defun queue-clear (queue)
|
|||
|
"Remove all items from QUEUE."
|
|||
|
(setf (queue-head queue) nil
|
|||
|
(queue-tail queue) nil))
|
|||
|
#+end_src
|
|||
|
|
|||
|
#+caption[Translate queue.el to Common Lisp: 3rd part]:
|
|||
|
#+caption: Translate ~queue.el~ to Common Lisp: 3rd part.
|
|||
|
#+name: lst:3rd-part-queue.lisp
|
|||
|
#+begin_src lisp -n :eval never :tangle queue.lisp
|
|||
|
;;; Package footer:
|
|||
|
;;; Make short function names for export with prefix `queue:' in `defpackage'.
|
|||
|
(setf (fdefinition 'all) #'queue-all)
|
|||
|
(setf (fdefinition 'clear) #'queue-clear)
|
|||
|
(setf (fdefinition 'copy) #'queue-copy)
|
|||
|
(setf (fdefinition 'create) #'make-queue)
|
|||
|
(setf (fdefinition 'dequeue) #'queue-dequeue)
|
|||
|
(setf (fdefinition 'empty) #'queue-empty)
|
|||
|
(setf (fdefinition 'enqueue) #'queue-enqueue)
|
|||
|
(setf (fdefinition 'first) #'queue-first)
|
|||
|
(setf (fdefinition 'head) #'queue-head)
|
|||
|
(setf (fdefinition 'last) #'queue-last)
|
|||
|
(setf (fdefinition 'length) #'queue-length)
|
|||
|
(setf (fdefinition 'nth) #'queue-nth)
|
|||
|
(setf (fdefinition 'prepend) #'queue-prepend)
|
|||
|
(setf (fdefinition 'p) #'queue-p)
|
|||
|
(setf (fdefinition 'tail) #'queue-tail)
|
|||
|
|
|||
|
;;; queue.lisp ends here
|
|||
|
#+end_src
|
|||
|
|
|||
|
* Common Lisp "CS325-USER" package setup to critique code
|
|||
|
:PROPERTIES:
|
|||
|
:CUSTOM_ID: sec:load-cs325
|
|||
|
:END:
|
|||
|
|
|||
|
I use the ~critique~ macro in Chris Riesbeck's Common Lisp ~CS325-USER~ package
|
|||
|
to improve my Common Lisp code. Therefore, I have installed the ~CS325~ library
|
|||
|
according to the [[https://courses.cs.northwestern.edu/325/admin/lisp-setup.html][CS325 Common Lisp Setup]] instructions and I have to execute
|
|||
|
listing [[lst:load-cs325]] (or its equivalence) to ~ql:quickload~ the ~:cs325~
|
|||
|
package. This is a prerequisite for executing listing
|
|||
|
[[lst:1st-part-queue-critiques]], [[lst:2nd-part-queue-critiques]], and
|
|||
|
[[lst:3rd-part-queue-critiques]].
|
|||
|
|
|||
|
#+caption[Load the "CS325-USER" package]:
|
|||
|
#+caption: Load the ~CS325-USER~ package.
|
|||
|
#+name: lst:load-cs325
|
|||
|
#+header: :wrap "src text -n"
|
|||
|
#+begin_src lisp -n :exports both :results output :package cl-user :tangle no
|
|||
|
;; The call (ql:quickload :cs325) is equivalent.
|
|||
|
(ql:quickload "cs325")
|
|||
|
#+end_src
|
|||
|
|
|||
|
#+caption[Result of loading the "CS325-USER" package]:
|
|||
|
#+caption: Result of loading the ~CS325-USER~ package.
|
|||
|
#+name: lst:load-cs325-result
|
|||
|
#+RESULTS: lst:load-cs325
|
|||
|
#+begin_src text -n
|
|||
|
To load "cs325":
|
|||
|
Load 1 ASDF system:
|
|||
|
cs325
|
|||
|
; Loading "cs325"
|
|||
|
|
|||
|
#+end_src
|
|||
|
|
|||
|
\clearpage
|
|||
|
|
|||
|
** Code critiques of "queue.lisp"
|
|||
|
:PROPERTIES:
|
|||
|
:CUSTOM_ID: sec:queue-code-critiques
|
|||
|
:END:
|
|||
|
|
|||
|
Execution of listing [[lst:1st-part-queue-critiques]], [[lst:2nd-part-queue-critiques]],
|
|||
|
and [[lst:3rd-part-queue-critiques]] may require prior execution of listing
|
|||
|
[[lst:load-cs325]] (or its equivalence) to ~ql:quickload~ the ~:cs325~ package.
|
|||
|
|
|||
|
#+caption[1st part of "queue.lisp" code critiques]:
|
|||
|
#+caption: 1st part of ~queue.lisp~ code critiques.
|
|||
|
#+header: :wrap "src text -n :exports none"
|
|||
|
#+name: lst:1st-part-queue-critiques
|
|||
|
#+begin_src lisp -n :exports code :package cs325-user :results output :tangle no
|
|||
|
(critique
|
|||
|
(defstruct queue
|
|||
|
"QUEUE structure holding the HEAD and the TAIL of QUEUE.
|
|||
|
HEAD is a list of all items and TAIL is a list of the last item.
|
|||
|
QUEUE is empty when HEAD and TAIL are nil."
|
|||
|
(head) (tail)))
|
|||
|
|
|||
|
(critique
|
|||
|
(defun queue-enqueue (queue item)
|
|||
|
"Add ITEM to the end of QUEUE."
|
|||
|
(if (queue-head queue)
|
|||
|
(setf (cdr (queue-tail queue))
|
|||
|
(setf (queue-tail queue) (list item)))
|
|||
|
(setf (queue-head queue)
|
|||
|
(setf (queue-tail queue) (list item))))))
|
|||
|
|
|||
|
(critique
|
|||
|
(defun queue-prepend (queue item)
|
|||
|
"Add ITEM before the front of QUEUE."
|
|||
|
(if (queue-head queue)
|
|||
|
(push item (queue-head queue))
|
|||
|
(setf (queue-head queue)
|
|||
|
(setf (queue-tail queue) (list item))))))
|
|||
|
|
|||
|
(critique
|
|||
|
(defun queue-dequeue (queue)
|
|||
|
"Remove the first item of QUEUE and return it.
|
|||
|
Return nil if QUEUE is empty."
|
|||
|
(unless (cdr (queue-head queue))
|
|||
|
(setf (queue-tail queue) nil))
|
|||
|
(pop (queue-head queue))))
|
|||
|
|
|||
|
(critique
|
|||
|
(defun queue-empty (queue)
|
|||
|
"Return t if QUEUE is empty, otherwise return nil."
|
|||
|
(null (queue-head queue))))
|
|||
|
#+end_src
|
|||
|
|
|||
|
#+RESULTS: lst:1st-part-queue-critiques
|
|||
|
#+begin_src text -n :exports none
|
|||
|
----------------------------------------------------------------------
|
|||
|
----------------------------------------------------------------------
|
|||
|
----------------------------------------------------------------------
|
|||
|
----------------------------------------------------------------------
|
|||
|
----------------------------------------------------------------------
|
|||
|
#+end_src
|
|||
|
|
|||
|
#+caption[2nd part of "queue.lisp" code critiques]:
|
|||
|
#+caption: 2nd part of ~queue.lisp~ code critiques.
|
|||
|
#+header: :wrap "src text -n :exports none"
|
|||
|
#+name: lst:2nd-part-queue-critiques
|
|||
|
#+begin_src lisp -n :exports code :package cs325-user :results output :tangle no
|
|||
|
(critique
|
|||
|
(defun queue-first (queue)
|
|||
|
"Return the first item in QUEUE, without removing it.
|
|||
|
Return nil if QUEUE is empty."
|
|||
|
(car (queue-head queue))))
|
|||
|
|
|||
|
;; CRITIQUE says of QUEUE-NTH:
|
|||
|
;; (NTH ...) is expensive. Lists are not arrays.
|
|||
|
;; Hint: use FIRST, REST, and/or a pointer to access elements of a list
|
|||
|
(critique
|
|||
|
(defun queue-nth (queue n)
|
|||
|
"Return the item at index N in QUEUE, without removing it.
|
|||
|
Return nil if the length of QUEUE is less than N.
|
|||
|
The first item in QUEUE has index 0."
|
|||
|
(cl:nth n (queue-head queue))))
|
|||
|
|
|||
|
(critique
|
|||
|
(defun queue-last (queue)
|
|||
|
"Return the last item in QUEUE, without removing it.
|
|||
|
Return nil if QUEUE is empty."
|
|||
|
(car (queue-tail queue))))
|
|||
|
|
|||
|
(critique
|
|||
|
(defun queue-all (queue)
|
|||
|
"Return a list of all items in QUEUE or nil if QUEUE is empty.
|
|||
|
The oldest item in QUEUE is the first in the list."
|
|||
|
(queue-head queue)))
|
|||
|
#+end_src
|
|||
|
|
|||
|
#+RESULTS: lst:2nd-part-queue-critiques
|
|||
|
#+begin_src text -n :exports none
|
|||
|
----------------------------------------------------------------------
|
|||
|
----------------------------------------------------------------------
|
|||
|
(NTH ...) is expensive. Lists are not arrays.
|
|||
|
Hint: use FIRST, REST, and/or a pointer to access elements of a list
|
|||
|
----------------------------------------------------------------------
|
|||
|
----------------------------------------------------------------------
|
|||
|
----------------------------------------------------------------------
|
|||
|
#+end_src
|
|||
|
|
|||
|
#+caption[3nd part of "queue.lisp" code critique]:
|
|||
|
#+caption: 3nd part of ~queue.lisp~ code critique.
|
|||
|
#+header: :wrap "src text -n :exports none"
|
|||
|
#+name: lst:3rd-part-queue-critiques
|
|||
|
#+begin_src lisp -n :exports code :package cs325-user :results output :tangle no
|
|||
|
(critique
|
|||
|
(defun queue-copy (queue)
|
|||
|
"Return a copy of QUEUE.
|
|||
|
The new queue contains all items of QUEUE in the same order.
|
|||
|
The items themselves are *not* copied."
|
|||
|
(let ((q (make-queue))
|
|||
|
(items (queue-head queue)))
|
|||
|
(when (queue-head queue)
|
|||
|
(setf (queue-head q) (list (car (queue-head queue)))
|
|||
|
(queue-tail q) (queue-head q))
|
|||
|
(dotimes (count (1- (length items)))
|
|||
|
(setq items (cdr items))
|
|||
|
(setf (queue-tail q)
|
|||
|
(setf (cdr (queue-tail q)) (list (car items))))))
|
|||
|
q)))
|
|||
|
|
|||
|
(critique
|
|||
|
(defun queue-length (queue)
|
|||
|
"Return the number of items in QUEUE."
|
|||
|
(length (queue-head queue))))
|
|||
|
|
|||
|
(critique
|
|||
|
(defun queue-clear (queue)
|
|||
|
"Remove all items from QUEUE."
|
|||
|
(setf (queue-head queue) nil
|
|||
|
(queue-tail queue) nil)))
|
|||
|
#+end_src
|
|||
|
|
|||
|
#+RESULTS: lst:3rd-part-queue-critiques
|
|||
|
#+begin_src text -n :exports none
|
|||
|
----------------------------------------------------------------------
|
|||
|
----------------------------------------------------------------------
|
|||
|
----------------------------------------------------------------------
|
|||
|
#+end_src
|
|||
|
|
|||
|
* Validation of package "queue"
|
|||
|
:PROPERTIES:
|
|||
|
:CUSTOM_ID: sec:queue-validation
|
|||
|
:END:
|
|||
|
|
|||
|
** Validation of all "queue" package loading loading messages
|
|||
|
:PROPERTIES:
|
|||
|
:CUSTOM_ID: sec:queue-loading-check
|
|||
|
:END:
|
|||
|
|
|||
|
#+caption[Validate all queue package loading messages]:
|
|||
|
#+caption: Validate all ~queue~ package loading messages.
|
|||
|
#+header: :wrap "src lisp -n :eval never"
|
|||
|
#+begin_src lisp -n :exports both :package cl-user :results output :tangle no
|
|||
|
(load "queue.lisp" :verbose t :print t)
|
|||
|
#+end_src
|
|||
|
|
|||
|
#+caption[Validation of all "queue" package loading messages]:
|
|||
|
#+caption: Validation of all ~queue~ package loading messages.
|
|||
|
#+RESULTS:
|
|||
|
#+begin_src lisp -n :eval never
|
|||
|
; loading #P"/Users/vermeulen/.emacs.d/queue.lisp"
|
|||
|
; #<PACKAGE "QUEUE">
|
|||
|
; #<PACKAGE "QUEUE">
|
|||
|
; QUEUE
|
|||
|
; QUEUE-ENQUEUE
|
|||
|
; QUEUE-PREPEND
|
|||
|
; QUEUE-DEQUEUE
|
|||
|
; QUEUE-EMPTY
|
|||
|
; QUEUE-FIRST
|
|||
|
; QUEUE-NTH
|
|||
|
; QUEUE-LAST
|
|||
|
; QUEUE-ALL
|
|||
|
; QUEUE-COPY
|
|||
|
; QUEUE-LENGTH
|
|||
|
; QUEUE-CLEAR
|
|||
|
; #<FUNCTION QUEUE-ALL>
|
|||
|
; #<FUNCTION QUEUE-CLEAR>
|
|||
|
; #<FUNCTION QUEUE-COPY>
|
|||
|
; #<FUNCTION MAKE-QUEUE>
|
|||
|
; #<FUNCTION QUEUE-DEQUEUE>
|
|||
|
; #<FUNCTION QUEUE-EMPTY>
|
|||
|
; #<FUNCTION QUEUE-ENQUEUE>
|
|||
|
; #<FUNCTION QUEUE-FIRST>
|
|||
|
; #<FUNCTION QUEUE-HEAD>
|
|||
|
; #<FUNCTION QUEUE-LAST>
|
|||
|
; #<FUNCTION QUEUE-LENGTH>
|
|||
|
; #<FUNCTION QUEUE-NTH>
|
|||
|
; #<FUNCTION QUEUE-PREPEND>
|
|||
|
; #<FUNCTION QUEUE-P>
|
|||
|
; #<FUNCTION QUEUE-TAIL>
|
|||
|
#+end_src
|
|||
|
|
|||
|
\clearpage
|
|||
|
|
|||
|
** Validation of all external "queue" package symbols
|
|||
|
:PROPERTIES:
|
|||
|
:CUSTOM_ID: sec:queue-externals-check
|
|||
|
:END:
|
|||
|
|
|||
|
#+caption[Validate all external "queue" package symbols]:
|
|||
|
#+caption: Validate all external ~queue~ package symbols.
|
|||
|
#+header: :wrap "src lisp -n :eval never"
|
|||
|
#+begin_src lisp -n :exports both :package cl-user :results output :tangle no
|
|||
|
;; BUG: Remove the first empty line of the output. This is not easy!
|
|||
|
(do-external-symbols (s (find-package :queue))
|
|||
|
(print s))
|
|||
|
#+end_src
|
|||
|
|
|||
|
#+caption[Validation of all external "queue" package symbols]:
|
|||
|
#+caption: Validation of all external ~queue~ package symbols.
|
|||
|
#+RESULTS:
|
|||
|
#+begin_src lisp -n :eval never
|
|||
|
|
|||
|
QUEUE:HEAD
|
|||
|
QUEUE:LAST
|
|||
|
QUEUE:DEQUEUE
|
|||
|
QUEUE:LENGTH
|
|||
|
QUEUE:PREPEND
|
|||
|
QUEUE:FIRST
|
|||
|
QUEUE:NTH
|
|||
|
QUEUE:ENQUEUE
|
|||
|
QUEUE:COPY
|
|||
|
QUEUE:CREATE
|
|||
|
QUEUE:P
|
|||
|
QUEUE:ALL
|
|||
|
QUEUE:EMPTY
|
|||
|
QUEUE:CLEAR
|
|||
|
QUEUE:TAIL
|
|||
|
#+end_src
|
|||
|
|
|||
|
* Common Lisp "QUEUE" regression testing
|
|||
|
:PROPERTIES:
|
|||
|
:CUSTOM_ID: sec:queue-client-testing
|
|||
|
:header-args:lisp: :tangle test-queue.lisp
|
|||
|
:END:
|
|||
|
|
|||
|
# FIXME: test-queue-dequeue
|
|||
|
|
|||
|
Listing [[lst:5am-test-queue-header]], [[lst:5am-test-queue-create]],
|
|||
|
[[lst:5am-test-queue-enqueue]], [[lst:5am-test-queue-prepend]],
|
|||
|
[[lst:5am-test-queue-empty]], [[lst:5am-test-queue-first]], [[lst:5am-test-queue-nth]],
|
|||
|
[[lst:5am-test-queue-last]], [[lst:5am-test-queue-all]], [[lst:5am-test-queue-copy]],
|
|||
|
[[lst:5am-test-queue-length]], [[lst:5am-test-queue-clear]] and
|
|||
|
[[lst:5am-test-queue-footer]] are tangled into [[./test-queue.lisp][test-queue.lisp]].
|
|||
|
|
|||
|
#+caption[Setup Common Lisp "QUEUE" regression testing]:
|
|||
|
#+caption: Setup Common Lisp ~QUEUE~ regression testing.
|
|||
|
#+name: lst:5am-test-queue-header
|
|||
|
#+begin_src lisp -n :package cl-user :results silent
|
|||
|
;;; test-queue.lisp --- 5am regression testing of queue.lisp
|
|||
|
|
|||
|
(ql:quickload :fiveam)
|
|||
|
(load "queue.lisp" :verbose t :print t)
|
|||
|
#+end_src
|
|||
|
|
|||
|
#+caption[Define "TEST-QUEUE-CREATE"]:
|
|||
|
#+caption: Define ~TEST-QUEUE-CREATE~.
|
|||
|
#+name: lst:5am-test-queue-create
|
|||
|
#+begin_src lisp -n :package cl-user :results silent
|
|||
|
(5am:test test-queue-create
|
|||
|
"Test queue:create."
|
|||
|
(let ((queue (queue:create)))
|
|||
|
(5am:is (eq nil (queue:head queue))
|
|||
|
"Not: (eq nil (queue:head queue))")
|
|||
|
(5am:is (eq nil (queue:tail queue))
|
|||
|
"Not: (eq nil (queue:tail queue))")))
|
|||
|
#+end_src
|
|||
|
|
|||
|
#+caption[Run "TEST-QUEUE-CREATE"]:
|
|||
|
#+caption: Run ~TEST-QUEUE-CREATE~.
|
|||
|
#+header: :wrap "src text -n :exports never"
|
|||
|
#+begin_src lisp -n :package cl-user :results output :tangle no
|
|||
|
(5am:run! 'test-queue-create)
|
|||
|
#+end_src
|
|||
|
|
|||
|
#+RESULTS:
|
|||
|
#+begin_src text -n :exports never
|
|||
|
|
|||
|
Running test TEST-QUEUE-CREATE ..
|
|||
|
Did 2 checks.
|
|||
|
Pass: 2 (100%)
|
|||
|
Skip: 0 ( 0%)
|
|||
|
Fail: 0 ( 0%)
|
|||
|
#+end_src
|
|||
|
|
|||
|
#+caption[Define "TEST-QUEUE-ENQUEUE"]:
|
|||
|
#+caption: Define ~TEST-QUEUE-ENQUEUE~.
|
|||
|
#+name: lst:5am-test-queue-enqueue
|
|||
|
#+begin_src lisp -n :package cl-user :results silent
|
|||
|
(5am:test test-queue-enqueue
|
|||
|
"Test queue:enqueue."
|
|||
|
(let ((data '((nil) (nil a) (nil a nil) (nil a nil b) (nil a nil b nil)
|
|||
|
(a) (a nil) (a nil b) (a nil b nil) (a nil b nil c)))
|
|||
|
(queue (queue:create)))
|
|||
|
(dolist (datum data)
|
|||
|
(queue:clear queue)
|
|||
|
(dolist (item datum)
|
|||
|
(queue:enqueue queue item))
|
|||
|
(5am:is (equal (queue:head queue) datum)
|
|||
|
"Not: (equal (queue:head queue) datum)")
|
|||
|
(5am:is (eq (car (queue:tail queue)) (car (reverse datum)))
|
|||
|
"Not: (eq (car (queue:tail queue)) (car (reverse datum)))"))))
|
|||
|
#+end_src
|
|||
|
|
|||
|
#+caption[Run "TEST-QUEUE-ENQUEUE"]:
|
|||
|
#+caption: Run ~TEST-QUEUE-ENQUEUE~.
|
|||
|
#+header: :wrap "src text -n :exports never"
|
|||
|
#+begin_src lisp -n :package cl-user :results output :tangle no
|
|||
|
(5am:run! 'test-queue-enqueue)
|
|||
|
#+end_src
|
|||
|
|
|||
|
#+RESULTS:
|
|||
|
#+begin_src text -n :exports never
|
|||
|
|
|||
|
Running test TEST-QUEUE-ENQUEUE ....................
|
|||
|
Did 20 checks.
|
|||
|
Pass: 20 (100%)
|
|||
|
Skip: 0 ( 0%)
|
|||
|
Fail: 0 ( 0%)
|
|||
|
#+end_src
|
|||
|
|
|||
|
#+caption[Define "TEST-QUEUE-PREPEND"]:
|
|||
|
#+caption: Define ~TEST-QUEUE-PREPEND~.
|
|||
|
#+name: lst:5am-test-queue-prepend
|
|||
|
#+begin_src lisp -n :package cl-user :results silent
|
|||
|
(5am:test test-queue-prepend
|
|||
|
"Test queue:prepend."
|
|||
|
(let ((data '((nil) (nil a) (nil a nil) (nil a nil b) (nil a nil b nil)
|
|||
|
(a) (a nil) (a nil b) (a nil b nil) (a nil b nil c)))
|
|||
|
(queue (queue:create)))
|
|||
|
(dolist (datum data)
|
|||
|
(queue:clear queue)
|
|||
|
(dolist (item datum)
|
|||
|
(queue:prepend queue item))
|
|||
|
(5am:is (equal (queue:head queue) (reverse datum))
|
|||
|
"Not: (equal (queue:head queue) (reverse datum))")
|
|||
|
(5am:is (eq (car (queue:tail queue)) (car datum))
|
|||
|
"Not: (eq (car (queue:tail queue)) (car datum))"))))
|
|||
|
#+end_src
|
|||
|
|
|||
|
#+caption[Run "TEST-QUEUE-PREPEND"]:
|
|||
|
#+caption: Run ~TEST-QUEUE-PREPEND~.
|
|||
|
#+header: :wrap "src text -n :exports never"
|
|||
|
#+begin_src lisp -n :package cl-user :results output :tangle no
|
|||
|
(5am:run! 'test-queue-prepend)
|
|||
|
#+end_src
|
|||
|
|
|||
|
#+RESULTS:
|
|||
|
#+begin_src text -n :exports never
|
|||
|
|
|||
|
Running test TEST-QUEUE-PREPEND ....................
|
|||
|
Did 20 checks.
|
|||
|
Pass: 20 (100%)
|
|||
|
Skip: 0 ( 0%)
|
|||
|
Fail: 0 ( 0%)
|
|||
|
#+end_src
|
|||
|
|
|||
|
#+caption[Define "TEST-QUEUE-DEQUEUE"]:
|
|||
|
#+caption: Define ~TEST-QUEUE-DEQUEUE~.
|
|||
|
#+name: lst:5am-test-queue-dequeue
|
|||
|
#+begin_src lisp -n :package cl-user :results silent
|
|||
|
(5am:test test-queue-dequeue
|
|||
|
"Test queue:dequeue."
|
|||
|
(let ((data '((nil) (nil a) (nil a nil) (nil a nil b) (nil a nil b nil)
|
|||
|
(a) (a nil) (a nil b) (a nil b nil) (a nil b nil c)))
|
|||
|
(queue (queue:create)))
|
|||
|
(5am:is (not (car (queue:dequeue queue)))
|
|||
|
"Not: (car (queue:dequeue queue))")
|
|||
|
(dolist (datum data)
|
|||
|
(dolist (item datum)
|
|||
|
(queue:enqueue queue item))
|
|||
|
(dotimes (n (length datum))
|
|||
|
(5am:is (equal (queue:dequeue queue) (cl:nth n datum))
|
|||
|
"Not: (equal (queue:dequeue queue) (cl:nth n datum))")))))
|
|||
|
#+end_src
|
|||
|
|
|||
|
#+caption[Run "TEST-QUEUE-DEQUEUE"]:
|
|||
|
#+caption: Run ~TEST-QUEUE-DEQUEUE~.
|
|||
|
#+header: :wrap "src text -n :exports never"
|
|||
|
#+begin_src lisp -n :package cl-user :results output :tangle no
|
|||
|
(5am:run! 'test-queue-dequeue)
|
|||
|
#+end_src
|
|||
|
|
|||
|
#+RESULTS:
|
|||
|
#+begin_src text -n :exports never
|
|||
|
|
|||
|
Running test TEST-QUEUE-DEQUEUE ...............................
|
|||
|
Did 31 checks.
|
|||
|
Pass: 31 (100%)
|
|||
|
Skip: 0 ( 0%)
|
|||
|
Fail: 0 ( 0%)
|
|||
|
#+end_src
|
|||
|
|
|||
|
#+caption[Define "TEST-QUEUE-EMPTY"]:
|
|||
|
#+caption: Define ~TEST-QUEUE-EMPTY~.
|
|||
|
#+name: lst:5am-test-queue-empty
|
|||
|
#+begin_src lisp -n :package cl-user :results silent
|
|||
|
(5am:test test-queue-empty
|
|||
|
"Test queue:empty."
|
|||
|
(let ((data '((nil) (nil a) (nil a nil) (nil a nil b) (nil a nil b nil)
|
|||
|
(a) (a nil) (a nil b) (a nil b nil) (a nil b nil c))))
|
|||
|
(dolist (datum data)
|
|||
|
(let ((queue (queue:create)))
|
|||
|
(queue:clear queue)
|
|||
|
(dolist (item datum)
|
|||
|
(queue:enqueue queue item))
|
|||
|
(dotimes (count (1- (length datum)))
|
|||
|
(queue:dequeue queue))
|
|||
|
(5am:is (not (queue:empty queue))
|
|||
|
"Not: (equal (queue:head queue) (reverse datum))")
|
|||
|
(queue:dequeue queue)
|
|||
|
(5am:is (queue:empty queue)
|
|||
|
"Not: (eq (car (queue:tail queue)) (car datum))")))))
|
|||
|
#+end_src
|
|||
|
|
|||
|
#+caption[Run "TEST-QUEUE-EMPTY"]:
|
|||
|
#+caption: Run ~TEST-QUEUE-EMPTY~.
|
|||
|
#+header: :wrap "src text -n :exports never"
|
|||
|
#+begin_src lisp -n :package cl-user :results output :tangle no
|
|||
|
(5am:run! 'test-queue-empty)
|
|||
|
#+end_src
|
|||
|
|
|||
|
#+RESULTS:
|
|||
|
#+begin_src text -n :exports never
|
|||
|
|
|||
|
Running test TEST-QUEUE-EMPTY ....................
|
|||
|
Did 20 checks.
|
|||
|
Pass: 20 (100%)
|
|||
|
Skip: 0 ( 0%)
|
|||
|
Fail: 0 ( 0%)
|
|||
|
#+end_src
|
|||
|
|
|||
|
#+caption[Define "TEST-QUEUE-FIRST"]:
|
|||
|
#+caption: Define ~TEST-QUEUE-FIRST~.
|
|||
|
#+name: lst:5am-test-queue-first
|
|||
|
#+begin_src lisp -n :package cl-user :results silent
|
|||
|
(5am:test test-queue-first
|
|||
|
"Test queue:first."
|
|||
|
(let ((data '((nil) (nil a) (nil a nil) (nil a nil b) (nil a nil b nil)
|
|||
|
(a) (a nil) (a nil b) (a nil b nil) (a nil b nil c))))
|
|||
|
(dolist (datum data)
|
|||
|
(let ((queue (queue:create)))
|
|||
|
(queue:clear queue)
|
|||
|
(dolist (item datum)
|
|||
|
(queue:enqueue queue item))
|
|||
|
(5am:is (eq (queue:first queue) (car datum))
|
|||
|
"Not: (eq (queue:first queue) (car datum))")
|
|||
|
(5am:is (equal (queue:head queue) datum)
|
|||
|
"Not: (equal (queue:head queue) datum)")))))
|
|||
|
#+end_src
|
|||
|
|
|||
|
#+caption[Run "TEST-QUEUE-FIRST"]:
|
|||
|
#+caption: Run ~TEST-QUEUE-FIRST~.
|
|||
|
#+header: :wrap "src text -n :exports never"
|
|||
|
#+begin_src lisp -n :package cl-user :results output :tangle no
|
|||
|
(5am:run! 'test-queue-first)
|
|||
|
#+end_src
|
|||
|
|
|||
|
#+RESULTS:
|
|||
|
#+begin_src text -n :exports never
|
|||
|
|
|||
|
Running test TEST-QUEUE-FIRST ....................
|
|||
|
Did 20 checks.
|
|||
|
Pass: 20 (100%)
|
|||
|
Skip: 0 ( 0%)
|
|||
|
Fail: 0 ( 0%)
|
|||
|
#+end_src
|
|||
|
|
|||
|
#+caption[Define "TEST-QUEUE-NTH"]:
|
|||
|
#+caption: Define ~TEST-QUEUE-NTH~.
|
|||
|
#+name: lst:5am-test-queue-nth
|
|||
|
#+begin_src lisp -n :package cl-user :results silent
|
|||
|
(5am:test test-queue-nth
|
|||
|
"Test queue:nth."
|
|||
|
(let ((data '((nil) (nil a) (nil a nil) (nil a nil b) (nil a nil b nil)
|
|||
|
(a) (a nil) (a nil b) (a nil b nil) (a nil b nil c))))
|
|||
|
(dolist (datum data)
|
|||
|
(let ((queue (queue:create)))
|
|||
|
(queue:clear queue)
|
|||
|
(dolist (item datum)
|
|||
|
(queue:enqueue queue item))
|
|||
|
(dotimes (n (1- (length datum)))
|
|||
|
(5am:is (eq (queue:nth queue n) (cl:nth n datum))
|
|||
|
"Not: (eq (queue:nth queue n) (cl:nth n datum))")
|
|||
|
(5am:is (not (queue:nth queue (length datum)))
|
|||
|
"Not: (not (queue:nth queue (length datum)))"))))))
|
|||
|
#+end_src
|
|||
|
|
|||
|
#+caption[Run "TEST-QUEUE-NTH"]:
|
|||
|
#+caption: Run ~TEST-QUEUE-NTH~.
|
|||
|
#+header: :wrap "src text -n :exports never"
|
|||
|
#+begin_src lisp -n :package cl-user :results output :tangle no
|
|||
|
(5am:run! 'test-queue-nth)
|
|||
|
#+end_src
|
|||
|
|
|||
|
#+RESULTS:
|
|||
|
#+begin_src text -n :exports never
|
|||
|
|
|||
|
Running test TEST-QUEUE-NTH ........................................
|
|||
|
Did 40 checks.
|
|||
|
Pass: 40 (100%)
|
|||
|
Skip: 0 ( 0%)
|
|||
|
Fail: 0 ( 0%)
|
|||
|
#+end_src
|
|||
|
|
|||
|
#+caption[Define "TEST-QUEUE-LAST"]:
|
|||
|
#+caption: Define ~TEST-QUEUE-LAST~.
|
|||
|
#+name: lst:5am-test-queue-last
|
|||
|
#+begin_src lisp -n :package cl-user :results silent
|
|||
|
(5am:test test-queue-last
|
|||
|
"Test queue:last."
|
|||
|
(let ((data '((nil) (nil a) (nil a nil) (nil a nil b) (nil a nil b nil)
|
|||
|
(a) (a nil) (a nil b) (a nil b nil) (a nil b nil c)))
|
|||
|
(queue (queue:create)))
|
|||
|
(5am:is (eq (queue:last queue) nil)
|
|||
|
"Not: (eq (queue:last queue) nil)")
|
|||
|
(dolist (datum data)
|
|||
|
(queue:clear queue)
|
|||
|
(dolist (item datum)
|
|||
|
(queue:enqueue queue item))
|
|||
|
(5am:is (eq (queue:last queue) (car (reverse datum)))
|
|||
|
"Not: (eq (queue:last queue) (car (reverse datum)))"))))
|
|||
|
#+end_src
|
|||
|
|
|||
|
#+caption[Run "TEST-QUEUE-LAST"]:
|
|||
|
#+caption: Run ~TEST-QUEUE-LAST~.
|
|||
|
#+header: :wrap "src text -n :exports never"
|
|||
|
#+begin_src lisp -n :package cl-user :results output :tangle no
|
|||
|
(5am:run! 'test-queue-last)
|
|||
|
#+end_src
|
|||
|
|
|||
|
#+RESULTS:
|
|||
|
#+begin_src text -n :exports never
|
|||
|
|
|||
|
Running test TEST-QUEUE-LAST ...........
|
|||
|
Did 11 checks.
|
|||
|
Pass: 11 (100%)
|
|||
|
Skip: 0 ( 0%)
|
|||
|
Fail: 0 ( 0%)
|
|||
|
#+end_src
|
|||
|
|
|||
|
#+caption[Define "TEST-QUEUE-ALL"]:
|
|||
|
#+caption: Define ~TEST-QUEUE-ALL~.
|
|||
|
#+name: lst:5am-test-queue-all
|
|||
|
#+begin_src lisp -n :package cl-user :results silent
|
|||
|
(5am:test test-queue-all
|
|||
|
"Test queue:all."
|
|||
|
(let ((data '((nil) (nil a) (nil a nil) (nil a nil b) (nil a nil b nil)
|
|||
|
(a) (a nil) (a nil b) (a nil b nil) (a nil b nil c)))
|
|||
|
(queue (queue:create)))
|
|||
|
(5am:is (eq (queue:all queue) nil)
|
|||
|
"Not: (eq (queue:all queue) nil)")
|
|||
|
(dolist (datum data)
|
|||
|
(queue:clear queue)
|
|||
|
(dolist (item datum)
|
|||
|
(queue:enqueue queue item))
|
|||
|
(5am:is (equal (queue:all queue) datum)
|
|||
|
"Not: (equal (queue:all queue) datum)"))))
|
|||
|
#+end_src
|
|||
|
|
|||
|
#+caption[Run "TEST-QUEUE-ALL"]:
|
|||
|
#+caption: Run ~TEST-QUEUE-ALL~.
|
|||
|
#+header: :wrap "src text -n :exports never"
|
|||
|
#+begin_src lisp -n :package cl-user :results output :tangle no
|
|||
|
(5am:run! 'test-queue-all)
|
|||
|
#+end_src
|
|||
|
|
|||
|
#+RESULTS:
|
|||
|
#+begin_src text -n :exports never
|
|||
|
|
|||
|
Running test TEST-QUEUE-ALL ...........
|
|||
|
Did 11 checks.
|
|||
|
Pass: 11 (100%)
|
|||
|
Skip: 0 ( 0%)
|
|||
|
Fail: 0 ( 0%)
|
|||
|
#+end_src
|
|||
|
|
|||
|
#+caption[Define "TEST-QUEUE-COPY"]:
|
|||
|
#+caption: Define ~TEST-QUEUE-COPY~.
|
|||
|
#+name: lst:5am-test-queue-copy
|
|||
|
#+begin_src lisp -n :package cl-user :results silent
|
|||
|
(5am:test test-queue-copy
|
|||
|
"Test queue:copy."
|
|||
|
(let ((one (queue:create))
|
|||
|
(two nil)
|
|||
|
(data '((nil) (nil a) (nil a nil) (nil a nil b) (nil a nil b nil)
|
|||
|
(a) (a nil) (a nil b) (a nil b nil) (a nil b nil c))))
|
|||
|
(dolist (datum data)
|
|||
|
(queue:clear one)
|
|||
|
(dolist (item datum)
|
|||
|
(queue:enqueue one item))
|
|||
|
;; Check for equality of ORIGINAL and COPY:
|
|||
|
(setq two (queue:copy one)) ;;
|
|||
|
(5am:is (equal (queue:head one) (queue:head two))
|
|||
|
"Not: (equal (queue:head one) (queue:head two))")
|
|||
|
(5am:is (equal (queue:tail one) (queue:tail two))
|
|||
|
"Not: (equal (queue:tail one) (queue:tail two))")
|
|||
|
;; Check that the COPY does not share structure with the ORIGINAL:
|
|||
|
(queue:clear one)
|
|||
|
(5am:is (eq nil (queue:head one))
|
|||
|
"Not: (eq nil (queue:head one))")
|
|||
|
(5am:is (eq nil (queue:tail one))
|
|||
|
"Not: (eq nil (queue:tail one))")
|
|||
|
(5am:is (equal (queue:head two) datum)
|
|||
|
"Not: (equal (queue:head two) datum)")
|
|||
|
(5am:is (eq (car (queue:tail two)) (car (reverse datum)))
|
|||
|
"Not: (eq (car (queue:tail two)) (car (reverse datum)))")
|
|||
|
;; Check that the ORIGINAL does not share structure with the COPY:
|
|||
|
(setq one (queue:copy two))
|
|||
|
(setq two (queue:copy one))
|
|||
|
(queue:clear two)
|
|||
|
(5am:is (eq nil (queue:head two))
|
|||
|
"Not: (eq nil (queue:head two))")
|
|||
|
(5am:is (eq nil (queue:tail two))
|
|||
|
"Not: (eq nil (queue:tail two))")
|
|||
|
(5am:is (equal (queue:head one) datum)
|
|||
|
"Not: (equal (queue:head one) datum)")
|
|||
|
(5am:is (eq (car (queue:tail one)) (car (reverse datum)))
|
|||
|
"Not: (eq (car (queue:tail one)) (car (reverse datum)))"))))
|
|||
|
#+end_src
|
|||
|
|
|||
|
#+caption[Run "TEST-QUEUE-COPY"]:
|
|||
|
#+caption: Run ~TEST-QUEUE-COPY~.
|
|||
|
#+header: :wrap "src text -n :exports never"
|
|||
|
#+begin_src lisp -n :package cl-user :results output :tangle no
|
|||
|
(5am:run! 'test-queue-copy)
|
|||
|
#+end_src
|
|||
|
|
|||
|
#+RESULTS:
|
|||
|
#+begin_src text -n :exports never
|
|||
|
|
|||
|
Running test TEST-QUEUE-COPY ....................................................................................................
|
|||
|
Did 100 checks.
|
|||
|
Pass: 100 (100%)
|
|||
|
Skip: 0 ( 0%)
|
|||
|
Fail: 0 ( 0%)
|
|||
|
#+end_src
|
|||
|
|
|||
|
#+caption[Define "TEST-QUEUE-LENGTH"]:
|
|||
|
#+caption: Define ~TEST-QUEUE-LENGTH~.
|
|||
|
#+name: lst:5am-test-queue-length
|
|||
|
#+begin_src lisp -n :package cl-user :results silent
|
|||
|
(5am:test test-queue-length
|
|||
|
"Test queue:all."
|
|||
|
(let ((data '((nil) (nil a) (nil a nil) (nil a nil b) (nil a nil b nil)
|
|||
|
(a) (a nil) (a nil b) (a nil b nil) (a nil b nil c)))
|
|||
|
(queue (queue:create)))
|
|||
|
(5am:is (eq (queue:length queue) (cl:length nil))
|
|||
|
"Not: (eq (eq (queue:length queue) (cl:length nil)))")
|
|||
|
(dolist (datum data)
|
|||
|
(queue:clear queue)
|
|||
|
(dolist (item datum)
|
|||
|
(queue:enqueue queue item))
|
|||
|
(5am:is (equal (queue:length queue) (cl:length datum))
|
|||
|
"Not: (queue:length queue) (cl:length datum)"))))
|
|||
|
#+end_src
|
|||
|
|
|||
|
#+caption[Run "TEST-QUEUE-LENGTH"]:
|
|||
|
#+caption: Run ~TEST-QUEUE-LENGTH~.
|
|||
|
#+header: :wrap "src text -n :exports never"
|
|||
|
#+begin_src lisp -n :package cl-user :results output :tangle no
|
|||
|
(5am:run! 'test-queue-length)
|
|||
|
#+end_src
|
|||
|
|
|||
|
#+RESULTS:
|
|||
|
#+begin_src text -n :exports never
|
|||
|
|
|||
|
Running test TEST-QUEUE-LENGTH ...........
|
|||
|
Did 11 checks.
|
|||
|
Pass: 11 (100%)
|
|||
|
Skip: 0 ( 0%)
|
|||
|
Fail: 0 ( 0%)
|
|||
|
#+end_src
|
|||
|
|
|||
|
#+caption[Define "TEST-QUEUE-CLEAR"]:
|
|||
|
#+caption: Define ~TEST-QUEUE-CLEAR~.
|
|||
|
#+name: lst:5am-test-queue-clear
|
|||
|
#+begin_src lisp -n :package cl-user :results silent
|
|||
|
(5am:test test-queue-clear
|
|||
|
"Test queue:clear."
|
|||
|
(let ((data '((nil) (nil a) (nil a nil) (nil a nil b) (nil a nil b nil)
|
|||
|
(a) (a nil) (a nil b) (a nil b nil) (a nil b nil c)))
|
|||
|
(queue (queue:create)))
|
|||
|
(5am:is (queue:empty queue) "Not: (queue:empty queue)")
|
|||
|
(dolist (datum data)
|
|||
|
(queue:clear queue)
|
|||
|
(5am:is (queue:empty queue) "Not: (queue:empty queue)")
|
|||
|
(dolist (item datum)
|
|||
|
(queue:enqueue queue item))
|
|||
|
(5am:is (not (queue:empty queue)) "Not: (not (queue:empty queue))"))))
|
|||
|
#+end_src
|
|||
|
|
|||
|
#+caption[Run "TEST-QUEUE-CLEAR"]:
|
|||
|
#+caption: Run ~TEST-QUEUE-CLEAR~.
|
|||
|
#+header: :wrap "src text -n :exports never"
|
|||
|
#+begin_src lisp -n :package cl-user :results output :tangle no
|
|||
|
(5am:run! 'test-queue-clear)
|
|||
|
#+end_src
|
|||
|
|
|||
|
#+RESULTS:
|
|||
|
#+begin_src text -n :exports never
|
|||
|
|
|||
|
Running test TEST-QUEUE-CLEAR .....................
|
|||
|
Did 21 checks.
|
|||
|
Pass: 21 (100%)
|
|||
|
Skip: 0 ( 0%)
|
|||
|
Fail: 0 ( 0%)
|
|||
|
#+end_src
|
|||
|
|
|||
|
#+caption[Run all tests]:
|
|||
|
#+caption: Run all tests.
|
|||
|
#+header: :wrap "src text -n :exports never"
|
|||
|
#+name: lst:5am-test-queue-footer
|
|||
|
#+begin_src lisp -n :package cl-user :results output
|
|||
|
(5am:run-all-tests)
|
|||
|
|
|||
|
;;; test-queue.lisp ends here
|
|||
|
#+end_src
|
|||
|
|
|||
|
#+caption: Run all tests result.
|
|||
|
#+name: lst:5am-run-all-tests
|
|||
|
#+RESULTS: lst:5am-test-queue-footer
|
|||
|
#+begin_src text -n :exports never
|
|||
|
|
|||
|
Running test suite NIL
|
|||
|
Running test TEST-QUEUE-CREATE ..
|
|||
|
Running test TEST-QUEUE-ENQUEUE ....................
|
|||
|
Running test TEST-QUEUE-PREPEND ....................
|
|||
|
Running test TEST-QUEUE-DEQUEUE ...............................
|
|||
|
Running test TEST-QUEUE-EMPTY ....................
|
|||
|
Running test TEST-QUEUE-FIRST ....................
|
|||
|
Running test TEST-QUEUE-NTH ........................................
|
|||
|
Running test TEST-QUEUE-LAST ...........
|
|||
|
Running test TEST-QUEUE-ALL ...........
|
|||
|
Running test TEST-QUEUE-COPY ....................................................................................................
|
|||
|
Running test TEST-QUEUE-LENGTH ...........
|
|||
|
Running test TEST-QUEUE-CLEAR .....................
|
|||
|
Did 307 checks.
|
|||
|
Pass: 307 (100%)
|
|||
|
Skip: 0 ( 0%)
|
|||
|
Fail: 0 ( 0%)
|
|||
|
#+end_src
|
|||
|
|
|||
|
* Emacs Lisp
|
|||
|
|
|||
|
#+caption: Emacs Lisp ~KEU~ implentation: 1st part of ~keu.el~.
|
|||
|
#+begin_src emacs-lisp -n :lexical t
|
|||
|
;;; keu.el --- handle FIFOs of items -*- lexical-binding: t; -*-
|
|||
|
|
|||
|
(defun keu-create ()
|
|||
|
"Create an empty keu."
|
|||
|
(list nil nil))
|
|||
|
|
|||
|
(defun keu-enqueue (keu item)
|
|||
|
"Add ITEM to the rear of KEU."
|
|||
|
(if (car keu)
|
|||
|
(setcdr (cadr keu) (setf (cadr keu) (list item)))
|
|||
|
(setcar keu (car (setcdr keu (list (list item)))))))
|
|||
|
|
|||
|
(defun keu-prepend (keu item)
|
|||
|
"Add ITEM in front of the KEU head."
|
|||
|
(if (car keu)
|
|||
|
(push item (car keu))
|
|||
|
(setcar keu (car (setcdr keu `((,item)))))))
|
|||
|
|
|||
|
(defun keu-dequeue (keu)
|
|||
|
"Remove the first ITEM from KEU and return it.
|
|||
|
Returns nil if KEU is empty."
|
|||
|
(unless (car keu)
|
|||
|
(setf (cdr keu) '(nil)))
|
|||
|
(pop (car keu)))
|
|||
|
|
|||
|
(defun keu-empty (keu)
|
|||
|
"Return t if KEU is empty, otherwise return nil."
|
|||
|
(null (car keu)))
|
|||
|
|
|||
|
(defun keu-first (keu)
|
|||
|
"Return the first ITEM from KEU or nil if it is empty,
|
|||
|
without removing it from KEU."
|
|||
|
(caar keu))
|
|||
|
#+end_src
|
|||
|
|
|||
|
#+caption: Emacs Lisp ~KEU~ implentation: 2nd part of ~key.el~.
|
|||
|
#+begin_src emacs-lisp -n :lexical t
|
|||
|
(defun keu-nth (keu n)
|
|||
|
"Return the Nth ITEM from KEU, without removing it.
|
|||
|
If the length of the KEU is less than N, return nil. The first
|
|||
|
element in KEU has index 0."
|
|||
|
(nth n (car keu)))
|
|||
|
|
|||
|
(defun keu-last (keu)
|
|||
|
"Return the last element of KEU, without removing it.
|
|||
|
Returns nil if KEU is empty."
|
|||
|
(cadr keu))
|
|||
|
|
|||
|
(defun keu-all (keu)
|
|||
|
"Return a list of all ITEMS in KEU or nil if it is empty.
|
|||
|
The oldest ITEM in KEU is the first in the list."
|
|||
|
(car keu))
|
|||
|
|
|||
|
(defun keu-copy (keu)
|
|||
|
"Return a copy of KEU.
|
|||
|
The new queue contains all ITEMS in KEU in the same order.
|
|||
|
The ITEMS themselves are *not* copied."
|
|||
|
(let ((q (keu-create))
|
|||
|
(items (car keu)))
|
|||
|
(when (car keu)
|
|||
|
(setcar q (list (caar keu)))
|
|||
|
(setcdr q (list (car q)))
|
|||
|
(while (setq items (cdr items))
|
|||
|
(setcdr q (list (setcdr (cadr q) (list (car items)))))))
|
|||
|
q))
|
|||
|
|
|||
|
(defun keu-length (keu)
|
|||
|
"Return the number of ITEMS in KEU."
|
|||
|
(length (car keu)))
|
|||
|
|
|||
|
;; BUG: does not return (nil nil), but ((nil) (nil)) when not empty
|
|||
|
(defun keu-clear (keu)
|
|||
|
"Remove all elements from KEU."
|
|||
|
(setf (car keu) (list nil nil)))
|
|||
|
#+end_src
|
|||
|
|
|||
|
#+caption: Emacs Lisp ~KEU~ implentation: 3rd part of ~key.el~.
|
|||
|
#+begin_src emacs-lisp -n :eval never :lexical t
|
|||
|
(defmacro keu--when-generators (then)
|
|||
|
"Evaluate THEN if `generator' library is available."
|
|||
|
(declare (debug t))
|
|||
|
(if (require 'generator nil 'noerror) then))
|
|||
|
|
|||
|
(keu--when-generators
|
|||
|
(iter-defun keu-iterator-create (keu)
|
|||
|
"Return a KEU iterator object.
|
|||
|
|
|||
|
Calling `iter-next' on this object will retrieve the next element
|
|||
|
from KEU, which itself is not modified."
|
|||
|
(let ((list (car keu)))
|
|||
|
(while list (iter-yield (pop list))))))
|
|||
|
|
|||
|
;; New functionality:
|
|||
|
|
|||
|
(defun items-enqueue (keu items)
|
|||
|
"Add ITEMS to the KEU rear."
|
|||
|
(dolist (item items)
|
|||
|
(keu-enqueue keu item)))
|
|||
|
|
|||
|
(defun items-dequeue (keu)
|
|||
|
"Remove all ITEMS from the from the KEU head and return them.
|
|||
|
The order of the removed ITEMS equals the order of the ITEMS in KEU."
|
|||
|
(let (result)
|
|||
|
(while (not (keu-empty keu))
|
|||
|
(push (keu-dequeue keu) result))
|
|||
|
(reverse result)))
|
|||
|
|
|||
|
(provide 'keu)
|
|||
|
;;; keu.el ends here
|
|||
|
#+end_src
|
|||
|
|
|||
|
** ~KEU~ Elisp Regression Testing
|
|||
|
|
|||
|
#+caption: Elisp Regression Testing ~KEU~: 1st part of ~test-keu.el~.
|
|||
|
#+begin_src emacs-lisp -n :tangle test-keu.el
|
|||
|
;;; test-keu.el --- ERT for keu.el -*- lexical-binding: t; -*-
|
|||
|
|
|||
|
(require 'ert)
|
|||
|
(require 'keu)
|
|||
|
|
|||
|
(ert-deftest keu-create-test ()
|
|||
|
(should (equal (keu-create) (list nil nil))))
|
|||
|
|
|||
|
(ert-deftest keu-enqueue-test ()
|
|||
|
(let ((keu (keu-create))
|
|||
|
(data '((nil) (nil a) (nil a nil) (nil a nil b) (nil a nil b nil)
|
|||
|
(a) (a nil) (a nil b) (a nil b nil) (a nil b nil c))))
|
|||
|
(dolist (datum data)
|
|||
|
(dolist (item datum)
|
|||
|
(keu-enqueue keu item))
|
|||
|
(should (equal keu `(,datum ,(list (car (reverse datum)))))))))
|
|||
|
|
|||
|
(ert-deftest keu-dequeue-test ()
|
|||
|
(let ((keu (keu-create))
|
|||
|
(data '((nil) (nil a) (nil a nil) (nil a nil b) (nil a nil b nil)
|
|||
|
(a) (a nil) (a nil b) (a nil b nil) (a nil b nil c))))
|
|||
|
(dolist (datum data)
|
|||
|
(dolist (item datum)
|
|||
|
(keu-enqueue keu item))
|
|||
|
(let (mutad)
|
|||
|
(dotimes (n (length datum))
|
|||
|
(push (keu-dequeue keu) mutad))
|
|||
|
(should (equal datum (reverse mutad)))))))
|
|||
|
|
|||
|
(ert-deftest keu-prepend-test ()
|
|||
|
(let ((keu (keu-create))
|
|||
|
(data '(a b c)))
|
|||
|
(keu-prepend keu (nth 1 data))
|
|||
|
(keu-prepend keu (nth 0 data))
|
|||
|
(keu-enqueue keu (nth 2 data))
|
|||
|
(should (equal data (list (keu-nth keu 0) (keu-nth keu 1) (keu-nth keu 2))))))
|
|||
|
#+end_src
|
|||
|
|
|||
|
#+caption: Elisp Regression Testing ~KEU~: 2nd part of ~test-keu.el~.
|
|||
|
#+begin_src emacs-lisp -n :tangle test-keu.el
|
|||
|
(ert-deftest keu-empty-test ()
|
|||
|
(let ((ok (keu-create))
|
|||
|
(no (keu-create))
|
|||
|
(data '((nil) (nil a) (nil a nil) (nil a nil b) (nil a nil b nil)
|
|||
|
(a) (a nil) (a nil b) (a nil b nil) (a nil b nil c))))
|
|||
|
(dolist (datum data)
|
|||
|
(dolist (item datum) (keu-enqueue no item))
|
|||
|
(dotimes (count (length datum)) (keu-dequeue no))
|
|||
|
(should (equal t (keu-empty ok)))
|
|||
|
(dolist (item datum) (keu-enqueue no item))
|
|||
|
(dotimes (count (1- (length datum))) (keu-dequeue no))
|
|||
|
(should (equal nil (keu-empty no))))))
|
|||
|
|
|||
|
(ert-deftest keu-copy-test ()
|
|||
|
(let ((old (keu-create))
|
|||
|
(data '((nil) (nil a) (nil a nil) (nil a nil b) (nil a nil b nil)
|
|||
|
(a) (a nil) (a nil b) (a nil b nil) (a nil b nil c))))
|
|||
|
(dolist (datum data)
|
|||
|
(keu-clear old)
|
|||
|
(items-enqueue old datum)
|
|||
|
(let ((new (keu-copy keu)))
|
|||
|
(message "datum: `%S'" datum)
|
|||
|
(message "old: `%S'" old)
|
|||
|
(message "new: `%S'" new)
|
|||
|
(should (equal old new))))))
|
|||
|
|
|||
|
(ert-deftest keu-iterator-test ()
|
|||
|
(let ((keu (keu-create))
|
|||
|
(data '((nil) (nil a) (nil a nil) (nil a nil b) (nil a nil b nil)
|
|||
|
(a) (a nil) (a nil b) (a nil b nil) (a nil b nil c))))
|
|||
|
(dolist (datum data)
|
|||
|
(items-enqueue keu datum)
|
|||
|
(let ((keu-iterator (keu-iterator-create keu)))
|
|||
|
(dotimes (n (length datum))
|
|||
|
(should (equal (nth n datum) (iter-next keu-iterator))))))))
|
|||
|
|
|||
|
;;; test-keu.el ends here
|
|||
|
#+end_src
|
|||
|
|
|||
|
* [[https://common-lisp-libraries.readthedocs.io/fiveam/][Fiveam Common Lisp Regression Testing Framework]]
|
|||
|
|
|||
|
Here, I explore how to use [[https://common-lisp-libraries.readthedocs.io/fiveam/][fiveam]].
|
|||
|
|
|||
|
#+caption[Load the "FIVEAM" package]:
|
|||
|
#+caption: Load the ~FIVEAM~ package.
|
|||
|
#+header: :wrap "src text -n"
|
|||
|
#+begin_src lisp -n :results output
|
|||
|
(ql:quickload :fiveam)
|
|||
|
#+end_src
|
|||
|
|
|||
|
#+caption[Result of loading the "FIVEAM" package]:
|
|||
|
#+caption: Result of loading the ~FIVEAM~ package.
|
|||
|
#+RESULTS:
|
|||
|
#+begin_src text -n
|
|||
|
To load "fiveam":
|
|||
|
Load 1 ASDF system:
|
|||
|
fiveam
|
|||
|
; Loading "fiveam"
|
|||
|
|
|||
|
#+end_src
|
|||
|
|
|||
|
#+caption[Define a test from within in the "FIVEAM" package]:
|
|||
|
#+caption: Define a test from within in the ~FIVEAM~ package.
|
|||
|
#+begin_src lisp -n :package fiveam :results silent
|
|||
|
(test 5am-test-demo
|
|||
|
"This demonstrates the basic use of test and check."
|
|||
|
(is (listp (list 1 2)))
|
|||
|
(is (= 5 (+ 2 3)) "This should pass.")
|
|||
|
(is (= 4 4.1) "~D and ~D are not = to each other." 4 4.1))
|
|||
|
#+end_src
|
|||
|
|
|||
|
#+caption[Run the "5AM-TEST-DEMO" test from with the "FIVEAM" package]:
|
|||
|
#+caption: Run the ~5AM-TEST-DEMO~ test from with the ~FIVEAM~ package.
|
|||
|
#+header: :wrap "src text -n"
|
|||
|
#+begin_src lisp -n :package fiveam :results output
|
|||
|
(run! '5am-test-demo)
|
|||
|
#+end_src
|
|||
|
|
|||
|
#+RESULTS:
|
|||
|
#+begin_src text -n
|
|||
|
|
|||
|
Running test 5AM-TEST-DEMO ..f
|
|||
|
Did 3 checks.
|
|||
|
Pass: 2 (66%)
|
|||
|
Skip: 0 ( 0%)
|
|||
|
Fail: 1 (33%)
|
|||
|
Failure Details:
|
|||
|
--------------------------------
|
|||
|
5AM-TEST-DEMO [This demonstrates the basic use of test and check.]:
|
|||
|
4 and 4.1 are not = to each other.
|
|||
|
--------------------------------
|
|||
|
#+end_src
|
|||
|
|
|||
|
#+caption[Define a test from within the "CL-USER" package]:
|
|||
|
#+caption: Define a test from within the ~CL-USER~ package.
|
|||
|
#+begin_src lisp -n :results silent
|
|||
|
(5am:test cl-test-demo
|
|||
|
"This demonstrates the basic use of test and check."
|
|||
|
(5am:is (listp (list 1 2)))
|
|||
|
(5am:is (= 5 (+ 2 3)) "This should pass.")
|
|||
|
(5am:is (= 4 4.1) "~D and ~D are not = to each other." 4 4.1))
|
|||
|
#+end_src
|
|||
|
|
|||
|
#+caption[Run the "CL-TEST-DEMO" test from within the "CL-USER" package]:
|
|||
|
#+caption: Run the ~CL-TEST-DEMO~ test from within the ~CL-USER~ package.
|
|||
|
#+header: :wrap "src text -n"
|
|||
|
#+begin_src lisp -n :results output
|
|||
|
(5am:run! 'cl-test-demo)
|
|||
|
#+end_src
|
|||
|
|
|||
|
#+RESULTS:
|
|||
|
#+begin_src text -n
|
|||
|
|
|||
|
Running test CL-TEST-DEMO ..f
|
|||
|
Did 3 checks.
|
|||
|
Pass: 2 (66%)
|
|||
|
Skip: 0 ( 0%)
|
|||
|
Fail: 1 (33%)
|
|||
|
Failure Details:
|
|||
|
--------------------------------
|
|||
|
CL-TEST-DEMO [This demonstrates the basic use of test and check.]:
|
|||
|
4 and 4.1 are not = to each other.
|
|||
|
--------------------------------
|
|||
|
#+end_src
|
|||
|
|
|||
|
* Common Lisp
|
|||
|
|
|||
|
#+caption: Common Lisp ~KEU~ implementation.
|
|||
|
#+begin_src lisp -n :package cl-user :results silent
|
|||
|
;;; keu.lisp --- handle FIFOs of items
|
|||
|
|
|||
|
(defun keu-create ()
|
|||
|
"Create an empty keu."
|
|||
|
(list nil nil))
|
|||
|
|
|||
|
;; BUG: in case of `(null item)'
|
|||
|
(defun keu-enqueue (keu item)
|
|||
|
"Add ITEM to the rear of KEU."
|
|||
|
(if (car keu)
|
|||
|
(setf (cdr (cadr keu)) (setf (cadr keu) (list item)))
|
|||
|
(setf (car keu) (car (setf (cdr keu) (list (list item)))))))
|
|||
|
|
|||
|
(defun keu-prepend (keu item)
|
|||
|
"Add ITEM in front of the KEU head."
|
|||
|
(if (car keu)
|
|||
|
(push item (car keu))
|
|||
|
(setf (car keu) (car (setf (cdr keu) (list (list item)))))))
|
|||
|
|
|||
|
(defun keu-dequeue (keu)
|
|||
|
"Remove the first ITEM from KEU and return it.
|
|||
|
Returns nil if the KEU is empty."
|
|||
|
(unless (car keu)
|
|||
|
(setf (cdr keu) '(nil)))
|
|||
|
(prog1
|
|||
|
(caar keu)
|
|||
|
(setf (car keu) (cdar keu))))
|
|||
|
|
|||
|
(defun keu-empty (keu)
|
|||
|
"Return t if KEU is empty, otherwise return nil."
|
|||
|
(null (car keu)))
|
|||
|
#+end_src
|
|||
|
|
|||
|
#+caption: Common Lisp KEU scaffolding.
|
|||
|
#+begin_src lisp -n :package cl-user :results silent
|
|||
|
(defun keu-first (keu)
|
|||
|
"Return the first ITEM from KEU or nil if it is empty,
|
|||
|
without removing it from KEU."
|
|||
|
(caar keu))
|
|||
|
|
|||
|
(defun keu-nth (keu n)
|
|||
|
"Return the Nth ITEM from KEU, without removing it.
|
|||
|
If the length of the KEU is less than N, return nil. The first
|
|||
|
element in KEU has index 0."
|
|||
|
(nth n (car keu)))
|
|||
|
|
|||
|
(defun keu-last (keu)
|
|||
|
"Return the last element of KEU, without removing it.
|
|||
|
Returns nil if the QUEUE is empty."
|
|||
|
(cadr keu))
|
|||
|
|
|||
|
(defun keu-all (keu)
|
|||
|
"Return a list of all ITEMS in QUEUE or nil if it is empty.
|
|||
|
The oldest ITEM in KEU is the first in the list."
|
|||
|
(car keu))
|
|||
|
|
|||
|
(defun items-enqueue (keu items)
|
|||
|
"Add ITEMS to the KEU rear."
|
|||
|
(dolist (item items)
|
|||
|
(keu-enqueue keu item)))
|
|||
|
|
|||
|
(defun items-dequeue (keu)
|
|||
|
"Remove all ITEMS from the KEU head and return them.
|
|||
|
The order of the removed ITEMS equals the order of the ITEMS in KEU."
|
|||
|
(loop while (not (keu-empty keu))
|
|||
|
collect (keu-dequeue keu)))
|
|||
|
|
|||
|
(defun keu-copy (keu)
|
|||
|
"CHEAT"
|
|||
|
(let ((queue (keu-create)))
|
|||
|
(items-enqueue queue (car keu))
|
|||
|
queue))
|
|||
|
#+end_src
|
|||
|
|
|||
|
#+caption: Common Lisp ~QUEUE~ testing.
|
|||
|
#+begin_src lisp -n :package cl-user :results silent
|
|||
|
;; Testing:
|
|||
|
|
|||
|
(let ((keu (keu-create))
|
|||
|
(data '(0 1 2)))
|
|||
|
(items-enqueue keu data)
|
|||
|
(equal (items-dequeue keu) data))
|
|||
|
|
|||
|
(let ((keu (keu-create))
|
|||
|
(data '(0 (nil) 2)))
|
|||
|
(items-enqueue keu data)
|
|||
|
`(,(items-dequeue keu) must equal ,data))
|
|||
|
|
|||
|
(let ((keu (keu-create))
|
|||
|
(data '(0 1 2)))
|
|||
|
(keu-prepend keu (nth 1 data))
|
|||
|
(keu-prepend keu (nth 0 data))
|
|||
|
(keu-enqueue keu (nth 2 data))
|
|||
|
(equal data (items-dequeue keu)))
|
|||
|
|
|||
|
(let ((keu (keu-create))
|
|||
|
(data '(a b c)))
|
|||
|
(items-enqueue keu data)
|
|||
|
(equal (keu-copy keu) keu))
|
|||
|
#+end_src
|
|||
|
|
|||
|
** ~queue-lisp~ code critique
|
|||
|
|
|||
|
#+caption[Common Lisp code critique]:
|
|||
|
#+caption: Common Lisp code critique.
|
|||
|
#+header: :wrap "src text -n :exports none"
|
|||
|
#+begin_src lisp -n :exports code :package cs325-user :results output :tangle no
|
|||
|
(critique
|
|||
|
(defun keu-create ()
|
|||
|
"Create an empty keu."
|
|||
|
(list nil nil)))
|
|||
|
|
|||
|
(critique
|
|||
|
(defun keu-enqueue (keu item)
|
|||
|
"Add ITEM to the rear of KEU."
|
|||
|
(if (car keu)
|
|||
|
(setf (cdr (cadr keu)) (setf (cadr keu) (list item)))
|
|||
|
(setf (car keu) (car (setf (cdr keu) (list (list item))))))))
|
|||
|
|
|||
|
(critique
|
|||
|
(defun keu-prepend (keu item)
|
|||
|
"Add ITEM in front of the KEU head."
|
|||
|
(if (car keu)
|
|||
|
(push item (car keu))
|
|||
|
(setf (car keu) (car (setf (cdr keu) (list (list item))))))))
|
|||
|
|
|||
|
(critique
|
|||
|
(defun keu-dequeue (keu)
|
|||
|
"Remove the first ITEM from KEU and return it.
|
|||
|
Returns nil if the KEU is empty."
|
|||
|
(unless (car keu)
|
|||
|
(setf (cdr keu) '(nil)))
|
|||
|
(prog1
|
|||
|
(caar keu)
|
|||
|
(setf (car keu) (cdar keu)))) )
|
|||
|
|
|||
|
(critique
|
|||
|
(defun keu-empty (keu)
|
|||
|
"Return t if KEU is empty, otherwise return nil."
|
|||
|
(null (car keu))))
|
|||
|
|
|||
|
(critique
|
|||
|
(defun items-enqueue (keu items)
|
|||
|
"Add ITEMS to the KEU rear."
|
|||
|
(dolist (item items)
|
|||
|
(keu-enqueue keu item))))
|
|||
|
|
|||
|
(critique
|
|||
|
(defun items-dequeue (keu)
|
|||
|
"Remove all ITEMS from the KEU head and return them.
|
|||
|
The order of the removed ITEMS equals the order of the ITEMS in KEU."
|
|||
|
(loop while (not (fifo-empty keu))
|
|||
|
collect (fifo-dequeue keu))))
|
|||
|
#+end_src
|
|||
|
|
|||
|
#+RESULTS:
|
|||
|
#+begin_src text -n :exports none
|
|||
|
----------------------------------------------------------------------
|
|||
|
----------------------------------------------------------------------
|
|||
|
----------------------------------------------------------------------
|
|||
|
----------------------------------------------------------------------
|
|||
|
----------------------------------------------------------------------
|
|||
|
----------------------------------------------------------------------
|
|||
|
----------------------------------------------------------------------
|
|||
|
#+end_src
|
|||
|
|
|||
|
** Common Lisp Code disassembly :noexport:
|
|||
|
|
|||
|
*** ~Cl keu-create~ disassembly
|
|||
|
|
|||
|
#+caption: Common Lisp ~keu-create~ disassembly.
|
|||
|
#+header: :wrap "src lisp -n :eval never :tangle no"
|
|||
|
#+begin_src lisp -n :exports both :package cl-user :results output :tangle no
|
|||
|
(disassemble 'keu-create)
|
|||
|
#+end_src
|
|||
|
|
|||
|
#+RESULTS:
|
|||
|
#+begin_src lisp -n :eval never :tangle no
|
|||
|
; disassembly for KEU-CREATE
|
|||
|
; Size: 215 bytes. Origin: #x5365698F ; KEU-CREATE
|
|||
|
; 98F: 498B4510 MOV RAX, [R13+16] ; thread.binding-stack-pointer
|
|||
|
; 993: 488945C0 MOV [RBP-64], RAX
|
|||
|
; 997: 4D896D28 MOV [R13+40], R13 ; thread.pseudo-atomic-bits
|
|||
|
; 99B: 498B5558 MOV RDX, [R13+88] ; thread.cons-tlab
|
|||
|
; 99F: 488D4220 LEA RAX, [RDX+32]
|
|||
|
; 9A3: 493B4560 CMP RAX, [R13+96]
|
|||
|
; 9A7: 0F87AA000000 JA L2
|
|||
|
; 9AD: 49894558 MOV [R13+88], RAX ; thread.cons-tlab
|
|||
|
; 9B1: L0: 48C70217010050 MOV QWORD PTR [RDX], #x50000117 ; NIL
|
|||
|
; 9B8: 48C7421017010050 MOV QWORD PTR [RDX+16], #x50000117 ; NIL
|
|||
|
; 9C0: 48C7421817010050 MOV QWORD PTR [RDX+24], #x50000117 ; NIL
|
|||
|
; 9C8: 488D4217 LEA RAX, [RDX+23]
|
|||
|
; 9CC: 48894208 MOV [RDX+8], RAX
|
|||
|
; 9D0: 80CA07 OR DL, 7
|
|||
|
; 9D3: 4D316D28 XOR [R13+40], R13 ; thread.pseudo-atomic-bits
|
|||
|
; 9D7: 7402 JEQ L1
|
|||
|
; 9D9: CC09 INT3 9 ; pending interrupt trap
|
|||
|
; 9DB: L1: C9 LEAVE
|
|||
|
; 9DC: F8 CLC
|
|||
|
; 9DD: C3 RET
|
|||
|
; 9DE: CC10 INT3 16 ; Invalid argument count trap
|
|||
|
; 9E0: CC10 INT3 16 ; Invalid argument count trap
|
|||
|
; 9E2: 6A10 PUSH 16
|
|||
|
; 9E4: FF142588050050 CALL [#x50000588] ; #x52A005B0: LIST-ALLOC-TRAMP
|
|||
|
; 9EB: 58 POP RAX
|
|||
|
; 9EC: E933FAFFFF JMP #x53656424 ; #<FUNCTION ITEMS-DEQUEUE {5365639B}>
|
|||
|
; 9F1: CC10 INT3 16 ; Invalid argument count trap
|
|||
|
; 9F3: CC10 INT3 16 ; Invalid argument count trap
|
|||
|
; 9F5: CC10 INT3 16 ; Invalid argument count trap
|
|||
|
; 9F7: CC10 INT3 16 ; Invalid argument count trap
|
|||
|
; 9F9: 6A10 PUSH 16
|
|||
|
; 9FB: FF142588050050 CALL [#x50000588] ; #x52A005B0: LIST-ALLOC-TRAMP
|
|||
|
; A02: 58 POP RAX
|
|||
|
; A03: E9BAFCFFFF JMP #x536566C2 ; #<FUNCTION KEU-PREPEND {5365666B}>
|
|||
|
; A08: 6A10 PUSH 16
|
|||
|
; A0A: FF142588050050 CALL [#x50000588] ; #x52A005B0: LIST-ALLOC-TRAMP
|
|||
|
; A11: 58 POP RAX
|
|||
|
; A12: E904FDFFFF JMP #x5365671B ; #<FUNCTION KEU-PREPEND {5365666B}>
|
|||
|
; A17: 6A10 PUSH 16
|
|||
|
; A19: FF142588050050 CALL [#x50000588] ; #x52A005B0: LIST-ALLOC-TRAMP
|
|||
|
; A20: 58 POP RAX
|
|||
|
; A21: E926FDFFFF JMP #x5365674C ; #<FUNCTION KEU-PREPEND {5365666B}>
|
|||
|
; A26: CC10 INT3 16 ; Invalid argument count trap
|
|||
|
; A28: 6A10 PUSH 16
|
|||
|
; A2A: FF142588050050 CALL [#x50000588] ; #x52A005B0: LIST-ALLOC-TRAMP
|
|||
|
; A31: 58 POP RAX
|
|||
|
; A32: E903FEFFFF JMP #x5365683A ; #<FUNCTION KEU-ENQUEUE {536567CB}>
|
|||
|
; A37: 6A10 PUSH 16
|
|||
|
; A39: FF142588050050 CALL [#x50000588] ; #x52A005B0: LIST-ALLOC-TRAMP
|
|||
|
; A40: 58 POP RAX
|
|||
|
; A41: E980FEFFFF JMP #x536568C6 ; #<FUNCTION KEU-ENQUEUE {536567CB}>
|
|||
|
; A46: 6A10 PUSH 16
|
|||
|
; A48: FF142588050050 CALL [#x50000588] ; #x52A005B0: LIST-ALLOC-TRAMP
|
|||
|
; A4F: 58 POP RAX
|
|||
|
; A50: E9A2FEFFFF JMP #x536568F7 ; #<FUNCTION KEU-ENQUEUE {536567CB}>
|
|||
|
; A55: CC10 INT3 16 ; Invalid argument count trap
|
|||
|
; A57: L2: 6A20 PUSH 32
|
|||
|
; A59: FF142588050050 CALL [#x50000588] ; #x52A005B0: LIST-ALLOC-TRAMP
|
|||
|
; A60: 5A POP RDX
|
|||
|
; A61: E94BFFFFFF JMP L0
|
|||
|
#+end_src
|
|||
|
|
|||
|
*** ~CL keu-enqueue~ disassembly
|
|||
|
|
|||
|
#+caption: Common Lisp ~keu-enqueue~ disassembly.
|
|||
|
#+header: :wrap "src lisp -n :eval never :tangle no"
|
|||
|
#+begin_src lisp -n :exports both :package cl-user :results output :tangle no
|
|||
|
(disassemble 'keu-enqueue)
|
|||
|
#+end_src
|
|||
|
|
|||
|
#+RESULTS:
|
|||
|
#+begin_src lisp -n :eval never :tangle no
|
|||
|
; disassembly for KEU-ENQUEUE
|
|||
|
; Size: 435 bytes. Origin: #x53653073 (segment 1 of 2) ; KEU-ENQUEUE
|
|||
|
; 073: 498B4510 MOV RAX, [R13+16] ; thread.binding-stack-pointer
|
|||
|
; 077: 488945D8 MOV [RBP-40], RAX
|
|||
|
; 07B: 418D41F9 LEA EAX, [R9-7]
|
|||
|
; 07F: A80F TEST AL, 15
|
|||
|
; 081: 0F856F010000 JNE L13
|
|||
|
; 087: 4D8B41F9 MOV R8, [R9-7]
|
|||
|
; 08B: 4981F817010050 CMP R8, #x50000117 ; NIL
|
|||
|
; 092: 0F84A4000000 JEQ L6
|
|||
|
; 098: 4D8B4101 MOV R8, [R9+1]
|
|||
|
; 09C: 418D40F9 LEA EAX, [R8-7]
|
|||
|
; 0A0: A80F TEST AL, 15
|
|||
|
; 0A2: 0F8591000000 JNE L5
|
|||
|
; 0A8: 4D8B50F9 MOV R10, [R8-7]
|
|||
|
; 0AC: 4D8B5901 MOV R11, [R9+1]
|
|||
|
; 0B0: 4D896D28 MOV [R13+40], R13 ; thread.pseudo-atomic-bits
|
|||
|
; 0B4: 498B4558 MOV RAX, [R13+88] ; thread.cons-tlab
|
|||
|
; 0B8: 4C8D4010 LEA R8, [RAX+16]
|
|||
|
; 0BC: 4D3B4560 CMP R8, [R13+96]
|
|||
|
; 0C0: 0F87DF010000 JA L14
|
|||
|
; 0C6: 4D894558 MOV [R13+88], R8 ; thread.cons-tlab
|
|||
|
; 0CA: L0: 488938 MOV [RAX], RDI
|
|||
|
; 0CD: 48C7400817010050 MOV QWORD PTR [RAX+8], #x50000117 ; NIL
|
|||
|
; 0D5: 4C8D4007 LEA R8, [RAX+7]
|
|||
|
; 0D9: 4D316D28 XOR [R13+40], R13 ; thread.pseudo-atomic-bits
|
|||
|
; 0DD: 7402 JEQ L1
|
|||
|
; 0DF: CC09 INT3 9 ; pending interrupt trap
|
|||
|
; 0E1: L1: 4981FB17010050 CMP R11, #x50000117 ; NIL
|
|||
|
; 0E8: 744C JEQ L4
|
|||
|
; 0EA: 418D43F9 LEA EAX, [R11-7]
|
|||
|
; 0EE: A80F TEST AL, 15
|
|||
|
; 0F0: 7544 JNE L4
|
|||
|
; 0F2: 498BC3 MOV RAX, R11
|
|||
|
; 0F5: 48C1E80A SHR RAX, 10
|
|||
|
; 0F9: 25FFFF0F00 AND EAX, 1048575
|
|||
|
; 0FE: 41C6040400 MOV BYTE PTR [R12+RAX], 0
|
|||
|
; 103: 4D8943F9 MOV [R11-7], R8
|
|||
|
; 107: 4981FA17010050 CMP R10, #x50000117 ; NIL
|
|||
|
; 10E: 7423 JEQ L3
|
|||
|
; 110: 418D42F9 LEA EAX, [R10-7]
|
|||
|
; 114: A80F TEST AL, 15
|
|||
|
; 116: 751B JNE L3
|
|||
|
; 118: 498BC2 MOV RAX, R10
|
|||
|
; 11B: 48C1E80A SHR RAX, 10
|
|||
|
; 11F: 25FFFF0F00 AND EAX, 1048575
|
|||
|
; 124: 41C6040400 MOV BYTE PTR [R12+RAX], 0
|
|||
|
; 129: 4D894201 MOV [R10+1], R8
|
|||
|
; 12D: 498BD0 MOV RDX, R8
|
|||
|
; 130: L2: C9 LEAVE
|
|||
|
; 131: F8 CLC
|
|||
|
; 132: C3 RET
|
|||
|
; 133: L3: CC65 INT3 101 ; OBJECT-NOT-CONS-ERROR
|
|||
|
; 135: 28 BYTE #X28 ; R10(d)
|
|||
|
; 136: L4: CC65 INT3 101 ; OBJECT-NOT-CONS-ERROR
|
|||
|
; 138: 2C BYTE #X2C ; R11(d)
|
|||
|
; 139: L5: CC52 INT3 82 ; OBJECT-NOT-LIST-ERROR
|
|||
|
; 13B: 20 BYTE #X20 ; R8(d)
|
|||
|
; 13C: L6: 4D896D28 MOV [R13+40], R13 ; thread.pseudo-atomic-bits
|
|||
|
; 140: 498B4558 MOV RAX, [R13+88] ; thread.cons-tlab
|
|||
|
; 144: 4C8D4010 LEA R8, [RAX+16]
|
|||
|
; 148: 4D3B4560 CMP R8, [R13+96]
|
|||
|
; 14C: 0F8762010000 JA L15
|
|||
|
; 152: 4D894558 MOV [R13+88], R8 ; thread.cons-tlab
|
|||
|
; 156: L7: 488938 MOV [RAX], RDI
|
|||
|
; 159: 48C7400817010050 MOV QWORD PTR [RAX+8], #x50000117 ; NIL
|
|||
|
; 161: 4C8D5007 LEA R10, [RAX+7]
|
|||
|
; 165: 4D316D28 XOR [R13+40], R13 ; thread.pseudo-atomic-bits
|
|||
|
; 169: 7402 JEQ L8
|
|||
|
; 16B: CC09 INT3 9 ; pending interrupt trap
|
|||
|
; 16D: L8: 4D896D28 MOV [R13+40], R13 ; thread.pseudo-atomic-bits
|
|||
|
; 171: 498B4558 MOV RAX, [R13+88] ; thread.cons-tlab
|
|||
|
; 175: 4C8D4010 LEA R8, [RAX+16]
|
|||
|
; 179: 4D3B4560 CMP R8, [R13+96]
|
|||
|
; 17D: 0F8740010000 JA L16
|
|||
|
; 183: 4D894558 MOV [R13+88], R8 ; thread.cons-tlab
|
|||
|
; 187: L9: 4C8910 MOV [RAX], R10
|
|||
|
; 18A: 48C7400817010050 MOV QWORD PTR [RAX+8], #x50000117 ; NIL
|
|||
|
; 192: 4C8D4007 LEA R8, [RAX+7]
|
|||
|
; 196: 4D316D28 XOR [R13+40], R13 ; thread.pseudo-atomic-bits
|
|||
|
; 19A: 7402 JEQ L10
|
|||
|
; 19C: CC09 INT3 9 ; pending interrupt trap
|
|||
|
; 19E: L10: 4981F917010050 CMP R9, #x50000117 ; NIL
|
|||
|
; 1A5: 7447 JEQ L12
|
|||
|
; 1A7: 498BC1 MOV RAX, R9
|
|||
|
; 1AA: 48C1E80A SHR RAX, 10
|
|||
|
; 1AE: 25FFFF0F00 AND EAX, 1048575
|
|||
|
; 1B3: 41C6040400 MOV BYTE PTR [R12+RAX], 0
|
|||
|
; 1B8: 4D894101 MOV [R9+1], R8
|
|||
|
; 1BC: 4D8B40F9 MOV R8, [R8-7]
|
|||
|
; 1C0: 4981F917010050 CMP R9, #x50000117 ; NIL
|
|||
|
; 1C7: 741D JEQ L11
|
|||
|
; 1C9: 498BC1 MOV RAX, R9
|
|||
|
; 1CC: 48C1E80A SHR RAX, 10
|
|||
|
; 1D0: 25FFFF0F00 AND EAX, 1048575
|
|||
|
; 1D5: 41C6040400 MOV BYTE PTR [R12+RAX], 0
|
|||
|
; 1DA: 4D8941F9 MOV [R9-7], R8
|
|||
|
; 1DE: 498BD0 MOV RDX, R8
|
|||
|
; 1E1: E94AFFFFFF JMP L2
|
|||
|
; 1E6: L11: B817010050 MOV EAX, #x50000117 ; NIL
|
|||
|
; 1EB: CC65 INT3 101 ; OBJECT-NOT-CONS-ERROR
|
|||
|
; 1ED: 00 BYTE #X00 ; RAX(d)
|
|||
|
; 1EE: L12: B817010050 MOV EAX, #x50000117 ; NIL
|
|||
|
; 1F3: CC65 INT3 101 ; OBJECT-NOT-CONS-ERROR
|
|||
|
; 1F5: 00 BYTE #X00 ; RAX(d)
|
|||
|
; 1F6: L13: CC52 INT3 82 ; OBJECT-NOT-LIST-ERROR
|
|||
|
; 1F8: 24 BYTE #X24 ; R9(d)
|
|||
|
; Origin #x536532A5 (segment 2 of 2) ; KEU-ENQUEUE
|
|||
|
; 2A5: L14: 6A10 PUSH 16
|
|||
|
; 2A7: FF142588050050 CALL [#x50000588] ; #x52A005B0: LIST-ALLOC-TRAMP
|
|||
|
; 2AE: 58 POP RAX
|
|||
|
; 2AF: E916FEFFFF JMP L0
|
|||
|
; 2B4: L15: 6A10 PUSH 16
|
|||
|
; 2B6: FF142588050050 CALL [#x50000588] ; #x52A005B0: LIST-ALLOC-TRAMP
|
|||
|
; 2BD: 58 POP RAX
|
|||
|
; 2BE: E993FEFFFF JMP L7
|
|||
|
; 2C3: L16: 6A10 PUSH 16
|
|||
|
; 2C5: FF142588050050 CALL [#x50000588] ; #x52A005B0: LIST-ALLOC-TRAMP
|
|||
|
; 2CC: 58 POP RAX
|
|||
|
; 2CD: E9B5FEFFFF JMP L9
|
|||
|
#+end_src
|
|||
|
|
|||
|
*** ~CL keu-dequeue~ disassembly
|
|||
|
|
|||
|
#+caption: Common Lisp ~fifo-dequeue~ disassembly.
|
|||
|
#+header: :wrap "src lisp -n :eval never :tangle no"
|
|||
|
#+begin_src lisp -n :exports both :package cl-user :results output :tangle no
|
|||
|
(disassemble 'keu-dequeue)
|
|||
|
#+end_src
|
|||
|
|
|||
|
#+RESULTS:
|
|||
|
#+begin_src lisp -n :eval never :tangle no
|
|||
|
; disassembly for KEU-DEQUEUE
|
|||
|
; Size: 160 bytes. Origin: #x53652E43 ; KEU-DEQUEUE
|
|||
|
; 43: 498B4510 MOV RAX, [R13+16] ; thread.binding-stack-pointer
|
|||
|
; 47: 488945E8 MOV [RBP-24], RAX
|
|||
|
; 4B: 418D40F9 LEA EAX, [R8-7]
|
|||
|
; 4F: A80F TEST AL, 15
|
|||
|
; 51: 0F8589000000 JNE L5
|
|||
|
; 57: 4D8B48F9 MOV R9, [R8-7]
|
|||
|
; 5B: 4981F917010050 CMP R9, #x50000117 ; NIL
|
|||
|
; 62: 7525 JNE L0
|
|||
|
; 64: 4981F817010050 CMP R8, #x50000117 ; NIL
|
|||
|
; 6B: 746B JEQ L4
|
|||
|
; 6D: 488B0D8CFDFFFF MOV RCX, [RIP-628] ; '(NIL)
|
|||
|
; 74: 498BC0 MOV RAX, R8
|
|||
|
; 77: 48C1E80A SHR RAX, 10
|
|||
|
; 7B: 25FFFF0F00 AND EAX, 1048575
|
|||
|
; 80: 41C6040400 MOV BYTE PTR [R12+RAX], 0
|
|||
|
; 85: 49894801 MOV [R8+1], RCX
|
|||
|
; 89: L0: 4D8B48F9 MOV R9, [R8-7]
|
|||
|
; 8D: 418D41F9 LEA EAX, [R9-7]
|
|||
|
; 91: A80F TEST AL, 15
|
|||
|
; 93: 7540 JNE L3
|
|||
|
; 95: 498B51F9 MOV RDX, [R9-7]
|
|||
|
; 99: 4D8B48F9 MOV R9, [R8-7]
|
|||
|
; 9D: 418D41F9 LEA EAX, [R9-7]
|
|||
|
; A1: A80F TEST AL, 15
|
|||
|
; A3: 752D JNE L2
|
|||
|
; A5: 4D8B4901 MOV R9, [R9+1]
|
|||
|
; A9: 4981F817010050 CMP R8, #x50000117 ; NIL
|
|||
|
; B0: 7418 JEQ L1
|
|||
|
; B2: 498BC0 MOV RAX, R8
|
|||
|
; B5: 48C1E80A SHR RAX, 10
|
|||
|
; B9: 25FFFF0F00 AND EAX, 1048575
|
|||
|
; BE: 41C6040400 MOV BYTE PTR [R12+RAX], 0
|
|||
|
; C3: 4D8948F9 MOV [R8-7], R9
|
|||
|
; C7: C9 LEAVE
|
|||
|
; C8: F8 CLC
|
|||
|
; C9: C3 RET
|
|||
|
; CA: L1: B817010050 MOV EAX, #x50000117 ; NIL
|
|||
|
; CF: CC65 INT3 101 ; OBJECT-NOT-CONS-ERROR
|
|||
|
; D1: 00 BYTE #X00 ; RAX(d)
|
|||
|
; D2: L2: CC52 INT3 82 ; OBJECT-NOT-LIST-ERROR
|
|||
|
; D4: 24 BYTE #X24 ; R9(d)
|
|||
|
; D5: L3: CC52 INT3 82 ; OBJECT-NOT-LIST-ERROR
|
|||
|
; D7: 24 BYTE #X24 ; R9(d)
|
|||
|
; D8: L4: B817010050 MOV EAX, #x50000117 ; NIL
|
|||
|
; DD: CC65 INT3 101 ; OBJECT-NOT-CONS-ERROR
|
|||
|
; DF: 00 BYTE #X00 ; RAX(d)
|
|||
|
; E0: L5: CC52 INT3 82 ; OBJECT-NOT-LIST-ERROR
|
|||
|
; E2: 20 BYTE #X20 ; R8(d)
|
|||
|
#+end_src
|
|||
|
|
|||
|
* Local variables :noexport:
|
|||
|
|
|||
|
# Emacs looks for "Local variables:" after the last "newline-formfeed".
|
|||
|
|
|||
|
# Local Variables:
|
|||
|
# compile-command: "latexmk -interaction=nonstopmode -lualatex -pvc elisp-to-cl-lesson.tex"
|
|||
|
# eval: (org-eval-emacs-lisp-setup-blocks)
|
|||
|
# fill-column: 80
|
|||
|
# End:
|