mirror of
https://github.com/teo3300/rust-mal.git
synced 2026-01-12 01:05:32 +01:00
Adding string processing
- 'character' type, only contructible by decomposing strings - 'boom' builtin function ;) to create a list of characters from a string - string library with basic manipulation functionalities - slightly improved Makefile
This commit is contained in:
16
Makefile
16
Makefile
@ -1,17 +1,23 @@
|
||||
default: build-release
|
||||
|
||||
build-release:
|
||||
cargo build --release
|
||||
@echo "Build release"
|
||||
@cargo build --release
|
||||
|
||||
test:
|
||||
cargo test --release
|
||||
@echo "Test release"
|
||||
@cargo test --release
|
||||
|
||||
conf:
|
||||
mkdir -p ${HOME}/.config/mal
|
||||
@echo "Copy core and libraries"
|
||||
@mkdir -p ${HOME}/.config/mal
|
||||
cp -f core/core.mal ${HOME}/.config/mal/
|
||||
mkdir -p ${HOME}/.config/mal/libs
|
||||
@mkdir -p ${HOME}/.config/mal/libs
|
||||
cp -f libs/* ${HOME}/.config/mal/libs/
|
||||
|
||||
install: build-release test conf
|
||||
@echo "Install mal"
|
||||
sudo cp target/release/rust-mal /usr/local/bin/mal
|
||||
sudo chown ${USER} /usr/local/bin/mal
|
||||
@sudo chown ${USER} /usr/local/bin/mal
|
||||
@echo "To start mal run:"
|
||||
@printf "\tmal [path/module.mal ...]\n\n"
|
||||
|
||||
@ -139,6 +139,8 @@
|
||||
(def! BANNER
|
||||
(str
|
||||
"; rust-mal: a toy lisp interpreter written in rust\n"
|
||||
"; $ mal [filename ...] : load specified modules at start\n"
|
||||
"; (load-file <name>) : load specified module while mal is running\n"
|
||||
"; (find [pattern...]) : list symbols matching all patterns\n"
|
||||
"; (help <symbol>) : print information about a symbol\n"
|
||||
";\n"
|
||||
|
||||
40
libs/string.mal
Normal file
40
libs/string.mal
Normal file
@ -0,0 +1,40 @@
|
||||
(def! char (fn* [l]
|
||||
(car (boom l))))
|
||||
|
||||
(def! char? (fn* [a]
|
||||
(= (type a) :char)))
|
||||
|
||||
(def! string? (fn* [a]
|
||||
(= (type a) :string)))
|
||||
|
||||
(def! strc (fn* [l]
|
||||
(def! strc-r (fn* [l s]
|
||||
(if (empty? l)
|
||||
s
|
||||
(strc-r (cdr l) (str s (car l))))))
|
||||
(strc-r l "")))
|
||||
|
||||
(def! split (fn* [s c]
|
||||
"Split the string at every occurrence of character sc"
|
||||
(if (and (string? s)
|
||||
(char? c))
|
||||
(def! split-r (fn* [l t r]
|
||||
(if (empty? l)
|
||||
(cons t r)
|
||||
(do (def! cc (car l))
|
||||
(if (= cc c)
|
||||
(split-r (cdr l) "" (cons t r))
|
||||
(split-r (cdr l) (str t cc) r))))))
|
||||
(raise "split: accepts a string and a char as arguments"))
|
||||
(reverse (split-r (boom s) "" '()))))
|
||||
|
||||
(def! join (fn* [l s]
|
||||
"Join element of list l to a stiring, using s as separator"
|
||||
(def! join-r (fn* [l t]
|
||||
(if (empty? l)
|
||||
t
|
||||
(join-r (cdr l) (str t s (car l))))))
|
||||
(join-r (cdr l) (car l))))
|
||||
|
||||
(def! chsub (fn* [s c1 c2]
|
||||
(strc (map-if (fn* [x] (= x c1)) (fn* [x] c2) (boom s)))))
|
||||
@ -1,8 +1,8 @@
|
||||
use std::{cell::RefCell, env, rc::Rc};
|
||||
|
||||
use crate::env::{
|
||||
any_zero, arithmetic_op, car, comparison_op, env_new, env_set, mal_car, mal_cdr, mal_cons,
|
||||
mal_exit, Env,
|
||||
any_zero, arithmetic_op, car, comparison_op, env_new, env_set, mal_boom, mal_car, mal_cdr,
|
||||
mal_cons, mal_exit, Env,
|
||||
};
|
||||
|
||||
// This is the first time I implement a macro, and I'm copying it
|
||||
@ -75,6 +75,8 @@ pub fn ns_init() -> Env {
|
||||
"=" => Fun(mal_equals, "Return true if the first two parameters are the same type and content, in case of lists propagate to all elements (NOT IMPLEMENTED for 'Map', 'Fun' and 'MalFun')"),
|
||||
"car" => Fun(|a| mal_car(car(a)?), "Returns the first element of the list, NIL if its empty"),
|
||||
"cdr" => Fun(|a| mal_cdr(car(a)?), "Returns all the list but the first element"),
|
||||
// A tribute to PHP's explode (PHP, a language I never used)
|
||||
"boom" => Fun(mal_boom, "Split a string into a list of string\n; BE CAREFUL WHEN USING"),
|
||||
"read-string" => Fun(|a| read_str(Reader::new().push(car(a)?.if_string()?)).map_err(MalErr::severe), "Tokenize and read the first argument"),
|
||||
"slurp" => Fun(|a| Ok(Str(read_file(car(a)?.if_string()?)?)), "Read a file and return the content as a string"),
|
||||
"atom" => Fun(|a| Ok(Atom(Rc::new(RefCell::new(car(a).unwrap_or_default().clone())))), "Return an atom pointing to the given arg"),
|
||||
|
||||
@ -257,3 +257,9 @@ pub fn mal_exit(list: &[MalType]) -> MalRet {
|
||||
_ => exit(-1),
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: find another way to process strings
|
||||
pub fn mal_boom(args: &[MalType]) -> MalRet {
|
||||
let string = car(args)?.if_string()?;
|
||||
Ok(M::List(string.chars().map(M::Ch).collect()))
|
||||
}
|
||||
|
||||
@ -54,6 +54,13 @@ pub fn pr_str(ast: &MalType, print_readably: bool) -> String {
|
||||
M::Fun(..) => "#<builtin>".to_string(),
|
||||
M::MalFun { .. } => "#<function>".to_string(),
|
||||
M::Atom(sub) => format!("Atom({})", pr_str(&sub.borrow(), print_readably)),
|
||||
M::Ch(c) => {
|
||||
if print_readably {
|
||||
format!("#\\{}", c)
|
||||
} else {
|
||||
c.to_string()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -23,6 +23,7 @@ pub enum MalType {
|
||||
Sym(MalStr),
|
||||
Key(MalStr),
|
||||
Str(MalStr),
|
||||
Ch(char),
|
||||
Int(isize),
|
||||
Bool(bool),
|
||||
Atom(Rc<RefCell<MalType>>),
|
||||
@ -95,6 +96,7 @@ impl MalType {
|
||||
M::Vector(_) => "vector",
|
||||
M::Map(_) => "map",
|
||||
M::Atom(_) => "atom",
|
||||
M::Ch(_) => "char",
|
||||
})
|
||||
.into())
|
||||
}
|
||||
@ -108,6 +110,7 @@ fn mal_compare(args: (&MalType, &MalType)) -> bool {
|
||||
(M::Nil, M::Nil) => true,
|
||||
(M::Bool(a), M::Bool(b)) => a == b,
|
||||
(M::Int(a), M::Int(b)) => a == b,
|
||||
(M::Ch(a), M::Ch(b)) => a == b,
|
||||
(M::Key(a), M::Key(b)) | (M::Str(a), M::Str(b)) | (M::Sym(a), M::Sym(b)) => a == b,
|
||||
(M::List(a), M::List(b)) | (M::Vector(a), M::Vector(b)) => {
|
||||
a.len() == b.len() && a.iter().zip(b.iter()).all(mal_compare)
|
||||
|
||||
Reference in New Issue
Block a user