do some things and stuff?
This commit is contained in:
parent
7a028fbaa2
commit
1337da395c
|
@ -22,19 +22,20 @@ fn coll? {
|
|||
(_) -> false
|
||||
}
|
||||
|
||||
fn ordered? {
|
||||
"Returns true if a value is an indexed collection: list or tuple."
|
||||
fn indexed? {
|
||||
"Returns true if a value is indexed (can use `at`): list, tuple, or string."
|
||||
(coll as :list) -> true
|
||||
(coll as :tuple) -> true
|
||||
(coll as :string) -> true
|
||||
(_) -> false
|
||||
}
|
||||
|
||||
fn assoc? {
|
||||
"Returns true if a value is an associative collection: a dict or a pkg."
|
||||
(d as :dict) -> true
|
||||
(_) -> false
|
||||
}
|
||||
& for now we don't need this: we don't have pkgs
|
||||
& fn assoc? {
|
||||
& "Returns true if a value is an associative collection: a dict or a pkg."
|
||||
& (d as :dict) -> true
|
||||
& (_) -> false
|
||||
& }
|
||||
|
||||
& &&& nil: working with nothing
|
||||
|
||||
|
@ -121,13 +122,13 @@ fn fn? {
|
|||
|
||||
& what we need for some very basic list manipulation
|
||||
fn first {
|
||||
"Retrieves the first element of an ordered collection--a tuple or a list. If the collection is empty, returns nil."
|
||||
"Retrieves the first element of an ordered collection: tuple, list, or string. If the collection is empty, returns nil."
|
||||
([]) -> nil
|
||||
(()) -> nil
|
||||
& ("") -> nil
|
||||
("") -> nil
|
||||
(xs as :list) -> base :first (xs)
|
||||
(xs as :tuple) -> base :first (xs)
|
||||
& (str as :string) -> base :slice (str, 0, 1)
|
||||
(str as :string) -> base :first (str)
|
||||
}
|
||||
|
||||
fn rest {
|
||||
|
@ -136,7 +137,13 @@ fn rest {
|
|||
(()) -> ()
|
||||
(xs as :list) -> base :rest (xs)
|
||||
(xs as :tuple) -> base :rest (xs)
|
||||
& (str as :string) -> base :rest (str)
|
||||
(str as :string) -> base :rest (str)
|
||||
}
|
||||
|
||||
fn last {
|
||||
"Returns the last element of a list or tuple."
|
||||
(xs) if indexed? (xs) -> base :last (xs)
|
||||
(_) -> nil
|
||||
}
|
||||
|
||||
fn inc {
|
||||
|
@ -178,6 +185,21 @@ fn any? {
|
|||
(_) -> false
|
||||
}
|
||||
|
||||
fn at {
|
||||
"Returns the element at index n of a list or tuple, or the byte at index n of a string. Zero-indexed: the first element is at index 0. Returns nil if nothing is found in a list or tuple; returns an empty string if nothing is found in a string."
|
||||
(i as :number) -> at (_, i)
|
||||
(xs as :list, i as :number) -> base :at (xs, i)
|
||||
(xs as :tuple, i as :number) -> base :at (xs, i)
|
||||
(str as :string, i as :number) -> base :at (str, i)
|
||||
(_) -> nil
|
||||
}
|
||||
|
||||
fn second {
|
||||
"Returns the second element of a list or tuple."
|
||||
(xs) if indexed? (xs) -> at (xs, 1)
|
||||
(_) -> nil
|
||||
}
|
||||
|
||||
fn list? {
|
||||
"Returns true if the value is a list."
|
||||
(l as :list) -> true
|
||||
|
@ -185,7 +207,7 @@ fn list? {
|
|||
}
|
||||
|
||||
fn list {
|
||||
"Takes a value and returns it as a list. For values, it simply wraps them in a list. For collections, conversions are as follows. A tuple->list conversion preservers order and length. Unordered collections do not preserve order: sets and dicts don't have predictable or stable ordering in output. Dicts return lists of (key, value) tuples."
|
||||
"Takes a value and returns it as a list. For atomic values, it simply wraps them in a list. For collections, conversions are as follows. A tuple->list conversion preservers order and length. Dicts return lists of `(key, value)`` tuples, but watch out: dicts are not ordered and may spit out their pairs in any order. If you wish to get a list of chars in a string, use `chars`."
|
||||
(x) -> base :list (x)
|
||||
}
|
||||
|
||||
|
@ -257,7 +279,9 @@ fn keep {
|
|||
}
|
||||
|
||||
fn concat {
|
||||
"Combines two lists, strings, or sets."
|
||||
"Combines lists, strings, or sets."
|
||||
(x as :string) -> x
|
||||
(xs as :list) -> xs
|
||||
(x as :string, y as :string) -> "{x}{y}"
|
||||
(xs as :list, ys as :list) -> base :concat (xs, ys)
|
||||
& (xs as :set, ys as :set) -> base :concat (xs, ys)
|
||||
|
@ -265,7 +289,7 @@ fn concat {
|
|||
}
|
||||
|
||||
fn contains? {
|
||||
"Returns true if a set or list contains a value."
|
||||
"Returns true if a list contains a value."
|
||||
& (value, s as :set) -> bool (base :get (s, value))
|
||||
(value, l as :list) -> loop (l) with {
|
||||
([]) -> false
|
||||
|
@ -363,18 +387,13 @@ fn downcase {
|
|||
}
|
||||
|
||||
fn chars {
|
||||
"Takes a string and returns its characters as a list. Works only for strings with only ascii characters. Panics on any non-ascii characters."
|
||||
"Takes a string and returns its characters as a list. Each member of the list corresponds to a utf-8 character."
|
||||
(str as :string) -> match base :chars (str) with {
|
||||
(:ok, chrs) -> chrs
|
||||
(:err, msg) -> panic! msg
|
||||
}
|
||||
}
|
||||
|
||||
fn chars/safe {
|
||||
"Takes a string and returns its characters as a list, wrapped in a result tuple. Works only for strings with only ascii characters. Returns an error tuple on any non-ascii characters."
|
||||
(str as :string) -> base :chars (str)
|
||||
}
|
||||
|
||||
fn ws? {
|
||||
"Tells if a string is a whitespace character."
|
||||
(" ") -> true
|
||||
|
@ -395,16 +414,24 @@ fn strip {
|
|||
(x) -> x
|
||||
}
|
||||
|
||||
fn condenser (charlist, curr_char) -> if and (
|
||||
ws? (curr_char)
|
||||
do charlist > last > ws?)
|
||||
then charlist
|
||||
else append (charlist, curr_char)
|
||||
|
||||
fn condense {
|
||||
"Condenses the whitespace in a string. All whitespace will be replaced by spaces, and any repeated whitespace will be reduced to a single space."
|
||||
(str as :string) -> {
|
||||
let chrs = chars (str)
|
||||
let condensed = fold (condenser, chrs, [])
|
||||
join (condensed)
|
||||
}
|
||||
}
|
||||
|
||||
fn words {
|
||||
"Takes a string and returns a list of the words in the string. Strips all whitespace."
|
||||
(str as :string) -> {
|
||||
let no_punct = strip (str)
|
||||
let strs = split (no_punct, " ")
|
||||
fn worder (l, s) -> if empty? (s)
|
||||
then l
|
||||
else append (l, s)
|
||||
fold (worder, strs, [])
|
||||
}
|
||||
(str as :string) -> do str > condense > strip > split (_, " ")
|
||||
}
|
||||
|
||||
fn sentence {
|
||||
|
@ -804,35 +831,6 @@ fn range {
|
|||
}
|
||||
|
||||
& additional list operations now that we have comparitors
|
||||
fn at {
|
||||
"Returns the element at index n of a list or tuple, or the byte at index n of a string. Zero-indexed: the first element is at index 0. Returns nil if nothing is found in a list or tuple; returns an empty string if nothing is found in a string."
|
||||
(xs as :list, n as :number) -> base :at (xs, n)
|
||||
(xs as :tuple, n as :number) -> base :at (xs, n)
|
||||
& (str as :string, n as :number) -> {
|
||||
& let raw = base :at (str, n)
|
||||
& when {
|
||||
& nil? (raw) -> nil
|
||||
& gte? (raw, 128) -> panic! "not an ASCII char"
|
||||
& true -> base :str_slice (str, n, inc (n))
|
||||
& }
|
||||
& }
|
||||
(_) -> nil
|
||||
}
|
||||
|
||||
& fn first {
|
||||
& "Returns the first element of a list or tuple."
|
||||
& (xs) if ordered? -> at (xs, 0)
|
||||
& }
|
||||
|
||||
fn second {
|
||||
"Returns the second element of a list or tuple."
|
||||
(xs) if ordered? (xs) -> at (xs, 1)
|
||||
}
|
||||
|
||||
fn last {
|
||||
"Returns the last element of a list or tuple."
|
||||
(xs) if ordered? (xs) -> at (xs, dec (count (xs)))
|
||||
}
|
||||
|
||||
fn slice {
|
||||
"Returns a slice of a list or a string, representing a sub-list or sub-string."
|
||||
|
@ -843,12 +841,14 @@ fn slice {
|
|||
neg? (start) -> slice (xs, 0, end)
|
||||
true -> base :slice (xs, start, end)
|
||||
}
|
||||
(str as :string, end as :number) -> base :str_slice (str, 0, end)
|
||||
(str as :string, start as :number, end as :number) -> base :str_slice (str, start, end)
|
||||
(str as :string, end as :number) -> base :slice (str, 0, end)
|
||||
(str as :string, start as :number, end as :number) -> base :slice (str, start, end)
|
||||
}
|
||||
|
||||
fn slice_n {
|
||||
"Returns a slice of a list or a string, representing a sub-list or sub-string."
|
||||
(xs as :list, n as :number) -> slice (xs, 0, n)
|
||||
(str as :string, n as :number) -> slice (str, 0, n)
|
||||
(xs as :list, start as :number, n as :number) -> slice (xs, start, add (start, n))
|
||||
(str as :string, start as :number, n as :number) -> slice (str, start, add (start, n))
|
||||
}
|
||||
|
@ -858,6 +858,54 @@ fn butlast {
|
|||
(xs as :list) -> slice (xs, 0, dec (count (xs)))
|
||||
}
|
||||
|
||||
fn indices_of {
|
||||
"Takes a list or string and returns a list of all the indices where the scrutinee appears. Returns an empty list if the scrutinee does not appear in the search target."
|
||||
(target as :list, scrutinee) -> {
|
||||
fn searcher ((i, indices), curr) -> if eq? (scrutinee, curr)
|
||||
then (inc (i), append (indices, i))
|
||||
else (inc (i), indices)
|
||||
let (_, idxes) = fold (searcher, target, (0, []))
|
||||
idxes
|
||||
}
|
||||
& (target as :string, scrutinee as :string) -> {
|
||||
& let scrut_len = count (scrutinee)
|
||||
& fn searcher ((i, indices), curr) -> {
|
||||
& let srch_substr = slice_n (remaining, scrut_len)
|
||||
& if eq? (scrutinee, srch_substr)
|
||||
& then (inc (i), append (indices, i))
|
||||
& else (inc (i), indices)
|
||||
& }
|
||||
& let (_, idxes) = fold (searcher, target, (0, []))
|
||||
& idxes
|
||||
& }
|
||||
}
|
||||
|
||||
fn index_of {
|
||||
"Takes a list or string returns the first index at which the scrutinee appears. Returns `nil` if the scrutinee does not appear in the search target."
|
||||
(target as :list, scrutinee) -> first (indices_of (target, scrutinee))
|
||||
(target as :string, scrutinee as :string) -> first (indices_of (target, scrutinee))
|
||||
}
|
||||
& (target as :list, scrutinee) -> loop (0) with {
|
||||
& (i) if gte? (i, count (target)) -> nil
|
||||
& (i) -> if eq? (scrutinee, at (target, i))
|
||||
& then i
|
||||
& else recur (inc (i))
|
||||
& }
|
||||
& (target as :string, scrutinee as :string) -> {
|
||||
& let scrut_len = count (scrutinee)
|
||||
& loop (0, target) with {
|
||||
& (i, "") -> nil
|
||||
& (i, remaining) -> {
|
||||
& let srch_substr = slice_n (remaining, scrut_len)
|
||||
& if eq? (scrutinee, srch_substr)
|
||||
& then i
|
||||
& else recur (inc (i), rest (remaining))
|
||||
& }
|
||||
& }
|
||||
& }
|
||||
& }
|
||||
|
||||
|
||||
&&& keywords: funny names
|
||||
fn keyword? {
|
||||
"Returns true if a value is a keyword, otherwise returns false."
|
||||
|
@ -865,6 +913,13 @@ fn keyword? {
|
|||
(_) -> false
|
||||
}
|
||||
|
||||
fn key? {
|
||||
"Returns true if a value can be used as a key in a dict: if it's a string or a keyword."
|
||||
(kw as :keyword) -> true
|
||||
(str as :string) -> true
|
||||
(_) -> false
|
||||
}
|
||||
|
||||
& TODO: determine if Ludus should have a `keyword` function that takes a string and returns a keyword. Too many panics, it has weird memory consequences, etc.
|
||||
|
||||
fn assoc {
|
||||
|
@ -872,7 +927,9 @@ fn assoc {
|
|||
() -> #{}
|
||||
(d as :dict) -> d
|
||||
(d as :dict, k as :keyword, val) -> base :assoc (d, k, val)
|
||||
(d as :dict, k as :string, val) -> base :assoc (d, k, val)
|
||||
(d as :dict, (k as :keyword, val)) -> base :assoc (d, k, val)
|
||||
(d as :dict, (k as :string, val)) -> base :assoc (d, k, val)
|
||||
}
|
||||
|
||||
fn dissoc {
|
||||
|
@ -881,18 +938,17 @@ fn dissoc {
|
|||
(d as :dict, k as :keyword) -> base :dissoc (d, k)
|
||||
}
|
||||
|
||||
& TODO: consider merging `get` and `at`
|
||||
fn get {
|
||||
"Takes a key, dict, and optional default value; returns the value at key. If the value is not found, returns nil or the default value."
|
||||
(k as :keyword) -> get (k, _)
|
||||
(k as :keyword, d as :dict) -> base :get (d, k)
|
||||
(k as :keyword, d as :dict, default) -> match base :get (d, k) with {
|
||||
(d as :dict, k as :keyword) -> base :get (d, k)
|
||||
(d as :dict, k as :keyword, default) -> match base :get (d, k) with {
|
||||
nil -> default
|
||||
val -> val
|
||||
}
|
||||
(k as :string) -> get (k, _)
|
||||
(k as :string, d as :dict) -> base :get (d, k)
|
||||
(k as :string, d as :dict, default) -> match base :get (d, k) with {
|
||||
(d as :dict, k as :string) -> base :get (d, k)
|
||||
(d as :dict, k as :string, default) -> match base :get (d, k) with {
|
||||
nil -> default
|
||||
val -> val
|
||||
}
|
||||
|
@ -914,15 +970,14 @@ fn values {
|
|||
(d as :dict) -> do d > list > map (second, _)
|
||||
}
|
||||
|
||||
& TODO: add sets to this?
|
||||
fn has? {
|
||||
"Takes a key and a dict, and returns true if there is a non-`nil` value stored at the key."
|
||||
(k as :keyword) -> has? (k, _)
|
||||
(k as :keyword, d as :dict) -> do d > get (k) > some?
|
||||
(k as :keyword) -> has? (_, k)
|
||||
(d as :dict, k as :keyword) -> do d > get (k) > some?
|
||||
}
|
||||
|
||||
fn dict {
|
||||
"Takes a list or tuple of (key, value) tuples and returns it as a dict. Returns dicts unharmed."
|
||||
"Takes a list or tuple of `(key, value)` tuples and returns it as a dict. Returns dicts unharmed."
|
||||
(d as :dict) -> d
|
||||
(l as :list) -> fold (assoc, l)
|
||||
(t as :tuple) -> do t > list > dict
|
||||
|
@ -1118,7 +1173,7 @@ fn fetch {
|
|||
let pid = self ()
|
||||
spawn! (fn () -> request_fetch! (pid, url))
|
||||
receive {
|
||||
(:reply, response) -> response
|
||||
(:reply, (_, response)) -> response
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1495,7 +1550,7 @@ fn key_pressed? {
|
|||
append
|
||||
assert!
|
||||
assoc
|
||||
assoc?
|
||||
& assoc?
|
||||
at
|
||||
atan/2
|
||||
back!
|
||||
|
@ -1511,12 +1566,12 @@ fn key_pressed? {
|
|||
cdr
|
||||
ceil
|
||||
chars
|
||||
chars/safe
|
||||
clear!
|
||||
coll?
|
||||
colors
|
||||
colours
|
||||
concat
|
||||
condense
|
||||
cons
|
||||
console
|
||||
contains?
|
||||
|
@ -1561,6 +1616,9 @@ fn key_pressed? {
|
|||
hideturtle!
|
||||
home!
|
||||
inc
|
||||
indexed?
|
||||
index_of
|
||||
indices_of
|
||||
inv
|
||||
inv/0
|
||||
inv/safe
|
||||
|
@ -1589,7 +1647,6 @@ fn key_pressed? {
|
|||
odd?
|
||||
ok
|
||||
ok?
|
||||
ordered?
|
||||
pc!
|
||||
pd!
|
||||
pencolor
|
||||
|
|
|
@ -403,7 +403,7 @@ function __wbg_get_imports() {
|
|||
_assertBoolean(ret);
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbindgen_closure_wrapper8157 = function() { return logError(function (arg0, arg1, arg2) {
|
||||
imports.wbg.__wbindgen_closure_wrapper8160 = function() { return logError(function (arg0, arg1, arg2) {
|
||||
const ret = makeMutClosure(arg0, arg1, 359, __wbg_adapter_20);
|
||||
return ret;
|
||||
}, arguments) };
|
||||
|
|
BIN
pkg/rudus_bg.wasm
(Stored with Git LFS)
BIN
pkg/rudus_bg.wasm
(Stored with Git LFS)
Binary file not shown.
56
src/base.rs
56
src/base.rs
|
@ -83,18 +83,9 @@ pub fn r#bool(x: &Value) -> Value {
|
|||
pub fn chars(x: &Value) -> Value {
|
||||
match x {
|
||||
Value::Interned(s) => {
|
||||
let chars = s.chars();
|
||||
|
||||
let mut charlist = vector![];
|
||||
for char in chars {
|
||||
if char.is_ascii() {
|
||||
charlist.push_back(Value::String(Rc::new(char.to_string())))
|
||||
} else {
|
||||
return Value::Tuple(Rc::new(vec![
|
||||
Value::Keyword("err"),
|
||||
Value::String(Rc::new(format!("{char} is not an ascii character"))),
|
||||
]));
|
||||
}
|
||||
for char in s.chars() {
|
||||
charlist.push_back(Value::string(char.to_string()))
|
||||
}
|
||||
Value::Tuple(Rc::new(vec![
|
||||
Value::Keyword("ok"),
|
||||
|
@ -102,18 +93,9 @@ pub fn chars(x: &Value) -> Value {
|
|||
]))
|
||||
}
|
||||
Value::String(s) => {
|
||||
let chars = s.chars();
|
||||
|
||||
let mut charlist = vector![];
|
||||
for char in chars {
|
||||
if char.is_ascii() {
|
||||
charlist.push_back(Value::String(Rc::new(char.to_string())))
|
||||
} else {
|
||||
return Value::Tuple(Rc::new(vec![
|
||||
Value::Keyword("err"),
|
||||
Value::String(Rc::new(format!("{char} is not an ascii character"))),
|
||||
]));
|
||||
}
|
||||
for char in s.chars() {
|
||||
charlist.push_back(Value::string(char.to_string()))
|
||||
}
|
||||
Value::Tuple(Rc::new(vec![
|
||||
Value::Keyword("ok"),
|
||||
|
@ -210,6 +192,14 @@ pub fn first(ordered: &Value) -> Value {
|
|||
Some(n) => n.clone(),
|
||||
None => Value::Nil,
|
||||
},
|
||||
Value::String(s) => match s.chars().next() {
|
||||
Some(char) => Value::string(char.to_string()),
|
||||
None => Value::Nil,
|
||||
},
|
||||
Value::Interned(s) => match s.chars().next() {
|
||||
Some(char) => Value::string(char.to_string()),
|
||||
None => Value::Nil,
|
||||
},
|
||||
_ => unreachable!("internal Ludus error"),
|
||||
}
|
||||
}
|
||||
|
@ -232,6 +222,20 @@ pub fn at(ordered: &Value, i: &Value) -> Value {
|
|||
None => Value::Nil,
|
||||
}
|
||||
}
|
||||
(Value::String(s), Value::Number(n)) => {
|
||||
let i = f64::from(*n) as usize;
|
||||
match s.chars().nth(i) {
|
||||
Some(n) => Value::string(n.to_string()),
|
||||
None => Value::Nil,
|
||||
}
|
||||
}
|
||||
(Value::Interned(s), Value::Number(n)) => {
|
||||
let i = f64::from(*n) as usize;
|
||||
match s.chars().nth(i) {
|
||||
Some(n) => Value::string(n.to_string()),
|
||||
None => Value::Nil,
|
||||
}
|
||||
}
|
||||
_ => unreachable!("internal Ludus error"),
|
||||
}
|
||||
}
|
||||
|
@ -264,6 +268,14 @@ pub fn last(ordered: &Value) -> Value {
|
|||
Some(x) => x.clone(),
|
||||
None => Value::Nil,
|
||||
},
|
||||
Value::String(s) => match s.chars().last() {
|
||||
Some(char) => Value::string(char.to_string()),
|
||||
None => Value::Nil,
|
||||
},
|
||||
Value::Interned(s) => match s.chars().last() {
|
||||
Some(char) => Value::string(char.to_string()),
|
||||
None => Value::Nil,
|
||||
},
|
||||
_ => unreachable!("internal Ludus error"),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1286,7 +1286,7 @@ impl Compiler {
|
|||
let mut chunks = vec![];
|
||||
|
||||
for (arity, mut compiler) in compilers {
|
||||
compiler.emit_op(Op::PanicNoMatch);
|
||||
compiler.emit_op(Op::PanicNoFnMatch);
|
||||
let chunk = compiler.chunk;
|
||||
if self.debug {
|
||||
println!("=== function chuncktion: {name}/{arity} ===");
|
||||
|
|
|
@ -98,9 +98,16 @@ pub fn panic(panic: Panic) -> String {
|
|||
// panic.call_stack.last().unwrap().chunk().dissasemble();
|
||||
// console_log!("{:?}", panic.call_stack.last().unwrap().chunk().spans);
|
||||
let mut msgs = vec![];
|
||||
let msg = match panic.msg {
|
||||
let Panic { msg, scrutinee, .. } = &panic;
|
||||
let msg = match msg {
|
||||
PanicMsg::Generic(ref s) => s,
|
||||
_ => &"no match".to_string(),
|
||||
PanicMsg::NoFnMatch => &format!(
|
||||
"no match against {}",
|
||||
scrutinee
|
||||
.as_ref()
|
||||
.expect("expect a match panic to have a scrutinee")
|
||||
),
|
||||
_ => &String::from("no match"),
|
||||
};
|
||||
msgs.push(msg.clone());
|
||||
msgs.push(traceback(&panic));
|
||||
|
|
Loading…
Reference in New Issue
Block a user