schala/schala-repl/src/repl/mod.rs

252 lines
7.5 KiB
Rust
Raw Normal View History

use std::sync::Arc;
2019-05-26 04:16:40 -07:00
use std::collections::HashSet;
2018-09-21 19:43:50 -07:00
use crate::language::{ProgrammingLanguageInterface,
2019-07-08 21:02:07 -07:00
ComputationRequest, LangMetaResponse, LangMetaRequest};
2018-10-15 20:52:34 -07:00
mod command_tree;
2019-06-02 00:48:59 -07:00
use self::command_tree::CommandTree;
2019-05-14 00:40:38 -07:00
mod repl_options;
2019-05-21 02:46:07 -07:00
use repl_options::ReplOptions;
2019-06-02 00:27:12 -07:00
mod directive_actions;
2019-05-22 03:32:00 -07:00
mod directives;
2019-06-01 13:11:07 -07:00
use directives::directives_from_pass_names;
2019-06-04 15:02:51 -07:00
mod help;
2019-07-08 21:02:07 -07:00
mod response;
use response::ReplResponse;
2018-09-21 19:43:50 -07:00
const HISTORY_SAVE_FILE: &'static str = ".schala_history";
const OPTIONS_SAVE_FILE: &'static str = ".schala_repl";
type InterpreterDirectiveOutput = Option<String>;
2019-03-14 03:42:39 -07:00
pub struct Repl {
2019-06-06 23:50:08 -07:00
pub interpreter_directive_sigil: char,
2019-03-12 00:01:30 -07:00
line_reader: ::linefeed::interface::Interface<::linefeed::terminal::DefaultTerminal>,
2019-06-21 02:01:46 -07:00
language_states: Vec<Box<dyn ProgrammingLanguageInterface>>,
2019-05-21 02:46:07 -07:00
options: ReplOptions,
2019-03-12 00:01:30 -07:00
}
2019-09-28 17:31:37 -07:00
#[derive(Clone)]
enum PromptStyle {
Normal,
Multiline
}
2019-03-14 03:42:39 -07:00
impl Repl {
2019-06-21 02:01:46 -07:00
pub fn new(initial_states: Vec<Box<dyn ProgrammingLanguageInterface>>) -> Repl {
2019-03-12 00:01:30 -07:00
use linefeed::Interface;
let line_reader = Interface::new("schala-repl").unwrap();
let interpreter_directive_sigil = ':';
2019-03-14 03:42:39 -07:00
Repl {
2019-05-14 00:40:38 -07:00
interpreter_directive_sigil,
line_reader,
language_states: initial_states,
2019-05-21 02:46:07 -07:00
options: ReplOptions::new(),
2019-03-12 00:01:30 -07:00
}
}
pub fn run_repl(&mut self) {
println!("Schala MetaInterpreter version {}", crate::VERSION_STRING);
2019-03-12 00:01:30 -07:00
println!("Type {}help for help with the REPL", self.interpreter_directive_sigil);
2019-05-14 00:40:38 -07:00
self.load_options();
2019-03-12 00:01:30 -07:00
self.handle_repl_loop();
2019-03-14 04:08:32 -07:00
self.save_before_exit();
2019-03-12 00:01:30 -07:00
println!("Exiting...");
}
2019-05-14 00:40:38 -07:00
fn load_options(&mut self) {
self.line_reader.load_history(HISTORY_SAVE_FILE).unwrap_or(());
2019-05-21 02:46:07 -07:00
match ReplOptions::load_from_file(OPTIONS_SAVE_FILE) {
2019-05-14 00:40:38 -07:00
Ok(options) => {
2019-05-21 02:46:07 -07:00
self.options = options;
2019-05-14 00:40:38 -07:00
},
Err(()) => ()
};
}
2019-03-12 00:01:30 -07:00
fn handle_repl_loop(&mut self) {
use linefeed::ReadResult::*;
2019-09-28 17:31:37 -07:00
let sigil = self.interpreter_directive_sigil;
'main: loop {
macro_rules! match_or_break {
($line:expr) => {
match $line {
Err(e) => {
println!("readline IO Error: {}", e);
break 'main;
},
Ok(Eof) | Ok(Signal(_)) => break 'main,
Ok(Input(ref input)) => input,
}
}
}
2019-03-12 00:01:30 -07:00
self.update_line_reader();
2019-09-28 17:31:37 -07:00
let line = self.line_reader.read_line();
let input: &str = match_or_break!(line);
self.line_reader.add_history_unique(input.to_string());
let mut chars = input.chars().peekable();
let repl_responses = match chars.nth(0) {
Some(ch) if ch == sigil => {
if chars.peek() == Some(&'{') {
let mut buf = String::new();
buf.push_str(input.get(2..).unwrap());
'multiline: loop {
self.set_prompt(PromptStyle::Multiline);
let new_line = self.line_reader.read_line();
let new_input = match_or_break!(new_line);
if new_input.starts_with(":}") {
break 'multiline;
} else {
buf.push_str(new_input);
buf.push_str("\n");
}
}
self.handle_input(&buf)
} else {
match self.handle_interpreter_directive(input) {
2019-07-08 21:02:07 -07:00
Some(directive_output) => println!("<> {}", directive_output),
None => (),
}
2019-09-28 17:31:37 -07:00
continue
2019-03-12 00:01:30 -07:00
}
2019-09-28 17:31:37 -07:00
},
_ => self.handle_input(input)
};
for repl_response in repl_responses.iter() {
println!("{}", repl_response);
2019-03-12 00:01:30 -07:00
}
}
}
fn update_line_reader(&mut self) {
let tab_complete_handler = TabCompleteHandler::new(self.interpreter_directive_sigil, self.get_directives());
2019-06-01 13:11:07 -07:00
self.line_reader.set_completer(Arc::new(tab_complete_handler)); //TODO fix this here
2019-09-28 17:31:37 -07:00
self.set_prompt(PromptStyle::Normal);
}
fn set_prompt(&mut self, prompt_style: PromptStyle) {
let prompt_str = match prompt_style {
PromptStyle::Normal => ">> ".to_string(),
PromptStyle::Multiline => ">| ".to_string(),
};
2019-03-12 00:01:30 -07:00
self.line_reader.set_prompt(&prompt_str).unwrap();
}
2019-03-14 04:08:32 -07:00
fn save_before_exit(&self) {
self.line_reader.save_history(HISTORY_SAVE_FILE).unwrap_or(());
2019-05-21 02:46:07 -07:00
self.options.save_to_file(OPTIONS_SAVE_FILE);
2019-03-12 00:01:30 -07:00
}
fn handle_interpreter_directive(&mut self, input: &str) -> InterpreterDirectiveOutput {
2019-03-14 21:25:37 -07:00
let mut iter = input.chars();
iter.next();
let arguments: Vec<&str> = iter
2019-03-14 21:25:37 -07:00
.as_str()
.split_whitespace()
.collect();
if arguments.len() < 1 {
2019-03-14 21:25:37 -07:00
return None;
}
let directives = self.get_directives();
directives.perform(self, &arguments)
}
2019-06-21 02:01:46 -07:00
fn get_cur_language_state(&mut self) -> &mut Box<dyn ProgrammingLanguageInterface> {
//TODO this is obviously not complete
&mut self.language_states[0]
2019-03-12 00:01:30 -07:00
}
2019-07-08 21:02:07 -07:00
fn handle_input(&mut self, input: &str) -> Vec<ReplResponse> {
2019-05-26 04:16:40 -07:00
let mut debug_requests = HashSet::new();
2019-05-21 02:46:07 -07:00
for ask in self.options.debug_asks.iter() {
2019-05-26 04:16:40 -07:00
debug_requests.insert(ask.clone());
}
2019-07-08 21:02:07 -07:00
let request = ComputationRequest { source: input, debug_requests };
let ref mut language_state = self.get_cur_language_state();
let response = language_state.run_computation(request);
2019-07-08 21:02:07 -07:00
response::handle_computation_response(response, &self.options)
2019-03-12 00:01:30 -07:00
}
2018-09-21 19:43:50 -07:00
fn get_directives(&mut self) -> CommandTree {
let language_state = self.get_cur_language_state();
2019-06-01 13:11:07 -07:00
let pass_names = match language_state.request_meta(LangMetaRequest::StageNames) {
LangMetaResponse::StageNames(names) => names,
_ => vec![],
};
directives_from_pass_names(&pass_names)
2018-09-21 19:43:50 -07:00
}
}
2019-05-22 03:32:00 -07:00
2018-09-21 19:43:50 -07:00
struct TabCompleteHandler {
sigil: char,
top_level_commands: CommandTree,
}
use linefeed::complete::{Completion, Completer};
use linefeed::terminal::Terminal;
impl TabCompleteHandler {
fn new(sigil: char, top_level_commands: CommandTree) -> TabCompleteHandler {
TabCompleteHandler {
top_level_commands,
sigil,
}
}
}
impl<T: Terminal> Completer<T> for TabCompleteHandler {
fn complete(&self, word: &str, prompter: &::linefeed::prompter::Prompter<T>, start: usize, _end: usize) -> Option<Vec<Completion>> {
let line = prompter.buffer();
2019-03-20 00:24:46 -07:00
if !line.starts_with(self.sigil) {
return None;
}
let mut words = line[1..(if start == 0 { 1 } else { start })].split_whitespace();
let mut completions = Vec::new();
let mut command_tree: Option<&CommandTree> = Some(&self.top_level_commands);
loop {
match words.next() {
None => {
let top = match command_tree {
Some(CommandTree::Top(_)) => true,
_ => false
};
let word = if top { word.get(1..).unwrap() } else { word };
2019-06-06 22:21:50 -07:00
for cmd in command_tree.map(|x| x.get_subcommands()).unwrap_or(vec![]).into_iter() {
2019-03-20 00:24:46 -07:00
if cmd.starts_with(word) {
completions.push(Completion {
completion: format!("{}{}", if top { ":" } else { "" }, cmd),
display: Some(cmd.to_string()),
suffix: ::linefeed::complete::Suffix::Some(' ')
})
2018-09-21 19:43:50 -07:00
}
}
2019-03-20 00:24:46 -07:00
break;
},
Some(s) => {
let new_ptr: Option<&CommandTree> = command_tree.and_then(|cm| match cm {
CommandTree::Top(children) => children.iter().find(|c| c.get_cmd() == s),
CommandTree::NonTerminal { children, .. } => children.iter().find(|c| c.get_cmd() == s),
CommandTree::Terminal { children, .. } => children.iter().find(|c| c.get_cmd() == s),
2019-03-20 00:24:46 -07:00
});
command_tree = new_ptr;
2018-09-21 19:43:50 -07:00
}
}
}
2019-03-20 00:24:46 -07:00
Some(completions)
2018-09-21 19:43:50 -07:00
}
}