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