summaryrefslogtreecommitdiff
path: root/README.md
diff options
context:
space:
mode:
authorDaniel Cerqueira <dan.git@lispclub.com>2025-05-28 12:37:55 +0100
committerDaniel Cerqueira <dan.git@lispclub.com>2025-05-28 12:37:55 +0100
commit1c217ee6ac5aacd2f37b1d0e69a71ac94afd5acd (patch)
treea808042b0a0c4ea180c128cc49e2198bb29850a4 /README.md
Init lali programming language.
Diffstat (limited to 'README.md')
-rw-r--r--README.md377
1 files changed, 377 insertions, 0 deletions
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..d193110
--- /dev/null
+++ b/README.md
@@ -0,0 +1,377 @@
+<!--
+lali (Lali Another Lisp Implementation)
+
+Author: Daniel Cerqueira (dan.git@lispclub.com)
+Maintainer: Daniel Cerqueira (dan.git@lispclub.com)
+Version: 0.0
+Keywords: lali, lisp, implementation, interpreter, lisp1.5,
+ computer programming language
+Homepage: https://gitlab.com/alexandre1985/lali
+SPDX-License-Identifier: GFDL-1.3-or-later
+
+Copyright (C) 2025 Daniel Cerqueira
+
+This file is part of lali.
+
+Permission is granted to copy, distribute and/or modify this document under the
+terms of the GNU Free Documentation License, Version 1.3 or any later version
+published by the Free Software Foundation; with no Invariant Sections, no
+Front-Cover Texts, and no Back-Cover Texts.
+
+You should have received a copy of the GNU Free Documentation License along with
+this program; if not, see <https://www.gnu.org/licenses/>.
+
+
+lali software is based on tiny-lisp <https://github.com/matp/tiny-lisp/> version
+from 2016, written by Matthias Pirstitz, which is released to public domain under
+Unlicense <https://unlicense.org/>.
+-->
+
+# lali (Lali Another Lisp Implementation)
+
+lali is a small implementation of LISP, derived from tiny-lisp, written in
+standard C11. It has features such as macros, tail recursion and a copying garbage
+collector.
+
+lali extends and improves the original LISP. It tries to stay connected to the
+roots, while been greatly powerful. Also, aims at bringing joy to the programmer's
+mind. What you write, becomes part of who you are.
+
+
+## Language features
+
+* read-eval-print loop
+* interpretation from stdin or file arguments
+* numbers, strings, symbols and lists
+* functions and macros
+* lexical scoping
+* scheme-style dotted parameter lists
+* scheme-style tail recursion
+* copying garbage collector
+
+### Numeric operations
+
+lali supports the five arithmetic operations `+`, `-`, `*`, `/`, `%` (with `%` the
+operands are automatically converted to integer) as well as the relational
+operations `!` (different), `<`, `>`:
+
+ (- 5) ; -5
+ (- 5 3.2 .6) ; 1.2
+ (< 2 4 6.8) ; t
+ (> 4 8) ; f
+ (! 4 4) ; f
+
+#### Random number operation
+
+`(random [number])` takes no or one number argument, returns a random number below
+`number` until 0.
+
+ (random) ; 2.1442e+09
+ (random 2) ; returns 0 or 1
+
+### Processing operations
+
+`(quote x)`, or `'x`, returns an expression without being processed:
+
+ (quote a) ; a
+ 'b ; b
+ (quote (a b c)) ; (a b c)
+ '(a b c) ; (a b c)
+
+`(say x)`, or `\x`, returns an expression without being processed, and with the
+first `car` as `()`. This result is useful when processing a list and wanting to
+do something at the beginning, because other cons cells should never have `()` in
+`car`:
+
+ (say a) ; (() . a)
+ \b ; (() . b)
+ (say (a b c)) ; (() a b c)
+ \(a b c) ; (() a b c)
+
+### List operations
+
+`(list ...)` returns a list containing the supplied arguments:
+
+ (list) ; ()
+ (list 1 2 3) ; (1 2 3)
+ (list 1 2 (+ 1 2)) ; (1 2 3)
+
+`(cons x y)` creates a new cons cell, the `car` of which is `x` and the `cdr` of
+which is `y`:
+
+ (cons 1 2) ; (1 . 2)
+ (cons 1 '(2)) ; (1 2)
+
+`(car x)`, `(cdr x)` return the `car` and `cdr` of a list respectively:
+
+ (car '(1 2 3)) ; 1
+ (cdr '(1 2 3)) ; (2 3)
+
+### Predicates
+
+`(dif x y)` returns `t` if `x` and `y` refers to different objects, or if they are
+numbers with different values, or if they are string objects with different
+contents:
+
+ (dif 1 1) ; f
+ (dif 1 2) ; t
+ (dif 'a 'a) ; f
+ (dif 'a 'b) ; t
+ (dif t t) ; f
+ (dif t f) ; t
+ (dif '(1 2) '(1 2)) ; t
+
+`(differ x y)` returns `t` if `x` and `y` are objects of different structure and
+each of their elements satisfy the `dif` predicate, otherwise `f`:
+
+ (differ '(1 2) '(1)) ; t
+ (differ '(1 2) '(1 2)) ; f
+
+`(space x)` returns `t` if `x` is an empty list `()`, otherwise `f`:
+
+ (space ()) ; t
+ (space 1) ; f
+ (space t) ; f
+ (space '(1 2)) ; f
+
+`(ap x)` returns `t` if `x` is "an existing thing" (not an empty list `()`),
+otherwise `f`:
+
+ (ap ()) ; f
+ (ap 1) ; t
+ (ap t) ; t
+ (ap '(1 2)) ; t
+
+`(atom x)` returns `t` if `x` is an atom (and not an empty list `()`), otherwise
+`f`:
+
+ (atom ()) ; f
+ (atom 1) ; t
+ (atom t) ; t
+ (atom '(1 2)) ; f
+
+`(listp x)` returns `t` if `x` is either an empty list `()`, or a list with
+atom(s), otherwise `f`:
+
+ (listp ()) ; t
+ (listp 1) ; f
+ (listp t) ; f
+ (listp '(1 2)) ; t
+
+`(consp x)` returns `t` if `x` is a list with atom(s), otherwise `f`:
+
+ (consp ()) ; f
+ (consp 1) ; f
+ (consp t) ; f
+ (consp '(1 2)) ; t
+
+`(zerop x)` returns `t` if `x` is the number zero, otherwise `f`:
+
+ (zerop 0) ; t
+ (zerop 1) ; f
+ (zerop t) ; error: t is not a number
+
+### Logical operations
+
+`(not x)` returns `t` if `x` is `f`, return `n` if `x` is `n`, otherwise returns
+`f`.
+
+`(and ...)` evaluates its arguments one at a time from left to right. As soon as
+any argument evaluates to `f`, `and` returns `f` without evaluating the remaining
+arguments. Otherwise, it returns the value produced by evaluating its last
+argument. If no arguments are supplied, `and` returns `t`:
+
+ (and) ; t
+ (and 1 2 3) ; 3
+ (and 1 f 3) ; f
+
+`(or ...)` evaluates its arguments one at a time from left to right. As soon as
+any argument does not evaluate to `f`, `or` returns its value without evaluating
+the remaining arguments. Otherwise, it returns `f`:
+
+ (or) ; f
+ (or 1 2 3) ; 1
+ (or f f 3) ; 3
+
+### Conditionals
+
+`(cond ...)` takes zero or more clauses, each of the form `(test [expr...])`.
+`cond` returns the result of evaluating `expr...` of the first clause for which
+`test` does not evaluate to `f` without evaluating the remaining clauses. If a
+clause does not supply `expr...`, the result of evaluating `test` is returned
+instead. If every `test` evaluates to `f`, or no clauses are given, `cond` returns
+`n`:
+
+ (cond) ; n
+ (cond (f 'Hello)
+ (t 'World)) ; World
+ (cond (f 'Hello)
+ ('World)) ; World
+ (cond (t 'Hello)
+ ('World)) ; Hello
+
+`(fill ...)` is similar to `cond`. But `fill` differs in its evaluation of `test`.
+When `test` does evaluate to `f` returns the result of evaluating `expr...`
+without evaluating the remaining clauses:
+
+ (fill) ; n
+ (fill (f 'Hello)
+ (t 'World)) ; Hello
+ (fill (f 'Hello)
+ ('World)) ; Hello
+ (fill (t 'Hello)
+ ('World)) ; World
+
+### Grouping functions
+
+`(prog ...)` takes zero or more expressions, and evaluates each expression, one
+after the other. Returns the evaluation of the last expression. Returns `n`, if no
+expression is given:
+
+ (prog) ; n
+ (prog 'Hello
+ (atom f)) ; t
+ (prog 'Hello
+ n
+ (car 'World)) ; error: World is not a list
+
+`(progs expr ...)` is similar to `prog`. But `progs` differs when the evaluation
+of any expression is equal to the symbol of the evaluation of `expr`, stopping
+evaluation of further expressions and returning the symbol of the evaluation of
+`expr` immediately:
+
+ (progs n) ; n
+ (progs n
+ 'Hello
+ (atom f)) ; t
+ (progs n
+ 'Hello
+ n
+ (car 'World)) ; n
+
+### Defining variables
+
+`(set expr-var-A expr-val-A ...)` binds the result of evaluating `expr-val-A` to
+the symbol of `expr-var-A` evaluated. More than one `expr-var`-`expr-val` pair can
+be given. `set` returns the value bound to the last symbol:
+
+ (set 'a 1
+ 'b 2) ; 2
+ a ; 1
+ b ; 2
+
+### Defining functions
+
+`(lambda params expr...)` creates an anonymous function with parameters `params`
+and body `expr...`. `params` can be `()`, a symbol, or a list of symbols. If
+`params` is a symbol or a dotted list of symbols, the function will accept a
+variable number of arguments:
+
+ ((lambda ()
+ 'Hello)) ; Hello
+ ((lambda (a b)
+ (+ a b)) 1 2) ; 3
+ ((lambda (a b . c)
+ c) 1 2 3 4 5) ; (3 4 5)
+ ((lambda args
+ args) 1 2 3 4 5) ; (1 2 3 4 5)
+
+`(defun name params expr...)` creates a function and binds it to the symbol
+`name`:
+
+ (defun plus1 (x)
+ (+ x 1)) ; #<Lambda (x)>
+ (plus1 2) ; 3
+
+### Defining macros
+
+`(macro params expr...)` creates an anonymous macro with parameters `params` and
+body `expr...`.
+
+`(defmacro name params expr...)` creates a macro and binds it to the symbol
+`name`.
+
+Macros are different from functions in that they do not evaluate their arguments
+when called. Instead, we can think of them as taking expressions as input and
+returning a new expression as output.
+
+Imagine we want to implement `(when test expr...)`:
+
+ (set 'x '(1 2 3)) ; (1 2 3)
+ (when (consp x)
+ (car x)) ; 1
+ (set 'x 'hello)
+ (when (consp x)
+ (car x)) ; n
+
+`when`, if implemented as a function, would not work correctly, since `expr...`
+would be evaluated as soon as `when` is called:
+
+ (set 'x 'Hello)
+ (when (consp x)
+ (car x)) ; error: Hello is not a list
+
+However, we can implement `when` as a macro that wraps `expr...` in a call to
+`if`:
+
+ (defmacro when (test . expr)
+ (list cond (list test (cons 'prog expr))))
+
+`(when (consp x) (car x))` would then produce the expression
+`(cond ((consp x) (prog (car x))))` which yields the expected behaviour.
+
+### Input operation
+
+`(read)` takes zero arguments, and reads an expression from standard input:
+
+ (set 'a (read)) ; type (Hello World!) followed by an enter
+ a ; (Hello World!)
+
+### Eval operation
+
+`(eval expr)` takes one expression, and evaluates `expr`:
+
+ (eval t) ; t
+ (prog
+ (set 'a 'b)
+ (eval a)) ; error: b has no value
+ (prog
+ (set 'a 'b)
+ (eval 'a)) ; b
+ (eval '(atom 1)) ; t
+ (eval '(run it)) ; error: run has no value
+ (prog
+ (set 'a 'b)
+ (eval '(a it))) ; error: b is not a function
+
+### Output operations
+
+`(newline)` prints a newline to the standard output.
+
+`(print x)` prints `x` to the standard output, exactly the way it was entered:
+
+ (print '(Hello World!)) ; prints (Hello World!) followed by a space
+
+`(princ x)` is similar to `print`. But `princ` differs in its handling of lists
+outputting them without the initial/ending parenthesis:
+
+ (princ '(Hello World!)) ; prints Hello World! followed by a space
+
+### Time operation
+
+`(time)` gives a timestamp in (sec minute hour day month year dow dst utcoff).
+
+ (time) ; (23 54 17 13 5 2025 2 t 3600)
+
+## Copyright
+
+You can find the licenses of lali in the LICENSES/ directory.
+
+lali uses year ranges in its copyright text. For example, the year range
+"2025-2027" means years "2025, 2026, and 2027".
+
+
+## Contributions
+
+If you want to contribute, please send me (git) patches over email. My address is
+at the source's copyright notices. Thank you!