#[macro_export]
macro_rules! molt_ok {
() => (
Ok($crate::Value::empty())
);
($arg:expr) => (
Ok($crate::Value::from($arg))
);
($($arg:tt)*) => (
Ok($crate::Value::from(format!($($arg)*)))
)
}
#[macro_export]
macro_rules! molt_err {
($arg:expr) => (
Err($crate::Exception::molt_err($crate::Value::from($arg)))
);
($($arg:tt)*) => (
Err($crate::Exception::molt_err($crate::Value::from(format!($($arg)*))))
)
}
#[macro_export]
macro_rules! molt_err_help {
($arg:expr) => {{
let mut e = $crate::Exception::molt_err($crate::Value::from($arg));
e.to_help();
Err(e)
}};
($($arg:tt)*) => {{
let mut e = $crate::Exception::molt_err($crate::Value::from(format!($($arg)*)));
e.to_help();
Err(e)
}}
}
#[macro_export]
macro_rules! molt_err_uncompleted {
($arg:expr) => {{
let mut e = $crate::Exception::molt_err($crate::Value::from($arg));
e.to_uncomplete();
Err(e)
}};
($($arg:tt)*) => {{
let mut e = $crate::Exception::molt_err($crate::Value::from(format!($($arg)*)));
e.to_uncomplete();
Err(e)
}}
}
#[macro_export]
macro_rules! molt_throw {
($code:expr, $msg:expr) => (
Err($crate::Exception::molt_err2($crate::Value::from($code), $crate::Value::from($msg)))
);
($code:expr, $($arg:tt)*) => (
Err($crate::Exception::molt_err2($crate::Value::from($code), $crate::Value::from(format!($($arg)*))))
)
}
#[macro_export]
macro_rules! join_strings {
() => {
""
};
($a:expr $(,)?) => {
$a
};
($a:expr, $b:expr $(,)?) => {
concat!($a, " or ", $b)
};
($a:expr, $($rest:expr),+ $(,)?) => {
concat!($a, ", ", join_strings!($($rest),+))
};
}
#[macro_export]
macro_rules! _gen_subcommand_generic {
($subc:expr, [ $( ($cmd_name:tt, $cmd_func:expr$(,)?) ),* $(,)?] $(,)?) => {
{
#[inline]
fn f<Ctx:'static>(interp: &mut $crate::prelude::Interp<Ctx>, argv: &[$crate::prelude::Value]) -> $crate::prelude::MoltResult {
check_args($subc, argv, $subc + 1, 0, "subcommand ?arg ...?")?;
let sub_name = argv[$subc].as_str();
match sub_name {
$(
$cmd_name => $cmd_func(interp, argv),
)*
_ => molt_err!("unknown or ambiguous subcommand \"{}\", must be:\n{}.", sub_name, join_strings!( $($cmd_name,)* )),
}
}
f
}
}
}
#[macro_export]
macro_rules! gen_subcommand {
($ctx_type:ty, $subc:expr, [ $( ($cmd_name:tt, $cmd_space:tt, $cmd_func:expr, $cmd_help:expr$(,)?) ),* $(,)?] $(,)?) => {
{
#[inline]
fn f(interp: &mut $crate::prelude::Interp<$ctx_type>, argv: &[$crate::prelude::Value]) -> $crate::prelude::MoltResult {
check_args($subc, argv, $subc + 1, 0, "subcommand ?arg ...?")?;
let sub_name = argv[$subc].as_str();
const HELP_MSG: &str = join_helps_subcmd!( $( [$cmd_name,$cmd_space,$cmd_help], )* );
match sub_name {
$(
$cmd_name => $cmd_func(interp, argv),
)*
"-help" => molt_ok!("usage of{}:\n{}",argv[0..$subc].iter().map(|v|v.as_str()).collect::<Vec<&str>>().join(" "),HELP_MSG),
_ => molt_err_help!("unknown subcommand in \"{} {}\", usage:\n{}", argv[0..$subc].iter().map(|v|v.as_str()).collect::<Vec<&str>>().join(" "),sub_name,HELP_MSG ),
}
}
f
}
}
}
#[macro_export]
macro_rules! join_helps_subcmd {
( ) => {
""
};
( [$first:expr, $second:expr, $third:expr]$(,)? ) => {
concat!(" ", $first, " ", $second, $third, "\n -help")
};
( [$first:expr, $second:expr, $third:expr], $( [$rest_first:expr, $rest_second:expr, $rest_third:expr] ),+$(,)? ) => {
concat!(
" ", $first, " ", $second, $third, "\n",
join_helps_subcmd!($( [$rest_first, $rest_second, $rest_third] ),+)
)
};
}
#[macro_export]
macro_rules! join_helps {
( ) => {
""
};
( [$first:expr, $second:expr, $third:expr]$(,)? ) => {
concat!(" ", $first, " ", $second, $third, "\n help [-all]")
};
( [$first:expr, $second:expr, $third:expr], $( [$rest_first:expr, $rest_second:expr, $rest_third:expr] ),+$(,)? ) => {
concat!(
" ", $first, " ", $second, $third, "\n",
join_helps!($( [$rest_first, $rest_second, $rest_third] ),+)
)
};
}
#[macro_export]
macro_rules! gen_command {
($ctx_type:ty, [ $( ($native_name:tt, $native_func:expr $(,)?) ),* $(,)?], [ $( ($embedded_name:tt, $embedded_space:tt, $embedded_func:expr, $embedded_help:tt $(,)?) ),* $(,)?] $(,)?) => {
$crate::prelude::Command::new(
{fn f(name: &str, interp: &mut $crate::prelude::Interp<$ctx_type>, argv: &[$crate::prelude::Value]) -> $crate::prelude::MoltResult {
const HELP_MSG: &str = join_helps!( $( [$embedded_name,$embedded_space,$embedded_help], )* );
match name {
$crate::prelude::_APPEND => $crate::prelude::cmd_append(interp, argv),
$crate::prelude::_ARRAY => $crate::prelude::cmd_array(interp, argv),
$crate::prelude::_ASSERT_EQ => $crate::prelude::cmd_assert_eq(interp, argv),
$crate::prelude::_BREAK => $crate::prelude::cmd_break(interp, argv),
$crate::prelude::_CATCH => $crate::prelude::cmd_catch(interp, argv),
$crate::prelude::_CONTINUE => $crate::prelude::cmd_continue(interp, argv),
$crate::prelude::_DICT => $crate::prelude::cmd_dict(interp, argv),
$crate::prelude::_ERROR => $crate::prelude::cmd_error(interp, argv),
$crate::prelude::_EXPR => $crate::prelude::cmd_expr(interp, argv),
$crate::prelude::_FOR => $crate::prelude::cmd_for(interp, argv),
$crate::prelude::_FOREACH => $crate::prelude::cmd_foreach(interp, argv),
$crate::prelude::_GLOBAL => $crate::prelude::cmd_global(interp, argv),
$crate::prelude::_IF => $crate::prelude::cmd_if(interp, argv),
$crate::prelude::_INCR => $crate::prelude::cmd_incr(interp, argv),
$crate::prelude::_INFO => $crate::prelude::cmd_info(interp, argv),
$crate::prelude::_JOIN => $crate::prelude::cmd_join(interp, argv),
$crate::prelude::_LAPPEND => $crate::prelude::cmd_lappend(interp, argv),
$crate::prelude::_LINDEX => $crate::prelude::cmd_lindex(interp, argv),
$crate::prelude::_LIST => $crate::prelude::cmd_list(interp, argv),
$crate::prelude::_LLENGTH => $crate::prelude::cmd_llength(interp, argv),
$crate::prelude::_PROC => $crate::prelude::cmd_proc(interp, argv),
$crate::prelude::_PUTS => $crate::prelude::cmd_puts(interp, argv),
$crate::prelude::_RENAME => $crate::prelude::cmd_rename(interp, argv),
$crate::prelude::_RETURN => $crate::prelude::cmd_return(interp, argv),
$crate::prelude::_SET => $crate::prelude::cmd_set(interp, argv),
$crate::prelude::_STRING => $crate::prelude::cmd_string(interp, argv),
$crate::prelude::_THROW => $crate::prelude::cmd_throw(interp, argv),
$crate::prelude::_TIME => $crate::prelude::cmd_time(interp, argv),
$crate::prelude::_UNSET => $crate::prelude::cmd_unset(interp, argv),
$crate::prelude::_WHILE => $crate::prelude::cmd_while(interp, argv),
"help" => {
if let Some(v)= argv.get(1){
if v.as_str()=="-all"{
let proc_command_names = interp.proc_command_names();
if proc_command_names.is_empty(){
return molt_ok!("usage of {}:\ntcl:\n {}\n{}:\n{}", interp.name,interp.native_command_names(),interp.name,HELP_MSG);
}else{
return molt_ok!("usage of {}:\ntcl:\n {}\n{}:\n{}\nprocedure:\n {}", interp.name,interp.native_command_names(),interp.name,HELP_MSG,proc_command_names);
}
}
}
molt_ok!("usage of {}:\n{}",interp.name,HELP_MSG)},
$(
$native_name => $native_func(interp, argv),
)*
$(
$embedded_name => $embedded_func(interp, argv),
)*
other => {
if let Some(proc) = interp.get_proc(other) {
proc.clone().execute(interp, argv)
} else {
let proc_command_names = interp.proc_command_names();
if proc_command_names.is_empty(){
molt_err_help!("unknown command \"{}\", valid commands:\ntcl:\n {}\n{}:\n{}", name,interp.native_command_names(),interp.name,HELP_MSG)
}else{
molt_err_help!("unknown command \"{}\", valid commands:\ntcl:\n {}\n{}:\n{}\nprocedure:\n {}", name,interp.native_command_names(),interp.name,HELP_MSG,proc_command_names)
}
}
}
}
}
f as fn(&str, &mut $crate::prelude::Interp<$ctx_type>, &[$crate::prelude::Value]) -> $crate::prelude::MoltResult
},
{fn f(name: &str, interp: &$crate::prelude::Interp<$ctx_type>) -> Option<$crate::prelude::CommandType> {
match name {
$crate::prelude::_APPEND => Some($crate::prelude::CommandType::Native),
$crate::prelude::_ARRAY => Some($crate::prelude::CommandType::Native),
$crate::prelude::_ASSERT_EQ => Some($crate::prelude::CommandType::Native),
$crate::prelude::_BREAK => Some($crate::prelude::CommandType::Native),
$crate::prelude::_CATCH => Some($crate::prelude::CommandType::Native),
$crate::prelude::_CONTINUE => Some($crate::prelude::CommandType::Native),
$crate::prelude::_DICT => Some($crate::prelude::CommandType::Native),
$crate::prelude::_ERROR => Some($crate::prelude::CommandType::Native),
$crate::prelude::_EXPR => Some($crate::prelude::CommandType::Native),
$crate::prelude::_FOR => Some($crate::prelude::CommandType::Native),
$crate::prelude::_FOREACH => Some($crate::prelude::CommandType::Native),
$crate::prelude::_GLOBAL => Some($crate::prelude::CommandType::Native),
$crate::prelude::_IF => Some($crate::prelude::CommandType::Native),
$crate::prelude::_INCR => Some($crate::prelude::CommandType::Native),
$crate::prelude::_INFO => Some($crate::prelude::CommandType::Native),
$crate::prelude::_JOIN => Some($crate::prelude::CommandType::Native),
$crate::prelude::_LAPPEND => Some($crate::prelude::CommandType::Native),
$crate::prelude::_LINDEX => Some($crate::prelude::CommandType::Native),
$crate::prelude::_LIST => Some($crate::prelude::CommandType::Native),
$crate::prelude::_LLENGTH => Some($crate::prelude::CommandType::Native),
$crate::prelude::_PROC => Some($crate::prelude::CommandType::Native),
$crate::prelude::_PUTS => Some($crate::prelude::CommandType::Native),
$crate::prelude::_RENAME => Some($crate::prelude::CommandType::Native),
$crate::prelude::_RETURN => Some($crate::prelude::CommandType::Native),
$crate::prelude::_SET => Some($crate::prelude::CommandType::Native),
$crate::prelude::_STRING => Some($crate::prelude::CommandType::Native),
$crate::prelude::_THROW => Some($crate::prelude::CommandType::Native),
$crate::prelude::_TIME => Some($crate::prelude::CommandType::Native),
$crate::prelude::_UNSET => Some($crate::prelude::CommandType::Native),
$crate::prelude::_WHILE => Some($crate::prelude::CommandType::Native),
$(
$native_name => Some($crate::prelude::CommandType::Native),
)*
$(
$embedded_name => Some($crate::prelude::CommandType::Embedded),
)*
other => {
if interp.contains_proc(other) {
Some($crate::prelude::CommandType::Proc)
} else {
None
}
}
}
}
f as fn(&str, &$crate::prelude::Interp<$ctx_type>) -> Option<$crate::prelude::CommandType>
},
&[
$crate::prelude::_APPEND,
$crate::prelude::_ARRAY,
$crate::prelude::_ASSERT_EQ,
$crate::prelude::_BREAK,
$crate::prelude::_CATCH,
$crate::prelude::_CONTINUE,
$crate::prelude::_DICT,
$crate::prelude::_ERROR,
$crate::prelude::_EXPR,
$crate::prelude::_FOR,
$crate::prelude::_FOREACH,
$crate::prelude::_GLOBAL,
$crate::prelude::_IF,
$crate::prelude::_INCR,
$crate::prelude::_INFO,
$crate::prelude::_JOIN,
$crate::prelude::_LAPPEND,
$crate::prelude::_LINDEX,
$crate::prelude::_LIST,
$crate::prelude::_LLENGTH,
$crate::prelude::_PROC,
$crate::prelude::_PUTS,
$crate::prelude::_RENAME,
$crate::prelude::_RETURN,
$crate::prelude::_SET,
$crate::prelude::_STRING,
$crate::prelude::_THROW,
$crate::prelude::_TIME,
$crate::prelude::_UNSET,
$crate::prelude::_WHILE,
$(
$native_name,
)*
],
&[
$(
$embedded_name,
)*
]
)
};
}
#[cfg(test)]
mod tests {
use crate::*;
#[test]
fn test_molt_ok() {
let result: MoltResult = molt_ok!();
assert_eq!(Ok(Value::empty()), result);
let result: MoltResult = molt_ok!(5);
assert_eq!(Ok(Value::from(5)), result);
let result: MoltResult = molt_ok!("Five");
assert_eq!(Ok(Value::from("Five")), result);
let result: MoltResult = molt_ok!("The answer is {}.", 5);
assert_eq!(Ok(Value::from("The answer is 5.")), result);
}
#[test]
fn test_molt_err() {
check_err(molt_err!("error message"), "error message");
check_err(molt_err!("error {}", 5), "error 5");
}
#[test]
fn test_molt_throw() {
check_throw(molt_throw!("MYERR", "error message"), "MYERR", "error message");
check_throw(molt_throw!("MYERR", "error {}", 5), "MYERR", "error 5");
}
fn check_err(result: MoltResult, msg: &str) -> bool {
match result {
Err(exception) => exception.is_error() && exception.value() == msg.into(),
_ => false,
}
}
fn check_throw(result: MoltResult, code: &str, msg: &str) -> bool {
match result {
Err(exception) => {
exception.is_error()
&& exception.value() == msg.into()
&& exception.error_code() == code.into()
}
_ => false,
}
}
}