From aade7981530ae7ce550e50edd734e1759fd52694 Mon Sep 17 00:00:00 2001 From: Justus K Date: Fri, 24 Jul 2020 23:23:24 +0200 Subject: [PATCH 1/7] Start to implement REPL - Move binaries into `bin` folder - History support - Command hinting - Matching bracket highlighting --- Cargo.toml | 14 +++++- src/bin/repl/helper.rs | 88 ++++++++++++++++++++++++++++++++++++ src/bin/repl/mod.rs | 82 +++++++++++++++++++++++++++++++++ src/{main.rs => bin/swcc.rs} | 0 src/bin/swcci.rs | 12 +++++ 5 files changed, 194 insertions(+), 2 deletions(-) create mode 100644 src/bin/repl/helper.rs create mode 100644 src/bin/repl/mod.rs rename src/{main.rs => bin/swcc.rs} (100%) create mode 100644 src/bin/swcci.rs diff --git a/Cargo.toml b/Cargo.toml index 04300c67..dcbe3dd3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,6 +40,9 @@ atty = { version = "0.2", default-features = false, optional = true } git-testament = { version = "0.1", optional = true } rand = { version = "0.7", optional = true } rodio = { version = "0.11.0", optional = true } +rustyline = { version = "6.2.0", optional = true, default-features = false } +rustyline-derive = { version = "0.3.1", optional = true } +dirs-next = { version = "1.0.1", optional = true } [dev-dependencies] env_logger = { version = "0.7", default-features = false } @@ -50,20 +53,27 @@ proptest = "^0.9.6" proptest-derive = "0.1" [features] -default = ["cc", "codegen", "color-backtrace"] +default = ["cc", "codegen", "color-backtrace", "repl"] # The `swcc` binary cc = ["ansi_term", "git-testament", "tempfile", "pico-args", "codegen", "atty"] codegen = ["cranelift", "cranelift-module", "cranelift-object"] jit = ["codegen", "cranelift-simplejit"] +# The `swcci` binary +repl = ["jit", "rustyline", "rustyline-derive", "ansi_term", "dirs-next"] salty = ["rand", "rodio"] # for internal use _test_headers = [] [[bin]] name = "swcc" -path = "src/main.rs" +path = "src/bin/swcc.rs" required-features = ["cc"] +[[bin]] +name = "swcci" +path = "src/bin/swcci.rs" +required-features = ["repl"] + [[bench]] name = "examples" harness = false diff --git a/src/bin/repl/helper.rs b/src/bin/repl/helper.rs new file mode 100644 index 00000000..a87757f4 --- /dev/null +++ b/src/bin/repl/helper.rs @@ -0,0 +1,88 @@ +use ansi_term::Style; +use rustyline::{ + completion::Completer, + highlight::{Highlighter, MatchingBracketHighlighter}, + hint::Hinter, + validate::{ValidationContext, ValidationResult, Validator}, +}; +use rustyline_derive::Helper; +use std::borrow::Cow; + +#[derive(Helper)] +pub struct ReplHelper { + highlighter: MatchingBracketHighlighter, + commands: Vec, +} + +impl ReplHelper { + pub fn new(commands: Vec) -> Self { + Self { + commands, + highlighter: Default::default(), + } + } +} + +impl Highlighter for ReplHelper { + fn highlight<'l>(&self, line: &'l str, pos: usize) -> Cow<'l, str> { + // TODO: Syntax highlighting. + self.highlighter.highlight(line, pos) + } + + fn highlight_hint<'h>(&self, hint: &'h str) -> Cow<'h, str> { + let hint = Style::new().dimmed().paint(hint); + Cow::Owned(hint.to_string()) + } + + fn highlight_char(&self, line: &str, pos: usize) -> bool { + self.highlighter.highlight_char(line, pos) + } +} + +impl Validator for ReplHelper { + fn validate(&self, ctx: &mut ValidationContext) -> rustyline::Result { + let input = ctx.input(); + let mut stack = vec![]; + + for c in input.chars() { + match c { + '(' | '[' | '{' => stack.push(c), + ')' | ']' | '}' => match (stack.pop(), c) { + (Some('('), ')') | (Some('['), ']') | (Some('{'), '}') => {} + (_, _) => { + return Ok(ValidationResult::Invalid(Some( + "unclosed delimiter".to_string(), + ))); + } + }, + _ => continue, + } + } + + if stack.is_empty() { + Ok(ValidationResult::Valid(None)) + } else { + Ok(ValidationResult::Incomplete) + } + } +} + +impl Hinter for ReplHelper { + fn hint(&self, line: &str, pos: usize, ctx: &rustyline::Context<'_>) -> Option { + let start = &line[..pos]; + if !start.starts_with(super::PREFIX) { + return None; + } + let start = &start[1..]; + self.commands + .iter() + .find(|cmd| cmd.starts_with(start)) + .map(|hint| String::from(&hint[start.len()..])) + } +} + +impl Completer for ReplHelper { + type Candidate = String; + + // TODO: Complete method names, types, etc. +} diff --git a/src/bin/repl/mod.rs b/src/bin/repl/mod.rs new file mode 100644 index 00000000..03832497 --- /dev/null +++ b/src/bin/repl/mod.rs @@ -0,0 +1,82 @@ +//! Repl implementation using [`rustyline`]. +//! +//! [`rustyline`]: https://docs.rs/rustyline + +use dirs_next::home_dir; +use helper::ReplHelper; +use rustyline::{error::ReadlineError, Cmd, CompletionType, Config, EditMode, Editor, KeyPress}; +use std::path::PathBuf; + +mod helper; + +/// The prefix for commands inside the repl. +const PREFIX: &str = ":"; +const VERSION: &str = env!("CARGO_PKG_VERSION"); +const PROMPT: &str = ">> "; + +#[derive(Debug)] +pub struct Repl { + editor: Editor, +} + +impl Repl { + pub fn new() -> Self { + let config = Config::builder() + .history_ignore_space(true) + .history_ignore_dups(false) + .completion_type(CompletionType::List) + .edit_mode(EditMode::Emacs) + .tab_stop(4) + .build(); + let mut editor = Editor::with_config(config); + + let helper = ReplHelper::new(vec!["help".to_string()]); + editor.set_helper(Some(helper)); + + editor.bind_sequence(KeyPress::Up, Cmd::LineUpOrPreviousHistory(1)); + editor.bind_sequence(KeyPress::Down, Cmd::LineDownOrNextHistory(1)); + + Self { editor } + } + + pub fn run(&mut self) -> rustyline::Result<()> { + self.load_history(); + + println!("Saltwater {}", VERSION); + println!(r#"Type "{}help" for more information."#, PREFIX); + let result = loop { + let line = self.editor.readline(PROMPT); + match line { + Ok(line) => self.process_line(line), + // Ctrl + c will abort the current line. + Err(ReadlineError::Interrupted) => continue, + // Ctrl + d will exit the repl. + Err(ReadlineError::Eof) => break Ok(()), + Err(err) => break Err(err), + } + }; + self.save_history(); + + result + } + + fn history_path() -> Option { + let mut history = home_dir()?; + history.push(".saltwater_history"); + Some(history) + } + + fn save_history(&self) -> Option<()> { + let path = Self::history_path()?; + self.editor.save_history(&path).ok() + } + + fn load_history(&mut self) -> Option<()> { + let path = Self::history_path()?; + self.editor.load_history(&path).ok() + } + + fn process_line(&mut self, line: String) { + self.editor.add_history_entry(line); + } +} diff --git a/src/main.rs b/src/bin/swcc.rs similarity index 100% rename from src/main.rs rename to src/bin/swcc.rs diff --git a/src/bin/swcci.rs b/src/bin/swcci.rs new file mode 100644 index 00000000..707a2713 --- /dev/null +++ b/src/bin/swcci.rs @@ -0,0 +1,12 @@ +mod repl; + +fn main() { + let mut repl = repl::Repl::new(); + match repl.run() { + Ok(_) => {} + Err(err) => { + println!("Unknown error in the REPL occurred: {}", err); + std::process::exit(1); + } + } +} From ec8ec36cd75c73b55bed9af68adddcb3caeb2d72 Mon Sep 17 00:00:00 2001 From: Justus K Date: Sat, 25 Jul 2020 00:20:47 +0200 Subject: [PATCH 2/7] Command completion works --- src/bin/repl/commands.rs | 26 ++++++++++++++++++++++++++ src/bin/repl/helper.rs | 35 ++++++++++++++++++++++++++++++----- src/bin/repl/mod.rs | 40 ++++++++++++++++++++++++++++------------ 3 files changed, 84 insertions(+), 17 deletions(-) create mode 100644 src/bin/repl/commands.rs diff --git a/src/bin/repl/commands.rs b/src/bin/repl/commands.rs new file mode 100644 index 00000000..690f3711 --- /dev/null +++ b/src/bin/repl/commands.rs @@ -0,0 +1,26 @@ +use super::Repl; +use std::collections::HashMap; + +pub fn default_commands() -> HashMap<&'static str, fn(&mut Repl, &str)> { + let mut map = HashMap::<&'static str, fn(&mut Repl, &str)>::new(); + map.insert("help", help_command); + map.insert("quit", quit_command); + map.insert("q", quit_command); + map +} + +fn help_command(_repl: &mut Repl, _args: &str) { + println!( + " +Available commands: + {p}help Shows this message + {p}quit|q Quits the repl +", + p = super::PREFIX + ); +} + +fn quit_command(repl: &mut Repl, _args: &str) { + repl.save_history(); + std::process::exit(0) +} diff --git a/src/bin/repl/helper.rs b/src/bin/repl/helper.rs index a87757f4..b28074a4 100644 --- a/src/bin/repl/helper.rs +++ b/src/bin/repl/helper.rs @@ -1,9 +1,12 @@ +use super::PREFIX; use ansi_term::Style; use rustyline::{ - completion::Completer, + completion::{extract_word, Completer}, highlight::{Highlighter, MatchingBracketHighlighter}, hint::Hinter, + line_buffer::LineBuffer, validate::{ValidationContext, ValidationResult, Validator}, + Context, }; use rustyline_derive::Helper; use std::borrow::Cow; @@ -11,11 +14,11 @@ use std::borrow::Cow; #[derive(Helper)] pub struct ReplHelper { highlighter: MatchingBracketHighlighter, - commands: Vec, + commands: Vec<&'static str>, } impl ReplHelper { - pub fn new(commands: Vec) -> Self { + pub fn new(commands: Vec<&'static str>) -> Self { Self { commands, highlighter: Default::default(), @@ -68,9 +71,9 @@ impl Validator for ReplHelper { } impl Hinter for ReplHelper { - fn hint(&self, line: &str, pos: usize, ctx: &rustyline::Context<'_>) -> Option { + fn hint(&self, line: &str, pos: usize, _ctx: &Context<'_>) -> Option { let start = &line[..pos]; - if !start.starts_with(super::PREFIX) { + if !start.starts_with(PREFIX) { return None; } let start = &start[1..]; @@ -84,5 +87,27 @@ impl Hinter for ReplHelper { impl Completer for ReplHelper { type Candidate = String; + fn complete( + &self, + line: &str, + pos: usize, + _ctx: &Context<'_>, + ) -> rustyline::Result<(usize, Vec)> { + let (idx, word) = extract_word(line, pos, None, &[]); + if !line.starts_with(PREFIX) { + return Ok((0, vec![])); + } + let word = word.trim_matches(PREFIX); + + let commands = self + .commands + .iter() + .filter(|cmd| cmd.starts_with(word)) + .map(|x| x.to_string()) + .collect::>(); + + Ok((idx + 1, commands)) + } + // TODO: Complete method names, types, etc. } diff --git a/src/bin/repl/mod.rs b/src/bin/repl/mod.rs index 03832497..6507a6a4 100644 --- a/src/bin/repl/mod.rs +++ b/src/bin/repl/mod.rs @@ -2,21 +2,23 @@ //! //! [`rustyline`]: https://docs.rs/rustyline +use commands::default_commands; use dirs_next::home_dir; use helper::ReplHelper; use rustyline::{error::ReadlineError, Cmd, CompletionType, Config, EditMode, Editor, KeyPress}; -use std::path::PathBuf; +use std::{collections::HashMap, path::PathBuf}; +mod commands; mod helper; /// The prefix for commands inside the repl. -const PREFIX: &str = ":"; +const PREFIX: char = ':'; const VERSION: &str = env!("CARGO_PKG_VERSION"); const PROMPT: &str = ">> "; -#[derive(Debug)] pub struct Repl { editor: Editor, + commands: HashMap<&'static str, fn(&mut Repl, &str)>, } impl Repl { @@ -26,17 +28,20 @@ impl Repl { .history_ignore_dups(false) .completion_type(CompletionType::List) .edit_mode(EditMode::Emacs) + .max_history_size(1000) .tab_stop(4) .build(); let mut editor = Editor::with_config(config); - let helper = ReplHelper::new(vec!["help".to_string()]); + let commands = default_commands(); + let helper = ReplHelper::new(commands.keys().map(|x| *x).collect()); editor.set_helper(Some(helper)); editor.bind_sequence(KeyPress::Up, Cmd::LineUpOrPreviousHistory(1)); editor.bind_sequence(KeyPress::Down, Cmd::LineDownOrNextHistory(1)); + editor.bind_sequence(KeyPress::Tab, Cmd::Complete); - Self { editor } + Self { editor, commands } } pub fn run(&mut self) -> rustyline::Result<()> { @@ -60,12 +65,6 @@ impl Repl { result } - fn history_path() -> Option { - let mut history = home_dir()?; - history.push(".saltwater_history"); - Some(history) - } - fn save_history(&self) -> Option<()> { let path = Self::history_path()?; self.editor.save_history(&path).ok() @@ -76,7 +75,24 @@ impl Repl { self.editor.load_history(&path).ok() } + fn history_path() -> Option { + let mut history = home_dir()?; + history.push(".saltwater_history"); + Some(history) + } + fn process_line(&mut self, line: String) { - self.editor.add_history_entry(line); + self.editor.add_history_entry(line.clone()); + + if line.trim().starts_with(PREFIX) { + let name = line.split(' ').next().unwrap(); + + match self.commands.get(&name[1..]) { + Some(action) => action(self, &line[name.len()..]), + None => println!("unknown command '{}'", name), + } + } else { + todo!() + } } } From 53ac521aa8c82a8729faf21bd8248479d4cf931d Mon Sep 17 00:00:00 2001 From: Justus K Date: Sat, 25 Jul 2020 11:20:16 +0200 Subject: [PATCH 3/7] Cleanup some code - repl history is now saved in data_dir --- src/bin/repl/commands.rs | 7 ++++--- src/bin/repl/helper.rs | 24 +++++++++++++++++++----- src/bin/repl/mod.rs | 11 ++++++----- src/bin/swcci.rs | 2 +- 4 files changed, 30 insertions(+), 14 deletions(-) diff --git a/src/bin/repl/commands.rs b/src/bin/repl/commands.rs index 690f3711..10d70a73 100644 --- a/src/bin/repl/commands.rs +++ b/src/bin/repl/commands.rs @@ -4,16 +4,17 @@ use std::collections::HashMap; pub fn default_commands() -> HashMap<&'static str, fn(&mut Repl, &str)> { let mut map = HashMap::<&'static str, fn(&mut Repl, &str)>::new(); map.insert("help", help_command); + map.insert("h", help_command); map.insert("quit", quit_command); map.insert("q", quit_command); map } fn help_command(_repl: &mut Repl, _args: &str) { - println!( - " + print!( + "\ Available commands: - {p}help Shows this message + {p}help|h Shows this message {p}quit|q Quits the repl ", p = super::PREFIX diff --git a/src/bin/repl/helper.rs b/src/bin/repl/helper.rs index b28074a4..bc396b91 100644 --- a/src/bin/repl/helper.rs +++ b/src/bin/repl/helper.rs @@ -1,10 +1,9 @@ use super::PREFIX; use ansi_term::Style; use rustyline::{ - completion::{extract_word, Completer}, + completion::{extract_word, Candidate, Completer}, highlight::{Highlighter, MatchingBracketHighlighter}, hint::Hinter, - line_buffer::LineBuffer, validate::{ValidationContext, ValidationResult, Validator}, Context, }; @@ -54,7 +53,7 @@ impl Validator for ReplHelper { (Some('('), ')') | (Some('['), ']') | (Some('{'), '}') => {} (_, _) => { return Ok(ValidationResult::Invalid(Some( - "unclosed delimiter".to_string(), + "extra closing delimiter".to_string(), ))); } }, @@ -84,8 +83,23 @@ impl Hinter for ReplHelper { } } +/// Wrapper around a `&'static str` to be used for completion candidates. +pub struct CompletionCandidate { + display: &'static str, +} + +impl Candidate for CompletionCandidate { + fn display(&self) -> &str { + self.display + } + + fn replacement(&self) -> &str { + self.display + } +} + impl Completer for ReplHelper { - type Candidate = String; + type Candidate = CompletionCandidate; fn complete( &self, @@ -103,7 +117,7 @@ impl Completer for ReplHelper { .commands .iter() .filter(|cmd| cmd.starts_with(word)) - .map(|x| x.to_string()) + .map(|x| CompletionCandidate { display: x }) .collect::>(); Ok((idx + 1, commands)) diff --git a/src/bin/repl/mod.rs b/src/bin/repl/mod.rs index 6507a6a4..39000063 100644 --- a/src/bin/repl/mod.rs +++ b/src/bin/repl/mod.rs @@ -3,7 +3,7 @@ //! [`rustyline`]: https://docs.rs/rustyline use commands::default_commands; -use dirs_next::home_dir; +use dirs_next::data_dir; use helper::ReplHelper; use rustyline::{error::ReadlineError, Cmd, CompletionType, Config, EditMode, Editor, KeyPress}; use std::{collections::HashMap, path::PathBuf}; @@ -34,7 +34,7 @@ impl Repl { let mut editor = Editor::with_config(config); let commands = default_commands(); - let helper = ReplHelper::new(commands.keys().map(|x| *x).collect()); + let helper = ReplHelper::new(commands.keys().copied().collect()); editor.set_helper(Some(helper)); editor.bind_sequence(KeyPress::Up, Cmd::LineUpOrPreviousHistory(1)); @@ -76,15 +76,16 @@ impl Repl { } fn history_path() -> Option { - let mut history = home_dir()?; - history.push(".saltwater_history"); + let mut history = data_dir()?; + history.push("saltwater_history"); Some(history) } fn process_line(&mut self, line: String) { self.editor.add_history_entry(line.clone()); - if line.trim().starts_with(PREFIX) { + let line = line.trim(); + if line.starts_with(PREFIX) { let name = line.split(' ').next().unwrap(); match self.commands.get(&name[1..]) { diff --git a/src/bin/swcci.rs b/src/bin/swcci.rs index 707a2713..be1741e6 100644 --- a/src/bin/swcci.rs +++ b/src/bin/swcci.rs @@ -5,7 +5,7 @@ fn main() { match repl.run() { Ok(_) => {} Err(err) => { - println!("Unknown error in the REPL occurred: {}", err); + println!("error: {}", err); std::process::exit(1); } } From bfe1441ec4d697bebf6a770b4d6448e102fd55d6 Mon Sep 17 00:00:00 2001 From: Justus K Date: Sat, 25 Jul 2020 11:25:16 +0200 Subject: [PATCH 4/7] Fix broken r2d2 scream path --- src/bin/swcc.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/swcc.rs b/src/bin/swcc.rs index d941b5d9..020fe0b9 100644 --- a/src/bin/swcc.rs +++ b/src/bin/swcc.rs @@ -554,7 +554,7 @@ mod backtrace { #[cfg(feature = "salty")] fn play_scream() -> Result<(), ()> { - const SCREAM: &[u8] = include_bytes!("data/R2D2-Scream.ogg"); + const SCREAM: &[u8] = include_bytes!("../data/R2D2-Scream.ogg"); let device = rodio::default_output_device().ok_or(())?; let source = rodio::Decoder::new(std::io::Cursor::new(SCREAM)).or(Err(()))?; rodio::play_raw(&device, rodio::source::Source::convert_samples(source)); From 33619bbbab50b591f3ac8a17a14db9b811b66f4e Mon Sep 17 00:00:00 2001 From: Justus K Date: Mon, 3 Aug 2020 14:40:08 +0200 Subject: [PATCH 5/7] Code execution in repl works now - Any expression will be wrapped in a function - This function will be transmute'd into a rust function and then be called. - NOTE: Code doesn't compile right now. --- src/bin/repl/mod.rs | 92 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 91 insertions(+), 1 deletion(-) diff --git a/src/bin/repl/mod.rs b/src/bin/repl/mod.rs index 39000063..9b6fadb7 100644 --- a/src/bin/repl/mod.rs +++ b/src/bin/repl/mod.rs @@ -5,7 +5,12 @@ use commands::default_commands; use dirs_next::data_dir; use helper::ReplHelper; +use hir::{Declaration, Expr}; use rustyline::{error::ReadlineError, Cmd, CompletionType, Config, EditMode, Editor, KeyPress}; +use saltwater::{ + data, hir, initialize_jit_module, ir, types, CompileError, Locatable, Parser, + PreProcessorBuilder, PureAnalyzer, SyntaxError, Type, JIT, +}; use std::{collections::HashMap, path::PathBuf}; mod commands; @@ -93,7 +98,92 @@ impl Repl { None => println!("unknown command '{}'", name), } } else { - todo!() + match self.execute_code(line) { + Ok(_) => {} + Err(err) => { + // TODO: Proper error reporting + println!("error: {}", err.data); + } + } } } + + fn execute_code(&mut self, code: &str) -> Result<(), CompileError> { + let module = initialize_jit_module(); + + let expr = analyze_expr(code)?; + let expr_ty = expr.ctype.clone(); + let decl = wrap_expr(expr); + let module = ir::compile(module, vec![decl], false).0?; + + let mut jit = JIT::from(module); + jit.finalize(); + let execute_fun = jit + .get_compiled_function("execute") + .expect("this is not good."); + + match expr_ty { + Type::Long(signed) => { + let result = unsafe { + let execute: unsafe extern "C" fn() -> u64 = std::mem::transmute(execute_fun); + execute() + }; + match signed { + true => println!("=> {}", result as i64), + false => println!("=> {}", result), + } + } + // TODO: Implement execution for more types + ty => println!("error: expression returns unsupported type: {:?}", ty), + }; + Ok(()) + } +} + +/// Takes an expression and wraps it into a `execute` function that looks like the following: +/// +/// ``` +/// execute() { +/// return ; +/// } +/// ``` +fn wrap_expr(expr: Expr) -> Locatable { + let fun = hir::Variable { + ctype: types::Type::Function(types::FunctionType { + return_type: Box::new(expr.ctype.clone()), + params: vec![], + varargs: false, + }), + storage_class: data::StorageClass::Extern, + qualifiers: Default::default(), + id: saltwater::InternedStr::get_or_intern("execute"), + }; + + let span = expr.location; + let return_stmt = span.with(hir::StmtType::Return(Some(expr))); + let init = hir::Initializer::FunctionBody(vec![return_stmt]); + let decl = hir::Declaration { + // FIXME: Currently doesn't work. Just make insert() pub? + symbol: fun.insert(), + init: Some(init), + }; + span.with(decl) +} + +fn analyze_expr(code: &str) -> Result> { + let code = format!("{}\n", code).into_boxed_str(); + let cpp = PreProcessorBuilder::new(code).build(); + let mut parser = Parser::new(cpp, false); + let expr = parser.expr()?; + + let mut analyzer = PureAnalyzer::new(); + let expr = analyzer.expr(expr); + + // FIXME: error_handler is private so this doesn't work right now. + // Please review and propose a solution. + // if let Some(err) = analyzer.error_handler.pop_front() { + // return Err(err); + // } + + Ok(expr) } From 72093edc84cb4e5674a54b5d4fbab3a1ea9f488a Mon Sep 17 00:00:00 2001 From: Justus K Date: Tue, 4 Aug 2020 13:05:03 +0200 Subject: [PATCH 6/7] Cleanup some code - Add errors method to ErrorHandler - Make `Variable::insert` pub --- src/analyze/mod.rs | 9 +++++++++ src/bin/repl/helper.rs | 2 +- src/bin/repl/mod.rs | 4 ++-- src/bin/swcc.rs | 3 ++- src/data/hir.rs | 3 ++- 5 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/analyze/mod.rs b/src/analyze/mod.rs index b872f2f7..ec7f94e7 100644 --- a/src/analyze/mod.rs +++ b/src/analyze/mod.rs @@ -139,6 +139,15 @@ impl PureAnalyzer { pub fn warnings(&mut self) -> VecDeque { std::mem::take(&mut self.error_handler.warnings) } + + /// Return all errors seen so far. + /// + /// These errors are consumed and will not be returned if you call + /// `errors()` again. + pub fn errors(&mut self) -> VecDeque { + self.error_handler.by_ref().collect() + } + // I type these a lot #[inline(always)] fn err(&mut self, e: SemanticError, l: Location) { diff --git a/src/bin/repl/helper.rs b/src/bin/repl/helper.rs index bc396b91..0f2f2129 100644 --- a/src/bin/repl/helper.rs +++ b/src/bin/repl/helper.rs @@ -75,7 +75,7 @@ impl Hinter for ReplHelper { if !start.starts_with(PREFIX) { return None; } - let start = &start[1..]; + let start = &start[PREFIX.len_utf8()..]; self.commands .iter() .find(|cmd| cmd.starts_with(start)) diff --git a/src/bin/repl/mod.rs b/src/bin/repl/mod.rs index 9b6fadb7..e93602be 100644 --- a/src/bin/repl/mod.rs +++ b/src/bin/repl/mod.rs @@ -114,6 +114,7 @@ impl Repl { let expr = analyze_expr(code)?; let expr_ty = expr.ctype.clone(); let decl = wrap_expr(expr); + // FIXME: `ir` module is currently private. let module = ir::compile(module, vec![decl], false).0?; let mut jit = JIT::from(module); @@ -156,14 +157,13 @@ fn wrap_expr(expr: Expr) -> Locatable { }), storage_class: data::StorageClass::Extern, qualifiers: Default::default(), - id: saltwater::InternedStr::get_or_intern("execute"), + id: "execute".into(), }; let span = expr.location; let return_stmt = span.with(hir::StmtType::Return(Some(expr))); let init = hir::Initializer::FunctionBody(vec![return_stmt]); let decl = hir::Declaration { - // FIXME: Currently doesn't work. Just make insert() pub? symbol: fun.insert(), init: Some(init), }; diff --git a/src/bin/swcc.rs b/src/bin/swcc.rs index 020fe0b9..a072ea9c 100644 --- a/src/bin/swcc.rs +++ b/src/bin/swcc.rs @@ -554,7 +554,8 @@ mod backtrace { #[cfg(feature = "salty")] fn play_scream() -> Result<(), ()> { - const SCREAM: &[u8] = include_bytes!("../data/R2D2-Scream.ogg"); + const SCREAM: &[u8] = + include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "data/R2D2-Scream.ogg")); let device = rodio::default_output_device().ok_or(())?; let source = rodio::Decoder::new(std::io::Cursor::new(SCREAM)).or(Err(()))?; rodio::play_raw(&device, rodio::source::Source::convert_samples(source)); diff --git a/src/data/hir.rs b/src/data/hir.rs index 5a15f946..909ad5ab 100644 --- a/src/data/hir.rs +++ b/src/data/hir.rs @@ -115,7 +115,8 @@ impl Symbol { } impl Variable { - pub(crate) fn insert(self) -> Symbol { + /// Inserts this `Variable` into the global symbol table. + pub fn insert(self) -> Symbol { SYMBOL_TABLE.with(|store| store.borrow_mut().insert(self)) } } From 3f950e8ac46e0998cd947be89657986125f1e926 Mon Sep 17 00:00:00 2001 From: Justus K Date: Tue, 4 Aug 2020 13:22:33 +0200 Subject: [PATCH 7/7] Add more number types to repl and make ir public --- src/bin/repl/mod.rs | 45 +++++++++++++++++++++++++++++++++------------ src/ir/mod.rs | 2 +- src/lib.rs | 2 +- 3 files changed, 35 insertions(+), 14 deletions(-) diff --git a/src/bin/repl/mod.rs b/src/bin/repl/mod.rs index e93602be..e03640c3 100644 --- a/src/bin/repl/mod.rs +++ b/src/bin/repl/mod.rs @@ -12,6 +12,7 @@ use saltwater::{ PreProcessorBuilder, PureAnalyzer, SyntaxError, Type, JIT, }; use std::{collections::HashMap, path::PathBuf}; +use types::ArrayType; mod commands; mod helper; @@ -21,6 +22,15 @@ const PREFIX: char = ':'; const VERSION: &str = env!("CARGO_PKG_VERSION"); const PROMPT: &str = ">> "; +macro_rules! execute { + ($fun:ident, $ty:path, $action:expr) => { + $action(unsafe { + let execute: unsafe extern "C" fn() -> $ty = std::mem::transmute($fun); + execute() + }); + }; +} + pub struct Repl { editor: Editor, commands: HashMap<&'static str, fn(&mut Repl, &str)>, @@ -114,26 +124,37 @@ impl Repl { let expr = analyze_expr(code)?; let expr_ty = expr.ctype.clone(); let decl = wrap_expr(expr); - // FIXME: `ir` module is currently private. let module = ir::compile(module, vec![decl], false).0?; let mut jit = JIT::from(module); jit.finalize(); - let execute_fun = jit + let fun = jit .get_compiled_function("execute") .expect("this is not good."); match expr_ty { - Type::Long(signed) => { - let result = unsafe { - let execute: unsafe extern "C" fn() -> u64 = std::mem::transmute(execute_fun); - execute() - }; - match signed { - true => println!("=> {}", result as i64), - false => println!("=> {}", result), - } - } + Type::Short(signed) => execute!(fun, i16, |x| match signed { + true => println!("=> {}", x), + false => println!("=> {}", x as u16), + }), + Type::Int(signed) => execute!(fun, i32, |x| match signed { + true => println!("=> {}", x), + false => println!("=> {}", x as u32), + }), + Type::Long(signed) => execute!(fun, i64, |x| match signed { + true => println!("=> {}", x), + false => println!("=> {}", x as u64), + }), + Type::Float => execute!(fun, f32, |f| println!("=> {}", f)), + Type::Double => execute!(fun, f64, |f| println!("=> {}", f)), + + Type::Char(_) => execute!(fun, char, |c| println!("=> {}", c)), + Type::Bool => execute!(fun, bool, |b| println!("=> {}", b)), + Type::Void => unsafe { + let execute: unsafe extern "C" fn() = std::mem::transmute(fun); + execute() + }, + // TODO: Implement execution for more types ty => println!("error: expression returns unsupported type: {:?}", ty), }; diff --git a/src/ir/mod.rs b/src/ir/mod.rs index 920fe856..678d1b84 100644 --- a/src/ir/mod.rs +++ b/src/ir/mod.rs @@ -104,7 +104,7 @@ struct Compiler { } /// Compile a program from a high level IR to a Cranelift Module -pub(crate) fn compile( +pub fn compile( module: Module, program: Vec>, debug: bool, diff --git a/src/lib.rs b/src/lib.rs index 8aab4781..75681d03 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -88,7 +88,7 @@ pub mod data; mod fold; pub mod intern; #[cfg(feature = "codegen")] -mod ir; +pub mod ir; mod lex; mod parse;