liblmin(3)

NAME
liblmin - small embeddable LISP library

SYNOPSIS
-llmin

DESCRIPTION

liblmin is a small LISP implementation intended to be embeddable as an extension language in other projects. It is inspired and strongly influenced by Robert van Engelen's tinylisp: https://github.com/Robert-van-Engelen/tinylisp It conforms to Greenspun's tenth rule: it's an ad hoc, informally-specified, bug-ridden, slow implementation of half of Common Lisp. Well, maybe not half - Common Lisp is huge!

About this man page. If you're going to be writing LISP code, look at the LANGUAGE sections. If you're going to be embedding the library in something else, or writing new primitives in C, look at the API sections.

LANGUAGE - GENERAL
Symbol binding is static/lexical, not dynamic.

Whitespace is mostly ignored, one exception being the dotted-pair form of a CONS cell. The dot must be surrounded by whitespace.

Number literals can be decimal integers or floats, or hexadecimal integers preceded by "0x". Character literals are enclosed by single-quotes and string literals are enclosed by double-quotes. Both can have backslash escape sequences to allow otherwise illegal characters:

-   \t turns into a tab

-   \n turns into a newline

-   \r turns into a carriage-return

-   \" turns into a double-quote

-   \' turns into a single-quote

-   \\ turns into a backslash

Vector literals look like a list but enclosed by [] instead of ().

A semicolon comments out the rest of the line - except not if it's inside a character or string literal.

LANGUAGE - DIALECT
The function names are mostly Common Lisp, some Scheme, and some changes from both of them. For instance, setting a variable in CL is (setq), and in Scheme it's (set!); in liblmin it's (setq!). In CL, the middle-level equality testing function is (eql), and in Scheme it's (eqv?); liblmin uses (eql?). In general, liblmin follows the Scheme convention of a '?' suffix on predicates, unlike CL's "p" convention; and the CL convention of a 'q' suffix when the first argument is not evaluated.

In some LISPs, atom names are converted to upper case, or matched case-insensitively. Liblmin does neither of those things. Atom names are stored and matched as entered.

The take-away from all this is you should not expect existing programs in CL or Scheme to run without some changes.

LANGUAGE - CELLS
The basic cell types in any LISP are ATOM, CONS, and NIL. To these, liblmin adds:

-   PRIM - a primitive function. You can add new ones to extend the language with C routines.

-   CLOSURE - a LISP function, plus the environment it was defined in. These are created by (lambda).

-   MACRO - a template for lexically re-writing some LISP code. These are created by (macro).

-   NUM - a number. Can be either an integer or a float.

-   CHR - a character.

-   STR - a string.

-   STREAM - a file pointer. The standard Unix files stdin, stdout, stderr are available in LISP as pre-defined STREAMs.

-   VECTOR - a 1-D array of cells. The contents don't all have to be the same cell type. The length of the vector grows as needed, with new elements set to the vector's "fill" value.

-   DATA - a block of uninterpreted data. Can be used as a rudimentary method of adding domain-specific data items to the language.

-   The distinguishing characteristic of an ERREX is that it gets automatically propagated up the call stack until explicitly checked for. It is used to implement both errors and exceptions. It carries along a payload, which can be any cell type. For errors, the payload is a string, while for exceptions it's an atom and a value. ERREXs also carry a callstack showing where they were created, as a debugging aid.

Errors are created via the (err) function. Generally they are not caught, and are allowed to propagate up to the top level and be reported to the user.

Exceptions are created by (throw) and caught by (catch). Uncaught exceptions that make it to the top level are reported to the user as errors.

LANGUAGE - PRIMITIVES AND BUILTINS

A primitive is a function written in C; a builtin is a function written in LISP, but still compiled in to the library. Otherwise they are the same idea: functions available in the base system. Liblmin has about 200 of them. Listing and describing them all here would make the man page pretty huge. Instead, use the builtin function (doc). Call it with an atom or string argument and it will describe the objects that match. As an example, here is (doc) describing itself:
    > (doc 'doc)
    doc: CLOSURE ("atom|string" key "string?" desc)
    Shows documentation of either the atom passed in, or all atoms
    matching the string passed in. With a second argument, adds
    that string as the documentation for the atom.
As the above example says, calling (doc) with a string instead of an atom will show everything that matches the string. And also, with a second argument you can use (doc) to attach a doc string to your own functions. If you're writing any sort of library, this is highly recommended. You can use the function (doc-check) to look for any global definitions that don't have (doc) strings attached.

There's also (apropos), which shows a briefer description of the matched definitions.

Note that (doc "") and (apropos "") will match everything and describe every global definition. This could be useful if you want to make a big list.

LANGUAGE - TYPE CHECKING
Liblmin provides a rudimentary type checking facility for both LISP lambdas and C primitives. Argument types are specified by string annotations in the formal parameter list, before the parameter name they apply to.

A typespec is a string containing:

-   a type name, one of: atom cons list function number integer character string stream vector data any

-   multiple type names separated by vertical bars

-   a trailing *?+ operator indicating zero-or-more, zero-or-one, one-or-more

If a parameter list's typespecs include an operator, it must be on the last typespec.

All of this is optional. If you don't want to use it, or can't figure out what type to declare on your function, just leave it out.

Typespacs are checked for correct syntax at define time, and checked against the actual parameters at call time - but see the SPECIAL FORMS section below.

LANGUAGE - SPECIAL FORMS
In LISP a special form is an executable function or macro that is called in other than the usual way. It's kind of non-specific. In Common Lisp there are only a few of them and you can't add more. In liblmin, the meaning is more specific: the form's arguments are passed in verbatim, instead of being evaluated, error-checked, and type-checked as they are when calling non-special forms. There are a variety of reasons why a form might need to be special; the most common is that it's a control structure and will be evaluating its args conditionally or repeatedly, such as (cond) or (while). Another reason is if it wants the args unevaluated, for example (quote) and (setq!). Liblmin has about twenty special forms.

LANGUAGE - GARBAGE COLLECTION
liblmin implements mark-sweep garbage collection. There's an API call to invoke it: lmin_gc(). The standard REPL lmin(1) calls garbage collection at the end of each loop. If you embed liblmin in your own app, you should call lmin_gc() regularly too.

LANGUAGE - STUPID LAMBDA TRICKS
Normally, lambda's formal parameters argument is a list of atoms. If you put something else there, you can get some unusual effects.

For example if the formal parameter arg is an atom instead of a list of atoms, it gets bound to the entire list of actual parameters:
    > ((lambda args (write-line args)) 1 2 3 4 5)
    (1 2 3 4 5)
If the formal parameter arg is a dotted list instead of a proper list, the atom after the dot gets bound to a list of the remaining actual parameters:
    > ((lambda (a . rest) (write-line rest)) 1 2 3 4 5)
    (2 3 4 5)
    > ((lambda (a b . rest) (write-line rest)) 1 2 3 4 5)
    (3 4 5)
No idea if these things work in all LISPs or if they are specific to how liblmin implements lambda binding.

API
All liblmin functions and data types use the "lmin_" prefix.

void lmin_init( void );
    Call this to initialize liblmin.

void lmin_term( void );
    Call this to terminate liblmin.

lmin_cellidx lmin_parse( const char* str );
    Parse a string into an lmin expression. The string must contain exactly one complete expression, not two or zero. If you have a string that may or may not have a complete expression, go ahead and call lmin_parse on it, and check if the return value equals lmin_err_incomplete.

lmin_cellidx lmin_define( lmin_cellidx var, lmin_cellidx val );
    Make a global definition.

lmin_cellidx lmin_eval( lmin_cellidx c );
    Evaluate an lmin expression. If the evaluation environment e is nil, the global environment is used.

size_t lmin_list_length( lmin_cellidx c );
    Get the length of a list.

lmin_cellidx lmin_tostring( lmin_cellidx c );
    Convert an expression to a STR.

const char* lmin_tocstring( lmin_cellidx c );
    Convert an expression to a C string (that gets garbage collected later).

lmin_cellidx lmin_eval_args( lmin_cellidx c, lmin_cellidx e );
    Evaluate an argument list. Usually the first thing done by a C primitive function.

lmin_cellidx lmin_check_errs( lmin_cellidx vals );
    Check an evaluated argument list for any errors. The first one found gets returned, and if there are none then the return is t. Usually the second thing done by a C primitive function.

lmin_cellidx lmin_check_errs( lmin_cellidx vals );
    Check an evaluated argument list against the formal parameters list, including optional typespecs. Usually the third thing done by a C primitive function.

lmin_write( lmin_cellidx c, FILE* f );
    Write an lmin expression to a file pointer.

void lmin_write_line( lmin_cellidx c, FILE* fp );
    Write an expression to a file pointer, followed by a newline.

lmin_cellidx lmin_read( FILE* fp, int* linesP );
    Read an expression from a file pointer.

void lmin_show_errex( FILE* fp, const char* filename, int linenum, lmin_cellidx errex );
    Show an ERREX.

void lmin_gc( void );
    Do garbage collection.

lmin_cellidx lmin_perm( lmin_cellidx c )
    Mark a cell permanent - immune from garbage collection.

lmin_celltype lmin_cell_get_type( lmin_cellidx c );
    Get a cell's lmin_celltype.

const char* lmin_celltype_str( lmin_celltype ct );
    Get the name of an lmin_celltype.

void lmin_load_addon( const char* name, const char** strs, int nstrs );
    Parse and eval the expressions in an array of strings. Useful for loading some compiled-in functions.

lmin_cellidx lmin_call( const char* name, ... );
    Call a LISP function from C. The first arg is the name of the function; the rest are lmin_cellidx; the last argument must be lmin_nil.

API - CELLS
In addition to the above, each cell type has maker / predicate / getter routines, and possibly setter routines too. They follow a consistent naming convention. The makers are "lmin_[celltype]_make()"; the predicates are "lmin_[celltype]_is()"; the getters are "lmin_[celltype]_[item]_get()". and the setters are "lmin_[celltype]_[item]_set()". See liblmin.h for specifics.

SEE ALSO
lmin(1) - a simple REPL that calls this library

BUGS / DEFICIENCIES

Does not currently handle Unicode.

There are some lexical ambiguites between a single-quote used to quote a list and a single-quote used to quote a character literal. For example:
    '('a' 'b')
Is that a quoted list containing two character literals, or a left-paren character literal followed by garbage? Liblmin does a little look-ahead to detect this case and interprets it as the list with two charlits one. But what about this:
    '('a 'b)
Is that a quoted list containing two quoted atoms, or a left-paren charlit followed by garbage? This one is harder to detect. It's also not likely to show up in real input, since everything inside a quoted list is already quoted. However if you really need to do this, either of these will work:
    '( 'a 'b)
    (quote ('a 'b))

Executable forms inside a vector literal are not evaluated, as with a quoted list. If you do want them evaluated, create the vector using the (vector) primitive instead. Like so:
    > [1 (+ 2 3) 4]           ; not evaluated
    [1 (+ 2 3) 4]
    > (vector 3 1 (+ 2 3) 4)  ; evaluated
    [1 5 4]

AUTHOR
Copyright (C) 2026 by Jef Poskanzer <jef@mail.acme.com>. All rights reserved.