muss/src/repl.rs

182 lines
6.4 KiB
Rust
Raw Normal View History

2022-01-03 22:53:57 +00:00
//! Read, Execute, Print Loop functionality
use std::io::{self, Read, Stdin, Write};
2022-01-03 22:53:57 +00:00
use mps_interpreter::MpsRunner;
use mps_player::{MpsController, MpsPlayer};
2022-01-03 22:53:57 +00:00
2022-01-04 01:15:19 +00:00
use super::channel_io::{channel_io, ChannelWriter};
use super::cli::CliArgs;
2022-01-04 01:15:19 +00:00
struct ReplState {
stdin: Stdin,
line_number: usize,
statement_buf: Vec<u8>,
writer: ChannelWriter,
in_literal: Option<char>,
bracket_depth: usize,
curly_depth: usize,
2022-01-04 01:15:19 +00:00
}
impl ReplState {
fn new(chan_writer: ChannelWriter) -> Self {
Self {
stdin: io::stdin(),
line_number: 0,
statement_buf: Vec::new(),
writer: chan_writer,
in_literal: None,
bracket_depth: 0,
curly_depth: 0,
2022-01-04 01:15:19 +00:00
}
}
}
2022-01-03 22:53:57 +00:00
pub fn repl(args: CliArgs) {
/*let mut terminal = termios::Termios::from_fd(0 /* stdin */).unwrap();
terminal.c_lflag &= !termios::ICANON; // no echo and canonical mode
termios::tcsetattr(0, termios::TCSANOW, &mut terminal).unwrap();*/
2022-01-04 01:15:19 +00:00
let (writer, reader) = channel_io();
2022-02-03 22:04:01 +00:00
let volume = args.volume.clone();
2022-01-03 22:53:57 +00:00
let player_builder = move || {
let runner = MpsRunner::with_stream(reader);
2022-01-31 14:22:36 +00:00
2022-02-03 22:04:01 +00:00
let player = MpsPlayer::new(runner).unwrap();
if let Some(vol) = volume {
player.set_volume(vol);
}
player
2022-01-03 22:53:57 +00:00
};
2022-01-04 01:15:19 +00:00
let mut state = ReplState::new(writer);
2022-01-03 22:53:57 +00:00
if let Some(playlist_file) = &args.playlist {
println!("Playlist mode (output: `{}`)", playlist_file);
let mut player = player_builder();
2022-01-31 14:22:36 +00:00
let mut playlist_writer =
io::BufWriter::new(std::fs::File::create(playlist_file).unwrap_or_else(|_| {
panic!("Abort: Cannot create writeable file `{}`", playlist_file)
}));
2022-01-04 01:15:19 +00:00
read_loop(&args, &mut state, || {
match player.save_m3u8(&mut playlist_writer) {
Ok(_) => {}
Err(e) => {
error_prompt(e, &args);
// consume any further errors (this shouldn't actually write anything)
while let Err(e) = player.save_m3u8(&mut playlist_writer) {
error_prompt(e, &args);
}
2022-01-24 21:12:29 +00:00
}
2022-01-03 22:53:57 +00:00
}
playlist_writer
.flush()
.expect("Failed to flush playlist to file");
2022-01-04 01:15:19 +00:00
});
2022-01-03 22:53:57 +00:00
} else {
println!("Playback mode (output: audio device)");
let ctrl = MpsController::create_repl(player_builder);
2022-01-04 01:15:19 +00:00
read_loop(&args, &mut state, || {
if args.wait {
match ctrl.wait_for_empty() {
Ok(_) => {}
Err(e) => error_prompt(e, &args),
2022-01-04 01:15:19 +00:00
}
} else {
// consume all incoming errors
let mut had_err = true;
while had_err {
let mut new_had_err = false;
for e in ctrl.check_ack() {
error_prompt(e, &args);
new_had_err = true;
}
had_err = new_had_err;
2022-01-04 01:15:19 +00:00
}
2022-01-03 22:53:57 +00:00
}
2022-01-04 01:15:19 +00:00
});
}
}
fn read_loop<F: FnMut()>(args: &CliArgs, state: &mut ReplState, mut execute: F) -> ! {
let mut read_buf: [u8; 1] = [0];
2022-01-04 01:15:19 +00:00
prompt(&mut state.line_number, args);
loop {
let mut read_count = 0;
//read_buf[0] = 0;
while read_count == 0 {
2022-01-04 01:15:19 +00:00
// TODO: enable raw mode (char by char) reading of stdin
read_count = state
.stdin
.read(&mut read_buf)
.expect("Failed to read stdin");
2022-01-04 01:15:19 +00:00
}
//println!("Read {}", read_buf[0]);
state.statement_buf.push(read_buf[0]);
2022-01-04 01:15:19 +00:00
match read_buf[0] as char {
'"' | '`' => {
if let Some(c) = state.in_literal {
if c == read_buf[0] as char {
state.in_literal = None;
}
} else {
state.in_literal = Some(read_buf[0] as char);
}
2022-01-24 21:12:29 +00:00
}
'(' => state.bracket_depth += 1,
')' => if state.bracket_depth != 0 { state.bracket_depth -= 1 },
'{' => state.curly_depth += 1,
'}' => if state.curly_depth != 0 { state.curly_depth -= 1 },
';' => {
if state.in_literal.is_none() {
state
.writer
.write(state.statement_buf.as_slice())
.expect("Failed to write to MPS interpreter");
execute();
state.statement_buf.clear();
}
2022-01-24 21:12:29 +00:00
}
2022-01-04 01:15:19 +00:00
'\n' => {
let statement_result = std::str::from_utf8(state.statement_buf.as_slice());
2022-01-29 21:13:52 +00:00
if statement_result.is_ok() && statement_result.unwrap().trim().starts_with('?') {
//println!("Got {}", statement_result.unwrap());
repl_commands(statement_result.unwrap().trim());
state.statement_buf.clear();
} else if state.bracket_depth == 0 && state.in_literal.is_none() && state.curly_depth == 0 {
2022-01-29 21:13:52 +00:00
state.statement_buf.push(b';');
state
.writer
.write(state.statement_buf.as_slice())
.expect("Failed to write to MPS interpreter");
execute();
state.statement_buf.clear();
}
2022-01-04 01:15:19 +00:00
prompt(&mut state.line_number, args);
2022-01-24 21:12:29 +00:00
}
_ => {}
2022-01-03 22:53:57 +00:00
}
}
}
#[inline(always)]
2022-01-04 01:15:19 +00:00
fn prompt(line: &mut usize, args: &CliArgs) {
print!("{}{}", line, args.prompt);
2022-01-03 22:53:57 +00:00
*line += 1;
std::io::stdout().flush().expect("Failed to flush stdout");
}
#[inline(always)]
fn error_prompt(error: mps_player::PlaybackError, args: &CliArgs) {
eprintln!("E{}{}", args.prompt, error.message());
}
fn repl_commands(command_str: &str) {
2022-01-29 21:13:52 +00:00
let words: Vec<&str> = command_str.split(' ').map(|s| s.trim()).collect();
match words[0] {
2022-01-04 02:22:22 +00:00
"?help" => println!("{}", super::help::HELP_STRING),
"?function" | "?functions" => println!("{}", super::help::FUNCTIONS),
"?filter" | "?filters" => println!("{}", super::help::FILTERS),
2022-01-25 15:03:27 +00:00
"?sort" | "?sorter" | "?sorters" => println!("{}", super::help::SORTERS),
"?proc" | "?procedure" | "?procedures" => println!("{}", super::help::PROCEDURES),
_ => println!("Unknown command, try ?help"),
}
}