{"id":26577190,"url":"https://github.com/john-z-yang/lisp","last_synced_at":"2025-10-17T15:11:34.507Z","repository":{"id":53848154,"uuid":"521691601","full_name":"john-z-yang/lisp","owner":"john-z-yang","description":"Lisp bytecode compiler/interpreter","archived":false,"fork":false,"pushed_at":"2023-12-16T05:39:13.000Z","size":9820,"stargazers_count":7,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2023-12-16T07:39:16.882Z","etag":null,"topics":["compiler","cpp","interpreter","lisp","lisp-interpreter"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/john-z-yang.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2022-08-05T15:38:22.000Z","updated_at":"2023-12-16T07:39:16.883Z","dependencies_parsed_at":"2023-11-19T22:23:30.536Z","dependency_job_id":"a901b7a7-37fa-451f-b94e-69a5d96239f1","html_url":"https://github.com/john-z-yang/lisp","commit_stats":null,"previous_names":[],"tags_count":0,"template":null,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/john-z-yang%2Flisp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/john-z-yang%2Flisp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/john-z-yang%2Flisp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/john-z-yang%2Flisp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/john-z-yang","download_url":"https://codeload.github.com/john-z-yang/lisp/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245052649,"owners_count":20553162,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["compiler","cpp","interpreter","lisp","lisp-interpreter"],"created_at":"2025-03-23T03:36:50.363Z","updated_at":"2025-10-17T15:11:29.464Z","avatar_url":"https://github.com/john-z-yang.png","language":"C++","readme":"# Lisp Interpreter \u0026middot; [![build](https://github.com/john-z-yang/lisp/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/john-z-yang/lisp/actions/workflows/ci.yml)\n\nInterpreter for a subset of [R4RS](https://people.csail.mit.edu/jaffer/r4rs_toc.html) Scheme written in C++. Implemented through a compiler and a virtual machine. (See [Technical Overview section](#technical-overview) for more details.)\n\n\u003cp align=\"center\"\u003e\n    \u003cimg src=\"https://raw.githubusercontent.com/john-z-yang/lisp/master/docs/assets/fib_range.png\" style=\"max-width:768px;  width:100%;\"\u003e\n\u003c/p\u003e\n\n## Getting Started\n\nThese instructions will give you a copy of the interpreter up and running on\nyour machine.\n\n### Prerequisites\n\n- [g++](https://gcc.gnu.org/), or any C++ compiler that supports [C++20](https://en.cppreference.com/w/cpp/20) and [computed goto](https://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html)\n- [make](https://www.gnu.org/software/make/)\n\n### Installing\n\nClone this repository.\n\n```bash\ngit clone https://github.com/john-z-yang/lisp\n```\n\nBuild the project. (**Note**: the default recipe will compile with sanitizers ([`ASan`](https://clang.llvm.org/docs/AddressSanitizer.html) and [`UBSan`](https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html)\\) which will hinder performance, use `CXXFLAGS_ASAN` variable to control them).\n\n```bash\nmake\n```\n\nThe executable (`lisp`) will be in the `bin` directory.\n\nExecute without argument to run in interactive mode.\n\n```console\nfoo@bar:~$ bin/lisp\nLisp (C++ std: 202002, Nov 30 2022, 13:49:06)\nType \"(quit)\" or trigger EOF to exit the session.\nlisp\u003e (quote (Hello World!))\n(Hello World!)\nlisp\u003e (quit)\nFarewell.\nfoo@bar:~$\n```\n\nSupply the file name of a lisp script as the argument to run them.\n\n```console\nfoo@bar:~$ echo '(display (quote (Hello World!)))' \u003e hello_world.lisp\nfoo@bar:~$ bin/lisp hello_world.lisp\n(Hello World!)\nfoo@bar:~$\n```\n\nNote: if you like to use the functions defined in `lisp/lib/`. You will need to set the `LISP_LIB_ENV` environment variable to its absolute path.\n\n```console\nfoo@bar:~$ export LISP_LIB_ENV=/PATH/TO/lisp/lib\n```\n\n_Happy hacking!_\n\n## Technical Overview\n\nThe interpreter is implemented through a [bytecode](https://en.wikipedia.org/wiki/Bytecode) compiler and [stack-based virtual machine](https://en.wikipedia.org/wiki/Stack_machine).\n\n```mermaid\nflowchart TB\n  repl[REPL]\n  vm[Virtual Machine]\n  subgraph compiler[Compiler\u0026nbsp\u0026nbsp\u0026nbsp\u0026nbsp\u0026nbsp\u0026nbsp\u0026nbsp\u0026nbsp\u0026nbsp\u0026nbsp\u0026nbsp\u0026nbsp\u0026nbsp\u0026nbsp\u0026nbsp\u0026nbsp\u0026nbsp\u0026nbsp\u0026nbsp\u0026nbsp\u0026nbsp\u0026nbsp\u0026nbsp\u0026nbsp\u0026nbsp\u0026nbsp\u0026nbsp\u0026nbsp\u0026nbsp\u0026nbsp\u0026nbsp\u0026nbsp\u0026nbsp\u0026nbsp\u0026nbsp\u0026nbsp\u0026nbsp\u0026nbsp\u0026nbsp\u0026nbsp\u0026nbsp\u0026nbsp\u0026nbsp\u0026nbsp\u0026nbsp\u0026nbsp\u0026nbsp\u0026nbsp\u0026nbsp\u0026nbsp\u0026nbsp\u0026nbsp\u0026nbsp\u0026nbsp\u0026nbsp\u0026nbsp\u0026nbsp\u0026nbsp\u0026nbsp\u0026nbsp\u0026nbsp\u0026nbsp]\n    direction TB\n    tok[Tokenization]\n    par[Parsing]\n    macro[Macro expansion]\n    byte[Bytecode generation]\n\n    tok -- Token --\u003e par\n    par -- S-expression --\u003e macro\n    macro -- S-expression --\u003e byte\n  end\n\n  repl -- std::string --\u003e tok\n  byte -- Bytecode --\u003e vm\n  macro -- Macro invocation --\u003e vm\n```\n\nThe bytecode compiler supports compliation of the following 7 primitive special forms:\n\n- Atomic values\n- Symbol assignments\n- Ternary if expressions\n- Function calls\n- Lambda expressions\n- Macro expansions\n- Macro definitions\n\nAll other syntaxes are defined as macros, and are lowered/desugared into the 7 special forms. (see the `/lib` directory)\n\n### Example\n\nSource code\n\n```lisp\n(define fac\n  (lambda (n)\n    (cond ((or (not (number? n)) (\u003c n 0))\n           (display \"Invalid argument for fac\"))\n          ((= n 0)\n           1)\n          (else\n           (* n (fac (- n 1)))))))\n```\n\n\u003cdetails\u003e\n\u003csummary\u003eClick to see the lowered/desugared S-expression (after macro expansion)\u003c/summary\u003e\n\n```lisp\n(define fac\n  (lambda (n)\n    (if ((lambda (#:gensym-100) (if #:gensym-100 #:gensym-100 (\u003c n 0))) (not (number? n)))\n        (begin (display \"Invalid argument for fac\"))\n    (if (= n 0)\n        (begin 1)\n    (if #t\n        (begin (* n (fac (- n 1))))\n    '())))))\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eClick to see the disassembly (after compilation)\u003c/summary\u003e\n\n```lisp\n\u003cClosure at 0x107cfcd50\u003e, instance of:\n    \u003cFunction at 0x10b0ae920, arity: 1, upvalues: 0\u003e\nConstants:\n    \u003cFunction at 0x10b09bd20\u003e, not, number?, display, \"Invalid argument for fac\", =, 0, 1, #t, *, fac, -, 1, ()\nBytecodes (raw):\n    11 00 00 01 01 07 01 07 02 0c 01 01 01 01 01 01\n    01 0f 00 0b 11 03 07 03 06 04 01 01 0e 00 30 07\n    05 0c 01 06 06 01 02 0f 00 07 11 03 06 07 0e 00\n    1e 06 08 0f 00 17 11 03 07 09 0c 01 07 0a 07 0b\n    0c 01 06 0c 01 02 01 01 01 02 0e 00 02 06 0d 0d\n    00 05 01 04 03 02\nBytecodes:\n    2                  0 MAKE_NIL\n    3                  1 MAKE_CLOSURE            \u003cFunction at 0x10b09bd20\u003e\n                                                 LOCAL 1\n    3                  5 LOAD_SYM                not\n    3                  7 LOAD_SYM                number?\n    3                  9 LOAD_STACK              1\n    3                 11 CALL                    1\n    3                 13 CALL                    1\n    3                 15 CALL                    1\n    3                 17 POP_JUMP_IF_FALSE       11\n    7                 20 MAKE_NIL\n    4                 21 POP_TOP\n    4                 22 LOAD_SYM                display\n    4                 24 LOAD_CONST              \"Invalid argument for fac\"\n    4                 26 CALL                    1\n    4                 28 JUMP                    48\n    5                 31 LOAD_SYM                =\n    5                 33 LOAD_STACK              1\n    5                 35 LOAD_CONST              0\n    5                 37 CALL                    2\n    5                 39 POP_JUMP_IF_FALSE       7\n    7                 42 MAKE_NIL\n    6                 43 POP_TOP\n    6                 44 LOAD_CONST              1\n    6                 46 JUMP                    30\n    7                 49 LOAD_CONST              #t\n    7                 51 POP_JUMP_IF_FALSE       23\n    7                 54 MAKE_NIL\n    8                 55 POP_TOP\n    8                 56 LOAD_SYM                *\n    8                 58 LOAD_STACK              1\n    8                 60 LOAD_SYM                fac\n    8                 62 LOAD_SYM                -\n    8                 64 LOAD_STACK              1\n    8                 66 LOAD_CONST              1\n    8                 68 CALL                    2\n    8                 70 CALL                    1\n    8                 72 CALL                    2\n    8                 74 JUMP                    2\n    7                 77 LOAD_CONST              ()\n    3                 79 SET_STACK               0\n    3                 81 CLOSE_UPVALUE           1\n    3                 83 POP                     3\n    3                 85 RETURN\n\n\u003cFunction at 0x10b09bd20, arity: 1, upvalues: 1\u003e\nConstants:\n    \u003c, 0\nBytecodes (raw):\n    11 0c 01 0f 00 05 0c 01 0e 00 08 07 00 0a 00 06\n    01 01 02 0d 00 04 03 02\nBytecodes:\n    3                  0 MAKE_NIL\n    3                  1 LOAD_STACK              1\n    3                  3 POP_JUMP_IF_FALSE       5\n    3                  6 LOAD_STACK              1\n    3                  8 JUMP                    8\n    3                 11 LOAD_SYM                \u003c\n    3                 13 LOAD_UPVALUE            0\n    3                 15 LOAD_CONST              0\n    3                 17 CALL                    2\n    3                 19 SET_STACK               0\n    3                 21 POP                     3\n    3                 23 RETURN\n```\n\n\u003c/details\u003e\n\n### File structure\n\n```text\n.\n├── bin                # Compiled binaries\n├── docs               # Supporting documentation\n├── lib                # Non-native Syntax/functions\n├── src/\n│   ├── code           # Source for Opcodes / Code Objects\n│   ├── compile        # Source for compiler\n│   ├── error          # Source for runtime/syntax errors\n│   ├── fn             # Source for native functions\n│   ├── repl           # Source for interactive environment\n│   ├── runtime        # Source for VM\n│   └── sexpr          # Source for s-expressions\n└── tests              # Tests\n```\n\n## Running the tests\n\n```bash\nmake test\n```\n\nTests are defined in the `tests` directory. Each test suite is a pair of lisp code (`.lisp`) and its expected output (`.expect`).\n\nThe `test` command runs the `.lisp` file and generates a `.out` file by redirecting `stdout`. Finally, it `diff`s the `.out` file against the `.expect` file.\n\n### Sample test suite (`combine`)\n\nLisp code (`combine.lisp`)\n\n```lisp\n(define combine\n  (lambda (f)\n    (lambda (x y)\n      (if (null? x) (quote ())\n        (f (list (car x) (car y))\n           ((combine f) (cdr x) (cdr y)))))))\n\n(define zip (combine cons))\n\n(display (zip (list 1 2 3 4) (list 5 6 7 8)))\n```\n\nWhen executed, it should behave like this\n\n```console\nfoo@bar:~$ bin/lisp combine.lisp\n((1 5) (2 6) (3 7) (4 8))\nfoo@bar:~$\n```\n\nSo we create the `.expect` file for expected output (`tests/combine.expect`).\n\n```lisp\n((1 5) (2 6) (3 7) (4 8))\n```\n\nAdd the new test to the `TESTS` variable in `makefile`.\n\n```make\nTESTS = $(TESTDIR)/combine # Along with other tests.\n```\n\nTests will be executed from `make test`.\n\n```make\ntest: $(TESTS)\n\n$(TESTDIR)/%: $(TESTDIR)/%.lisp $(TESTDIR)/%.expect $(OUTDIR)/lisp\n    (export LISP_LIB_ENV=$(LIBDIR); $(OUTDIR)/lisp $@.lisp \u003e\u003e $@.out 2\u003e\u00261)\n    diff $@.expect $@.out\n    rm $@.out\n```\n\n## Author\n\n- **John Yang** - [john-z-yang](https://github.com/john-z-yang)\n\nSee also the list of\n[contributors](https://github.com/john-z-yang/lisp/contributors)\nwho participated in this project.\n\n## Acknowledgments\n\n- [(How to Write a (Lisp) Interpreter (in Python))](http://www.norvig.com/lispy.html) by [Peter Norvig](https://norvig.com/)\n- [Crafting Interpreters](https://craftinginterpreters.com) by [Bob Nystrom](https://journal.stuffwithstuff.com/)\n- Special thanks to [Sophie](https://github.com/yqstan) for pointing out that parameter eval order is different across C++ compiler implementations.\n- [Stackoverflow: What is a trampoline function?](https://stackoverflow.com/questions/189725/what-is-a-trampoline-function) Question by [Benoit](https://stackoverflow.com/users/10703/benoit), Solutions by toyvo (no longer active) and [Piotr Kukielka](https://stackoverflow.com/users/704905/piotr-kukielka).\n- [By example: Continuation-passing style in JavaScript](https://matt.might.net/articles/by-example-continuation-passing-style/) by [Matt Might](https://matt.might.net/)\n- [How to compile with continuations](https://matt.might.net/articles/cps-conversion/) by [Matt Might](https://matt.might.net/)\n\u003cbr\u003e\n\n---\n\u003cp align=center\u003e\n\u003cimg src=\"https://imgs.xkcd.com/comics/lisp_cycles.png\"\u003e\n\u003c/p\u003e\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjohn-z-yang%2Flisp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjohn-z-yang%2Flisp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjohn-z-yang%2Flisp/lists"}