A practical metaprogramming language
What is Lisp ?
Lisp is a family of programming languages based on lambda calculus and symbolic metaprogramming.
The main appeal of Lisp is being able to edit the language itself and extend the syntax. This allows Lisp users to implement abstractions such as OOP themselves instead of waiting for the language implementation to evolve.
What's metaprogramming ?
Metaprogramming means writing programs that write programs. This is what Lisp users do when they extend the language. In the case of Antilisp, this also refers to writing programs with the sole purpose of generating code automatically.
Easy to learn. Easy to use.
(for (i (range 10))
(print i))
; -> 0 1 2 3 4 5 6 7 8 9
Antilisp is based on modern abstractions from modern languages.
This makes the language easy to learn even if you never used Lisp.
(print (list 1 2 3)) ; -> (1 2 3)
Lisp is a high level dynamically typed language, like Python or Javascript.
The power of Lisp
(defmacro (let name value . body)
`((fn (,name)
,@body)
,value))
; JS equivalent:
; (function(x) {"{"}
; f(x);
; g(x);
; {"}"})(preload_stuff())
(let x (preload-stuff)
(f x)
(g x))
Lisp has a powerful syntax expansion system. You can define your own constructs to match your needs.
Try it for your next task. No technical debt if you don't like it.
; example from https://docs.github.com/en/actions/writing-workflows/quickstart
(def (echo text)
; (sum) uses the object-oriented (+) and supports any type for which (+) is defined
(+ "echo " (yaml-quote text)))
;(print (echo "message"))
; -> echo "message"
(save-workflow ".github/workflows/github-actions-demo.yml"
(workflow "GitHub Actions Demo" "push"
;; the run-name field is not implemented in the library:
;'run-name "${{ github.actor }} is testing out GitHub Actions 🚀"
; declare the jobs
(job "Explore-GitHub-Actions" "ubuntu-latest"
; as you can see, the syntax is very easy to understand
(run "echo "🎉 The job was automatically triggered by a ${{ github.event_name }} event."")
; but escaping quotes to nest the echo parameter is annoying
(run "echo "🐧 This job is now running on a ${{ runner.os }} server hosted by GitHub!"")
; let's abstract it:
(run (echo "🔎 The name of your branch is ${{ github.ref }} and your repository is ${{ github.repository }}."))
(use
; optional fields can be set with keyword (named) arguments
'name "Check out repository code"
"actions/checkout@v4")
(run (echo "💡 The ${{ github.repository }} repository has been cloned to the runner."))
(run (echo "🖥️ The workflow is now ready to test your code on the runner."))
(run
'name "List files in the repository"
"ls ${{ github.workspace }}")
(run (echo "🍏 This job's status is ${{ job.status }}.")))))
Antilisp is designed for cross-language code manipulation.
Apply the power of Lisp metaprogramming to any other language to get the job done in record time.
cat .github/workflows/github-actions-demo.yml
name: GitHub Actions Demo
on: [push]
jobs:
Explore-GitHub-Actions:
runs-on: ubuntu-latest
steps:
- run: "echo "🎉 The job was automatically triggered by a ${{ github.event_name }} event.""
- run: "echo "🐧 This job is now running on a ${{ runner.os }} server hosted by GitHub!""
- run: "echo "🔎 The name of your branch is ${{ github.ref }} and your repository is ${{ github.repository }}.""
- uses: "actions/checkout@v4"
name: "Check out repository code"
- run: "echo "💡 The ${{ github.repository }} repository has been cloned to the runner.""
- run: "echo "🖥️ The workflow is now ready to test your code on the runner.""
- run: "ls ${{ github.workspace }}"
- run: "echo "🍏 This job's status is ${{ job.status }}.""
Once your source code is generated, you can choose to stop using Antilisp and forget about it. Just edit the code you generated directly.
Ultimate flexibility
; (char-parser "my source code" index) -> (new-index . char) | nil
(def char-parser (source position)
(if (is-eof source position)
nil
(cons (+ 1 position) (get-char source position))))
; add the parser to the set of value readers used by the language
(set value-readers (cons char-parser value-readers))
Antilisp is built on the idea that the user should have complete control.
Antilisp lets you extend or override the parser to change how your code is read by the interpreter in real time. You are in full control of the tools you use to build software.
Note: custom readers are not usable yet
Self-documented
(print (repr current-scope))
-> (env ()
true true
false false
nil nil
type <type type>
integer <type int>
string <type str>
...
to-string (fn (x) ...)
repr (fn (x) ...)
...
get-doc <native function get_doc>
set-doc <native function set_doc>
...
method-store (dict
repr ...
add ...
length ...
at ...
set-at ...
iter ...
to-symbol ...
to-string ...
in ...)
...)
(print (get-doc method-store))
-> internal dictionary used by generics to store and retrieve methods
(print (repr (at (get-metadata let) "source")))
-> (defmacro (let name value . body)
(quasiquote
((fn ((unquote name))
(set-symbol (quote (unquote name)) (unquote value))
(unquote-splicing body)) nil)))
Antilisp exposes everything to the user. Functions, classes, code and even documentation are first-class citizens.
If you want to inspect something, you can get its literal representation with (repr), or use reflection and consult the documentation with (get-doc) and the source code with (get-metadata).