use std::io::Write;
use std::{fs::read_to_string, io, path::Path};
use crate::{
context::Context,
parser::{Command, CommandType},
repl::repl,
scope::Scope,
};
fn number(name: &str, ctx: &mut Context, scope: &mut Scope) -> Result<f64, String> {
let evaluate = arg(name, ctx, scope)?;
get_number(
evaluate,
format!(
"for parameter \"{}\" of \"{}\"",
name,
ctx.last_call.as_ref().unwrap()
),
)
}
fn string(name: &str, ctx: &mut Context, scope: &mut Scope) -> Result<String, String> {
let evaluate = arg(name, ctx, scope)?;
match evaluate.get(0) {
Some(v) => Ok(v.clone()),
None => Err(format!(
"Expected value for parameter \"{}\" of \"{}\"",
name,
ctx.last_call.as_ref().unwrap()
)),
}
}
fn get_number(val: Vec<String>, at: String) -> Result<f64, String> {
let Some(a) = val.get(0) else {
return Err(format!("Expected number {at}"));
};
a.parse::<f64>()
.map_err(|_| format!("Couldn't parse number {at}"))
}
fn unevaluated_arg(name: &str, ctx: &mut Context, scope: &mut Scope) -> Result<Command, String> {
{
if let Some(arg) = scope.get_arg() {
if let Command {
kind: CommandType::Assign(to),
start: _,
end: _,
} = arg
{
Err(format!(", found assignment to \"{}\"", to))
} else {
Ok(arg)
}
} else {
Err("".to_string())
}
}
.map_err(|e| {
format!(
"Expected parameter \"{}\" for built-in \"{}\"",
name,
ctx.last_call.as_ref().unwrap()
) + &e
})
}
fn arg(name: &str, ctx: &mut Context, scope: &mut Scope) -> Result<Vec<String>, String> {
let arg = unevaluated_arg(name, ctx, scope)?;
ctx.evaluate(&mut scope.child(vec![arg]))
}
impl Default for Context {
fn default() -> Context {
let mut ctx = Context::new();
ctx.builtin("join", |_, e| {
e.list = vec![e.list.join("")];
Ok(())
});
ctx.builtin("joinwith", |c, e| {
let with = &arg("sep", c, e)?[0];
e.list = vec![e.list.join(with)];
Ok(())
});
ctx.builtin("debug", |ctx, e| {
repl(ctx, &mut e.clone());
Ok(())
});
ctx.builtin("sum", |_, e| {
let res = e
.as_numbers()?
.into_iter()
.reduce(|a, b| a + b)
.map(|a| a.to_string());
e.list.clear();
if let Some(a) = res {
e.list.push(a)
}
Ok(())
});
ctx.builtin("max", |_, e| {
let res = e
.as_numbers()?
.into_iter()
.reduce(|a, b| a.max(b))
.map(|a| a.to_string());
e.list.clear();
if let Some(a) = res {
e.list.push(a)
}
Ok(())
});
ctx.builtin("sort", |_, e| {
e.list.sort();
Ok(())
});
ctx.builtin("repeat", |ctx, e| {
let to_append = &e.list.clone();
for _ in 1..(number("amount", ctx, e)? as usize) {
e.list.append(&mut to_append.clone())
}
Ok(())
});
ctx.builtin("void", |_, e| void(e));
ctx.builtin("clear", |_, e| void(e));
ctx.builtin("just", |_, e| void(e));
ctx.builtin("empty", |_, e| void(e));
ctx.builtin("false", |_, e| void(e));
ctx.builtin("only", |_, e| void(e));
ctx.builtin("none", |_, e| void(e));
ctx.builtin("words", |_, e| {
e.list = e
.list
.iter()
.flat_map(|e| e.split(' ').map(|s| s.to_string()).collect::<Vec<String>>())
.collect();
Ok(())
});
ctx.builtin("chars", |_, e| {
e.list = e
.list
.iter()
.flat_map(|e| e.chars().map(|c| c.to_string()).collect::<Vec<String>>())
.collect();
Ok(())
});
ctx.builtin("input", |_, e| {
let mut s = String::new();
io::stdout().flush().unwrap();
io::stdin().read_line(&mut s).unwrap();
s = s.trim().to_string();
e.list.push(s);
Ok(())
});
ctx.builtin("error", |ctx, e| Err(arg("msg", ctx, e)?[0].clone()));
ctx.builtin("on-error", |ctx, e| {
let subs = arg("pattern", ctx, e)?;
let res = arg("callback", ctx, e);
let then = unevaluated_arg("then", ctx, e)?;
if let Err(msg) = res {
if msg.contains(&subs[0]) {
let mut child = e.child(vec![then]);
child.list = vec![format!("{}", msg)];
ctx.evaluate(&mut child)?;
}
};
Ok(())
});
ctx.builtin("read", |ctx, env| {
let path = arg("file", ctx, env)?;
env.list.append(
&mut path
.iter()
.map(|p| match read_to_string(Path::new(p)) {
Ok(e) => Ok(e.split('\n').map(|e| e.to_string()).collect()),
Err(e) => Err(format!("Can't read \"{}\": {}", p, e)),
})
.collect::<Result<Vec<Vec<String>>, String>>()?
.into_iter()
.flatten()
.collect::<Vec<String>>(),
);
Ok(())
});
ctx.builtin("to", |ctx, e| {
let to = number("to", ctx, e)? as i64 + 1;
let from_numbers = match e.list.pop() {
Some(e) => vec![e],
None => Vec::new(),
};
let from = get_number(from_numbers, "to go from".to_string())?;
e.list
.append(&mut ((from as i64)..to).map(|i| i.to_string()).collect());
Ok(())
});
ctx.builtin("reverse", |_, env| {
env.list.reverse();
Ok(())
});
ctx.builtin("the", |ctx, env| {
let object = arg("object", ctx, env)?;
let key = string("key", ctx, env)?;
let Some(at) = object.iter().position(|e| e == &key) else {
env.list.clear();
return Ok(())
};
let Some(size) = object.get(at + 1) else {
return Err(format!("Invalid object, couldn't find size of key {}", key))
};
let size = get_number(vec![size.clone()], format!("size of key {}", key))?;
env.list = object[at + 2..at + 2 + size as usize].to_vec();
Ok(())
});
ctx.builtin("find", |ctx, env| {
let to_find = arg("list", ctx, env)?;
if to_find.is_empty() {
return Err("Can't search list with empty value".to_string());
}
let mut found = Vec::new();
for (position, window) in env.list.windows(to_find.len()).enumerate() {
if window == to_find {
found.push((position + 1).to_string());
break;
}
}
env.list = found;
Ok(())
});
ctx.builtin("numbered", |_ctx, env| {
env.list = env
.list
.iter()
.enumerate()
.flat_map(|(i, v)| vec![(i + 1).to_string(), v.clone()])
.collect();
Ok(())
});
ctx.builtin("entry", |ctx, env| {
let other = arg("name", ctx, env)?;
env.list.reverse();
todo!();
Ok(())
});
ctx.builtin("entries", |ctx, env| {
let other = arg("name", ctx, env)?;
env.list.reverse();
todo!();
Ok(())
});
ctx.builtin("/", |ctx, env| {
let other = number("dividend", ctx, env)?;
env.list = env
.as_numbers()?
.iter()
.map(|i| (*i / other).to_string())
.collect();
Ok(())
});
ctx.builtin("*", |ctx, env| {
let other = number("factor", ctx, env)?;
env.list = env
.as_numbers()?
.iter()
.map(|i| (i * other).to_string())
.collect();
Ok(())
});
ctx.builtin("-", |ctx, env| {
let other = number("factor", ctx, env)?;
env.list = env
.as_numbers()?
.iter()
.map(|i| (i - other).to_string())
.collect();
Ok(())
});
ctx.builtin("+", |ctx, env| {
let other = number("factor", ctx, env)?;
env.list = env
.as_numbers()?
.iter()
.map(|i| (i + other).to_string())
.collect();
Ok(())
});
ctx.builtin("mod", |ctx, env| {
let other = number("num", ctx, env)?;
env.list = env
.as_numbers()?
.iter()
.map(|i| (i % other).to_string())
.collect();
Ok(())
});
ctx.builtin("then", |ctx, env| {
let body = unevaluated_arg("body", ctx, env)?;
if env.list.is_empty() {
env.list.clear()
} else {
let mut child = env.child(vec![body]);
env.list = ctx.evaluate(&mut child)?;
}
Ok(())
});
ctx.builtin("else", |ctx, env| {
let body = unevaluated_arg("body", ctx, env)?;
if env.list.is_empty() {
let mut child = env.child(vec![body]);
env.list = ctx.evaluate(&mut child)?;
}
Ok(())
});
ctx.builtin("is", |ctx, env| {
let other = arg("other", ctx, env)?;
if env.list != other {
env.list.clear();
} else if other.is_empty() {
env.list.push("true".to_string());
}
Ok(())
});
ctx.builtin(">", |ctx, env| {
if env.list.last().unwrap().parse::<f64>().unwrap() <= number("other", ctx, env)? {
env.list.clear();
};
Ok(())
});
ctx.builtin("<", |ctx, env| {
if env.list.last().unwrap().parse::<f64>().unwrap() >= number("other", ctx, env)? {
env.list.clear();
};
Ok(())
});
ctx.builtin("loop", |ctx, env| {
let body = unevaluated_arg("body", ctx, env)?;
loop {
if ctx.exit {
ctx.exit = false;
break;
}
ctx.evaluate(&mut env.child(vec![body.clone()]))?;
}
Ok(())
});
ctx.builtin("write", |_, env| {
for line in env.list.iter() {
println!("{}", line)
}
Ok(())
});
ctx.builtin("print", |ctx, env| {
println!("{}", arg("text", ctx, env)?.join("\n"));
Ok(())
});
ctx.builtin("out", |_, env| {
print!("{}", env.list.join("\n"));
Ok(())
});
ctx.builtin("each", |ctx, env| {
let body = unevaluated_arg("body", ctx, env)?;
env.list = env
.list
.iter()
.map(|v| {
let mut scope = env.child(vec![body.clone()]);
scope.list = vec![v.to_owned()];
ctx.evaluate(&mut scope)
})
.collect::<Result<Vec<Vec<String>>, String>>()?
.into_iter()
.flatten()
.collect();
Ok(())
});
ctx.builtin("while", |ctx, env| {
let condition = unevaluated_arg("condition", ctx, env)?;
let body = unevaluated_arg("body", ctx, env)?;
let mut result = Vec::new();
while !ctx.exit {
if ctx.exit {
ctx.exit = false;
break;
}
let mut scope = env.child(vec![condition.clone()]);
if ctx.evaluate(&mut scope)?.is_empty() {
break;
};
let mut scope = env.child(vec![body.clone()]);
result.append(&mut ctx.evaluate(&mut scope)?);
}
env.list.append(&mut result);
Ok(())
});
ctx.builtin("until", |ctx, env| {
let condition = unevaluated_arg("condition", ctx, env)?;
let body = unevaluated_arg("body", ctx, env)?;
let mut result = Vec::new();
loop {
if ctx.exit {
ctx.exit = false;
break;
}
let mut scope = env.child(vec![condition.clone()]);
if !ctx.evaluate(&mut scope)?.is_empty() {
break;
};
let mut scope = env.child(vec![body.clone()]);
result.append(&mut ctx.evaluate(&mut scope)?);
}
env.list.append(&mut result);
Ok(())
});
ctx.builtin("exit", |ctx, _env| {
ctx.exit = true;
Ok(())
});
ctx.builtin("seperated-by", |ctx, env| {
let sep = arg("sep", ctx, env)?;
let body = unevaluated_arg("body", ctx, env)?;
let iter = env.list.clone().into_iter().chain(sep.clone().into_iter());
env.list.clear();
let mut chunk = Vec::new();
for v in iter {
chunk.push(v);
if chunk.len() >= sep.len() && chunk[chunk.len() - sep.len()..] == sep {
for _ in 0..sep.len() {
chunk.pop();
}
let mut child = env.child(vec![body.clone()]);
child.list = chunk.clone();
chunk.clear();
// TODO: This doesn't seem to work.
env.list.append(&mut ctx.evaluate(&mut child)?);
}
}
Ok(())
});
ctx.builtin("every", |ctx, env| {
let nth = (arg("step", ctx, env)?[0])
.parse::<i64>()
.map_err(|_| "Couldn't parse number".to_string())?;
env.list = env
.list
.iter()
.enumerate()
.filter_map(|(i, v)| {
if i % (nth as usize) == 0 {
Some(v.to_owned())
} else {
None
}
})
.collect();
Ok(())
});
ctx.builtin("size", |_, env| {
env.list = vec![env.list.len().to_string()];
Ok(())
});
ctx.builtin("at", |ctx, env| {
let mut vals = Vec::new();
for i in arg("indices", ctx, env)?.into_iter() {
if let Ok(index) = TryInto::<usize>::try_into(
i.parse::<i64>()
.map_err(|_| "Couldn't parse index".to_string())?
- 1,
) {
if let Some(v) = env.list.get(index) {
vals.push(v.to_string())
}
}
}
env.list = vals;
Ok(())
});
ctx.builtin("take", |ctx, env| {
let mut indices = arg("indices", ctx, env)?
.into_iter()
.map(|s| {
s.parse::<usize>()
.map_err(|_| "Couldn't parse index".to_string())
})
.collect::<Result<Vec<usize>, String>>()?;
indices.sort();
indices.reverse();
for i in indices {
let i = i
.checked_sub(1)
.ok_or_else(|| "Invalid index".to_string())?;
if env.list.get(i).is_some() {
env.list.remove(i);
}
}
Ok(())
});
ctx.builtin("packs", |ctx, env| {
let mut res = Vec::new();
let step = arg("size", ctx, env)?
.get(0)
.unwrap()
.parse::<i64>()
.map_err(|_| "Can't parse number".to_string())?;
let call = unevaluated_arg("body", ctx, env)?;
for i in 0..(env.list.len() as f64 / step as f64).floor() as i64 {
let child = &mut env.child(vec![call.clone()]);
child.list = (env.list[(i * step) as usize..(i * step + step) as usize]).to_owned();
res.append(&mut ctx.evaluate(child)?)
}
env.list = res;
Ok(())
});
ctx.info("packs", vec!["size", "body"], "Call a body with each pack")
.unwrap();
ctx
}
}
fn void(e: &mut Scope) -> Result<(), String> {
e.list.clear();
Ok(())
}