Parameterize Repl over language type
This commit is contained in:
parent
0f7e568341
commit
3cbe80e933
@ -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()
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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<Box<dyn ProgrammingLanguageInterface>>) {
|
||||
let mut repl = repl::Repl::new(langs);
|
||||
repl.run_repl();
|
||||
}
|
||||
|
||||
pub fn run_noninteractive(filenames: Vec<PathBuf>, languages: Vec<Box<dyn ProgrammingLanguageInterface>>) {
|
||||
// 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),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -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<L: ProgrammingLanguageInterface>(
|
||||
&self,
|
||||
repl: &mut Repl<L>,
|
||||
arguments: &Vec<&str>,
|
||||
) -> InterpreterDirectiveOutput {
|
||||
let mut dir_pointer: &CommandTree = self;
|
||||
let mut idx = 0;
|
||||
|
||||
|
@ -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<L: ProgrammingLanguageInterface>(
|
||||
&self,
|
||||
repl: &mut Repl<L>,
|
||||
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<L: ProgrammingLanguageInterface>(
|
||||
repl: &mut Repl<L>,
|
||||
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")),
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ fn get_list(passes_directives: &Vec<CommandTree>, include_help: bool) -> Vec<Com
|
||||
|
||||
vec![
|
||||
CommandTree::terminal("exit", Some("exit the REPL"), vec![], QuitProgram),
|
||||
//TODO there should be an alias for this
|
||||
CommandTree::terminal("quit", Some("exit the REPL"), vec![], QuitProgram),
|
||||
CommandTree::terminal(
|
||||
"help",
|
||||
@ -85,15 +86,6 @@ fn get_list(passes_directives: &Vec<CommandTree>, include_help: bool) -> Vec<Com
|
||||
),
|
||||
],
|
||||
),
|
||||
CommandTree::nonterm(
|
||||
"lang",
|
||||
Some("switch between languages, or go directly to a langauge by name"),
|
||||
vec![
|
||||
CommandTree::nonterm_no_further_tab_completions("next", None),
|
||||
CommandTree::nonterm_no_further_tab_completions("prev", None),
|
||||
CommandTree::nonterm("go", None, vec![]),
|
||||
],
|
||||
),
|
||||
CommandTree::terminal(
|
||||
"doc",
|
||||
Some("Get language-specific help for an item"),
|
||||
|
@ -2,9 +2,13 @@ use std::fmt::Write as FmtWrite;
|
||||
|
||||
use super::command_tree::CommandTree;
|
||||
use super::{InterpreterDirectiveOutput, Repl};
|
||||
use crate::language::ProgrammingLanguageInterface;
|
||||
use colored::*;
|
||||
|
||||
pub fn help(repl: &mut Repl, arguments: &[&str]) -> InterpreterDirectiveOutput {
|
||||
pub fn help<L: ProgrammingLanguageInterface>(
|
||||
repl: &mut Repl<L>,
|
||||
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<L: ProgrammingLanguageInterface>(repl: &mut Repl<L>) -> 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()
|
||||
<L as ProgrammingLanguageInterface>::language_name()
|
||||
)
|
||||
.unwrap();
|
||||
writeln!(buf, "-----------------------").unwrap();
|
||||
|
@ -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<String>;
|
||||
|
||||
pub struct Repl {
|
||||
pub struct Repl<L: ProgrammingLanguageInterface> {
|
||||
/// 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<Box<dyn ProgrammingLanguageInterface>>,
|
||||
language_state: L,
|
||||
options: ReplOptions,
|
||||
}
|
||||
|
||||
@ -38,8 +38,8 @@ enum PromptStyle {
|
||||
Multiline,
|
||||
}
|
||||
|
||||
impl Repl {
|
||||
pub fn new(initial_states: Vec<Box<dyn ProgrammingLanguageInterface>>) -> Repl {
|
||||
impl<L: ProgrammingLanguageInterface> Repl<L> {
|
||||
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<dyn ProgrammingLanguageInterface> {
|
||||
//TODO this is obviously not complete
|
||||
&mut self.language_states[0]
|
||||
}
|
||||
|
||||
fn handle_input(&mut self, input: &str) -> Vec<ReplResponse> {
|
||||
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![],
|
||||
};
|
||||
|
52
src/main.rs
52
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<String> = std::env::args().collect();
|
||||
let matches = command_line_options()
|
||||
@ -17,17 +18,50 @@ fn main() {
|
||||
exit(0);
|
||||
}
|
||||
|
||||
let langs: Vec<Box<dyn ProgrammingLanguageInterface>> =
|
||||
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<PathBuf> = 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<L: ProgrammingLanguageInterface>(filenames: Vec<PathBuf>, 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");
|
||||
|
Loading…
Reference in New Issue
Block a user