Update lisp doc and examples (#428)

* Update documentation

* Add macro?

* Rewrite and and or with macros

* Move string-join

* Update built-in autocompletion

* Use define instead of def in core and examples

* Add changelog to doc

* Move aliases to lib

* Add let macro

* Add caar cadr cdar cddr functions

* Add fixme

* Fix let macro
This commit is contained in:
Vincent Ollivier 2022-11-01 11:02:50 +01:00 committed by GitHub
parent 0f08b0838e
commit ab9b488a49
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 179 additions and 107 deletions

View File

@ -5,38 +5,70 @@ of the Shell.
MOROS Lisp is a Lisp-1 dialect inspired by Scheme and Clojure.
It started from [Risp](https://github.com/stopachka/risp) and was extended to
include the seven primitive operators and the two special forms of John
McCarthy's paper "Recursive Functions of Symbolic Expressions and Their
## Changelog
### 0.1.0 (2021-07-21)
MOROS Lisp started from [Risp](https://github.com/stopachka/risp) and was
extended to include the seven primitive operators and the two special forms of
John McCarthy's paper "Recursive Functions of Symbolic Expressions and Their
Computation by Machine" (1960) and "The Roots of Lisp" (2002) by Paul Graham.
In version 0.2.0 the whole implementation was refactored and the parser was
rewritten to use [Nom](https://github.com/Geal/nom). This allowed the addition
of strings to the language and reading from the filesystem.
### 0.2.0 (2021-12-04)
The whole implementation was refactored and the parser was rewritten to use
[Nom](https://github.com/Geal/nom). This allowed the addition of strings to the
language and reading from the filesystem.
### 0.3.0 (2022-12-12)
Rewrite the evaluation code, add new functions and a core library.
## Types
### 0.3.1 (2022-06-06)
Rewrite parts of the code and add new functions and examples.
### 0.3.2 (2022-07-02)
- Add new functions
### 0.3.2 (2022-08-25)
- Add new functions
### 0.4.0 (2022-08-25)
- Rewrite a lot of the code
- Add integer and big integer support
- Add tail call optimization (TCO)
- Add macro support
## Overview
### Types
- Basics: `bool`, `list`, `symbol`, `string`
- Numbers: `float`, `int`, `bigint`
## Seven Primitive Operators
### Built-in Operators
- `quote` (with the `'` syntax)
- `quasiquote` (with the `` ` ``)
- `unquote` (with the `,` syntax)
- `unquote-splicing` (with the `,@` syntax)
- `atom` (aliased to `atom?`)
- `eq` (aliased to `eq?`)
- `car` (aliased to `first`)
- `cdr` (aliased to `rest`)
- `cons`
- `if`
- `cond`
## Two Special Forms
- `label` (aliased to `define` and `def`)
- `lambda` (aliased to `function`, `fun`, and `fn`)
## Additional Builtins
- `defun` (aliased to `defn`)
- `set`
- `while`
- `set`
- `define` (aliased to `def` and `label`)
- `function` (aliased to `fun` and `lambda`)
- `macro` (aliased to `mac`)
- `define-function` (aliased to `def-fun`)
- `define-macro` (aliased to `def-mac`)
- `apply`
- `eval`
- `expand`
- `do` (aliased to `begin` and `progn`)
- `load`
### Primitive Operators
- `append`
- `type`
- `string`
- `string->number`
@ -44,20 +76,19 @@ of strings to the language and reading from the filesystem.
- `number->bytes` and `bytes->number`
- `regex-find`
- `system`
- `load`
- Arithmetic operations: `+`, `-`, `*`, `/`, `%`, `^`
- Trigonometric functions: `acos`, `asin`, `atan`, `cos`, `sin`, `tan`
- Comparisons: `>`, `<`, `>=`, `<=`, `=`
- Boolean operations: `not`, `and`, `or`
- String operations: `lines`
- File IO: `read-file`, `read-file-bytes`, `write-file-bytes`, `append-file-bytes`
## Core Library
### Core Library
- `nil`, `nil?`, `eq?`
- `atom?`, `string?`, `boolean?`, `symbol?`, `number?`, `list?`, `function?`, `lambda?`
- `first`, `second`, `third`, `rest`
- `map`, `reduce`, `append`, `reverse`
- `atom?`, `string?`, `boolean?`, `symbol?`, `number?`, `list?`, `function?`, `macro?`
- `caar`, `cadr`, `cdar`, `cddr`, `first`, `second`, `third`, `rest`
- `map`, `reduce`, `reverse`, `range`
- `let`
- `string-join`
- `read-line`, `read-char`
- `print`, `println`
@ -65,6 +96,8 @@ of strings to the language and reading from the filesystem.
- `uptime`, `realtime`
- `regex-match?`
- Boolean operations: `not`, `and`, `or`
## Usage
The interpreter can be invoked from the shell:
@ -85,7 +118,7 @@ with the following content:
```lisp
(load "/lib/lisp/core.lsp")
(def (fibonacci n)
(define (fibonacci n)
(if (< n 2) n
(+ (fibonacci (- n 1)) (fibonacci (- n 2)))))
@ -106,21 +139,21 @@ Would produce the following output:
```lisp
(load "/lib/lisp/core.lsp")
(def foo 42) # Variable definition
(define foo 42) # Variable definition
(def double (fun (x) (* x 2))) # Function definition
(def (double x) (* x 2)) # Shortcut
(define double (fun (x) (* x 2))) # Function definition
(define (double x) (* x 2)) # Shortcut
(double foo) # => 84
(def (map f ls)
(define (map f ls)
(if (nil? ls) nil
(cons
(f (first ls))
(map f (rest ls)))))
(def bar (quote (1 2 3)))
(def bar '(1 2 3)) # Shortcut
(define bar (quote (1 2 3)))
(define bar '(1 2 3)) # Shortcut
(map double bar) # => (2 4 6)
@ -135,7 +168,7 @@ Would produce the following output:
(= foo 10) # => true
(def name "Alice")
(define name "Alice")
(string "Hello, " name) # => "Hello, Alice"

28
dsk/lib/lisp/alias.lsp Normal file
View File

@ -0,0 +1,28 @@
(define def
(macro args `(define ,@args)))
(define mac
(macro args `(macro ,@args)))
(define fun
(macro args `(function ,@args)))
(define def-mac
(macro args `(define-macro ,@args)))
(define def-fun
(macro args `(define-function ,@args)))
(define label
(macro args `(define ,@args)))
(define lambda
(macro args `(function ,@args)))
(define progn
(macro args `(do ,@args)))
(define begin
(macro args `(do ,@args)))

View File

@ -1,99 +1,119 @@
(def (eq? x y)
(load "/lib/lisp/alias.lsp")
(define (eq? x y)
(eq x y))
(def (atom? x)
(define (atom? x)
(atom x))
(def (string? x)
(define (string? x)
(eq? (type x) "string"))
(def (boolean? x)
(define (boolean? x)
(eq? (type x) "boolean"))
(def (symbol? x)
(define (symbol? x)
(eq? (type x) "symbol"))
(def (number? x)
(define (number? x)
(eq? (type x) "number"))
(def (list? x)
(define (list? x)
(eq? (type x) "list"))
(def (function? x)
(define (function? x)
(eq? (type x) "function"))
(def nil '())
(define (macro? x)
(eq? (type x) "macro"))
(def (nil? x)
(define nil '())
(define (nil? x)
(eq? x nil))
(def (not x)
(define (not x)
(if x false true))
(def (or x y)
(if x true (if y true false)))
(define-macro (or x y)
`(if ,x true (if ,y true false)))
(def (and x y)
(if x (if y true false) false))
(define-macro (and x y)
`(if ,x (if ,y true false) false))
(def (rest x)
(define-macro (let params values body)
`((function ,params ,body) ,@values))
(define (caar x)
(car (car x)))
(define (cadr x)
(car (cdr x)))
(define (cdar x)
(cdr (car x)))
(define (cddr x)
(cdr (cdr x)))
(define (rest x)
(cdr x))
(def (first x)
(define (first x)
(car x))
(def (second x)
(define (second x)
(first (rest x)))
(def (third x)
(define (third x)
(second (rest x)))
(def (reduce f ls)
(define (reduce f ls)
(if (nil? (rest ls)) (first ls)
(f (first ls) (reduce f (rest ls)))))
(def (string-join ls s)
(reduce (fn (x y) (string x s y)) ls))
(def (map f ls)
(define (map f ls)
(if (nil? ls) nil
(cons
(f (first ls))
(map f (rest ls)))))
(def (reverse x)
(define (reverse x)
(if (nil? x) x
(append (reverse (rest x)) (cons (first x) '()))))
(def (range i n)
(define (range i n)
(if (= i n) nil
(append (list i) (range (+ i 1) n))))
(def (read-line)
(define (string-join ls s)
(reduce (fn (x y) (string x s y)) ls))
(define (read-line)
(bytes->string (reverse (rest (reverse (read-file-bytes "/dev/console" 256))))))
(def (read-char)
(define (read-char)
(bytes->string (read-file-bytes "/dev/console" 4)))
(def (print exp)
(define (print exp)
(do
(append-file-bytes "/dev/console" (string->bytes (string exp)))
'()))
(def (println exp)
(define (println exp)
(print (string exp "\n")))
(def (uptime)
(define (uptime)
(bytes->number (read-file-bytes "/dev/clk/uptime" 8) "float"))
(def (realtime)
(define (realtime)
(bytes->number (read-file-bytes "realtime" 8) "float"))
(def (write-file path str)
(define (write-file path str)
(write-file-bytes path (string->bytes str)))
(def (append-file path str)
(define (append-file path str)
(append-file-bytes path (string->bytes str)))
(def (regex-match? pattern str)
(define (regex-match? pattern str)
(not (nil? (regex-find pattern str))))

View File

@ -1,10 +1,10 @@
(load "/lib/lisp/core.lsp")
(def (factorial-helper n acc)
(define (factorial-helper n acc)
(if (< n 2) acc
(factorial-helper (- n 1) (* acc n))))
(def (factorial n)
(define (factorial n)
(factorial-helper n 1))
(println

View File

@ -1,6 +1,6 @@
(load "/lib/lisp/core.lsp")
(def (fibonacci n)
(define (fibonacci n)
(if (< n 2) n
(+ (fibonacci (- n 1)) (fibonacci (- n 2)))))

View File

@ -1,26 +1,26 @@
(load "/lib/lisp/core.lsp")
(def (pi-digits digits)
(define (pi-digits digits)
(do
(def i 0)
(def q 1)
(def r 0)
(def t 1)
(def k 1)
(def n 3)
(def l 3)
(define i 0)
(define q 1)
(define r 0)
(define t 1)
(define k 1)
(define n 3)
(define l 3)
(while (<= i digits)
(if (< (- (+ (* q 4) r) t) (* n t))
(do
(print (string n (if (= i 0) "." "")))
(set i (+ i 1))
(def nr (* 10 (- r (* n t))))
(define nr (* 10 (- r (* n t))))
(set n (- (/ (* 10 (+ (* 3 q) r)) t) (* 10 n)))
(set q (* q 10))
(set r nr))
(do
(def nr (* (+ (* 2 q) r) l))
(def nn (/ (+ 2 (* q k 7) (* r l)) (* t l)))
(define nr (* (+ (* 2 q) r) l))
(define nn (/ (+ 2 (* q k 7) (* r l)) (* t l)))
(set q (* q k))
(set t (* t l))
(set l (+ l 2))

View File

@ -1,6 +1,6 @@
(load "/lib/lisp/core.lsp")
(def (sum n acc)
(define (sum n acc)
(if (= n 0) acc (sum (- n 1) (+ n acc))))
(println

View File

@ -48,6 +48,7 @@ pub fn copy_files(verbose: bool) {
create_dir("/lib/lisp", verbose);
copy_file("/lib/lisp/core.lsp", include_bytes!("../../dsk/lib/lisp/core.lsp"), verbose);
copy_file("/lib/lisp/alias.lsp", include_bytes!("../../dsk/lib/lisp/alias.lsp"), verbose);
copy_file("/tmp/alice.txt", include_bytes!("../../dsk/tmp/alice.txt"), verbose);
copy_file("/tmp/machines.txt", include_bytes!("../../dsk/tmp/machines.txt"), verbose);

View File

@ -148,10 +148,19 @@ pub fn eval_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Vec<Exp>, E
args.iter().map(|x| eval(x, env)).collect()
}
pub const BUILT_INS: [&str; 24] = [
"quote", "atom", "eq", "car", "cdr", "cons", "cond", "label", "lambda", "define", "def",
"function", "fun", "fn", "if", "while", "defun", "defn", "apply", "eval", "progn", "begin",
"do", "load"
pub const BUILT_INS: [&str; 32] = [
"quote", "quasiquote", "unquote", "unquote-splicing",
"atom", "eq", "car", "cdr", "cons",
"if", "cond", "while",
"set",
"define", "def", "label",
"function", "fun", "lambda",
"macro", "mac",
"define-function", "def-fun",
"define-macro", "def-mac",
"apply", "eval", "expand",
"do", "begin", "progn",
"load"
];
pub fn eval(exp: &Exp, env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {

View File

@ -55,27 +55,7 @@ pub fn expand(exp: &Exp, env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
ensure_length_eq!(list, 2);
expand_quasiquote(&list[1])
}
Exp::Sym(s) if s == "begin" || s == "progn" => {
let mut res = vec![Exp::Sym("do".to_string())];
res.extend_from_slice(&list[1..]);
expand(&Exp::List(res), env)
}
Exp::Sym(s) if s == "def" || s == "label" => {
let mut res = vec![Exp::Sym("define".to_string())];
res.extend_from_slice(&list[1..]);
expand(&Exp::List(res), env)
}
Exp::Sym(s) if s == "fun" || s == "fn" || s == "lambda" => {
let mut res = vec![Exp::Sym("function".to_string())];
res.extend_from_slice(&list[1..]);
expand(&Exp::List(res), env)
}
Exp::Sym(s) if s == "mac" => {
let mut res = vec![Exp::Sym("macro".to_string())];
res.extend_from_slice(&list[1..]);
expand(&Exp::List(res), env)
}
Exp::Sym(s) if s == "define-function" || s == "def-fun" || s == "define" => {
Exp::Sym(s) if s == "define-function" || s == "define" => {
ensure_length_eq!(list, 3);
match (&list[1], &list[2]) {
(Exp::List(args), Exp::List(_)) => {
@ -93,7 +73,7 @@ pub fn expand(exp: &Exp, env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
_ => Err(Err::Reason("Expected first argument to be a symbol or a list".to_string()))
}
}
Exp::Sym(s) if s == "define-macro" || s == "def-mac" => {
Exp::Sym(s) if s == "define-macro" => {
ensure_length_eq!(list, 3);
match (&list[1], &list[2]) {
(Exp::List(args), Exp::List(_)) => {

View File

@ -169,6 +169,7 @@ fn parse_eval(exp: &str, env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
}
fn strip_comments(s: &str) -> String {
// FIXME: This doesn't handle `#` inside a string
s.split('#').next().unwrap().into()
}
@ -343,7 +344,7 @@ fn test_lisp() {
assert_eq!(eval!("(if (> 2 4) 1 2)"), "2");
// while
assert_eq!(eval!("(do (def i 0) (while (< i 5) (set i (+ i 1))) i)"), "5");
assert_eq!(eval!("(do (define i 0) (while (< i 5) (set i (+ i 1))) i)"), "5");
// define
eval!("(define a 2)");