diff --git a/schala-lang/language/src/schala.rs b/schala-lang/language/src/schala.rs index 7d4dc4d..0f94e3a 100644 --- a/schala-lang/language/src/schala.rs +++ b/schala-lang/language/src/schala.rs @@ -174,11 +174,11 @@ fn stage_names() -> Vec<&'static str> { impl ProgrammingLanguageInterface for Schala { - fn language_name(&self) -> String { + fn language_name() -> String { "Schala".to_owned() } - fn source_file_suffix(&self) -> String { + fn source_file_suffix() -> String { "schala".to_owned() } diff --git a/schala-repl/src/language.rs b/schala-repl/src/language.rs index 1528ad5..6cc4840 100644 --- a/schala-repl/src/language.rs +++ b/schala-repl/src/language.rs @@ -2,8 +2,8 @@ use std::collections::HashSet; use std::time; pub trait ProgrammingLanguageInterface { - fn language_name(&self) -> String; - fn source_file_suffix(&self) -> String; + fn language_name() -> String; + fn source_file_suffix() -> String; fn run_computation(&mut self, _request: ComputationRequest) -> ComputationResponse; diff --git a/schala-repl/src/lib.rs b/schala-repl/src/lib.rs index c5674f3..435b54e 100644 --- a/schala-repl/src/lib.rs +++ b/schala-repl/src/lib.rs @@ -7,12 +7,6 @@ extern crate includedir; extern crate phf; extern crate serde_json; -use std::collections::HashSet; -use std::fs::File; -use std::io::Read; -use std::path::PathBuf; -use std::process::exit; - mod language; mod repl; @@ -21,49 +15,7 @@ pub use language::{ LangMetaRequest, LangMetaResponse, ProgrammingLanguageInterface, }; +pub use repl::Repl; + include!(concat!(env!("OUT_DIR"), "/static.rs")); const VERSION_STRING: &'static str = "0.1.0"; - -pub fn start_repl(langs: Vec>) { - let mut repl = repl::Repl::new(langs); - repl.run_repl(); -} - -pub fn run_noninteractive(filenames: Vec, languages: Vec>) { - // for now, ony do something with the first filename - - let filename = &filenames[0]; - let ext = filename - .extension() - .and_then(|e| e.to_str()) - .unwrap_or_else(|| { - println!("Source file `{}` has no extension.", filename.display()); - exit(1); - }); - - let mut language = Box::new( - languages - .into_iter() - .find(|lang| lang.source_file_suffix() == ext) - .unwrap_or_else(|| { - println!("Extension .{} not recognized", ext); - exit(1); - }), - ); - - let mut source_file = File::open(filename).unwrap(); - let mut buffer = String::new(); - source_file.read_to_string(&mut buffer).unwrap(); - - let request = ComputationRequest { - source: &buffer, - debug_requests: HashSet::new(), - }; - - let response = language.run_computation(request); - match response.main_output { - Ok(s) => println!("{}", s), - Err(s) => println!("{}", s), - }; -} - diff --git a/schala-repl/src/repl/command_tree.rs b/schala-repl/src/repl/command_tree.rs index 09e2120..bf853a9 100644 --- a/schala-repl/src/repl/command_tree.rs +++ b/schala-repl/src/repl/command_tree.rs @@ -1,4 +1,5 @@ use super::{InterpreterDirectiveOutput, Repl}; +use crate::language::ProgrammingLanguageInterface; use crate::repl::directive_actions::DirectiveAction; use colored::*; @@ -85,7 +86,11 @@ impl CommandTree { self.get_children().iter().map(|x| x.get_cmd()).collect() } - pub fn perform(&self, repl: &mut Repl, arguments: &Vec<&str>) -> InterpreterDirectiveOutput { + pub fn perform( + &self, + repl: &mut Repl, + arguments: &Vec<&str>, + ) -> InterpreterDirectiveOutput { let mut dir_pointer: &CommandTree = self; let mut idx = 0; diff --git a/schala-repl/src/repl/directive_actions.rs b/schala-repl/src/repl/directive_actions.rs index e53a591..fc36b53 100644 --- a/schala-repl/src/repl/directive_actions.rs +++ b/schala-repl/src/repl/directive_actions.rs @@ -1,5 +1,7 @@ use super::{InterpreterDirectiveOutput, Repl}; -use crate::language::{DebugAsk, DebugResponse, LangMetaRequest, LangMetaResponse}; +use crate::language::{ + DebugAsk, DebugResponse, LangMetaRequest, LangMetaResponse, ProgrammingLanguageInterface, +}; use crate::repl::help::help; use std::fmt::Write as FmtWrite; @@ -20,7 +22,11 @@ pub enum DirectiveAction { } impl DirectiveAction { - pub fn perform(&self, repl: &mut Repl, arguments: &[&str]) -> InterpreterDirectiveOutput { + pub fn perform( + &self, + repl: &mut Repl, + arguments: &[&str], + ) -> InterpreterDirectiveOutput { use DirectiveAction::*; match self { Null => None, @@ -30,8 +36,10 @@ impl DirectiveAction { ::std::process::exit(0) } ListPasses => { - let language_state = repl.get_cur_language_state(); - let pass_names = match language_state.request_meta(LangMetaRequest::StageNames) { + let pass_names = match repl + .language_state + .request_meta(LangMetaRequest::StageNames) + { LangMetaResponse::StageNames(names) => names, _ => vec![], }; @@ -46,7 +54,6 @@ impl DirectiveAction { Some(buf) } ShowImmediate => { - let cur_state = repl.get_cur_language_state(); let stage_name = match arguments.get(0) { Some(s) => s.to_string(), None => return Some(format!("Must specify a thing to debug")), @@ -55,7 +62,7 @@ impl DirectiveAction { stage_name: stage_name.clone(), token: None, }); - let meta_response = cur_state.request_meta(meta); + let meta_response = repl.language_state.request_meta(meta); let response = match meta_response { LangMetaResponse::ImmediateDebug(DebugResponse { ask, value }) => match ask { @@ -100,43 +107,37 @@ impl DirectiveAction { }); None } - TotalTimeOff => total_time_off(repl, arguments), - TotalTimeOn => total_time_on(repl, arguments), - StageTimeOff => stage_time_off(repl, arguments), - StageTimeOn => stage_time_on(repl, arguments), + TotalTimeOff => { + repl.options.show_total_time = false; + None + } + TotalTimeOn => { + repl.options.show_total_time = true; + None + } + StageTimeOff => { + repl.options.show_stage_times = false; + None + } + StageTimeOn => { + repl.options.show_stage_times = true; + None + } Doc => doc(repl, arguments), } } } -fn total_time_on(repl: &mut Repl, _: &[&str]) -> InterpreterDirectiveOutput { - repl.options.show_total_time = true; - None -} - -fn total_time_off(repl: &mut Repl, _: &[&str]) -> InterpreterDirectiveOutput { - repl.options.show_total_time = false; - None -} - -fn stage_time_on(repl: &mut Repl, _: &[&str]) -> InterpreterDirectiveOutput { - repl.options.show_stage_times = true; - None -} - -fn stage_time_off(repl: &mut Repl, _: &[&str]) -> InterpreterDirectiveOutput { - repl.options.show_stage_times = false; - None -} - -fn doc(repl: &mut Repl, arguments: &[&str]) -> InterpreterDirectiveOutput { +fn doc( + repl: &mut Repl, + arguments: &[&str], +) -> InterpreterDirectiveOutput { arguments .get(0) .map(|cmd| { let source = cmd.to_string(); let meta = LangMetaRequest::Docs { source }; - let cur_state = repl.get_cur_language_state(); - match cur_state.request_meta(meta) { + match repl.language_state.request_meta(meta) { LangMetaResponse::Docs { doc_string } => Some(doc_string), _ => Some(format!("Invalid doc response")), } diff --git a/schala-repl/src/repl/directives.rs b/schala-repl/src/repl/directives.rs index 59e78a1..0e6bb0d 100644 --- a/schala-repl/src/repl/directives.rs +++ b/schala-repl/src/repl/directives.rs @@ -28,6 +28,7 @@ fn get_list(passes_directives: &Vec, include_help: bool) -> Vec, include_help: bool) -> Vec InterpreterDirectiveOutput { +pub fn help( + repl: &mut Repl, + arguments: &[&str], +) -> InterpreterDirectiveOutput { match arguments { [] => return global_help(repl), commands => { @@ -46,7 +50,7 @@ fn get_directive_from_commands<'a>( matched_directive } -fn global_help(repl: &mut Repl) -> InterpreterDirectiveOutput { +fn global_help(repl: &mut Repl) -> InterpreterDirectiveOutput { let mut buf = String::new(); writeln!( @@ -69,12 +73,11 @@ fn global_help(repl: &mut Repl) -> InterpreterDirectiveOutput { .unwrap(); } - let ref lang = repl.get_cur_language_state(); writeln!(buf, "").unwrap(); writeln!( buf, "Language-specific help for {}", - lang.language_name() + ::language_name() ) .unwrap(); writeln!(buf, "-----------------------").unwrap(); diff --git a/schala-repl/src/repl/mod.rs b/schala-repl/src/repl/mod.rs index d578548..2fa9d85 100644 --- a/schala-repl/src/repl/mod.rs +++ b/schala-repl/src/repl/mod.rs @@ -1,6 +1,6 @@ +use colored::*; use std::collections::HashSet; use std::sync::Arc; -use colored::*; use crate::language::{ ComputationRequest, LangMetaRequest, LangMetaResponse, ProgrammingLanguageInterface, @@ -22,13 +22,13 @@ const OPTIONS_SAVE_FILE: &'static str = ".schala_repl"; type InterpreterDirectiveOutput = Option; -pub struct Repl { +pub struct Repl { /// If this is the first character typed by a user into the repl, the following - /// will be interpreted as a directive to the REPL rather than a command in the + /// will be interpreted as a directive to the REPL rather than a command in the /// running programming language. sigil: char, line_reader: ::linefeed::interface::Interface<::linefeed::terminal::DefaultTerminal>, - language_states: Vec>, + language_state: L, options: ReplOptions, } @@ -38,8 +38,8 @@ enum PromptStyle { Multiline, } -impl Repl { - pub fn new(initial_states: Vec>) -> Repl { +impl Repl { + pub fn new(initial_state: L) -> Self { use linefeed::Interface; let line_reader = Interface::new("schala-repl").unwrap(); let sigil = ':'; @@ -47,7 +47,7 @@ impl Repl { Repl { sigil, line_reader, - language_states: initial_states, + language_state: initial_state, options: ReplOptions::new(), } } @@ -55,7 +55,8 @@ impl Repl { pub fn run_repl(&mut self) { println!("Schala meta-interpeter version {}", crate::VERSION_STRING); println!( - "Type {} for help with the REPL", format!("{}help", self.sigil).bright_green().bold() + "Type {} for help with the REPL", + format!("{}help", self.sigil).bright_green().bold() ); self.load_options(); self.handle_repl_loop(); @@ -68,10 +69,10 @@ impl Repl { .load_history(HISTORY_SAVE_FILE) .unwrap_or(()); match ReplOptions::load_from_file(OPTIONS_SAVE_FILE) { - Ok(options) => { - self.options = options; - } - Err(e) => eprintln!("{}",e) + Ok(options) => { + self.options = options; + } + Err(e) => eprintln!("{}", e), } } @@ -132,8 +133,7 @@ impl Repl { } fn update_line_reader(&mut self) { - let tab_complete_handler = - TabCompleteHandler::new(self.sigil, self.get_directives()); + let tab_complete_handler = TabCompleteHandler::new(self.sigil, self.get_directives()); self.line_reader .set_completer(Arc::new(tab_complete_handler)); //TODO fix this here self.set_prompt(PromptStyle::Normal); @@ -166,11 +166,6 @@ impl Repl { directives.perform(self, &arguments) } - fn get_cur_language_state(&mut self) -> &mut Box { - //TODO this is obviously not complete - &mut self.language_states[0] - } - fn handle_input(&mut self, input: &str) -> Vec { let mut debug_requests = HashSet::new(); for ask in self.options.debug_asks.iter() { @@ -181,14 +176,15 @@ impl Repl { source: input, debug_requests, }; - let ref mut language_state = self.get_cur_language_state(); - let response = language_state.run_computation(request); + let response = self.language_state.run_computation(request); response::handle_computation_response(response, &self.options) } fn get_directives(&mut self) -> CommandTree { - let language_state = self.get_cur_language_state(); - let pass_names = match language_state.request_meta(LangMetaRequest::StageNames) { + let pass_names = match self + .language_state + .request_meta(LangMetaRequest::StageNames) + { LangMetaResponse::StageNames(names) => names, _ => vec![], }; diff --git a/src/main.rs b/src/main.rs index a87aa8c..fe151e2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,9 @@ -use schala_repl::{run_noninteractive, start_repl, ProgrammingLanguageInterface}; +use schala_repl::{Repl, ProgrammingLanguageInterface, ComputationRequest}; -use std::path::PathBuf; -use std::process::exit; +use std::{fs::File, io::Read, path::PathBuf, process::exit, collections::HashSet}; +use schala_lang::Schala; +//TODO specify multiple langs, and have a way to switch between them fn main() { let args: Vec = std::env::args().collect(); let matches = command_line_options() @@ -17,17 +18,50 @@ fn main() { exit(0); } - let langs: Vec> = - vec![Box::new(schala_lang::Schala::new())]; - if matches.free.is_empty() { - start_repl(langs); + let state = Schala::new(); + let mut repl = Repl::new(state); + repl.run_repl(); } else { - let paths = matches.free.iter().map(PathBuf::from).collect(); - run_noninteractive(paths, langs); + let paths: Vec = matches.free.iter().map(PathBuf::from).collect(); + //TODO handle more than one file + let filename = &paths[0]; + let extension = filename.extension().and_then(|e| e.to_str()) + .unwrap_or_else(|| { + eprintln!("Source file `{}` has no extension.", filename.display()); + exit(1); + }); + + //TODO this proably should be a macro for every supported language + if extension == Schala::source_file_suffix() { + run_noninteractive(paths, Schala::new()); + } else { + eprintln!("Extension .{} not recognized", extension); + exit(1); + } } } +pub fn run_noninteractive(filenames: Vec, mut language: L) { + // for now, ony do something with the first filename + + let filename = &filenames[0]; + let mut source_file = File::open(filename).unwrap(); + let mut buffer = String::new(); + source_file.read_to_string(&mut buffer).unwrap(); + + let request = ComputationRequest { + source: &buffer, + debug_requests: HashSet::new(), + }; + + let response = language.run_computation(request); + match response.main_output { + Ok(s) => println!("{}", s), + Err(s) => eprintln!("{}", s), + }; +} + fn command_line_options() -> getopts::Options { let mut options = getopts::Options::new(); options.optflag("h", "help", "Show help text");