mirror of https://github.com/vinc/moros.git
Add namespaces to lisp (#511)
* Add file module * Add r and t literals * Add string support in contains * Add number, string, and regex modules * Fix tests * Fix lib * Fix split in lib * The % operator is for remainder instead of modulo * Rename internal binary functions * Add mod and rem and alias % to the latter * Use dot instead of colon for namespacing * Rename *-file* functions * Update www * Update changelog
This commit is contained in:
parent
314118c35f
commit
f806c3a338
|
@ -1,6 +1,7 @@
|
|||
# Changelog
|
||||
|
||||
## Unreleased
|
||||
- Add namespaces to lisp ([#511](https://github.com/vinc/moros/pull/511))
|
||||
- Update smoltcp from 0.9.1 to 0.10.0 ([#510](https://github.com/vinc/moros/pull/510))
|
||||
|
||||
## 0.10.0 (2023-06-21)
|
||||
|
|
27
doc/lisp.md
27
doc/lisp.md
|
@ -40,21 +40,20 @@ MOROS Lisp is a Lisp-1 dialect inspired by Scheme, Clojure, and Ruby!
|
|||
- `load`
|
||||
|
||||
### Primitive Operators
|
||||
- `append`
|
||||
- `type`, `number-type` (aliased to `num-type`)
|
||||
- `type`, `number.type` (aliased to `num.type`)
|
||||
- `string` (aliased to `str`)
|
||||
- `string->number` (aliased to to `str->num`)
|
||||
- `string->binary` and `binary->string` (aliased to `str->bin` and `bin->str`)
|
||||
- `number->binary` and `binary->number` (aliased to `num->bin` and `bin->num`)
|
||||
- `regex-find`
|
||||
- `regex.find`
|
||||
- `system`
|
||||
- Arithmetic operations: `+`, `-`, `*`, `/`, `%`, `^`, `abs`
|
||||
- Arithmetic operations: `+`, `-`, `*`, `/`, `^`, `abs`, `mod`, `rem` (aliased to `%`)
|
||||
- Trigonometric functions: `acos`, `asin`, `atan`, `cos`, `sin`, `tan`
|
||||
- Comparisons: `>`, `<`, `>=`, `<=`, `=`
|
||||
- File IO: `read-file`, `read-file-binary`, `write-file-binary`, `append-file-binary`
|
||||
- List: `chunks`, `sort`, `unique` (aliased to `uniq`), `min`, `max`
|
||||
- String: `trim`, `split`
|
||||
- Enumerable: `length` (aliased to `len`), `nth`, `first`, `second`, `third`, `last`, `rest`, `slice`
|
||||
- String: `string.trim`, `string.split`
|
||||
- List: `concat`, `chunks`, `sort`, `unique` (aliased to `uniq`), `min`, `max`
|
||||
- File: `file.size`, `file.open`, `file.close`, `file.read`, `file.write`
|
||||
|
||||
### Core Library
|
||||
- `nil`, `nil?`, `list?`
|
||||
|
@ -64,12 +63,15 @@ MOROS Lisp is a Lisp-1 dialect inspired by Scheme, Clojure, and Ruby!
|
|||
- `map`, `reduce`, `reverse` (aliased to `rev`), `range`, `filter`, `intersection`
|
||||
- `not`, `and`, `or`
|
||||
- `let`
|
||||
- `join-string` (aliased to `join-str`), `lines`, `words`, `chars`
|
||||
- `string.join` (aliased to `str.join`), `lines`, `words`, `chars`
|
||||
- `regex.match?`
|
||||
|
||||
### File Library
|
||||
- `read`, `write`, `append`
|
||||
- `read-binary`, `write-binary`, `append-binary`
|
||||
- `read-line`, `read-char`
|
||||
- `p`, `print`
|
||||
- `write-file`, `append-file`
|
||||
- `uptime`, `realtime`
|
||||
- `regex-match?`
|
||||
- `p`, `print`
|
||||
|
||||
### Compatibility Library
|
||||
|
||||
|
@ -194,3 +196,6 @@ language and reading from the filesystem.
|
|||
- Add full support for line and inline comments
|
||||
- Add params to function representations
|
||||
- Add docstring to functions
|
||||
|
||||
### Unreleased
|
||||
- Add file, number, string, and regex namespaces
|
||||
|
|
|
@ -64,12 +64,12 @@
|
|||
(def (reverse x)
|
||||
"Reverse list"
|
||||
(if (nil? x) x
|
||||
(append (reverse (tail x)) (cons (head x) '()))))
|
||||
(concat (reverse (tail x)) (cons (head x) '()))))
|
||||
|
||||
(def (range start stop)
|
||||
"Return a list of integers from start to stop excluded"
|
||||
(if (= start stop) nil
|
||||
(append (list start) (range (+ start 1) stop))))
|
||||
(concat (list start) (range (+ start 1) stop))))
|
||||
|
||||
(def (min lst)
|
||||
"Return the minimum element of the list"
|
||||
|
@ -82,56 +82,27 @@
|
|||
(def (abs x)
|
||||
(if (> x 0) x (- x)))
|
||||
|
||||
(def (join-string ls s)
|
||||
(def (mod a b)
|
||||
(rem (+ (rem a b) b) b))
|
||||
|
||||
(def (string.join 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 (regex.match? pattern s)
|
||||
(not (nil? (regex.find pattern str))))
|
||||
|
||||
(def (read-char)
|
||||
"Read char from the console"
|
||||
(binary->string (read-file-binary "/dev/console" 4)))
|
||||
(def (lines text)
|
||||
"Split text into a list of lines"
|
||||
(string.split (string.trim text) "\n"))
|
||||
|
||||
(def (p exp)
|
||||
"Print expression to the console"
|
||||
(do
|
||||
(append-file-binary "/dev/console" (string->binary (string exp)))
|
||||
'()))
|
||||
(def (words text)
|
||||
"Split text into a list of words"
|
||||
(string.split text " "))
|
||||
|
||||
(def (print exp)
|
||||
"Print expression to the console with a newline"
|
||||
(p (string exp "\n")))
|
||||
|
||||
(def (uptime)
|
||||
(binary->number (read-file-binary "/dev/clk/uptime" 8) "float"))
|
||||
|
||||
(def (realtime)
|
||||
(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 (chars text)
|
||||
"Split text into a list of chars"
|
||||
(string.split text ""))
|
||||
|
||||
(def (first lst)
|
||||
(nth lst 0))
|
||||
|
@ -147,9 +118,13 @@
|
|||
(if (= (length lst) 0) 0 (- (length lst) 1))))
|
||||
|
||||
# Short aliases
|
||||
|
||||
(var % rem)
|
||||
(var str string)
|
||||
(var num-type number-type)
|
||||
(var join-str join-string)
|
||||
(var str.split string.split)
|
||||
(var str.join string.join)
|
||||
(var str.trim string.trim)
|
||||
(var num.type number.type)
|
||||
(var str->num string->number)
|
||||
(var str->bin string->binary)
|
||||
(var num->bin number->binary)
|
||||
|
@ -164,3 +139,5 @@
|
|||
(var len length)
|
||||
(var rev reverse)
|
||||
(var uniq unique)
|
||||
|
||||
(load "/lib/lisp/file.lsp")
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
(var stdin 0)
|
||||
(var stdout 1)
|
||||
(var stderr 2)
|
||||
|
||||
# Read
|
||||
|
||||
(def (read-binary path)
|
||||
"Read binary file"
|
||||
(do
|
||||
(var size (file.size path))
|
||||
(var file (file.open path "r"))
|
||||
(var data (file.read file size))
|
||||
(file.close file)
|
||||
data))
|
||||
|
||||
(def (read path)
|
||||
"Read text file"
|
||||
(binary->string (read-binary path)))
|
||||
|
||||
# Write
|
||||
|
||||
(def (write-binary path data)
|
||||
"Write binary to file"
|
||||
(do
|
||||
(var file (file.open path "w"))
|
||||
(file.write file data)
|
||||
(file.close file)))
|
||||
|
||||
(def (write path text)
|
||||
"Write text to file"
|
||||
(write-binary path (string->binary text)))
|
||||
|
||||
# Append
|
||||
|
||||
(def (append-binary path data)
|
||||
"Append binary to file"
|
||||
(do
|
||||
(var file (file.open path "a"))
|
||||
(file.write file data)
|
||||
(file.close file)))
|
||||
|
||||
(def (append path text)
|
||||
"Append text to file"
|
||||
(append-binary path (string->binary text)))
|
||||
|
||||
# Console
|
||||
|
||||
(def (read-line)
|
||||
"Read line from the console"
|
||||
(string.trim (binary->string (file.read stdin 256))))
|
||||
|
||||
(def (read-char)
|
||||
"Read char from the console"
|
||||
(binary->string (file.read stdin 4)))
|
||||
|
||||
(def (p exp)
|
||||
"Print expression to the console"
|
||||
(do
|
||||
(file.write stdout (string->binary (string exp)))
|
||||
'()))
|
||||
|
||||
(def (print exp)
|
||||
"Print expression to the console with a newline"
|
||||
(p (string exp "\n")))
|
||||
|
||||
# Special
|
||||
|
||||
(def (uptime)
|
||||
(binary->number (read-binary "/dev/clk/uptime") "float"))
|
||||
|
||||
(def (realtime)
|
||||
(binary->number (read-binary "/dev/clk/realtime") "float"))
|
|
@ -15,7 +15,7 @@
|
|||
(str " " (f c) (if (< c 100) " " "") c (ansi-color 0 0)))
|
||||
|
||||
(def (colors fs i j)
|
||||
(join-str (map (fun (c) (color fs c)) (range i j)) ""))
|
||||
(str.join (map (fun (c) (color fs c)) (range i j)) ""))
|
||||
|
||||
(print (colors fg 30 38))
|
||||
(print (colors fg 90 98))
|
||||
|
|
|
@ -50,8 +50,9 @@ pub fn copy_files(verbose: bool) {
|
|||
copy_file("/ini/fonts/zap-vga-8x16.psf", include_bytes!("../../dsk/ini/fonts/zap-vga-8x16.psf"), verbose);
|
||||
|
||||
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("/lib/lisp/core.lsp", include_bytes!("../../dsk/lib/lisp/core.lsp"), verbose);
|
||||
copy_file("/lib/lisp/file.lsp", include_bytes!("../../dsk/lib/lisp/file.lsp"), verbose);
|
||||
//copy_file("/lib/lisp/legacy.lsp", include_bytes!("../../dsk/lib/lisp/legacy.lsp"), verbose);
|
||||
|
||||
copy_file("/tmp/alice.txt", include_bytes!("../../dsk/tmp/alice.txt"), verbose);
|
||||
|
|
|
@ -34,10 +34,10 @@ pub fn default_env() -> Rc<RefCell<Env>> {
|
|||
data.insert("+".to_string(), Exp::Primitive(primitive::lisp_add));
|
||||
data.insert("-".to_string(), Exp::Primitive(primitive::lisp_sub));
|
||||
data.insert("/".to_string(), Exp::Primitive(primitive::lisp_div));
|
||||
data.insert("%".to_string(), Exp::Primitive(primitive::lisp_mod));
|
||||
data.insert("^".to_string(), Exp::Primitive(primitive::lisp_exp));
|
||||
data.insert("<<".to_string(), Exp::Primitive(primitive::lisp_shl));
|
||||
data.insert(">>".to_string(), Exp::Primitive(primitive::lisp_shr));
|
||||
data.insert("rem".to_string(), Exp::Primitive(primitive::lisp_rem));
|
||||
data.insert("cos".to_string(), Exp::Primitive(primitive::lisp_cos));
|
||||
data.insert("acos".to_string(), Exp::Primitive(primitive::lisp_acos));
|
||||
data.insert("asin".to_string(), Exp::Primitive(primitive::lisp_asin));
|
||||
|
@ -46,19 +46,13 @@ pub fn default_env() -> Rc<RefCell<Env>> {
|
|||
data.insert("tan".to_string(), Exp::Primitive(primitive::lisp_tan));
|
||||
data.insert("trunc".to_string(), Exp::Primitive(primitive::lisp_trunc));
|
||||
data.insert("system".to_string(), Exp::Primitive(primitive::lisp_system));
|
||||
data.insert("read-file".to_string(), Exp::Primitive(primitive::lisp_read_file));
|
||||
data.insert("read-file-binary".to_string(), Exp::Primitive(primitive::lisp_read_file_bytes));
|
||||
data.insert("write-file-binary".to_string(), Exp::Primitive(primitive::lisp_write_file_bytes));
|
||||
data.insert("append-file-binary".to_string(), Exp::Primitive(primitive::lisp_append_file_bytes));
|
||||
data.insert("string".to_string(), Exp::Primitive(primitive::lisp_string));
|
||||
data.insert("string->binary".to_string(), Exp::Primitive(primitive::lisp_string_bytes));
|
||||
data.insert("binary->string".to_string(), Exp::Primitive(primitive::lisp_bytes_string));
|
||||
data.insert("binary->number".to_string(), Exp::Primitive(primitive::lisp_bytes_number));
|
||||
data.insert("number->binary".to_string(), Exp::Primitive(primitive::lisp_number_bytes));
|
||||
data.insert("regex-find".to_string(), Exp::Primitive(primitive::lisp_regex_find));
|
||||
data.insert("string->binary".to_string(), Exp::Primitive(primitive::lisp_string_binary));
|
||||
data.insert("binary->string".to_string(), Exp::Primitive(primitive::lisp_binary_string));
|
||||
data.insert("binary->number".to_string(), Exp::Primitive(primitive::lisp_binary_number));
|
||||
data.insert("number->binary".to_string(), Exp::Primitive(primitive::lisp_number_binary));
|
||||
data.insert("string->number".to_string(), Exp::Primitive(primitive::lisp_string_number));
|
||||
data.insert("type".to_string(), Exp::Primitive(primitive::lisp_type));
|
||||
data.insert("number-type".to_string(), Exp::Primitive(primitive::lisp_number_type));
|
||||
data.insert("parse".to_string(), Exp::Primitive(primitive::lisp_parse));
|
||||
data.insert("list".to_string(), Exp::Primitive(primitive::lisp_list));
|
||||
data.insert("sort".to_string(), Exp::Primitive(primitive::lisp_sort));
|
||||
|
@ -67,10 +61,19 @@ pub fn default_env() -> Rc<RefCell<Env>> {
|
|||
data.insert("contains?".to_string(), Exp::Primitive(primitive::lisp_contains));
|
||||
data.insert("slice".to_string(), Exp::Primitive(primitive::lisp_slice));
|
||||
data.insert("chunks".to_string(), Exp::Primitive(primitive::lisp_chunks));
|
||||
data.insert("split".to_string(), Exp::Primitive(primitive::lisp_split));
|
||||
data.insert("trim".to_string(), Exp::Primitive(primitive::lisp_trim));
|
||||
data.insert("length".to_string(), Exp::Primitive(primitive::lisp_length));
|
||||
data.insert("append".to_string(), Exp::Primitive(primitive::lisp_append));
|
||||
data.insert("concat".to_string(), Exp::Primitive(primitive::lisp_concat));
|
||||
|
||||
data.insert("number.type".to_string(), Exp::Primitive(primitive::lisp_number_type));
|
||||
data.insert("regex.find".to_string(), Exp::Primitive(primitive::lisp_regex_find));
|
||||
data.insert("string.split".to_string(), Exp::Primitive(primitive::lisp_string_split));
|
||||
data.insert("string.trim".to_string(), Exp::Primitive(primitive::lisp_string_trim));
|
||||
|
||||
data.insert("file.size".to_string(), Exp::Primitive(primitive::lisp_file_size));
|
||||
data.insert("file.open".to_string(), Exp::Primitive(primitive::lisp_file_open));
|
||||
data.insert("file.read".to_string(), Exp::Primitive(primitive::lisp_file_read));
|
||||
data.insert("file.write".to_string(), Exp::Primitive(primitive::lisp_file_write));
|
||||
data.insert("file.close".to_string(), Exp::Primitive(primitive::lisp_file_close));
|
||||
|
||||
// Setup autocompletion
|
||||
*FUNCTIONS.lock() = data.keys().cloned().chain(BUILT_INS.map(String::from)).collect();
|
||||
|
|
|
@ -20,7 +20,7 @@ pub fn expand_quasiquote(exp: &Exp) -> Result<Exp, Err> {
|
|||
}
|
||||
Exp::List(l) if l.len() == 2 && l[0] == Exp::Sym("unquote-splice".to_string()) => {
|
||||
Ok(Exp::List(vec![
|
||||
Exp::Sym("append".to_string()),
|
||||
Exp::Sym("concat".to_string()),
|
||||
l[1].clone(),
|
||||
expand_quasiquote(&Exp::List(list[1..].to_vec()))?
|
||||
]))
|
||||
|
|
|
@ -445,8 +445,12 @@ fn test_lisp() {
|
|||
assert_eq!(eval!("(^ 2 4)"), "16");
|
||||
assert_eq!(eval!("(^ 2 4 2)"), "256"); // Left to right
|
||||
|
||||
// modulo
|
||||
assert_eq!(eval!("(% 3 2)"), "1");
|
||||
// remainder
|
||||
assert_eq!(eval!("(rem 0 2)"), "0");
|
||||
assert_eq!(eval!("(rem 1 2)"), "1");
|
||||
assert_eq!(eval!("(rem 2 2)"), "0");
|
||||
assert_eq!(eval!("(rem 3 2)"), "1");
|
||||
assert_eq!(eval!("(rem -1 2)"), "-1");
|
||||
|
||||
// comparisons
|
||||
assert_eq!(eval!("(< 6 4)"), "false");
|
||||
|
@ -469,7 +473,8 @@ fn test_lisp() {
|
|||
assert_eq!(eval!("(string \"foo \" 3)"), "\"foo 3\"");
|
||||
assert_eq!(eval!("(equal? \"foo\" \"foo\")"), "true");
|
||||
assert_eq!(eval!("(equal? \"foo\" \"bar\")"), "false");
|
||||
assert_eq!(eval!("(split \"a\nb\nc\" \"\n\")"), "(\"a\" \"b\" \"c\")");
|
||||
assert_eq!(eval!("(string.trim \"abc\n\")"), "\"abc\"");
|
||||
assert_eq!(eval!("(string.split \"a\nb\nc\" \"\n\")"), "(\"a\" \"b\" \"c\")");
|
||||
|
||||
// apply
|
||||
assert_eq!(eval!("(apply + '(1 2 3))"), "6");
|
||||
|
@ -506,9 +511,9 @@ fn test_lisp() {
|
|||
assert_eq!(eval!("(^ 2 128)"), "340282366920938463463374607431768211456"); // -> bigint
|
||||
assert_eq!(eval!("(^ 2.0 128)"), "340282366920938500000000000000000000000.0"); // -> float
|
||||
|
||||
assert_eq!(eval!("(number-type 9223372036854775807)"), "\"int\"");
|
||||
assert_eq!(eval!("(number-type 9223372036854775808)"), "\"bigint\"");
|
||||
assert_eq!(eval!("(number-type 9223372036854776000.0)"), "\"float\"");
|
||||
assert_eq!(eval!("(number.type 9223372036854775807)"), "\"int\"");
|
||||
assert_eq!(eval!("(number.type 9223372036854775808)"), "\"bigint\"");
|
||||
assert_eq!(eval!("(number.type 9223372036854776000.0)"), "\"float\"");
|
||||
|
||||
// quasiquote
|
||||
eval!("(variable x 'a)");
|
||||
|
@ -532,7 +537,7 @@ fn test_lisp() {
|
|||
assert_eq!(eval!("foo"), "10");
|
||||
|
||||
// args
|
||||
eval!("(variable list* (function args (append args '())))");
|
||||
eval!("(variable list* (function args (concat args '())))");
|
||||
assert_eq!(eval!("(list* 1 2 3)"), "(1 2 3)");
|
||||
|
||||
// comments
|
||||
|
|
|
@ -25,7 +25,7 @@ use nom::sequence::preceded;
|
|||
use nom::sequence::tuple;
|
||||
|
||||
fn is_symbol_letter(c: char) -> bool {
|
||||
let chars = "<>=-+*/%^?:";
|
||||
let chars = "<>=-+*/%^?.";
|
||||
c.is_alphanumeric() || chars.contains(c)
|
||||
}
|
||||
|
||||
|
@ -34,6 +34,8 @@ fn parse_str(input: &str) -> IResult<&str, Exp> {
|
|||
value("\\", tag("\\")),
|
||||
value("\"", tag("\"")),
|
||||
value("\n", tag("n")),
|
||||
value("\r", tag("r")),
|
||||
value("\t", tag("t")),
|
||||
)))), |inner| inner.unwrap_or("".to_string()));
|
||||
let (input, s) = delimited(char('"'), escaped, char('"'))(input)?;
|
||||
Ok((input, Exp::Str(s)))
|
||||
|
|
|
@ -4,8 +4,9 @@ use super::{float, number, string};
|
|||
use super::{bytes, numbers, strings};
|
||||
|
||||
use crate::{ensure_length_eq, ensure_length_gt, expected, could_not};
|
||||
use crate::api::fs;
|
||||
use crate::api::regex::Regex;
|
||||
use crate::api::syscall;
|
||||
use crate::sys::fs::OpenFlag;
|
||||
use crate::usr::shell;
|
||||
|
||||
use alloc::format;
|
||||
|
@ -75,7 +76,7 @@ pub fn lisp_div(args: &[Exp]) -> Result<Exp, Err> {
|
|||
Ok(Exp::Num(res))
|
||||
}
|
||||
|
||||
pub fn lisp_mod(args: &[Exp]) -> Result<Exp, Err> {
|
||||
pub fn lisp_rem(args: &[Exp]) -> Result<Exp, Err> {
|
||||
ensure_length_gt!(args, 0);
|
||||
let args = numbers(args)?;
|
||||
for arg in &args[1..] {
|
||||
|
@ -162,49 +163,6 @@ pub fn lisp_system(args: &[Exp]) -> Result<Exp, Err> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn lisp_read_file(args: &[Exp]) -> Result<Exp, Err> {
|
||||
ensure_length_eq!(args, 1);
|
||||
let path = string(&args[0])?;
|
||||
let contents = fs::read_to_string(&path).or(could_not!("read file"))?;
|
||||
Ok(Exp::Str(contents))
|
||||
}
|
||||
|
||||
pub fn lisp_read_file_bytes(args: &[Exp]) -> Result<Exp, Err> {
|
||||
ensure_length_eq!(args, 2);
|
||||
let path = string(&args[0])?;
|
||||
let len = number(&args[1])?;
|
||||
let mut buf = vec![0; len.try_into()?];
|
||||
let n = fs::read(&path, &mut buf).or(could_not!("read file"))?;
|
||||
buf.resize(n, 0);
|
||||
Ok(Exp::List(buf.iter().map(|b| Exp::Num(Number::from(*b))).collect()))
|
||||
}
|
||||
|
||||
pub fn lisp_write_file_bytes(args: &[Exp]) -> Result<Exp, Err> {
|
||||
ensure_length_eq!(args, 2);
|
||||
let path = string(&args[0])?;
|
||||
match &args[1] {
|
||||
Exp::List(list) => {
|
||||
let buf = bytes(list)?;
|
||||
let n = fs::write(&path, &buf).or(could_not!("write file"))?;
|
||||
Ok(Exp::Num(Number::from(n)))
|
||||
}
|
||||
_ => expected!("second argument to be a list")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lisp_append_file_bytes(args: &[Exp]) -> Result<Exp, Err> {
|
||||
ensure_length_eq!(args, 2);
|
||||
let path = string(&args[0])?;
|
||||
match &args[1] {
|
||||
Exp::List(list) => {
|
||||
let buf = bytes(list)?;
|
||||
let n = fs::append(&path, &buf).or(could_not!("write file"))?;
|
||||
Ok(Exp::Num(Number::from(n)))
|
||||
}
|
||||
_ => expected!("second argument to be a list")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lisp_string(args: &[Exp]) -> Result<Exp, Err> {
|
||||
let args: Vec<String> = args.iter().map(|arg| match arg {
|
||||
Exp::Str(s) => format!("{}", s),
|
||||
|
@ -213,14 +171,14 @@ pub fn lisp_string(args: &[Exp]) -> Result<Exp, Err> {
|
|||
Ok(Exp::Str(args.join("")))
|
||||
}
|
||||
|
||||
pub fn lisp_string_bytes(args: &[Exp]) -> Result<Exp, Err> {
|
||||
pub fn lisp_string_binary(args: &[Exp]) -> Result<Exp, Err> {
|
||||
ensure_length_eq!(args, 1);
|
||||
let s = string(&args[0])?;
|
||||
let buf = s.as_bytes();
|
||||
Ok(Exp::List(buf.iter().map(|b| Exp::Num(Number::from(*b))).collect()))
|
||||
}
|
||||
|
||||
pub fn lisp_bytes_string(args: &[Exp]) -> Result<Exp, Err> {
|
||||
pub fn lisp_binary_string(args: &[Exp]) -> Result<Exp, Err> {
|
||||
ensure_length_eq!(args, 1);
|
||||
match &args[0] {
|
||||
Exp::List(list) => {
|
||||
|
@ -232,7 +190,7 @@ pub fn lisp_bytes_string(args: &[Exp]) -> Result<Exp, Err> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn lisp_bytes_number(args: &[Exp]) -> Result<Exp, Err> {
|
||||
pub fn lisp_binary_number(args: &[Exp]) -> Result<Exp, Err> {
|
||||
ensure_length_eq!(args, 2);
|
||||
match (&args[0], &args[1]) { // TODO: default type to "int" and make it optional
|
||||
(Exp::List(list), Exp::Str(kind)) => {
|
||||
|
@ -248,25 +206,12 @@ pub fn lisp_bytes_number(args: &[Exp]) -> Result<Exp, Err> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn lisp_number_bytes(args: &[Exp]) -> Result<Exp, Err> {
|
||||
pub fn lisp_number_binary(args: &[Exp]) -> Result<Exp, Err> {
|
||||
ensure_length_eq!(args, 1);
|
||||
let n = number(&args[0])?;
|
||||
Ok(Exp::List(n.to_be_bytes().iter().map(|b| Exp::Num(Number::from(*b))).collect()))
|
||||
}
|
||||
|
||||
pub fn lisp_regex_find(args: &[Exp]) -> Result<Exp, Err> {
|
||||
ensure_length_eq!(args, 2);
|
||||
match (&args[0], &args[1]) {
|
||||
(Exp::Str(regex), Exp::Str(s)) => {
|
||||
let res = Regex::new(regex).find(s).map(|(a, b)| {
|
||||
vec![Exp::Num(Number::from(a)), Exp::Num(Number::from(b))]
|
||||
}).unwrap_or(vec![]);
|
||||
Ok(Exp::List(res))
|
||||
}
|
||||
_ => expected!("arguments to be a regex and a string")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lisp_string_number(args: &[Exp]) -> Result<Exp, Err> {
|
||||
ensure_length_eq!(args, 1);
|
||||
let s = string(&args[0])?;
|
||||
|
@ -289,16 +234,6 @@ pub fn lisp_type(args: &[Exp]) -> Result<Exp, Err> {
|
|||
Ok(Exp::Str(exp.to_string()))
|
||||
}
|
||||
|
||||
pub fn lisp_number_type(args: &[Exp]) -> Result<Exp, Err> {
|
||||
ensure_length_eq!(args, 1);
|
||||
match args[0] {
|
||||
Exp::Num(Number::Int(_)) => Ok(Exp::Str("int".to_string())),
|
||||
Exp::Num(Number::BigInt(_)) => Ok(Exp::Str("bigint".to_string())),
|
||||
Exp::Num(Number::Float(_)) => Ok(Exp::Str("float".to_string())),
|
||||
_ => expected!("argument to be a number")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lisp_parse(args: &[Exp]) -> Result<Exp, Err> {
|
||||
ensure_length_eq!(args, 1);
|
||||
let s = string(&args[0])?;
|
||||
|
@ -334,10 +269,10 @@ pub fn lisp_sort(args: &[Exp]) -> Result<Exp, Err> {
|
|||
|
||||
pub fn lisp_contains(args: &[Exp]) -> Result<Exp, Err> {
|
||||
ensure_length_eq!(args, 2);
|
||||
if let Exp::List(list) = &args[0] {
|
||||
Ok(Exp::Bool(list.contains(&args[1])))
|
||||
} else {
|
||||
expected!("first argument to be a list")
|
||||
match &args[0] {
|
||||
Exp::List(l) => Ok(Exp::Bool(l.contains(&args[1]))),
|
||||
Exp::Str(s) => Ok(Exp::Bool(s.contains(&string(&args[1])?))),
|
||||
_ => expected!("first argument to be a list or a string"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -393,7 +328,58 @@ pub fn lisp_chunks(args: &[Exp]) -> Result<Exp, Err> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn lisp_split(args: &[Exp]) -> Result<Exp, Err> {
|
||||
pub fn lisp_length(args: &[Exp]) -> Result<Exp, Err> {
|
||||
ensure_length_eq!(args, 1);
|
||||
match &args[0] {
|
||||
Exp::List(list) => Ok(Exp::Num(Number::from(list.len()))),
|
||||
Exp::Str(string) => Ok(Exp::Num(Number::from(string.chars().count()))),
|
||||
_ => expected!("a list or a string")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lisp_concat(args: &[Exp]) -> Result<Exp, Err> {
|
||||
// TODO: This could also concat strings
|
||||
let mut res = vec![];
|
||||
for arg in args {
|
||||
if let Exp::List(list) = arg {
|
||||
res.extend_from_slice(list);
|
||||
} else {
|
||||
return expected!("a list")
|
||||
}
|
||||
}
|
||||
Ok(Exp::List(res))
|
||||
}
|
||||
|
||||
// Number module
|
||||
|
||||
pub fn lisp_number_type(args: &[Exp]) -> Result<Exp, Err> {
|
||||
ensure_length_eq!(args, 1);
|
||||
match args[0] {
|
||||
Exp::Num(Number::Int(_)) => Ok(Exp::Str("int".to_string())),
|
||||
Exp::Num(Number::BigInt(_)) => Ok(Exp::Str("bigint".to_string())),
|
||||
Exp::Num(Number::Float(_)) => Ok(Exp::Str("float".to_string())),
|
||||
_ => expected!("argument to be a number")
|
||||
}
|
||||
}
|
||||
|
||||
// Regex module
|
||||
|
||||
pub fn lisp_regex_find(args: &[Exp]) -> Result<Exp, Err> {
|
||||
ensure_length_eq!(args, 2);
|
||||
match (&args[0], &args[1]) {
|
||||
(Exp::Str(regex), Exp::Str(s)) => {
|
||||
let res = Regex::new(regex).find(s).map(|(a, b)| {
|
||||
vec![Exp::Num(Number::from(a)), Exp::Num(Number::from(b))]
|
||||
}).unwrap_or(vec![]);
|
||||
Ok(Exp::List(res))
|
||||
}
|
||||
_ => expected!("arguments to be a regex and a string")
|
||||
}
|
||||
}
|
||||
|
||||
// String module
|
||||
|
||||
pub fn lisp_string_split(args: &[Exp]) -> Result<Exp, Err> {
|
||||
ensure_length_eq!(args, 2);
|
||||
match (&args[0], &args[1]) {
|
||||
(Exp::Str(string), Exp::Str(pattern)) => {
|
||||
|
@ -409,7 +395,7 @@ pub fn lisp_split(args: &[Exp]) -> Result<Exp, Err> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn lisp_trim(args: &[Exp]) -> Result<Exp, Err> {
|
||||
pub fn lisp_string_trim(args: &[Exp]) -> Result<Exp, Err> {
|
||||
ensure_length_eq!(args, 1);
|
||||
if let Exp::Str(s) = &args[0] {
|
||||
Ok(Exp::Str(s.trim().to_string()))
|
||||
|
@ -418,23 +404,76 @@ pub fn lisp_trim(args: &[Exp]) -> Result<Exp, Err> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn lisp_length(args: &[Exp]) -> Result<Exp, Err> {
|
||||
// File module
|
||||
|
||||
pub fn lisp_file_size(args: &[Exp]) -> Result<Exp, Err> {
|
||||
ensure_length_eq!(args, 1);
|
||||
match &args[0] {
|
||||
Exp::List(list) => Ok(Exp::Num(Number::from(list.len()))),
|
||||
Exp::Str(string) => Ok(Exp::Num(Number::from(string.chars().count()))),
|
||||
_ => expected!("a list or a string")
|
||||
let path = string(&args[0])?;
|
||||
match syscall::info(&path) {
|
||||
Some(info) => Ok(Exp::Num(Number::from(info.size() as usize))),
|
||||
None => return could_not!("open file"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lisp_append(args: &[Exp]) -> Result<Exp, Err> {
|
||||
let mut res = vec![];
|
||||
for arg in args {
|
||||
if let Exp::List(list) = arg {
|
||||
res.extend_from_slice(list);
|
||||
} else {
|
||||
return expected!("a list")
|
||||
}
|
||||
pub fn lisp_file_open(args: &[Exp]) -> Result<Exp, Err> {
|
||||
ensure_length_eq!(args, 2);
|
||||
let path = string(&args[0])?;
|
||||
let mode = string(&args[1])?;
|
||||
|
||||
let mut flags = match mode.as_ref() {
|
||||
"a" => OpenFlag::Append as usize,
|
||||
"r" => OpenFlag::Read as usize,
|
||||
"w" => OpenFlag::Write as usize,
|
||||
_ => return expected!("valid mode"),
|
||||
};
|
||||
flags |= match syscall::info(&path) {
|
||||
Some(info) if info.is_device() => OpenFlag::Device as usize,
|
||||
Some(info) if info.is_dir() => OpenFlag::Dir as usize,
|
||||
None if &mode == "r" => return could_not!("open file"),
|
||||
None => OpenFlag::Create as usize,
|
||||
_ => 0
|
||||
};
|
||||
|
||||
match syscall::open(&path, flags) {
|
||||
Some(handle) => Ok(Exp::Num(Number::from(handle))),
|
||||
None => could_not!("open file"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lisp_file_close(args: &[Exp]) -> Result<Exp, Err> {
|
||||
ensure_length_eq!(args, 1);
|
||||
let handle = number(&args[0])?.try_into()?;
|
||||
syscall::close(handle);
|
||||
Ok(Exp::List(vec![]))
|
||||
}
|
||||
|
||||
pub fn lisp_file_read(args: &[Exp]) -> Result<Exp, Err> {
|
||||
ensure_length_eq!(args, 2);
|
||||
let handle = number(&args[0])?.try_into()?;
|
||||
let len = number(&args[1])?;
|
||||
|
||||
let mut buf = vec![0; len.try_into()?];
|
||||
match syscall::read(handle, &mut buf) {
|
||||
Some(n) => {
|
||||
buf.resize(n, 0);
|
||||
Ok(Exp::List(buf.iter().map(|b| Exp::Num(Number::from(*b))).collect()))
|
||||
}
|
||||
None => could_not!("read file"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lisp_file_write(args: &[Exp]) -> Result<Exp, Err> {
|
||||
ensure_length_eq!(args, 2);
|
||||
let handle = number(&args[0])?.try_into()?;
|
||||
|
||||
match &args[1] {
|
||||
Exp::List(list) => {
|
||||
let buf = bytes(list)?;
|
||||
match syscall::write(handle, &buf) {
|
||||
Some(n) => Ok(Exp::Num(Number::from(n))),
|
||||
None => could_not!("write file"),
|
||||
}
|
||||
}
|
||||
_ => expected!("second argument to be a list")
|
||||
}
|
||||
Ok(Exp::List(res))
|
||||
}
|
||||
|
|
|
@ -56,21 +56,20 @@ of the Shell.</p>
|
|||
<h3>Primitive Operators</h3>
|
||||
|
||||
<ul>
|
||||
<li><code>append</code></li>
|
||||
<li><code>type</code>, <code>number-type</code> (aliased to <code>num-type</code>)</li>
|
||||
<li><code>type</code>, <code>number.type</code> (aliased to <code>num.type</code>)</li>
|
||||
<li><code>string</code> (aliased to <code>str</code>)</li>
|
||||
<li><code>string->number</code> (aliased to to <code>str->num</code>)</li>
|
||||
<li><code>string->binary</code> and <code>binary->string</code> (aliased to <code>str->bin</code> and <code>bin->str</code>)</li>
|
||||
<li><code>number->binary</code> and <code>binary->number</code> (aliased to <code>num->bin</code> and <code>bin->num</code>)</li>
|
||||
<li><code>regex-find</code></li>
|
||||
<li><code>regex.find</code></li>
|
||||
<li><code>system</code></li>
|
||||
<li>Arithmetic operations: <code>+</code>, <code>-</code>, <code>*</code>, <code>/</code>, <code>%</code>, <code>^</code>, <code>abs</code></li>
|
||||
<li>Arithmetic operations: <code>+</code>, <code>-</code>, <code>*</code>, <code>/</code>, <code>^</code>, <code>abs</code>, <code>mod</code>, <code>rem</code> (aliased to <code>%</code>)</li>
|
||||
<li>Trigonometric functions: <code>acos</code>, <code>asin</code>, <code>atan</code>, <code>cos</code>, <code>sin</code>, <code>tan</code></li>
|
||||
<li>Comparisons: <code>></code>, <code><</code>, <code>>=</code>, <code><=</code>, <code>=</code></li>
|
||||
<li>File IO: <code>read-file</code>, <code>read-file-binary</code>, <code>write-file-binary</code>, <code>append-file-binary</code></li>
|
||||
<li>List: <code>chunks</code>, <code>sort</code>, <code>unique</code> (aliased to <code>uniq</code>), <code>min</code>, <code>max</code></li>
|
||||
<li>String: <code>trim</code>, <code>split</code></li>
|
||||
<li>Enumerable: <code>length</code> (aliased to <code>len</code>), <code>nth</code>, <code>first</code>, <code>second</code>, <code>third</code>, <code>last</code>, <code>rest</code>, <code>slice</code></li>
|
||||
<li>String: <code>string.trim</code>, <code>string.split</code></li>
|
||||
<li>List: <code>concat</code>, <code>chunks</code>, <code>sort</code>, <code>unique</code> (aliased to <code>uniq</code>), <code>min</code>, <code>max</code></li>
|
||||
<li>File: <code>file.size</code>, <code>file.open</code>, <code>file.close</code>, <code>file.read</code>, <code>file.write</code></li>
|
||||
</ul>
|
||||
|
||||
<h3>Core Library</h3>
|
||||
|
@ -83,12 +82,18 @@ of the Shell.</p>
|
|||
<li><code>map</code>, <code>reduce</code>, <code>reverse</code> (aliased to <code>rev</code>), <code>range</code>, <code>filter</code>, <code>intersection</code></li>
|
||||
<li><code>not</code>, <code>and</code>, <code>or</code></li>
|
||||
<li><code>let</code></li>
|
||||
<li><code>join-string</code> (aliased to <code>join-str</code>), <code>lines</code>, <code>words</code>, <code>chars</code></li>
|
||||
<li><code>string.join</code> (aliased to <code>str.join</code>), <code>lines</code>, <code>words</code>, <code>chars</code></li>
|
||||
<li><code>regex.match?</code></li>
|
||||
</ul>
|
||||
|
||||
<h3>File Library</h3>
|
||||
|
||||
<ul>
|
||||
<li><code>read</code>, <code>write</code>, <code>append</code></li>
|
||||
<li><code>read-binary</code>, <code>write-binary</code>, <code>append-binary</code></li>
|
||||
<li><code>read-line</code>, <code>read-char</code></li>
|
||||
<li><code>p</code>, <code>print</code></li>
|
||||
<li><code>write-file</code>, <code>append-file</code></li>
|
||||
<li><code>uptime</code>, <code>realtime</code></li>
|
||||
<li><code>regex-match?</code></li>
|
||||
<li><code>p</code>, <code>print</code></li>
|
||||
</ul>
|
||||
|
||||
<h3>Compatibility Library</h3>
|
||||
|
@ -229,5 +234,11 @@ language and reading from the filesystem.</p>
|
|||
<li>Add params to function representations</li>
|
||||
<li>Add docstring to functions</li>
|
||||
</ul>
|
||||
|
||||
<h3>Unreleased</h3>
|
||||
|
||||
<ul>
|
||||
<li>Add file, number, string, and regex namespaces</li>
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
||||
|
|
Loading…
Reference in New Issue