use crate::dict::dict_new;
use crate::expr;
use crate::gen_command;
use crate::list::list_to_string;
use crate::molt_err;
use crate::molt_ok;
use crate::parser;
use crate::parser::Script;
use crate::parser::Word;
use crate::scope::ScopeStack;
use crate::types::*;
use crate::value::Value;
use std::collections::HashMap;
use std::rc::Rc;
cfg_if::cfg_if! {
if #[cfg(feature = "wasm")] {
use wasm_timer::Instant;
}else{
use std::time::Instant;
}
}
const OPT_CODE: &str = "-code";
const OPT_LEVEL: &str = "-level";
const OPT_ERRORCODE: &str = "-errorcode";
const OPT_ERRORINFO: &str = "-errorinfo";
const ZERO: &str = "0";
pub enum CommandType {
Native,
Embedded,
Proc,
}
pub struct Command<Ctx: 'static> {
fn_execute: fn(&str, &mut Interp<Ctx>, &[Value]) -> MoltResult,
fn_type: fn(&str, &Interp<Ctx>) -> Option<CommandType>,
native_names: &'static [&'static str],
embedded_names: &'static [&'static str],
}
impl<Ctx> Command<Ctx> {
#[inline]
pub fn new(
fn_execute: fn(&str, &mut Interp<Ctx>, &[Value]) -> MoltResult,
fn_type: fn(&str, &Interp<Ctx>) -> Option<CommandType>,
native_names: &'static [&'static str],
embedded_names: &'static [&'static str],
) -> Self {
Self { fn_execute, fn_type, native_names, embedded_names }
}
}
cfg_if::cfg_if! {
if #[cfg(feature = "std_buff")] {
pub struct Interp<Ctx> where
Ctx: 'static,
{
pub name: &'static str,
command: Command<Ctx>,
procs: HashMap<String, Rc<Procedure>>,
scopes: ScopeStack,
pub context: Ctx,
pub std_buff: Vec<Result<Value,Exception>>,
recursion_limit: usize,
num_levels: usize,
profile_map: HashMap<String, ProfileRecord>,
continue_on_error: bool,
}
}else{
pub struct Interp<Ctx> where
Ctx: 'static,
{
pub name: &'static str,
command: Command<Ctx>,
procs: HashMap<String, Rc<Procedure>>,
scopes: ScopeStack,
pub context: Ctx,
recursion_limit: usize,
num_levels: usize,
profile_map: HashMap<String, ProfileRecord>,
continue_on_error: bool,
}
}
}
#[derive(Debug, Clone, Copy)]
struct ProfileRecord {
count: u128,
nanos: u128,
}
impl ProfileRecord {
fn new() -> Self {
Self { count: 0, nanos: 0 }
}
}
impl Interp<()> {
pub fn default() -> Self {
use crate::prelude::*;
let command = gen_command!(
(),
[
(_SOURCE, cmd_source),
(_EXIT, cmd_exit),
(_PARSE, cmd_parse),
(_PDUMP, cmd_pdump),
(_PCLEAR, cmd_pclear)
],
[]
);
Interp::new((), command, true, "default-app")
}
}
impl<Ctx> Interp<Ctx>
where
Ctx: 'static,
{
#[inline]
pub fn contains_proc(&self, proc_name: &str) -> bool {
self.procs.contains_key(proc_name)
}
#[inline]
pub fn get_proc(&self, proc_name: &str) -> Option<&Rc<Procedure>> {
self.procs.get(proc_name)
}
#[inline]
pub fn new(
context: Ctx,
command: Command<Ctx>,
use_env: bool,
name: &'static str,
) -> Self {
cfg_if::cfg_if! {
if #[cfg(feature = "std_buff")] {
let mut interp = Self {
name,
command,
recursion_limit: 1000,
procs: HashMap::new(),
context,
std_buff: Vec::new(),
scopes: ScopeStack::new(),
num_levels: 0,
profile_map: HashMap::new(),
continue_on_error: false,
};
} else {
let mut interp = Self {
name,
recursion_limit: 1000,
command,
procs: HashMap::new(),
context,
scopes: ScopeStack::new(),
num_levels: 0,
profile_map: HashMap::new(),
continue_on_error: false,
};
}
}
interp.set_scalar("errorInfo", Value::empty()).unwrap();
if use_env {
interp.populate_env();
}
interp
}
#[inline]
fn populate_env(&mut self) {
for (key, value) in std::env::vars() {
let _ = self.set_element("env", &key, value.into());
}
}
#[inline]
pub fn eval(&mut self, script: &str) -> MoltResult {
let value = Value::from(script);
self.eval_value(&value)
}
#[inline]
pub fn eval_value(&mut self, value: &Value) -> MoltResult {
self.num_levels += 1;
if self.num_levels > self.recursion_limit {
self.num_levels -= 1;
return molt_err!("too many nested calls to Interp::eval (infinite loop?)");
}
let mut result = self.eval_script(&*value.as_script()?);
self.num_levels -= 1;
if self.num_levels == 0 {
if let Err(mut exception) = result {
if exception.code() == ResultCode::Return {
exception.decrement_level();
}
result = match exception.code() {
ResultCode::Okay => Ok(exception.value()),
ResultCode::Error => Err(exception),
ResultCode::Return => Err(exception), ResultCode::Break => molt_err!("invoked \"break\" outside of a loop"),
ResultCode::Continue => {
molt_err!("invoked \"continue\" outside of a loop")
}
ResultCode::Other(_) => molt_err!("unexpected result code."),
};
}
}
if let Err(exception) = &result {
if exception.is_error() {
self.set_global_error_data(exception.error_data())?;
}
}
result
}
#[inline]
fn set_global_error_data(
&mut self,
error_data: Option<&ErrorData>,
) -> Result<(), Exception> {
if let Some(data) = error_data {
self.scopes.set_global("errorInfo", data.error_info())?;
self.scopes.set_global("errorCode", data.error_code())?;
}
Ok(())
}
pub(crate) fn eval_script(&mut self, script: &Script) -> MoltResult {
let mut result_value: MoltResult = Ok(Value::empty());
for word_vec in script.commands() {
let words = match self.eval_word_vec(word_vec.words()) {
Ok(words) => words,
Err(e) => {
if e.code() == ResultCode::Error && self.continue_on_error {
if let Err(e) = result_value {
cfg_if::cfg_if! {
if #[cfg(feature = "wasm")] {
self.std_buff.push(Err(e.clone()));
}
}
}
result_value = Err(e);
continue;
}
return Err(e);
}
};
if words.is_empty() {
break;
}
let name = words[0].as_str();
if let Err(e) = result_value {
cfg_if::cfg_if! {
if #[cfg(feature = "wasm")] {
self.std_buff.push(Err(e.clone()));
}
}
}
let result = (self.command.fn_execute)(name, self, words.as_slice());
if let Ok(v) = result {
result_value = Ok(v);
} else if let Err(mut exception) = result {
match exception.code() {
ResultCode::Error => {
if exception.is_new_error() {
exception.add_error_info("while executing");
exception.add_error_info(&format!(
" \"{}\"",
&list_to_string(&words)
));
}
}
_ => return Err(exception),
}
if !self.continue_on_error {
return Err(exception);
} else {
result_value = Err(exception);
}
} else {
unreachable!();
}
}
result_value
}
#[inline]
fn eval_word_vec(&mut self, words: &[Word]) -> Result<MoltList, Exception> {
let mut list: MoltList = Vec::new();
for word in words {
if let Word::Expand(word_to_expand) = word {
let value = self.eval_word(word_to_expand)?;
for val in &*value.as_list()? {
list.push(val.clone());
}
} else {
list.push(self.eval_word(word)?);
}
}
Ok(list)
}
#[inline]
pub(crate) fn eval_word(&mut self, word: &Word) -> MoltResult {
match word {
Word::Value(val) => Ok(val.clone()),
Word::VarRef(name) => self.scalar(name),
Word::ArrayRef(name, index_word) => {
let index = self.eval_word(index_word)?;
self.element(name, index.as_str())
}
Word::Script(script) => self.eval_script(script),
Word::Tokens(tokens) => {
let tlist = self.eval_word_vec(tokens)?;
let string: String = tlist.iter().map(|i| i.as_str()).collect();
Ok(Value::from(string))
}
Word::Expand(_) => panic!("recursive Expand!"),
Word::String(str) => Ok(Value::from(str)),
}
}
pub(crate) fn return_options(&self, result: &MoltResult) -> Value {
let mut opts = dict_new();
match result {
Ok(_) => {
opts.insert(OPT_CODE.into(), ZERO.into());
opts.insert(OPT_LEVEL.into(), ZERO.into());
}
Err(exception) => {
match exception.code() {
ResultCode::Okay => unreachable!(), ResultCode::Error => {
let data =
exception.error_data().expect("Error has no error data");
opts.insert(OPT_CODE.into(), "1".into());
opts.insert(OPT_ERRORCODE.into(), data.error_code());
opts.insert(OPT_ERRORINFO.into(), data.error_info());
}
ResultCode::Return => {
opts.insert(
OPT_CODE.into(),
exception.next_code().as_int().into(),
);
if let Some(data) = exception.error_data() {
opts.insert(OPT_ERRORCODE.into(), data.error_code());
opts.insert(OPT_ERRORINFO.into(), data.error_info());
}
}
ResultCode::Break => {
opts.insert(OPT_CODE.into(), "3".into());
}
ResultCode::Continue => {
opts.insert(OPT_CODE.into(), "4".into());
}
ResultCode::Other(num) => {
opts.insert(OPT_CODE.into(), num.into());
}
}
opts.insert(OPT_LEVEL.into(), Value::from(exception.level() as MoltInt));
}
}
Value::from(opts)
}
#[inline]
pub fn complete(&mut self, script: &str) -> bool {
parser::parse(script).is_ok()
}
#[inline]
pub fn expr(&mut self, expr: &Value) -> MoltResult {
let result = expr::expr(self, expr);
if let Err(exception) = &result {
self.set_global_error_data(exception.error_data())?;
}
result
}
#[inline]
pub fn expr_bool(&mut self, expr: &Value) -> Result<bool, Exception> {
self.expr(expr)?.as_bool()
}
#[inline]
pub fn expr_int(&mut self, expr: &Value) -> Result<MoltInt, Exception> {
self.expr(expr)?.as_int()
}
#[inline]
pub fn expr_float(&mut self, expr: &Value) -> Result<MoltFloat, Exception> {
self.expr(expr)?.as_float()
}
#[inline]
pub fn var(&self, var_name: &Value) -> MoltResult {
let var_name = &*var_name.as_var_name();
match var_name.index() {
Some(index) => self.element(var_name.name(), index),
None => self.scalar(var_name.name()),
}
}
#[inline]
pub fn var_exists(&self, var_name: &Value) -> bool {
let var_name = &*var_name.as_var_name();
match var_name.index() {
Some(index) => self.scopes.elem_exists(var_name.name(), index),
None => self.scopes.exists(var_name.name()),
}
}
#[inline]
pub fn set_var(&mut self, var_name: &Value, value: Value) -> Result<(), Exception> {
let var_name = &*var_name.as_var_name();
match var_name.index() {
Some(index) => self.set_element(var_name.name(), index, value),
None => self.set_scalar(var_name.name(), value),
}
}
#[inline]
pub fn set_var_return(&mut self, var_name: &Value, value: Value) -> MoltResult {
let var_name = &*var_name.as_var_name();
match var_name.index() {
Some(index) => self.set_element_return(var_name.name(), index, value),
None => self.set_scalar_return(var_name.name(), value),
}
}
#[inline]
pub fn scalar(&self, name: &str) -> MoltResult {
self.scopes.get(name)
}
#[inline]
pub fn set_scalar(&mut self, name: &str, value: Value) -> Result<(), Exception> {
self.scopes.set(name, value)
}
#[inline]
pub fn set_scalar_return(&mut self, name: &str, value: Value) -> MoltResult {
self.scopes.set(name, value.clone())?;
Ok(value)
}
#[inline]
pub fn element(&self, name: &str, index: &str) -> MoltResult {
self.scopes.get_elem(name, index)
}
#[inline]
pub fn set_element(
&mut self,
name: &str,
index: &str,
value: Value,
) -> Result<(), Exception> {
self.scopes.set_elem(name, index, value)
}
#[inline]
pub fn set_element_return(
&mut self,
name: &str,
index: &str,
value: Value,
) -> MoltResult {
self.scopes.set_elem(name, index, value.clone())?;
Ok(value)
}
#[inline]
pub fn unset(&mut self, name: &str) {
self.scopes.unset(name);
}
#[inline]
pub fn unset_var(&mut self, name: &Value) {
let var_name = name.as_var_name();
if let Some(index) = var_name.index() {
self.unset_element(var_name.name(), index);
} else {
self.unset(var_name.name());
}
}
#[inline]
pub fn unset_element(&mut self, array_name: &str, index: &str) {
self.scopes.unset_element(array_name, index);
}
#[inline]
pub fn vars_in_scope(&self) -> MoltList {
self.scopes.vars_in_scope()
}
#[inline]
pub fn vars_in_global_scope(&self) -> MoltList {
self.scopes.vars_in_global_scope()
}
#[inline]
pub fn vars_in_local_scope(&self) -> MoltList {
self.scopes.vars_in_local_scope()
}
#[inline]
pub fn upvar(&mut self, level: usize, name: &str) {
assert!(level <= self.scopes.current(), "Invalid scope level");
self.scopes.upvar(level, name);
}
#[inline]
pub fn push_scope(&mut self) {
self.scopes.push();
}
#[inline]
pub fn pop_scope(&mut self) {
self.scopes.pop();
}
#[inline]
pub fn scope_level(&self) -> usize {
self.scopes.current()
}
#[inline]
pub(crate) fn array_unset(&mut self, array_name: &str) {
self.scopes.array_unset(array_name);
}
#[inline]
pub fn array_exists(&self, array_name: &str) -> bool {
self.scopes.array_exists(array_name)
}
#[inline]
pub fn array_get(&self, array_name: &str) -> MoltList {
self.scopes.array_get(array_name)
}
#[inline]
pub fn array_set(&mut self, array_name: &str, kvlist: &[Value]) -> MoltResult {
if kvlist.len() % 2 == 0 {
self.scopes.array_set(array_name, kvlist)?;
molt_ok!()
} else {
molt_err!("list must have an even number of elements")
}
}
#[inline]
pub fn array_names(&self, array_name: &str) -> MoltList {
self.scopes.array_indices(array_name)
}
#[inline]
pub fn array_size(&self, array_name: &str) -> usize {
self.scopes.array_size(array_name)
}
#[inline]
pub(crate) fn add_proc(&mut self, name: &str, parms: &[Value], body: &Value) {
self.procs.insert(
name.into(),
Rc::new(Procedure { parms: parms.to_owned(), body: body.clone() }),
);
}
#[inline]
pub fn has_proc(&self, name: &str) -> bool {
self.procs.contains_key(name)
}
#[inline]
pub fn rename_proc(&mut self, old_name: &str, new_name: &str) {
if let Some(proc) = self.procs.remove(old_name) {
self.procs.insert(new_name.into(), proc);
}
}
#[inline]
pub fn remove_proc(&mut self, name: &str) {
self.procs.remove(name);
}
#[inline]
pub fn command_names(&self) -> MoltList {
let mut vec: MoltList =
self.command.native_names.iter().map(|&s| Value::from(s)).collect();
vec.extend(self.command.embedded_names.iter().map(|&s| Value::from(s)));
vec.extend(self.procs.keys().map(Value::from));
vec
}
#[inline]
pub fn native_command_names(&self) -> String {
self.command.native_names.join(", ")
}
#[inline]
pub fn proc_command_names(&self) -> String {
self.procs
.keys()
.map(String::as_str)
.collect::<Vec<&str>>()
.join(", ")
}
#[inline]
pub fn command_type(&self, cmd_name: &str) -> MoltResult {
match (self.command.fn_type)(cmd_name, self) {
Some(CommandType::Native) => molt_ok!("native"),
Some(CommandType::Proc) => molt_ok!("proc"),
Some(CommandType::Embedded) => molt_ok!(self.name),
None => molt_err!("\"{}\" isn't a command", cmd_name),
}
}
#[inline]
pub fn proc_names(&self) -> MoltList {
let vec: MoltList =
self.procs.iter().map(|(name, _)| Value::from(name)).collect();
vec
}
#[inline]
pub fn proc_body(&self, procname: &str) -> MoltResult {
if let Some(proc) = self.procs.get(procname) {
return molt_ok!(proc.body.clone());
}
molt_err!("\"{}\" isn't a procedure", procname)
}
#[inline]
pub fn proc_args(&self, procname: &str) -> MoltResult {
if let Some(proc) = self.procs.get(procname) {
let vec: MoltList = proc
.parms
.iter()
.map(|item| item.as_list().expect("invalid proc parms")[0].clone())
.collect();
return molt_ok!(Value::from(vec));
}
molt_err!("\"{}\" isn't a procedure", procname)
}
#[inline]
pub fn proc_default(
&self,
procname: &str,
arg: &str,
) -> Result<Option<Value>, Exception> {
if let Some(proc) = self.procs.get(procname) {
for argvec in &proc.parms {
let argvec = argvec.as_list()?; if argvec[0].as_str() == arg {
if argvec.len() == 2 {
return Ok(Some(argvec[1].clone()));
} else {
return Ok(None);
}
}
}
return molt_err!(
"procedure \"{}\" doesn't have an argument \"{}\"",
procname,
arg
);
}
molt_err!("\"{}\" isn't a procedure", procname)
}
#[inline]
pub fn recursion_limit(&self) -> usize {
self.recursion_limit
}
#[inline]
pub fn set_recursion_limit(&mut self, limit: usize) {
self.recursion_limit = limit;
}
pub fn profile_save(&mut self, name: &str, start: Instant) {
let dur = Instant::now().duration_since(start).as_nanos();
let rec = self.profile_map.entry(name.into()).or_insert_with(ProfileRecord::new);
rec.count += 1;
rec.nanos += dur;
}
pub fn profile_clear(&mut self) {
self.profile_map.clear();
}
pub fn profile_dump(&self) {
if self.profile_map.is_empty() {
println!("no profile data");
} else {
for (name, rec) in &self.profile_map {
let avg = rec.nanos / rec.count;
println!("{} nanos {}, count={}", avg, name, rec.count);
}
}
}
pub fn continue_on_error(&self) -> bool {
self.continue_on_error
}
pub fn set_continue_on_error(&mut self, c: bool) {
self.continue_on_error = c;
}
}
#[derive(Debug, Clone)]
pub struct Procedure {
parms: MoltList,
body: Value,
}
impl Procedure {
pub fn execute<Ctx>(&self, interp: &mut Interp<Ctx>, argv: &[Value]) -> MoltResult
where
Ctx: 'static,
{
interp.push_scope();
let mut argi = 1;
for (speci, spec) in self.parms.iter().enumerate() {
let vec = &*spec.as_list()?; assert!(vec.len() == 1 || vec.len() == 2);
if vec[0].as_str() == "args" && speci == self.parms.len() - 1 {
interp.set_scalar("args", Value::from(&argv[argi..]))?;
argi = argv.len();
break;
}
if argi < argv.len() {
interp.set_scalar(vec[0].as_str(), argv[argi].clone())?;
argi += 1;
continue;
}
if vec.len() == 2 {
interp.set_scalar(vec[0].as_str(), vec[1].clone())?;
} else {
return self.wrong_num_args(&argv[0]);
}
}
if argi != argv.len() {
return self.wrong_num_args(&argv[0]);
}
let result = interp.eval_value(&self.body);
interp.pop_scope();
if let Err(mut exception) = result {
if exception.code() == ResultCode::Return {
exception.decrement_level();
}
return match exception.code() {
ResultCode::Okay => Ok(exception.value()),
ResultCode::Error => Err(exception),
ResultCode::Return => Err(exception), ResultCode::Break => molt_err!("invoked \"break\" outside of a loop"),
ResultCode::Continue => {
molt_err!("invoked \"continue\" outside of a loop")
}
ResultCode::Other(_) => molt_err!("unexpected result code."),
};
}
result
}
fn wrong_num_args(&self, name: &Value) -> MoltResult {
let mut msg = String::new();
msg.push_str("wrong # args: should be \"");
msg.push_str(name.as_str());
for (i, arg) in self.parms.iter().enumerate() {
msg.push(' ');
if arg.as_str() == "args" && i == self.parms.len() - 1 {
msg.push_str("?arg ...?");
break;
}
let vec = arg.as_list().expect("error in proc arglist validation!");
if vec.len() == 1 {
msg.push_str(vec[0].as_str());
} else {
msg.push('?');
msg.push_str(vec[0].as_str());
msg.push('?');
}
}
msg.push_str("\"");
molt_err!(&msg)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new() {
let interp = Interp::default();
assert!(!interp.command_names().is_empty());
}
#[test]
fn test_eval() {
let mut interp = Interp::default();
assert_eq!(interp.eval("set a 1"), Ok(Value::from("1")));
assert!(ex_match(&interp.eval("error 2"), Exception::molt_err(Value::from("2"))));
assert_eq!(interp.eval("return 3"), Ok(Value::from("3")));
assert!(ex_match(
&interp.eval("break"),
Exception::molt_err(Value::from("invoked \"break\" outside of a loop"))
));
assert!(ex_match(
&interp.eval("continue"),
Exception::molt_err(Value::from("invoked \"continue\" outside of a loop"))
));
}
fn ex_match(r: &MoltResult, expected: Exception) -> bool {
if let Err(e) = r {
e.code() == expected.code() && e.value() == expected.value()
} else {
false
}
}
#[test]
fn test_eval_value() {
let mut interp = Interp::default();
assert_eq!(interp.eval_value(&Value::from("set a 1")), Ok(Value::from("1")));
assert!(ex_match(
&interp.eval_value(&Value::from("error 2")),
Exception::molt_err(Value::from("2"))
));
assert_eq!(interp.eval_value(&Value::from("return 3")), Ok(Value::from("3")));
assert!(ex_match(
&interp.eval_value(&Value::from("break")),
Exception::molt_err(Value::from("invoked \"break\" outside of a loop"))
));
assert!(ex_match(
&interp.eval_value(&Value::from("continue")),
Exception::molt_err(Value::from("invoked \"continue\" outside of a loop"))
));
}
#[test]
fn test_complete() {
let mut interp = Interp::default();
assert!(interp.complete("abc"));
assert!(interp.complete("a {bc} [def] \"ghi\" xyz"));
assert!(!interp.complete("a {bc"));
assert!(!interp.complete("a [bc"));
assert!(!interp.complete("a \"bc"));
}
#[test]
fn test_expr() {
let mut interp = Interp::default();
assert_eq!(interp.expr(&Value::from("1 + 2")), Ok(Value::from(3)));
assert_eq!(
interp.expr(&Value::from("a + b")),
Err(Exception::molt_err(Value::from("unknown math function \"a\"")))
);
}
#[test]
fn test_expr_bool() {
let mut interp = Interp::default();
assert_eq!(interp.expr_bool(&Value::from("1")), Ok(true));
assert_eq!(interp.expr_bool(&Value::from("0")), Ok(false));
assert_eq!(
interp.expr_bool(&Value::from("a")),
Err(Exception::molt_err(Value::from("unknown math function \"a\"")))
);
}
#[test]
fn test_expr_int() {
let mut interp = Interp::default();
assert_eq!(interp.expr_int(&Value::from("1 + 2")), Ok(3));
assert_eq!(
interp.expr_int(&Value::from("a")),
Err(Exception::molt_err(Value::from("unknown math function \"a\"")))
);
}
#[test]
fn test_expr_float() {
let mut interp = Interp::default();
let val = interp
.expr_float(&Value::from("1.1 + 2.2"))
.expect("floating point value");
assert!((val - 3.3).abs() < 0.001);
assert_eq!(
interp.expr_float(&Value::from("a")),
Err(Exception::molt_err(Value::from("unknown math function \"a\"")))
);
}
#[test]
fn test_recursion_limit() {
let mut interp = Interp::default();
assert_eq!(interp.recursion_limit(), 1000);
interp.set_recursion_limit(100);
assert_eq!(interp.recursion_limit(), 100);
assert!(dbg!(interp.eval("proc myproc {} { myproc }")).is_ok());
assert!(ex_match(
&interp.eval("myproc"),
Exception::molt_err(Value::from(
"too many nested calls to Interp::eval (infinite loop?)"
))
));
}
#[test]
fn context_forgotten_2_commands() {
use crate::prelude::*;
let _interp = Interp::new(
(),
gen_command!(
(),
[
(_SOURCE, cmd_source),
(_EXIT, cmd_exit),
(_PARSE, cmd_parse),
(_PDUMP, cmd_pdump),
(_PCLEAR, cmd_pclear),
],
[("dummy", " ", dummy_cmd, ""), ("dummy2", "", dummy_cmd, ""),],
),
true,
"",
);
}
fn dummy_cmd(_: &mut Interp<()>, _: &[Value]) -> MoltResult {
molt_err!("Not really meant to be called")
}
}