1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
use molt::Interp; use molt::MoltList; use molt::ResultCode; use molt::Value; use rustyline::error::ReadlineError; use rustyline::Editor; use std::fs; /// Invokes an interactive REPL for the given interpreter, using `rustyline` line editing. /// /// The REPL will display the given prompt to the user. Press `^C` to terminate /// the REPL, returning control to the caller. Entering `exit` will also normallycause the /// application to terminate (but the `exit` command can be removed or redefined by the /// application). /// /// See [`molt::interp`](../molt/interp/index.html) for details on how to configure and /// add commands to a Molt interpreter. /// /// # Example /// /// ``` /// use molt::Interp; /// /// // FIRST, create and initialize the interpreter. /// let mut interp = Interp::new(); /// /// // NOTE: commands can be added to the interpreter here. /// /// // NEXT, invoke the REPL. /// molt_shell::repl(&mut interp, "% "); /// ``` pub fn repl(interp: &mut Interp, prompt: &str) { let mut rl = Editor::<()>::new(); loop { let readline = rl.readline(prompt); match readline { Ok(line) => { let line = line.trim(); if !line.is_empty() { match interp.eval(line) { Ok(value) | Err(ResultCode::Return(value)) => { rl.add_history_entry(line); // Don't output empty values. if !value.as_str().is_empty() { println!("{}", value); } } Err(ResultCode::Error(msg)) => { println!("{}", msg); } result => { // Must be Break or Continue, which should have been caught // by eval(), so this should never happen. But panicking would // be rude. println!("Unexpected eval return: {:?}", result); } } } } Err(ReadlineError::Interrupted) => { println!("^C"); break; } Err(ReadlineError::Eof) => break, Err(err) => { println!("I/O Error: {:?}", err); break; } } } } /// Executes a script from a set of command line arguments. /// /// `args[0]` is presumed to be the name of a Molt script file, with any subsequent /// arguments being arguments to pass to the script. The script will be be executed in /// the context of the given interpreter. /// /// # Molt Variables /// /// The calling information will be passed to the interpreter in the form of Molt /// variables: /// /// * The Molt variable `arg0` will be set to the `arg0` value. /// * The Molt variable `argv` will be set to a Molt list containing the remainder of the /// `argv` array. /// /// See [`molt::interp`](../molt/interp/index.html) for details on how to configure and /// add commands to a Molt interpreter. /// /// # Example /// /// ``` /// use molt::Interp; /// use std::env; /// /// // FIRST, get the command line arguments. /// let args: Vec<String> = env::args().collect(); /// /// // NEXT, create and initialize the interpreter. /// let mut interp = Interp::new(); /// /// // NOTE: commands can be added to the interpreter here. /// /// // NEXT, evaluate the file, if any. /// if args.len() > 1 { /// molt_shell::script(&mut interp, &args[1..]); /// } else { /// eprintln!("Usage: myshell *filename.tcl"); /// } /// ``` pub fn script(interp: &mut Interp, args: &[String]) { let arg0 = &args[0]; let argv = &args[1..]; match fs::read_to_string(&args[0]) { Ok(script) => execute_script(interp, script, arg0, argv), Err(e) => println!("{}", e), } } /// Executes a script read from a file, with any command-line arguments, in /// the context of the given interpreter. The `script` is the text of the /// script, `arg0` is the name of the script file, and `argv` contains the script /// arguments. /// /// # Molt Variables /// /// The calling information will be passed to the interpreter in the form of Molt /// variables: /// /// * The Molt variable `arg0` will be set to the `arg0` value. /// * The Molt variable `argv` will be set to the `argv` array as a Molt list. fn execute_script(interp: &mut Interp, script: String, arg0: &str, argv: &[String]) { let argv: MoltList = argv.iter().map(Value::from).collect(); interp.set_var("arg0", &Value::from(arg0)); interp.set_var("argv", &Value::from(argv)); match interp.eval(&script) { Ok(_) => (), Err(ResultCode::Return(_)) => (), Err(ResultCode::Error(msg)) => { eprintln!("{}", msg); std::process::exit(1); } result => { // Break or Continue; should never happen, since eval() is supposed to convert // these to errors. panic!("Unexpected eval return: {:?}", result) } } }