mirror of https://github.com/vinc/moros.git
Add if and while to Lisp (#418)
* Change Lisp loop into while * Rename define to def * Replace cond by if * Fix pi computation * Fix number parsing error * Fix or function * Add test to while * Rewrite test * Add missing fun * Add examples to doc
This commit is contained in:
parent
b09f881c64
commit
fcf3a7e72f
55
doc/lisp.md
55
doc/lisp.md
|
@ -34,6 +34,8 @@ of strings to the language and reading from the filesystem.
|
|||
|
||||
## Additional Builtins
|
||||
- `defun` (aliased to `defn`)
|
||||
- `set`
|
||||
- `while`
|
||||
- `apply`
|
||||
- `type`
|
||||
- `string`
|
||||
|
@ -83,15 +85,13 @@ with the following content:
|
|||
```lisp
|
||||
(load "/lib/lisp/core.lsp")
|
||||
|
||||
(define (fibonacci n)
|
||||
(cond
|
||||
((< n 2) n)
|
||||
(true (+ (fibonacci (- n 1)) (fibonacci (- n 2))))))
|
||||
(def (fibonacci n)
|
||||
(if (< n 2) n
|
||||
(+ (fibonacci (- n 1)) (fibonacci (- n 2)))))
|
||||
|
||||
(println
|
||||
(cond
|
||||
((nil? args) "Usage: fibonacci <num>")
|
||||
(true (fibonacci (string->number (car args))))))
|
||||
(if (nil? args) "Usage: fibonacci <num>"
|
||||
(fibonacci (string->number (car args)))))
|
||||
```
|
||||
|
||||
Would produce the following output:
|
||||
|
@ -100,3 +100,44 @@ Would produce the following output:
|
|||
> lisp /tmp/lisp/fibonacci.lsp 20
|
||||
6755
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
```lisp
|
||||
(load "/lib/lisp/core.lsp")
|
||||
|
||||
(def foo 42) # Variable definition
|
||||
|
||||
(def double (fun (x) (* x 2))) # Function definition
|
||||
(def (double x) (* x 2)) # Shortcut
|
||||
|
||||
(double foo) # => 84
|
||||
|
||||
(def (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
|
||||
|
||||
(map double bar) # => (2 4 6)
|
||||
|
||||
(map (fun (x) (+ x 1)) '(4 5 6)) # => (5 6 7)
|
||||
|
||||
(set foo 0) # Variable assignment
|
||||
|
||||
(= foo 10) # => false
|
||||
|
||||
(while (< foo 10)
|
||||
(set foo (+ foo 1)))
|
||||
|
||||
(= foo 10) # => true
|
||||
|
||||
(def name "Alice")
|
||||
|
||||
(string "Hello, " name) # => "Hello, Alice"
|
||||
|
||||
(^ 2 128) # => 340282366920938463463374607431768211456
|
||||
```
|
||||
|
|
|
@ -1,112 +1,103 @@
|
|||
(define (eq? x y)
|
||||
(def (eq? x y)
|
||||
(eq x y))
|
||||
|
||||
(define (atom? x)
|
||||
(def (atom? x)
|
||||
(atom x))
|
||||
|
||||
(define (string? x)
|
||||
(def (string? x)
|
||||
(eq? (type x) "string"))
|
||||
|
||||
(define (boolean? x)
|
||||
(def (boolean? x)
|
||||
(eq? (type x) "boolean"))
|
||||
|
||||
(define (symbol? x)
|
||||
(def (symbol? x)
|
||||
(eq? (type x) "symbol"))
|
||||
|
||||
(define (number? x)
|
||||
(def (number? x)
|
||||
(eq? (type x) "number"))
|
||||
|
||||
(define (list? x)
|
||||
(def (list? x)
|
||||
(eq? (type x) "list"))
|
||||
|
||||
(define (function? x)
|
||||
(def (function? x)
|
||||
(eq? (type x) "function"))
|
||||
|
||||
(define nil '())
|
||||
(def nil '())
|
||||
|
||||
(define (nil? x)
|
||||
(def (nil? x)
|
||||
(eq? x nil))
|
||||
|
||||
(define (and x y)
|
||||
(cond
|
||||
(x (cond (y true) (true false)))
|
||||
(true false)))
|
||||
(def (not x)
|
||||
(if x false true))
|
||||
|
||||
(define (not x)
|
||||
(cond (x false) (true true)))
|
||||
(def (or x y)
|
||||
(if x true (if y true false)))
|
||||
|
||||
(define (or x y)
|
||||
(cond (x true) (y true) (true false)))
|
||||
(def (and x y)
|
||||
(if x (if y true false) false))
|
||||
|
||||
(define (rest x)
|
||||
(def (rest x)
|
||||
(cdr x))
|
||||
|
||||
(define (first x)
|
||||
(def (first x)
|
||||
(car x))
|
||||
|
||||
(define (second x)
|
||||
(def (second x)
|
||||
(first (rest x)))
|
||||
|
||||
(define (third x)
|
||||
(def (third x)
|
||||
(second (rest x)))
|
||||
|
||||
(define (reduce f ls)
|
||||
(cond
|
||||
((nil? (rest ls)) (first ls))
|
||||
(true (f (first ls) (reduce f (rest ls))))))
|
||||
(def (reduce f ls)
|
||||
(if (nil? (rest ls)) (first ls)
|
||||
(f (first ls) (reduce f (rest ls)))))
|
||||
|
||||
(define (string-join ls s)
|
||||
(def (string-join ls s)
|
||||
(reduce (fn (x y) (string x s y)) ls))
|
||||
|
||||
(define (map f ls)
|
||||
(cond
|
||||
((nil? ls) nil)
|
||||
(true (cons
|
||||
(def (map f ls)
|
||||
(if (nil? ls) nil
|
||||
(cons
|
||||
(f (first ls))
|
||||
(map f (rest ls))))))
|
||||
(map f (rest ls)))))
|
||||
|
||||
(define (append x y)
|
||||
(cond
|
||||
((nil? x) y)
|
||||
(true (cons (first x) (append (rest x) y)))))
|
||||
(def (append x y)
|
||||
(if (nil? x) y
|
||||
(cons (first x) (append (rest x) y))))
|
||||
|
||||
(define (reverse x)
|
||||
(cond
|
||||
((nil? x) x)
|
||||
(true (append (reverse (rest x)) (cons (first x) '())))))
|
||||
(def (reverse x)
|
||||
(if (nil? x) x
|
||||
(append (reverse (rest x)) (cons (first x) '()))))
|
||||
|
||||
(define (range i n)
|
||||
(cond
|
||||
((= i n) nil)
|
||||
(true (append (list i) (range (+ i 1) n)))))
|
||||
(def (range i n)
|
||||
(if (= i n) nil
|
||||
(append (list i) (range (+ i 1) n))))
|
||||
|
||||
(define (read-line)
|
||||
(def (read-line)
|
||||
(bytes->string (reverse (rest (reverse (read-file-bytes "/dev/console" 256))))))
|
||||
|
||||
(define (read-char)
|
||||
(def (read-char)
|
||||
(bytes->string (read-file-bytes "/dev/console" 4)))
|
||||
|
||||
(define (print exp)
|
||||
(def (print exp)
|
||||
(do
|
||||
(append-file-bytes "/dev/console" (string->bytes (string exp)))
|
||||
'()))
|
||||
|
||||
(define (println exp)
|
||||
(do
|
||||
(print exp)
|
||||
(print "\n")))
|
||||
(def (println exp)
|
||||
(print (string exp "\n")))
|
||||
|
||||
(define (uptime)
|
||||
(def (uptime)
|
||||
(bytes->number (read-file-bytes "/dev/clk/uptime" 8) "float"))
|
||||
|
||||
(define (realtime)
|
||||
(def (realtime)
|
||||
(bytes->number (read-file-bytes "realtime" 8) "float"))
|
||||
|
||||
(define (write-file path str)
|
||||
(def (write-file path str)
|
||||
(write-file-bytes path (string->bytes str)))
|
||||
|
||||
(define (append-file path str)
|
||||
(def (append-file path str)
|
||||
(append-file-bytes path (string->bytes str)))
|
||||
|
||||
(define (regex-match? pattern str)
|
||||
(def (regex-match? pattern str)
|
||||
(not (nil? (regex-find pattern str))))
|
||||
|
|
|
@ -1,14 +1,12 @@
|
|||
(load "/lib/lisp/core.lsp")
|
||||
|
||||
(define (factorial-helper n acc)
|
||||
(cond
|
||||
((< n 2) acc)
|
||||
(true (factorial-helper (- n 1) (* acc n)))))
|
||||
(def (factorial-helper n acc)
|
||||
(if (< n 2) acc
|
||||
(factorial-helper (- n 1) (* acc n))))
|
||||
|
||||
(define (factorial n)
|
||||
(def (factorial n)
|
||||
(factorial-helper n 1))
|
||||
|
||||
(println
|
||||
(cond
|
||||
((nil? args) "Usage: factorial <num>")
|
||||
(true (factorial (string->number (car args))))))
|
||||
(if (nil? args) "Usage: factorial <num>"
|
||||
(factorial (string->number (car args)))))
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
(load "/lib/lisp/core.lsp")
|
||||
|
||||
(define (fibonacci n)
|
||||
(cond
|
||||
((< n 2) n)
|
||||
(true (+ (fibonacci (- n 1)) (fibonacci (- n 2))))))
|
||||
(def (fibonacci n)
|
||||
(if (< n 2) n
|
||||
(+ (fibonacci (- n 1)) (fibonacci (- n 2)))))
|
||||
|
||||
(println
|
||||
(cond
|
||||
((nil? args) "Usage: fibonacci <num>")
|
||||
(true (fibonacci (string->number (car args))))))
|
||||
(if (nil? args) "Usage: fibonacci <num>"
|
||||
(fibonacci (string->number (car args)))))
|
||||
|
|
|
@ -1,38 +1,34 @@
|
|||
(load "/lib/lisp/core.lsp")
|
||||
|
||||
(define (pi-digits y)
|
||||
(def (pi-digits digits)
|
||||
(do
|
||||
(define dot true)
|
||||
(define q 1)
|
||||
(define r 0)
|
||||
(define t 1)
|
||||
(define k 1)
|
||||
(define n 3)
|
||||
(define l 3)
|
||||
(map
|
||||
(lambda (j)
|
||||
(def i 0)
|
||||
(def q 1)
|
||||
(def r 0)
|
||||
(def t 1)
|
||||
(def k 1)
|
||||
(def n 3)
|
||||
(def l 3)
|
||||
(while (<= i digits)
|
||||
(if (< (- (+ (* q 4) r) t) (* n t))
|
||||
(do
|
||||
(cond
|
||||
((< (- (+ (* q 4) r) t) (* n t)) (do
|
||||
(print (string n (cond (dot ".") (true ""))))
|
||||
(set dot false)
|
||||
(define nr (* 10 (- r (* n t))))
|
||||
(set n (- (/ (* 10 (+ (* 3 q) r)) t) (* 10 n)))
|
||||
(set q (* q 10))
|
||||
(set r nr)))
|
||||
(true (do
|
||||
(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))
|
||||
(set k (+ k 1))
|
||||
(set n nn)
|
||||
(set r nr))))))
|
||||
(range 0 y))
|
||||
n))
|
||||
(print (string n (if (= i 0) "." "")))
|
||||
(set i (+ i 1))
|
||||
(def 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)))
|
||||
(set q (* q k))
|
||||
(set t (* t l))
|
||||
(set l (+ l 2))
|
||||
(set k (+ k 1))
|
||||
(set n nn)
|
||||
(set r nr))))
|
||||
""))
|
||||
|
||||
(println
|
||||
(cond
|
||||
((nil? args) "Usage: pi <precision>")
|
||||
(true (pi-digits (string->number (car args))))))
|
||||
(if (nil? args) "Usage: pi <precision>"
|
||||
(pi-digits (string->number (car args)))))
|
||||
|
|
|
@ -80,7 +80,18 @@ fn eval_cond_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err>
|
|||
_ => return Err(Err::Reason("Expected lists of predicate and expression".to_string())),
|
||||
}
|
||||
}
|
||||
Ok(Exp::List(Vec::new()))
|
||||
Ok(Exp::List(vec![]))
|
||||
}
|
||||
|
||||
fn eval_if_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
|
||||
ensure_length_gt!(args, 1);
|
||||
if eval(&args[0], env)? == Exp::Bool(true) {
|
||||
eval(&args[1], env)
|
||||
} else if args.len() > 2 {
|
||||
eval(&args[2], env)
|
||||
} else {
|
||||
Ok(Exp::List(vec![]))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eval_label_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
|
||||
|
@ -117,11 +128,16 @@ fn eval_set_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
|
|||
}
|
||||
}
|
||||
|
||||
fn eval_loop_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
|
||||
ensure_length_eq!(args, 1);
|
||||
loop {
|
||||
eval(&args[0], env)?;
|
||||
fn eval_while_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
|
||||
ensure_length_gt!(args, 1);
|
||||
let cond = &args[0];
|
||||
let mut res = Exp::List(vec![]);
|
||||
while eval(cond, env)? == Exp::Bool(true) {
|
||||
for arg in &args[1..] {
|
||||
res = eval(arg, env)?;
|
||||
}
|
||||
}
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
fn eval_lambda_args(args: &[Exp]) -> Result<Exp, Err> {
|
||||
|
@ -182,9 +198,10 @@ fn eval_load_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err>
|
|||
Ok(Exp::Bool(true))
|
||||
}
|
||||
|
||||
pub const BUILT_INS: [&str; 22] = [
|
||||
pub const BUILT_INS: [&str; 24] = [
|
||||
"quote", "atom", "eq", "car", "cdr", "cons", "cond", "label", "lambda", "define", "def",
|
||||
"function", "fun", "fn", "defun", "defn", "apply", "eval", "progn", "begin", "do", "load"
|
||||
"function", "fun", "fn", "if", "while", "defun", "defn", "apply", "eval", "progn", "begin", "do",
|
||||
"load"
|
||||
];
|
||||
|
||||
fn eval_built_in_form(exp: &Exp, args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Option<Result<Exp, Err>> {
|
||||
|
@ -192,26 +209,27 @@ fn eval_built_in_form(exp: &Exp, args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Op
|
|||
Exp::Sym(s) => {
|
||||
match s.as_ref() {
|
||||
// Seven Primitive Operators
|
||||
"quote" => Some(eval_quote_args(args)),
|
||||
"atom" => Some(eval_atom_args(args, env)),
|
||||
"eq" => Some(eval_eq_args(args, env)),
|
||||
"car" => Some(eval_car_args(args, env)),
|
||||
"cdr" => Some(eval_cdr_args(args, env)),
|
||||
"cons" => Some(eval_cons_args(args, env)),
|
||||
"cond" => Some(eval_cond_args(args, env)),
|
||||
"quote" => Some(eval_quote_args(args)),
|
||||
"atom" => Some(eval_atom_args(args, env)),
|
||||
"eq" => Some(eval_eq_args(args, env)),
|
||||
"car" => Some(eval_car_args(args, env)),
|
||||
"cdr" => Some(eval_cdr_args(args, env)),
|
||||
"cons" => Some(eval_cons_args(args, env)),
|
||||
"cond" => Some(eval_cond_args(args, env)),
|
||||
|
||||
// Two Special Forms
|
||||
"label" | "define" | "def" => Some(eval_label_args(args, env)),
|
||||
"lambda" | "function" | "fn" => Some(eval_lambda_args(args)),
|
||||
"label" | "define" | "def" => Some(eval_label_args(args, env)),
|
||||
"lambda" | "function" | "fun" | "fn" => Some(eval_lambda_args(args)),
|
||||
|
||||
"set" => Some(eval_set_args(args, env)),
|
||||
"loop" => Some(eval_loop_args(args, env)),
|
||||
"defun" | "defn" => Some(eval_defun_args(args, env)),
|
||||
"apply" => Some(eval_apply_args(args, env)),
|
||||
"eval" => Some(eval_eval_args(args, env)),
|
||||
"progn" | "begin" | "do" => Some(eval_progn_args(args, env)),
|
||||
"load" => Some(eval_load_args(args, env)),
|
||||
_ => None,
|
||||
"if" => Some(eval_if_args(args, env)),
|
||||
"set" => Some(eval_set_args(args, env)),
|
||||
"while" => Some(eval_while_args(args, env)),
|
||||
"defun" | "defn" => Some(eval_defun_args(args, env)),
|
||||
"apply" => Some(eval_apply_args(args, env)),
|
||||
"eval" => Some(eval_eval_args(args, env)),
|
||||
"progn" | "begin" | "do" => Some(eval_progn_args(args, env)),
|
||||
"load" => Some(eval_load_args(args, env)),
|
||||
_ => None,
|
||||
}
|
||||
},
|
||||
_ => None,
|
||||
|
|
|
@ -23,7 +23,6 @@ use alloc::vec::Vec;
|
|||
use alloc::vec;
|
||||
use core::cell::RefCell;
|
||||
use core::convert::TryInto;
|
||||
use core::f64::consts::PI;
|
||||
use core::fmt;
|
||||
use lazy_static::lazy_static;
|
||||
use spin::Mutex;
|
||||
|
@ -290,6 +289,7 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
|||
|
||||
#[test_case]
|
||||
fn test_lisp() {
|
||||
use core::f64::consts::PI;
|
||||
let env = &mut default_env();
|
||||
|
||||
macro_rules! eval {
|
||||
|
@ -343,6 +343,15 @@ fn test_lisp() {
|
|||
assert_eq!(eval!("(cond ((< 2 4) 1) (true 2))"), "1");
|
||||
assert_eq!(eval!("(cond ((> 2 4) 1) (true 2))"), "2");
|
||||
|
||||
// if
|
||||
assert_eq!(eval!("(if (< 2 4) 1)"), "1");
|
||||
assert_eq!(eval!("(if (> 2 4) 1)"), "()");
|
||||
assert_eq!(eval!("(if (< 2 4) 1 2)"), "1");
|
||||
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");
|
||||
|
||||
// label
|
||||
eval!("(label a 2)");
|
||||
assert_eq!(eval!("(+ a 1)"), "3");
|
||||
|
|
|
@ -192,6 +192,7 @@ impl FromStr for Number {
|
|||
type Err = Err;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let err = Err(Err::Reason("Could not parse number".to_string()));
|
||||
if s.contains('.') {
|
||||
if let Ok(n) = s.parse() {
|
||||
return Ok(Number::Float(n));
|
||||
|
@ -206,6 +207,9 @@ impl FromStr for Number {
|
|||
}
|
||||
let mut res = BigInt::from(0);
|
||||
for c in chars {
|
||||
if !c.is_ascii_digit() {
|
||||
return err;
|
||||
}
|
||||
let d = c as u8 - b'0';
|
||||
res = res * BigInt::from(10) + BigInt::from(d as u32);
|
||||
}
|
||||
|
@ -214,7 +218,7 @@ impl FromStr for Number {
|
|||
} /* else if let Ok(n) = s.parse() { // FIXME: rust-lld: error: undefined symbol: fmod
|
||||
return Ok(Number::BigInt(n));
|
||||
} */
|
||||
Err(Err::Reason("Could not parse number".to_string()))
|
||||
err
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue