diff options
author | Daniel Cerqueira <dan.git@lispclub.com> | 2025-05-28 12:37:55 +0100 |
---|---|---|
committer | Daniel Cerqueira <dan.git@lispclub.com> | 2025-05-28 12:37:55 +0100 |
commit | 1c217ee6ac5aacd2f37b1d0e69a71ac94afd5acd (patch) | |
tree | a808042b0a0c4ea180c128cc49e2198bb29850a4 /README.md |
Init lali programming language.
Diffstat (limited to 'README.md')
-rw-r--r-- | README.md | 377 |
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! |