Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 15 additions & 16 deletions src/bin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,7 @@ fn new_editor(env: &Environment) -> Editor<DuneHelper> {
rl
}

fn strip_ansi_escapes(text: impl ToString) -> String {
let text = text.to_string();

fn strip_ansi_escapes(text: &str) -> String {
let mut result = String::new();
let mut is_in_escape = false;
for ch in text.chars() {
Expand All @@ -77,15 +75,17 @@ fn strip_ansi_escapes(text: impl ToString) -> String {
result
}

fn readline(prompt: impl ToString, rl: &mut Editor<DuneHelper>) -> String {
let prompt = prompt.to_string();
fn readline(prompt: String, rl: &mut Editor<DuneHelper>) -> String {
loop {
let prompt = prompt.clone();
let stripped = strip_ansi_escapes(&prompt);

// This MUST be called to update the prompt.
if let Some(helper) = rl.helper_mut() {
helper.set_prompt(&prompt);
helper.set_prompt(prompt);
}

match rl.readline(&strip_ansi_escapes(&prompt)) {
match rl.readline(&stripped) {
Ok(line) => return line,
Err(ReadlineError::Interrupted) => {
return String::new();
Expand All @@ -111,8 +111,8 @@ impl DuneHelper {
/// This method MUST be called to update the prompt.
/// If this method is not called, the prompt will not
/// update.
fn set_prompt(&mut self, prompt: impl ToString) {
self.colored_prompt = prompt.to_string();
fn set_prompt(&mut self, prompt: String) {
self.colored_prompt = prompt;
}

fn update_env(&mut self, env: &Environment) {
Expand Down Expand Up @@ -189,7 +189,7 @@ fn syntax_highlight(line: &str) -> String {
is_colored = true;
result.push_str(b);
}
(TokenKind::Punctuation, o @ ("@" | "\'" | "=" | "|" | ">>" | "->" | "~>")) => {
(TokenKind::Punctuation, o @ ("\'" | "=" | "->" | "~>")) => {
result.push_str("\x1b[96m");
is_colored = true;
result.push_str(o);
Expand Down Expand Up @@ -445,7 +445,7 @@ fn repl(
loop {
let mut env = atomic_env.lock().unwrap();
let mut rl = atomic_rl.lock().unwrap();
let cwd = env.get_cwd();
let cwd = env.get_cwd().to_string();
// let prompt = format!("{}", Expression::Apply(Box::new(env.get("prompt").unwrap()), vec![env.get_cwd().into()]).eval(&mut env)?);

let prompt = Expression::Apply(
Expand All @@ -457,11 +457,12 @@ fn repl(
}
.to_string(),
)),
vec![cwd.clone().into()],
vec![cwd.to_string().into()],
)
.eval(&mut env)
.unwrap_or_else(|_| format!("{}$ ", cwd).into())
.to_string();

rl.helper_mut()
.expect("No helper")
.set_prompt(prompt.clone());
Expand All @@ -481,10 +482,8 @@ fn repl(
let val = expr.eval(&mut env);
match val.clone() {
Ok(Expression::Symbol(name)) => {
if let Err(e) =
Expression::Apply(Box::new(Expression::Symbol(name)), vec![])
.eval(&mut env)
{
let apply = Expression::Apply(Box::new(Expression::Symbol(name)), vec![]);
if let Err(e) = apply.eval(&mut env) {
eprintln!("{}", e)
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/binary/init/fmt_module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ pub fn get() -> Expression {
(b_tree_map! {
String::from("strip") => Expression::builtin("strip", |args, env| {
super::check_exact_args_len("strip", &args, 1)?;
Ok(crate::strip_ansi_escapes(args[0].eval(env)?).into())
Ok(crate::strip_ansi_escapes(&args[0].eval(env)?.to_string()).into())
}, "strips all colors and styling from a string"),

String::from("wrap") => Expression::builtin("wrap", wrap,
Expand Down
61 changes: 30 additions & 31 deletions src/binary/init/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::fmt::Write;

use dune::{Builtin, Environment, Error, Expression, Int};

#[cfg(feature = "chess-engine")]
Expand Down Expand Up @@ -31,8 +33,8 @@ pub fn init(env: &mut Environment) {
env.define("parse", parse_module::get());
operator_module::add_to(env);

env.define("exit", env.get("os").unwrap()["exit"].clone());
env.define("cd", env.get("os").unwrap()["cd"].clone());
env.define("exit", env.get_ref("os").unwrap()["exit"].clone());
env.define("cd", env.get_ref("os").unwrap()["cd"].clone());
env.define("quit", env.get("exit").unwrap());

env.define_builtin(
Expand Down Expand Up @@ -77,7 +79,7 @@ pub fn init(env: &mut Environment) {
"print",
|args, env| {
for (i, arg) in args.iter().enumerate() {
let x = arg.clone().eval(env)?;
let x = arg.eval(env)?;
if i < args.len() - 1 {
print!("{} ", x)
} else {
Expand All @@ -94,7 +96,7 @@ pub fn init(env: &mut Environment) {
"debug",
|args, env| {
for (i, arg) in args.iter().enumerate() {
let x = arg.clone().eval(env)?;
let x = arg.eval(env)?;
if i < args.len() - 1 {
print!("{:?} ", x)
} else {
Expand All @@ -111,7 +113,7 @@ pub fn init(env: &mut Environment) {
"println",
|args, env| {
for (i, arg) in args.iter().enumerate() {
let x = arg.clone().eval(env)?;
let x = arg.eval(env)?;
if i < args.len() - 1 {
print!("{} ", x)
} else {
Expand All @@ -130,15 +132,15 @@ pub fn init(env: &mut Environment) {
|args, env| {
let mut prompt = String::new();
for (i, arg) in args.iter().enumerate() {
let x = arg.clone().eval(env)?;
let x = arg.eval(env)?;
if i < args.len() - 1 {
prompt += &format!("{} ", x)
write!(prompt, "{} ", x).unwrap();
} else {
prompt += &format!("{}", x)
write!(prompt, "{}", x).unwrap();
}
}
let mut rl = crate::new_editor(env);
Ok(Expression::String(crate::readline(&prompt, &mut rl)))
Ok(Expression::String(crate::readline(prompt, &mut rl)))
},
"get user input",
);
Expand All @@ -147,7 +149,7 @@ pub fn init(env: &mut Environment) {
"to",
|args, env| {
if args.len() == 2 {
match (args[0].clone().eval(env)?, args[1].clone().eval(env)?) {
match (args[0].eval(env)?, args[1].eval(env)?) {
(Expression::Integer(m), Expression::Integer(n)) => Ok(Expression::List(
(m..n).map(Expression::Integer).collect::<Vec<Expression>>(),
)),
Expand Down Expand Up @@ -200,31 +202,31 @@ pub fn init(env: &mut Environment) {
let mut arr = args[0].eval(env)?;
let idx = args[1].eval(env)?;
let val = args[2].eval(env)?;
match (&mut arr, &idx) {
match (&mut arr, idx) {
(Expression::Map(exprs), Expression::String(key)) => {
exprs.insert(key.clone(), val);
exprs.insert(key, val);
}
(Expression::List(exprs), Expression::Integer(i)) => {
(Expression::List(exprs), Expression::Integer(ref mut i)) => {
if *i as usize <= exprs.len() {
exprs.insert(*i as usize, val);
} else {
return Err(Error::CustomError(format!(
"index {} out of bounds for {:?}",
idx, arr
*i, arr
)));
}
}
(Expression::String(s), Expression::Integer(i)) => {
(Expression::String(s), Expression::Integer(ref mut i)) => {
if *i as usize <= s.len() {
s.insert_str(*i as usize, &val.to_string());
} else {
return Err(Error::CustomError(format!(
"index {} out of bounds for {:?}",
idx, arr
*i, arr
)));
}
}
_ => {
(_, idx) => {
return Err(Error::CustomError(format!(
"cannot insert {:?} into {:?} with index {:?}",
val, arr, idx
Expand Down Expand Up @@ -305,7 +307,7 @@ pub fn init(env: &mut Environment) {
Expression::List(x) => Ok(if x.is_empty() {
Expression::None
} else {
x[0].clone()
x.into_iter().next().unwrap()
}),
otherwise => Err(Error::CustomError(format!(
"cannot get the head of a non-list {}",
Expand Down Expand Up @@ -352,14 +354,14 @@ pub fn init(env: &mut Environment) {
"eval",
|args, env| {
let mut new_env = env.clone();
args[0].clone().eval(env)?.eval(&mut new_env)
args[0].eval(env)?.eval(&mut new_env)
},
"evaluate an expression without changing the environment",
);

env.define_builtin(
"exec",
|args, env| args[0].clone().eval(env)?.eval(env),
|args, env| args[0].eval(env)?.eval(env),
"evaluate an expression in the current environment",
);

Expand Down Expand Up @@ -402,7 +404,7 @@ pub fn init(env: &mut Environment) {
}

fn check_args_len(
name: impl ToString,
name: &str,
args: &[Expression],
expected_len: std::ops::RangeFrom<usize>,
) -> Result<(), Error> {
Expand All @@ -411,23 +413,20 @@ fn check_args_len(
} else {
Err(Error::CustomError(format!(
"too few arguments to function {}",
name.to_string()
name
)))
}
}

fn check_exact_args_len(
name: impl ToString,
args: &[Expression],
expected_len: usize,
) -> Result<(), Error> {
fn check_exact_args_len(name: &str, args: &[Expression], expected_len: usize) -> Result<(), Error> {
if args.len() == expected_len {
Ok(())
} else {
Err(Error::CustomError(if args.len() > expected_len {
format!("too many arguments to function {}", name.to_string())
let error_message = if args.len() > expected_len {
format!("too many arguments to function {}", name)
} else {
format!("too few arguments to function {}", name.to_string())
}))
format!("too few arguments to function {}", name)
};
Err(Error::CustomError(error_message))
}
}
58 changes: 26 additions & 32 deletions src/binary/init/pipe_module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,6 @@ fn pipe_builtin(args: Vec<Expression>, env: &mut Environment) -> Result<Expressi
// The buffer of the STDOUT of the last command.
let mut buf = vec![];

// A dummy value to hold where `expr_to_command` stores its resulting command.
let mut x = Command::new("dummy");

// For every command, pipe in the previous output buffer,
// and get the result.
for (i, expr) in args.iter().enumerate() {
Expand All @@ -41,23 +38,23 @@ fn pipe_builtin(args: Vec<Expression>, env: &mut Environment) -> Result<Expressi
// If the expression can be interpreted as a command (a call to a program),
// then execute it and get its standard output (using the last expression's
// result as the standard input).
match expr_to_command(&mut x, expr, env)? {
match expr_to_command(expr, env)? {
// If the expression is a command:
Some(mut cmd) => {
if is_first {
// If this is the first command, we inherit the current STDIN.
cmd = cmd.stdin(Stdio::inherit());
cmd.stdin(Stdio::inherit());
} else {
// Otherwise, we use the piped STDOUT of the previous command.
cmd = cmd.stdin(Stdio::piped());
cmd.stdin(Stdio::piped());
}

if is_last {
// If this is the last command, we inherit the current STDOUT.
cmd = cmd.stdout(Stdio::inherit());
cmd.stdout(Stdio::inherit());
} else {
// Otherwise, we collect the stdout and pipe it to the next command.
cmd = cmd.stdout(Stdio::piped());
cmd.stdout(Stdio::piped());
}

// Try to execute the command.
Expand Down Expand Up @@ -169,11 +166,7 @@ fn pipe_builtin(args: Vec<Expression>, env: &mut Environment) -> Result<Expressi
/// `Ok(Some(cmd))`.
///
/// Otherwise, the program will return `Ok(None)`.
fn expr_to_command<'a>(
cmd: &'a mut Command,
expr: &Expression,
env: &mut Environment,
) -> Result<Option<&'a mut Command>, Error> {
fn expr_to_command(expr: &Expression, env: &mut Environment) -> Result<Option<Command>, Error> {
let bindings = env
.bindings
.clone()
Expand All @@ -187,44 +180,45 @@ fn expr_to_command<'a>(

Ok(match expr {
// If the command is quoted or in parentheses, try to get the inner command.
Expression::Group(expr) | Expression::Quote(expr) => expr_to_command(cmd, expr, env)?,
Expression::Group(expr) | Expression::Quote(expr) => expr_to_command(expr, env)?,
// If the command is an undefined symbol with some arguments.
Expression::Apply(f, args) => match **f {
Expression::Symbol(ref name) => {
let cmd_name = match env.get(name) {
Expression::Apply(f, args) => match &**f {
Expression::Symbol(name) => {
let cmd_name: &str = match env.get_ref(name) {
// If the symbol is an alias, then execute the alias.
Some(Expression::Symbol(alias)) => alias,
// If the symbol is bound to something like `5`, this isn't a command.
Some(_) => return Ok(None),
// If the symbol is not bound, then it is a command.
None => name.clone(),
None => name,
};
*cmd = Command::new(cmd_name);
Some(
cmd.current_dir(env.get_cwd()).envs(bindings).args(
args.iter()
.filter(|&x| x != &Expression::None)
.map(|x| Ok(format!("{}", x.eval(env)?)))
.collect::<Result<Vec<String>, Error>>()?,
),
)
let mut cmd = Command::new(cmd_name);
cmd.current_dir(env.get_cwd()).envs(bindings).args(
args.iter()
.filter(|&x| x != &Expression::None)
.map(|x| Ok(x.eval(env)?.to_string()))
.collect::<Result<Vec<String>, Error>>()?,
);
Some(cmd)
}
_ => None,
},

// If the command is an undefined symbol, or an alias.
Expression::Symbol(name) => match env.get(name) {
Expression::Symbol(name) => match env.get_ref(name) {
// If the symbol is an alias, then execute the alias.
Some(Expression::Symbol(name)) => {
*cmd = Command::new(name);
Some(cmd.current_dir(env.get_cwd()).envs(bindings))
let mut cmd = Command::new(name);
cmd.current_dir(env.get_cwd()).envs(bindings);
Some(cmd)
}
// If the symbol is bound to something like `5`, this isn't a command.
Some(_) => None,
// If the symbol is not defined, use the symbol as the program name.
None => {
*cmd = Command::new(name);
Some(cmd.current_dir(env.get_cwd()).envs(bindings))
let mut cmd = Command::new(name);
cmd.current_dir(env.get_cwd()).envs(bindings);
Some(cmd)
}
},

Expand Down
Loading