From a4dac3826c0e438e8f9991b0eba9608a6c111048 Mon Sep 17 00:00:00 2001 From: Jan Tuomi Date: Thu, 25 Nov 2021 09:28:39 +0200 Subject: Add REPL --- src/main.rs | 98 ++++++++++++++++++++++++++++++++++++++++++++++++---------- src/runtime.rs | 11 ++++--- 2 files changed, 88 insertions(+), 21 deletions(-) diff --git a/src/main.rs b/src/main.rs index 8ff5efc..4e89250 100644 --- a/src/main.rs +++ b/src/main.rs @@ -21,31 +21,95 @@ macro_rules! extract_enum_value { }; } -fn main() { +fn main() -> Result<(), String> { use ast::Program; use pest::Parser; use std::env; use std::fs; - let script_path = env::args().nth(1).expect("no script file specified"); - - let unparsed_file = fs::read_to_string(script_path).expect("cannot read file"); - let parse_tree_result = lparser::LParser::parse(lparser::Rule::program, &unparsed_file); - - if parse_tree_result.is_err() { - println!("{}", parse_tree_result.unwrap_err()); - return; + let mut args = env::args(); + let command = args.next().unwrap(); + let subcommand_result = args.next(); + if subcommand_result.is_none() { + println!("usage: {c} run FILE\n {c} repl", c = command.as_str()); + return Ok(()); } + let subcommand = subcommand_result.unwrap(); - let mut parse_tree = parse_tree_result.unwrap(); + match subcommand.as_str() { + "run" => { + let script_path = args.next().ok_or("[run] no script file specified")?; + let source = fs::read_to_string(script_path).or(Err("[run] cannot read file"))?; + let parse_tree_result = lparser::LParser::parse(lparser::Rule::program, &source); + if parse_tree_result.is_err() { + println!("{}", parse_tree_result.unwrap_err()); + return Err("[run] failed to parse".to_owned()); + } + let mut parse_tree = parse_tree_result.unwrap(); + let syntax_tree: Program = ast::from_parse_tree(&mut parse_tree); + let process_result = runtime::process(&syntax_tree, None); + if process_result.is_err() { + println!("{}", process_result.unwrap_err()); + return Err("[run] failed to run".to_owned()); + } + Ok(()) + } + "repl" => { + use runtime::*; + use std::collections::HashMap; + use std::io::{stdin, stdout, Write}; + use std::rc::Rc; - // println!("parse tree = {:#?}", parse_tree); - let syntax_tree: Program = ast::from_parse_tree(&mut parse_tree); - // println!("syntax tree = {:#?}", syntax_tree); + println!("REPL"); + println!("\"#exit\" to exit"); + println!("\"#symbols\" to display symbol table\n"); - let process_result = runtime::process(&syntax_tree, None); - if process_result.is_err() { - println!("{}", process_result.unwrap_err()); - return; + let mut symbol_table: HashMap> = HashMap::new(); + let mut s = String::new(); + let mut appending_input = false; + loop { + if appending_input { + s.push('\n'); + print!("| "); + } else { + s.clear(); + print!("> "); + } + let _ = stdout().flush(); + let read_line_result = stdin().read_line(&mut s); + if read_line_result.is_err() { + println!("{}\n", read_line_result.unwrap_err()); + continue; + } + s = s.trim().to_owned(); + if s.starts_with("#exit") { + return Ok(()); + } else if s.starts_with("#symbols") { + for (symbol, expr) in &symbol_table { + println!("{}:\n{}\n", symbol, *expr); + } + continue; + } else if !s.ends_with(";") && s != "" && !s.starts_with("#") { + appending_input = true; + continue; + } else { + appending_input = false; + } + + let parse_tree_result = lparser::LParser::parse(lparser::Rule::program, &s); + if parse_tree_result.is_err() { + println!("{}\n", parse_tree_result.unwrap_err()); + continue; + } + let mut parse_tree = parse_tree_result.unwrap(); + let syntax_tree: Program = ast::from_parse_tree(&mut parse_tree); + let process_result = runtime::process(&syntax_tree, Some(&mut symbol_table)); + if process_result.is_err() { + println!("{}\n", process_result.unwrap_err()); + continue; + } + } + } + _ => return Err("invalid subcommand".to_owned()), } } diff --git a/src/runtime.rs b/src/runtime.rs index 6eff337..07d8a70 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -184,6 +184,8 @@ pub fn reduce_term( } Term::Builtin(_) => Ok((Rc::clone(&term_rc), 0)), Term::Lazy(symbol) => { + // TODO: alpha conversion + // Lift all substituted vs by current v_inc, and then update v_inc to match if resolve_lazy { let table_lookup_value = symbol_table .get(symbol) @@ -353,10 +355,11 @@ pub type ProcessResult = Result<(Vec>, HashMap>), Stri pub fn process( program: &ast::Program, - initial_symbol_table: Option>>, + initial_symbol_table: Option<&mut HashMap>>, ) -> ProcessResult { - let mut symbol_table: HashMap> = - initial_symbol_table.unwrap_or(HashMap::new()); + let empty_symbol_table = &mut HashMap::new(); + let symbol_table: &mut HashMap> = + initial_symbol_table.unwrap_or(empty_symbol_table); let mut output_terms: Vec> = vec![]; @@ -390,5 +393,5 @@ pub fn process( } } - Ok((output_terms, symbol_table)) + Ok((output_terms, symbol_table.clone())) } -- cgit v1.3