ListTalk is an experimental Lisp-family language and virtual machine written in C. The surface syntax is mostly Scheme-like S-expressions with a Smalltalk-style message-send shorthand, packages, classes, methods, macros, dynamic variables, conditions/restarts, modules, and a C embedding API.
The repository builds:
listtalk, an interactive REPL and script runner.libListTalkVM, the VM/runtime library used by the executable and native modules.libListTalkREPL, the reusable line-oriented REPL frontend.
ListTalk is under active development. The implementation is usable for experimentation, tests, and embedding work, but the language and C API should be treated as unstable.
- A C11 compiler
- Meson and Ninja
python3- Boehm-Demers-Weiser GC (
bdw-gc) - PCRE2's 8-bit library (
libpcre2-8) libeditor compatible readline headers, optional
When libedit is available at configure time, the REPL is built with editable
line input, history, and symbol/package completion. Without it, the REPL falls
back to standard line input.
meson setup build
meson compile -C buildRun the test suite:
meson test -C buildInstall:
meson install -C buildThe install target installs the executable, libListTalkVM, headers under
ListTalk/, pkg-config metadata, native modules, and source modules.
Start the REPL:
./build/listtalkEvaluate code without printing the result:
./build/listtalk -e '(define answer 42)'Evaluate code and print the result:
./build/listtalk -E '(+ 1 2)'Load a source file before continuing:
./build/listtalk -l path/to/file.ltRun a script:
./build/listtalk path/to/script.ltPass script arguments:
./build/listtalk path/to/script.lt arg1 arg2In script mode, ListTalk:command-line is bound to a list of strings containing
the script path followed by the script arguments.
Options are processed in order before the optional script path:
| Option | Description |
|---|---|
-e CODE, --eval CODE |
Evaluate one or more forms without printing their results. |
-E CODE, --eval-print CODE |
Evaluate one or more forms and print each result. |
-l PATH, --load PATH |
Load a source file into the current environment. |
-r MODULE, --require MODULE |
Require a module unless it is already provided. A leading : is accepted. |
-L PATH, --load-path PATH |
Prepend a module resolver path. |
--no-std-lib |
Do not add the standard installed source/native module paths. Must appear before environment-modifying options. |
-- |
Stop option parsing; remaining entries are positional arguments. |
If no script path is provided and at least one action option was used, ListTalk exits after processing those actions. If no script path and no action option are provided, it starts the REPL.
ListTalk reads Scheme/CL-style S-expressions:
(define (square x)
(* x x))
(square 12)Names are package-qualified with Package:symbol. Keywords are symbols in the
keyword package and start with :.
:ready
ListTalk:command-lineListTalk also supports bracket syntax for message sends:
[object selector]
[object selector: argument anotherSelector: value]
[Class new : initialize]These forms expand to send calls:
(send object :selector)
(send object :selector:anotherSelector: argument value)
(send (send Class :new) :initialize)Within a bracket send, : switches the receiver to the result of the send so
far. It can be a whitespace-delimited token (: initialize) or the first
character of the next selector component (:initialize).
Slot shorthand is available in method bodies. A token beginning with . reads
or writes a slot on ListTalk:self:
.name
(set! .name "new value")Classes and methods are defined in ListTalk code:
(define-class Point (Object) (x y))
(define-constructor [Point newX: x y: y]
(set! .x x)
(set! .y y)
self)
(define-method [Point x]
.x)
(define-method [Point y]
.y)
(define-method [Point magnitude]
(sqrt (+ (* .x .x) (* .y .y))))The core runtime includes native classes for objects, booleans, nil, symbols, strings, lists, vectors, bytevectors, numbers, dictionaries, sets, functions, macros, packages, streams, weak references, conditions, restarts, and date/time values.
Numeric support includes small integers, big integers, exact fractions, floats, and exact or inexact complex numbers:
(+ 1/2 1/3)
(* 1+2i 3+4i)
(sqrt -4)The runtime provides ordinary Lisp forms and macros such as lambda, let,
let*, define, set!, begin, if, when, unless, while, catch,
throw, and unwind-protect.
Conditions and restarts are available through handler-bind, restart-bind,
current-restarts, find-restart, and invoke-restart.
(catch :done
(throw :done 42))
(restart-bind ((:use-value value)
"Return a replacement value."
value)
(invoke-restart :use-value 10))Modules can be loaded or required from ListTalk code:
(load! :os)
(require :cmdopts)provide records that a module has been loaded:
(provide :my-module)The build currently provides native modules for:
osgccmdopts
It also installs the source modules:
html-gensxml(requires the native XML module), providingListTalk:sxml:parse-xmlandListTalk:sxml:as-string. Parsing retains comments and processing instructions when enabled with:comments #trueand:processing-instructions #true, respectively.
The optional native Markdown module uses md4c to parse Markdown into an S-expression AST and to render Markdown as HTML. It is built automatically when the md4c and md4c-html dependencies are available. Its detection can also be controlled explicitly when configuring the build:
meson setup build -Dmarkdown=enabled
# or: -Dmarkdown=disabledThis builds and installs markdown.ltm. Load it with (require :markdown);
its API is exposed in the ListTalk:markdown package, including parse,
valid?, event-count, and to-html.
The optional zlib.ltm module is likewise built automatically when zlib is
available, with -Dzlib=enabled and -Dzlib=disabled available as explicit
overrides. Load it with (require :zlib). The ListTalk:zlib package provides
compress and uncompress for bytevectors, the version constant, and the
dfsch-inspired gzip-open-for-input, gzip-open-for-output, and
gzip-open-for-append stream constructors. The same constructors are available
as GzipStream class methods named openForInput:, openForOutput:, and
openForAppend:.
When Expat is available, the optional xml.ltm module is built automatically;
use -Dxml=enabled or -Dxml=disabled to override detection. Its one-shot
ListTalk:XML:parse primitive accepts an XML bytevector and an instance of an
XMLEventHandler subclass. The abstract handler supplies no-op methods for
document, element, character, processing-instruction, comment, and CDATA
events, so subclasses only need to override events they use.
Additional module search paths can be supplied with -L or by passing resolver
paths to load!/require.
Installed consumers can use pkg-config:
pkg-config --cflags --libs ListTalkVMThe umbrella header is:
#include <ListTalk/ListTalk.h>Initialize the runtime before using VM APIs:
LT_INIT();Useful public entry points include LT_eval, LT_eval_sequence,
LT_eval_sequence_string, LT_apply, LT_send, and the LT_SEND/LT_APPLY
helper macros. Public class/value headers are installed under ListTalk/. The
REPL API is available from ListTalk/repl/repl.h; its prompt templates
accept %p as a placeholder for the current package.
src/vm/- evaluator, compiler, loader, environments, base forms, and VM services.src/classes/- native class implementations.src/modules/- native loadable modules.src/repl/- reusable REPL input, prompting, history, and completion logic.src/bin/listtalk/- command-line executable and its eval/print callback.runtime/init.lt- per-environment ListTalk runtime definitions embedded into the VM.runtime/init-once.lt- one-time ListTalk runtime method definitions and global mutations.modules/- source modules installed with the runtime.ListTalk/- public C headers.ListTalk/repl/- public REPL library header.tests/- C, CLI, and ListTalk language tests.design/- design notes for syntax and VM internals.
MIT. See LICENSE.