JitterLisp

This is the manual for JitterLisp (for GNU Jitter version 0.9.294, last updated on 19 January 2023), an efficient Lisp system using a Jittery VM.

Copyright © 2017-2021 Luca Saiu
Updated in 2022 by Luca Saiu
Written by Luca Saiu.

Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with no Front-Cover texts and with the Back-Cover text being “You have freedom to copy and modify this manual, like GNU software.”.

A copy of the GNU Free Documentation License is distributed in electronic form along with the software in the file doc/COPYING.DOC, and available on the web at the URL https://www.gnu.org/licenses/fdl.html.

All the code examples contained in this manual are released into the public domain, up to the extent of the applicable law.

The author’s personal web site https://ageinghacker.net contains his contact information.

Table of Contents


1 Introduction

JitterLisp is a comparatively simple and efficient Lisp system, relying on a Jittery virtual machine for compiled code.


1.1 History

Jitter (see The GNU Jitter Manual) was borne out of an attempt to optimize the programming language GNU epsilon (see The GNU epsilon Manual), my main long-term project. Dissatisfied with the speedup provided by a simple direct-threaded virtual machine I started exploring more advanced techniques, and the experiment eventually grew into the current virtual machine generator. A sub-project, some might argue, that got out of hand.

Still, Jitter had seen no major application yet. It needed to be stressed to find bugs and embedded in a practical system for me to discover what was missing and fill the gaps. I wanted a real application. A real programming language. A Lisp.

And the sub-sub-project has now also gotten out of hand, again branching off in its own interesting direction.


1.2 Goals

JitterLisp lives somewhere near the boundary between the end of the domain of complex toys, and where simple real Lisp systems begin; as long as we keep seeing it as an example for Jitter either classification will do. As for concrete applications it can certainly be made practical: the Lisp library can be extended, and JitterLisp itself could become both embeddable and extensible in C without unreasonable effort. If in the future I bent the system in some of these directions, as I might well do, I guess the result would be useful; but writing yet another Lisp system was never my primary goal.

Even without the ambitious scope of Jitter I consider JitterLisp useful to show the level of performance within the reach of small, clean systems. JitterLisp gets many design decisions correct but is not perfect or exceptionally clever; still it appears more efficient than every mature “dynamic language” implementation, in some cases to a spectacular degree—due more to the faults of others than to any merit of mine.

I will not even try to hide my own personal goal for JitterLisp: being able to say that most language implementations are inexcusably bad, as in fact they are, with a real implementation to present as a comparison point and not just a proof of concept; and numbers.
And so here I stand, claiming my right to rub JitterLisp on everyone’s face. How good it feels.


1.3 Features and influences

Every time I had to choose whether to support a feature or to omit it for simplicity my decision relied on two criteria:

  • Can the feature be omitted while keeping the language realistic? JitterLisp should remain a Lisp, not more difficult to learn for a Lisper than another simple dialect nor too fundamentally different. JitterLisp must also be a reasonably practical system with the core features a programmer would expect, and not just an idealized prototype.
    It must be possible to compare the JitterLisp language with standard dialects, and find them similar in practice.
  • Is the feature critical for performance? If omitting the feature granted JitterLisp a performance advantage with respect to other Lisps at the cost of making JitterLisp very cumbersome to use, then not supporting it would make performance comparisons unfair. Where, on the other hand, a feature has no performance overhead (particularly when not in use), I can in good conscience leave it out and claim that an extended JitterLisp with the feature would still perform as well as the current system.
    The language must make comparisons between the JitterLisp implementation and other Lisp implementations possible, and fair.

In the end the relative weight associated to each of the two criteria can only be a judgment call and some grey areas will remain, no matter how I strive for fairness. In case some reader felt that I skewed the comparison to JitterLisp’s advantage by choosing to include or exclude some feature, I am open to discussion.

JitterLisp draws inspiration from both Common Lisp and Scheme, without being compatible with either. However a competent programmer should be able to write code running unchanged on top of JitterLisp, a Common Lisp system or a Scheme system, through a layer of simple macros.

Several design ideas originate from my own GNU epsilon.


1.3.1 Comparison with other Lisps

The JitterLisp language is first of all a Lisp dialect, and as such will feel familiar to any programmer already mastering any other Lisp.

JitterLisp is a lexically-scoped higher-order homoiconic1 Lisp/1 featuring closures, garbage collection, quasiquoting and Turing-complete macros.

Every syntactic form in JitterLisp is an expression, and nontrivial expressions will contain sub-expressions; JitterLisp has no “statements” as a distinct syntactic category, nor special forms which are only valid at the top level. Variables are mutable by default, and iteration and recursion are both supported. Tail calls incur no space overhead except in inter-calls between compiled and interpreted code.

Typing is dynamic. JitterLisp datatypes include the empty list object, symbols, characters, conses and procedures. Non-empty lists are cdr-nested conses. At the present time fixnums are the only numeric type.

Each procedure may be either interpreted or compiled, and procedures of different kinds are allowed to call each other with no restrictions and no difference in observable behavior apart from performance. Interpreted procedures can be compiled at run time without affecting their identity as objects.

[FIXME: quoting and quasiquoting are Lispy, not epsilonian]

[FIXME: compare with standard Lisp dialects]

[FIXME: Many of the superficial features of JitterLisp, particularly at the lexical level, come from Scheme]

[FIXME: define operating at the top level, like Common Lisp’s defun but differently from Scheme]


1.3.2 epsilonian features

JitterLisp is more “dynamic” and more reflective than typical Lisp dialects: any expression is allowed to define, remove or alter any non-constant global binding; the Abstract Syntax Tree returned by macroexpansion and contained within each closure is a recursive data structure describing an expression in a minimalistic core language which can be interpreted, compiled, analyzed, optimized or in general used like any other data structure, at run time, in the language itself.

Exposed ASTs and reflection make it possible and natural for the compiler to be implemented as an ordinary procedure, itself written in Lisp and running on top of the interpreter or compiled by itself at run time.

These distinctly epsilonian features, while important in making the internal implementation simpler and more elegant, are meant to be invisible to the casual user. In particular user macros operate on ordinary non-AST Lisp data structures, usually through Lisp quasiquoting, and will feel instantly familiar to any Common Lisp or Emacs Lisp programmer; the fact that the core Lisp forms are themselves predefined macros expanding to ASTs may be safely ignored in almost all programs as an implementation detail.

See Interaction example for a quick demo session introducing most of the non-traditional features likely to surprise a Lisp user.


1.3.3 Simplifications


1.3.3.1 Intentionally omitted features

[FIXME: no modules, packages or namespaces]

[FIXME: no values]

[FIXME: no continuations]

[FIXME: no exceptions]

[FIXME: no variadic procedures (but I do have variadic macros)]

[FIXME: no OO]


1.3.3.2 Desirable but not implemented features

[FIXME: no stack traces]

[FIXME: no type dispatching]

[FIXME: only a small library]


1.4 License

JitterLisp is free software, distributed under the GNU General Public License, version 3 or later. There is no warranty whatsoever.

This manual is free documentation, distributed under the GNU Free Documentation License, version 1.3 or later.


2 Using JitterLisp

JitterLisp is distributed along with GNU Jitter as an advanced example of its use, and must be compiled from the source code.

JitterLisp has no separate distribution or build system of its own, but is easy to build from the Jitter build directory. The build process follows the GNU conventions and supports cross-compilation.

Once compiled JitterLisp is quite friendly to use. It supports GNU-style command-line options and can work either interactively with a REPL, or by running Lisp code from text files.


2.1 Obtaining the software

JitterLisp is distributed as an example within Jitter, and can be obtained along with the Jitter source code. It is written in C and itself, and the virtual machine it relies on for compiled closures is generated by Jitter. The current web page for Jitter, containing a link to a publicly readable git repository, is on my web site at the URL https://www.gnu.org/software/jitter .

As of 2021 JitterLisp’s development takes place on the main git branch named master.

I welcome feedback. If you need to contact me or want to discuss about JitterLisp please see Contacting the author in The Jitter Manual.


2.2 Building JitterLisp

JitterLisp belongs to Jitter’s examples. The examples are not built by the default make target but are easy to generate by running

make examples

from the Jitter build directory, or by running the test suite as explained below.

The Jitter test suite includes many cases based on JitterLisp, as compiled JitterLisp procedures are a convenient and realistic way of stressing Jitter and testing its generated code. Running Jitter’s test suite with

make check

(see Running the test suite in The Jitter Manual) from its build directory is sufficient to build JitterLisp as a side effect, in all of the variants (see JitterLisp executables) supported by the Jitter configuration. I highly recommend running the test suite in any case before using Jitter.

See Working with the Jitter sources in The Jitter Manual for detailed building instructions. The build system follows the modern GNU style and in particular the common practices and suggestions from the GNU Coding Standards (see The GNU Coding Standards), which sets a few additional requirements such as GNU Autoconf and GNU Automake for “developers”—with the caveat that, at the present time before I release official stable tarballs, every user has to follow the developer instructions.

Even if the dependency is not mandatory I strongly recommend the Boehm-Demers garbage collector, packaged by every modern GNU/Linux distributions and also easy to install from sources. Other optional dependencies are the GNU Readline and GNU Libtextstyle libraries.
Both dependencies are automatically checked by Jitter’s configure script; if a library or its C headers are missing the corresponding functionality will be disabled, or some JitterLisp variants will not be built.

Jitter’s build system encourages a separate build directory and supports advanced features such as cross compilation, but is standard enough to just work with the usual command line

./bootstrap && ./configure && make

in most practical cases.


2.3 JitterLisp executables

According to the system characteristics as detected by Jitter’s configure script and to configure-time options supplied by the user the build system will compile different versions of JitterLisp, with the intent of testing and benchmarking Jitter.

These different compiled programs will exhibit different performance profiles, but still be equivalent in terms of Lisp functionality.

One JitterLisp executable is built for each combination of the following configuration items:

safety

a JitterLisp program may be safe and check for operand types before executing every operation, or unsafe and omit some checks. An unsafe Lisp will be more efficient, but subject to crashes in case of user error;

garbage collection

JitterLisp supports the Boehm-Demers garbage collector but can also allocate heap memory without ever freeing the space, either because the garbage collection library is not available or for performance testing. The two possible configuration values are called Boehm or litter, respectively.

dispatch

there are currently four dispatches supported by Jitter: switch-dispatching, direct threading, minimal threading and no threading (see Dispatches in The Jitter Manual), each with its own performance and portability profile. JitterLisp will run with any dispatch.

Unless some configuration choice has been disabled multiplying the number of choices for each of the three items gives a total of 16 combinations and therefore 16 different executables, recognizable by suffixes in their file name. The safety suffix may be empty or ‘--unsafe’, the garbage collection suffix ‘--boehm’ or empty, and the dispatch suffix one of ‘--switch’, ‘--direct-threading’, ‘--minimal-threading’ and ‘--no-threading’.
A good combination for general use will be safe-Boehm-no-threading, implemented in the file bin/jitterlisp--boehm--no-threading relative to Jitter’s build directory. A user measuring performance in a “lowest-overhead” context might want to try bin/jitterlisp--unsafe--no-threading, paying close attention to how much memory is used. The shell command

ls bin/jitterlisp*

will print the full list of available JitterLisp programs.

“Default” JitterLisp executables, with no dispatch suffix in their name are also built using the best dispatch available in the current configuration. In the example above bin/jitterlisp--boehm will have the same functionality as bin/jitterlisp--boehm--no-threading.

In the following examples I will arbitrarily refer to bin/jitterlisp--boehm as the executable to run. The executable name is easy to replace with another, in case that configuration is not available; every JitterLisp executable supports the same command-line interface. The current working directory is always irrelevant when invoking any JitterLisp executable without file arguments, which makes the bin/ component of the path name safe to freely adapt as well.


2.4 Installing JitterLisp

The Jitter build system does not support installing examples at the present time.

It would certainly be possible to directly use Libtool and install Jitter’s libraries and executables. However, as a simpler alternative, a user can configure Jitter to generate static libraries only by calling configure with the option --disable-shared, and then build the examples. Each JitterLisp executable will be one self-contained file with no external run-time dependencies, easy to copy by hand into some directory within PATH or to another convenient location.

If the Jitter libraries are statically linked and every dynamic library is correctly installed then the JitterLisp executables will not access any other file by default: the large Lisp initialization file and the GPL license text are in fact embedded as constants within the native compiled file image (see constant-strings).


2.5 Invoking JitterLisp

A user can either run JitterLisp interactively via a Read-Eval-Print Loop, or use it to execute Lisp programs from any number of text files.

An interactive run is the default: simply invoking a JitterLisp executable with no arguments, for example by typing

bin/jitterlisp--boehm

at the shell prompt will print a welcome banner and then wait for the user to type in Lisp forms; the system will evaluate one form, print its result unless the result is #<nothing>, and then go back to waiting for the next form.
User input supports the line-editing facilities of GNU Readline, as long as Jitter has been configured with Readline support. Lisp forms entered by the user are allowed to span multiple lines.
The user can exit by closing the input, typically by pressing C-d on an empty line on GNU and other Unix systems.

Any non-option argument on the command line is taken as the relative path name of one Lisp file to run; JitterLisp accepts an arbitrary number of file names on the command line, and executes them in the given order. JitterLisp will omit the banner and, by default, run non-interactively if it receives any file name, exiting without executing the REPL.
A single dash ‘-’ is a non-option argument and stands for the process standard input, from which the program will read Lisp forms. Like the path name for a Lisp file, a - argument will make JitterLisp default to non-interactive mode.

Lisp forms from input files or ‘-, in either interactive or non-interactive mode, will be evaluated without automatically printing each result, to avoid clutter in the output. Of course a Lisp program may still call I/O procedures (see Input and output) in order to print out information as desired, without such explicit output being suppressed.

A run-time error while executing a Lisp program given on the command line will cause JitterLisp to exit with failure, without processing any remaining Lisp file.

JitterLisp executables default to treating any command-line argument beginning with a dash (‘-’) as an option which affects the system behavior in one of the ways described in this section. The exceptions are arguments constituted by exactly a single dash, as explained above, or any arguments following an argument constituted by exactly two dashes (‘--’): those are interpreted as non-options.

Several JitterLisp options affect the software behavior in some way which can be negated by another option restoring the default. When both an option and its opposite are given, the last option on the command line prevails.

I call negative options the options whose only purpose is to negate the effect of other options, restoring the default behavior. Negative options are useful for canceling the effect of some option which is convenient to always pass and then occasionally override, for example as part of a shell alias or by quick interactive line editing of a complex shell command line.
The output of --help, described below, shows negative options marked as ‘(default)’.

JitterLisp supports the standard GNU command-line options:

--version

Print version information and legal notices for JitterLisp, then exit with success. Since JitterLisp is distributed along with Jitter and currently lacks a version number of its own, the output will actually reflect Jitter’s version.

-?, --help

Print a terse description of JitterLisp’s command-line interface including the options documented here, then exit with success.

--usage

Print a list of the options documented here with no explanation, then exit with success.

--

Stop option processing, and interpret any following argument in the command line as a non-option. This may be useful to execute Lisp files whose path name starts with a dash.

Checking JitterLisp’s version to obtain a version number and nothing more may be of some use in shell scripts:

--dump-version

Print JitterLisp’s version number and exit with success. The output is in a single line with no spaces, and does not contain JitterLisp’s name or any other information.

The following options affect JitterLisp’s interaction mode:

-q, --no-repl, --batch

Run in non-interactive mode, without a REPL, independently from non-option arguments.

--repl, --no-batch

Run in interactive mode even if non-option arguments are given; in this case start the REPL after executing the given Lisp files.

It may be useful to run JitterLisp for evaluating one or a few Lisp forms from the command line, possibly along with some Lisp program passed as a non-option argument:

-e sexprs, --eval=sexprs

Evaluate the given possibly empty sequence of forms, printing the final result (see Sequencing). Evaluation and printing takes place after running all the Lisp files from non-option arguments, and before executing the REPL (when in interactive mode).
This option does not by itself change JitterLisp’s mode to non-interactive.

Unfortunately the quoting rules for Bourne shells make Lisp quoting and quasiquoting (see Quoting and quasiquoting) somewhat inconvenient to use from the Unix command line:

bin/jitterlisp--boehm --no-repl --eval='(let ((a 42)) `(a is ,a))'
bin/jitterlisp--boehm --no-repl --eval="(let ((a 'b)) \`(a is ,a))"
bin/jitterlisp--boehm --no-repl --eval='(let ((a '\''b)) `(a is ,a))'
bin/jitterlisp--boehm --no-repl --eval=\'the-result-is-a-symbol

At least in simple cases a user should be able to work around such problems by defining a macro in a Lisp file, to be used in the --eval argument without requiring complex quoting in the command line. An alternative is avoiding the quoting prefix notation (see prefix-notation) altogether, writing for example (quote b) instead of 'b within single-quoted shell text.

Some options affect the way JitterLisp prints out information and let users deviate from the default behavior.

--no-omit-nothing

Print back #<nothing> when it occurs as the result of a user form (see Uniques), in interactive use or with --eval. The default is to omit it, as #<nothing> represents an “uninteresting” result from a form which is normally evaluated for its side effects.

--colorize

Print Lisp data using color and font variations to make different object types easier to distinguish at a glance. The current implementation, quite crude, relies on ANSI terminal escape sequences and uses a choice of colors suitable for a black or dark background.

The default is to always use the default terminal color and font, without emitting escape sequences.

-c, --compact-uninterned

Print uninterned symbols (see uninterned-symbols) in an alternative, more compact notation using a per-symbol index rather than a memory address. The index may theoretically wrap around after allocating a very high number of uninterned symbols.

--cross-disassembler

Use the cross-disassembler specified at Jitter configuration time (see Disassembly in The Jitter Manual) rather than the default native disassembler to print out native code for compiled closures (see disassembly).

The cross-disassembler is useful for running JitterLisp on an emulator for a foreign hardware architecture, using an objdump executable from cross-configured GNU binutils having a name different from the objdump program running on the host machine.

The default, reasonable for running JitterLisp without command-line options on the host architecture it was configured for, is using the native disassembler.

--free-routines

Normally, when compiling a closure (see Compilation) the JitterLisp system keeps two versions of the resulting VM routine in memory: one non-executable routine, and one executable routine. The executable routine is necessary for executing the closure code, and therefore will always remain in memory until garbage-collected—or for ever, in the case of littering JitterLisp programs. The non-executable routine, on the other hand, can be destroyed immediately at compilation time to save memory; its only purpose after compilation is debugging, using disassemble and disassemble-vm (see Compilation and see Disassembly in The Jitter Manual).

By default JitterLisp keeps non-executable routines in memory, and garbage-collects them along with executable routines. By specifying this option only executable routines are kept, which saves memory at the cost of making debugging output less friendly if not, according to the dispatch, completely useless.

--no-verbose-litter

A JitterLisp executable littering memory (see litter) will by default print a message to the standard error every time a new block of memory is allocated. Such warnings, although distracting, are useful to remind the user that the running program is allocating memory from a growing heap which will not be freed, and are therefore enabled by default.
Non-littering executables will accept and silently ignore verbose littering options.

This option suppresses litter-allocation messages.

--time[=time]

When this option is given with time unspecified or different from ‘no’ the REPL times interactive commands and prints a human-readable message specifying the elapsed time in seconds after each evaluation. Valid values for time are ‘no’, ‘yes’ and ‘verbose’. A time value of ‘verbose’ causes the timed command to be printed back along with the time, which is convenient when interactively running multiple commands on the same line, to be executed in sequence and timed independently.

The default is not printing timings, as with ‘no’, when the option is not given. Giving the option without specifying time is equivalent to setting time to ‘yes’.

Only commands executed by the REPL are timed: this excludes library initialization, code from Lisp files specified in the command line, and arguments from the --eval option.

--omit-nothing
--no-colorize
--no-compact-uninterned
--no-cross-disassembler
--no-free-routines
--verbose-litter
--no-time

These are the negative counterparts of the options above, restoring the respective default behaviors.

The last group of options is mostly meant for the development of JitterLisp itself with respect to debugging or benchmarking.

-v, --verbose

Print out debugging information about JitterLisp’s execution progress, including the full macroexpansion and evaluation of each library form executed at startup. The default it not to print such information.

This option may occasionally be useful to discover the approximate origin of a problem, such as which of several provided Lisp files caused an error.

--no-optimization-rewriting

Disable VM instruction rewriting (see Instruction rewriting in The Jitter Manual). This is only useful for debugging rewrite rules or benchmarking their effect, or theoretically for making compilation more efficient at the price of slower execution of compiled code.

Instruction rewriting is enabled by default.

--no-library

Do not run the default Lisp library at initialization, exposing only the language and library features implemented in C.

The predefined Lisp globals available when using this option will be limited to the ones marked as “core macro”, and “primitive wrapper” in the reference documentation (see Lisp reference), plus the associated primitives; in particular there will be no support for high-level macro definitions (see high-level-macros), AST optimization (see AST optimization) or compilation (see Compilation). A few globals documented as “macro” have actually two separate implementations, the first in C as a “core macro”, to be used at initialization for bootstrapping the system, and then a second, cleaner or more powerful redefinition as an ordinary “macro”, in Lisp. The option --no-library exposes the core macro implementation in this case, which may differ in subtle ways from the normally available definition, and in fact is allowed to contradict the specification in this manual.

This option is meant for debugging JitterLisp itself.

--no-verbose
--optimization-rewriting
--library

The negative versions of the options above.


2.6 Interaction example

This section will present the main features setting JitterLisp apart from other Lisp systems to readers already familiar with other Lisp dialects.

I believe that a sufficiently motivated beginner should be able to follow this section, possibly after first understanding the fundamentals of Lisp from the beginning of the reference part of this document (see Lisp reference). I am particularly interested in feedback from readers who cannot program yet or are only weak programmers but are making a serious attempt to learn by following this route; any beginner with useful feedback to provide, positive or negative, is welcome to contact me (see Contacting the author in The Jitter Manual).

??????????? [FIXME: Write this] [FIXME: it’s a stack machine]

[FIXME: + is a macro]

[FIXME: look at an interpreted closure]

[FIXME: macroexpand]

[FIXME: do some simple computation over ASTs]

[FIXME: optimize explicitly]

[FIXME: optimize retroactively]

[FIXME: These are just ideas. The variable x is unbound.]

(append '(a b c) '(1 (2 3) 4))
  ⇒ (a b c 1 (2 3) 4)


x
  error→ unbound variable x
'x
  ⇒ x
(macroexpand x)
  error→ unbound variable x

(macroexpand 'x)
  ⇒ [variable x]
(macroexpand ''x)
  ⇒ [literal x]
(macroexpand '''x)
  ⇒ [literal (quote x)]
(macroexpand ''''x)
  ⇒ [literal (quote (quote x))]

(define (twice n)
  (* n 2))
(twice 10)
  ⇒ 20
twice
  ⇒
  #<interpreted-closure () (#<u680>)
    [primitive #<primitive 2* 1-ary> [variable #<u680>]]>

(define-macro (when-zero discriminand . body)
  `(when (zero? ,discriminand) ,@body))

(macroexpand '(when-zero a b c))
  ⇒
  [if [call [variable zero?] [variable a]]
        [sequence [variable b]
                  [variable c]]
        [literal #<nothing>]]
(when-zero 42 (display 'foo) (newline))
  →
  [if [call [variable zero?] [literal 42]]
      [sequence [call [variable display] [literal foo]]
                [call [variable newline]]]
      [literal #<nothing>]]
  ⇒
  #<nothing>

;; For people familiar with Common Lisp:
(define-macro (defun operator formals . body-forms)
  `(define (,operator ,@formals)
     ,@body-forms))
(define-macro (defmacro operator formals . body-forms)
  `(define-macro (,operator . ,formals) ;; FIXME: why not (,operator ,@formals) ?
     ,@body-forms))
;; And then:
(defun successor (n)
  (+ n 1))
(defmacro when-zero (discriminand . body)
  `(when (zero? ,discriminand)
     ,@body))

(define my-even?
  (letrec ((my-even? (lambda (n)
                       (cond ((= n 0)  #t)
                             ((= n 1)  #f)
                             (else     (my-odd? (- n 1))))))
           (my-odd?  (lambda (n)
                       (cond ((= n 0)  #f)
                             ((= n 1)  #t)
                             (else     (my-even? (- n 1)))))))
    my-even?))
(lambda () (letrec ((f 1) (g 2)) f))
  ⇒
  #<interpreted-closure () ()
    [let #<u702> [literal #<undefined>]
      [let #<u703> [literal #<undefined>]
        [sequence [set! #<u702> [literal 1]]
                  [sequence [set! #<u703> [literal 2]]
                            [variable #<u702>]]]]]>
(macroexpand '(begin))
  ⇒ [literal #<nothing>]
(macroexpand '(begin (newline)))
  ⇒ [call [variable newline]]
(macroexpand '(newline))
  ⇒ [call [variable newline]]
(macroexpand '(begin (display 42) (newline)))
  ⇒
  [sequence [call [variable display] [literal 42]]
            [call [variable newline]]]
(macroexpand '(begin (display 42) (newline) (newline)))
  ⇒
  [sequence [call [variable display] [literal 42]]
            [sequence [call [variable newline]]
                      [call [variable newline]]]]


3 Lisp reference

[FIXME: Fill this.]

[FIXME: we will first define Lisp data structures, then show how some of them are mapped into expressions] [FIXME: see homoiconicity-footnote]


3.1 Lisp data

[FIXME: boxed and unboxed objects]

All Lisp data have an output notation, and some can be read from input via a read syntax as well. The two syntaxes, where both defined, are compatible.
For example the number 1234, the boolean #t and the symbol this-is-a-symbol are written back by the system (when they occur as results in the REPL, or in output) as 1234, #t and this-is-a-symbol, in the same syntax available to the user for data input. However some other objects such as procedures can be described in the output (for example ‘#<interpreted-closure () (x-1 x-0) [primitive #<primitive cons 2-ary> [variable x-1] [variable x-0]]>’), but have no corresponding read syntax to be directly input as immediates. Any object can still be built programmatically.

[FIXME: Out of convenience the written notation of an object does not necessarily convey all of the information about it; for example, the memory address where an object is stored does not appear, yet is important in the execution semantics in order to check whether two objects are the same. The output notation does express the presence of substructure sharing within a single Lisp object, currently without showing more detail: an ellipsis (‘...’) within a printed object stands for another boxed Lisp sub-object, it is not specified which, that has been already printed at least once as part of object being shown. This does not distinguish between Direct-Acyclic-Graph structures, where two pointers simply converge to the same address, and pointer cycles making circular or “infinite” structures. Shared structures have no read syntax but can be built programmatically.]

It is worth to stress how the language defined in the rest of this section describes Lisp data, and not necessarily valid Lisp expressions. For example foo denotes a symbol object, but simply typing foo at the first REPL prompt will cause an unbound-variable error—in such a case the symbol object foo will be macroexpanded into a variable named ‘foo’, even when no such variable exists. In the same way the read (and write) syntax (1 2 3) denotes a well-formed Lisp datum, but would macroexpand to a procedure call with 1 as the operator, immediately causing a failure on evaluation. See Quoting and quasiquoting for information about the quoting syntax for building expressions evaluating to literal Lisp objects.

[FIXME: Fill this.]

Every Lisp datum contains a value and a type. Given a datum it is always possible to check for its type at run time by means of some predefined procedure (see type-checking-procedures).

Since JitterLisp has, at least currently, no subtyping relation, each object belongs to exactly one of the following types. However some predefined procedures are provided to check whether an object belongs to one of several related data types; for example closure? will recognize either an interpreted closure or a compiled closure as a “closure”.

[FIXME: any two object are comparable by identity: see eq?.]

[FIXME: talk about macroexpansion]


3.1.1 Uniques

[FIXME: Fill this.] [FIXME: immutability]

A unique object important in practice is the empty list:

unique: ()

In read syntax it is acceptable to separate the open and closed parentheses with arbitrary whitespace or comments, in which last case the closed parentheses will be found on a separate line.

There exists exactly one empty list object, written as (). It is used, in particular, as the trailing part of the of every list spine: see lists.

A Boolean or canonical Boolean object represents a truth value, either false or true. There are exactly two Booleans:

unique: #f
unique: #t

The false object is written as #f; the true object is written as #t.

In practical use #f tends to be more important than #t, as JitterLisp conditionals and loops distinguish between the #f value and any other Lisp object which is taken as “true” for the purposes of tests. A Lisp object used as a Boolean in this extended sense is called a generalized Boolean. Of course generalized Booleans are not necessarily unique objects.

[FIXME: a few words about the following uniques]

unique, no read syntax: #<nothing>
unique, no read syntax: #<eof>
unique, no read syntax: #<undefined>

[FIXME: what they are used for]


3.1.2 Fixnums


3.1.3 Characters


3.1.4 Symbols


3.1.5 Conses

A cons is a pair object containing exactly two other Lisp objects, allowed to be conses in their turn or objects of any other types. Conses are mutable, in the sense that the two components of a cons can be changed at run time while keeping the identity of the cons object.

The first object contained in a cons is called its car, the second is called its cdr.

Syntax: If a and b are two Lisp objects, then (a . b) is a cons containing a as its car and b as its cdr.

Let S be any nonempty sequence of the characters “a” and “d”. Then the caSr of a cons is the car of the cSr of the cons, and its cdSr is the cdr of its cSr. For example the caddr of an object is the car of the cdr of its cdr. Such composed cons selector names are convenient to use when referring to nested components of a cons, and the JitterLisp library provides functionality to access them with any S of length 1, 2 and 3—therefore, for a level of nesting up to 4.

A list is either the empty list object or a cons whose cdr is another list. For example (), (#t . ()), (#f . (3 . ())) and ((1 . 2) . ()) are lists; (#t . #t) is not a list.

A sublist of a nonempty list is either its cdr or a sublist of its cdr. The elements of a nonempty list are the car of the list and the elements of its sublists. The empty list has no sublists and no elements.
For example the list (foo . (10 . (bar . ()))) has the three sublists (10 . (bar . ())), (bar . ()) and (), and the three elements foo, 10 and bar. Given a nonempty list its first element and its first sublist can always be found by taking, respectively, the car and the cdr of the list. Taking the car and cdr of the first sublist yields the second element and the second sublist of the original list, and so on.
It is easy to see how some composed cons selectors applied to a list identify specific sublists and elements: the list sublists will be its cdr, cddr, cdddr, cddddr, and so on; then taking the car of the list itself and its sublists yields the list elements: therefore the first, second, third and fourth element of a list will be respectively its car, cadr, caddr and cadddr.

The following notation makes lists much easier to write, as it suppresses every dot on the list spine and many parentheses; it can be used on many non-list conses as well:

Compact cons notation: Let a and b be two objects. When b is either the empty list () or a cons, the cons (a . b) can be written in the compact cons notation: in compact notation both the dot and the parentheses around b are omitted.

JitterLisp always adopts the compact cons notation in output, whenever applicable. Its use in input is optional.

For example (7) is a compact way of writing (7 . ()), (1 2) is a compact way of writing (1 . (2 . ())), ((a)) is a compact way of writing ((a . ()) . ()), and (10 a . #t) is a compact way of writing the non-list (10 . (a . #t)).

A further syntactic convenience is available for particular conses, which are also lists:

Prefix notation: Let q be the read syntax for a Lisp object. Then:

  • 'q is an alternative read syntax for (quote q);
  • `q is an alternative read syntax for (quasiquote q);
  • ,q is an alternative read syntax for (unquote q); and
  • ,@q is an alternative read syntax for (unquote-splicing q).

The prefix notation is recognized by the reader, but currently not emitted in output. Its use in input, again, is optional.

The prefix notation serves to express quoting and quasiquoting in a more convenient way: see Quoting and quasiquoting.

It is worth stressing that lists are not special at the language level and do not constitute a separate type: the name “list” is just a characterization of the shape of some objects, common in practice, which include the empty list object and some conses.


3.1.6 Boxes

Boxes have no read syntax.


3.1.7 Interpreted closures


3.1.8 Compiled closures

[FIXME: An interpreted closure can destructively change to a compiled closure (but not vice-versa) without changing its identity.]


3.1.9 Primitives


3.1.10 Abstract Syntax Trees

An Abstract Syntax Tree or AST is a recursive type representing code as a data structure. ASTs only support the core language forms which remain after macroexpanding away (see macroexpansion, see Macroexpanding) every macro use: Lisp code rewritten not to use any macro turns into an AST, which can be then interpreted or compiled, or even analyzed and transformed by other Lisp code.

JitterLisp in fact contains no executor for Lisp: there are only an AST interpreter, written in C, and an AST compiler written in Lisp and translating ASTs into code for the Jittery VM. It is thanks to the reflection capabilities provided by the AST data type that it is possible to write a self-compiler, and also a self-optimizer, in the language itself.

Even if ASTs have no read syntax the JitterLisp library provides facilities to conveniently inspect and build ASTs: see AST operations.

See Abstract Syntax Trees and their semantics for a complete description of the structure of Abstract Syntax Trees and their behavior.


3.1.11 Macros


3.1.12 Primitive macros


3.2 Abstract Syntax Trees and their semantics

JitterLisp code is executed by first translating user syntax into Abstract Syntax Trees, which are easy to interpret or compile. Far from being only an implementation device, JitterLisp ASTs are also exposed to the user who may execute them (see Code execution) and also work on them just as on any other Lisp data structure.

There is no read syntax allowing the user to directly enter an AST as a literal datum; however the library includes sufficient functionality to inspect or build ASTs (see AST operations). Such functionality is used extensively in macroexpansion and compilation.


3.2.1 AST syntax

ASTs define a minimalistic programming language, operating on Lisp data but otherwise not particularly similar to Lisp. The syntax given here is the output syntax used by the implementation for printing AST data, for example as the result of macroexpand (see Macroexpanding). This syntax is designed to be simple and unambiguous. In order to visually stand apart from user syntax it indicates grouping by brackets (‘[’, ‘]’) rather than parentheses.

A dot (‘.’) in the syntax below indicates the convention of writing a sequence of zero or more entities, represented in memory as a list, without the surrounding parentheses. For example a valid primitive following the syntax ‘[primitive operator . operands]’ will be [primitive + [literal 2] [variable x]], where [literal 2] and [variable x] are ASTs encoded as the two elements of a list within the AST for the primitive.

Every AST represents an expression. Each expression has exactly one of the following forms, named after the identifier immediately following the open bracket:

  • [literal value], a literal.

    The literal value may be any Lisp datum.

  • [variable name], a variable or variable reference.

    The variable name must be a symbol.

  • [primitive operator . operands], a primitive or primitive use.

    The primitive operator or primitive name must be a symbol which is the name of a primitive. The primitive operands must be a list of ASTs, having exactly as many elements as the arguments expected by the named primitive.

  • [let name bound-expression body], a let or block.

    The name or bound variable name in a block must be a symbol. A block bound-expression must be an AST. A block body must also be an AST.

  • [define name expression], a definition or global definition.

    The global definition name must be a symbol. The definition expression or defined expression must be an AST.

  • [set! name expression], an assignment.

    The assignment variable name must be a symbol. The assignment expression must be an AST.

  • [sequence expression-0 expression-1], a sequence.

    Each of the first expression or expression-0 and the second expression or expression-1 in a sequence must be an AST.

  • [if condition then-branch else-branch], a conditional.

    The conditional condition and its two branches must all be ASTs.

  • [while guard body], a while or loop.

    Each of the loop guard and body must be an AST.

  • [lambda formals body], a lambda or abstraction.

    The lambda or abstraction formals or formal arguments must be a list of symbols, all different from one another. The lambda body must be an AST.

  • [call operator . operands], a call or procedure call.

    The call (or procedure call) operator must be an AST. The call operands must be a list of ASTs.

The forms above specify the entirety of the AST syntax: every JitterLisp form, before being executed, is translated into an AST having one of the shapes described above, which in many cases will contain other ASTs; all the language power and complexity arise solely from the combination of those AST forms.
Conspicuously absent from the forms above is any form of syntactic abstraction such as macros: macros serve to automatically rewrite complex user syntax into a combination of simple ASTs, so that no use of a macro survives the expansion process.

It is important to notice that the syntactic constraints specified above, for example [if ] having exactly three subforms and the first component of [set! ] being a symbol, are verified at AST construction time: it is impossible to build a syntactically incorrect AST without immediately failing. In this sense only syntactically correct ASTs may be said to “exist”.


3.2.2 AST semantics

JitterLisp ASTs constitute an eager imperative call-by-value language based on named, lexically scoped variables and global state.

What follows is an attempt of describing the behavior of AST evaluation in a precise way without the burden of complicated notation.

The mathematically inclined reader will see immediately how the AST semantics would be easy to model in an operational style, with logic rules having multiple premises and one conclusion. Here I will content myself with rendering such rules into more accessible English.


3.2.2.1 Semantics preliminaries

JitterLisp, like most modern Lisp dialects with the (now partial) exception of Emacs Lisp, is lexically scoped. This means that a variable use refers to the innermost variable binding with the same name which syntactically contains the variable AST as a sub-AST. This reference may be different from the most recent binding in time still in force for the same variable—which is used in “dynamically scoped” languages, but not in JitterLisp.

An environment is a mapping from each variable name to its associated value. During the evaluation of an AST some environment is always implicitly in force, which dictates the current value of every variable.

Variable scopes can be nested, adding bindings to the local environment which are visible by variable references until the scope ends. An inner block or call may shadow a variable binding, by re-binding a variable with the same name to a possibly new value and thus making the original variable temporarily inaccessible, as long as the new environment re-binding the same variable is in force.

Some variables may be globally bound: the global binding for a variable records the default value of a variable to be used when a variable is not bound in the currently active environment.


3.2.2.2 Failure

JitterLisp treats failure or errors in a trivial way.

If evaluation fails at any point out of the REPL, then the error is fatal: the execution ends, and the JitterLisp program exits reporting failure.

If evaluation fails when evaluating a form entered at the REPL, then the error is equally unrecoverable, and the AST evaluation terminates reporting failure; however the REPL is not exited, and the user has the opportunity to try and execute other forms in a state altered by the changes which were executed with success before the error, starting again in an empty local environment.

JitterLisp currently provides no exception system, either at the AST level or as part of higher layers.
The error primitive (see Erroring out) provides a way to explicitly fail under program control, but it is not possible to recover from such a failure with any form similar to “catch” or “try” as they exist in other Lisp dialects or in other languages.


3.2.2.3 AST semantics in detail

An AST is evaluated in a given environment, which is empty at the top level.

Evaluating an AST will either fail or yield exactly one value, allowed to have any JitterLisp data type (see Lisp data), as the result. [FIXME: non-termination]

In the following, particularly within examples, I will use the following notation:


Evaluation notation: form, environmentresult [-| output]
form, environment error→

To mean, respectively, that a given form evaluated with a given environment either returns a result (possibly with an output side effect), or fails. An environment, as stated already, is a mapping from variables to values.

[FIXME: Update the evaluation notation and the description below: a form should return a new environment as the result, and possibly a new global environment as well.] [FIXME: distinguish environment extension from environment update. This is important for let versus set!, below.]

ASTs must be finite in size and non-circular2; however different parts of the same AST are allowed to share structure, as long as AST objects in memory form a DAG and not a cyclic graph.

  • [literal value]

    Evaluating a literal AST yields its value as the result, as contained within the AST and independently from its type, without any further processing, independently from the environment. The evaluation of a literal AST always terminates and never fails.

    • Examples: [literal #t], {} ⇒ #t
    • [literal (a . 42)], {} ⇒ (a . 42)
    • [literal a], {} ⇒ a
    • [literal a], {a = 42} ⇒ a
    • [literal [literal a]], {} ⇒ [literal a]
  • [variable name]

    If the named variable is bound in the current environment, then the result is its value in the environment; otherwise, if the variable is not bound in the environment but is bound globally, the result is its global value. If the variable is unbound both in the environment and globally then the form evaluation fails.

    • Examples: [variable a], {} ⇒ 4 if a is globally bound to 4
    • [variable a], {} error→ if a is globally unbound
    • [variable a], {a = 7} ⇒ 7 independently from global bindings
  • [primitive operator . operands]

    Evaluating a primitive begins by evaluating the operands, in a strict left-to-right order, to obtain the operand values as results. Then, unless evaluation has failed already on the operands, the result is the result of applying the primitive to the operand values, according to the primitive behavior. This primitive application may fail, for example if the operand values do not have the expected types, in which case the evaluation of the entire AST fails.

    • Examples: [primitive 1+ [literal 42]], {} ⇒ 43
    • [primitive negate [variable a]], {a = 7} ⇒ -7
    • [primitive 1+ [variable a]], {} error→ if a is globally unbound or bound to a non-number
    • [primitive 1+ [literal a]], {} error→
    • [primitive display [literal 42]], {} ⇒ #<nothing> -| 42
  • [let name bound-expression body]

    A block serves to introduce a local variable, whose binding is visible within the body.

    Evaluating a block starts with the evaluation of the bound expression in the current environment, whose result is the bound value. Then the block body is evaluated in an environment extended by binding the bound variable to the bound value: the result of this evaluation of the body is the result of evaluation of the block.

    If evaluating either the bound expression or the body fails, then the evaluation of the entire block fails.

    • Examples: [let a [literal 42] [primitive 1+ [variable a]]], {} ⇒ 43
    • [let a [primitive 1+ [literal 42]] [primitive negate [variable a]]], {} ⇒ -43
    • [let a [literal 42] [let a [literal #t] [variable a]]], {} ⇒ #t
  • [define name expression]

    A global definition serves to alter the global binding of a variable.

    In order to evaluate a global definition we first evaluate its expression in the current environment, obtaining the defined value. Then we alter the global binding for the named variable, setting it to the defined value. Only the global binding is ever altered by define, even if the variable happens to be bound locally at the time of the definition.

    A global definition fails if either the expression fails or the named variable has already been made constant (see constants).

    The result of a global definition AST is #<nothing>.

  • [set! name expression]

    An assignment destructively modifies the given environment, replacing the current value for the name variable with another. This altered binding is visible in the following code using the same environment.

    An assignment evaluation starts by evaluating the expression in the given environment, yielding the assigned value as result. Then, unless the expression evaluation failed, we alter a binding. If the named variable is locally bound, then the local environment is modified by changing the binding for the named variable to the assigned value; otherwise, if the named variable is globally bound, we set its global binding to the assigned value.

    Evaluating an assignment AST fails when evaluating the expression fails, or because the variable is unbound (both locally and globally) at the time of the assignment, or because the variable is locally unbound and globally bound as a constant.

    The result of an assignment AST is #<nothing>.

  • [sequence expression-0 expression-1]

    A sequence serves to evaluate two ASTs one after the other. The fact that the sub-ASTs of a sequence are exactly two is not a limitation, since by nesting sequences one inside the other, it is possible to evaluate any number of ASTs.

    Evaluation starts by evaluating the first expression in the given environment, ignoring its result. Then, in the same environment, the second expression is evaluated; its result will be the result of the sequence AST evaluation.

    Evaluating a sequence fails if evaluating either the first or the second expression fails.

  • [if condition then-branch else-branch]

    [FIXME: write]

  • [while guard body]

    [FIXME: write]

  • [lambda formals body]

    [FIXME: write]

  • [call operator . operands]

    [FIXME: write]


3.2.2.4 From ASTs to JitterLisp

At this point, in principle, it would be possible to describe how each individual JitterLisp form expands into ASTs.

Except in interesting or particularly subtle cases, I will avoid such pedantry.

In fact one of the justifications for having a core-based language like JitterLisp (and epsilon) is that once the core and the expansion mechanisms are specified in a sufficiently formal way, every language extension automatically inherits the mathematical precision of its definition; the source code of each macro can be, I argue, a rigorous enough specification.

Not more than a handful of macros called core macros are defined in C, and even those should present no particular difficulty. For example begin (see Sequencing) is a core macro, defined in jitterlisp-macros.c:

/* Return the expansion of the given list of forms as a sequence.
   This is what the primitive macro begin does and the same code is
   used whenever a form sequence is allowed, for example in a lambda
   body. */
static jitterlisp_object
jitterlisp_macroexpand_begin (jitterlisp_object forms,
                              jitterlisp_object env)
{
  /* Zero-form body. */
  if (JITTERLISP_IS_EMPTY_LIST (forms))
    return jitterlisp_ast_make_literal (JITTERLISP_NOTHING);

  /* Ill-formed body. */
  if (! JITTERLISP_IS_CONS (forms))
    jitterlisp_error_cloned ("begin: non-list body");

  /* At this point we have to look inside the cons. */
  jitterlisp_object first = JITTERLISP_EXP_C_A_CAR(forms);
  jitterlisp_object rest = JITTERLISP_EXP_C_A_CDR(forms);

  /* One-form body. */
  if (JITTERLISP_IS_EMPTY_LIST (rest))
    return jitterlisp_macroexpand (first, env);

  /* Multiple-form body. */
  return jitterlisp_ast_make_sequence
            (jitterlisp_macroexpand (first, env),
             jitterlisp_macroexpand_begin (rest, env));
}

The code shows that the sub-forms within a begin form are recursively expanded in right-nested sequence ASTs, and that there are special cases for a begin with zero or one sub-forms. It is easy to check that this is indeed the case, by observing the ASTs generated by macroexpand from begin forms at the REPL:

(macroexpand '(begin))
  ⇒
  [literal #<nothing>]
(macroexpand '(begin 42))
  ⇒
  [literal 42]
(macroexpand '(begin (display 42)))
  ⇒
  [call [variable display] [literal 42]]
(macroexpand '(begin (display 42) (newline)))
  ⇒
  [sequence
    [call [variable display] [literal 42]]
    [call [variable newline]]]
(macroexpand '(begin (display 42) (newline) (display 43) (newline)))
  ⇒
  [sequence
    [call [variable display] [literal 42]]
    [sequence
      [call [variable newline]]
      [sequence
        [call [variable display] [literal 43]]
        [call [variable newline]]]]]

The description above should be sufficient for a motivated reader, along with macroexpand and the informal description in the following, to follow the details of every macro definition in the implementation.


3.3 Mapping Lisp data to expressions

[FIXME: Fill this.] [FIXME: ASTs are validated at construction time: the implementation never needs to worry about whether an already existing AST is syntactically well-formed; this makes interpretation more efficient and other things simpler.]


3.4 Program structure

[FIXME: Write this]


3.4.1 Expressions

[FIXME: Fill this.]


3.4.2 Definitions

[FIXME: Fill this.]


3.4.3 Constants

[FIXME: primitive wrappers are global constants]


3.5 Lisp language forms

[FIXME: Write this.]

The distinction between language and library is not clear-cut in a system like JitterLisp with strong syntactic abstraction, where almost all of the language is implemented in itself. Here I present the linguistic features which are “fundamental” in a conceptual way, independently from the way they are implemented.

Users interested in distinguishing which individual feature is implemented in C rather than as a Lisp macro may check the tag (“macro” or “core macro”, and only occasionally in this chapter “primitive wrapper”) in the documentation of each item and then follow the sources, with the caveat that a few (important) forms are implemented more than once: first by a temporary definition in C, in order to run the library and initialize the global state; then, from the library itself, the feature is re-defined in Lisp in a more general or powerful way.

[FIXME: Write this]

[FIXME: lexical scoping]

[FIXME: left-to-right]

[FIXME: case sensitive]

[FIXME: metasyntactic conventions: the dot]

[FIXME: erroring out]

[FIXME: Write something]

[FIXME: There is a lot of redundancy, by design. It is desirable to use the most specific form available, for readability and occasionally also for efficiency’s sake.]

[FIXME: dot notation, again, like in AST syntax: here, however, we are speaking of user syntax with Lisp conses.]


3.5.1 Definition forms

macro: define defined-thing . forms
macro: define-constant defined-thing . forms
macro: define-non-optimized defined-thing . forms
macro: define-constant-non-optimized defined-thing . forms

[FIXME: Write this]

macro: define-macro (name . args-pattern) . forms

There is currently no syntactic support for local macros, such as Common Lisp’s macrolet.


3.5.2 Sequencing

[FIXME: sequencing is nice]

[FIXME: “implicit progn” or “implicit begin”: I do it in more cases than Common Lisp and Scheme]

macro: begin . forms

[FIXME: Write this]

macro: begin-from-first index . forms

[FIXME: Write this]

macro: begin1 form-1 . other-forms
macro: begin2 form-1 form-2 . other-forms
macro: begin3 form-1 form-2 form-3 . other-forms
macro: begin4 form-1 form-2 form-3 form-4 . other-forms

[FIXME: Write this]

macro: begin-from-last index . forms

[FIXME: Write this]

macro: begin-1 form-1 . other-forms
macro: begin-2 form-1 form-2 . other-forms
macro: begin-3 form-1 form-2 form-3 . other-forms
macro: begin-4 form-1 form-2 form-3 form-4 . other-forms

[FIXME: Write this]

begin-1 is only provided for symmetry, and is functionally equivalent to begin with the restriction of requiring at least one form.


3.5.3 Conditionals

A conditional form consists in evaluating some condition form and then decide which form to evaluate next according to the result of the condition.

core macro: if condition then-form . else-forms

[FIXME: Write this]

macro: when condition . forms

[FIXME: Write this]

macro: unless condition . forms

[FIXME: Write this]

core macro: cond . clauses

[FIXME: Write this]

macro: case discriminand . clauses

[FIXME: Write this]


3.5.4 Procedures

core macro: lambda formals . forms

3.5.5 Blocks

A block introduces a scope where some variables are locally bound.

Following a lexical scoping discipline a variable binding is visible in the syntactic region where it has been bound and in every callee, as long as it is not shadowed by an inner binding for the same variable.

macro: let bindings . forms
core macro: let* bindings . forms
macro: letrec bindings . forms
macro: let name bindings . forms

[FIXME: Write this]

The fourth variant, with name being a symbol, is listed here out of completeness but belongs with looping forms; see named let for its description.

macro: destructuring-bind pattern structure . forms

[FIXME: Write this. Common Lisp calls “template” what I call “pattern”.]


3.5.6 Assignment

An assignment operation destructively modifies an existing variable binding.

core macro: set! x . forms

Evaluate the forms forms left-to-right, then assign the result of the last one (or #<nothing> if no forms were given) to the innermost binding for the variable x.

Error out if x is not a symbol, if x is unbound, or if the binding for x is global and constant.

[FIXME: speak about constants]

Rationale: [FIXME: efficiency of compiled code]

primitive wrapper: make-constant! e

[FIXME: write. e must evaluate to a symbol, which is allowed to be also lexically bound; however only its global binding is affected. If called on a globally unbound symbol, it becomes impossible to ever assign or define it.]


3.5.7 Loops

A loop form consists in executing the same body forms potentially multiple times, in sequence.

core macro: while guard . forms

[FIXME: Write this]

macro: do clauses (guard . result-forms) . body-forms

[FIXME: Write this]

macro: dotimes (x count-form . result-forms) . body-forms
macro: dotimesdown (x count-form . result-forms) . body-forms

[FIXME: Write this]

macro: dolist (x list-form . result-forms) . body-forms

[FIXME: Write this]

macro: named-let name bindings . forms
macro: let name bindings . forms

[FIXME: Write this]

The other, simpler, syntax for let is described among block forms: see let.


3.5.8 Making macros

core macro: low-level-macro . forms

[FIXME: Write this. Speak of low-level-macro-args]

macro: macro (macro-name . pattern) . forms

[FIXME: Write this. This uses destructuring bind see destructuring-bind]

The macros above would suffice, along with define, for globally binding macros. However define-macro is provided as a more convenient syntax: see define-macro.


3.5.9 Quoting and quasiquoting

core macro: quote thing
macro: quasiquote template

[FIXME: link prefixes. Remind that ’ is usable]


3.6 Lisp library

[FIXME: Write this]

[FIXME: Every symbol documented here is a global constant, except for macros; rationale: it is convenient to be able to re-define macros in a backwards-compatible way, and macros are not critical to performance as macro calls are always replaced with AST combinations before evaluation]


3.6.1 Type checking


3.6.1.1 Actual type cheking

[FIXME: “Actual” is a terrible word to use here.]

[FIXME: Write. These return a canonical Boolean and never fail.]

primitive wrapper: fixnum? x
primitive wrapper: unique? x
primitive wrapper: boolean? x
primitive wrapper: character? x
primitive wrapper: null? x
primitive wrapper: eof? x
primitive wrapper: nothing? x
primitive wrapper: undefined? x
primitive wrapper: box? x
primitive wrapper: symbol? x
primitive wrapper: cons? x
primitive wrapper: primitive? x
primitive wrapper: macro? x
primitive wrapper: interpreted-closure? x
primitive wrapper: compiled-closure? x
primitive wrapper: ast? x
primitive wrapper: non-null? x
primitive wrapper: non-cons? x
primitive wrapper: non-symbol? x

3.6.1.2 Pseudo-type checking

procedure: number? x
procedure: closure? x
procedure: list? x
procedure: singleton? x
procedure: null-or-singleton? x
procedure: symbols? x
procedure: alist? x

3.6.2 Boolean operations

primitive wrapper: not x
macro: or . clauses
macro: lispy-or . clauses
macro: and . clauses
macro: lispy-and . clauses

[FIXME: lispy-and is provided just for symmetry with lispy-or, and is in fact an alias for and. The traditional Lisp definition for and introduces no inefficiencies in this case, differently from the traditional Lisp implementation of or.]

primitive wrapper: boolean-canonicalize x

3.6.3 Arithmetic

[FIXME: fixnum only for the time being]

primitive wrapper: negate x
primitive wrapper: 1+ x
primitive wrapper: 1- x
primitive wrapper: 2* x
primitive wrapper: 2/ x
primitive wrapper: 2quotient x
primitive wrapper: 2remainder x
primitive wrapper: primordial-+ x y
primitive wrapper: primordial-- x y
primitive wrapper: primordial-* x y
primitive wrapper: primordial-/ x y
primitive wrapper: quotient x y
primitive wrapper: remainder x y
procedure: *-by-sums-procedure x y
procedure: **-procedure b e
macro: + . args
macro: * . args
macro: *-by-sums . args
macro: ** . args
macro: - . args
macro: / . args
procedure: square x
procedure: even? x
procedure: odd? x
fixnum: fixnum-bit-no
fixnum: most-negative-fixnum
fixnum: most-positive-fixnum

3.6.4 Number comparison

[FIXME: fixnum only for the time being]

primitive wrapper: zero? x
primitive wrapper: non-zero? x
primitive wrapper: positive? x
primitive wrapper: non-positive? x
primitive wrapper: negative? x
primitive wrapper: non-negative? x
primitive wrapper: = x y
primitive wrapper: < x y
primitive wrapper: > x y
primitive wrapper: <= x y
primitive wrapper: >= x y
primitive wrapper: <> x y

3.6.5 Symbol handling

primitive wrapper: gensym
primitive wrapper: interned-symbols
primitive wrapper: symbol-global s
primitive wrapper: make-constant! s
primitive wrapper: constant? s
primitive wrapper: defined? s
primitive wrapper: undefine s

3.6.6 Box handling

primitive wrapper: box x
primitive wrapper: box-get b
primitive wrapper: box-set! b x

3.6.7 Cons handling

primitive wrapper: cons a b
primitive wrapper: car c
primitive wrapper: cdr c
procedure: caar c
procedure: cdar c
procedure: cadr c
procedure: cddr c
procedure: caaar c
procedure: cadar c
procedure: caadr c
procedure: caddr c
procedure: cdaar c
procedure: cddar c
procedure: cdadr c
procedure: cdddr c
procedure: caaaar c
procedure: caadar c
procedure: caaadr c
procedure: caaddr c
procedure: cadaar c
procedure: caddar c
procedure: cadadr c
procedure: cadddr c
procedure: cdaaar c
procedure: cdadar c
procedure: cdaadr c
procedure: cdaddr c
procedure: cddaar c
procedure: cdddar c
procedure: cddadr c
procedure: cddddr c
primitive wrapper: set-car! c x
primitive wrapper: set-cdr! c x
procedure: set-caar! c x
procedure: set-cdar! c x
procedure: set-cadr! c x
procedure: set-cddr! c x
procedure: set-caaar! c x
procedure: set-cadar! c x
procedure: set-caadr! c x
procedure: set-caddr! c x
procedure: set-cdaar! c x
procedure: set-cddar! c x
procedure: set-cdadr! c x
procedure: set-cdddr! c x
procedure: set-caaaar! c x
procedure: set-caadar! c x
procedure: set-caaadr! c x
procedure: set-caaddr! c x
procedure: set-cadaar! c x
procedure: set-caddar! c x
procedure: set-cadadr! c x
procedure: set-cadddr! c x
procedure: set-cdaaar! c x
procedure: set-cdadar! c x
procedure: set-cdaadr! c x
procedure: set-cdaddr! c x
procedure: set-cddaar! c x
procedure: set-cdddar! c x
procedure: set-cddadr! c x
procedure: set-cddddr! c x
procedure: car-or-nil c
procedure: cdr-or-nil c
macro: improper-list first-element . more-elements
macro: cons* first-element . more-elements
macro: circular-list first-element . more-elements

3.6.8 List handling

procedure: singleton
macro: list . elements
procedure: replicate n x
procedure: make-list n x
procedure: last-cons xs
procedure: last xs
procedure: all-but-last xs
procedure: all-but-last-reversed xs
procedure: length xs
procedure: reverse xs
procedure: reverse! xs
procedure: append-procedure xs ys
procedure: append-reversed xs ys
procedure: append!-procedure xs ys
macro: append . xss
macro: append! . xss
procedure: flatten xss
procedure: flatten! xss
procedure: flatten-reversed xss
procedure: flatten-reversed! xss
procedure: list-copy xs
procedure: nth-cons xs n
procedure: nth-cons-or-nil xs n
procedure: nth xs n
procedure: take xs n
procedure: take-reversed xs n
procedure: take! xs n
procedure: drop xs n
procedure: drop! xs n
procedure: zip xs n
procedure: zip-reversed xs n
procedure: unzip xs n
procedure: unzip-reversed xs n
procedure: map f xs
procedure: map-reversed f xs
procedure: map! f xs
procedure: fold-left f x xs
procedure: fold-right f xs y
procedure: fold-right! f xs y
procedure: exists? p xs
procedure: for-all? p xs
procedure: filter p xs
procedure: filter-reversed p xs
procedure: range a b
procedure: range-reversed a b
procedure: iota n
procedure: sort xs
procedure: list-has? xs x
procedure: list-without xs x
procedure: list-without! xs x
procedure: all-different?

[FIXME: quadratic]


3.6.9 Alist handling

procedure: assq key alist
procedure: rassq value alist
procedure: del-assq-1 key alist
procedure: del-assq key alist
procedure: del-assq-noncopying key alist
procedure: del-assq-list keys alist
procedure: del-assq-list-noncopying keys alist
procedure: alist-copy alist
procedure: alist-get key alist

3.6.10 Higher order

procedure: compose-procedure f g
procedure: compose-eta f g
macro: compose . fs
macro: compose-pipeline . fs
procedure: square-function f
procedure: square-function-eta f
procedure: iterate f n
procedure: iterate-eta f n
procedure: iterate-pre f n
procedure: iterate-post f n
macro: lambda-wrapper rator arity

3.6.11 Variadic utility

macro: define-left-nested-variadic-extension name original-name neutral
macro: define-right-nested-variadic-extension name original-name neutral
macro: define-associative-variadic-extension name original-name neutral

[FIXME: yes, the things that follow are really procedures]

procedure: variadic-left-deep rator neutral rands
procedure: variadic-right-deep rator neutral rands

3.6.12 Sets as lists

[FIXME: Performance caveat]

[FIXME: order caveat]

[FIXME: The word “set” as used here refers to a collection of objects in the mathematical sense, and has nothing to do with the action of “setting” as performed by set-thing! procedures.]

unique: set-empty
procedure: set-empty? s
procedure: set-singleton x
macro: set . elements
procedure: set-has? s x
procedure: set-with s x
procedure: set-without s x
procedure: set-unite-procedure s1 s2
procedure: set-intersect-procedure s1 s2
procedure: set-subtract-procedure s1 s2
macro: set-unite . sets
macro: set-intersect . sets
macro: set-subtract first-set . other-sets
procedure: list->set xs

[FIXME: No set->list is needed].


3.6.13 AST operations

[FIXME: if the argument is not an AST these all fail.]

primitive wrapper: ast-literal?
primitive wrapper: ast-variable?
primitive wrapper: ast-define?
primitive wrapper: ast-if?
primitive wrapper: ast-set!?
primitive wrapper: ast-while?
primitive wrapper: ast-primitive?
primitive wrapper: ast-call?
primitive wrapper: ast-lambda?
primitive wrapper: ast-let?
primitive wrapper: ast-sequence?

[FIXME: types: when we fail]

primitive wrapper: ast-literal value
primitive wrapper: ast-variable name
primitive wrapper: ast-define name body
primitive wrapper: ast-if condition then else
primitive wrapper: ast-set! name body
primitive wrapper: ast-while guard body
primitive wrapper: ast-primitive operator operands
primitive wrapper: ast-call operator operands
primitive wrapper: ast-lambda formals body
primitive wrapper: ast-let bound-name bound-form body
primitive wrapper: ast-sequence first second

[FIXME: these fail if the argument is not an AST or if it’s an AST of the wrong case] [FIXME: no destructive operations right now. They would be easy to add, even if using them in an unrestricted way might destroy compiler properties; anyway, it is already possible to destroy the same compiler implied properties, by violating other unenforced invariants]

primitive wrapper: ast-literal-value ast
primitive wrapper: ast-variable-name ast
primitive wrapper: ast-define-name ast
primitive wrapper: ast-define-body ast
primitive wrapper: ast-if-condition ast
primitive wrapper: ast-if-then ast
primitive wrapper: ast-if-else ast
primitive wrapper: ast-set!-name ast
primitive wrapper: ast-set!-body ast
primitive wrapper: ast-while-guard ast
primitive wrapper: ast-while-body ast
primitive wrapper: ast-primitive-operator ast
primitive wrapper: ast-primitive-operands ast
primitive wrapper: ast-call-operator ast
primitive wrapper: ast-call-operands ast
primitive wrapper: ast-lambda-formals ast
primitive wrapper: ast-lambda-body ast
primitive wrapper: ast-let-bound-name ast
primitive wrapper: ast-let-bound-form ast
primitive wrapper: ast-let-body ast
primitive wrapper: ast-sequence-first ast
primitive wrapper: ast-sequence-second ast

[FIXME: An example showing a constuction, check and field access for the same case, to highlight the lexical conventions.]


3.6.14 AST optimization

[FIXME: Fill this.]


3.6.15 Macroexpanding

[FIXME: There is currently no syntactic support for local macros, such as Common Lisp’s macrolet.]

primitive wrapper: primordial-macroexpand form env
macro: macroexpand form . optional-env

3.6.16 Code execution

primitive wrapper: apply operator operands
primitive wrapper: apply-primitive operator operands
primitive wrapper: primordial-eval form env
macro: eval form . optional-env

3.6.17 Compilation

[FIXME: Fill this.]


3.6.18 Input and output

JitterLisp provides very crude I/O functionalities. Files are not implemented, so input and output only use the standard input and standard output, respectively, or the terminal via ReadLine.


3.6.18.1 Character I/O

The following functionality provides a thin wrapper over C for reading or writing one character at a time.

primitive wrapper: character-read

Read a single character from the terminal and return it, or return #<eof> if the input ends.

The implementation is a thin wrapper over the getchar function from the C library.

primitive wrapper: character-display c

Print the single character c to the standard output. Error out if c is not a character object.

primitive wrapper: newline

Print a newline character to the standard output.

If newline were not already provided in the library it could be defined as:

(define-constant (newline)
  (character-display #\newline))

3.6.18.2 Lisp object I/O

primitive wrapper: read

Read one Lisp object in its input representation from the terminal, using the Readline functionality if enabled, and return it. Return #<eof> if the input ends before an object is entered.

The input may span several lines and include whitespace or even Lisp comments, but the read object must be exactly one.

Error out on syntax error, or if a second object starts after the first.

primitive wrapper: display thing

Emit the printed representation of thing to the standard output. No newline character or other terminator is added.


3.6.19 Debugging and profiling

primitive wrapper: print-locations

Print data location information, mapping runtime VM data structures into hardware machine resources. This is convenient for reading disassemblies.

primitive wrapper: print-defects

Print the names of all defective specialised instructions. Notice that the set of defective specialised instructions may be different from the set of replaced specialised instructions: when any call-related specialised instruction is defective then every call-related specialised instruction will be replaced.
The output is meant for the user.

primitive wrapper: print-defect-replacements

Print information about defective specialised instruction, listing the name of each replaced specialised instruction along with the specialised instruction replacing it. About the difference between defective specialised instruction and replaced specialised instruction see print-defects.
The output is meant for the user.

primitive wrapper: print-profile-specialized

Print cumulative profile information about the specialised VM instructions run up to this point. This just prints a warning if JitterLisp was not compiled with profiling instrumentation enabled, defining at least one of the CPP macros JITTERLISPVM_PROFILE_COUNT and JITTERLISPVM_PROFILE_SAMPLE; since profiling instrumentation incurs a performance penalty it is disabled by default.

primitive wrapper: print-profile-unspecialized

This primitive wrapper behaves exactly like print-profile-specialized, except that it prints information about unspecialised VM instructions.

primitive wrapper: reset-profile!

Reset profile information, changing the execution count for every instruction back to zero.


3.6.21 Unclassified (move)

procedure: identity x
primitive wrapper: eq? x y
primitive wrapper: not-eq? x y
primitive wrapper: error thing
primitive wrapper: gc

4 C API

[FIXME: Fill this.]


5 Internals

[FIXME: Fill this.]

[FIXME: it’s a stack machine: two stacks plus registers. Explain calling conventions. Explain closure representations. Explain tagging, possibly at the beginning]


Symbol and reserved syntax index

What follows is an alphabetical list of read syntax (see Lisp data) and predefined JitterLisp globals, without distinction between core definitions coming from the C code and library definitions from the library written in Lisp and executed at startup.

In the same sense the index glosses over the difference in type among globally defined variables: some may be procedures or primitive wrappers and other macros, from either the core or the library.

Most of the symbols named here are constant. Some, particularly macros, may not be.

Jump to:   #   '   (   )   *   +   ,   -   .   /   1   2   <   =   >   `  
A   B   C   D   E   F   G   I   L   M   N   O   P   Q   R   S   T   U   V   W   Z  
Index Entry  Section

#
#<eof> (no read syntax): Uniques
#<nothing> (no read syntax): Uniques
#<nothing> (no read syntax): AST semantics in detail
#<nothing> (no read syntax): AST semantics in detail
#<undefined> (no read syntax): Uniques
#f: Uniques
#t: Uniques

'
' (apostrophe, prefix for quote): Conses
' (apostrophe, prefix for quote): Quoting and quasiquoting

(
( (open parenthesis, cons syntax): Conses
() (empty list object): Uniques

)
) (closed parenthesis, cons syntax): Conses

*
*: Arithmetic
**: Arithmetic
**-procedure: Arithmetic
*-by-sums: Arithmetic
*-by-sums-procedure: Arithmetic

+
+: Arithmetic

,
, (comma, prefix for unquote): Conses
, (comma, prefix for unquote): Quoting and quasiquoting
,@ (comma at, prefix for unquote-splicing): Conses
,@ (comma at, prefix for unquote-splicing): Quoting and quasiquoting

-
-: Arithmetic

.
. (dot), compact cons notation: Conses
. (dot, cons syntax): Conses

/
/: Arithmetic

1
1+: Arithmetic
1-: Arithmetic

2
2*: Arithmetic
2/: Arithmetic
2quotient: Arithmetic
2remainder: Arithmetic

<
<: Number comparison
<=: Number comparison
<>: Number comparison

=
=: Number comparison

>
>: Number comparison
>=: Number comparison

`
` (backtick, prefix for quasiquote): Conses
` (backtick, prefix for quasiquote): Quoting and quasiquoting

A
alist-copy: Alist handling
alist-get: Alist handling
alist?: Pseudo-type checking
all-but-last: List handling
all-but-last-reversed: List handling
all-different?: List handling
and: Boolean operations
apostrophe ('), prefix for quote: Conses
apostrophe ('), prefix for quote: Quoting and quasiquoting
append: List handling
append!: List handling
append!-procedure: List handling
append-procedure: List handling
append-reversed: List handling
apply: Code execution
apply-primitive: Code execution
assq: Alist handling
ast-call: AST operations
ast-call-operands: AST operations
ast-call-operator: AST operations
ast-call?: AST operations
ast-define: AST operations
ast-define-body: AST operations
ast-define-name: AST operations
ast-define?: AST operations
ast-if: AST operations
ast-if-condition: AST operations
ast-if-else: AST operations
ast-if-then: AST operations
ast-if?: AST operations
ast-lambda: AST operations
ast-lambda-body: AST operations
ast-lambda-formals: AST operations
ast-lambda?: AST operations
ast-let: AST operations
ast-let-body: AST operations
ast-let-bound-form: AST operations
ast-let-bound-name: AST operations
ast-let?: AST operations
ast-literal: AST operations
ast-literal-value: AST operations
ast-literal?: AST operations
ast-primitive: AST operations
ast-primitive-operands: AST operations
ast-primitive-operator: AST operations
ast-primitive?: AST operations
ast-sequence: AST operations
ast-sequence-first: AST operations
ast-sequence-second: AST operations
ast-sequence?: AST operations
ast-set!: AST operations
ast-set!-body: AST operations
ast-set!-name: AST operations
ast-set!?: AST operations
ast-variable: AST operations
ast-variable-name: AST operations
ast-variable?: AST operations
ast-while: AST operations
ast-while-body: AST operations
ast-while-guard: AST operations
ast-while?: AST operations
ast?: Actual type checking

B
backtick (`), prefix for quasiquote: Conses
backtick (`), prefix for quasiquote: Quoting and quasiquoting
begin: From ASTs to JitterLisp
begin: From ASTs to JitterLisp
begin: Sequencing
begin-1: Sequencing
begin-2: Sequencing
begin-3: Sequencing
begin-4: Sequencing
begin-from-first: Sequencing
begin-from-last: Sequencing
begin1: Sequencing
begin2: Sequencing
begin3: Sequencing
begin4: Sequencing
boolean-canonicalize: Boolean operations
boolean?: Actual type checking
box: Box handling
box-get: Box handling
box-set!: Box handling
box?: Actual type checking

C
caaaar: Cons handling
caaadr: Cons handling
caaar: Cons handling
caadar: Cons handling
caaddr: Cons handling
caadr: Cons handling
caar: Cons handling
cadaar: Cons handling
cadadr: Cons handling
cadar: Cons handling
caddar: Cons handling
cadddr: Cons handling
caddr: Cons handling
cadr: Cons handling
call/cc (non-existing form): Intentionally omitted features
car: Cons handling
car-or-nil: Cons handling
case: Conditionals
catch (non-existing form): Intentionally omitted features
catch (non-existing form): Failure
cdaaar: Cons handling
cdaadr: Cons handling
cdaar: Cons handling
cdadar: Cons handling
cdaddr: Cons handling
cdadr: Cons handling
cdar: Cons handling
cddaar: Cons handling
cddadr: Cons handling
cddar: Cons handling
cdddar: Cons handling
cddddr: Cons handling
cdddr: Cons handling
cddr: Cons handling
cdr: Cons handling
cdr-or-nil: Cons handling
character-display: Character I/O
character-read: Character I/O
character?: Actual type checking
circular-list: Cons handling
closure?: Pseudo-type checking
comma (,), prefix for unquote: Conses
comma (,), prefix for unquote: Quoting and quasiquoting
comma at (,@), prefix for unquote-splicing: Conses
comma at (,@), prefix for unquote-splicing: Quoting and quasiquoting
compiled-closure?: Actual type checking
compose: Higher order
compose-eta: Higher order
compose-pipeline: Higher order
compose-procedure: Higher order
cond: Conditionals
cons: Cons handling
cons*: Cons handling
cons?: Actual type checking
constant?: Symbol handling
copying: Legal notices

D
define: Definition forms
define: Making macros
define-associative-variadic-extension: Variadic utility
define-constant: Definition forms
define-constant-non-optimized: Definition forms
define-left-nested-variadic-extension: Variadic utility
define-macro: Definition forms
define-macro: Making macros
define-non-optimized: Definition forms
define-right-nested-variadic-extension: Variadic utility
defined?: Symbol handling
del-assq: Alist handling
del-assq-1: Alist handling
del-assq-list: Alist handling
del-assq-list-noncopying: Alist handling
del-assq-noncopying: Alist handling
destructuring-bind: Blocks
display: Lisp object I/O
do: Loops
dolist: Loops
dot (.), compact cons notation: Conses
dot (.), cons syntax: Conses
dotimes: Loops
dotimesdown: Loops
drop: List handling
drop!: List handling

E
eof?: Actual type checking
eq?: Unclassified
error: Failure
error: Unclassified
eval: Code execution
even?: Arithmetic
exists?: List handling

F
filter: List handling
filter-reversed: List handling
fixnum-bit-no: Arithmetic
fixnum?: Actual type checking
flatten: List handling
flatten!: List handling
flatten-reversed: List handling
flatten-reversed!: List handling
fold-left: List handling
fold-right: List handling
fold-right!: List handling
for-all?: List handling

G
gc: Unclassified
gensym: Symbol handling

I
identity: Unclassified
if: Conditionals
improper-list: Cons handling
interned-symbols: Symbol handling
interpreted-closure?: Actual type checking
iota: List handling
iterate: Higher order
iterate-eta: Higher order
iterate-post: Higher order
iterate-pre: Higher order

L
lambda: Procedures
lambda-wrapper: Higher order
last: List handling
last-cons: List handling
length: List handling
let: Blocks
let*: Blocks
let, named: Blocks
let, named: Loops
letrec: Blocks
lispy-and: Boolean operations
lispy-or: Boolean operations
lispy-or: Boolean operations
list: List handling
list->set: Sets as lists
list-copy: List handling
list-has?: List handling
list-without: List handling
list-without!: List handling
list?: Pseudo-type checking
low-level-macro: Making macros
low-level-macro-args: Making macros

M
macro: Making macros
macro?: Actual type checking
macroexpand: From ASTs to JitterLisp
macroexpand: From ASTs to JitterLisp
macroexpand: Macroexpanding
macrolet (non-existing form): Definition forms
macrolet (non-existing form): Macroexpanding
make-constant!: Assignment
make-constant!: Symbol handling
make-list: List handling
map: List handling
map!: List handling
map-reversed: List handling
most-negative-fixnum: Arithmetic
most-positive-fixnum: Arithmetic

N
named let: Blocks
named let: Loops
named-let: Loops
negate: Arithmetic
negative?: Number comparison
newline: Character I/O
no-warranty: Legal notices
non-cons?: Actual type checking
non-negative?: Number comparison
non-null?: Actual type checking
non-positive?: Number comparison
non-symbol?: Actual type checking
non-zero?: Number comparison
not: Boolean operations
not-eq?: Unclassified
nothing?: Actual type checking
nth: List handling
nth-cons: List handling
nth-cons-or-nil: List handling
null-or-singleton?: Pseudo-type checking
null?: Actual type checking
number?: Pseudo-type checking

O
odd?: Arithmetic
or: Boolean operations

P
parentheses (()), empty list object: Uniques
parenthesis, compact cons notation: Conses
parenthesis, cons syntax: Conses
period (.), compact cons notation: Conses
period (.), cons syntax: Conses
positive?: Number comparison
primitive?: Actual type checking
primordial-*: Arithmetic
primordial-+: Arithmetic
primordial--: Arithmetic
primordial-/: Arithmetic
primordial-eval: Code execution
primordial-macroexpand: Macroexpanding
print-defect-replacements: Debugging and profiling
print-defects: Debugging and profiling
print-locations: Debugging and profiling
print-profile-specialized: Debugging and profiling
print-profile-unspecialized: Debugging and profiling

Q
quasiquote: Quoting and quasiquoting
quasiquote: Quoting and quasiquoting
quote: Quoting and quasiquoting
quote: Quoting and quasiquoting
quotient: Arithmetic

R
range: List handling
range-reversed: List handling
rassq: Alist handling
read: Lisp object I/O
remainder: Arithmetic
replicate: List handling
reset-profile!: Debugging and profiling
reverse: List handling
reverse!: List handling

S
set: Sets as lists
set!: Assignment
set->list (non-existing procedure): Sets as lists
set-caaaar!: Cons handling
set-caaadr!: Cons handling
set-caaar!: Cons handling
set-caadar!: Cons handling
set-caaddr!: Cons handling
set-caadr!: Cons handling
set-caar!: Cons handling
set-cadaar!: Cons handling
set-cadadr!: Cons handling
set-cadar!: Cons handling
set-caddar!: Cons handling
set-cadddr!: Cons handling
set-caddr!: Cons handling
set-cadr!: Cons handling
set-car!: Cons handling
set-cdaaar!: Cons handling
set-cdaadr!: Cons handling
set-cdaar!: Cons handling
set-cdadar!: Cons handling
set-cdaddr!: Cons handling
set-cdadr!: Cons handling
set-cdar!: Cons handling
set-cddaar!: Cons handling
set-cddadr!: Cons handling
set-cddar!: Cons handling
set-cdddar!: Cons handling
set-cddddr!: Cons handling
set-cdddr!: Cons handling
set-cddr!: Cons handling
set-cdr!: Cons handling
set-empty: Sets as lists
set-empty?: Sets as lists
set-has?: Sets as lists
set-intersect: Sets as lists
set-intersect-procedure: Sets as lists
set-singleton: Sets as lists
set-subtract: Sets as lists
set-subtract-procedure: Sets as lists
set-unite: Sets as lists
set-unite-procedure: Sets as lists
set-with: Sets as lists
set-without: Sets as lists
singleton: List handling
singleton?: Pseudo-type checking
sort: List handling
square: Arithmetic
square-function: Higher order
square-function-eta: Higher order
symbol-global: Symbol handling
symbol?: Actual type checking
symbols?: Pseudo-type checking

T
take: List handling
take!: List handling
take-reversed: List handling
throw (non-existing form): Intentionally omitted features
throw (non-existing form): Failure

U
undefine: Symbol handling
undefined?: Actual type checking
unique?: Actual type checking
unless: Conditionals
unquote: Quoting and quasiquoting
unquote: Quoting and quasiquoting
unquote-splicing: Quoting and quasiquoting
unquote-splicing: Quoting and quasiquoting
unzip: List handling
unzip-reversed: List handling

V
values (non-existing form): Intentionally omitted features
variadic-left-deep: Variadic utility
variadic-right-deep: Variadic utility

W
when: Conditionals
while: Loops

Z
zero?: Number comparison
zip: List handling
zip-reversed: List handling


Concept index

Jump to:   -   .   /  
A   B   C   D   E   F   G   H   I   J   L   M   N   O   P   Q   R   S   T   U   V   W   Y  
Index Entry  Section

-
-, command-line non-option argument: Invoking JitterLisp
--, command-line option: Invoking JitterLisp
--batch, command-line option: Invoking JitterLisp
--colorize, command-line option: Invoking JitterLisp
--compact-uninterned, command-line option: Invoking JitterLisp
--cross-disassembler, command-line option: Invoking JitterLisp
--disable-shared, Jitter configure option: Installing JitterLisp
--dump-version, command-line option: Invoking JitterLisp
--eval, command-line option: Invoking JitterLisp
--free-routines, command-line option: Invoking JitterLisp
--help, command-line option: Invoking JitterLisp
--library, command-line option: Invoking JitterLisp
--no-batch, command-line option: Invoking JitterLisp
--no-colorize, command-line option: Invoking JitterLisp
--no-compact-uninterned, command-line option: Invoking JitterLisp
--no-cross-disassembler, command-line option: Invoking JitterLisp
--no-free-routines, command-line option: Invoking JitterLisp
--no-library, command-line option: Invoking JitterLisp
--no-omit-nothing, command-line option: Invoking JitterLisp
--no-optimization-rewriting, command-line option: Invoking JitterLisp
--no-repl, command-line option: Invoking JitterLisp
--no-time, command-line option: Invoking JitterLisp
--no-verbose, command-line option: Invoking JitterLisp
--no-verbose-litter, command-line option: Invoking JitterLisp
--omit-nothing, command-line option: Invoking JitterLisp
--optimization-rewriting, command-line option: Invoking JitterLisp
--repl, command-line option: Invoking JitterLisp
--time=time, command-line option: Invoking JitterLisp
--usage, command-line option: Invoking JitterLisp
--verbose, command-line option: Invoking JitterLisp
--verbose-litter, command-line option: Invoking JitterLisp
--version, command-line option: Invoking JitterLisp
-?, command-line option: Invoking JitterLisp
-c, command-line option: Invoking JitterLisp
-e, command-line option: Invoking JitterLisp
-r, command-line option: Invoking JitterLisp
-v, command-line option: Invoking JitterLisp

.
. AST meta-syntax: AST syntax

/
/1, Lisp: Comparison with other Lisps

A
Abstract Syntax Tree (AST): epsilonian features
Abstract Syntax Tree (AST): epsilonian features
Abstract Syntax Tree (AST): Abstract Syntax Trees
Abstract Syntax Tree (AST): Abstract Syntax Trees and their semantics
abstraction, AST semantics: AST semantics in detail
abstraction, AST syntax: AST syntax
abstraction, syntactic: AST syntax
address: Lisp data
alias, shell: Invoking JitterLisp
ambition: Interaction example
ANSI terminal escape sequence: Invoking JitterLisp
architecture, hardware: Invoking JitterLisp
argument (formal), lambda, AST semantics: AST semantics in detail
argument (formal), lambda, AST syntax: AST syntax
assignment: Assignment
assignment, AST semantics: AST semantics in detail
assignment, AST syntax: AST syntax
AST output syntax: AST syntax
AST syntactic constraint: AST syntax
AST syntax (output): AST syntax
author, contacting: Obtaining the software
Autoconf: Building JitterLisp
Automake: Building JitterLisp

B
beginner: Interaction example
block: Blocks
block, AST semantics: AST semantics in detail
block, AST syntax: AST syntax
body, block, AST semantics: AST semantics in detail
body, block, AST syntax: AST syntax
body, lambda, AST semantics: AST semantics in detail
body, lambda, AST syntax: AST syntax
body, while, AST semantics: AST semantics in detail
body, while, AST syntax: AST syntax
Boehm-Demers garbage collector: Building JitterLisp
Boehm-Demers garbage collector: JitterLisp executables
Boolean: Uniques
Boolean, canonical: Uniques
Boolean, generalized: Uniques
bound expression, block, AST semantics: AST semantics in detail
bound expression, block, AST syntax: AST syntax
bound value, block: AST semantics in detail
bound variable, block, AST semantics: AST semantics in detail
bound variable, block, AST syntax: AST syntax
box: Boxes
boxed, Lisp object: Lisp data
branch, conditional, AST semantics: AST semantics in detail
branch, conditional, AST syntax: AST syntax
branch, git: Obtaining the software
build directory (Jitter): Using JitterLisp
build directory (Jitter): Building JitterLisp
build directory (Jitter): Building JitterLisp
build directory (Jitter): Building JitterLisp
build directory (Jitter): JitterLisp executables
build system: Using JitterLisp
build system, GNU: Building JitterLisp

C
C: Obtaining the software
C: Invoking JitterLisp
C, macro defined in: Invoking JitterLisp
C, macro defined in: From ASTs to JitterLisp
C-d, terminal input closing: Invoking JitterLisp
caddr: Conses
call name, call, AST semantics: AST semantics in detail
call name, call, AST syntax: AST syntax
call, AST semantics: AST semantics in detail
call, AST syntax: AST syntax
call-by-value: AST semantics
call-related VM instruction: Debugging and profiling
call-related VM instruction: Debugging and profiling
canonical Boolean: Uniques
car: Conses
cdr: Comparison with other Lisps
cdr: Conses
character: Comparison with other Lisps
character: Characters
character-based input/output: Character I/O
check, make target (Jitter): Building JitterLisp
checking, type: Type checking
circularity: Lisp data
circularity (forbidden), AST: AST semantics in detail
Coding Standards, GNU: Building JitterLisp
color: Invoking JitterLisp
color: Invoking JitterLisp
color: Invoking JitterLisp
command line: JitterLisp executables
command line: Invoking JitterLisp
command line option (jitterlisp): Invoking JitterLisp
command-line options: Invoking JitterLisp
common GNU options, command-line: Invoking JitterLisp
Common Lisp: Features and influences
Common Lisp: epsilonian features
compact cons notation: Conses
compatibility, other Lisp dialects (none): Features and influences
compilation: Invoking JitterLisp
compiled closure: Compiled closures
compiled procedure: Comparison with other Lisps
compiled procedure: Comparison with other Lisps
compiler: epsilonian features
compiler, self-: Abstract Syntax Trees
complexity, syntax: AST syntax
composed selector, cons: Conses
concept index: Concept index
condition, conditional, AST semantics: AST semantics in detail
condition, conditional, AST syntax: AST syntax
conditional: Uniques
conditional: Conditionals
conditional, AST semantics: AST semantics in detail
conditional, AST syntax: AST syntax
configuration item, JitterLisp: JitterLisp executables
configure script, Jitter: Building JitterLisp
configure script, Jitter: JitterLisp executables
configure script, Jitter: Installing JitterLisp
cons: Comparison with other Lisps
cons: Conses
cons notation, compact: Conses
constant: Assignment
contacting the author: Obtaining the software
copying (software software): License
copying (software): Legal notices
core macro: Invoking JitterLisp
core macro: From ASTs to JitterLisp
criterion, for feature inclusion: Features and influences
criterion, for feature inclusion: Features and influences
criterion, for feature inclusion: Features and influences
cross compilation: Using JitterLisp
cross compilation: Building JitterLisp
cross compiling: Building JitterLisp
cross-disassembly: Invoking JitterLisp
current working directory: JitterLisp executables
cycle (of pointers, within a Lisp object): Lisp data
cyclicity (forbidden), AST: AST semantics in detail

D
DAG, AST in memory: AST semantics in detail
DAG, output syntax: Lisp data
data location, print: Debugging and profiling
datatype: Comparison with other Lisps
debug: Invoking JitterLisp
default library, Lisp: Invoking JitterLisp
default, command-line option: Invoking JitterLisp
defective specialised instruction, print: Debugging and profiling
defective specialised instruction, print replacements: Debugging and profiling
defined value, global definition: AST semantics in detail
design criterion: Features and influences
Directed Acyclic Graph, AST in memory: AST semantics in detail
Directed Acyclic Graph, output syntax: Lisp data
directory, build (Jitter): Building JitterLisp
directory, current working: JitterLisp executables
disassembly: Invoking JitterLisp
disassembly: Invoking JitterLisp
disassembly, cross: Invoking JitterLisp
disassembly, cross: Invoking JitterLisp
disassembly, native: Invoking JitterLisp
dispatch, Jitter: JitterLisp executables
distribution: Using JitterLisp
distribution: Using JitterLisp
distribution: Obtaining the software
distribution: Obtaining the software
dot (.), AST meta-syntax: AST syntax
downloading: Obtaining the software
dynamic language: epsilonian features

E
eager: AST semantics
element, list: Conses
else branch, conditional, AST semantics: AST semantics in detail
else branch, conditional, AST syntax: AST syntax
empty list: Comparison with other Lisps
empty list: Uniques
emulator: Invoking JitterLisp
epsilon: History
epsilon: Features and influences
epsilon: epsilonian features
error: Failure
error, at the REPL: Failure
error, out of the REPL: Failure
escape sequence, ANSI terminal: Invoking JitterLisp
exceptions (non-existing feature): Failure
expresion-0, sequence, AST semantics: AST semantics in detail
expresion-0, sequence, AST syntax: AST syntax
expresion-1, sequence, AST semantics: AST semantics in detail
expresion-1, sequence, AST syntax: AST syntax
expression: Comparison with other Lisps
expression, assignment, AST semantics: AST semantics in detail
expression, assignment, AST syntax: AST syntax
expression, AST: AST syntax
expression, global definition, AST semantics: AST semantics in detail
expression, global definition, AST syntax: AST syntax

F
failure: Failure
failure, at the REPL: Failure
failure, out of the REPL: Failure
false: Uniques
FDL: License
feature: Features and influences
feature: Comparison with other Lisps
feature: Comparison with other Lisps
feature: Comparison with other Lisps
feature: Comparison with other Lisps
feature: Comparison with other Lisps
feature inclusion criterion: Features and influences
feature inclusion criterion: Features and influences
feature inclusion criterion: Features and influences
feedback: Obtaining the software
file (JitterLisp code): Using JitterLisp
file name, non-option argument: Invoking JitterLisp
first expression, sequence, AST semantics: AST semantics in detail
first expression, sequence, AST syntax: AST syntax
fixnum: Comparison with other Lisps
fixnum: Fixnums
font: Invoking JitterLisp
font: Invoking JitterLisp
font: Invoking JitterLisp
form, language: Lisp language forms
form, language: Lisp language forms
form, top-level (absent in JitterLisp): Comparison with other Lisps
formal, lambda, AST semantics: AST semantics in detail
formal, lambda, AST syntax: AST syntax
free documentation: License
Free Documentation License: License
free software: License

G
garbage collection: Comparison with other Lisps
garbage collector, Boehm-Demers: Building JitterLisp
garbage collector, Boehm-Demers: JitterLisp executables
General Public License: License
generalized Boolean: Uniques
git: Obtaining the software
git branch: Obtaining the software
global definition, AST semantics: AST semantics in detail
global definition, AST syntax: AST syntax
global state: AST semantics
GNU Autoconf: Building JitterLisp
GNU Automake: Building JitterLisp
GNU binutils: Invoking JitterLisp
GNU build system: Using JitterLisp
GNU build system: Building JitterLisp
GNU Coding Standards: Building JitterLisp
GNU conventions, build system: Using JitterLisp
GNU epsilon: History
GNU epsilon: epsilonian features
GNU Free Documentation License: License
GNU General Public License: License
GNU General Public License: Installing JitterLisp
GNU Libtextstyle: Building JitterLisp
GNU Libtool: Installing JitterLisp
GNU Readline: Building JitterLisp
GNU Readline: Invoking JitterLisp
GNU Readline: Input and output
GNU, standard options, command-line: Using JitterLisp
GNU, standard options, command-line: Invoking JitterLisp
GPL: License
GPL: Installing JitterLisp
guard, while, AST semantics: AST semantics in detail
guard, while, AST syntax: AST syntax

H
Hans Boehm’s garbage collector: Building JitterLisp
Hans Boehm’s garbage collector: JitterLisp executables
hardware architecture, host (cross-compilation): Invoking JitterLisp
higher order: Comparison with other Lisps
highlight, Lisp type: Invoking JitterLisp
history: History
homoiconicity: Comparison with other Lisps
homoiconicity: Lisp reference
host (cross-compilation): Invoking JitterLisp

I
I/O: Invoking JitterLisp
I/O: Input and output
identity, procedure across compiling: Comparison with other Lisps
if, AST syntax: AST syntax
imperative: AST semantics
index, for concepts: Concept index
index, for symbols: Symbol and reserved syntax index
influence: Features and influences
initialization file, JitterLisp: Installing JitterLisp
initialization file, JitterLisp: Internals
input: Input and output
input, closing: Invoking JitterLisp
input, standard: Invoking JitterLisp
input/output, character-based: Character I/O
input/output, for Lisp objects: Lisp object I/O
installing (by hand, for the time being): Installing JitterLisp
instruction, VM, rewriting: Invoking JitterLisp
instruction, VM, specialised: Debugging and profiling
instruction, VM, specialised: Debugging and profiling
interaction (REPL): Using JitterLisp
interpreted closure: Interpreted closures
interpreted procedure: Comparison with other Lisps
interpreted procedure: Comparison with other Lisps
invoking: JitterLisp executables
invoking: Invoking JitterLisp
iteration: Comparison with other Lisps

J
Jitter: History
Jitter: Using JitterLisp
Jitter: Building JitterLisp
jitterlisp and related executables, invoking: JitterLisp executables
jitterlisp and related executables, invoking: Invoking JitterLisp

L
lambda, AST semantics: AST semantics in detail
lambda, AST syntax: AST syntax
language form: Lisp language forms
language form: Lisp language forms
learning Lisp: Interaction example
let, AST semantics: AST semantics in detail
let, AST syntax: AST syntax
lexical scoping: Comparison with other Lisps
lexical scoping: Semantics preliminaries
lexical scoping: Blocks
library (native, non-Lisp): Installing JitterLisp
library file, JitterLisp: Installing JitterLisp
library file, JitterLisp: Internals
library, default, Lisp: Invoking JitterLisp
Libtextstyle: Building JitterLisp
Libtool: Installing JitterLisp
license, for JitterLisp: License
license, for this manual: License
license, text in JitterLisp executables: Installing JitterLisp
line editing: Invoking JitterLisp
Lisp default library: Invoking JitterLisp
Lisp object, input/output: Lisp object I/O
Lisp/1: Comparison with other Lisps
list: Comparison with other Lisps
list: Conses
list, empty: Uniques
literal, AST semantics: AST semantics in detail
literal, AST syntax: AST syntax
litter: JitterLisp executables
litter: Invoking JitterLisp
local variable: AST semantics in detail
location, print: Debugging and profiling
logic rules, operational semantics (in the reader’s head): AST semantics
loop: Uniques
loop: Loops
loop, AST semantics: AST semantics in detail
loop, AST syntax: AST syntax
Luca Saiu, contacting: Obtaining the software

M
macro: Features and influences
macro: Comparison with other Lisps
macro: epsilonian features
macro: AST syntax
macro (type): Macros
macro, core: Invoking JitterLisp
macro, core: From ASTs to JitterLisp
macroexpansion: epsilonian features
macroexpansion: From ASTs to JitterLisp
macroexpansion: From ASTs to JitterLisp
macroexpansion: Macroexpanding
make check (Jitter): Building JitterLisp
manual, license: License
master, git branch: Obtaining the software
message, litter allocation: Invoking JitterLisp
message, litter allocation: Invoking JitterLisp
minimalism: AST syntax
mutability, variable: Comparison with other Lisps

N
name, assignment, AST semantics: AST semantics in detail
name, assignment, AST syntax: AST syntax
name, block, AST semantics: AST semantics in detail
name, block, AST syntax: AST syntax
name, global definition, AST semantics: AST semantics in detail
name, global definition, AST syntax: AST syntax
name, variable, AST semantics: AST semantics in detail
name, variable, AST syntax: AST syntax
negative option, command line: Invoking JitterLisp
no warranty: License
no warranty: Legal notices
no’, option argument for --time=time: Invoking JitterLisp
non-executable, Jittery routine: Invoking JitterLisp
non-option argument: Invoking JitterLisp
notation, cons, compact: Conses
notation, prefix: Conses
number: Comparison with other Lisps

O
objdump: Invoking JitterLisp
object, Lisp, input/output: Lisp object I/O
obtaining: Obtaining the software
operand values, primitive, AST semantics: AST semantics in detail
operands, call, AST semantics: AST semantics in detail
operands, call, AST syntax: AST syntax
operands, primitive, AST semantics: AST semantics in detail
operands, primitive, AST syntax: AST syntax
operational semantics (in the reader’s head): AST semantics
operator, call, AST semantics: AST semantics in detail
operator, call, AST syntax: AST syntax
operator, primitive, AST semantics: AST semantics in detail
operator, primitive, AST syntax: AST syntax
optimizer, self-: Abstract Syntax Trees
option, command line (jitterlisp): JitterLisp executables
option, command line (jitterlisp): Invoking JitterLisp
options, command-line: Invoking JitterLisp
order, higher: Comparison with other Lisps
output: Invoking JitterLisp
output syntax, cons: Conses

P
path name, file, non-option argument: Invoking JitterLisp
pattern: Blocks
prefix notation: Conses
primitive: Primitives
primitive macro: Primitive macros
primitive name, primitive, AST semantics: AST semantics in detail
primitive name, primitive, AST syntax: AST syntax
primitive wrapper: Invoking JitterLisp
primitive, AST semantics: AST semantics in detail
primitive, AST syntax: AST syntax
print replacements, defective specialised instructions: Debugging and profiling
print, defective specialised instructions: Debugging and profiling
print, location: Debugging and profiling
print, profile, specialised: Debugging and profiling
print, profile, unspecialised: Debugging and profiling
procedure: Comparison with other Lisps
procedure: Comparison with other Lisps
procedure: Comparison with other Lisps
procedure call, AST semantics: AST semantics in detail
procedure call, AST syntax: AST syntax
procedure, compiled: Comparison with other Lisps
procedure, interpreted: Comparison with other Lisps
profile, reset: Debugging and profiling
profile, specialised, print: Debugging and profiling
profile, unspecialised, print: Debugging and profiling
programmer, non-: Interaction example
promising beginner: Interaction example

Q
quasiquoting: Comparison with other Lisps
quasiquoting: epsilonian features
quasiquoting: Quoting and quasiquoting
quoting: Comparison with other Lisps
quoting: Quoting and quasiquoting

R
read syntax: Lisp data
read syntax, cons: Conses
Read-Eval-Print Loop: Using JitterLisp
Read-Eval-Print Loop: Invoking JitterLisp
Readline: Building JitterLisp
Readline: Invoking JitterLisp
Readline: Input and output
Readline: Lisp object I/O
recursion: Comparison with other Lisps
reference, variable, AST syntax: AST syntax
reflection: epsilonian features
reflection: Abstract Syntax Trees
REPL: Using JitterLisp
REPL: From ASTs to JitterLisp
replacement, defective specialised instructions, printing: Debugging and profiling
reset, profile: Debugging and profiling
rewriting: Invoking JitterLisp
routine, executable, Jittery: Invoking JitterLisp
routine, Jittery: Invoking JitterLisp
routine, non-executable, Jittery: Invoking JitterLisp

S
safe, JitterLisp configuration item: JitterLisp executables
Saiu, Luca, contacting: Obtaining the software
Schadenfreude: Goals
Schadenfreude: Goals
Scheme: Features and influences
scoping: AST semantics
scoping, lexical: Comparison with other Lisps
scoping, lexical: Semantics preliminaries
scoping, lexical: Blocks
script, shell: Invoking JitterLisp
second expression, sequence, AST semantics: AST semantics in detail
second expression, sequence, AST syntax: AST syntax
self-compiler: Abstract Syntax Trees
self-optimizer: Abstract Syntax Trees
sequence, AST semantics: AST semantics in detail
sequence, AST syntax: AST syntax
sequence, escape, ANSI terminal: Invoking JitterLisp
sequencing: Sequencing
set!, AST semantics: AST semantics in detail
set!, AST syntax: AST syntax
shared library: Installing JitterLisp
sharing: Lisp data
shell: Invoking JitterLisp
shell alias: Invoking JitterLisp
software, license: License
source: Using JitterLisp
source: Obtaining the software
space overhead, tail call: Comparison with other Lisps
specialised, profile, print: Debugging and profiling
spine, list: Conses
standard error: Invoking JitterLisp
standard GNU options, command-line: Invoking JitterLisp
standard input: Invoking JitterLisp
state: AST semantics
statement (absent in JitterLisp): Comparison with other Lisps
static library: Installing JitterLisp
static scoping: Comparison with other Lisps
static scoping: Semantics preliminaries
static scoping: Blocks
structure sharing, AST: AST semantics in detail
sub-expression: Comparison with other Lisps
sub-project: History
sub-sub-project: History
sublist: Conses
substructure sharing: Lisp data
symbol: Comparison with other Lisps
symbol: Symbols
symbol index: Symbol and reserved syntax index
syntactic abstraction: AST syntax
syntactic complexity: AST syntax
syntactic constraint, AST: AST syntax
syntax: Lisp data
syntax (output), ASTs: AST syntax
syntax, cons: Conses

T
tail call: Comparison with other Lisps
tarball, stable release (not yet): Building JitterLisp
template (Common Lisp): Blocks
terminal escape sequence, ANSI: Invoking JitterLisp
test suite, Jitter: Building JitterLisp
then branch, conditional, AST semantics: AST semantics in detail
then branch, conditional, AST syntax: AST syntax
top-level form (absent in JitterLisp): Comparison with other Lisps
true: Uniques
type: Comparison with other Lisps
type checking: Type checking

U
unboxed, Lisp object: Lisp data
unique: Uniques
unsafe, JitterLisp configuration item: JitterLisp executables
unspecialised, profile, print: Debugging and profiling
usage (JitterLisp): Using JitterLisp

V
value, literal, AST semantics: AST semantics in detail
value, literal, AST syntax: AST syntax
variable: Comparison with other Lisps
variable name, assignment, AST semantics: AST semantics in detail
variable name, assignment, AST syntax: AST syntax
variable, AST semantics: AST semantics in detail
variable, AST syntax: AST syntax
variadic, procedure (absent in JitterLisp): Intentionally omitted features
verbose’, option argument for --time=time: Invoking JitterLisp
verbosity: Invoking JitterLisp
VM instruction rewriting: Invoking JitterLisp
VM instruction, specialised: Debugging and profiling
VM instruction, specialised: Debugging and profiling

W
warranty, lack thereof: License
warranty, lack thereof: Legal notices
while, AST semantics: AST semantics in detail
while, AST syntax: AST syntax
write syntax: Lisp data

Y
yes’, option argument for --time=time: Invoking JitterLisp


Footnotes

(1)

[FIXME: justify; not as homoiconic as other Lisps appear, but still more than epsilon. I argue that any Lisp system aiming at decent performance must do something like that; the difference is that I expose this mechanism.]

(2)

At the time of writing this restriction is moot. It is in fact impossible in the current implementation to build a circular AST, for two reasons: first of all, ASTs are immutable from JitterLisp, with the currently existing primitives; second, letrec (see Blocks) relies on assignment to a variable when building circular closures, and the strict constraint-checking policy of ASTs along with call-by-value evaluation prevents a “temporary” value from being used inside an AST to be built and then replaced later.