use crate::interp::Interp;
use crate::types::*;
use crate::*;
use std::fs;
use std::time::Instant;
pub fn cmd_append(interp: &mut Interp, argv: &[Value]) -> MoltResult {
check_args(1, argv, 2, 0, "varName ?value value ...?")?;
let var_name = argv[1].as_str();
let mut new_string: String = interp
.var(var_name)
.and_then(|val| Ok(val.to_string()))
.unwrap_or_else(|_| String::new());
for item in &argv[2..] {
new_string.push_str(item.as_str());
}
molt_ok!(interp.set_and_return(var_name, new_string.into()))
}
pub fn cmd_assert_eq(_interp: &mut Interp, argv: &[Value]) -> MoltResult {
check_args(1, argv, 3, 3, "received expected")?;
if argv[1] == argv[2] {
molt_ok!()
} else {
molt_err!(
"assertion failed: received \"{}\", expected \"{}\".",
argv[1],
argv[2]
)
}
}
pub fn cmd_break(_interp: &mut Interp, argv: &[Value]) -> MoltResult {
check_args(1, argv, 1, 1, "")?;
Err(ResultCode::Break)
}
pub fn cmd_catch(interp: &mut Interp, argv: &[Value]) -> MoltResult {
check_args(1, argv, 2, 3, "script ?resultVarName?")?;
let result = interp.eval_body(&argv[1]);
let (code, value) = match result {
Ok(val) => (0, val),
Err(ResultCode::Error(val)) => (1, val),
Err(ResultCode::Return(val)) => (2, val),
Err(ResultCode::Break) => (3, Value::empty()),
Err(ResultCode::Continue) => (4, Value::empty()),
};
if argv.len() == 3 {
interp.set_and_return(argv[2].as_str(), value);
}
Ok(Value::from(code))
}
pub fn cmd_continue(_interp: &mut Interp, argv: &[Value]) -> MoltResult {
check_args(1, argv, 1, 1, "")?;
Err(ResultCode::Continue)
}
pub fn cmd_error(_interp: &mut Interp, argv: &[Value]) -> MoltResult {
check_args(1, argv, 2, 2, "message")?;
molt_err!(argv[1].clone())
}
pub fn cmd_exit(_interp: &mut Interp, argv: &[Value]) -> MoltResult {
check_args(1, argv, 1, 2, "?returnCode?")?;
let return_code: MoltInt = if argv.len() == 1 {
0
} else {
argv[1].as_int()?
};
std::process::exit(return_code as i32)
}
pub fn cmd_expr(interp: &mut Interp, argv: &[Value]) -> MoltResult {
check_args(1, argv, 2, 2, "expr")?;
interp.expr(&argv[1])
}
pub fn cmd_for(interp: &mut Interp, argv: &[Value]) -> MoltResult {
check_args(1, argv, 5, 5, "start test next command")?;
let start = &argv[1];
let test = &argv[2];
let next = &argv[3];
let command = &argv[4];
interp.eval_value(start)?;
while interp.expr_bool(test)? {
let result = interp.eval_body(command);
match result {
Ok(_) => (),
Err(ResultCode::Break) => break,
Err(ResultCode::Continue) => (),
_ => return result,
}
let result = interp.eval_body(next);
match result {
Ok(_) => (),
Err(ResultCode::Break) => break,
Err(ResultCode::Continue) => {
return molt_err!("invoked \"continue\" outside of a loop");
}
_ => return result,
}
}
molt_ok!()
}
pub fn cmd_foreach(interp: &mut Interp, argv: &[Value]) -> MoltResult {
check_args(1, argv, 4, 4, "varList list body")?;
let var_list = &*argv[1].as_list()?;
let list = &*argv[2].as_list()?;
let body = &argv[3];
let mut i = 0;
while i < list.len() {
for var_name in var_list {
if i < list.len() {
interp.set_var(var_name.as_str(), &list[i]);
i += 1;
} else {
interp.set_and_return(var_name.as_str(), Value::empty());
}
}
let result = interp.eval_body(body);
match result {
Ok(_) => (),
Err(ResultCode::Break) => break,
Err(ResultCode::Continue) => (),
_ => return result,
}
}
molt_ok!()
}
pub fn cmd_global(interp: &mut Interp, argv: &[Value]) -> MoltResult {
if interp.scope_level() > 0 {
for name in &argv[1..] {
interp.upvar(0, name.as_str());
}
}
molt_ok!()
}
#[derive(Eq, PartialEq, Debug)]
enum IfWants {
Expr,
ThenBody,
SkipThenClause,
ElseClause,
ElseBody,
}
pub fn cmd_if(interp: &mut Interp, argv: &[Value]) -> MoltResult {
let mut argi = 1;
let mut wants = IfWants::Expr;
while argi < argv.len() {
match wants {
IfWants::Expr => {
wants = if interp.expr_bool(&argv[argi])? {
IfWants::ThenBody
} else {
IfWants::SkipThenClause
};
}
IfWants::ThenBody => {
if argv[argi].as_str() == "then" {
argi += 1;
}
if argi < argv.len() {
return interp.eval_body(&argv[argi]);
} else {
break;
}
}
IfWants::SkipThenClause => {
if argv[argi].as_str() == "then" {
argi += 1;
}
if argi < argv.len() {
argi += 1;
wants = IfWants::ElseClause;
}
continue;
}
IfWants::ElseClause => {
if argv[argi].as_str() == "elseif" {
wants = IfWants::Expr;
} else {
wants = IfWants::ElseBody;
continue;
}
}
IfWants::ElseBody => {
if argv[argi].as_str() == "else" {
argi += 1;
if argi == argv.len() {
return molt_err!(
"wrong # args: no script following after \"{}\" argument",
argv[argi - 1]
);
}
}
if argi < argv.len() {
return interp.eval_body(&argv[argi]);
} else {
break;
}
}
}
argi += 1;
}
if argi < argv.len() {
return molt_err!("wrong # args: extra words after \"else\" clause in \"if\" command");
} else if wants == IfWants::Expr {
return molt_err!(
"wrong # args: no expression after \"{}\" argument",
argv[argi - 1]
);
} else if wants == IfWants::ThenBody || wants == IfWants::SkipThenClause {
return molt_err!(
"wrong # args: no script following after \"{}\" argument",
argv[argi - 1]
);
} else {
molt_ok!() }
}
pub fn cmd_incr(interp: &mut Interp, argv: &[Value]) -> MoltResult {
check_args(1, argv, 2, 3, "varName ?increment?")?;
let increment: MoltInt = if argv.len() == 3 {
argv[2].as_int()?
} else {
1
};
let var_name = argv[1].as_str();
let new_value = increment
+ interp
.var(var_name)
.and_then(|val| Ok(val.as_int()?))
.unwrap_or_else(|_| 0);
molt_ok!(interp.set_and_return(var_name, new_value.into()))
}
pub fn cmd_info(interp: &mut Interp, argv: &[Value]) -> MoltResult {
check_args(1, argv, 2, 0, "subcommand ?arg ...?")?;
let subc = Subcommand::find(&INFO_SUBCOMMANDS, argv[1].as_str())?;
(subc.1)(interp, argv)
}
const INFO_SUBCOMMANDS: [Subcommand; 3] = [
Subcommand("commands", cmd_info_commands),
Subcommand("complete", cmd_info_complete),
Subcommand("vars", cmd_info_vars),
];
pub fn cmd_info_commands(interp: &mut Interp, _argv: &[Value]) -> MoltResult {
molt_ok!(Value::from(interp.command_names()))
}
pub fn cmd_info_complete(interp: &mut Interp, argv: &[Value]) -> MoltResult {
check_args(2, argv, 3, 3, "command")?;
if interp.complete(argv[2].as_str()) {
molt_ok!(true)
} else {
molt_ok!(false)
}
}
pub fn cmd_info_vars(interp: &mut Interp, _argv: &[Value]) -> MoltResult {
molt_ok!(Value::from(interp.vars_in_scope()))
}
pub fn cmd_join(_interp: &mut Interp, argv: &[Value]) -> MoltResult {
check_args(1, argv, 2, 3, "list ?joinString?")?;
let list = &argv[1].as_list()?;
let join_string = if argv.len() == 3 {
argv[2].to_string()
} else {
" ".to_string()
};
let list: Vec<String> = list.iter().map(|v| v.to_string()).collect();
molt_ok!(list.join(&join_string))
}
pub fn cmd_lappend(interp: &mut Interp, argv: &[Value]) -> MoltResult {
check_args(1, argv, 2, 0, "varName ?value ...?")?;
let var_name = argv[1].as_str();
let var_result = interp.var(var_name);
let mut list: MoltList = if var_result.is_ok() {
var_result.expect("got value").to_list()?
} else {
Vec::new()
};
let mut values = argv[2..].to_owned();
list.append(&mut values);
molt_ok!(interp.set_and_return(var_name, Value::from(list)))
}
pub fn cmd_lindex(_interp: &mut Interp, argv: &[Value]) -> MoltResult {
check_args(1, argv, 2, 0, "list ?index ...?")?;
let mut value = argv[1].clone();
for index_val in &argv[2..] {
let list = value.as_list()?;
let index = index_val.as_int()?;
value = if index < 0 || index as usize >= list.len() {
Value::empty()
} else {
list[index as usize].clone()
};
}
molt_ok!(value)
}
pub fn cmd_list(_interp: &mut Interp, argv: &[Value]) -> MoltResult {
molt_ok!(&argv[1..])
}
pub fn cmd_llength(_interp: &mut Interp, argv: &[Value]) -> MoltResult {
check_args(1, argv, 2, 2, "list")?;
molt_ok!(argv[1].as_list()?.len() as MoltInt)
}
pub fn cmd_pdump(interp: &mut Interp, argv: &[Value]) -> MoltResult {
check_args(1, argv, 1, 1, "")?;
interp.profile_dump();
molt_ok!()
}
pub fn cmd_pclear(interp: &mut Interp, argv: &[Value]) -> MoltResult {
check_args(1, argv, 1, 1, "")?;
interp.profile_clear();
molt_ok!()
}
pub fn cmd_proc(interp: &mut Interp, argv: &[Value]) -> MoltResult {
check_args(1, argv, 4, 4, "name args body")?;
let name = argv[1].as_str();
let args = &*argv[2].as_list()?;
let body = argv[3].as_str();
for arg in args {
let vec = arg.as_list()?;
if vec.is_empty() {
return molt_err!("argument with no name");
} else if vec.len() > 2 {
return molt_err!("too many fields in argument specifier \"{}\"", arg);
}
}
interp.add_proc(name, args, body);
molt_ok!()
}
pub fn cmd_puts(_interp: &mut Interp, argv: &[Value]) -> MoltResult {
check_args(1, argv, 2, 2, "string")?;
println!("{}", argv[1]);
molt_ok!()
}
pub fn cmd_rename(interp: &mut Interp, argv: &[Value]) -> MoltResult {
check_args(1, argv, 3, 3, "oldName newName")?;
let old_name = argv[1].as_str();
let new_name = argv[2].as_str();
if !interp.has_command(old_name) {
return molt_err!("can't rename \"{}\": command doesn't exist", old_name);
}
if new_name.is_empty() {
interp.remove_command(old_name);
} else {
interp.rename_command(old_name, new_name);
}
molt_ok!()
}
pub fn cmd_return(_interp: &mut Interp, argv: &[Value]) -> MoltResult {
check_args(1, argv, 1, 2, "?value?")?;
let value = if argv.len() == 1 {
Value::empty()
} else {
argv[1].clone()
};
Err(ResultCode::Return(value))
}
pub fn cmd_set(interp: &mut Interp, argv: &[Value]) -> MoltResult {
check_args(1, argv, 2, 3, "varName ?newValue?")?;
let var_name = argv[1].as_str();
if argv.len() == 3 {
molt_ok!(interp.set_and_return(var_name, argv[2].clone()))
} else {
molt_ok!(interp.var(var_name)?.clone())
}
}
pub fn cmd_source(interp: &mut Interp, argv: &[Value]) -> MoltResult {
check_args(1, argv, 2, 2, "filename")?;
let filename = argv[1].as_str();
match fs::read_to_string(filename) {
Ok(script) => interp.eval(&script),
Err(e) => molt_err!("couldn't read file \"{}\": {}", filename, e),
}
}
pub fn cmd_time(interp: &mut Interp, argv: &[Value]) -> MoltResult {
check_args(1, argv, 2, 3, "command ?count?")?;
let command = &argv[1];
let count = if argv.len() == 3 {
argv[2].as_int()?
} else {
1
};
let start = Instant::now();
for _i in 0..count {
let result = interp.eval_value(command);
if result.is_err() {
return result;
}
}
let span = start.elapsed();
let avg = if count > 0 {
span.as_nanos() / (count as u128)
} else {
0
} as MoltInt;
molt_ok!("{} nanoseconds per iteration", avg)
}
pub fn cmd_unset(interp: &mut Interp, argv: &[Value]) -> MoltResult {
check_args(1, argv, 2, 2, "varName")?;
interp.unset_var(argv[1].as_str());
molt_ok!()
}
pub fn cmd_while(interp: &mut Interp, argv: &[Value]) -> MoltResult {
check_args(1, argv, 3, 3, "test command")?;
while interp.expr_bool(&argv[1])? {
let result = interp.eval_body(&argv[2]);
match result {
Ok(_) => (),
Err(ResultCode::Break) => break,
Err(ResultCode::Continue) => (),
_ => return result,
}
}
molt_ok!()
}