Skip to content

chestso/ditty

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

199 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Ditty Lisp

An embeddable Lisp interpreter library and syntax highlighter written in C. This implementation follows traditional Lisp naming conventions and provides a REPL for testing and demonstration.

Documentation:

  • Language Guide β€” Conceptual guide covering evaluation, special forms, macros, conventions, and more
  • Built-in Function Reference β€” Auto-generated per-function documentation with parameters, return values, and examples
  • Embedding Guide β€” C API reference, tagged-pointer object representation, and Boehm GC rules for embedders

Features

Core Language

  • Data Types: Numbers, integers, booleans, strings (UTF-8), characters, symbols, keywords, lists, vectors, hash tables, lambdas, errors, regex, string ports, file streams
  • Special Forms: quote, quasiquote, if, define, set!, lambda, defmacro, let/let*, progn, do, cond, case, and, or, condition-case, unwind-protect
  • Reader Syntax: pkg:symbol qualified access (resolved at eval time via env_lookup_in_package)
  • Function Parameters: Required, optional (&optional), and rest (&rest) parameters with lambda
  • Macros: Code transformation with defmacro, quasiquote (`), unquote (,), unquote-splicing (,@), and built-in defun, defvar, defconst, defalias macros
  • Functions: Arithmetic, strings, lists, vectors, hash tables, regex (PCRE2), file I/O, string ports, filesystem, packages, profiling
  • Type Predicates: null?, atom?, pair?, list?, integer?, boolean?, number?, string?, char?, symbol?, keyword?, vector?, hash-table?, function?, callable?, error?, regex?

See LANGUAGE_GUIDE.md for concepts and BUILTIN_REFERENCE.md for complete function listings and examples.

Advanced Features

  • Package System: Symbol-based namespace management with in-package, current-package, and pkg:symbol qualified access syntax
  • Library System: Load-once library loading with require/provide, export/use-package for selective imports, and DITTY_LISP_PATH + XDG-based search paths
  • Condition System: Emacs Lisp-style error handling with signal, condition-case, unwind-protect, and error introspection
  • Tail Call Optimization: Trampoline-based tail recursion enables efficient recursive algorithms without stack overflow
  • Lexical Scoping: First-class functions and closures with captured environments
  • Regex Support: Full PCRE2 integration for advanced pattern matching with UTF-8 support
  • UTF-8 Support: Full Unicode string support with codepoint-based operations
    • length, substring, and string-ref use codepoint indexing
    • Handles multi-byte characters correctly (CJK, emoji, etc.)
  • EOL-Aware File I/O: File streams auto-detect their line-ending style (LF or CRLF) on open-for-read and transparently preserve it on write, Emacs Lisp style. write-string and write-line translate any embedded \n to the stream's stored EOL, so tools like source formatters can round-trip Windows files without any manual \r handling.
  • Memory Management: Automatic garbage collection with Boehm GC
  • Semantic Classification: The lisp_sf_kind() API classifies special forms by role (quote, define, control, macro-def, binding, body, exception) β€” used by Flare for accurate highlighting without hardcoded keyword lists

Flare β€” Syntax Highlighting

Flare is a built-in syntax highlighting module (compiled into libditty.a) that produces ANSI terminal output at 8-color, 16-color, 256-color, and truecolor depths.

  • Runtime-driven classification: Uses the ditty Environment as the single source of truth for symbol classification β€” no parallel hardcoded keyword lists
  • Two lexers: Ditty Lisp and CommonMark/Markdown (fenced code blocks with lisp/ditty info strings are sub-lexed through the Lisp lexer)
  • Four built-in styles: Dracula, Monokai, GitHub Dark, GitHub Light
  • Modular pipeline: Lex β†’ Style lookup β†’ Format, each stage pluggable
  • flare CLI: A cat-like tool that highlights source files to the terminal, with auto-detection of file language

Quick Start

; Arithmetic and variables
(define x 10)
(+ x 20)                             ; => 30

; Functions
(define square (lambda (n) (* n n)))
(square 5)                           ; => 25

; Named functions (defun macro)
(defun cube (n) (* n n n))
(cube 3)                             ; => 27

; Optional and rest parameters
(defun flex (a &optional b &rest rest)
  (list a b rest))
(flex 1 2 3 4 5)                     ; => (1 2 (3 4 5))

; Lists
(map (lambda (x) (* x 2)) '(1 2 3))  ; => (2 4 6)
(filter even? '(1 2 3 4 5 6))        ; => (2 4 6)

; Strings with Unicode
(length "Hello, δΈ–η•Œ! 🌍")            ; => 12

; Conditionals
(if (> 10 5) "yes" "no")             ; => "yes"

; Error handling
(condition-case err
  (/ 1 0)
  (error (format nil "caught: ~A" (error-message err))))

For full documentation, see LANGUAGE_GUIDE.md (concepts) and BUILTIN_REFERENCE.md (function reference)

Building

Prerequisites

Install the required dependencies:

Linux (Debian/Ubuntu):

sudo apt-get install libgc-dev libpcre2-dev autoconf automake

Linux (Fedora 41+):

sudo dnf install gc-devel pcre2-devel autoconf automake

macOS:

brew install bdw-gc pcre2 autoconf automake

Build from Source

Pure GNU Autotools β€” no wrapper script:

./autogen.sh
mkdir build && cd build
PKG_CONFIG_PATH=$HOME/.local/lib/pkgconfig:$HOME/.local/lib64/pkgconfig \
  ../configure --prefix=$HOME/.local
make -j$(nproc)
make check          # Run the test suite
make install        # Install library, headers, REPL, pkg-config file

Useful targets (from build/):

  • make β€” build libditty.a (includes Flare) and flare and cli/ditty
  • make check β€” run the test suite
  • make install β€” install library, headers, REPL, flare, pkg-config files
  • make format β€” clang-format on C, shfmt on shell, prettier on Markdown, lisp-fmt on Lisp
  • make bear β€” produce compile_commands.json for clangd

The default build enables AddressSanitizer and UndefinedBehaviorSanitizer. Use --enable-release for an optimized production build or --enable-debug for an unsanitized debug build (-O0 -g3).

Windows

Ditty builds natively on Windows using MSYS2/UCRT64. The #ifdef _WIN32 guards in the source handle Windows-specific APIs (file paths, timing, environment variables). The interactive REPL requires boba, which also builds natively on Windows via MSYS2.

# In an MSYS2 UCRT64 shell:
./autogen.sh
mkdir build && cd build
../configure --prefix=$HOME/.local
make -j$(nproc)
make check
make install

Emacs Major Mode

An Emacs major mode for ditty source (ditty-mode.el) lives in emacs/ and is installed by make install to $(datadir)/emacs/site-lisp/ditty/. See the ;;; Commentary: section at the top of emacs/ditty-mode.el for installation and usage instructions.

Installation

sudo make install

This installs:

  • ditty executable
  • flare executable (syntax highlighting CLI)
  • libditty.a static library (interpreter + syntax highlighting)
  • Header files to $(includedir)/ditty/ (including highlight.h for Flare)
  • ditty.pc pkg-config file
  • lisp-fmt.lisp Lisp source formatter

Usage

Command Line Options

ditty                      # Start interactive REPL
ditty -e "CODE"            # Execute CODE and exit
ditty FILE [FILE...]       # Execute FILE(s) and exit
ditty FILE -- [ARG...]     # Run FILE with script arguments
ditty -h, --help           # Show help message

flare β€” Syntax Highlighting CLI

flare FILE                        # Highlight FILE to stdout (truecolor + dracula)
flare -f 256 -s monokai FILE      # 256-color monokai
flare -l commonmark README.md    # Highlight Markdown
flare -                           # Read stdin
flare --help                      # Show options

Options: -f/--format (truecolor, 256, 16, 8), -s/--style (dracula, monokai, github-dark, github-light), -l/--language (auto, ditty, commonmark/markdown). Auto-detection selects by file extension.

REPL Mode

./cli/ditty

The REPL uses boba for inline terminal rendering β€” no alternate screen, output flows into the terminal's own scrollback. Syntax highlighting is applied to the input as you type via the Flare highlighting engine.

Features:

  • Live syntax highlighting - Input is highlighted as you type using Flare
  • Inline rendering - No alt-screen takeover; output appears in normal scrollback
  • Multi-line editing - Incomplete forms get continuation lines with auto-indent
  • Eval on Enter - Enter evaluates when the form is complete; inserts a newline otherwise
  • Input history - Arrow keys navigate previous inputs
  • Tab completion - Completes symbol names from the environment
  • Ctrl+C - Aborts the current edit and starts a fresh prompt
  • Ctrl+D - Exits the REPL on empty input; deletes char under cursor otherwise

REPL Commands:

  • /quit - Exit the REPL
  • /load <filename> - Load and execute a Lisp file

Command-Line Execution

./cli/ditty -e "(+ 1 2 3)"                               # => 6
./cli/ditty -e "(map (lambda (x) (* x 2)) '(1 2 3 4 5))" # => (2 4 6 8 10)
./cli/ditty -e '(concat "hello" " " "world")'            # => "hello world"

Exit code is 0 on success, 1 on error.

Running Files

./cli/ditty script.lisp

Embedding

The library is self-contained and can be integrated into any C project:

#include <ditty/lisp.h>

int main() {
    Environment* env = lisp_init();

    LispObject* result = lisp_eval_string("(+ 1 2 3)", env);
    char* output = lisp_print(result);
    printf("%s\n", output);  // Prints: 6

    lisp_cleanup();
    return 0;
}

For the full C API reference, object representation, GC rules, and integration options (pkg-config, subproject, vendoring), see EMBEDDING.md.

Authors

  • Thomas Christensen

License

MIT License