Skip to content

Commit

Permalink
Add docstring to lisp (#490)
Browse files Browse the repository at this point in the history
* Add docstring to lisp

* Add expected macro

* Add could_not macro

* Improve naming conventions

* Refactor code

* Add env function

* Update changelog
  • Loading branch information
vinc authored May 29, 2023
1 parent db03702 commit b1d7a1f
Show file tree
Hide file tree
Showing 11 changed files with 200 additions and 98 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Changelog

## Unreleased
- Add docstring to lisp (#490)
- Add full support for comments in lisp (#489)
- Add parenthesis matching to editor (#488)
- Upgrade smoltcp from 0.8.2 to 0.9.1 (#484)
Expand Down
8 changes: 7 additions & 1 deletion doc/lisp.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,10 @@ MOROS Lisp is a Lisp-1 dialect inspired by Scheme, Clojure, and Ruby!
- `define-function` (aliased to `def-fun`)
- `define-macro` (aliased to `def-mac`)
- `apply`
- `do`
- `doc`
- `eval`
- `expand`
- `do`
- `load`

### Primitive Operators
Expand Down Expand Up @@ -135,11 +136,14 @@ Would produce the following output:
(= i 10) # => true
(def (map f ls)
"Apply function to list"
(if (nil? ls) nil
(cons
(f (first ls))
(map f (rest ls)))))
(doc map) # => "Apply function to list"
(var bar (quote (1 2 3)))
(var bar '(1 2 3)) # Shortcut
Expand Down Expand Up @@ -188,3 +192,5 @@ Rewrite parts of the code and add new functions and examples.
### 0.5.0 (unpublished)
- Rename or add aliases to many functions
- Add full support for line and inline comments
- Add params to function representations
- Add docstring to functions
3 changes: 3 additions & 0 deletions dsk/lib/lisp/alias.lsp
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,6 @@

(var rest
(macro args `(tail ,@args)))

(var help
(macro args `(doc ,@args)))
25 changes: 20 additions & 5 deletions dsk/lib/lisp/core.lsp
Original file line number Diff line number Diff line change
Expand Up @@ -39,56 +39,69 @@
`((fun ,params ,body) ,@values))

(def (reduce f ls)
"Reduce the elements of the list with the function"
(if (nil? (tail ls)) (head ls)
(f (head ls) (reduce f (tail ls)))))

(def (map f ls)
"Apply the function to the elements of the list"
(if (nil? ls) nil
(cons
(f (head ls))
(map f (tail ls)))))

(def (filter f ls)
"Filter the elements of the list with the function"
(if (nil? ls) nil
(if (f (head ls))
(cons (head ls) (filter f (tail ls)))
(filter f (tail ls)))))

(def (intersection a b)
"Return elements found in both lists"
(filter (fun (x) (contains? b x)) a))

(def (reverse x)
"Reverse list"
(if (nil? x) x
(append (reverse (tail x)) (cons (head x) '()))))

(def (range i n)
(def (range start stop)
"Return a list of integers from start to stop excluded"
(if (= i n) nil
(append (list i) (range (+ i 1) n))))

(def (min lst)
"Return the minimum element of the list"
(head (sort lst)))

(def (max lst)
"Return the maximum element of the list"
(head (reverse (sort lst))))

(def (abs x)
(if (> x 0) x (- x)))

(def (join-string ls s)
"Join the elements of the list with the string"
(reduce (fun (x y) (string x s y)) ls))

(def (read-line)
"Read line from the console"
(binary->string (reverse (tail (reverse (read-file-binary "/dev/console" 256))))))

(def (read-char)
"Read char from the console"
(binary->string (read-file-binary "/dev/console" 4)))

(def (p exp)
"Print expression to the console"
(do
(append-file-binary "/dev/console" (string->binary (string exp)))
'()))

(def (print exp)
"Print expression to the console with a newline"
(p (string exp "\n")))

(def (uptime)
Expand All @@ -98,21 +111,26 @@
(binary->number (read-file-binary "/dev/clk/realtime" 8) "float"))

(def (write-file path s)
"Write string to file"
(write-file-binary path (string->binary s)))

(def (append-file path s)
"Append string to file"
(append-file-binary path (string->binary s)))

(def (regex-match? pattern s)
(not (nil? (regex-find pattern str))))

(def (lines contents)
"Split contents into a list of lines"
(split (trim contents) "\n"))

(def (words contents)
"Split contents into a list of words"
(split contents " "))

(def (chars contents)
"Split contents into a list of chars"
(split contents ""))

(def (first lst)
Expand All @@ -128,24 +146,21 @@
(nth lst
(if (= (length lst) 0) 0 (- (length lst) 1))))

# Short aliases
(var str string)
(var num-type number-type)
(var join-str join-string)

(var str->num string->number)
(var str->bin string->binary)
(var num->bin number->binary)
(var bin->str binary->string)
(var bin->num binary->number)

(var bool? boolean?)
(var str? string?)
(var sym? symbol?)
(var num? number?)

(var fun? function?)
(var mac? macro?)

(var len length)
(var rev reverse)
(var uniq unique)
25 changes: 18 additions & 7 deletions src/usr/lisp/env.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
use super::FORMS;
use super::FUNCTIONS;
use super::primitive;
use super::eval::BUILT_INS;
use super::eval::eval_args;
use super::{Err, Exp, Number};
use crate::{could_not, expected};

use alloc::collections::BTreeMap;
use alloc::format;
use alloc::rc::Rc;
use alloc::string::String;
use alloc::string::ToString;
use alloc::vec::Vec;
use core::borrow::Borrow;
use core::cell::RefCell;
use core::f64::consts::PI;
Expand Down Expand Up @@ -71,19 +73,28 @@ pub fn default_env() -> Rc<RefCell<Env>> {
data.insert("append".to_string(), Exp::Primitive(primitive::lisp_append));

// Setup autocompletion
*FORMS.lock() = data.keys().cloned().chain(BUILT_INS.map(String::from)).collect();
*FUNCTIONS.lock() = data.keys().cloned().chain(BUILT_INS.map(String::from)).collect();

Rc::new(RefCell::new(Env { data, outer: None }))
}

pub fn env_keys(env: &Rc<RefCell<Env>>) -> Result<Vec<String>, Err> {
let env = env.borrow_mut();
let mut keys: Vec<String> = env.data.keys().map(|k| k.clone()).collect();
if let Some(outer_env) = &env.outer {
keys.extend_from_slice(&env_keys(outer_env)?);
}
Ok(keys)
}

pub fn env_get(key: &str, env: &Rc<RefCell<Env>>) -> Result<Exp, Err> {
let env = env.borrow_mut();
match env.data.get(key) {
Some(exp) => Ok(exp.clone()),
None => {
match &env.outer {
Some(outer_env) => env_get(key, outer_env.borrow()),
None => Err(Err::Reason(format!("Unexpected symbol '{}'", key))),
None => could_not!("find symbol '{}'", key),
}
}
}
Expand All @@ -99,7 +110,7 @@ pub fn env_set(key: &str, val: Exp, env: &Rc<RefCell<Env>>) -> Result<Exp, Err>
None => {
match &env.outer {
Some(outer_env) => env_set(key, val, outer_env.borrow()),
None => Err(Err::Reason(format!("Unexpected symbol '{}'", key))),
None => could_not!("find symbol '{}'", key),
}
}
}
Expand Down Expand Up @@ -142,17 +153,17 @@ fn inner_env(kind: InnerEnv, params: &Exp, args: &[Exp], outer: &mut Rc<RefCell<
if n != m {
let s = if n != 1 { "s" } else { "" };
let a = if is_variadic { "at least " } else { "" };
return Err(Err::Reason(format!("Expected {}{} argument{}, got {}", a, n, s, m)));
return expected!("{}{} argument{}, got {}", a, n, s, m);
}
for (exp, arg) in list.iter().zip(args.iter()) {
if let Exp::Sym(s) = exp {
data.insert(s.clone(), arg.clone());
} else {
return Err(Err::Reason("Expected symbols in the argument list".to_string()));
return expected!("params to be a list of symbols");
}
}
}
_ => return Err(Err::Reason("Expected args form to be a list".to_string())),
_ => return expected!("params to be a list"),
}
Ok(Rc::new(RefCell::new(Env { data, outer: Some(Rc::new(RefCell::new(outer.borrow_mut().clone()))) })))
}
Expand Down
Loading

0 comments on commit b1d7a1f

Please sign in to comment.