use std::io::Read; use std::ops::Deref; use rustyline::error::ReadlineError; use rustyline::Editor; use clap::{Arg, App}; use nom::branch::alt; use nom::combinator::{opt, map}; use nom::sequence::{preceded, terminated, separated_pair, delimited}; use nom::character::complete::{multispace0, multispace1}; use nom::bytes::complete::{take_until, tag}; use nom::{IResult, Err}; use rain_lang::parser::{ ast::{Let, Expr}, builder::Builder, parse_statement, parse_expr, parse_bool }; use std::fs::File; use std::io::BufReader; /// A repl statement #[derive(Debug, Clone, Eq, PartialEq)] pub enum ReplStatement<'a> { Let(Let<'a>), Expr(Expr<'a>), Command(Command) } /// A repl command #[derive(Debug, Clone, Eq, PartialEq)] pub enum Command { Include(String), PrintBuilderState(bool), ShowDefinitions(bool), ShowParse(bool), } /// Parse a repl statement pub fn parse_repl_statement(input: &str) -> IResult<&str, ReplStatement> { terminated(alt(( map(parse_command, |c| ReplStatement::Command(c)), map(parse_statement, |l| ReplStatement::Let(l)), map(parse_expr, |e| ReplStatement::Expr(e)), )), multispace0)(input) } /// Parse a repl command pub fn parse_command(input: &str) -> IResult<&str, Command> { delimited( delimited(multispace0, tag("#"), multispace0), alt(( map( separated_pair(tag("show_parse"), multispace1, opt(parse_bool)), |(_, b)| Command::ShowParse(b.unwrap_or(true)) ), map( separated_pair(tag("builder_state"), multispace1, opt(parse_bool)), |(_, b)| Command::PrintBuilderState(b.unwrap_or(true)) ), map( separated_pair(tag("show_definitions"), multispace1, opt(parse_bool)), |(_, b)| Command::ShowDefinitions(b.unwrap_or(true)) ), map( separated_pair( tag("include"), multispace0, delimited(tag("<"), take_until(">"), tag(">")) ), |(_, f)| Command::Include(String::from(f)) ) )), preceded(multispace0, tag(";")) )(input) } /// A very simple repl for the `rain` IR #[derive(Debug)] pub struct Repl { buffer: String, builder: Builder, cursor: usize, show_parse: bool, show_definitions: bool, prompt: &'static str } const DEFAULT_PROMPT: &'static str = ">>> "; impl Repl { pub fn new() -> Repl { Repl { buffer: String::new(), builder: Builder::new(), cursor: 0, show_parse: false, show_definitions: false, prompt: DEFAULT_PROMPT } } pub fn handle_command(&mut self, command: Command) { match command { Command::ShowParse(b) => { self.show_parse = b; }, Command::ShowDefinitions(d) => { self.show_definitions = d; }, Command::Include(f) => { let file = match File::open(&f) { Ok(file) => file, Err(err) => { eprintln!("Error opening file {:?}: {:?}", f, err); return } }; let mut buf_reader = BufReader::new(file); match buf_reader.read_to_string(&mut self.buffer) { Ok(bytes) => println!("Read {} bytes from {:?}", bytes, f), Err(err) => eprintln!("Error reading file {:?}: {:?}", f, err) } }, Command::PrintBuilderState(pretty) => if pretty { println!("Builder state: {:#?}", self.builder) } else { println!("{:?}", self.builder) } } } pub fn handle_line(&mut self, line: &str) -> bool { self.buffer.push_str(line); self.buffer.push('\n'); self.prompt = DEFAULT_PROMPT; while self.buffer.len() > self.cursor { let (rest, statement) = match parse_repl_statement(&self.buffer[self.cursor..]) { Ok(res) => res, Err(err) => match err { Err::Incomplete(_) => { self.prompt = ""; return false }, err => { eprintln!("Parse error: {:?}", err); return true } } }; let begin = self.cursor; let end = self.buffer.len() - rest.len(); self.cursor = end; match statement { ReplStatement::Command(c) => self.handle_command(c), ReplStatement::Let(l) => { match self.builder.build_let(&l) { Ok(defs) => { if self.show_parse { println!("Parsed let: {:?} => {:?}", &self.buffer[begin..end], l) } if self.show_definitions { println!("Defined {} symbols:", defs.len()); for def in defs.iter() { print!("{} = {}", def.name, def.value); if let Some(previous) = &def.previous { println!(" (previously {})", previous) } else { print!("\n") } } } }, Err(err) => { println!( "Error building let IR: {:#?}\n===========\nAST:\n{:?}\n", err, l ) } } }, ReplStatement::Expr(e) => { match self.builder.build_expr(&e) { Ok(val) => { if self.show_parse { println!("Parsed expr: {:?} => {:?}", &self.buffer[begin..end], e) } println!("{}", val.data().deref()) }, Err(err) => { println!( "Error building expr IR: {:#?}\n===========\nAST:\n{:?}\n", err, e ) } } } }; } true } pub fn buffer(&self) -> &str { &self.buffer } pub fn clear_buffer(&mut self) { self.buffer.clear(); self.cursor = 0; } } fn main() { let matches = App::new("rain repl") .version("0.0") .author("Jad Ghalayini ") .about("repl for rain programs") .arg(Arg::with_name("history") .short("h") .long("history") .value_name("FILE") .help("Sets a file to save/load history") .takes_value(true)) .get_matches(); let history = matches.value_of("history"); let mut rl = Editor::<()>::new(); let mut repl = Repl::new(); if let Some(history) = history { if rl.load_history(history).is_err() { println!("No previous history loaded from {:?}.", history); } else { println!("Loaded history from {:?}.", history) } } else { println!("No previous history loaded.") } loop { let line = rl.readline(repl.prompt); match line { Ok(line) => { if repl.handle_line(line.as_str()) { rl.add_history_entry(repl.buffer()); repl.clear_buffer(); } }, Err(ReadlineError::Interrupted) | Err(ReadlineError::Eof) => { break }, Err(err) => { eprintln!("Error: {:?}", err); break; } } } if let Some(history) = history { rl.save_history(history).unwrap(); } }