s-expression-interpreter

star 2

Build and understand s-expression interpreters in Python and C. Covers lexer-free tokenization, recursive descent parsing, eval-apply cycles, SymbolTable scoping, lval heap allocation with mpc, sfsexp library integration, and language semantics across Scheme (R4RS/R7RS), Common Lisp, and Clojure. Use when building a Lisp/Scheme interpreter from scratch, implementing s-expression data structures in C, adding function definitions and lexical scoping to an evaluator, or understanding how homoiconic languages process code-as-data through read-eval-print loops.

tangledgroup By tangledgroup schedule Updated 6/11/2026

name: s-expression-interpreter description: Build and understand s-expression interpreters in Python and C. Covers lexer-free tokenization, recursive descent parsing, eval-apply cycles, SymbolTable scoping, lval heap allocation with mpc, sfsexp library integration, and language semantics across Scheme (R4RS/R7RS), Common Lisp, and Clojure. Use when building a Lisp/Scheme interpreter from scratch, implementing s-expression data structures in C, adding function definitions and lexical scoping to an evaluator, or understanding how homoiconic languages process code-as-data through read-eval-print loops.

S-Expressions Interpreter

Overview

An s-expression interpreter reads parenthesized prefix notation (s-expressions), parses them into an internal representation, evaluates the resulting structure through a recursive eval-apply cycle, and prints the result. The same syntax represents both code and data — this property, called homoiconicity, is what enables Lisp-family languages to manipulate programs as data structures.

This skill covers three implementation approaches:

  • Python interpreters — from 16-line proofs-of-concept to full implementations with function definitions, lexical scoping, and special forms
  • C s-expression libraries — production parsing/manipulation (sfsexp) and educational heap-based lval structures with mpc parsing
  • Language semantics — how Scheme, Common Lisp, and Clojure define functions, handle arguments, and evaluate expressions

When to Use

  • Building a Lisp or Scheme interpreter from scratch in Python or C
  • Implementing s-expression parsing without external lexer/parser libraries
  • Adding lexical scoping and function definitions to an existing evaluator
  • Understanding the eval-apply cycle that powers all Lisp implementations
  • Integrating sfsexp for s-expression data handling in C/C++ programs
  • Translating idioms between Python, Scheme, Common Lisp, and Clojure
  • Studying how homoiconic languages separate reading from evaluation

Core Concepts

Homoiconicity: Code is data. The expression (+ 1 2) is simultaneously a computation (returns 3) and a data structure (a list of three elements). Interpreters exploit this by manipulating programs as lists before evaluating them.

S-expression structure: Every s-expression is either an atom (number, symbol, string) or a list of s-expressions in parentheses. This recursive definition represents any finite tree exactly.

Tokenization without lexers: Lisp's parenthesized syntax allows trivial tokenization — pad parentheses with whitespace and split. No complex lexer needed: input.replace('(', ' ( ').replace(')', ' ) ').split().

Recursive descent parsing: Walk the token stream, building nested lists on ( and returning atoms otherwise. The parser output is the AST — no separate transformation step required.

Eval-apply cycle: eval takes an expression and environment, returns a value. For procedure calls, it evaluates the operator and all arguments, then apply creates a new environment frame binding parameters to arguments and evaluates the body. This recursion continues until expressions reduce to atoms.

Lexical scoping: Functions capture the environment where they were defined. Environments form a chain of frames; variable lookup walks from innermost outward. This enables closures — functions that remember their defining context.

Special forms: Certain operators (if, define, lambda, and, or) do not evaluate all arguments before execution. They control evaluation order and must be handled as explicit cases in the evaluator.

Advanced Topics

Python Interpreter Implementations

C S-Expression Implementations

Scheme Language Semantics

Common Lisp and Clojure Semantics

Install via CLI
npx skills add https://github.com/tangledgroup/tangled-skills --skill s-expression-interpreter
Repository Details
star Stars 2
call_split Forks 0
navigation Branch main
article Path SKILL.md
More from Creator
tangledgroup
tangledgroup Explore all skills →