mirror of https://github.com/vinc/moros.git
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:
parent
0f08b0838e
commit
ab9b488a49
93
doc/lisp.md
93
doc/lisp.md
|
@ -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"
|
||||
|
||||
|
|
|
@ -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)))
|
|
@ -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))))
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)))))
|
||||
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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(_)) => {
|
||||
|
|
|
@ -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)");
|
||||
|
|
Loading…
Reference in New Issue