diff --git a/Cargo.toml b/Cargo.toml index bafadbc..be901df 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,7 @@ [workspace] members = [ + "libeir_frontend", "libeir_diagnostics", "libeir_intern", "libeir_syntax_erl", @@ -16,4 +17,6 @@ members = [ "util/libeir_util_number", "util/libeir_util_parse", "util/libeir_util_parse_listing", + "util/libeir_util_dot_graph", + "util/scoped_cell", ] diff --git a/internal_lib/src/eir_internal_utils.erl b/internal_lib/src/eir_internal_utils.erl deleted file mode 100644 index b1fe431..0000000 --- a/internal_lib/src/eir_internal_utils.erl +++ /dev/null @@ -1,2 +0,0 @@ --module(eir_internal_utils). - diff --git a/internal_lib/src/erlang.erl b/internal_lib/src/erlang.erl deleted file mode 100644 index a4df0e2..0000000 --- a/internal_lib/src/erlang.erl +++ /dev/null @@ -1,21 +0,0 @@ --module(erlang). - --export(['+'/2, '-'/2]). --export([is_float/1, is_integer/1, is_number/1]). --export([error/1]). - -'+'(A, B) -> - eir_intrinsics:add(A, B). -'-'(A, B) -> - eir_intrinsics:sub(A, B). - -error(A) -> - eir_intrinsics:throw_error(A). - -is_float(T) -> - eir_intrinsics:is_float(T). -is_integer(T) -> - eir_intrinsics:is_smallint(T) - or eir_intrinsics:is_bigint(T). -is_number(T) -> - erlang:is_float(T) or erlang:is_integer(T). diff --git a/libeir_frontend/Cargo.toml b/libeir_frontend/Cargo.toml new file mode 100644 index 0000000..740eabc --- /dev/null +++ b/libeir_frontend/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "libeir_frontend" +version = "0.1.0" +authors = ["Hans Elias B. Josephsen "] +edition = "2018" +license = "MIT OR Apache-2.0" + +[dependencies] +libeir_ir = { path = "../libeir_ir" } +libeir_syntax_erl = { path = "../libeir_syntax_erl" } +libeir_util_parse = { path = "../util/libeir_util_parse" } +libeir_diagnostics = { path = "../libeir_diagnostics" } +libeir_util_parse_listing = { path = "../util/libeir_util_parse_listing" } + +[features] +default = ["frontend_erlang", "frontend_abstr_erlang", "frontend_eir"] +frontend_erlang = [] +frontend_abstr_erlang = [] +frontend_eir = [] diff --git a/libeir_frontend/README.md b/libeir_frontend/README.md new file mode 100644 index 0000000..97de546 --- /dev/null +++ b/libeir_frontend/README.md @@ -0,0 +1,8 @@ +# libeir_frontend + +Wrapper crate for all the different frontends in this project. + +Currently includes: +* eir frontend +* erlang frontend +* abstract erlang frontend diff --git a/libeir_frontend/src/abstr_erlang.rs b/libeir_frontend/src/abstr_erlang.rs new file mode 100644 index 0000000..2c4f631 --- /dev/null +++ b/libeir_frontend/src/abstr_erlang.rs @@ -0,0 +1,92 @@ +use std::path::Path; +use std::borrow::Cow; + +use libeir_util_parse_listing::{parser::ParseError, ast::Root}; +use libeir_util_parse::{Parse, ArcCodemap, Source, FileMapSource, ToDiagnostic, error_tee}; +use libeir_ir::Module; +use libeir_diagnostics::{Diagnostic, FileName}; +use libeir_syntax_erl::{lower_module, LowerError, lower_abstr}; + +use super::{Frontend, FrontendErrorReceiver}; + +pub enum Error { + Parse(ParseError), + Lower(LowerError), +} +impl ToDiagnostic for Error { + fn to_diagnostic(&self) -> Diagnostic { + match self { + Error::Parse(err) => err.to_diagnostic(), + Error::Lower(err) => err.to_diagnostic(), + } + } +} +impl From for Error { + fn from(err: ParseError) -> Error { + Error::Parse(err) + } +} +impl From for Error { + fn from(err: LowerError) -> Error { + Error::Lower(err) + } +} + +pub struct AbstrErlangFrontend {} + +impl AbstrErlangFrontend { + pub fn new() -> Self { + AbstrErlangFrontend {} + } +} + +impl Frontend for AbstrErlangFrontend { + type Error = Error; + + fn parse_source<'a>( + &self, + errors: &'a mut FrontendErrorReceiver<'a, Self::Error>, + codemap: ArcCodemap, + source: FileMapSource, + ) -> Result { + error_tee(errors, |mut errors| { + let root: Root = Root::parse(&(), &codemap, &mut errors.make_into_adapter(), source)?; + let ast = lower_abstr(&root); + let eir = lower_module(&mut errors.make_into_adapter(), &codemap, &ast)?; + Ok(eir) + }) + } + + fn parse_string<'a>( + &self, + errors: &'a mut FrontendErrorReceiver<'a, Self::Error>, + codemap: ArcCodemap, + source: &str, + ) -> Result { + let filemap = { + codemap.write().unwrap().add_filemap( + FileName::Virtual(Cow::Borrowed("nofile")), + source.to_owned(), + ) + }; + self.parse_source(errors, codemap, FileMapSource::new(filemap)) + } + + fn parse_file<'a>( + &self, + errors: &'a mut FrontendErrorReceiver<'a, Self::Error>, + codemap: ArcCodemap, + source: &Path, + ) -> Result { + match FileMapSource::from_path(codemap.clone(), source) { + Err(err) => { + errors.error(>::file_map_error(err).into()); + Err(()) + }, + Ok(source) => self.parse_source(errors, codemap, source), + } + } + +} + +impl_dyn_frontend!(AbstrErlangFrontend); diff --git a/libeir_frontend/src/eir.rs b/libeir_frontend/src/eir.rs new file mode 100644 index 0000000..7552754 --- /dev/null +++ b/libeir_frontend/src/eir.rs @@ -0,0 +1,95 @@ +use std::path::Path; +use std::borrow::Cow; + +use libeir_diagnostics::{Diagnostic, FileName}; +use libeir_util_parse::{Parse, ArcCodemap, Source, FileMapSource, ToDiagnostic, error_tee}; +use libeir_ir::{ + Module, + text::{ + parser::ParserError, + ast::{LowerError, Module as ModuleAst}, + }, +}; + +use super::{Frontend, FrontendErrorReceiver}; + +pub enum Error { + Parser(ParserError), + Lower(LowerError), +} +impl ToDiagnostic for Error { + fn to_diagnostic(&self) -> Diagnostic { + match self { + Error::Parser(err) => err.to_diagnostic(), + Error::Lower(err) => err.to_diagnostic(), + } + } +} +impl From for Error { + fn from(err: ParserError) -> Self { + Error::Parser(err) + } +} +impl From for Error { + fn from(err: LowerError) -> Self { + Error::Lower(err) + } +} + +pub struct EirFrontend {} + +impl EirFrontend { + pub fn new() -> Self { + EirFrontend {} + } +} + +impl Frontend for EirFrontend { + type Error = Error; + + fn parse_source<'a>( + &self, + errors: &'a mut FrontendErrorReceiver<'a, Self::Error>, + codemap: ArcCodemap, + source: FileMapSource, + ) -> Result { + error_tee(errors, |mut errors| { + let ast = ModuleAst::parse(&(), &codemap, &mut errors.make_into_adapter(), source)?; + let eir = ast.lower(&mut errors.make_into_adapter())?; + Ok(eir) + }) + } + + fn parse_string<'a>( + &self, + errors: &'a mut FrontendErrorReceiver<'a, Self::Error>, + codemap: ArcCodemap, + source: &str, + ) -> Result { + let filemap = { + codemap.write().unwrap().add_filemap( + FileName::Virtual(Cow::Borrowed("nofile")), + source.to_owned(), + ) + }; + self.parse_source(errors, codemap, FileMapSource::new(filemap)) + } + + fn parse_file<'a>( + &self, + errors: &'a mut FrontendErrorReceiver<'a, Self::Error>, + codemap: ArcCodemap, + source: &Path, + ) -> Result { + match FileMapSource::from_path(codemap.clone(), source) { + Err(err) => { + errors.error(>::file_map_error(err).into()); + Err(()) + }, + Ok(source) => self.parse_source(errors, codemap, source), + } + } + +} + +impl_dyn_frontend!(EirFrontend); diff --git a/libeir_frontend/src/erlang.rs b/libeir_frontend/src/erlang.rs new file mode 100644 index 0000000..2c7432e --- /dev/null +++ b/libeir_frontend/src/erlang.rs @@ -0,0 +1,97 @@ +use std::path::Path; +use std::borrow::Cow; + +use libeir_syntax_erl::{ + lower_module, ParseConfig, ParserError, LowerError, + ast::Module as ModuleAst, +}; +use libeir_diagnostics::{Diagnostic, FileName}; +use libeir_util_parse::{Parse, ArcCodemap, Source, FileMapSource, ToDiagnostic, error_tee}; +use libeir_ir::Module; + +use super::{Frontend, FrontendErrorReceiver}; + +pub enum Error { + Parser(ParserError), + Lower(LowerError), +} +impl ToDiagnostic for Error { + fn to_diagnostic(&self) -> Diagnostic { + match self { + Error::Parser(err) => err.to_diagnostic(), + Error::Lower(err) => err.to_diagnostic(), + } + } +} +impl Into for ParserError { + fn into(self) -> Error { + Error::Parser(self) + } +} +impl Into for LowerError { + fn into(self) -> Error { + Error::Lower(self) + } +} + +pub struct ErlangFrontend { + config: ParseConfig, +} + +impl ErlangFrontend { + pub fn new(config: ParseConfig) -> Self { + ErlangFrontend { + config, + } + } +} + +impl Frontend for ErlangFrontend { + type Error = Error; + + fn parse_source<'a>( + &self, + errors: &'a mut FrontendErrorReceiver<'a, Self::Error>, + codemap: ArcCodemap, + source: FileMapSource, + ) -> Result { + error_tee(errors, |mut errors| { + let ast = ModuleAst::parse(&self.config, &codemap, &mut errors.make_into_adapter(), source)?; + let eir = lower_module(&mut errors.make_into_adapter(), &codemap, &ast)?; + Ok(eir) + }) + } + + fn parse_string<'a>( + &self, + errors: &'a mut FrontendErrorReceiver<'a, Self::Error>, + codemap: ArcCodemap, + source: &str, + ) -> Result { + let filemap = { + codemap.write().unwrap().add_filemap( + FileName::Virtual(Cow::Borrowed("nofile")), + source.to_owned(), + ) + }; + self.parse_source(errors, codemap, FileMapSource::new(filemap)) + } + + fn parse_file<'a>( + &self, + errors: &'a mut FrontendErrorReceiver<'a, Self::Error>, + codemap: ArcCodemap, + source: &Path, + ) -> Result { + match FileMapSource::from_path(codemap.clone(), source) { + Err(err) => { + errors.error(>::file_map_error(err).into()); + Err(()) + }, + Ok(source) => self.parse_source(errors, codemap, source), + } + } + +} + +impl_dyn_frontend!(ErlangFrontend); diff --git a/libeir_frontend/src/lib.rs b/libeir_frontend/src/lib.rs new file mode 100644 index 0000000..3bbd572 --- /dev/null +++ b/libeir_frontend/src/lib.rs @@ -0,0 +1,182 @@ +use std::path::Path; + +use libeir_ir::Module; +use libeir_util_parse::{ArcCodemap, FileMapSource, ErrorReceiver}; +use libeir_diagnostics::Diagnostic; + +macro_rules! impl_dyn_frontend { + ($struct:ident) => { + impl crate::DynFrontend for $struct { + + fn parse_source_dyn<'a>( + &self, + codemap: libeir_util_parse::ArcCodemap, + source: libeir_util_parse::FileMapSource, + ) -> (Result, Vec) + { + let mut errors = libeir_util_parse::Errors::new(); + let res = self.parse_source(&mut errors, codemap, source); + (res, errors.iter_diagnostics().collect()) + } + + fn parse_string_dyn<'a>( + &self, + codemap: libeir_util_parse::ArcCodemap, + source: &str, + ) -> (Result, Vec) + { + let mut errors = libeir_util_parse::Errors::new(); + let res = self.parse_string(&mut errors, codemap, source); + (res, errors.iter_diagnostics().collect()) + } + + fn parse_file_dyn<'a>( + &self, + codemap: libeir_util_parse::ArcCodemap, + source: &std::path::Path, + ) -> (Result, Vec) + { + let mut errors = libeir_util_parse::Errors::new(); + let res = self.parse_file(&mut errors, codemap, source); + (res, errors.iter_diagnostics().collect()) + } + + } + }; +} + +#[cfg(feature = "frontend_erlang")] +pub mod erlang; + +#[cfg(feature = "frontend_abstr_erlang")] +pub mod abstr_erlang; + +#[cfg(feature = "frontend_eir")] +pub mod eir; + +pub type FrontendErrorReceiver<'a, E> = dyn ErrorReceiver + 'a; + +pub trait DynFrontend { + + fn parse_source_dyn<'a>( + &self, + codemap: ArcCodemap, + source: FileMapSource, + ) -> (Result, Vec); + + fn parse_string_dyn<'a>( + &self, + codemap: ArcCodemap, + source: &str, + ) -> (Result, Vec); + + fn parse_file_dyn<'a>( + &self, + codemap: ArcCodemap, + source: &Path, + ) -> (Result, Vec); + +} + +pub trait Frontend: DynFrontend { + type Error; + + fn parse_source<'a>( + &self, + errors: &'a mut FrontendErrorReceiver<'a, Self::Error>, + codemap: ArcCodemap, + source: FileMapSource, + ) -> Result; + + fn parse_string<'a>( + &self, + errors: &'a mut FrontendErrorReceiver<'a, Self::Error>, + codemap: ArcCodemap, + source: &str, + ) -> Result; + + fn parse_file<'a>( + &self, + errors: &'a mut FrontendErrorReceiver<'a, Self::Error>, + codemap: ArcCodemap, + source: &Path, + ) -> Result; + +} + +pub enum AnyFrontend { + #[cfg(feature = "frontend_erlang")] + Erlang(erlang::ErlangFrontend), + #[cfg(feature = "frontend_abstr_erlang")] + AbstrErlang(abstr_erlang::AbstrErlangFrontend), + #[cfg(feature = "frontend_eir")] + Eir(eir::EirFrontend), +} + +impl DynFrontend for AnyFrontend { + + fn parse_source_dyn<'a>( + &self, + codemap: ArcCodemap, + source: FileMapSource, + ) -> (Result, Vec) + { + match self { + #[cfg(feature = "frontend_erlang")] + AnyFrontend::Erlang(front) => front.parse_source_dyn(codemap, source), + #[cfg(feature = "frontend_abstr_erlang")] + AnyFrontend::AbstrErlang(front) => front.parse_source_dyn(codemap, source), + #[cfg(feature = "frontend_eir")] + AnyFrontend::Eir(front) => front.parse_source_dyn(codemap, source), + } + } + + fn parse_string_dyn<'a>( + &self, + codemap: ArcCodemap, + source: &str, + ) -> (Result, Vec) + { + match self { + #[cfg(feature = "frontend_erlang")] + AnyFrontend::Erlang(front) => front.parse_string_dyn(codemap, source), + #[cfg(feature = "frontend_abstr_erlang")] + AnyFrontend::AbstrErlang(front) => front.parse_string_dyn(codemap, source), + #[cfg(feature = "frontend_eir")] + AnyFrontend::Eir(front) => front.parse_string_dyn(codemap, source), + } + } + + fn parse_file_dyn<'a>( + &self, + codemap: ArcCodemap, + source: &Path, + ) -> (Result, Vec) + { + match self { + #[cfg(feature = "frontend_erlang")] + AnyFrontend::Erlang(front) => front.parse_file_dyn(codemap, source), + #[cfg(feature = "frontend_abstr_erlang")] + AnyFrontend::AbstrErlang(front) => front.parse_file_dyn(codemap, source), + #[cfg(feature = "frontend_eir")] + AnyFrontend::Eir(front) => front.parse_file_dyn(codemap, source), + } + } + +} + +impl From for AnyFrontend { + fn from(f: erlang::ErlangFrontend) -> Self { + AnyFrontend::Erlang(f) + } +} +impl From for AnyFrontend { + fn from(f: abstr_erlang::AbstrErlangFrontend) -> Self { + AnyFrontend::AbstrErlang(f) + } +} +impl From for AnyFrontend { + fn from(f: eir::EirFrontend) -> Self { + AnyFrontend::Eir(f) + } +} diff --git a/libeir_interpreter/Cargo.toml b/libeir_interpreter/Cargo.toml index d78d6c1..93dca85 100644 --- a/libeir_interpreter/Cargo.toml +++ b/libeir_interpreter/Cargo.toml @@ -14,7 +14,6 @@ num = "0.2" num-traits = "0.2" tempdir = "0.3" lazy_static = "1.2" -pretty = "0.3.3" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" @@ -26,6 +25,8 @@ libeir_util_number = { path = "../util/libeir_util_number" } num-bigint = { git = "https://github.com/hansihe/num-bigint.git" } +pretty = "0.7" + #[dependencies.rug] #version = "1.2" #default-features = false diff --git a/libeir_interpreter/src/lib.rs b/libeir_interpreter/src/lib.rs index 1b44cdf..1eedef8 100644 --- a/libeir_interpreter/src/lib.rs +++ b/libeir_interpreter/src/lib.rs @@ -1,4 +1,4 @@ -#![deny(warnings)] +//#![deny(warnings)] //! LIR interpreter with zero consideration of performance. //! Made as an experiment to narrow down relevant implementation diff --git a/libeir_interpreter/src/term.rs b/libeir_interpreter/src/term.rs index f945d1c..34995ce 100644 --- a/libeir_interpreter/src/term.rs +++ b/libeir_interpreter/src/term.rs @@ -560,121 +560,122 @@ impl Term { } } - pub fn to_doc(term: Rc) -> ::pretty::Doc<'static, ::pretty::BoxDoc<'static>> { - use ::pretty::{ Doc }; - match &*term { - Term::Nil => Doc::text("[]"), - Term::Integer(int) => Doc::text(int.to_string()), - Term::Float(num) => Doc::text(num.0.to_string()), - Term::Atom(atom) => Doc::text(atom.to_string()), - Term::Tuple(items) => { - let docs: Vec<_> = items.iter().map(|i| Term::to_doc(i.clone())).collect(); - Doc::text("{") - .append(Doc::intersperse(docs, Doc::text(","))) - .append(Doc::text("}")) - }, - Term::ListCell(_, _) => { - let (head, tail) = Term::as_inproper_list(&term); - let head_docs: Vec<_> = head.iter().map(|i| Term::to_doc(i.clone())).collect(); - let tail_doc = Term::to_doc(tail); - Doc::text("[") - .append(Doc::intersperse(head_docs, Doc::text(","))) - .append(Doc::text("|")) - .append(tail_doc) - .append(Doc::text("]")) - }, - //Term::ListCell(head, tail) if head.len() == 0 => tail.to_doc(), - //Term::ListCell(head, tail) => { - // let head_docs: Vec<_> = head.iter().map(|i| i.to_doc()).collect(); - // let tail_doc = tail.to_doc(); - // Doc::text("[") - // .append(Doc::intersperse(head_docs, Doc::text(","))) - // .append(Doc::text("|")) - // .append(tail_doc) - // .append(Doc::text("]")) - //}, - Term::Map(map) => { - let entries_doc: Vec<_> = map.sorted.iter() - .map(|(k, v)| { - Doc::group( - Term::to_doc(k.clone()) - .append(Doc::text("=>")) - .append(Term::to_doc(v.clone())) - ) - }).collect(); - Doc::text("%{") - .append(Doc::intersperse(entries_doc, Doc::text(","))) - .append(Doc::text("}")) - }, - Term::Pid(pid) => Doc::text(format!("Pid<{}>", pid.0)), - Term::Reference(refe) => Doc::text(format!("Reference<{}>", refe.0)), - Term::Binary(bin) => { - if let Some(slice) = bin.try_as_byte_aligned_slice() { - if let Ok(utf) = std::str::from_utf8(slice) { - return Doc::text("\"") - .append(Doc::text(utf.to_string())) - .append(Doc::text("\"")); - } - } - - // TODO bit length - let items: Vec<_> = bin.iter_bytes() - .map(|v| Doc::text(v.to_string())) - .collect(); - Doc::text("<") - .append(Doc::intersperse(items, Doc::text(","))) - .append(Doc::text(">")) - }, - Term::BinarySlice { buf, bit_offset, bit_length } => { - if bit_length % 8 == 0 { - let from = BitSlice::with_offset_length( - &**buf, *bit_offset, *bit_length); - - let byte_len = bit_length / 8; - let mut bin = vec![0; byte_len]; - bit_copy(&from, &mut bin as &mut [u8]); - - if let Ok(utf) = std::str::from_utf8(&bin) { - return Doc::text("\"") - .append(Doc::text(utf.to_string())) - .append(Doc::text("\"")); - } else { - let items: Vec<_> = bin.iter() - .map(|v| Doc::text(v.to_string())) - .collect(); - Doc::text("<") - .append(Doc::intersperse(items, Doc::text(","))) - .append(Doc::text(">")) - } - } else { - let items: Vec<_> = buf.iter_words() - .map(|v| Doc::text(v.to_string())) - .collect(); - Doc::text("<") - .append(Doc::intersperse(items, Doc::text(","))) - .append(Doc::text(">")) - } - }, - Term::BoundLambda { ident, block, .. } => { - Doc::text(format!("Bound<{}:{}/{}@{}>", ident.module.name, ident.name.name, - ident.arity, block)) - }, - Term::CapturedFunction { ident } => { - Doc::text(format!("Captured<{}:{}/{}>", ident.module.name, ident.name.name, ident.arity)) - }, - Term::ValueList(items) => { - let docs: Vec<_> = items.iter().map(|i| Term::to_doc(i.clone())).collect(); - Doc::text("<") - .append(Doc::intersperse(docs, Doc::text(","))) - .append(Doc::text(">")) - } - Term::ReturnOk => { - Doc::text(format!("ReturnOk")) - } - Term::ReturnThrow => { - Doc::text(format!("ReturnThrow")) - } - } + pub fn to_doc(term: Rc) -> pretty::Doc<'static, pretty::BoxDoc<'static>> { + unimplemented!() + //use pretty::{ Doc }; + //match &*term { + // Term::Nil => Doc::text("[]"), + // Term::Integer(int) => Doc::text(int.to_string()), + // Term::Float(num) => Doc::text(num.0.to_string()), + // Term::Atom(atom) => Doc::text(atom.to_string()), + // Term::Tuple(items) => { + // let docs: Vec<_> = items.iter().map(|i| Term::to_doc(i.clone())).collect(); + // Doc::text("{") + // .append(Doc::intersperse(docs, Doc::text(","))) + // .append(Doc::text("}")) + // }, + // Term::ListCell(_, _) => { + // let (head, tail) = Term::as_inproper_list(&term); + // let head_docs: Vec<_> = head.iter().map(|i| Term::to_doc(i.clone())).collect(); + // let tail_doc = Term::to_doc(tail); + // Doc::text("[") + // .append(Doc::intersperse(head_docs, Doc::text(","))) + // .append(Doc::text("|")) + // .append(tail_doc) + // .append(Doc::text("]")) + // }, + // //Term::ListCell(head, tail) if head.len() == 0 => tail.to_doc(), + // //Term::ListCell(head, tail) => { + // // let head_docs: Vec<_> = head.iter().map(|i| i.to_doc()).collect(); + // // let tail_doc = tail.to_doc(); + // // Doc::text("[") + // // .append(Doc::intersperse(head_docs, Doc::text(","))) + // // .append(Doc::text("|")) + // // .append(tail_doc) + // // .append(Doc::text("]")) + // //}, + // Term::Map(map) => { + // let entries_doc: Vec<_> = map.sorted.iter() + // .map(|(k, v)| { + // Doc::group( + // Term::to_doc(k.clone()) + // .append(Doc::text("=>")) + // .append(Term::to_doc(v.clone())) + // ) + // }).collect(); + // Doc::text("%{") + // .append(Doc::intersperse(entries_doc, Doc::text(","))) + // .append(Doc::text("}")) + // }, + // Term::Pid(pid) => Doc::text(format!("Pid<{}>", pid.0)), + // Term::Reference(refe) => Doc::text(format!("Reference<{}>", refe.0)), + // Term::Binary(bin) => { + // if let Some(slice) = bin.try_as_byte_aligned_slice() { + // if let Ok(utf) = std::str::from_utf8(slice) { + // return Doc::text("\"") + // .append(Doc::text(utf.to_string())) + // .append(Doc::text("\"")); + // } + // } + + // // TODO bit length + // let items: Vec<_> = bin.iter_bytes() + // .map(|v| Doc::text(v.to_string())) + // .collect(); + // Doc::text("<") + // .append(Doc::intersperse(items, Doc::text(","))) + // .append(Doc::text(">")) + // }, + // Term::BinarySlice { buf, bit_offset, bit_length } => { + // if bit_length % 8 == 0 { + // let from = BitSlice::with_offset_length( + // &**buf, *bit_offset, *bit_length); + + // let byte_len = bit_length / 8; + // let mut bin = vec![0; byte_len]; + // bit_copy(&from, &mut bin as &mut [u8]); + + // if let Ok(utf) = std::str::from_utf8(&bin) { + // return Doc::text("\"") + // .append(Doc::text(utf.to_string())) + // .append(Doc::text("\"")); + // } else { + // let items: Vec<_> = bin.iter() + // .map(|v| Doc::text(v.to_string())) + // .collect(); + // Doc::text("<") + // .append(Doc::intersperse(items, Doc::text(","))) + // .append(Doc::text(">")) + // } + // } else { + // let items: Vec<_> = buf.iter_words() + // .map(|v| Doc::text(v.to_string())) + // .collect(); + // Doc::text("<") + // .append(Doc::intersperse(items, Doc::text(","))) + // .append(Doc::text(">")) + // } + // }, + // Term::BoundLambda { ident, block, .. } => { + // Doc::text(format!("Bound<{}:{}/{}@{}>", ident.module.name, ident.name.name, + // ident.arity, block)) + // }, + // Term::CapturedFunction { ident } => { + // Doc::text(format!("Captured<{}:{}/{}>", ident.module.name, ident.name.name, ident.arity)) + // }, + // Term::ValueList(items) => { + // let docs: Vec<_> = items.iter().map(|i| Term::to_doc(i.clone())).collect(); + // Doc::text("<") + // .append(Doc::intersperse(docs, Doc::text(","))) + // .append(Doc::text(">")) + // } + // Term::ReturnOk => { + // Doc::text(format!("ReturnOk")) + // } + // Term::ReturnThrow => { + // Doc::text(format!("ReturnThrow")) + // } + //} } } diff --git a/libeir_ir/Cargo.toml b/libeir_ir/Cargo.toml index cc846a9..e68e47b 100644 --- a/libeir_ir/Cargo.toml +++ b/libeir_ir/Cargo.toml @@ -23,7 +23,7 @@ lazy_static = "1.2.0" itertools = "0.8.0" cranelift-entity = "0.30.0" -bumpalo = "2.6.0" +bumpalo = { git = "https://github.com/hansihe/bumpalo", branch = "nightly_alloc", features = ["nightly", "collections"] } petgraph = "0.4" diff --git a/libeir_ir/src/algo/equality.rs b/libeir_ir/src/algo/equality.rs index 6f8f9fb..d677be7 100644 --- a/libeir_ir/src/algo/equality.rs +++ b/libeir_ir/src/algo/equality.rs @@ -168,7 +168,7 @@ mod tests { #[test] fn basic_equality() { let ir1 = parse_function_unwrap(" -foo:bar/1 { +a'foo':a'bar'/1 { entry(%ret, %thr, %arg1): block2(%arg1); block2(%b): @@ -179,7 +179,7 @@ foo:bar/1 { "); let ir2 = parse_function_unwrap(" -a:b/1 { +a'a':a'b'/1 { entry(%ret, %thr, %arg1): block2(%arg1); block3(%a): @@ -195,14 +195,14 @@ a:b/1 { #[test] fn args_length_inequality() { let ir1 = parse_function_unwrap(" -foo:bar/2 { +a'foo':a'bar'/2 { entry(%ret, %thr, %arg1, %arg2): %ret(%arg1); } "); let ir2 = parse_function_unwrap(" -foo:bar/1 { +a'foo':a'bar'/1 { entry(%ret, %thr, %arg1): %ret(%arg1); } @@ -214,14 +214,14 @@ foo:bar/1 { #[test] fn args_read_inequality() { let ir1 = parse_function_unwrap(" -foo:bar/2 { +a'foo':a'bar'/2 { entry(%ret, %thr, %arg1, %arg2): %ret(%arg1); } "); let ir2 = parse_function_unwrap(" -foo:bar/2 { +a'foo':a'bar'/2 { entry(%ret, %thr, %arg1, %arg2): %ret(%arg2); } @@ -233,14 +233,14 @@ foo:bar/2 { #[test] fn args_read_const_equality() { let ir1 = parse_function_unwrap(" -foo:bar/2 { +a'foo':a'bar'/2 { entry(%ret, %thr, %arg1, %arg2): %ret(a'a'); } "); let ir2 = parse_function_unwrap(" -foo:bar/2 { +a'foo':a'bar'/2 { entry(%ret, %thr, %arg1, %arg2): %ret(a'a'); } @@ -252,14 +252,14 @@ foo:bar/2 { #[test] fn args_read_const_inequality() { let ir1 = parse_function_unwrap(" -foo:bar/2 { +a'foo':a'bar'/2 { entry(%ret, %thr, %arg1, %arg2): %ret(a'a'); } "); let ir2 = parse_function_unwrap(" -foo:bar/2 { +a'foo':a'bar'/2 { entry(%ret, %thr, %arg1, %arg2): %ret(a'b'); } @@ -271,7 +271,7 @@ foo:bar/2 { #[test] fn args_prim_inequality() { let ir1 = parse_function_unwrap(" -foo:bar/1 { +a'foo':a'bar'/1 { entry(%ret, %thr, %arg1): %fun = a'a':a'a'/1; %fun(%ret, %thr, %arg1); @@ -279,7 +279,7 @@ foo:bar/1 { "); let ir2 = parse_function_unwrap(" -foo:bar/1 { +a'foo':a'bar'/1 { entry(%ret, %thr, %arg1): %fun = a'a':a'b'/1; %fun(%ret, %thr, %arg1); diff --git a/libeir_ir/src/algo/live.rs b/libeir_ir/src/algo/live.rs index 540b146..326fe3d 100644 --- a/libeir_ir/src/algo/live.rs +++ b/libeir_ir/src/algo/live.rs @@ -159,7 +159,7 @@ mod tests { fn test_simple() { let (ir, map) = crate::parse_function_map_unwrap(" -foo:bar/1 { +a'foo':a'bar'/1 { b1(%ret, %thr): b2(); b2(): @@ -193,7 +193,7 @@ foo:bar/1 { fn test_cycle() { let (ir, map) = crate::parse_function_map_unwrap(" -foo:bar/1 { +a'foo':a'bar'/1 { b1(%ret, %thr, %a): b2(%a, []); b2(%b, %c): diff --git a/libeir_ir/src/algo/mangle/tests.rs b/libeir_ir/src/algo/mangle/tests.rs index 9fe5eec..2540b8e 100644 --- a/libeir_ir/src/algo/mangle/tests.rs +++ b/libeir_ir/src/algo/mangle/tests.rs @@ -1,4 +1,4 @@ -use crate::NilTerm; +use crate::{NilTerm, StandardFormatConfig}; use super::Mangler; use super::ToT; @@ -7,7 +7,7 @@ use super::ToT; fn simple_mangle() { let (mut ir, map) = crate::parse_function_map_unwrap(" -foo:bar/1 { +a'foo':a'bar'/1 { entry(%ret, %thr, %a): b1(); b1(): @@ -35,7 +35,7 @@ foo:bar/1 { b.block_set_entry(new_b1); let after = crate::parse_function_unwrap(" -foo:bar/1 { +a'foo':a'bar'/1 { entry(%ret): b1(); b1(): @@ -51,7 +51,7 @@ foo:bar/1 { fn mangle_primop() { let (mut ir, map) = crate::parse_function_map_unwrap(" -foo:bar/1 { +a'foo':a'bar'/1 { entry(%ret, %thr, %a): %ret({%a}); } @@ -70,7 +70,7 @@ foo:bar/1 { let new_b1 = mangler.run(&mut b); let after = crate::parse_function_unwrap(" -foo:bar/1 { +a'foo':a'bar'/1 { entry(%ret, %thr, %a): %ret({[]}); } @@ -84,7 +84,7 @@ foo:bar/1 { fn mangle_recursive() { let (mut ir, map) = crate::parse_function_map_unwrap(" -foo:bar/2 { +a'foo':a'bar'/2 { entry(%ret, %thr, %a, %b): b2(%a); b1(%m): @@ -99,7 +99,7 @@ foo:bar/2 { "); let mut b = ir.builder(); - println!("{}", b.fun().to_text()); + println!("{}", b.fun().to_text(&mut StandardFormatConfig::default())); let mut mangler = Mangler::new(); @@ -117,10 +117,10 @@ foo:bar/2 { let new_entry = mangler.run(&mut b); b.block_set_entry(new_entry); - println!("{}", b.fun().to_text()); + println!("{}", b.fun().to_text(&mut StandardFormatConfig::default())); let after = crate::parse_function_unwrap(" -foo:bar/2 { +a'foo':a'bar'/2 { entry(%ret, %thr, %a, %b): b1(%a); b1(%m): @@ -135,7 +135,7 @@ foo:bar/2 { #[test] fn mangle_entry() { let (mut ir, map) = crate::parse_function_map_unwrap(" -foo:bar/0 { +a'foo':a'bar'/0 { block0(%1, %2): unreachable; block1(%4, %5): @@ -159,14 +159,14 @@ foo:bar/0 { let new_entry = mangler.run(&mut b); b.block_set_entry(new_entry); - println!("{}", b.fun().to_text()); + println!("{}", b.fun().to_text(&mut StandardFormatConfig::default())); let mut errors = Vec::new(); b.fun().validate(&mut errors); assert_eq!(errors.len(), 0, "{:#?}", errors); let after = crate::parse_function_unwrap(" -foo:bar/0 { +a'foo':a'bar'/0 { block3(%9, %10): block4(%10); block4(%12): diff --git a/libeir_ir/src/constant/mod.rs b/libeir_ir/src/constant/mod.rs index e5cf760..a64186f 100644 --- a/libeir_ir/src/constant/mod.rs +++ b/libeir_ir/src/constant/mod.rs @@ -112,16 +112,16 @@ impl ConstantContainer { val.into_const(self) } - pub fn print(&self, val: Const, fmt: &mut T) - where T: crate::text::TextFormatter - { - match &self.const_values[val] { - ConstKind::Atomic(atomic) => { - fmt.write(&format!("{}", atomic)); - } - _ => unimplemented!() - } - } + //pub fn print(&self, val: Const, fmt: &mut T) + //where T: crate::text::TextFormatter + //{ + // match &self.const_values[val] { + // ConstKind::Atomic(atomic) => { + // fmt.write(&format!("{}", atomic)); + // } + // _ => unimplemented!() + // } + //} pub fn as_bool(&self, val: Const) -> Option { let kind = &self.const_values[val]; @@ -154,11 +154,21 @@ impl ConstantContainer { } write!(out, "}}").unwrap(); } - // TODO - kind => { - println!("Unimplemented constant write: {:?}", kind); - write!(out, "?").unwrap(); - }, //unimplemented!("{:?}", kind) + ConstKind::Map { keys, values } => { + write!(out, "%{{").unwrap(); + for (n, (key, val)) in keys.as_slice(&self.const_pool).iter() + .zip(values.as_slice(&self.const_pool)) + .enumerate() + { + if n != 0 { + write!(out, ", ").unwrap(); + } + self.write(*key, out); + write!(out, ": ").unwrap(); + self.write(*val, out); + } + write!(out, "}}").unwrap(); + } } } diff --git a/libeir_ir/src/function/builder/mod.rs b/libeir_ir/src/function/builder/mod.rs index c78e5be..34b1f05 100644 --- a/libeir_ir/src/function/builder/mod.rs +++ b/libeir_ir/src/function/builder/mod.rs @@ -43,7 +43,9 @@ impl IntoValue for PrimOp { impl IntoValue for T where T: IntoConst { fn into_value<'a>(self, b: &mut FunctionBuilder<'a>) -> Value { let constant = b.fun.constant_container.from(self); - b.fun.values.push(ValueKind::Const(constant)) + let value = b.fun.values.push(ValueKind::Const(constant)); + b.fun.constant_values.insert(value); + value } } diff --git a/libeir_ir/src/function/mod.rs b/libeir_ir/src/function/mod.rs index 9d77794..f7d495a 100644 --- a/libeir_ir/src/function/mod.rs +++ b/libeir_ir/src/function/mod.rs @@ -514,15 +514,4 @@ impl Function { self.block_args(self.block_entry()).len() } - pub fn to_text(&self) -> String { - use crate::text::{ ToEirText, ToEirTextContext }; - - let mut ctx = ToEirTextContext::new(); - - let mut out = Vec::new(); - self.to_eir_text(&mut ctx, 0, &mut out).unwrap(); - String::from_utf8(out).unwrap() - } - } - diff --git a/libeir_ir/src/function/op.rs b/libeir_ir/src/function/op.rs index 80a284d..4549256 100644 --- a/libeir_ir/src/function/op.rs +++ b/libeir_ir/src/function/op.rs @@ -173,7 +173,7 @@ pub enum OpKind { /// It consists of 3 operations, `receive_start`, `receive_wait` /// and `receive_done`. They are always called in this pattern: /// - /// ``` + /// ```ignore /// v /// receive_start /// | diff --git a/libeir_ir/src/graph/mod.rs b/libeir_ir/src/graph/mod.rs index 957a803..3148100 100644 --- a/libeir_ir/src/graph/mod.rs +++ b/libeir_ir/src/graph/mod.rs @@ -1,5 +1,5 @@ mod block_graph; -pub use block_graph::BlockGraph; +pub use block_graph::{BlockGraph, EntityVisitMap}; mod live_block_graph; pub use live_block_graph::LiveBlockGraph; diff --git a/libeir_ir/src/lib.rs b/libeir_ir/src/lib.rs index 4ed6ebf..c375f6b 100644 --- a/libeir_ir/src/lib.rs +++ b/libeir_ir/src/lib.rs @@ -1,5 +1,5 @@ #![feature(specialization)] -#![deny(warnings)] +//#![deny(warnings)] use std::fmt::{ Display, Formatter }; use std::cmp::Ordering; @@ -47,7 +47,7 @@ pub use text::{ parse_function_map, parse_function_map_unwrap, parse_module, parse_module_unwrap }; -pub use text::printer::{ ToEirText, ToEirTextContext }; +pub use text::printer::{ FormatConfig, StandardFormatConfig }; pub mod binary; pub use binary::{ BinaryEntrySpecifier, Endianness }; diff --git a/libeir_ir/src/module.rs b/libeir_ir/src/module.rs index 256caf5..e3ce796 100644 --- a/libeir_ir/src/module.rs +++ b/libeir_ir/src/module.rs @@ -4,7 +4,7 @@ use std::collections::BTreeMap; use cranelift_entity::{PrimaryMap, entity_impl}; use libeir_intern::{Ident, Symbol}; -use crate::{Function, FunctionIdent, ToEirText, ToEirTextContext}; +use crate::{Function, FunctionIdent}; #[derive(Debug)] pub struct FunctionDefinition { @@ -73,14 +73,6 @@ impl Module { def_mut } - pub fn to_text(&self) -> String { - let mut ctx = ToEirTextContext::new(); - - let mut out = Vec::new(); - self.to_eir_text(&mut ctx, 0, &mut out).unwrap(); - String::from_utf8(out).unwrap() - } - pub fn ident_index(&self, ident: &FunctionIdent) -> Option { self.name_map.get(&(ident.name.name, ident.arity)).cloned() } diff --git a/libeir_ir/src/text/ast/lower.rs b/libeir_ir/src/text/ast/lower.rs index 6a2ed01..01ed461 100644 --- a/libeir_ir/src/text/ast/lower.rs +++ b/libeir_ir/src/text/ast/lower.rs @@ -13,7 +13,7 @@ use crate::{Block, Value}; use crate::PatternNode; use crate::text::ast; -type ErrCollector<'a> = &'a mut dyn ErrorReceiver; +type ErrCollector<'a> = &'a mut dyn ErrorReceiver; #[derive(Debug, Snafu)] pub enum LowerError { diff --git a/libeir_ir/src/text/dot_printer.rs b/libeir_ir/src/text/dot_printer.rs index debc828..c543f86 100644 --- a/libeir_ir/src/text/dot_printer.rs +++ b/libeir_ir/src/text/dot_printer.rs @@ -1,5 +1,9 @@ +use std::marker::PhantomData; + use crate::Function; -use super::printer::{ ToEirTextFun, ToEirTextContext }; +use pretty::Arena; +use super::printer as pr; +use super::printer::{FunctionFormatData, BlockFormatSink, FormatConfig, FormatState}; use libeir_util_dot_graph::GraphPrinter; @@ -9,13 +13,26 @@ pub fn function_into_graph_printer(fun: &Function, g: &mut GraphPrinter) where O: std::fmt::Write, { - let mut to_eir_ctx = ToEirTextContext::new(); - - let mut buf = Vec::new(); - - super::printer::print_constants(&mut to_eir_ctx, fun, 0, &mut buf).unwrap(); - let text = std::str::from_utf8(&buf).unwrap(); - g.node("constants", text); + let mut buf = String::new(); + + let mut config = pr::FormatConfig { + width: 80, + block_iterator_config: pr::DfsBlockIteratorConfig, + value_formatter: pr::StandardValueFormatter, + block_value_layout: pr::ReferencePrimopBlockValueLayout::default(), + }; + let mut state = FormatState { + function: fun, + nesting: 0, + }; + + let arena = Arena::new(); + let mut ctx = FunctionFormatData { + arena: &arena, + buf: String::new(), + value_buf: Vec::new(), + config: PhantomData, + }; let block_graph = fun.block_graph(); let mut block_dfs = Dfs::new(&block_graph, fun.block_entry()); @@ -23,10 +40,9 @@ where while let Some(block) = block_dfs.next(&block_graph) { let block_val = fun.block_value(block); - buf.clear(); - block.to_eir_text_fun(&mut to_eir_ctx, fun, 0, &mut buf).unwrap(); - let text = std::str::from_utf8(&buf).unwrap(); - g.node(block_val, text); + let doc = ctx.block_to_doc(&mut config, &mut state, block); + doc.render_fmt(80, &mut buf).unwrap(); + g.node(block_val, &buf); for out in block_graph.neighbors(block) { let out_val = fun.block_value(out); diff --git a/libeir_ir/src/text/mod.rs b/libeir_ir/src/text/mod.rs index 8a585f7..6b0e8d6 100644 --- a/libeir_ir/src/text/mod.rs +++ b/libeir_ir/src/text/mod.rs @@ -1,65 +1,67 @@ +//pub mod printer; +//pub use printer::{ ToEirText, ToEirTextFun, ToEirTextContext }; + pub mod printer; -pub use printer::{ ToEirText, ToEirTextFun, ToEirTextContext }; pub mod dot_printer; pub use dot_printer::function_to_dot; -pub trait TextFormatter { - // TODO add result - fn write(&mut self, text: &str); - fn newline(&mut self); - - fn enter_indent(&mut self, dist: usize); - fn exit_indent(&mut self, dist: usize); -} - -pub struct BufferTextFormatter { - indent: usize, - buf: String, -} - -impl Default for BufferTextFormatter { - fn default() -> Self { - BufferTextFormatter { - indent: 0, - buf: String::new(), - } - } -} - -impl BufferTextFormatter { - - pub fn new() -> Self { - Self::default() - } - - pub fn clear(&mut self) { - self.indent = 0; - self.buf.clear(); - } - -} - -impl TextFormatter for BufferTextFormatter { - - fn write(&mut self, text: &str) { - self.buf.push_str(text); - } - fn newline(&mut self) { - self.buf.push('\n'); - for _ in 0..self.indent { - self.buf.push(' '); - } - } - - fn enter_indent(&mut self, _dist: usize) { - self.indent += 1; - } - fn exit_indent(&mut self, _dist: usize) { - self.indent -= 1; - } - -} +//pub trait TextFormatter { +// // TODO add result +// fn write(&mut self, text: &str); +// fn newline(&mut self); +// +// fn enter_indent(&mut self, dist: usize); +// fn exit_indent(&mut self, dist: usize); +//} +// +//pub struct BufferTextFormatter { +// indent: usize, +// buf: String, +//} +// +//impl Default for BufferTextFormatter { +// fn default() -> Self { +// BufferTextFormatter { +// indent: 0, +// buf: String::new(), +// } +// } +//} +// +//impl BufferTextFormatter { +// +// pub fn new() -> Self { +// Self::default() +// } +// +// pub fn clear(&mut self) { +// self.indent = 0; +// self.buf.clear(); +// } +// +//} +// +//impl TextFormatter for BufferTextFormatter { +// +// fn write(&mut self, text: &str) { +// self.buf.push_str(text); +// } +// fn newline(&mut self) { +// self.buf.push('\n'); +// for _ in 0..self.indent { +// self.buf.push(' '); +// } +// } +// +// fn enter_indent(&mut self, _dist: usize) { +// self.indent += 1; +// } +// fn exit_indent(&mut self, _dist: usize) { +// self.indent -= 1; +// } +// +//} pub mod parser; pub use parser::{ diff --git a/libeir_ir/src/text/parser/errors.rs b/libeir_ir/src/text/parser/errors.rs index 457f3b9..f1d9bc8 100644 --- a/libeir_ir/src/text/parser/errors.rs +++ b/libeir_ir/src/text/parser/errors.rs @@ -8,9 +8,10 @@ use crate::text::ast::LowerError; pub type ParseError = lalrpop_util::ParseError; -pub type ParserErrors = libeir_util_parse::ParserErrors; +pub type Errors = libeir_util_parse::Errors; #[derive(Debug, Snafu)] +#[snafu(visibility = "pub(super)")] pub enum ParserError { #[snafu(display("{}", source))] diff --git a/libeir_ir/src/text/parser/grammar.lalrpop b/libeir_ir/src/text/parser/grammar.lalrpop index 0efb451..0475dc4 100644 --- a/libeir_ir/src/text/parser/grammar.lalrpop +++ b/libeir_ir/src/text/parser/grammar.lalrpop @@ -13,9 +13,10 @@ use crate::text::ast::{Module, ModuleItem, Function, FunctionItem, Label, Assignment, UnpackValueListOp, IfBoolOp, TraceCaptureRawOp, MatchEntry, MatchKind, MatchOp, CaseOp, CaseEntry, CasePattern}; -use super::errors::{ParserError, ParserErrors}; +use super::ParserErrorReceiver; +use super::errors::{ParserError, Errors}; -grammar(errors: &mut dyn ErrorReceiver); +grammar<'a>(errors: &'a mut ParserErrorReceiver<'a>); #[inline] Comma: Vec = { @@ -29,7 +30,7 @@ Comma: Vec = { }; pub Module: Module = { - "{" "}" => { + "{" "}" => { Module { name: name, items, @@ -42,7 +43,7 @@ ModuleItem: ModuleItem = { }; pub StandaloneFunction: (Ident, Function) = { - ":" "/" "{" "}" => { + ":" "/" "{" "}" => { ( module, Function { @@ -55,7 +56,7 @@ pub StandaloneFunction: (Ident, Function) = { }; Function: Function = { - "/" "{" "}" => { + "/" "{" "}" => { Function { name: name, arity: arity, diff --git a/libeir_ir/src/text/parser/mod.rs b/libeir_ir/src/text/parser/mod.rs index 3ed9bca..06b072f 100644 --- a/libeir_ir/src/text/parser/mod.rs +++ b/libeir_ir/src/text/parser/mod.rs @@ -1,10 +1,6 @@ -use std::borrow::Cow; -use std::sync::{Arc, RwLock}; -use std::path::Path; - -use libeir_diagnostics::{CodeMap, FileName, ByteIndex}; +use libeir_diagnostics::ByteIndex; use libeir_intern::Ident; -use libeir_util_parse::{Source, FileMapSource, Scanner, ErrorReceiver}; +use libeir_util_parse::{Parser, Parse, Source, Scanner, ErrorReceiver, SourceError, ArcCodemap, error_tee}; use crate::FunctionIdent; @@ -12,53 +8,90 @@ mod lexer; use lexer::{Lexer, Token}; mod errors; -use self::errors::{ParserError, ParserErrors}; +pub use self::errors::ParserError; +use self::errors::Errors; use super::ast::LowerMap; -pub fn module(text: &str) -> (Result, ParserErrors) { - let parser = Parser::new(); - let mut errors = ParserErrors::new(parser.codemap.clone()); +pub fn module_codemap(text: &str, codemap: &ArcCodemap) -> (Result, Errors) { + let mut errors = Errors::new(); + + let parser = Parser::new(()); + + let res = match parser.parse_string(&mut errors, &codemap, text) { + Ok(module) => { + let module: super::ast::Module = module; - let module: super::ast::Module = match parser.parse_string(&mut errors, text) { - Ok(module) => module, - Err(()) => return (Err(()), errors), + error_tee(&mut errors, |mut errors| { + let mut adapter = errors.make_into_adapter(); + + match module.lower(&mut adapter) { + Ok(module) => Ok(module), + Err(()) => Err(()), + } + }) + }, + Err(()) => Err(()), }; - match module.lower(&mut errors) { - Ok(module) => (Ok(module), errors), - Err(()) => (Err(()), errors), - } + (res, errors) +} + +pub fn module(text: &str) -> (Result, Errors) { + let codemap = ArcCodemap::default(); + module_codemap(text, &codemap) } pub fn module_unwrap(text: &str) -> crate::Module { - match module(text) { + let codemap = ArcCodemap::default(); + match module_codemap(text, &codemap) { (Ok(module), errors) => { - errors.print(); + errors.print(&codemap); module }, (Err(()), errors) => { - errors.print(); + errors.print(&codemap); panic!(); }, } } -pub fn function_map(text: &str) -> (Result<(crate::Function, LowerMap), ()>, ParserErrors) { - let parser = Parser::new(); - let mut errors = ParserErrors::new(parser.codemap.clone()); +pub fn function_map_codemap(text: &str, codemap: &ArcCodemap) -> (Result<(crate::Function, LowerMap), ()>, Errors) { + let mut errors = Errors::new(); + + let parser = Parser::new(()); - let (name, function): (Ident, super::ast::Function) = match parser.parse_string(&mut errors, text) { - Ok(module) => module, - Err(()) => return (Err(()), errors), + let ret = match parser.parse_string(&mut errors, &codemap, text) { + Ok(named) => { + let named: NamedFunction = named; + + error_tee(&mut errors, |mut errors| { + let mut adapter = errors.make_into_adapter(); + + match named.function.lower(&mut adapter, named.name) { + Ok(res) => Ok(res), + Err(()) => Err(()), + } + }) + }, + Err(()) => Err(()), }; - match function.lower(&mut errors, name) { - Ok(res) => (Ok(res), errors), - Err(()) => (Err(()), errors), + (ret, errors) +} + +pub fn function_map(text: &str) -> (Result<(crate::Function, LowerMap), ()>, Errors) { + let codemap = ArcCodemap::default(); + function_map_codemap(text, &codemap) +} + +pub fn function_codemap(text: &str, codemap: &ArcCodemap) -> (Result, Errors) { + match function_map_codemap(text, codemap) { + (Ok((fun, _)), errors) => (Ok(fun), errors), + (Err(()), errors) => (Err(()), errors), } } -pub fn function(text: &str) -> (Result, ParserErrors) { +pub fn function(text: &str) -> (Result, Errors) { match function_map(text) { (Ok((fun, _)), errors) => (Ok(fun), errors), (Err(()), errors) => (Err(()), errors), @@ -66,35 +99,39 @@ pub fn function(text: &str) -> (Result, ParserErrors) { } pub fn function_unwrap(text: &str) -> crate::Function { - match function(text) { + let codemap = ArcCodemap::default(); + match function_codemap(text, &codemap) { (Ok(fun), errors) => { - errors.print(); + errors.print(&codemap); fun }, (Err(()), errors) => { - errors.print(); + errors.print(&codemap); panic!(); }, } } pub fn function_map_unwrap(text: &str) -> (crate::Function, LowerMap) { - match function_map(text) { + let codemap = ArcCodemap::default(); + match function_map_codemap(text, &codemap) { (Ok(fun), errors) => { - errors.print(); + errors.print(&codemap); fun }, (Err(()), errors) => { - errors.print(); + errors.print(&codemap); panic!(); }, } } +type ParserErrorReceiver<'a> = dyn ErrorReceiver + 'a; + #[cfg_attr(rustfmt, rustfmt_skip)] #[allow(unknown_lints)] #[allow(clippy)] -#[allow(unused_variables, dead_code, unused_imports)] +#[allow(unused_variables, dead_code, unused_imports, unused_parens)] pub(crate) mod grammar { // During the build step, `build.rs` will output the generated parser to `OUT_DIR` to avoid // adding it to the source directory, so we just directly include the generated parser here. @@ -108,77 +145,22 @@ pub(crate) mod grammar { include!(concat!(env!("OUT_DIR"), "/text/parser/grammar.rs")); } -pub struct Parser { - pub codemap: Arc>, +pub struct NamedFunction { + pub name: Ident, + pub function: super::ast::Function, } -impl Parser { - - pub fn new() -> Self { - Parser { - codemap: Arc::new(RwLock::new(CodeMap::new())), - } - } - - pub fn parse_string( - &self, - errors: &mut dyn ErrorReceiver, - source: S, - ) -> Result - where - S: AsRef, - T: Parse, - { - let filemap = - self.codemap.write().unwrap().add_filemap( - FileName::Virtual(Cow::Borrowed("nofile")), - source.as_ref().to_owned(), - ); - T::parse(FileMapSource::new(filemap), errors) - } - - pub fn parse_file( - &self, - errors: &mut dyn ErrorReceiver, - path: P, - ) -> Result - where - P: AsRef, - T: Parse, - { - match FileMapSource::from_path(self.codemap.clone(), path) { - Err(_err) => unimplemented!(), - Ok(source) => T::parse(source, errors), - } - } - -} - -pub trait Parse: Sized { +impl Parse for super::ast::Module { + type Parser = self::grammar::ModuleParser; + type Error = ParserError; + type Config = (); + type Token = std::result::Result<(ByteIndex, Token, ByteIndex), ParserError>; - fn parse( - source: S, - errors: &mut dyn ErrorReceiver - ) -> std::result::Result - where - S: Source, - { - let scanner = Scanner::new(source); - let lexer = Lexer::new(scanner); - Self::parse_tokens(lexer, errors) + fn file_map_error(err: SourceError) -> Self::Error { + ParserError::Source { source: err } } - fn parse_tokens( - tokens: S, - errors: &mut dyn ErrorReceiver - ) -> std::result::Result - where - S: IntoIterator>; - -} - -impl Parse for super::ast::Module { - fn parse_tokens(tokens: S, errors: &mut dyn ErrorReceiver) -> Result + fn parse_tokens(errors: &mut ParserErrorReceiver, tokens: S) -> Result where S: IntoIterator> { @@ -194,36 +176,72 @@ impl Parse for super::ast::Module { } } - if errors.is_failed() { + if (*errors).is_failed() { Err(()) } else { Ok(ret) } } + + fn parse(_config: &Self::Config, _codemap: &ArcCodemap, errors: &mut ParserErrorReceiver, source: S) -> Result + where + S: Source + { + let scanner = Scanner::new(source); + let lexer = Lexer::new(scanner); + Self::parse_tokens(errors, lexer) + } + } -impl Parse for (Ident, super::ast::Function) { - fn parse_tokens(tokens: S, errors: &mut dyn ErrorReceiver) -> Result +impl Parse for NamedFunction { + type Parser = self::grammar::StandaloneFunctionParser; + type Error = ParserError; + type Config = (); + type Token = std::result::Result<(ByteIndex, Token, ByteIndex), ParserError>; + + fn file_map_error(err: SourceError) -> Self::Error { + ParserError::Source { source: err } + } + + fn parse_tokens(errors: &mut ParserErrorReceiver, tokens: S) -> Result where S: IntoIterator> { let result = self::grammar::StandaloneFunctionParser::new() .parse(errors, tokens); - let ret; + let name; + let function; match result { - std::result::Result::Ok(ok) => ret = ok, + std::result::Result::Ok((i, f)) => { + name = i; + function = f; + }, std::result::Result::Err(err) => { errors.error(err.into()); return Err(()); } } - if errors.is_failed() { + if (*errors).is_failed() { Err(()) } else { - Ok(ret) + Ok(NamedFunction { + name, + function, + }) } } + + fn parse<'a, S>(_config: &Self::Config, _codemap: &ArcCodemap, errors: &'a mut ParserErrorReceiver<'a>, source: S) -> Result + where + S: Source + { + let scanner = Scanner::new(source); + let lexer = Lexer::new(scanner); + Self::parse_tokens(errors, lexer) + } + } impl FunctionIdent { @@ -271,44 +289,48 @@ impl FunctionIdent { #[cfg(test)] mod test { - use super::Parser; + use std::sync::{RwLock, Arc}; + + use super::{Parser, NamedFunction}; use crate::text::ast; - use super::errors::ParserErrors; use super::function_unwrap; + use libeir_diagnostics::CodeMap; + use libeir_util_parse::Errors; use libeir_intern::{Ident}; use pretty_assertions::assert_eq; #[test] fn parse_empty_function() { - let parser = Parser::new(); - let mut errors = ParserErrors::new(parser.codemap.clone()); + let codemap = Arc::new(RwLock::new(CodeMap::new())); + let mut errors = Errors::new(); - let _module: (Ident, ast::Function) = parser.parse_string(&mut errors, "foo:foo/1 {}") + let parser = Parser::new(()); + let _module: NamedFunction = parser.parse_string(&mut errors, &codemap, "a'foo':a'foo'/1 {}") .unwrap(); } #[test] #[should_panic] fn lower_empty_function_fails() { - function_unwrap("foo:foo/1 {}"); + function_unwrap("a'foo':a'foo'/1 {}"); } #[test] fn parse_kitchen_sink() { - let parser = Parser::new(); - let mut errors = ParserErrors::new(parser.codemap.clone()); - - let module: ast::Module = parser.parse_string(&mut errors, " -kitchen_sink { - something/1 { + let codemap = Arc::new(RwLock::new(CodeMap::new())); + let mut errors = Errors::new(); + let parser = Parser::new(()); + let module: ast::Module = parser.parse_string(&mut errors, &codemap, " +a'kitchen_sink' { + a'something'/1 { entry(%return, %throw, %num): %something = a'true'; %fun = a'a':a'b'/a'c'; %foobar(%woo, %hoo); } - second_fun/0 {} + a'second_fun'/0 {} } ").unwrap(); @@ -362,7 +384,7 @@ kitchen_sink { #[test] fn lower_add_one() { let _fun = function_unwrap(" -foo:add_one/1 { +a'foo':a'add_one'/1 { entry(%return, %throw, %num): %add_fun = a'erlang':a'+'/2; %add_fun(%return, %throw, %num, 1); diff --git a/libeir_ir/src/text/printer.rs b/libeir_ir/src/text/printer.rs deleted file mode 100644 index a84b6fa..0000000 --- a/libeir_ir/src/text/printer.rs +++ /dev/null @@ -1,572 +0,0 @@ -#![allow(clippy::write_with_newline)] - -use std::io::{ Write, Error as IoError }; - -use crate::{ Module, Function, FunctionIdent }; -use crate::{ Block, Value }; -use crate::{ OpKind, PrimOpKind, BinOp, MatchKind, BasicType, BinaryEntrySpecifier, Endianness, CallKind }; -use crate::ValueKind; -use crate::pattern::{ PatternContainer, PatternNode, PatternNodeKind }; - -use cranelift_entity::EntityRef; - -use std::collections::{HashSet, VecDeque, BTreeSet}; - -// Desired syntax: - -// ``` -// something { -// -// something:something/2@1.1 { -// entry(%0, %1): -// %2, %3 = call woo:hoo(%0) except l0(%3); -// tombstone %3; -// jump l1(%2); -// -// l0(%4): -// tombstone %2; -// return_error %4; -// -// l1(%5): -// return_ok %5; -// -// l2(): -// %6 = match_start on: %1, values: [%0] { -// clause { -// pattern [$0 = 0 | []]; -// }; -// }; -// jump l3(); -// l3(): -// match_body %6 fail err() leaves [clause1()]; -// -// err(): -// return_error a'nil'; -// -// clause1(): -// %7 = case_calues [$0]; -// case_guard_ok %7; -// -// } -// -// } -// ``` - -pub trait EirAnnotator { - fn annotate_function(&mut self, out: &mut String, fun: &Function); - fn annotate_block(&mut self, out: &mut String, fun: &Function, block: Block); -} - -//pub struct EirLiveValuesAnnotator { -// live: Option, -//} -//impl EirLiveValuesAnnotator { -// pub fn new() -> Self { -// EirLiveValuesAnnotator { -// live: None, -// } -// } -//} -//impl EirAnnotator for EirLiveValuesAnnotator { -// fn annotate_function(&mut self, out: &mut String, fun: &Function) { -// self.live = Some(fun.live_values()); -// } -// fn annotate_op(&mut self, out: &mut String, fun: &Function, op: Op) { -// let live = self.live.as_ref().unwrap(); -// if let Some(l) = live.flow_live.get(&op) { -// out.push_str(" live:"); -// println!("LVLEN: {:?}", live); -// for var in l.iter(&live.pool) { -// out.push_str(&format!(" %{}", var.index())); -// } -// } -// } -// fn annotate_ebb(&mut self, out: &mut String, fun: &Function, ebb: Ebb) { -// let live = self.live.as_ref().unwrap(); -// let l = &live.ebb_live[&ebb]; -// out.push_str("yay!"); -// } -//} - -#[derive(Default)] -pub struct ToEirTextContext { - out_buf: String, - annotators: Vec>, -} - -impl ToEirTextContext { - pub fn new() -> Self { - Self::default() - } - pub fn add_annotator(&mut self, ann: T) where T: EirAnnotator + 'static { - self.annotators.push(Box::new(ann)); - } - pub fn annotate_function(&mut self, fun: &Function) { - for ann in self.annotators.iter_mut() { - ann.annotate_function(&mut self.out_buf, fun); - } - } - pub fn annotate_block(&mut self, fun: &Function, block: Block) -> Option { - self.out_buf.clear(); - for ann in self.annotators.iter_mut() { - ann.annotate_block(&mut self.out_buf, fun, block); - } - if !self.out_buf.is_empty() { - Some(self.out_buf.to_string()) - } else { - None - } - } -} - -pub trait ToEirText { - fn to_eir_text(&self, ctx: &mut ToEirTextContext, indent: usize, out: &mut dyn Write) -> std::io::Result<()>; -} -pub trait ToEirTextFun { - fn to_eir_text_fun(&self, ctx: &mut ToEirTextContext, fun: &Function, indent: usize, - out: &mut dyn Write) -> std::io::Result<()>; -} - -fn write_indent(out: &mut dyn Write, indent: usize) -> std::io::Result<()> { - for _ in 0..indent { - write!(out, " ")?; - } - Ok(()) -} - -impl ToEirText for FunctionIdent { - fn to_eir_text(&self, _ctx: &mut ToEirTextContext, indent: usize, out: &mut dyn Write) -> std::io::Result<()> { - write_indent(out, indent)?; - write!(out, "{}:{}/{}", - self.module, self.name, self.arity)?; - Ok(()) - } -} - -pub fn print_constants(_ctx: &mut ToEirTextContext, _fun: &Function, _indent: usize, _out: &mut dyn Write) -> std::io::Result<()> { - // TODO - //let mut used_values = HashSet::new(); - //fun.used_values(&mut used_values); - - //let mut values: Vec<_> = used_values.iter().cloned().collect(); - //values.sort(); - - //for value in values.iter() { - // let typ = fun.value(*value); - // match typ { - // ValueType::Constant(cons) => { - // write_indent(out, indent)?; - // write!(out, "%{} = ", value.index())?; - // cons.to_eir_text(ctx, indent+1, out)?; - // write!(out, ";\n")?; - // }, - // ValueType::Variable => (), - // } - //} - - Ok(()) -} - -impl ToEirText for Module { - fn to_eir_text(&self, ctx: &mut ToEirTextContext, indent: usize, out: &mut dyn Write) -> std::io::Result<()> { - let funs: Vec<_> = self.index_iter().collect(); - - write_indent(out, indent)?; - write!(out, "{} {{\n\n", self.name())?; - - for idx in funs.iter() { - let fun_def = &self[*idx]; - let fun = fun_def.function(); - fun.to_eir_text(ctx, indent+1, out)?; - write!(out, "\n\n")?; - } - - write_indent(out, indent)?; - write!(out, "}}")?; - - Ok(()) - } -} - -impl ToEirText for Function { - fn to_eir_text(&self, ctx: &mut ToEirTextContext, indent: usize, out: &mut dyn Write) -> std::io::Result<()> { - ctx.annotate_function(self); - let ident = self.ident(); - - write_indent(out, indent)?; - write!(out, "{}/{} {{\n", ident.name, ident.arity)?; - - // Constants - print_constants(ctx, self, indent+1, out)?; - write!(out, "\n")?; - - // Blocks - let mut walked = BTreeSet::new(); - let mut to_walk = VecDeque::new(); - to_walk.push_back(self.block_entry()); - - while let Some(block) = to_walk.pop_front() { - if walked.contains(&block) { continue; } - walked.insert(block); - - self.block_walk_nested_values::<_, Result<(), ()>>(block, &mut |v| { - if let Some(inner) = self.value_block(v) { - to_walk.push_back(inner); - } - Ok(()) - }).unwrap(); - - block.to_eir_text_fun(ctx, self, indent+1, out)?; - write!(out, "\n")?; - } - - write_indent(out, indent)?; - write!(out, "}}")?; - - Ok(()) - } -} - -fn format_pattern(_ctx: &mut ToEirTextContext, pat: &PatternContainer, _indent: usize, - annotated_nodes: &HashSet, - node: PatternNode, out: &mut dyn Write) -> std::io::Result<()> { - if annotated_nodes.contains(&node) { - write!(out, "n{} @ ", node.index())?; - } - match pat.node_kind(node) { - PatternNodeKind::Wildcard => write!(out, "_")?, - _ => write!(out, "?")?, - } - - Ok(()) -} - -fn get_value_list<'a>(fun: &'a Function, value: Value) -> Option<&'a [Value]> { - if let Some(prim) = fun.value_primop(value) { - match fun.primop_kind(prim) { - crate::PrimOpKind::ValueList => - return Some(fun.primop_reads(prim)), - _ => (), - } - } - None -} - -impl ToEirTextFun for Block { - fn to_eir_text_fun(&self, ctx: &mut ToEirTextContext, fun: &Function, - indent: usize, out: &mut dyn Write) - -> std::io::Result<()> - { - - write_indent(out, indent)?; - write!(out, "{}(", self)?; - format_value_list(fun.block_args(*self), fun, out)?; - write!(out, "):\n")?; - - fun.block_walk_nested_values::<_, IoError>(*self, &mut |value| { - match fun.value_kind(value) { - ValueKind::PrimOp(prim) => { - write_indent(out, indent+1)?; - write!(out, "%{} = ", value.index())?; - - let reads = fun.primop_reads(prim); - - match fun.primop_kind(prim) { - PrimOpKind::CaptureFunction => { - assert!(reads.len() == 3); - format_value(reads[0], fun, out)?; - write!(out, ":")?; - format_value(reads[1], fun, out)?; - write!(out, "/")?; - format_value(reads[2], fun, out)?; - } - PrimOpKind::ListCell => { - assert!(reads.len() == 2); - write!(out, "[")?; - format_value(reads[0], fun, out)?; - write!(out, " | ")?; - format_value(reads[1], fun, out)?; - write!(out, "]")?; - } - PrimOpKind::ValueList => { - write!(out, "<")?; - format_value_list(reads, fun, out)?; - write!(out, ">")?; - } - PrimOpKind::BinOp(BinOp::Equal) => { - assert!(reads.len() == 2); - format_value(reads[0], fun, out)?; - write!(out, " == ")?; - format_value(reads[1], fun, out)?; - } - PrimOpKind::Tuple => { - write!(out, "{{")?; - for (i, value) in reads.iter().enumerate() { - if i != 0 { - write!(out, ", ")?; - } - format_value(*value, fun, out)?; - } - write!(out, "}}")?; - } - kind => { - write!(out, "{:?}", kind)?; - write!(out, "(")?; - format_value_list(reads, fun, out)?; - write!(out, ")")?; - }, - } - - write!(out, ";\n")?; - } - _ => (), - } - Ok(()) - })?; - - let args = fun.block_reads(*self); - if let Some(kind) = fun.block_kind(*self) { - write_indent(out, indent+1)?; - match kind { - OpKind::Case { clauses } => { - let clauses_num = clauses.len(&fun.pool.clause); - - let values_start = 1 + (clauses_num * 2); - - write!(out, "case ")?; - format_value(args[values_start], fun, out)?; - write!(out, " {{")?; - write!(out, "\n")?; - - for clause_num in 0..clauses_num { - let clause = clauses.get(0, &fun.pool.clause).unwrap(); - let clause_nodes = fun.pat().clause_root_nodes(clause); - - let base = 1 + (2 * clause_num); - let guard = args[base]; - let body = args[base + 1]; - - let mut annotated_nodes = HashSet::new(); - for bind in fun.pat().clause_binds(clause) { - annotated_nodes.insert(*bind); - } - - // Pattern body - write_indent(out, indent + 2)?; - write!(out, "(")?; - if !clause_nodes.is_empty() { - write!(out, "\n")?; - } - - for node in clause_nodes { - write_indent(out, indent + 3)?; - format_pattern(ctx, fun.pat(), indent+3, &annotated_nodes, *node, out)?; - write!(out, "\n")?; - } - - if !clause_nodes.is_empty() { - write_indent(out, indent + 2)?; - } - write!(out, ")")?; - - // Guard - write!(out, " guard ")?; - format_value(guard, fun, out)?; - - // Body - write!(out, " => ")?; - format_value(body, fun, out)?; - write!(out, "(")?; - let mut first = true; - for bind in fun.pat().clause_binds(clause) { - if !first { - write!(out, ", ")?; - } - first = false; - - write!(out, "n{}", bind.index())?; - } - write!(out, ");")?; - - write!(out, "\n")?; - } - - write_indent(out, indent + 2)?; - write!(out, "_ => ")?; - format_value(args[0], fun, out)?; - write!(out, ";")?; - write!(out, "\n")?; - - write_indent(out, indent + 1)?; - write!(out, "}}")?; - - } - OpKind::Match { branches } => { - let targets_opt = get_value_list(fun, args[0]); - let targets_one = &[args[0]]; - let targets = targets_opt.unwrap_or(targets_one); - - write!(out, "match ")?; - format_value(args[1], fun, out)?; - write!(out, " {{\n")?; - - for ((kind, arg), target) in branches.iter() - .zip(args[2..].iter()) - .zip(targets.iter()) - { - write_indent(out, indent + 2)?; - match kind { - MatchKind::Value => { - write!(out, "value ")?; - format_value(*arg, fun, out)?; - } - MatchKind::ListCell => { - write!(out, "[]")?; - } - MatchKind::Wildcard => { - write!(out, "_")?; - } - MatchKind::Tuple(n) => { - write!(out, "{{}} arity {}", n)?; - } - MatchKind::Type(BasicType::Map) => { - write!(out, "type %{{}}")?; - } - MatchKind::Type(_) => { - unimplemented!() - } - MatchKind::MapItem => { - write!(out, "%{{ ")?; - format_value(*arg, fun, out)?; - write!(out, "}}")?; - } - MatchKind::Binary(spec) => { - write!(out, "binary ")?; - - match spec { - BinaryEntrySpecifier::Integer { signed, endianness, unit } => { - if *signed { - write!(out, "signed ")?; - } else { - write!(out, "unsigned ")?; - } - match *endianness { - Endianness::Big => write!(out, "big ")?, - Endianness::Little => write!(out, "little ")?, - Endianness::Native => write!(out, "native ")?, - } - write!(out, "unit {} ", unit)?; - write!(out, "size ")?; - format_value(*arg, fun, out)?; - } - BinaryEntrySpecifier::Bytes { unit } => { - write!(out, "unit {} ", unit)?; - write!(out, "size ")?; - format_value(*arg, fun, out)?; - } - _ => unimplemented!("{:?}", spec), - } - - } - } - write!(out, " => ")?; - format_value(*target, fun, out)?; - write!(out, ";\n")?; - } - - write_indent(out, indent + 1)?; - write!(out, "}}")?; - } - OpKind::Call(CallKind::ControlFlow) => { - format_value(args[0], fun, out)?; - write!(out, "(")?; - format_value_list(&args[1..], fun, out)?; - write!(out, ")")?; - } - OpKind::Call(CallKind::Function) => { - format_value(args[0], fun, out)?; - write!(out, "(")?; - format_value_list(&args[3..], fun, out)?; - write!(out, ") => ")?; - format_value(args[1], fun, out)?; - write!(out, " except ")?; - format_value(args[2], fun, out)?; - } - OpKind::Intrinsic(name) => { - write!(out, "intrinsic {}(", name)?; - format_value_list(args, fun, out)?; - write!(out, ")")?; - } - OpKind::Unreachable => { - write!(out, "unreachable")?; - } - OpKind::IfBool => { - match args.len() { - 3 => { - write!(out, "if_bool ")?; - format_value(args[2], fun, out)?; - write!(out, " ")?; - format_value(args[0], fun, out)?; - write!(out, " ")?; - format_value(args[1], fun, out)?; - } - 4 => { - write!(out, "if_bool ")?; - format_value(args[3], fun, out)?; - write!(out, " ")?; - format_value(args[0], fun, out)?; - write!(out, " ")?; - format_value(args[1], fun, out)?; - write!(out, " ")?; - format_value(args[2], fun, out)?; - } - _ => panic!(), - } - } - OpKind::TraceCaptureRaw => { - assert!(args.len() == 1); - write!(out, "trace_capture_raw ")?; - format_value(args[0], fun, out)?; - } - OpKind::UnpackValueList(n) => { - assert!(args.len() == 2); - write!(out, "unpack ")?; - format_value(args[1], fun, out)?; - write!(out, " arity {} => ", n)?; - format_value(args[0], fun, out)?; - } - _ => { - write!(out, "{:?}(", kind)?; - format_value_list(args, fun, out)?; - write!(out, ")")?; - } - } - - write!(out, ";")?; - } - - Ok(()) - } -} - -fn format_value(value: Value, fun: &Function, out: &mut dyn Write) -> std::io::Result<()> { - match fun.value_kind(value) { - ValueKind::Block(block) => write!(out, "{}", block)?, - ValueKind::Const(cons) => { - fun.cons().write(cons, out); - }, - _ => write!(out, "%{}", value.index())?, - } - Ok(()) -} - -fn format_value_list(values: &[Value], fun: &Function, - out: &mut dyn Write) -> std::io::Result<()> { - for (idx, value) in values.iter().enumerate() { - if idx != 0 { - write!(out, ", ")?; - } - format_value(*value, fun, out)?; - } - Ok(()) -} diff --git a/libeir_ir/src/text/printer/constant.rs b/libeir_ir/src/text/printer/constant.rs new file mode 100644 index 0000000..e9d3839 --- /dev/null +++ b/libeir_ir/src/text/printer/constant.rs @@ -0,0 +1,101 @@ +use pretty::{Arena, RefDoc, DocAllocator}; + +use crate::{ConstantContainer, Const, ConstKind, AtomicTerm}; + +pub fn constant_to_doc<'a>( + arena: &'a Arena<'a>, + container: &ConstantContainer, + constant: Const +) -> RefDoc<'a, ()> +{ + constant_to_doc_state(arena, container, constant, ConstantState::Normal) +} + +#[derive(Debug, Copy, Clone)] +enum ConstantState { + Normal, + ListTail, +} + +macro_rules! norm_state { + ($arena:expr, $state:expr) => { + match $state { + ConstantState::Normal => $arena.nil(), + ConstantState::ListTail => $arena.space().append($arena.text("|")).append($arena.space()), + } + }; +} + +fn constant_to_doc_state<'a>( + arena: &'a Arena<'a>, + container: &ConstantContainer, + constant: Const, + state: ConstantState, +) -> RefDoc<'a, ()> { + match container.const_kind(constant) { + ConstKind::Atomic(atomic) => { + norm_state!(arena, state) + .append(atomic_to_doc(arena, atomic)) + .into_doc() + }, + ConstKind::ListCell { head, tail } => { + match state { + ConstantState::Normal => { + arena.nil() + .append(arena.text("[")) + .append(constant_to_doc_state(arena, container, *head, ConstantState::Normal)) + .append(constant_to_doc_state(arena, container, *tail, ConstantState::ListTail)) + .append(arena.text("]")) + .into_doc() + }, + ConstantState::ListTail => { + arena.nil() + .append(arena.text(",")) + .append(arena.space()) + .append(constant_to_doc_state(arena, container, *head, ConstantState::Normal)) + .append(constant_to_doc_state(arena, container, *tail, ConstantState::ListTail)) + .into_doc() + }, + } + }, + ConstKind::Tuple { entries } => { + norm_state!(arena, state) + .append(arena.text("{")) + .append(arena.intersperse( + entries.as_slice(&container.const_pool) + .iter() + .map(|c| constant_to_doc_state(arena, container, *c, ConstantState::Normal)), + arena.text(",").append(arena.space()) + )) + .append(arena.text("}")) + .into_doc() + }, + ConstKind::Map { keys, values } => { + norm_state!(arena, state) + .append(arena.text("%{")) + .append(arena.intersperse( + keys.as_slice(&container.const_pool) + .iter() + .zip(values.as_slice(&container.const_pool).iter()) + .map(|(k, v)| { + arena.nil() + .append(constant_to_doc_state(arena, container, *k, ConstantState::Normal)) + .append(arena.space()) + .append(arena.text("=>")) + .append(arena.space()) + .append(constant_to_doc_state(arena, container, *v, ConstantState::Normal)) + }), + arena.text(",").append(arena.space()) + )) + .append(arena.text("}")) + .into_doc() + }, + } +} + +fn atomic_to_doc<'a>( + arena: &'a Arena<'a>, + atomic: &AtomicTerm, +) -> RefDoc<'a, ()> { + arena.text(format!("{}", atomic)).into_doc() +} diff --git a/libeir_ir/src/text/printer/mod.rs b/libeir_ir/src/text/printer/mod.rs new file mode 100644 index 0000000..7b7c3c2 --- /dev/null +++ b/libeir_ir/src/text/printer/mod.rs @@ -0,0 +1,610 @@ +#![allow(unused)] + +use std::error::Error; +use std::fmt::Write; +use std::marker::PhantomData; +use std::collections::BTreeMap; + +use cranelift_entity::EntityRef; +use pretty::{DocAllocator, Arena, RefDoc}; +use petgraph::visit::Dfs; + +use crate::{Function, Block, Value, Const, ValueKind, OpKind, CallKind, PrimOpKind, Module}; +use crate::graph::EntityVisitMap; + +mod operation; +mod constant; + +type DynError = Box; + +//pub trait EirPrint +//where +// B: BlockIteratorConfig, +// V: ValueFormatter, +// L: BlockValueLayout, +//{ +// type Context; +// fn to_eir_text(&self, config: &FormatConfig, context: &Self::Context) -> String; +//} + +//impl EirPrint for Block +//where +// B: BlockIteratorConfig, +// V: ValueFormatter, +// L: BlockValueLayout, +//{ +//} + +fn get_value_list<'a>(fun: &'a Function, value: Value) -> Option<&'a [Value]> { + if let Some(prim) = fun.value_primop(value) { + match fun.primop_kind(prim) { + crate::PrimOpKind::ValueList => + return Some(fun.primop_reads(prim)), + _ => (), + } + } + None +} + +pub struct FormatConfig +where + B: BlockIteratorConfig, + V: ValueFormatter, + L: BlockValueLayout, +{ + pub width: usize, + + /// Encapsulates the iteration order for blocks within a function. + pub block_iterator_config: B, + + /// Formatter for values within a function. + pub value_formatter: V, + + /// Layout for values within a function. + pub block_value_layout: L, +} + +pub type StandardFormatConfig = FormatConfig; +impl Default for StandardFormatConfig { + fn default() -> Self { + FormatConfig { + width: 80, + block_iterator_config: DfsBlockIteratorConfig, + value_formatter: StandardValueFormatter, + block_value_layout: ReferencePrimopBlockValueLayout::default(), + } + } +} + +pub struct FormatState<'a> { + pub function: &'a Function, + pub nesting: usize, +} + +/// Iteration strategy for blocks within a function +pub trait BlockIteratorConfig { + type Iter: BlockIterator; + fn new(&self, fun: &Function) -> Self::Iter; +} +/// Implementation of block iteration strategy. +/// ## Invariants +/// * Each block MUST be returned at most once. +pub trait BlockIterator { + fn next(&mut self, fun: &Function) -> Option; +} + +pub struct DfsBlockIteratorConfig; +impl BlockIteratorConfig for DfsBlockIteratorConfig { + type Iter = DfsBlockIterator; + fn new(&self, fun: &Function) -> Self::Iter { + let graph = fun.block_graph(); + let entry = fun.block_entry(); + let dfs = Dfs::new(&graph, entry); + DfsBlockIterator { + dfs, + } + } +} +pub struct DfsBlockIterator { + dfs: Dfs>, +} +impl BlockIterator for DfsBlockIterator { + fn next(&mut self, fun: &Function) -> Option { + self.dfs.next(&fun.block_graph()) + } +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[allow(dead_code)] +pub enum ValueSite { + Decl, + Use, +} +pub trait ValueFormatter { + fn value(&self, out: &mut String, fun: &Function, site: ValueSite, value: Value); +} + +/// This value formatter prints values in the format supported by the +/// parser, and is considered the standard format. +pub struct StandardValueFormatter; +impl ValueFormatter for StandardValueFormatter { + fn value(&self, out: &mut String, fun: &Function, _site: ValueSite, value: Value) { + match fun.value_kind(value) { + ValueKind::Block(block) => write!(out, "{}", block).unwrap(), + _ => write!(out, "%{}", value.index()).unwrap(), + } + } +} + +pub trait BlockValueLayout { + /// Lays out the root scope for the module. This is called once + /// at the beginning of processing a module. + fn layout_module(&mut self, fun: &Function); + /// Lays out the given block. This will be called once for each block. + fn layout(&mut self, fun: &Function, block: Block); + + /// Values for the current layout. + fn values(&self) -> &[Value]; + + /// Queries whether the given value should be laid out inline, or if + /// it should be referenced by value. + fn should_layout(&self, value: Value, within: Option) -> bool; +} + +pub struct ReferencePrimopBlockValueLayout { + values: Vec, + values_set: BTreeMap, +} +impl Default for ReferencePrimopBlockValueLayout { + fn default() -> Self { + ReferencePrimopBlockValueLayout { + values: Vec::new(), + values_set: BTreeMap::new(), + } + } +} +impl BlockValueLayout for ReferencePrimopBlockValueLayout { + fn layout_module(&mut self, fun: &Function) {} + fn layout(&mut self, fun: &Function, block: Block) { + self.values.clear(); + self.values_set.clear(); + + fun.block_walk_nested_values::<_, ()>(block, &mut |value| { + if fun.value_primop(value).is_some() { + self.values.push(value); + } + Ok(()) + }); + + for (idx, value) in self.values.iter().enumerate() { + self.values_set.insert(*value, idx); + } + + let mut idx = 0; + let values_set = &self.values_set; + self.values.retain(|val| { + let ret = values_set[val] == idx; + idx += 1; + ret + }); + } + + fn values(&self) -> &[Value] { + &self.values + } + + fn should_layout(&self, value: Value, within: Option) -> bool { + !self.values_set.contains_key(&value) + } +} + +pub trait BlockFormatSink { + type LineIndex: Copy; + + fn write_indent(&mut self, num: usize) -> Result<(), DynError> { + for _ in 0..num { + self.write_str(" ")?; + } + Ok(()) + } + + fn write_str(&mut self, string: &str) -> Result<(), DynError>; + fn commit_line(&mut self) -> Result; + + /// Informs the sink that the given range of lines + /// contains the given blocks. + /// Blocks will never overlap, and this will be called + /// at most once for each block. + fn block_lines( + &mut self, + _block: Block, + _range: (Self::LineIndex, Self::LineIndex), + ) {} +} + +pub struct StringSink { + string: String, +} +impl StringSink { + pub fn new() -> Self { + StringSink { + string: String::new(), + } + } + pub fn finalize(self) -> String { + self.string + } +} +impl BlockFormatSink for &mut StringSink { + type LineIndex = (); + fn write_str(&mut self, string: &str) -> Result<(), DynError> { + self.string.push_str(string); + Ok(()) + } + fn commit_line(&mut self) -> Result<(), DynError> { + self.string.push('\n'); + Ok(()) + } +} + +pub(crate) struct FunctionFormatData<'a, B, V, L> +where + B: BlockIteratorConfig, + V: ValueFormatter, + L: BlockValueLayout, +{ + pub arena: &'a Arena<'a>, + pub buf: String, + pub value_buf: Vec, + pub config: PhantomData>, +} +impl<'a, B, V, L> FunctionFormatData<'a, B, V, L> +where + B: BlockIteratorConfig, + V: ValueFormatter, + L: BlockValueLayout, +{ + + pub(crate) fn block_to_doc( + &mut self, + config: &mut FormatConfig, + state: &mut FormatState, + block: Block, + ) -> RefDoc<'a, ()> { + let arena = self.arena; + + let ident = arena.as_string(block); + let args = arena.intersperse( + state.function.block_args(block) + .iter().map(|v| { + self.buf.clear(); + config.value_formatter.value( + &mut self.buf, + state.function, + ValueSite::Decl, + *v, + ); + arena.as_string(&self.buf) + }), + arena.text(", "), + ).parens(); + let header = ident + .append(args) + .append(":") + .group(); + + let body = self.block_body_to_doc(config, state, block); + + header.append(arena.hardline().append(body).nest(2)).into_doc() + } + + pub(crate) fn block_body_to_doc( + &mut self, + config: &mut FormatConfig, + state: &mut FormatState, + block: Block, + ) -> RefDoc<'a, ()> { + let arena = self.arena; + + //let mut value_buf = Vec::new(); + //state.function.block_walk_nested_values::<_, ()>(block, &mut |val| { + // match state.function.value_kind(val) { + // ValueKind::PrimOp(prim) => { + // value_buf.push(val); + // }, + // _ => (), + // } + // Ok(()) + //}); + //println!("value buf: {:?}", value_buf); + + config.block_value_layout.layout(state.function, block); + let value_buf = config.block_value_layout.values(); + + let values = arena.concat( + value_buf.iter().rev() + .map(|v| self.value_assign_to_doc(config, state, *v)) + .map(|v| arena.nil().append(v).append(arena.hardline())) + ); + + let op = self.block_op_to_doc(config, state, block); + + values + .append(op) + .into_doc() + } + + + pub(crate) fn value_assign_to_doc( + &mut self, + config: &FormatConfig, + state: &mut FormatState, + value: Value, + ) -> RefDoc<'a, ()> { + let arena = self.arena; + + let value_kind = state.function.value_kind(value); + let doc = match value_kind { + ValueKind::PrimOp(prim) => { + let prim_kind = state.function.primop_kind(prim); + let reads = state.function.primop_reads(prim); + + match prim_kind { + PrimOpKind::CaptureFunction => { + assert!(reads.len() == 3); + arena.nil() + .append(self.value_use_to_doc(config, state, reads[0])) + .append(arena.text(":")) + .append(self.value_use_to_doc(config, state, reads[1])) + .append(arena.text("/")) + .append(self.value_use_to_doc(config, state, reads[2])) + }, + PrimOpKind::Tuple => { + arena + .intersperse( + reads.iter() + .map(|r| self.value_use_to_doc(config, state, *r)), + arena.text(",").append(arena.space()) + ) + .enclose(arena.text("{"), arena.text("}")) + }, + PrimOpKind::ValueList => { + arena + .intersperse( + reads.iter() + .map(|r| self.value_use_to_doc(config, state, *r)), + arena.text(",").append(arena.space()) + ) + .enclose(arena.text("<"), arena.text(">")) + }, + PrimOpKind::ListCell => { + assert!(reads.len() == 2); + arena.nil() + .append(arena.text("[")) + .append(self.value_use_to_doc(config, state, reads[0])) + .append(arena.space()) + .append(arena.text("|")) + .append(arena.space()) + .append(self.value_use_to_doc(config, state, reads[1])) + .append(arena.text("]")) + }, + _ => unimplemented!("{:?}", prim_kind), + } + }, + _ => unimplemented!("{:?}", value_kind), + }; + + self.buf.clear(); + config.value_formatter.value( + &mut self.buf, + state.function, + ValueSite::Decl, + value, + ); + let value_doc = arena.as_string(&self.buf); + + arena.nil() + .append(value_doc) + .append(arena.space()) + .append(arena.text("=")) + .append(arena.space()) + .append(doc) + .append(arena.text(";")) + .into_doc() + } + + fn constant_to_doc( + &mut self, + _config: &FormatConfig, + state: &mut FormatState, + constant: Const, + ) -> RefDoc<'a, ()> { + self::constant::constant_to_doc(&self.arena, state.function.cons(), constant) + } + + fn value_use_to_doc( + &mut self, + config: &FormatConfig, + state: &mut FormatState, + value: Value + ) -> RefDoc<'a, ()> { + self.buf.clear(); + config.value_formatter.value( + &mut self.buf, + state.function, + ValueSite::Use, + value, + ); + self.arena.as_string(&self.buf).into_doc() + } + + +} + +fn format_function_body_state( + config: &mut FormatConfig, + state: &mut FormatState, + mut sink: S, +) -> Result<(), DynError> +where + B: BlockIteratorConfig, + V: ValueFormatter, + L: BlockValueLayout, + S: BlockFormatSink, +{ + let function = state.function; + let mut block_iter = config.block_iterator_config.new(function); + + let arena = Arena::new(); + let mut ctx = FunctionFormatData { + arena: &arena, + buf: String::new(), + value_buf: Vec::new(), + config: PhantomData, + }; + + let inner_width = config.width - (state.nesting * 2); + + while let Some(block) = block_iter.next(function) { + let doc = ctx.block_to_doc(config, state, block); + + ctx.buf.clear(); + doc.render_fmt(inner_width, &mut ctx.buf).unwrap(); + + let mut first_line = None; + let mut last_line = None; + + for line in ctx.buf.lines() { + sink.write_indent(state.nesting)?; + sink.write_str(line)?; + let line = sink.commit_line()?; + + first_line = Some(first_line.unwrap_or(line)); + last_line = Some(line); + } + + sink.block_lines(block, (first_line.unwrap(), last_line.unwrap())); + } + + Ok(()) +} + +pub fn format_function_body( + function: &Function, + config: &mut FormatConfig, + mut sink: S, +) -> Result<(), DynError> +where + B: BlockIteratorConfig, + V: ValueFormatter, + L: BlockValueLayout, + S: BlockFormatSink, +{ + let mut state = FormatState { + function, + nesting: 0, + }; + format_function_body_state(config, &mut state, sink) +} + +pub fn format_module( + module: &Module, + config: &mut FormatConfig, + mut sink: S, +) -> Result<(), DynError> +where + B: BlockIteratorConfig, + V: ValueFormatter, + L: BlockValueLayout, + S: BlockFormatSink, +{ + unimplemented!() +} + +impl Function { + + pub fn to_text(&self, config: &mut FormatConfig) -> String + where + B: BlockIteratorConfig, + V: ValueFormatter, + L: BlockValueLayout, + { + let mut sink = StringSink::new(); + format_function_body(self, config, &mut sink).unwrap(); + sink.finalize() + } + + pub fn to_text_standard(&self) -> String { + self.to_text(&mut StandardFormatConfig::default()) + } + + pub fn block_to_text(&self, block: Block, config: &mut FormatConfig) -> String + where + B: BlockIteratorConfig, + V: ValueFormatter, + L: BlockValueLayout, + { + let mut sink = StringSink::new(); + + let arena = Arena::new(); + let mut ctx = FunctionFormatData { + arena: &arena, + buf: String::new(), + value_buf: Vec::new(), + config: PhantomData, + }; + + let mut state = FormatState { + function: self, + nesting: 0, + }; + + let doc = ctx.block_to_doc(config, &mut state, block); + + ctx.buf.clear(); + doc.render_fmt(config.width, &mut ctx.buf).unwrap(); + ctx.buf + } + +} + +impl Module { + + pub fn to_text(&self, config: &mut FormatConfig) -> String + where + B: BlockIteratorConfig, + V: ValueFormatter, + L: BlockValueLayout, + { + let mut sink = StringSink::new(); + format_module(self, config, &mut sink).unwrap(); + sink.finalize() + } + + pub fn to_text_standard(&self) -> String { + self.to_text(&mut StandardFormatConfig::default()) + } + +} + +#[cfg(test)] +mod tests { + use super::{FormatConfig, StandardFormatConfig, StringSink, format_function_body}; + + #[test] + fn woo() { + let ir = crate::parse_function_unwrap(" +a'woo':a'hoo'/1 { + entry(%ret, %thr, %a): + %f1 = a'erlang':a'+'/2; + %f1(%a, 2) => b2 except %thr; + b2(%b): + %f2 = a'erlang':a'/'/2; + %f2(%b, 2) => %ret except %thr; +} +"); + let text = ir.to_text(&mut StandardFormatConfig::default()); + println!("{}", text); + } + +} diff --git a/libeir_ir/src/text/printer/operation.rs b/libeir_ir/src/text/printer/operation.rs new file mode 100644 index 0000000..9bc8268 --- /dev/null +++ b/libeir_ir/src/text/printer/operation.rs @@ -0,0 +1,139 @@ +use pretty::{RefDoc, DocAllocator}; + +use crate::{Block, OpKind, CallKind}; + +use super::{ + FunctionFormatData, FormatConfig, FormatState, + BlockIteratorConfig, ValueFormatter, BlockValueLayout, + get_value_list, +}; + +impl<'a, B, V, L> FunctionFormatData<'a, B, V, L> +where + B: BlockIteratorConfig, + V: ValueFormatter, + L: BlockValueLayout, +{ + + pub fn block_op_to_doc( + &mut self, + config: &FormatConfig, + state: &mut FormatState, + block: Block, + ) -> RefDoc<'a, ()> + { + let arena = self.arena; + + let op = state.function.block_kind(block).unwrap(); + let reads = state.function.block_reads(block); + + let op_doc = match op { + OpKind::Match { branches } => { + let targets_opt = get_value_list(state.function, reads[0]); + let targets_one = &[reads[0]]; + let targets = targets_opt.unwrap_or(targets_one); + + let block = arena.nil(); + + arena.nil() + .append(arena.text("match")) + .append(arena.space()) + .append(block.nest(1).braces()) + }, + OpKind::Call(CallKind::Function) => { + println!("args: {:?}", reads); + let fun_val = self.value_use_to_doc(config, state, reads[0]); + let call_args = arena.intersperse( + reads.iter().skip(3) + .map(|v| self.value_use_to_doc(config, state, *v)), + arena.text(",").append(arena.softline()), + ).nest(1).parens(); + let flow_val = self.value_use_to_doc(config, state, reads[1]); + let exc_val = self.value_use_to_doc(config, state, reads[2]); + arena.nil() + .append(fun_val) + .append(call_args) + .append(arena.space()) + .append(arena.text("=>")) + .append(arena.space()) + .append(flow_val) + .append(arena.space()) + .append(arena.text("except")) + .append(arena.space()) + .append(exc_val) + }, + OpKind::Call(CallKind::ControlFlow) => { + let fun_val = self.value_use_to_doc(config, state, reads[0]); + let call_args = arena.intersperse( + reads.iter().skip(1) + .map(|v| self.value_use_to_doc(config, state, *v)), + arena.text(",").append(arena.softline()), + ).nest(1).parens(); + arena.nil() + .append(fun_val) + .append(call_args) + }, + OpKind::TraceCaptureRaw => { + assert!(reads.len() == 1); + let arg = self.value_use_to_doc(config, state, reads[0]); + arena.nil() + .append(arena.text("trace_capture_raw")) + .append(arena.space()) + .append(arg) + }, + OpKind::UnpackValueList(n) => { + assert!(reads.len() == 2); + let block = self.value_use_to_doc(config, state, reads[0]); + let val = self.value_use_to_doc(config, state, reads[1]); + arena.nil() + .append(arena.text("unpack")) + .append(arena.space()) + .append(val) + .append(arena.space()) + .append(arena.text("arena")) + .append(arena.space()) + .append(arena.as_string(&format!("{}", n))) + .append(arena.space()) + .append(arena.text("=>")) + .append(arena.space()) + .append(block) + }, + OpKind::IfBool => { + match reads.len() { + 3 => { + arena.nil() + .append(arena.text("if_bool")) + .append(arena.space()) + .append(self.value_use_to_doc(config, state, reads[2])) + .append(arena.space()) + .append(self.value_use_to_doc(config, state, reads[0])) + .append(arena.space()) + .append(self.value_use_to_doc(config, state, reads[1])) + }, + 4 => { + arena.nil() + .append(arena.text("if_bool")) + .append(arena.space()) + .append(self.value_use_to_doc(config, state, reads[3])) + .append(arena.space()) + .append(self.value_use_to_doc(config, state, reads[0])) + .append(arena.space()) + .append(self.value_use_to_doc(config, state, reads[1])) + .append(arena.space()) + .append(self.value_use_to_doc(config, state, reads[2])) + }, + _ => panic!(), + } + }, + OpKind::Unreachable => arena.text("unreachable"), + _ => { + println!("UNIMPL: {:?}", op); + arena.text("unknown") + }, + }; + + op_doc.append(arena.text(";")).into_doc() + } + +} + diff --git a/libeir_lowerutils/src/tests.rs b/libeir_lowerutils/src/tests.rs index cdef221..9d4a024 100644 --- a/libeir_lowerutils/src/tests.rs +++ b/libeir_lowerutils/src/tests.rs @@ -4,7 +4,7 @@ use libeir_ir::parse_function_unwrap; fn simple_function() { let fun = parse_function_unwrap(" -foo:bar/1 { +a'foo':a'bar'/1 { entry(%ret, %thr, %a): if_bool %a one two; one(): @@ -21,7 +21,7 @@ foo:bar/1 { #[test] fn nested_functions() { let fun = parse_function_unwrap(" -foo:bar/1 { +a'foo':a'bar'/1 { entry(%ret, %thr, %a): %ret(inner); inner(%iret, %ithr): diff --git a/libeir_passes/Cargo.toml b/libeir_passes/Cargo.toml index ac2b86a..80e7304 100644 --- a/libeir_passes/Cargo.toml +++ b/libeir_passes/Cargo.toml @@ -9,6 +9,7 @@ license = "MIT OR Apache-2.0" libeir_ir = { path = "../libeir_ir" } libeir_intern = { path = "../libeir_intern" } libeir_util_pattern_compiler = { path = "../util/libeir_util_pattern_compiler" } +libeir_util_dot_graph = { path = "../util/libeir_util_dot_graph" } matches = "0.1.8" @@ -16,6 +17,13 @@ cranelift-entity = "0.30.0" petgraph = "0.4" -bumpalo = { git = "https://github.com/hansihe/bumpalo.git", features = [ "collections_hash" ] } +bumpalo = { git = "https://github.com/hansihe/bumpalo", branch = "nightly_alloc", features = ["nightly", "collections"] } fnv = "1.0.3" + +log = "0.4" + +hashbrown = { path = "../../hashbrown", features = ["raw", "nightly"] } + +[dev-dependencies] +simple_logger = "1.0" diff --git a/libeir_passes/src/compile_pattern/erlang_pattern_provider.rs b/libeir_passes/src/compile_pattern/erlang_pattern_provider.rs index b3f0fc6..611d373 100644 --- a/libeir_passes/src/compile_pattern/erlang_pattern_provider.rs +++ b/libeir_passes/src/compile_pattern/erlang_pattern_provider.rs @@ -1,7 +1,8 @@ use cranelift_entity::{ PrimaryMap, EntityList, ListPool, entity_impl }; use libeir_util_pattern_compiler::{ PatternProvider, ExpandedClauseNodes }; -use std::collections::{ HashMap, BTreeMap }; +use hashbrown::HashMap; +use std::collections::BTreeMap; use libeir_ir::{ Function, FunctionBuilder, Value, ValueKind, PrimOp }; use libeir_ir::BinaryEntrySpecifier; diff --git a/libeir_passes/src/compile_pattern/lower_cfg.rs b/libeir_passes/src/compile_pattern/lower_cfg.rs index 7571553..a1dcbb3 100644 --- a/libeir_passes/src/compile_pattern/lower_cfg.rs +++ b/libeir_passes/src/compile_pattern/lower_cfg.rs @@ -72,7 +72,7 @@ pub fn lower_cfg( let mut ctx = LowerCtx { provider, - mapping: BFnvHashMap::with_hasher_in(&bump, Default::default()), + mapping: BFnvHashMap::with_hasher_in(Default::default(), &bump), destinations, }; diff --git a/libeir_passes/src/compile_pattern/mod.rs b/libeir_passes/src/compile_pattern/mod.rs index a189210..757fda3 100644 --- a/libeir_passes/src/compile_pattern/mod.rs +++ b/libeir_passes/src/compile_pattern/mod.rs @@ -1,10 +1,10 @@ use matches::{ matches }; -use std::collections::HashMap; +use bumpalo::{Bump, collections::Vec as BVec}; +use hashbrown::HashMap; -use bumpalo::{Bump, collections::Vec as BVec, collections::HashMap as BHashMap}; use fnv::FnvBuildHasher; -type BFnvHashMap<'bump, K, V> = BHashMap; +type BFnvHashMap<'bump, K, V> = HashMap; use libeir_ir::OpKind; use libeir_ir::{ Value }; @@ -40,6 +40,9 @@ impl CompilePatternPass { } impl FunctionPass for CompilePatternPass { + fn name(&self) -> &str { + "compile_pattern" + } fn run_function_pass(&mut self, b: &mut FunctionBuilder) { self.compile_pattern(b); } diff --git a/libeir_passes/src/lib.rs b/libeir_passes/src/lib.rs index 38d7baf..9ba0245 100644 --- a/libeir_passes/src/lib.rs +++ b/libeir_passes/src/lib.rs @@ -1,6 +1,8 @@ #![deny(warnings)] -use libeir_ir::{ Module, FunctionBuilder }; +use log::{debug, trace}; + +use libeir_ir::{ Module, FunctionBuilder, StandardFormatConfig }; mod compile_pattern; pub use self::compile_pattern::CompilePatternPass; @@ -15,6 +17,7 @@ mod validate; pub use self::validate::ValidatePass; pub trait FunctionPass { + fn name(&self) -> &str; fn run_function_pass(&mut self, b: &mut FunctionBuilder); } @@ -41,14 +44,18 @@ impl PassManager { pub fn run(&mut self, module: &mut Module) { for fun_def in module.function_iter_mut() { let fun = fun_def.function_mut(); - println!("============ {}", fun.ident()); - println!("{}", fun.to_text()); + + let ident = *fun.ident(); + + debug!("============ {}", ident); + trace!("{}", fun.to_text(&mut StandardFormatConfig::default())); + let mut b = FunctionBuilder::new(fun); b.fun().graph_validate_global(); for pass in self.passes.iter_mut() { - //println!("{}", b.fun().to_text()); match pass { PassType::Function(fun_pass) => { + debug!("======== {} FUNCTION_PASS: {}", ident, fun_pass.name()); fun_pass.run_function_pass(&mut b); } } @@ -63,10 +70,15 @@ impl Default for PassManager { fn default() -> Self { let mut man = PassManager::new(); //man.push_function_pass(SimplifyCfgPass::new()); + man.push_function_pass(ValidatePass::new()); man.push_function_pass(CompilePatternPass::new()); + man.push_function_pass(ValidatePass::new()); man.push_function_pass(NaiveInlineClosuresPass::new()); + man.push_function_pass(ValidatePass::new()); man.push_function_pass(SimplifyCfgPass::new()); + man.push_function_pass(ValidatePass::new()); man.push_function_pass(NaiveInlineClosuresPass::new()); + man.push_function_pass(ValidatePass::new()); man } } diff --git a/libeir_passes/src/naive_inline_closures/mod.rs b/libeir_passes/src/naive_inline_closures/mod.rs index 27fcbf1..067fe5a 100644 --- a/libeir_passes/src/naive_inline_closures/mod.rs +++ b/libeir_passes/src/naive_inline_closures/mod.rs @@ -4,6 +4,8 @@ use libeir_ir::{Block, OpKind}; use super::FunctionPass; +use log::trace; + #[cfg(test)] mod tests; @@ -25,9 +27,10 @@ impl NaiveInlineClosuresPass { } impl FunctionPass for NaiveInlineClosuresPass { + fn name(&self) -> &str { + "naive_inline_closures" + } fn run_function_pass(&mut self, b: &mut FunctionBuilder) { - println!("{}", b.fun().to_text()); - println!("Inline Closures"); self.inline_closures(b); } } @@ -69,7 +72,7 @@ impl NaiveInlineClosuresPass { } } - println!("{:?}", self.calls_buf); + trace!("call_sites: {:?}", self.calls_buf); for (block, target) in self.calls_buf.iter().cloned() { // Signature of new entry block has no arguments @@ -96,8 +99,6 @@ impl NaiveInlineClosuresPass { b.block_clear(block); b.op_call_flow(block, new_block, &[]); } - - println!("{}", b.fun().to_text()); } } diff --git a/libeir_passes/src/naive_inline_closures/tests.rs b/libeir_passes/src/naive_inline_closures/tests.rs index 62ba371..dc7a51d 100644 --- a/libeir_passes/src/naive_inline_closures/tests.rs +++ b/libeir_passes/src/naive_inline_closures/tests.rs @@ -1,4 +1,4 @@ -use libeir_ir::parse_function_unwrap; +use libeir_ir::{parse_function_unwrap, StandardFormatConfig}; use crate::FunctionPass; @@ -6,7 +6,7 @@ use crate::FunctionPass; fn inline_basic_function() { let mut fun = parse_function_unwrap(" -foo:fun_shadowing/1 { +a'foo':a'fun_shadowing'/1 { entry(%ret, %thr, %A): b1(); b1(): @@ -27,7 +27,7 @@ foo:fun_shadowing/1 { pass.run_function_pass(&mut b); let after = parse_function_unwrap(" -foo:fun_shadowing/1 { +a'foo':a'fun_shadowing'/1 { entry(%ret, %thr, %A): b1(); b1(): @@ -47,7 +47,7 @@ foo:fun_shadowing/1 { fn inline_nested_functions() { let mut fun = parse_function_unwrap(" -foo:fun_shadowing/1 { +a'foo':a'fun_shadowing'/1 { entry(%ret, %thr, %A): b1(); b1(): @@ -79,10 +79,10 @@ foo:fun_shadowing/1 { let mut pass = super::NaiveInlineClosuresPass::new(); pass.run_function_pass(&mut b); - println!("{}", b.fun().to_text()); + println!("{}", b.fun().to_text(&mut StandardFormatConfig::default())); let after = parse_function_unwrap(" -foo:fun_shadowing/1 { +a'foo':a'fun_shadowing'/1 { entry(%ret, %thr, %A): b1(); b1(): diff --git a/libeir_passes/src/validate.rs b/libeir_passes/src/validate.rs index d216fcf..2e5a848 100644 --- a/libeir_passes/src/validate.rs +++ b/libeir_passes/src/validate.rs @@ -15,6 +15,9 @@ impl ValidatePass { } impl FunctionPass for ValidatePass { + fn name(&self) -> &str { + "validate" + } fn run_function_pass(&mut self, b: &mut FunctionBuilder) { self.err_buf.clear(); b.fun().validate(&mut self.err_buf); diff --git a/libeir_syntax_erl/Cargo.toml b/libeir_syntax_erl/Cargo.toml index 66fb305..ed149ee 100644 --- a/libeir_syntax_erl/Cargo.toml +++ b/libeir_syntax_erl/Cargo.toml @@ -29,7 +29,7 @@ itertools = "0.8" lazy_static = "1.2" either = "1.5" -bumpalo = { git = "https://github.com/hansihe/bumpalo.git", features = [ "collections_hash" ] } +bumpalo = { git = "https://github.com/hansihe/bumpalo", branch = "nightly_alloc", features = ["nightly", "collections"] } # [dependencies.rug] # version = "1.2" diff --git a/libeir_syntax_erl/src/abstr/lower.rs b/libeir_syntax_erl/src/abstr/lower.rs index e9746d1..5cc622d 100644 --- a/libeir_syntax_erl/src/abstr/lower.rs +++ b/libeir_syntax_erl/src/abstr/lower.rs @@ -1,12 +1,26 @@ use libeir_ir::ToPrimitive; use libeir_intern::Ident; -use libeir_diagnostics::DUMMY_SPAN; +use libeir_diagnostics::{ByteSpan, DUMMY_SPAN}; +use libeir_util_parse::MessageIgnore; use std::convert::TryInto; use crate::parser::ast; use libeir_util_parse_listing::ast as aast; +fn to_list_expr(id_gen: &mut ast::NodeIdGenerator, span: ByteSpan, mut list: Vec) -> ast::Expr { + let mut acc = ast::Expr::Nil(ast::Nil(span, id_gen.next())); + for elem in list.drain(..).rev() { + acc = ast::Expr::Cons(ast::Cons { + id: id_gen.next(), + span: span, + head: Box::new(elem), + tail: Box::new(acc), + }); + } + acc +} + pub fn lower(root: &aast::Root) -> ast::Module { let mut toplevel: Vec = Vec::new(); @@ -51,6 +65,23 @@ pub fn lower(root: &aast::Root) -> ast::Module { toplevel.push(ast::TopLevel::Attribute( ast::Attribute::Export(tuple.span, exports))); }, + "compile" => { + let opts = tuple + .entries[3] + .list_iter() + .unwrap() + .map(|item| { + match item { + aast::Item::Atom(ident) => + ast::Expr::Literal(ast::Literal::Atom(id_gen.next(), *ident)), + _ => unimplemented!("{:?}", item), + } + }) + .collect(); + toplevel.push(ast::TopLevel::Attribute( + ast::Attribute::Compile(tuple.span, to_list_expr(&mut id_gen, tuple.span, opts)) + )) + } "spec" => { continue; }, @@ -91,7 +122,12 @@ pub fn lower(root: &aast::Root) -> ast::Module { }; toplevel.push(ast::TopLevel::Record(record)); }, - n => unimplemented!("attribute {}", n), + "behaviour" => { + toplevel.push(ast::TopLevel::Attribute( + ast::Attribute::Behaviour(tuple.span, tuple.entries[3].atom().unwrap()) + )) + }, + n => unimplemented!("attribute {} {:?}", n, tuple), } }, "function" => { @@ -119,15 +155,15 @@ pub fn lower(root: &aast::Root) -> ast::Module { } } - let mut errs = vec![]; + let mut errors = MessageIgnore::new(); let module = ast::Module::new( - &mut errs, + &mut errors, DUMMY_SPAN, &mut id_gen, module_name.unwrap(), toplevel, ); - assert!(errs.len() == 0); + assert!(!errors.failed()); module } @@ -798,34 +834,70 @@ fn integer(item: &aast::Item) -> &aast::Int { #[cfg(test)] mod test { - use libeir_util_parse::{Parser, Parse}; - use libeir_util_parse_listing::parser::{ParseConfig, ParseError}; + use libeir_util_parse::{Parser, Parse, Errors, ArcCodemap, ToDiagnostic, error_tee}; + use libeir_util_parse_listing::parser::ParseError; use libeir_util_parse_listing::ast::Root; + use libeir_diagnostics::Diagnostic; + + use crate::LowerError; + + enum ParseOrLowerError { + Parse(ParseError), + Lower(LowerError), + } + impl ToDiagnostic for ParseOrLowerError { + fn to_diagnostic(&self) -> Diagnostic { + match self { + ParseOrLowerError::Parse(err) => err.to_diagnostic(), + ParseOrLowerError::Lower(err) => err.to_diagnostic(), + } + } + } + impl From for ParseOrLowerError { + fn from(e: ParseError) -> Self { + Self::Parse(e) + } + } + impl From for ParseOrLowerError { + fn from(e: LowerError) -> Self { + Self::Lower(e) + } + } fn parse<'a, T>(input: &'a str) -> T where - T: Parse + T: Parse { - let config = ParseConfig::default(); - let parser = Parser::new(config); - let err = match parser.parse_string::<&'a str, T>(input) { + let codemap = ArcCodemap::default(); + let parser = Parser::new(()); + + let mut errors = Errors::new(); + + match parser.parse_string::<&'a str, T>(&mut errors, &codemap, input) { Ok(ast) => return ast, - Err(err) => err, + Err(()) => { + errors.print(&codemap); + panic!() + }, }; - panic!("{:?}", err); } fn parse_file<'a, T>(input: &'a str) -> T where - T: Parse + T: Parse { - let config = ParseConfig::default(); - let parser = Parser::new(config); - let err = match parser.parse_file::<&'a str, T>(input) { + let codemap = ArcCodemap::default(); + let parser = Parser::new(()); + + let mut errors = Errors::new(); + + match parser.parse_file::<&'a str, T>(&mut errors, &codemap, input) { Ok(ast) => return ast, - Err(err) => err, + Err(()) => { + errors.print(&codemap); + panic!() + }, }; - panic!("{:?}", err); } #[test] @@ -863,19 +935,29 @@ mod test { #[test] fn match_suite() { - let config = ParseConfig::default(); - let codemap = config.codemap.clone(); + let codemap = ArcCodemap::default(); - let parser = Parser::new(config); - let ast = match parser.parse_file::<_, Root>("../test_data/match_SUITE.abstr") { - Ok(ast) => ast, - Err(err) => panic!("{:?}", err), - }; + let parser = Parser::new(()); + + let mut errors: Errors = Errors::new(); + let res = error_tee(&mut errors, |mut errors| { + match parser.parse_file::<_, Root>(&mut errors.make_into_adapter(), &codemap, "../test_data/match_SUITE.abstr") { + Ok(ast) => { + let module = super::lower(&ast); + crate::lower_module(&mut errors.make_into_adapter(), &codemap, &module) + }, + Err(()) => Err(()), + } + }); - let module = super::lower(&ast); + match res { + Ok(_res) => (), + Err(()) => { + errors.print(&codemap); + panic!(); + }, + } - let (res, _errors) = crate::lower_module(&*codemap, &module); - res.unwrap(); } } diff --git a/libeir_syntax_erl/src/lexer/lexer.rs b/libeir_syntax_erl/src/lexer/lexer.rs index 55a2847..16418b8 100644 --- a/libeir_syntax_erl/src/lexer/lexer.rs +++ b/libeir_syntax_erl/src/lexer/lexer.rs @@ -1,4 +1,3 @@ -use std::error::Error; use std::str::FromStr; use libeir_diagnostics::{ByteIndex, ByteOffset, ByteSpan}; @@ -650,7 +649,7 @@ where Err(e) => { return Token::Error(LexicalError::InvalidRadix { span: self.span(), - reason: e.description().to_string(), + reason: e.to_string(), }); } }; @@ -730,7 +729,7 @@ where Ok(f) => Token::Float(f), Err(e) => Token::Error(LexicalError::InvalidFloat { span: self.span(), - reason: e.description().to_string(), + reason: e.to_string(), }), } } diff --git a/libeir_syntax_erl/src/lexer.rs b/libeir_syntax_erl/src/lexer/mod.rs similarity index 100% rename from libeir_syntax_erl/src/lexer.rs rename to libeir_syntax_erl/src/lexer/mod.rs diff --git a/libeir_syntax_erl/src/lib.rs b/libeir_syntax_erl/src/lib.rs index 2516e33..c9c63a8 100644 --- a/libeir_syntax_erl/src/lib.rs +++ b/libeir_syntax_erl/src/lib.rs @@ -1,4 +1,4 @@ -#![deny(warnings)] +//#![deny(warnings)] #![feature(trait_alias)] mod abstr; @@ -10,5 +10,28 @@ mod lower; pub use self::lexer::*; pub use self::parser::*; pub use self::preprocessor::*; -pub use self::lower::lower_module; +pub use self::lower::{lower_module, LowerError}; pub use self::abstr::lower as lower_abstr; + +pub enum ErlangError { + Parser(ParserError), + Lower(LowerError), +} +impl From for ErlangError { + fn from(e: ParserError) -> Self { + ErlangError::Parser(e) + } +} +impl From for ErlangError { + fn from(e: LowerError) -> Self { + ErlangError::Lower(e) + } +} +impl libeir_util_parse::ToDiagnostic for ErlangError { + fn to_diagnostic(&self) -> libeir_diagnostics::Diagnostic { + match self { + ErlangError::Parser(err) => err.to_diagnostic(), + ErlangError::Lower(err) => err.to_diagnostic(), + } + } +} diff --git a/libeir_syntax_erl/src/lower/errors.rs b/libeir_syntax_erl/src/lower/errors.rs index 8502f29..be9c97c 100644 --- a/libeir_syntax_erl/src/lower/errors.rs +++ b/libeir_syntax_erl/src/lower/errors.rs @@ -1,3 +1,4 @@ +use libeir_util_parse::ToDiagnostic; use libeir_diagnostics::{ ByteSpan, Diagnostic, Label }; use super::expr::BinaryTypeName; @@ -60,9 +61,9 @@ pub enum LowerError { } -impl LowerError { +impl ToDiagnostic for LowerError { - pub fn to_diagnostic(&self) -> Diagnostic { + fn to_diagnostic(&self) -> Diagnostic { let msg = self.to_string(); match self { LowerError::NotAllowedInPattern { span } => { diff --git a/libeir_syntax_erl/src/lower/expr/literal.rs b/libeir_syntax_erl/src/lower/expr/literal.rs index 50bec0f..820c518 100644 --- a/libeir_syntax_erl/src/lower/expr/literal.rs +++ b/libeir_syntax_erl/src/lower/expr/literal.rs @@ -23,8 +23,7 @@ pub(super) fn lower_literal(ctx: &mut LowerCtx, b: &mut FunctionBuilder, block: match intern_string_const(*ident, b.cons_mut()) { Ok(cons) => b.value(cons), Err(err) => { - ctx.failed = true; - ctx.errors.push(err); + ctx.error(err); b.value(NilTerm) }, } diff --git a/libeir_syntax_erl/src/lower/expr.rs b/libeir_syntax_erl/src/lower/expr/mod.rs similarity index 100% rename from libeir_syntax_erl/src/lower/expr.rs rename to libeir_syntax_erl/src/lower/expr/mod.rs diff --git a/libeir_syntax_erl/src/lower.rs b/libeir_syntax_erl/src/lower/mod.rs similarity index 93% rename from libeir_syntax_erl/src/lower.rs rename to libeir_syntax_erl/src/lower/mod.rs index ccd8dca..db675a6 100644 --- a/libeir_syntax_erl/src/lower.rs +++ b/libeir_syntax_erl/src/lower/mod.rs @@ -11,6 +11,7 @@ use libeir_ir::{ use libeir_diagnostics::{ByteSpan, CodeMap}; use libeir_intern::{Symbol, Ident}; +use libeir_util_parse::ErrorReceiver; use crate::parser::ast::{Module, NamedFunction, Function, FunctionClause}; @@ -31,7 +32,7 @@ mod expr; use expr::{lower_block, lower_single}; mod errors; -use errors::LowerError; +pub use errors::LowerError; mod exception_handler_stack; use exception_handler_stack::ExceptionHandlerStack; @@ -51,8 +52,7 @@ pub(crate) struct LowerCtx<'a> { sentinel_value: Option, - errors: Vec, - failed: bool, + errors: &'a mut (dyn ErrorReceiver + 'a), val_buf: Vec, @@ -70,18 +70,22 @@ impl<'a> LowerCtx<'a> { /// In a case where, say, we have an unresolved variable, we need /// a dummy value that we can use. /// If this value is used in the resulting IR, it is REQUIRED that - /// `failed` be set to true. + /// `error` be called at least once, which sets the error receiver + /// to the failed state. pub fn sentinel(&self) -> IrValue { self.sentinel_value.unwrap() } pub fn error(&mut self, err: LowerError) { - self.failed = true; - self.errors.push(err); + self.errors.error(err); } pub fn warn(&mut self, err: LowerError) { - self.errors.push(err); + self.errors.warning(err); + } + + pub fn failed(&self) -> bool { + self.errors.is_failed() } pub fn resolve(&mut self, ident: Ident) -> IrValue { @@ -155,11 +159,11 @@ impl<'a> LowerCtx<'a> { } -pub fn lower_module( +pub fn lower_module<'a>( + errors: &'a mut (dyn ErrorReceiver + 'a), codemap: &RwLock, - module: &Module -) -> (Result, Vec) -{ + module: &Module, +) -> Result { // TODO sort functions for more deterministic compilation @@ -174,8 +178,7 @@ pub fn lower_module( sentinel_value: None, - errors: Vec::new(), - failed: false, + errors, val_buf: Vec::new(), @@ -203,21 +206,15 @@ pub fn lower_module( ctx.sentinel_value = Some(sentinel_value); lower_top_function(&mut ctx, &mut builder, function); - println!("FAIL: {:?}", ctx.failed); + println!("FAIL: {:?}", ctx.failed()); } ctx.exc_stack.finish(); - if ctx.failed { - ( - Err(()), - ctx.errors, - ) + if ctx.failed() { + Err(()) } else { - ( - Ok(ir_module), - ctx.errors, - ) + Ok(ir_module) } } diff --git a/libeir_syntax_erl/src/lower/tests.rs b/libeir_syntax_erl/src/lower/tests.rs index bb18665..e6a4f7a 100644 --- a/libeir_syntax_erl/src/lower/tests.rs +++ b/libeir_syntax_erl/src/lower/tests.rs @@ -1,41 +1,34 @@ use crate::ast::*; use crate::*; +use crate::parser::ParseConfig; use crate::lower::lower_module; -use libeir_ir::Module as IrModule; -use libeir_diagnostics::{ColorChoice, Emitter, StandardStreamEmitter}; +use libeir_ir::{Module as IrModule, StandardFormatConfig}; +use libeir_util_parse::{Errors, ArcCodemap}; -fn parse(input: &str, config: ParseConfig) -> (T, Parser) +fn parse(input: &str, config: ParseConfig, codemap: &ArcCodemap) -> T where - T: Parse>, + T: Parse, { let parser = Parser::new(config); - let errs = match parser.parse_string::<&str, T>(input) { - Ok(ast) => return (ast, parser), - Err(errs) => errs, + let mut errors = Errors::new(); + match parser.parse_string::<&str, T>(&mut errors, codemap, input) { + Ok(ast) => return ast, + Err(()) => (), }; - let emitter = StandardStreamEmitter::new(ColorChoice::Auto) - .set_codemap(parser.config.codemap.clone()); - for err in errs.iter() { - emitter.diagnostic(&err.to_diagnostic()).unwrap(); - } + errors.print(codemap); panic!("parse failed"); } fn lower(input: &str, config: ParseConfig) -> Result { - let (parsed, parser): (Module, _) = parse(input, config); + let codemap = ArcCodemap::default(); + // let mut errors = MultiErrors::new(config.codemap.clone()); + let parsed: Module = parse(input, config, &codemap); - let (res, messages) = { - let codemap = &*parser.config.codemap; - lower_module(codemap, &parsed) - }; - - let emitter = StandardStreamEmitter::new(ColorChoice::Auto) - .set_codemap(parser.config.codemap.clone()); - for err in messages.iter() { - emitter.diagnostic(&err.to_diagnostic()).unwrap(); - } + let mut errors = Errors::new(); + let res = lower_module(&mut errors, &codemap, &parsed); + errors.print(&codemap); res } @@ -63,7 +56,7 @@ pat(A, A) -> 1. ParseConfig::default() ).unwrap(); - print!("{}", fun.to_text()); + println!("{}", fun.to_text(&mut StandardFormatConfig::default())); } //#[test] diff --git a/libeir_syntax_erl/src/parser/ast/functions.rs b/libeir_syntax_erl/src/parser/ast/functions.rs index 9ad0d6a..37ba122 100644 --- a/libeir_syntax_erl/src/parser/ast/functions.rs +++ b/libeir_syntax_erl/src/parser/ast/functions.rs @@ -3,11 +3,12 @@ use std::fmt; use std::hash::{Hash, Hasher}; use libeir_diagnostics::{ByteSpan, Diagnostic, Label}; +use libeir_util_parse::ErrorReceiver; use crate::preprocessor::PreprocessorError; use super::{Expr, NodeIdGenerator, NodeId, Ident, Name, Arity, TypeSpec}; -use super::{ParseError, ParserError, TryParseResult}; +use super::ParserError; #[derive(Debug, Copy, Clone)] @@ -315,21 +316,22 @@ impl PartialEq for NamedFunction { } impl NamedFunction { pub fn new( - errs: &mut Vec, + errs: &mut dyn ErrorReceiver, span: ByteSpan, nid: &mut NodeIdGenerator, clauses: Vec, - ) -> TryParseResult { + ) -> Result { debug_assert!(clauses.len() > 0); let (head, rest) = clauses.split_first().unwrap(); if head.name.is_none() { - return Err(to_lalrpop_err!(PreprocessorError::ShowDiagnostic { + errs.error(PreprocessorError::ShowDiagnostic { diagnostic: Diagnostic::new_error("expected named function").with_label( Label::new_primary(head.span) .with_message("this clause has no name, but a name is required here") ), - })); + }.into()); + return Err(()); } let head_span = &head.span; @@ -341,7 +343,7 @@ impl NamedFunction { let mut last_clause = head_span.clone(); for clause in rest.iter() { if clause.name.is_none() { - return Err(to_lalrpop_err!(PreprocessorError::ShowDiagnostic { + errs.error(PreprocessorError::ShowDiagnostic { diagnostic: Diagnostic::new_error("expected named function clause") .with_label( Label::new_primary(clause.span).with_message( @@ -353,7 +355,8 @@ impl NamedFunction { "expected a clause with the same name as this clause" ) ), - })); + }.into()); + return Err(()); } let clause_span = &clause.span; @@ -362,7 +365,7 @@ impl NamedFunction { let clause_arity = clause_params.len(); if clause_name != name { - errs.push(to_lalrpop_err!(ParserError::ShowDiagnostic { + errs.error(ParserError::ShowDiagnostic { diagnostic: Diagnostic::new_error("unterminated function clause") .with_label(Label::new_primary(last_clause.clone()).with_message( "this clause ends with ';', indicating that another clause follows" @@ -371,11 +374,11 @@ impl NamedFunction { Label::new_secondary(clause_span.clone()) .with_message("but this clause has a different name") ), - })); + }); continue; } if clause_arity != arity { - errs.push(to_lalrpop_err!(ParserError::ShowDiagnostic { + errs.error(ParserError::ShowDiagnostic { diagnostic: Diagnostic::new_error("unterminated function clause") .with_label(Label::new_primary(last_clause.clone()).with_message( "this clause ends with ';', indicating that another clause follows" @@ -384,7 +387,7 @@ impl NamedFunction { Label::new_secondary(clause_span.clone()) .with_message("but this clause has a different arity") ), - })); + }); continue; } @@ -416,11 +419,11 @@ impl PartialEq for Lambda { } impl Lambda { pub fn new( - errs: &mut Vec, + errs: &mut dyn ErrorReceiver, span: ByteSpan, nid: &mut NodeIdGenerator, clauses: Vec, - ) -> TryParseResult { + ) -> Result { debug_assert!(clauses.len() > 0); let (head, rest) = clauses.split_first().unwrap(); @@ -437,7 +440,7 @@ impl Lambda { let clause_arity = clause_params.len(); if clause_name.is_some() { - return Err(to_lalrpop_err!(PreprocessorError::ShowDiagnostic { + errs.error(ParserError::ShowDiagnostic { diagnostic: Diagnostic::new_error("mismatched function clause") .with_label( Label::new_primary(clause_span.clone()) @@ -446,11 +449,12 @@ impl Lambda { .with_label(Label::new_secondary(last_clause.clone()).with_message( "but this clause is unnamed, all clauses must share the same name" )) - })); + }); + return Err(()); } if clause_arity != arity { - errs.push(to_lalrpop_err!(ParserError::ShowDiagnostic { + errs.error(ParserError::ShowDiagnostic { diagnostic: Diagnostic::new_error("mismatched function clause") .with_label(Label::new_primary(clause_span.clone()).with_message( "the arity of this clause does not match the previous clause" @@ -459,7 +463,7 @@ impl Lambda { Label::new_secondary(last_clause.clone()) .with_message("this is the previous clause") ), - })); + }); continue; } @@ -495,11 +499,11 @@ impl Function { } pub fn new( - errs: &mut Vec, + errs: &mut dyn ErrorReceiver, span: ByteSpan, nid: &mut NodeIdGenerator, clauses: Vec, - ) -> TryParseResult { + ) -> Result { debug_assert!(clauses.len() > 0); let (head, _rest) = clauses.split_first().unwrap(); diff --git a/libeir_syntax_erl/src/parser/ast.rs b/libeir_syntax_erl/src/parser/ast/mod.rs similarity index 100% rename from libeir_syntax_erl/src/parser/ast.rs rename to libeir_syntax_erl/src/parser/ast/mod.rs diff --git a/libeir_syntax_erl/src/parser/ast/module.rs b/libeir_syntax_erl/src/parser/ast/module.rs index 0d1afc7..1685e95 100644 --- a/libeir_syntax_erl/src/parser/ast/module.rs +++ b/libeir_syntax_erl/src/parser/ast/module.rs @@ -4,6 +4,7 @@ use std::collections::{HashMap, HashSet}; use libeir_diagnostics::{ByteSpan, DUMMY_SPAN, Diagnostic, Label}; use libeir_util_number::ToPrimitive; +use libeir_util_parse::ErrorReceiver; use super::NodeIdGenerator; use super::{Apply, Cons, Var, Nil, Remote, Tuple}; @@ -12,7 +13,7 @@ use super::{Callback, Record, TypeDef, TypeSig, TypeSpec}; use super::{Expr, Ident, Literal, Symbol}; use super::{FunctionClause, FunctionName, NamedFunction, ResolvedFunctionName, LocalFunctionName, PartiallyResolvedFunctionName}; -use super::{ParseError, ParserError}; +use super::ParserError; /// Represents expressions valid at the top level of a module body #[derive(Debug, Clone, PartialEq)] @@ -82,7 +83,7 @@ impl Module { /// /// And a few other similar lints pub fn new( - errs: &mut Vec, + errs: &mut dyn ErrorReceiver, span: ByteSpan, nid: &mut NodeIdGenerator, name: Ident, @@ -122,7 +123,7 @@ impl Module { module.vsn = Some(vsn); continue; } - errs.push(to_lalrpop_err!(ParserError::ShowDiagnostic { + errs.error(ParserError::ShowDiagnostic { diagnostic: Diagnostic::new_error("attribute is already defined") .with_label( Label::new_primary(aspan).with_message("redefinition occurs here") @@ -131,14 +132,14 @@ impl Module { Label::new_secondary(module.vsn.clone().map(|v| v.span()).unwrap()) .with_message("first defined here") ), - })); + }); } TopLevel::Attribute(Attribute::Author(aspan, author)) => { if module.author.is_none() { module.author = Some(author); continue; } - errs.push(to_lalrpop_err!(ParserError::ShowDiagnostic { + errs.error(ParserError::ShowDiagnostic { diagnostic: Diagnostic::new_error("attribute is already defined") .with_label( Label::new_primary(aspan).with_message("redefinition occurs here") @@ -147,14 +148,14 @@ impl Module { Label::new_secondary(module.vsn.clone().map(|v| v.span()).unwrap()) .with_message("first defined here") ), - })); + }); } TopLevel::Attribute(Attribute::OnLoad(aspan, fname)) => { if module.on_load.is_none() { module.on_load = Some(fname.to_local()); continue; } - errs.push(to_lalrpop_err!(ParserError::ShowDiagnostic { + errs.error(ParserError::ShowDiagnostic { diagnostic: Diagnostic::new_error("on_load can only be defined once") .with_label( Label::new_primary(aspan).with_message("redefinition occurs here") @@ -165,7 +166,7 @@ impl Module { ) .with_message("first defined here") ), - })) + }); } TopLevel::Attribute(Attribute::Import(aspan, from_module, mut imports)) => { for import in imports.drain(..) { @@ -178,7 +179,7 @@ impl Module { span: ref prev_span, .. }) => { - errs.push(to_lalrpop_err!(ParserError::ShowDiagnostic { + errs.error(ParserError::ShowDiagnostic { diagnostic: Diagnostic::new_warning("unused import") .with_label(Label::new_primary(aspan).with_message( "this import is a duplicate of a previous import" @@ -187,7 +188,7 @@ impl Module { Label::new_secondary(prev_span.clone()) .with_message("function was first imported here") ), - })); + }); } } } @@ -202,7 +203,7 @@ impl Module { span: ref prev_span, .. }) => { - errs.push(to_lalrpop_err!(ParserError::ShowDiagnostic { + errs.error(ParserError::ShowDiagnostic { diagnostic: Diagnostic::new_warning("already exported") .with_label( Label::new_primary(aspan) @@ -212,7 +213,7 @@ impl Module { Label::new_secondary(prev_span.clone()) .with_message("function was first exported here") ), - })); + }); } } } @@ -234,7 +235,7 @@ impl Module { span: ref prev_span, .. }) => { - errs.push(to_lalrpop_err!(ParserError::ShowDiagnostic { + errs.error(ParserError::ShowDiagnostic { diagnostic: Diagnostic::new_warning("type is already defined") .with_label( Label::new_primary(ty.span) @@ -244,7 +245,7 @@ impl Module { Label::new_secondary(prev_span.clone()) .with_message("type was first defined here") ), - })); + }); } } } @@ -258,7 +259,7 @@ impl Module { span: ref prev_span, .. }) => { - errs.push(to_lalrpop_err!(ParserError::ShowDiagnostic { + errs.error(ParserError::ShowDiagnostic { diagnostic: Diagnostic::new_warning("type already exported") .with_label( Label::new_primary(aspan) @@ -268,7 +269,7 @@ impl Module { Label::new_secondary(prev_span.clone()) .with_message("type was first exported here") ), - })); + }); } } } @@ -282,7 +283,7 @@ impl Module { span: ref prev_span, .. }) => { - errs.push(to_lalrpop_err!(ParserError::ShowDiagnostic { + errs.error(ParserError::ShowDiagnostic { diagnostic: Diagnostic::new_warning("duplicate behaviour declaration") .with_label( Label::new_primary(aspan) @@ -292,7 +293,7 @@ impl Module { Label::new_secondary(prev_span.clone()) .with_message("first declaration occurs here") ), - })); + }); } } } @@ -309,7 +310,7 @@ impl Module { } in &callback.sigs[1..] { if params.len() != arity { - errs.push(to_lalrpop_err!(ParserError::ShowDiagnostic { + errs.error(ParserError::ShowDiagnostic { diagnostic: Diagnostic::new_error("mismatched arity") .with_label( Label::new_primary(sigspan.clone()).with_message( @@ -322,7 +323,7 @@ impl Module { "expected arity was derived from this clause" ) ), - })); + }); } } } @@ -346,7 +347,7 @@ impl Module { .. }) => { println!("PREV: {:?}", a); - errs.push(to_lalrpop_err!(ParserError::ShowDiagnostic { + errs.error(ParserError::ShowDiagnostic { diagnostic: Diagnostic::new_error("cannot redefine callback") .with_label( Label::new_primary(callback.span) @@ -356,7 +357,7 @@ impl Module { Label::new_secondary(a.span.clone()) .with_message("callback first defined here") ), - })); + }); } } } @@ -373,7 +374,7 @@ impl Module { } in &typespec.sigs[1..] { if params.len() != arity { - errs.push(to_lalrpop_err!(ParserError::ShowDiagnostic { + errs.error(ParserError::ShowDiagnostic { diagnostic: Diagnostic::new_error("mismatched arity") .with_label( Label::new_primary(sigspan.clone()).with_message( @@ -386,7 +387,7 @@ impl Module { "expected arity was derived from this clause" ) ), - })); + }); } } } @@ -406,7 +407,7 @@ impl Module { span: ref prev_span, .. }) => { - errs.push(to_lalrpop_err!(ParserError::ShowDiagnostic { + errs.error(ParserError::ShowDiagnostic { diagnostic: Diagnostic::new_error("spec already defined") .with_label( Label::new_primary(typespec.span) @@ -416,7 +417,7 @@ impl Module { Label::new_secondary(prev_span.clone()) .with_message("spec first defined here") ), - })); + }); } } } @@ -426,7 +427,7 @@ impl Module { CompileOptions::from_expr(&module.name, &compile); module.compile = Some(opts); for diagnostic in validation_errs.drain(..) { - errs.push(to_lalrpop_err!(ParserError::ShowDiagnostic { diagnostic })); + errs.error(ParserError::ShowDiagnostic { diagnostic }); } continue; } @@ -435,7 +436,7 @@ impl Module { opts.merge_from_expr(&module.name, &compile) { for diagnostic in validation_errs.drain(..) { - errs.push(to_lalrpop_err!(ParserError::ShowDiagnostic { diagnostic })); + errs.error(ParserError::ShowDiagnostic { diagnostic }); } } } @@ -453,13 +454,13 @@ impl Module { span: ref orig_span, .. }) => { - errs.push(to_lalrpop_err!(ParserError::ShowDiagnostic { + errs.error(ParserError::ShowDiagnostic { diagnostic: Diagnostic::new_warning("redundant deprecation") .with_label(Label::new_primary(dspan.clone()) .with_message("this module is already deprecated by a previous declaration")) .with_label(Label::new_secondary(orig_span.clone()) .with_message("deprecation first declared here")), - })); + }); } Some(Deprecation::Function { .. }) => unreachable!(), }, @@ -470,13 +471,13 @@ impl Module { span: ref mspan, .. }) = module.deprecation { - errs.push(to_lalrpop_err!(ParserError::ShowDiagnostic { + errs.error(ParserError::ShowDiagnostic { diagnostic: Diagnostic::new_warning("redundant deprecation") .with_label(Label::new_primary(*fspan) .with_message("module is deprecated, so deprecating functions is redundant")) .with_label(Label::new_secondary(mspan.clone()) .with_message("module deprecation occurs here")) - })); + }); continue; } @@ -488,13 +489,13 @@ impl Module { span: ref prev_span, .. }) => { - errs.push(to_lalrpop_err!(ParserError::ShowDiagnostic { + errs.error(ParserError::ShowDiagnostic { diagnostic: Diagnostic::new_warning("redundant deprecation") .with_label(Label::new_primary(*fspan) .with_message("this function is already deprecated by a previous declaration")) .with_label(Label::new_secondary(prev_span.clone()) .with_message("deprecation first declared here")) - })); + }); } Some(Deprecation::Module { .. }) => unreachable!(), } @@ -506,7 +507,7 @@ impl Module { println!("CUSTOM ATTR: {:?}", attr); match attr.name.name.as_str().get() { "module" => { - errs.push(to_lalrpop_err!(ParserError::ShowDiagnostic { + errs.error(ParserError::ShowDiagnostic { diagnostic: Diagnostic::new_error("multiple module declarations") .with_label( Label::new_primary(attr.span.clone()) @@ -516,7 +517,7 @@ impl Module { Label::new_secondary(module.name.span.clone()) .with_message("module first declared here") ), - })); + }); continue; } "optional_callbacks" => { @@ -537,7 +538,7 @@ impl Module { span: ref prev_span, .. }) => { - errs.push(to_lalrpop_err!(ParserError::ShowDiagnostic { + errs.error(ParserError::ShowDiagnostic { diagnostic: Diagnostic::new_warning("redefined attribute") .with_label( Label::new_primary(attr.span.clone()) @@ -547,7 +548,7 @@ impl Module { Label::new_secondary(prev_span.clone()) .with_message("previously defined here") ) - })); + }); module.attributes.insert(attr.name.clone(), attr); } } @@ -565,7 +566,7 @@ impl Module { field.value = Some(atom!(nid, undefined)); } if let Some(prev) = fields.get(&field.name) { - errs.push(to_lalrpop_err!(ParserError::ShowDiagnostic { + errs.error(ParserError::ShowDiagnostic { diagnostic: Diagnostic::new_error("duplicate field in record") .with_label( Label::new_primary(field.name.span) @@ -575,7 +576,7 @@ impl Module { Label::new_primary(prev.span) .with_message("previous field") ), - })); + }); } fields.insert(field.name); field_idx_map.insert(field.name, idx); @@ -586,7 +587,7 @@ impl Module { }); } Some(prev) => { - errs.push(to_lalrpop_err!(ParserError::ShowDiagnostic { + errs.error(ParserError::ShowDiagnostic { diagnostic: Diagnostic::new_error("record already defined") .with_label( Label::new_primary(record.span) @@ -596,7 +597,7 @@ impl Module { Label::new_secondary(prev.record.span) .with_message("previously defined here") ), - })); + }); } } } @@ -616,12 +617,12 @@ impl Module { .unwrap_or(false); function.spec = match specs.get(&resolved_name) { None if warn_missing_specs => { - errs.push(to_lalrpop_err!(ParserError::ShowDiagnostic { + errs.error(ParserError::ShowDiagnostic { diagnostic: Diagnostic::new_warning("missing function spec").with_label( Label::new_primary(function.span.clone()) .with_message("expected type spec for this function") ) - })); + }); None } None => None, @@ -633,7 +634,7 @@ impl Module { } Entry::Occupied(initial_def) => { let def = initial_def.into_mut(); - errs.push(to_lalrpop_err!(ParserError::ShowDiagnostic { + errs.error(ParserError::ShowDiagnostic { diagnostic: Diagnostic::new_error( "clauses from the same function should be grouped together" ) @@ -645,7 +646,7 @@ impl Module { Label::new_secondary(def.span.clone()) .with_message("function is first defined here") ) - })); + }); def.clauses.append(&mut function.clauses); } } @@ -662,25 +663,25 @@ impl Module { // Verify on_load function exists if let Some(ref on_load_name) = module.on_load { if !module.functions.contains_key(on_load_name) { - errs.push(to_lalrpop_err!(ParserError::ShowDiagnostic { + errs.error(ParserError::ShowDiagnostic { diagnostic: Diagnostic::new_error("invalid on_load function").with_label( Label::new_primary(on_load_name.span.clone()) .with_message("this function is not defined in this module") ), - })); + }); } } // Check for orphaned type specs for (spec_name, spec) in &specs { if !module.functions.contains_key(&spec_name.to_local()) { - errs.push(to_lalrpop_err!(ParserError::ShowDiagnostic { + errs.error(ParserError::ShowDiagnostic { diagnostic: Diagnostic::new_warning("type spec for undefined function").with_label( Label::new_primary(spec.span.clone()).with_message( "this type spec has no corresponding function definition" ) ) - })); + }); } } diff --git a/libeir_syntax_erl/src/parser/errors.rs b/libeir_syntax_erl/src/parser/errors.rs index cc8673d..22ea411 100644 --- a/libeir_syntax_erl/src/parser/errors.rs +++ b/libeir_syntax_erl/src/parser/errors.rs @@ -1,13 +1,13 @@ use std::convert::From; -use libeir_util_parse::SourceError; +use libeir_util_parse::{SourceError, ToDiagnostic}; use libeir_diagnostics::{ByteIndex, ByteSpan, Diagnostic, Label}; use snafu::Snafu; use crate::lexer::{Token}; use crate::preprocessor::PreprocessorError; -pub type ParseError = lalrpop_util::ParseError; +pub type ParseError = lalrpop_util::ParseError; #[derive(Debug, Snafu)] pub enum ParserError { @@ -75,7 +75,7 @@ impl From for ParserError { } => ParserError::ExtraToken { span: ByteSpan::new(start, end), }, - lalrpop_util::ParseError::User { error: err } => err.into(), + lalrpop_util::ParseError::User { .. } => panic!(), } } } @@ -89,8 +89,10 @@ impl ParserError { _ => None, } } +} - pub fn to_diagnostic(&self) -> Diagnostic { +impl ToDiagnostic for ParserError { + fn to_diagnostic(&self) -> Diagnostic { let span = self.span(); let msg = self.to_string(); match self { diff --git a/libeir_syntax_erl/src/parser/grammar.lalrpop b/libeir_syntax_erl/src/parser/grammar.lalrpop index ee0dd42..d606958 100644 --- a/libeir_syntax_erl/src/parser/grammar.lalrpop +++ b/libeir_syntax_erl/src/parser/grammar.lalrpop @@ -6,10 +6,10 @@ use libeir_util_number::{Integer, ToPrimitive}; use crate::lexer::{Token, DelayedSubstitution, Symbol, Ident}; use crate::preprocessor::PreprocessorError; -use super::ParseError; +use super::{ParserError, ParserErrorReceiver}; use super::ast::*; -grammar( - errs: &mut Vec, +grammar<'a>( + errs: &'a mut ParserErrorReceiver<'a>, nid: &mut NodeIdGenerator, ); @@ -83,7 +83,10 @@ TopLevel: TopLevel = { FunctionDefinition: NamedFunction = { > "." - =>? NamedFunction::new(errs, span!(l, r), nid, clauses), + =>? match NamedFunction::new(errs, span!(l, r), nid, clauses) { + Ok(inner) => Ok(inner), + Err(()) => Err(to_lalrpop_err!(())), + }, }; FunctionHead: FunctionClause = { @@ -208,11 +211,12 @@ DeprecatedAttribute: Attribute = { ])) } other => { - Err(to_lalrpop_err!(PreprocessorError::ShowDiagnostic { + errs.error(PreprocessorError::ShowDiagnostic { diagnostic: Diagnostic::new_warning("invalid deprecated attribute") .with_label(Label::new_primary(span!(l, r)) .with_message("expected 'module', '{module, Flag}', 'Function/Arity', or '{Function/Arity, Flag}'")) - })) + }.into()); + Err(to_lalrpop_err!(())) } } }, @@ -246,11 +250,11 @@ DeprecatedFlag: DeprecatedFlag = { "next_version" => DeprecatedFlag::NextVersion, "next_major_release" => DeprecatedFlag::NextMajorRelease, other => { - errs.push(to_lalrpop_err!(ParserError::ShowDiagnostic { + errs.warning(ParserError::ShowDiagnostic { diagnostic: Diagnostic::new_warning("invalid deprecation flag") .with_label(Label::new_primary(span!(l, r)) .with_message(format!("expected one of 'eventually', 'next_version', or 'next_major_release', got '{}'", other))), - })); + }); DeprecatedFlag::Eventually } } @@ -305,13 +309,12 @@ TypeGuard: TypeGuard = { "is_subtype" => Ok(TypeGuard { span: span!(l, r), var, ty }), name => { - Err(lalrpop_util::ParseError::User { - error: PreprocessorError::ShowDiagnostic { - diagnostic: Diagnostic::new_error("invalid type constraint") - .with_label(Label::new_primary(span!(l, r)) - .with_message(format!("expected constraint in the form `Name :: Type`"))), - } - }) + errs.error(PreprocessorError::ShowDiagnostic { + diagnostic: Diagnostic::new_error("invalid type constraint") + .with_label(Label::new_primary(span!(l, r)) + .with_message(format!("expected constraint in the form `Name :: Type`"))), + }.into()); + Err(to_lalrpop_err!(())) } } }, @@ -657,7 +660,7 @@ Fun: Expr = { "fun" > "end" =>? { match Function::new(errs, span!(l, r), nid, clauses) { Ok(fun) => Ok(Expr::Fun(fun)), - Err(err) => Err(err) + Err(()) => Err(to_lalrpop_err!(())), } }, }; @@ -1032,7 +1035,7 @@ Float: f64 = { extern { type Location = ByteIndex; - type Error = PreprocessorError; + type Error = (); enum Token { // Docs diff --git a/libeir_syntax_erl/src/parser.rs b/libeir_syntax_erl/src/parser/mod.rs similarity index 80% rename from libeir_syntax_erl/src/parser.rs rename to libeir_syntax_erl/src/parser/mod.rs index 0eb3328..46eb353 100644 --- a/libeir_syntax_erl/src/parser.rs +++ b/libeir_syntax_erl/src/parser/mod.rs @@ -13,9 +13,12 @@ macro_rules! to_lalrpop_err ( ($error:expr) => (lalrpop_util::ParseError::User { error: $error }) ); +type ParserErrorReceiver<'a> = dyn ErrorReceiver + 'a; + #[cfg_attr(rustfmt, rustfmt_skip)] #[allow(unknown_lints)] #[allow(clippy)] +#[allow(unused_parens)] pub(crate) mod grammar { // During the build step, `build.rs` will output the generated parser to `OUT_DIR` to avoid // adding it to the source directory, so we just directly include the generated parser here. @@ -39,15 +42,12 @@ pub mod visitor; use std::collections::VecDeque; use std::path::PathBuf; -use std::sync::{Arc, RwLock}; -use libeir_util_parse::{Scanner, Source, SourceError, ParserConfig}; +use libeir_util_parse::{Scanner, Source, SourceError, ErrorReceiver, ArcCodemap, error_tee}; use libeir_util_parse::{Parser as GParser, Parse as GParse}; pub type Parser = GParser; -pub trait Parse = GParse>; - -use libeir_diagnostics::CodeMap; +pub trait Parse = GParse; use crate::lexer::Lexer; use crate::preprocessor::{MacroContainer, Preprocessed, Preprocessor}; @@ -59,34 +59,20 @@ pub use self::errors::*; pub type ParseResult = Result>; pub struct ParseConfig { - pub codemap: Arc>, pub warnings_as_errors: bool, pub no_warn: bool, pub include_paths: VecDeque, pub code_paths: VecDeque, pub macros: Option, } -impl ParserConfig for ParseConfig { - fn codemap(&self) -> &Arc> { - &self.codemap - } -} impl ParseConfig { - pub fn new(codemap: Arc>) -> Self { - ParseConfig { - codemap, - warnings_as_errors: false, - no_warn: false, - include_paths: VecDeque::new(), - code_paths: VecDeque::new(), - macros: None, - } + pub fn new() -> Self { + ParseConfig::default() } } impl Default for ParseConfig { fn default() -> Self { ParseConfig { - codemap: Arc::new(RwLock::new(CodeMap::new())), warnings_as_errors: false, no_warn: false, include_paths: VecDeque::new(), @@ -98,75 +84,98 @@ impl Default for ParseConfig { impl GParse for ast::Module { type Parser = grammar::ModuleParser; - type Error = Vec; + type Error = ParserError; type Config = ParseConfig; type Token = Preprocessed; fn file_map_error(err: SourceError) -> Self::Error { - vec![err.into()] + err.into() } - fn parse(config: &ParseConfig, source: S) -> ParseResult + fn parse<'a, S>( + config: &ParseConfig, + codemap: &ArcCodemap, + err: &'a mut ParserErrorReceiver<'a>, + source: S, + ) -> Result where S: Source, { - let scanner = Scanner::new(source); - let lexer = Lexer::new(scanner); - let tokens = Preprocessor::new(config, lexer); - Self::parse_tokens(tokens) + error_tee(err, |mut errors| { + let scanner = Scanner::new(source); + let lexer = Lexer::new(scanner); + error_tee(&mut errors.clone().make_into_adapter(), |preproc_errors| { + let tokens = Preprocessor::new(config, codemap.clone(), lexer, preproc_errors); + Self::parse_tokens(&mut errors, tokens) + }) + }) } - fn parse_tokens>(tokens: S) -> ParseResult { + fn parse_tokens<'a, S: IntoIterator>( + err: &'a mut ParserErrorReceiver<'a>, + tokens: S, + ) -> Result + { let mut nid = NodeIdGenerator::new(); - let mut errs = Vec::new(); let result = Self::Parser::new() - .parse(&mut errs, &mut nid, tokens) - .map_err(|e| e.map_error(|ei| ei.into())); - to_parse_result(errs, result) + .parse(err, &mut nid, tokens); + to_parse_result(err, result) } } impl GParse for ast::Expr { type Parser = grammar::ExprParser; - type Error = Vec; + type Error = ParserError; type Config = ParseConfig; type Token = Preprocessed; fn file_map_error(err: SourceError) -> Self::Error { - vec![err.into()] + err.into() } - fn parse(config: &ParseConfig, source: S) -> ParseResult + fn parse( + config: &ParseConfig, + codemap: &ArcCodemap, + err: &mut ParserErrorReceiver, + source: S, + ) -> Result where S: Source, { - let scanner = Scanner::new(source); - let lexer = Lexer::new(scanner); - let tokens = Preprocessor::new(config, lexer); - Self::parse_tokens(tokens) + error_tee(err, |mut errors| { + let scanner = Scanner::new(source); + let lexer = Lexer::new(scanner); + error_tee(&mut errors.clone().make_into_adapter(), |preproc_errors| { + let tokens = Preprocessor::new(config, codemap.clone(), lexer, preproc_errors); + Self::parse_tokens(&mut errors, tokens) + }) + }) } - fn parse_tokens>(tokens: S) -> ParseResult { + fn parse_tokens>( + err: &mut ParserErrorReceiver, + tokens: S, + ) -> Result + { let mut nid = NodeIdGenerator::new(); - let mut errs = Vec::new(); let result = Self::Parser::new() - .parse(&mut errs, &mut nid, tokens) - .map_err(|e| e.map_error(|ei| ei.into())); - to_parse_result(errs, result) + .parse(err, &mut nid, tokens); + to_parse_result(err, result) } } -fn to_parse_result(mut errs: Vec, result: Result) -> ParseResult { +fn to_parse_result(errs: &mut ParserErrorReceiver, result: Result) -> Result { match result { Ok(ast) => { - if errs.len() > 0 { - return Err(errs.drain(0..).map(ParserError::from).collect()); + if (*errs).is_failed() { + return Err(()); } Ok(ast) } + Err(lalrpop_util::ParseError::User { .. }) => Err(()), Err(err) => { - errs.push(err); - Err(errs.drain(0..).map(ParserError::from).collect()) + errs.error(err.into()); + Err(()) } } } @@ -179,51 +188,44 @@ mod test { use super::*; use libeir_diagnostics::ByteSpan; - use libeir_diagnostics::{ColorChoice, Emitter, StandardStreamEmitter}; + use libeir_util_parse::{Errors, ErrorOrWarning, ArcCodemap}; use crate::lexer::{Ident, Symbol}; use crate::preprocessor::PreprocessorError; - fn parse<'a, T>(input: &'a str) -> T + fn parse<'a, T>(config: ParseConfig, codemap: &ArcCodemap, input: &'a str) -> T where - T: Parse>, + T: Parse, { - let config = ParseConfig::default(); + let mut errors = Errors::new(); let parser = Parser::new(config); - let errs = match parser.parse_string::<&'a str, T>(input) { + match parser.parse_string::<&'a str, T>(&mut errors, codemap, input) { Ok(ast) => return ast, Err(errs) => errs, }; - let emitter = StandardStreamEmitter::new(ColorChoice::Auto) - .set_codemap(parser.config.codemap.clone()); - for err in errs.iter() { - emitter.diagnostic(&err.to_diagnostic()).unwrap(); - } + + errors.print(codemap); panic!("parse failed"); } - fn parse_fail(input: &'static str) -> Vec + fn parse_fail(config: ParseConfig, codemap: &ArcCodemap, input: &'static str) -> Errors where - T: Parse>, + T: Parse, { - let config = ParseConfig::default(); + let mut errors = Errors::new(); let parser = Parser::new(config); - match parser.parse_string::<&'static str, T>(input) { - Err(errs) => errs, + match parser.parse_string::<&'static str, T>(&mut errors, codemap, input) { + Err(()) => errors, _ => panic!("expected parse to fail, but it succeeded!"), } } macro_rules! module { - ($nid:expr, $name:expr, $body:expr) => {{ - let mut errs = Vec::new(); + ($codemap:expr, $nid:expr, $name:expr, $body:expr) => {{ + let mut errs = Errors::new(); let module = Module::new(&mut errs, ByteSpan::default(), $nid, $name, $body); - if errs.len() > 0 { - let emitter = StandardStreamEmitter::new(ColorChoice::Auto); - for err in errs.drain(..) { - let err = ParserError::from(err); - emitter.diagnostic(&err.to_diagnostic()).unwrap(); - } + if errs.is_failed() { + errs.print($codemap); panic!("failed to create expected module!"); } module @@ -232,15 +234,20 @@ mod test { #[test] fn parse_empty_module() { - let result: Module = parse("-module(foo)."); + let codemap = ArcCodemap::default(); + let config = ParseConfig::default(); + let result: Module = parse(config, &codemap, "-module(foo)."); let mut nid = NodeIdGenerator::new(); - let expected = module!(&mut nid, ident!("foo"), vec![]); + let expected = module!(&codemap, &mut nid, ident!("foo"), vec![]); assert_eq!(result, expected); } #[test] fn parse_module_with_multi_clause_function() { + let codemap = ArcCodemap::default(); + let config = ParseConfig::default(); let result: Module = parse( + config, &codemap, "-module(foo). foo([], Acc) -> Acc; @@ -275,13 +282,16 @@ foo([H|T], Acc) -> foo(T, [H|Acc]). clauses, spec: None, })); - let expected = module!(nid, ident!(foo), body); + let expected = module!(&codemap, nid, ident!(foo), body); assert_eq!(result, expected); } #[test] fn parse_if_expressions() { + let codemap = ArcCodemap::default(); + let config = ParseConfig::default(); let result: Module = parse( + config, &codemap, "-module(foo). unless(false) -> @@ -376,13 +386,16 @@ unless(Value) -> clauses, spec: None, })); - let expected = module!(nid, ident!(foo), body); + let expected = module!(&codemap, nid, ident!(foo), body); assert_eq!(result, expected); } #[test] fn parse_case_expressions() { + let codemap = ArcCodemap::default(); + let config = ParseConfig::default(); let result: Module = parse( + config, &codemap, "-module(foo). typeof(Value) -> @@ -453,13 +466,16 @@ typeof(Value) -> clauses, spec: None, })); - let expected = module!(nid, ident!(foo), body); + let expected = module!(&codemap, nid, ident!(foo), body); assert_eq!(result, expected); } #[test] fn parse_receive_expressions() { + let codemap = ArcCodemap::default(); + let config = ParseConfig::default(); let result: Module = parse( + config, &codemap, "-module(foo). loop(State, Timeout) -> @@ -539,13 +555,16 @@ loop(State, Timeout) -> clauses, spec: None, })); - let expected = module!(nid, ident!(foo), body); + let expected = module!(&codemap, nid, ident!(foo), body); assert_eq!(result, expected); } #[test] fn parse_preprocessor_if() { + let codemap = ArcCodemap::default(); + let config = ParseConfig::default(); let result: Module = parse( + config, &codemap, "-module(foo). -define(TEST, true). -define(OTP_VERSION, 21). @@ -610,7 +629,7 @@ system_version() -> spec: None, }; body.push(TopLevel::Function(system_version_fun)); - let expected = module!(nid, ident!(foo), body); + let expected = module!(&codemap, nid, ident!(foo), body); assert_eq!(result, expected); } @@ -621,16 +640,19 @@ system_version() -> // a writer everywhere. You can change this for testing by // going to the Preprocessor and finding the line where we handle // the warning directive and toggle the config flag + let codemap = ArcCodemap::default(); + let config = ParseConfig::default(); let mut errs = parse_fail::( + config, &codemap, "-module(foo). -warning(\"this is a compiler warning\"). -error(\"this is a compiler error\"). ", ); - match errs.pop() { - Some(ParserError::Preprocessor { + match errs.errors.pop() { + Some(ErrorOrWarning::Error(ParserError::Preprocessor { source: PreprocessorError::CompilerError { .. }, - }) => (), + })) => (), Some(err) => panic!( "expected compiler error, but got a different error instead: {:?}", err @@ -641,7 +663,10 @@ system_version() -> #[test] fn parse_try() { + let codemap = ArcCodemap::default(); + let config = ParseConfig::default(); let result: Module = parse( + config, &codemap, "-module(foo). example(File) -> @@ -715,13 +740,16 @@ example(File) -> clauses, spec: None, })); - let expected = module!(nid, ident!(foo), body); + let expected = module!(&codemap, nid, ident!(foo), body); assert_eq!(result, expected); } #[test] fn parse_try2() { + let codemap = ArcCodemap::default(); + let config = ParseConfig::default(); let _result: Module = parse( + config, &codemap, "-module(foo). example(File < 2) -> @@ -749,6 +777,7 @@ exw(File) -> #[test] fn parse_numbers() { let _result: Module = parse( + ParseConfig::default(), &ArcCodemap::default(), "-module(foo). foo(F) -> F-1+1/1*1. @@ -761,6 +790,7 @@ bar() -> - 2. #[test] fn parse_spec() { let _result: Module = parse( + ParseConfig::default(), &ArcCodemap::default(), "-module(foo). -spec bar() -> number. @@ -772,6 +802,7 @@ bar() -> 2. #[test] fn parse_binary_spec_constant() { let _result: Module = parse( + ParseConfig::default(), &ArcCodemap::default(), "-module(foo). -type txs_hash() :: <<_:(32 * 8)>>. @@ -788,7 +819,7 @@ bar() -> 2. let mut string = String::new(); file.unwrap().read_to_string(&mut string).unwrap(); - let _result: Module = parse(&string); + let _result: Module = parse(ParseConfig::default(), &ArcCodemap::default(), &string); } } diff --git a/libeir_syntax_erl/src/parser/visitor.rs b/libeir_syntax_erl/src/parser/visitor/mod.rs similarity index 100% rename from libeir_syntax_erl/src/parser/visitor.rs rename to libeir_syntax_erl/src/parser/visitor/mod.rs diff --git a/libeir_syntax_erl/src/preprocessor/errors.rs b/libeir_syntax_erl/src/preprocessor/errors.rs index 15068ff..1a1fe19 100644 --- a/libeir_syntax_erl/src/preprocessor/errors.rs +++ b/libeir_syntax_erl/src/preprocessor/errors.rs @@ -4,7 +4,7 @@ use libeir_diagnostics::{ByteSpan, Diagnostic, Label}; use itertools::Itertools; use snafu::Snafu; -use libeir_util_parse::{PathVariableSubstituteError, SourceError}; +use libeir_util_parse::{PathVariableSubstituteError, SourceError, ToDiagnostic}; use crate::lexer::{LexicalError, LexicalToken, TokenConvertError}; use crate::parser::ParserError; @@ -29,7 +29,7 @@ pub enum PreprocessorError { #[snafu(display("unable to parse constant expression"))] ParseError { span: ByteSpan, - errors: Vec, + inner: Box, }, #[snafu(display("{}", reason))] @@ -143,15 +143,14 @@ impl PreprocessorError { PreprocessorError::ShowDiagnostic { diagnostic } => diagnostic.clone(), PreprocessorError::Lexical { source } => source.to_diagnostic(), PreprocessorError::Source { source } => source.to_diagnostic(), - PreprocessorError::ParseError { errors, .. } => { + PreprocessorError::ParseError { inner, .. } => { let mut d = Diagnostic::new_error(msg) .with_label(Label::new_primary(span.unwrap()) .with_message("invalid constant expression")); - for err in errors.iter() { - let err = err.to_diagnostic(); - for label in err.labels { - d = d.with_label(label); - } + + let err = inner.to_diagnostic(); + for label in err.labels { + d = d.with_label(label); } d } diff --git a/libeir_syntax_erl/src/preprocessor.rs b/libeir_syntax_erl/src/preprocessor/mod.rs similarity index 94% rename from libeir_syntax_erl/src/preprocessor.rs rename to libeir_syntax_erl/src/preprocessor/mod.rs index 4c670c2..bd7f97f 100644 --- a/libeir_syntax_erl/src/preprocessor.rs +++ b/libeir_syntax_erl/src/preprocessor/mod.rs @@ -19,6 +19,6 @@ use libeir_diagnostics::ByteIndex; use crate::lexer::Token; /// The result produced by the preprocessor -pub type Preprocessed = std::result::Result<(ByteIndex, Token, ByteIndex), PreprocessorError>; +pub type Preprocessed = std::result::Result<(ByteIndex, Token, ByteIndex), ()>; type Result = std::result::Result; diff --git a/libeir_syntax_erl/src/preprocessor/preprocessor.rs b/libeir_syntax_erl/src/preprocessor/preprocessor.rs index be23c7a..803fdbf 100644 --- a/libeir_syntax_erl/src/preprocessor/preprocessor.rs +++ b/libeir_syntax_erl/src/preprocessor/preprocessor.rs @@ -9,7 +9,7 @@ use snafu::ResultExt; use libeir_diagnostics::{ByteIndex, ByteSpan, CodeMap, Diagnostic, Label}; use libeir_diagnostics::{Emitter, StandardStreamEmitter}; -use libeir_util_parse::Source; +use libeir_util_parse::{Source, ErrorReceiver, ErrorReceiverTee}; use crate::lexer::{symbols, IdentToken, Lexed, LexicalToken, Symbol, Token, DelayedSubstitution}; @@ -19,10 +19,25 @@ use crate::parser::ParseConfig; use super::macros::Stringify; use super::token_reader::{TokenBufferReader, TokenReader, TokenStreamReader}; use super::{Directive, MacroContainer, MacroIdent, MacroCall, MacroDef}; -use super::{Preprocessed, PreprocessorError, Result}; +use super::{Preprocessed, PreprocessorError, Result as PResult}; use super::errors; -pub struct Preprocessor { +type Errors<'a> = ErrorReceiverTee<'a, PreprocessorError, PreprocessorError>; + +macro_rules! error_into { + ($errors:expr, $result:expr) => { + match $result { + Ok(inner) => Ok(inner), + Err(error) => { + $errors.error(error.into()); + Err(()) + }, + } + }; +} + +pub struct Preprocessor<'a, Reader: TokenReader> { + errors: Errors<'a>, codemap: Arc>, reader: Reader, can_directive_start: bool, @@ -36,12 +51,11 @@ pub struct Preprocessor { warnings_as_errors: bool, no_warn: bool, } -impl Preprocessor> +impl<'a, S> Preprocessor<'a, TokenStreamReader> where S: Source, { - pub fn new(config: &ParseConfig, tokens: Lexer) -> Self { - let codemap = config.codemap.clone(); + pub fn new(config: &ParseConfig, codemap: Arc>, tokens: Lexer, errors: Errors<'a>) -> Self { let reader = TokenStreamReader::new(codemap.clone(), tokens); let code_paths = config.code_paths.clone(); let include_paths = config.include_paths.clone(); @@ -60,6 +74,7 @@ where ); Preprocessor { + errors, codemap, reader, can_directive_start: true, @@ -75,7 +90,7 @@ where } } } -impl Preprocessor +impl<'a, R, S> Preprocessor<'a, R> where R: TokenReader, { @@ -83,6 +98,7 @@ where let codemap = self.codemap.clone(); let reader = TokenBufferReader::new(codemap.clone(), tokens); Preprocessor { + errors: self.errors.clone(), codemap, reader, can_directive_start: false, @@ -102,7 +118,7 @@ where self.branches.iter().any(|b| !b.entered) } - fn next_token(&mut self) -> Result> { + fn next_token(&mut self) -> Result, ()> { loop { if let Some(token) = self.expanded_tokens.pop_front() { return Ok(Some(token)); @@ -125,13 +141,13 @@ where } } if !self.ignore() { - if let Some(m) = self.reader.try_read_macro_call(&self.macros)? { + if let Some(m) = error_into!(self.errors, self.reader.try_read_macro_call(&self.macros))? { self.macro_calls.insert(m.span().start(), m.clone()); - self.expanded_tokens = self.expand_macro(m)?; + self.expanded_tokens = error_into!(self.errors, self.expand_macro(m))?; continue; } } - if let Some(token) = self.reader.try_read_token()? { + if let Some(token) = error_into!(self.errors, self.reader.try_read_token())? { if self.ignore() { continue; } @@ -148,7 +164,7 @@ where Ok(None) } - fn expand_macro(&self, call: MacroCall) -> Result> { + fn expand_macro(&self, call: MacroCall) -> PResult> { if let Some(expanded) = self.try_expand_predefined_macro(&call)? { Ok(vec![expanded].into()) } else { @@ -156,7 +172,7 @@ where } } - fn try_expand_predefined_macro(&self, call: &MacroCall) -> Result> { + fn try_expand_predefined_macro(&self, call: &MacroCall) -> PResult> { let expanded = match call.name().as_str().get() { "FILE" => { let span = call.span(); @@ -201,7 +217,7 @@ where Ok(Some(expanded)) } - fn expand_userdefined_macro(&self, call: MacroCall) -> Result> { + fn expand_userdefined_macro(&self, call: MacroCall) -> PResult> { let definition = match self.macros.get(&call) { None => return Err(PreprocessorError::UndefinedMacro { call }), Some(def) => def, @@ -263,7 +279,7 @@ where &self, bindings: HashMap, replacement: &[LexicalToken], - ) -> Result> { + ) -> PResult> { let mut expanded = VecDeque::new(); let replacement_tokens: VecDeque<_> = replacement.iter().map(|t| Ok(t.clone())).collect(); let mut reader = TokenBufferReader::new(self.codemap.clone(), replacement_tokens); @@ -305,8 +321,8 @@ where Ok(expanded) } - fn try_read_directive(&mut self) -> Result> { - let directive: Directive = if let Some(directive) = self.reader.try_read()? { + fn try_read_directive(&mut self) -> Result, ()> { + let directive: Directive = if let Some(directive) = error_into!(self.errors, self.reader.try_read())? { directive } else { return Ok(None); @@ -323,15 +339,14 @@ where MacroDef::String(d.name.symbol())); } Directive::Include(ref d) if !ignore => { - let path = d.include(&self.include_paths) - .context(errors::BadDirective)?; - self.reader.inject_include(path)?; + let path = error_into!(self.errors, d.include(&self.include_paths) + .context(errors::BadDirective))?; + error_into!(self.errors, self.reader.inject_include(path))?; } Directive::IncludeLib(ref d) if !ignore => { - let path = d.include_lib(&self.code_paths) - .context(errors::BadDirective)?; - println!("{:?}", path); - self.reader.inject_include(path)?; + let path = error_into!(self.errors, d.include_lib(&self.code_paths) + .context(errors::BadDirective))?; + error_into!(self.errors, self.reader.inject_include(path))?; } Directive::Define(ref d) if !ignore => { self.macros @@ -353,10 +368,10 @@ where self.branches.push(Branch::new(entered)); } Directive::Else(_) => match self.branches.last_mut() { - None => return Err(PreprocessorError::OrphanedElse { directive }), + None => return error_into!(self.errors, Err(PreprocessorError::OrphanedElse { directive })), Some(branch) => { match branch.switch_to_else_branch() { - Err(_) => return Err(PreprocessorError::OrphanedElse { directive }), + Err(_) => return error_into!(self.errors, Err(PreprocessorError::OrphanedElse { directive })), Ok(_) => (), }; } @@ -364,7 +379,7 @@ where Directive::Elif(ref d) => { // Treat this like -endif followed by -if(Cond) match self.branches.pop() { - None => return Err(PreprocessorError::OrphanedElse { directive }), + None => return error_into!(self.errors, Err(PreprocessorError::OrphanedElse { directive })), Some(_) => { let entered = self.eval_conditional(d.span(), d.condition.clone())?; self.branches.push(Branch::new(entered)); @@ -372,13 +387,13 @@ where } } Directive::Endif(_) => match self.branches.pop() { - None => return Err(PreprocessorError::OrphanedEnd { directive }), + None => return error_into!(self.errors, Err(PreprocessorError::OrphanedEnd { directive })), Some(_) => (), }, Directive::Error(ref d) if !ignore => { let span = d.span(); let err = d.message.symbol().as_str().get().to_string(); - return Err(PreprocessorError::CompilerError { span: Some(span), reason: err }); + return error_into!(self.errors, Err(PreprocessorError::CompilerError { span: Some(span), reason: err })); } Directive::Warning(ref d) if !ignore => { if self.no_warn { @@ -387,7 +402,7 @@ where if self.warnings_as_errors { let span = d.span(); let err = d.message.symbol().as_str().get().to_string(); - return Err(PreprocessorError::CompilerError { span: Some(span), reason: err }); + return error_into!(self.errors, Err(PreprocessorError::CompilerError { span: Some(span), reason: err })); } let span = d.span(); let warn = d.message.symbol().as_str().get(); @@ -406,30 +421,55 @@ where Ok(Some(directive)) } - fn eval_conditional(&self, span: ByteSpan, condition: VecDeque) -> Result { + fn eval_conditional( + &mut self, + span: ByteSpan, + condition: VecDeque + ) -> Result + { use crate::lexer::Ident; use crate::parser::ast::{Expr, Literal}; use crate::parser::Parse; use crate::preprocessor::evaluator; - let pp = self.clone_with(condition); - let result = Expr::parse_tokens(pp); - match result { - Ok(expr) => match evaluator::eval(expr)? { - Expr::Literal(Literal::Atom(_, Ident { ref name, .. })) if *name == symbols::True => { - Ok(true) - } - Expr::Literal(Literal::Atom(_, Ident { ref name, .. })) if *name == symbols::False => { - Ok(false) - } - _other => return Err(PreprocessorError::InvalidConditional { span }), + let result = { + let mut adapter = self.errors.make_adapter( + move |v| PreprocessorError::ParseError { + span, + inner: Box::new(v), + }, + move |v| PreprocessorError::ParseError { + span, + inner: Box::new(v), + }, + ); + let pp = self.clone_with(condition); + // TODO add adapter which adds PreprocessorError between + //let adapter = ErrorReceiverAdapter::new( + // &mut self.errors, + //); + Expr::parse_tokens(&mut adapter, pp) + }; + match evaluator::eval(result?) { + Ok(Expr::Literal(Literal::Atom(_, Ident { ref name, .. }))) if *name == symbols::True => { + Ok(true) + } + Ok(Expr::Literal(Literal::Atom(_, Ident { ref name, .. }))) if *name == symbols::False => { + Ok(false) + } + Err(err) => { + self.errors.error(err.into()); + return Err(()); + } + _other => { + self.errors.error(PreprocessorError::InvalidConditional { span }.into()); + return Err(()); }, - Err(errors) => return Err(PreprocessorError::ParseError { span, errors }), } } } -impl Iterator for Preprocessor +impl<'a, R, S> Iterator for Preprocessor<'a, R> where R: TokenReader, { @@ -437,7 +477,7 @@ where fn next(&mut self) -> Option { match self.next_token() { - Err(e) => Some(Err(e)), + Err(()) => Some(Err(())), Ok(None) => None, Ok(Some(token)) => Some(Ok(token.into())), } @@ -456,7 +496,7 @@ impl Branch { entered, } } - pub fn switch_to_else_branch(&mut self) -> Result<()> { + pub fn switch_to_else_branch(&mut self) -> PResult<()> { if !self.then_branch { return Err(PreprocessorError::OrphanedBranchElse); } diff --git a/libeir_tests/Cargo.toml b/libeir_tests/Cargo.toml index 722f7d5..beb587f 100644 --- a/libeir_tests/Cargo.toml +++ b/libeir_tests/Cargo.toml @@ -13,3 +13,4 @@ libeir_diagnostics = { path = "../libeir_diagnostics" } libeir_intern = { path = "../libeir_intern" } libeir_interpreter = { path = "../libeir_interpreter" } libeir_lowerutils = { path = "../libeir_lowerutils" } +libeir_util_parse = { path = "../util/libeir_util_parse" } diff --git a/libeir_tests/src/control_flow/get_values.rs b/libeir_tests/src/control_flow/get_values.rs new file mode 100644 index 0000000..4f56e01 --- /dev/null +++ b/libeir_tests/src/control_flow/get_values.rs @@ -0,0 +1,46 @@ +use crate::lower; + +use libeir_ir::{ FunctionIdent }; +use libeir_syntax_erl::{ ParseConfig }; +use libeir_intern::Ident; +use libeir_passes::PassManager; + +use libeir_interpreter::{ VMState, Term }; + +//-module('Elixir.Keyword'). +// +//get_values(_keywords@1, _key@1) +// when +// is_list(_keywords@1) +// andalso +// is_atom(_key@1) -> +// _fun@1 = +// fun({_key@2,_val@1}) when _key@1 =:= _key@2 -> +// {true,_val@1}; +// ({_,_}) -> +// false +// end, +// lists:filtermap(_fun@1, _keywords@1). + +#[test] +fn get_values() { + + let mut eir_mod = lower( + "-module('Elixir.Keyword'). + +get_values(_key@1) -> + A = fun(_key@2) -> + _key@1 =:= _key@2 + end, + A. +", + ParseConfig::default() + ).unwrap(); + + let mut pass_manager = PassManager::default(); + pass_manager.run(&mut eir_mod); + + for fun in eir_mod.function_iter() { + let _ = fun.function().live_values(); + } +} diff --git a/libeir_tests/src/control_flow/mod.rs b/libeir_tests/src/control_flow/mod.rs index 9b552b3..eda4766 100644 --- a/libeir_tests/src/control_flow/mod.rs +++ b/libeir_tests/src/control_flow/mod.rs @@ -2,3 +2,4 @@ mod fib; //mod nth_root; mod accumulate_list; mod shadowing; +mod get_values; diff --git a/libeir_tests/src/lib.rs b/libeir_tests/src/lib.rs index 158dd28..94ab669 100644 --- a/libeir_tests/src/lib.rs +++ b/libeir_tests/src/lib.rs @@ -4,10 +4,11 @@ use std::path::Path; use libeir_ir::{ Module, FunctionIdent }; -use libeir_syntax_erl::{ Parse, Parser, ParseConfig, ParserError }; +use libeir_syntax_erl::{ Parse, Parser, ParseConfig, ParserError, ErlangError }; use libeir_syntax_erl::ast::{ Module as ErlAstModule }; use libeir_syntax_erl::lower_module; use libeir_diagnostics::{ Emitter, StandardStreamEmitter, ColorChoice }; +use libeir_util_parse::{Errors, ArcCodemap, error_tee}; mod patterns; mod list_comprehensions; @@ -17,74 +18,70 @@ mod errors; mod otp; mod ct_runner; -fn parse(input: &str, config: ParseConfig) -> (T, Parser) +fn parse(input: &str, config: ParseConfig) -> T where - T: Parse>, + T: Parse, { + let codemap = ArcCodemap::default(); + let mut errors = Errors::new(); + let parser = Parser::new(config); - let errs = match parser.parse_string::<&str, T>(input) { - Ok(ast) => return (ast, parser), - Err(errs) => errs, - }; - let emitter = StandardStreamEmitter::new(ColorChoice::Auto) - .set_codemap(parser.config.codemap.clone()); - for err in errs.iter() { - emitter.diagnostic(&err.to_diagnostic()).unwrap(); - } - panic!("parse failed"); + let res = parser.parse_string::<&str, T>(&mut errors, &codemap, input); + + errors.print(&codemap); + + res.unwrap() } -fn parse_file(path: P, config: ParseConfig) -> (T, Parser) +fn parse_file(path: P, config: ParseConfig) -> T where - T: Parse>, + T: Parse, P: AsRef, { + let codemap = ArcCodemap::default(); + let mut errors = Errors::new(); + let parser = Parser::new(config); - let errs = match parser.parse_file::<_, T>(path) { - Ok(ast) => return (ast, parser), - Err(errs) => errs, - }; - let emitter = StandardStreamEmitter::new(ColorChoice::Auto) - .set_codemap(parser.config.codemap.clone()); - for err in errs.iter() { - emitter.diagnostic(&err.to_diagnostic()).unwrap(); - } - panic!("parse failed"); + let res = parser.parse_file::<_, T>(&mut errors, &codemap, path); + + errors.print(&codemap); + + res.unwrap() } fn lower_file

(path: P, config: ParseConfig) -> Result where P: AsRef { - let (parsed, parser): (ErlAstModule, _) = parse_file(path, config); - let (res, messages) = { - let codemap = &*parser.config.codemap; - lower_module(codemap, &parsed) - }; - - let emitter = StandardStreamEmitter::new(ColorChoice::Auto) - .set_codemap(parser.config.codemap.clone()); - for err in messages.iter() { - emitter.diagnostic(&err.to_diagnostic()).unwrap(); - } + let codemap = ArcCodemap::default(); + let mut errors: Errors = Errors::new(); + + let eir_res = error_tee(&mut errors, |mut errors| { + let parser = Parser::new(config); + let ast = parser.parse_file(&mut errors.make_into_adapter(), &codemap, path)?; + let eir = lower_module(&mut errors.make_into_adapter(), &codemap, &ast)?; + Ok(eir) + }); + + errors.print(&codemap); - res + eir_res } pub fn lower(input: &str, config: ParseConfig) -> Result { - let (parsed, parser): (ErlAstModule, _) = parse(input, config); - let (res, messages) = { - let codemap = &*parser.config.codemap; - lower_module(codemap, &parsed) - }; - - let emitter = StandardStreamEmitter::new(ColorChoice::Auto) - .set_codemap(parser.config.codemap.clone()); - for err in messages.iter() { - emitter.diagnostic(&err.to_diagnostic()).unwrap(); - } + let codemap = ArcCodemap::default(); + let mut errors: Errors = Errors::new(); + + let eir_res = error_tee(&mut errors, |mut errors| { + let parser = Parser::new(config); + let ast = parser.parse_string(&mut errors.make_into_adapter(), &codemap, input)?; + let eir = lower_module(&mut errors.make_into_adapter(), &codemap, &ast)?; + Ok(eir) + }); + + errors.print(&codemap); - res + eir_res } pub fn write_dot(module: &Module, ident: Option) { @@ -93,10 +90,8 @@ pub fn write_dot(module: &Module, ident: Option) { let fun_def = &module[idx]; let fun = fun_def.function(); - let mut dot = Vec::::new(); - libeir_ir::text::dot_printer::function_to_dot(fun, &mut dot).unwrap(); - let dot_text = std::str::from_utf8(&dot).unwrap(); - print!("{}", dot_text); + let dot = libeir_ir::text::dot_printer::function_to_dot(fun); + print!("{}", dot); } else { unimplemented!() } diff --git a/libeir_tests/src/list_comprehensions.rs b/libeir_tests/src/list_comprehensions.rs index 967b2f9..24fc4d5 100644 --- a/libeir_tests/src/list_comprehensions.rs +++ b/libeir_tests/src/list_comprehensions.rs @@ -40,7 +40,7 @@ woo(N) -> [1 || erlang:is_integer(N)]. arity: 1, }; - println!("{}", eir_mod.to_text()); + println!("{}", eir_mod.to_text_standard()); let mut vm = VMState::new(); vm.add_builtin_modules(); @@ -86,7 +86,7 @@ woo(L) -> [X*2 || X <- L]. arity: 1, }; - println!("{}", eir_mod.to_text()); + println!("{}", eir_mod.to_text_standard()); let mut vm = VMState::new(); vm.add_builtin_modules(); @@ -200,7 +200,7 @@ perms(L) -> [H || H <- L]. libeir_lowerutils::analyze(fun); } - println!("{}", eir_mod.to_text()); + println!("{}", eir_mod.to_text_standard()); } #[test] @@ -232,7 +232,7 @@ perms(L) -> [[H|T] || H <- L, T <- perms(L--[H])]. dbg!(analysis); } - println!("{}", eir_mod.to_text()); + println!("{}", eir_mod.to_text_standard()); let fun = FunctionIdent { module: Ident::from_str("woo"), diff --git a/libeir_tests/src/otp/mod.rs b/libeir_tests/src/otp/mod.rs index 7f44cc7..4c26ccb 100644 --- a/libeir_tests/src/otp/mod.rs +++ b/libeir_tests/src/otp/mod.rs @@ -106,7 +106,7 @@ fn match_suite() { for fun_def in eir_mod.function_iter() { let fun = fun_def.function(); println!("{}", fun.ident()); - println!("{}", fun.to_text()); + println!("{}", fun.to_text_standard()); fun.graph_validate_global(); fun.live_values(); } diff --git a/tools/Cargo.toml b/tools/Cargo.toml index f523237..f4be69d 100644 --- a/tools/Cargo.toml +++ b/tools/Cargo.toml @@ -17,6 +17,8 @@ libeir_ir = { path = "../libeir_ir" } libeir_util_parse = { path = "../util/libeir_util_parse" } libeir_util_parse_listing = { path = "../util/libeir_util_parse_listing" } +libeir_frontend = { path = "../libeir_frontend" } + clap = "2.33.0" log = "0.4" fern = "0.5" diff --git a/tools/src/compile.rs b/tools/src/compile.rs index 4872a09..985184e 100644 --- a/tools/src/compile.rs +++ b/tools/src/compile.rs @@ -1,18 +1,22 @@ -use clap::{ Arg, App, ArgMatches, arg_enum, value_t, values_t }; +use clap::{Arg, App, ArgMatches, arg_enum, value_t, values_t}; -use std::io::Read; use std::io::Write; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; -use libeir_ir::{ Module, FunctionIdent, ToEirTextContext, ToEirText }; +use libeir_ir::FunctionIdent; use libeir_diagnostics::{ ColorChoice, Emitter, StandardStreamEmitter }; use libeir_passes::PassManager; -//use eir::FunctionIdent; -//use eir::text::{ ToEirText, ToEirTextContext, EirLiveValuesAnnotator }; +use libeir_frontend::{ + AnyFrontend, DynFrontend, + erlang::ErlangFrontend, + abstr_erlang::AbstrErlangFrontend, + eir::EirFrontend, +}; +use libeir_util_parse::ArcCodemap; arg_enum!{ #[derive(Debug, PartialEq, Eq)] @@ -72,12 +76,8 @@ impl LogLevel { } } -fn handle_erl(in_str: &str, matches: &ArgMatches) -> Option { - use libeir_syntax_erl::{ - lower_module, - ParseConfig, - Parser, - }; +fn make_erlang_frontend(matches: &ArgMatches) -> ErlangFrontend { + use libeir_syntax_erl::ParseConfig; let mut config = ParseConfig::default(); @@ -92,66 +92,15 @@ fn handle_erl(in_str: &str, matches: &ArgMatches) -> Option { } } - let parser = Parser::new(config); - - match parser.parse_string(in_str) { - Ok(ast) => { - let (res, messages) = { - let codemap = &*parser.config.codemap; - lower_module(codemap, &ast) - }; - - let emitter = StandardStreamEmitter::new(ColorChoice::Auto) - .set_codemap(parser.config.codemap.clone()); - for msg in messages.iter() { - emitter.diagnostic(&msg.to_diagnostic()).unwrap(); - } - - res.ok() - } - Err(err) => { - let emitter = StandardStreamEmitter::new(ColorChoice::Auto) - .set_codemap(parser.config.codemap.clone()); - for msg in err.iter() { - emitter.diagnostic(&msg.to_diagnostic()).unwrap(); - } - - None - } - } + ErlangFrontend::new(config) } -fn handle_abstr(in_str: &str, matches: &ArgMatches) -> Option { - use libeir_util_parse::{Parser, Parse}; - use libeir_util_parse_listing::parser::{ParseConfig, ParseError}; - use libeir_util_parse_listing::ast::Root; - - use libeir_syntax_erl::lower_module; - use libeir_syntax_erl::lower_abstr; - - let mut config = ParseConfig::default(); - let parser = Parser::new(config); - - let ast = parser.parse_string::<_, Root>(in_str).unwrap(); - - let module_ast = lower_abstr(&ast); - - let (res, messages) = { - let codemap = &*parser.config.codemap; - lower_module(codemap, &module_ast) - }; - - let emitter = StandardStreamEmitter::new(ColorChoice::Auto) - .set_codemap(parser.config.codemap.clone()); - for msg in messages.iter() { - emitter.diagnostic(&msg.to_diagnostic()).unwrap(); +fn make_frontend(matches: &ArgMatches) -> AnyFrontend { + match value_t!(matches, "IN_FORMAT", InputType).unwrap() { + InputType::Erl => make_erlang_frontend(matches).into(), + InputType::Abstr => AbstrErlangFrontend::new().into(), + InputType::Eir => EirFrontend::new().into(), } - - res.ok() -} - -fn handle_eir(in_str: &str, _matches: &ArgMatches) -> Option { - Some(libeir_ir::parse_module_unwrap(in_str)) } fn setup_logger(level: log::LevelFilter) { @@ -222,23 +171,24 @@ fn main() { setup_logger(value_t!(matches, "LOG_LEVEL", LogLevel).unwrap().to_filter()); + let frontend = make_frontend(&matches); + let in_file_name = matches.value_of("IN_FILE").unwrap(); + let in_file_path = Path::new(in_file_name); - let mut in_str = String::new(); - std::fs::File::open(&in_file_name).unwrap() - .read_to_string(&mut in_str).unwrap(); + let codemap = ArcCodemap::default(); + let (eir_res, diagnostics) = frontend.parse_file_dyn(codemap.clone(), &in_file_path); - let eir_ret = match value_t!(matches, "IN_FORMAT", InputType).unwrap() { - InputType::Erl => handle_erl(&in_str, &matches), - InputType::Abstr => handle_abstr(&in_str, &matches), - InputType::Eir => handle_eir(&in_str, &matches), - }; + let emitter = StandardStreamEmitter::new(ColorChoice::Auto) + .set_codemap(codemap.clone()); + for diag in diagnostics.iter() { + emitter.diagnostic(diag).unwrap(); + } - let mut eir = if let Some(eir) = eir_ret { - eir - } else { + if eir_res.is_err() { return; - }; + } + let mut eir = eir_res.unwrap(); match value_t!(matches, "COMPILE_LEVEL", CompileLevel).unwrap() { CompileLevel::High => {}, @@ -278,21 +228,19 @@ fn main() { .map(|val| FunctionIdent::parse_with_module( val, eir.name().clone()).unwrap()); - let mut print_ctx = ToEirTextContext::new(); //if matches.is_present("ANNOTATE_LIVE") { // print_ctx.add_annotator(EirLiveValuesAnnotator::new()); //} - let mut out_data = Vec::new(); + let out_data; let out_ext; let out_type = value_t!(matches, "OUT_FORMAT", OutputType).unwrap(); match out_type { OutputType::Eir => { if let Some(selected) = selected_function { - eir[&selected].function() - .to_eir_text(&mut print_ctx, 0, &mut out_data).unwrap(); + out_data = eir[&selected].function().to_text_standard(); } else { - eir.to_eir_text(&mut print_ctx, 0, &mut out_data).unwrap(); + out_data = eir.to_text_standard(); } out_ext = "eir"; }, @@ -302,8 +250,7 @@ fn main() { let fun_def = &eir[&selected_function]; let fun = fun_def.function(); - let dot = ::libeir_ir::text::function_to_dot(&fun); - out_data.extend(dot.bytes()); + out_data = ::libeir_ir::text::function_to_dot(&fun); out_ext = "dot"; } @@ -317,7 +264,7 @@ fn main() { println!("Writing to {}", out_file_name); let mut out = ::std::fs::File::create(&out_file_name).unwrap(); - out.write(&out_data).unwrap(); + out.write(out_data.as_bytes()).unwrap(); if let Some(dot_format) = matches.value_of("DOT_FORMAT") { assert!(out_type == OutputType::Dot); diff --git a/util/libeir_util_datastructures/Cargo.toml b/util/libeir_util_datastructures/Cargo.toml index 22b075a..46b73e4 100644 --- a/util/libeir_util_datastructures/Cargo.toml +++ b/util/libeir_util_datastructures/Cargo.toml @@ -9,4 +9,4 @@ license = "MIT OR Apache-2.0" [dependencies] cranelift-entity = "0.30.0" -hashbrown = { git = "https://github.com/hansihe/hashbrown.git", features = ["raw"] } +hashbrown = { path = "../../../hashbrown", features = ["raw"] } diff --git a/util/libeir_util_datastructures/src/aux_hash_map.rs b/util/libeir_util_datastructures/src/aux_hash_map.rs index 9efc6cf..94b2088 100644 --- a/util/libeir_util_datastructures/src/aux_hash_map.rs +++ b/util/libeir_util_datastructures/src/aux_hash_map.rs @@ -7,7 +7,7 @@ use std::fmt::Debug; //use cranelift_entity::{ListPool, EntityList, EntityRef}; //use cranelift_entity::packed_option::ReservedValue; -use hashbrown::raw::{RawTable, RawIter, Global, Alloc}; +use hashbrown::raw::{RawTable, RawIter, Global, AllocRef}; /// Hashes a data type with access to auxiliary data pub trait AuxHash { @@ -54,7 +54,7 @@ impl AuxEq for T where T: Eq { /// Implementation of a HashMap where the hash/equality functions /// requires additional information. #[derive(Clone)] -pub struct AuxHashMap where A: Clone + Alloc { +pub struct AuxHashMap where A: Clone + AllocRef { hash_builder: S, table: RawTable<(K, V), A>, aux: PhantomData, diff --git a/util/libeir_util_parse/Cargo.toml b/util/libeir_util_parse/Cargo.toml index 9f838e7..d07ddff 100644 --- a/util/libeir_util_parse/Cargo.toml +++ b/util/libeir_util_parse/Cargo.toml @@ -9,6 +9,7 @@ license = "MIT OR Apache-2.0" [dependencies] libeir_diagnostics = { path = "../../libeir_diagnostics" } +scoped_cell = { path = "../scoped_cell" } snafu = "0.5" diff --git a/util/libeir_util_parse/src/errors.rs b/util/libeir_util_parse/src/errors.rs index 8166a72..b3e8f8d 100644 --- a/util/libeir_util_parse/src/errors.rs +++ b/util/libeir_util_parse/src/errors.rs @@ -1,18 +1,43 @@ -use std::sync::{Arc, RwLock}; +use std::marker::PhantomData; +use std::rc::Rc; -use libeir_diagnostics::{Diagnostic, Emitter, StandardStreamEmitter, ColorChoice, CodeMap}; +use scoped_cell::{scoped_cell, ScopedCell}; -pub struct ParserErrors { - codemap: Arc>, - errors: Vec, +use libeir_diagnostics::{Diagnostic, Emitter, StandardStreamEmitter, ColorChoice}; + +use crate::ArcCodemap; + +// TODO: I would like to do a lot of things differently here, but things are +// tricky without GATs. Revisit once it lands. + +#[derive(Debug, Clone)] +pub enum ErrorOrWarning { + Error(E), + Warning(W), +} +impl ToDiagnostic for ErrorOrWarning +where + E: ToDiagnostic, + W: ToDiagnostic, +{ + fn to_diagnostic(&self) -> Diagnostic { + match self { + ErrorOrWarning::Error(err) => err.to_diagnostic(), + ErrorOrWarning::Warning(warn) => warn.to_diagnostic(), + } + } +} + +#[derive(Clone)] +pub struct Errors { + pub errors: Vec>, failed: bool, } -impl ParserErrors { +impl Errors { - pub fn new(codemap: Arc>) -> Self { - ParserErrors { - codemap: codemap, + pub fn new() -> Self { + Errors { errors: Vec::new(), failed: false, } @@ -22,28 +47,141 @@ impl ParserErrors { self.failed } - pub fn print(&self) + pub fn print(&self, codemap: &ArcCodemap) where E: ToDiagnostic, + W: ToDiagnostic, { let emitter = StandardStreamEmitter::new(ColorChoice::Auto) - .set_codemap(self.codemap.clone()); - for err in self.errors.iter() { - emitter.diagnostic(&err.to_diagnostic()).unwrap(); + .set_codemap(codemap.clone()); + for diag in self.iter_diagnostics() { + emitter.diagnostic(&diag).unwrap(); + } + } + + pub fn iter_diagnostics<'a>(&'a self) -> impl Iterator + 'a + where + E: ToDiagnostic, + W: ToDiagnostic, + { + self.errors.iter().map(|e| e.to_diagnostic()) + } + + pub fn errors_from(&mut self, other: Errors) + where + IE: Into, + IW: Into, + { + self.failed = self.failed | other.failed; + for ew in other.errors { + match ew { + ErrorOrWarning::Error(err) => self.errors.push(ErrorOrWarning::Error(err.into())), + ErrorOrWarning::Warning(warn) => self.errors.push(ErrorOrWarning::Warning(warn.into())), + } } } } -pub struct MessageIgnore { +impl ErrorReceiver for Errors { + type E = E; + type W = W; + + fn is_failed(&self) -> bool { + self.failed + } + + fn warning(&mut self, warning: W) { + self.errors.push(ErrorOrWarning::Warning(warning)); + } + + fn error(&mut self, error: E) { + self.errors.push(ErrorOrWarning::Error(error)); + self.failed = true; + } +} + +//pub struct MultiErrors { +// inner: Arc>>, +//} +// +//// No derive, we want copy impl regardless of whether E and W are copy +//impl Clone for MultiErrors { +// fn clone(&self) -> Self { +// Self { +// inner: self.inner.clone(), +// } +// } +//} +// +//impl MultiErrors { +// +// pub fn new() -> Self { +// Self::with_errors(Errors::new()) +// } +// +// pub fn with_errors(errors: Errors) -> Self { +// MultiErrors { +// inner: Arc::new(Mutex::new(errors)), +// } +// } +// +// pub fn inner_mut(&self) -> MutexGuard> { +// self.inner.lock().unwrap() +// } +// +// pub fn try_unwrap(self) -> Option> { +// Arc::try_unwrap(self.inner) +// .ok() +// .map(|v| v.into_inner().unwrap()) +// } +// +//} +// +//impl ErrorReceiver for MultiErrors { +// type E = E; +// type W = W; +// +// fn is_failed(&self) -> bool { +// self.inner.lock().unwrap().is_failed() +// } +// +// fn warning(&mut self, warning: W) { +// self.inner.lock().unwrap().warning(warning); +// } +// +// fn error(&mut self, error: E) { +// self.inner.lock().unwrap().error(error); +// } +//} +//impl ErrorReceiver for &mut MultiErrors { +// type E = E; +// type W = W; +// +// fn is_failed(&self) -> bool { +// self.inner.lock().unwrap().is_failed() +// } +// +// fn warning(&mut self, warning: W) { +// self.inner.lock().unwrap().warning(warning); +// } +// +// fn error(&mut self, error: E) { +// self.inner.lock().unwrap().error(error); +// } +//} + +pub struct MessageIgnore { failed: bool, + _phantom: PhantomData<(E, W)>, } -impl MessageIgnore { +impl MessageIgnore { pub fn new() -> Self { MessageIgnore { failed: false, + _phantom: PhantomData, } } @@ -53,41 +191,267 @@ impl MessageIgnore { } +impl ErrorReceiver for MessageIgnore { + type E = E; + type W = W; + + fn is_failed(&self) -> bool { + self.failed + } + fn warning(&mut self, _warning: W) {} + fn error(&mut self, _error: E) { + self.failed = true; + } +} + pub trait ToDiagnostic { fn to_diagnostic(&self) -> Diagnostic; } -pub trait ErrorReceiver { +pub trait ErrorReceiver { + type E; + type W; + fn is_failed(&self) -> bool; - fn warning(&mut self, warning: W); - fn error(&mut self, error: E); + fn warning(&mut self, warning: Self::W); + fn error(&mut self, error: Self::E); } -impl ErrorReceiver for ParserErrors +impl ErrorReceiver for &mut dyn ErrorReceiver { + type E = E; + type W = W; + fn is_failed(&self) -> bool { + (**self).is_failed() + } + fn warning(&mut self, warning: Self::W) { + (**self).warning(warning) + } + fn error(&mut self, error: Self::E) { + (**self).error(error) + } +} + +//pub struct ErrorReceiverAdapter { +// pub inner: I, +// pub error_adapter: FE, +// pub warning_adapter: FW, +// pub _phantom: PhantomData<(OE, OW)>, +//} +// +//impl ErrorReceiverAdapter { +// +// pub fn new(inner: I, error_adapter: FE, warning_adapter: FW) -> Self +// where +// I: ErrorReceiver, +// FE: Fn(OE) -> I::E + Sized, +// FW: Fn(OW) -> I::W + Sized, +// { +// Self { +// inner, +// error_adapter, +// warning_adapter, +// _phantom: PhantomData, +// } +// } +// +//} +// +//impl Clone for ErrorReceiverAdapter +//where +// I: Clone, +// FE: Clone, +// FW: Clone, +//{ +// fn clone(&self) -> Self { +// Self { +// inner: self.inner.clone(), +// error_adapter: self.error_adapter.clone(), +// warning_adapter: self.warning_adapter.clone(), +// _phantom: PhantomData, +// } +// } +//} +// +//impl ErrorReceiver for ErrorReceiverAdapter +//where +// I: ErrorReceiver, +// FE: Fn(OE) -> I::E + Sized, +// FW: Fn(OW) -> I::W + Sized, +//{ +// type E = OE; +// type W = OW; +// +// fn is_failed(&self) -> bool { +// self.inner.is_failed() +// } +// fn warning(&mut self, warning: OW) { +// self.inner.warning((self.warning_adapter)(warning)) +// } +// fn error(&mut self, error: OE) { +// self.inner.error((self.error_adapter)(error)) +// } +//} +// +//impl ErrorReceiver for &mut ErrorReceiverAdapter +//where +// I: ErrorReceiver, +// FE: Fn(OE) -> I::E + Sized, +// FW: Fn(OW) -> I::W + Sized, +//{ +// type E = OE; +// type W = OW; +// +// fn is_failed(&self) -> bool { +// self.inner.is_failed() +// } +// fn warning(&mut self, warning: OW) { +// self.inner.warning((self.warning_adapter)(warning)) +// } +// fn error(&mut self, error: OE) { +// self.inner.error((self.error_adapter)(error)) +// } +//} + +pub fn error_tee<'a, E, W, F, R>( + receiver: &'a mut (dyn ErrorReceiver + 'a), + fun: F, +) -> R where - E: Into, - W: Into, + F: FnOnce(ErrorReceiverTee) -> R { - fn is_failed(&self) -> bool { - self.failed + scoped_cell(receiver, |cell| { + let tee = ErrorReceiverTee { cell }; + fun(tee) + }) +} + +pub struct ErrorReceiverTee<'a, E, W> { + cell: ScopedCell + 'a>, +} +impl<'a, E, W> Clone for ErrorReceiverTee<'a, E, W> { + fn clone(&self) -> Self { + ErrorReceiverTee { + cell: self.cell.clone(), + } } +} - fn warning(&mut self, warning: W) { - self.errors.push(warning.into()); +impl<'a, E, W> ErrorReceiverTee<'a, E, W> { + + pub fn make_adapter( + &mut self, + error_adapter: FE, + warning_adapter: FW, + ) -> ErrorReceiverTeeAdapter<'a, E, W, NE, NW> + where + FE: Fn(NE) -> E + 'static, + FW: Fn(NW) -> W + 'static, + { + ErrorReceiverTeeAdapter { + cell: self.cell.clone(), + error_adapter: Rc::new(error_adapter), + warning_adapter: Rc::new(warning_adapter), + phantom: PhantomData, + } + } + + pub fn make_into_adapter(&mut self) -> ErrorReceiverTeeAdapter<'a, E, W, NE, NW> + where + NE: Into, + NW: Into, + { + ErrorReceiverTeeAdapter { + cell: self.cell.clone(), + error_adapter: Rc::new(|o| o.into()), + warning_adapter: Rc::new(|o| o.into()), + phantom: PhantomData, + } } +} + +impl<'a, E, W> ErrorReceiver for ErrorReceiverTee<'a, E, W> { + type E = E; + type W = W; + + fn is_failed(&self) -> bool { + self.cell.borrow_mut(|inner| { + (*inner).is_failed() + }) + } fn error(&mut self, error: E) { - self.errors.push(error.into()); - self.failed = true; + self.cell.borrow_mut(|inner| { + inner.error(error); + }); } + fn warning(&mut self, warning: W) { + self.cell.borrow_mut(|inner| { + inner.warning(warning); + }); + } +} + +pub struct ErrorReceiverTeeAdapter<'a, IE, IW, OE, OW> { + cell: ScopedCell + 'a>, + error_adapter: Rc IE>, + warning_adapter: Rc IW>, + phantom: PhantomData<(OE, OW)>, } -impl ErrorReceiver for MessageIgnore { +impl<'a, IE, IW, OE, OW> ErrorReceiver for ErrorReceiverTeeAdapter<'a, IE, IW, OE, OW> { + type E = OE; + type W = OW; + fn is_failed(&self) -> bool { - self.failed + self.cell.borrow_mut(|inner| { + (*inner).is_failed() + }) } - fn warning(&mut self, _warning: W) {} - fn error(&mut self, _error: E) { - self.failed = true; + fn error(&mut self, error: OE) { + self.cell.borrow_mut(|inner| { + inner.error((self.error_adapter)(error)); + }); + } + fn warning(&mut self, warning: OW) { + self.cell.borrow_mut(|inner| { + inner.warning((self.warning_adapter)(warning)); + }); } } + +impl<'a, IE, IW, OE, OW> Clone for ErrorReceiverTeeAdapter<'a, IE, IW, OE, OW> { + fn clone(&self) -> Self { + Self { + cell: self.cell.clone(), + error_adapter: self.error_adapter.clone(), + warning_adapter: self.warning_adapter.clone(), + phantom: PhantomData, + } + } +} + +#[cfg(test)] +mod tests { + use super::{Errors, ErrorReceiver, error_tee}; + + #[test] + fn basic_usage() { + + fn inner2(recv: &mut dyn ErrorReceiver) { + error_tee(recv, |_tee| { + }); + } + + fn inner(recv: &mut dyn ErrorReceiver) { + error_tee(recv, |mut tee| { + let mut adapter = tee.make_into_adapter(); + inner2(&mut adapter); + }); + } + + let mut errors = Errors::new(); + inner(&mut errors); + + } + +} diff --git a/util/libeir_util_parse/src/lib.rs b/util/libeir_util_parse/src/lib.rs index d8bc18a..7b06f56 100644 --- a/util/libeir_util_parse/src/lib.rs +++ b/util/libeir_util_parse/src/lib.rs @@ -15,11 +15,3 @@ pub use errors::*; mod parser; pub use parser::*; - -#[cfg(test)] -mod tests { - #[test] - fn it_works() { - assert_eq!(2 + 2, 4); - } -} diff --git a/util/libeir_util_parse/src/parser.rs b/util/libeir_util_parse/src/parser.rs index 68549f7..8f548b4 100644 --- a/util/libeir_util_parse/src/parser.rs +++ b/util/libeir_util_parse/src/parser.rs @@ -5,6 +5,9 @@ use std::path::Path; use libeir_diagnostics::{CodeMap, FileName}; use crate::{Source, SourceError, FileMapSource}; +use crate::ErrorReceiver; + +pub type ArcCodemap = Arc>; pub struct Parser { pub config: C, @@ -20,40 +23,48 @@ impl Parser { } -impl Parser where C: ParserConfig { +impl Parser { - pub fn parse_string(&self, source: S) -> Result::Error> + pub fn parse_string<'a, S, T>( + &self, + errors: &'a mut (dyn ErrorReceiver::Error, W = ::Error> + 'a), + codemap: &ArcCodemap, + source: S, + ) -> Result where S: AsRef, T: Parse, { - let codemap = self.config.codemap(); let filemap = { codemap.write().unwrap().add_filemap( FileName::Virtual(Cow::Borrowed("nofile")), source.as_ref().to_owned(), ) }; - >::parse(&self.config, FileMapSource::new(filemap)) + >::parse(&self.config, codemap, errors, FileMapSource::new(filemap)) } - pub fn parse_file(&self, path: P) -> Result::Error> + pub fn parse_file<'a, P, T>( + &self, + errors: &'a mut (dyn ErrorReceiver::Error, W = ::Error> + 'a), + codemap: &ArcCodemap, + path: P, + ) -> Result where P: AsRef, T: Parse, { - match FileMapSource::from_path(self.config.codemap().clone(), path) { - Err(err) => return Err(>::file_map_error(err)), - Ok(source) => >::parse(&self.config, source), + match FileMapSource::from_path(codemap.clone(), path) { + Err(err) => { + errors.error(>::file_map_error(err)); + Err(()) + }, + Ok(source) => >::parse(&self.config, codemap, errors, source), } } } -pub trait ParserConfig { - fn codemap(&self) -> &Arc>; -} - pub trait Parse { type Parser; type Error; @@ -63,12 +74,20 @@ pub trait Parse { fn file_map_error(err: SourceError) -> Self::Error; /// Initializes a token stream for the underlying parser and invokes parse_tokens - fn parse(config: &Self::Config, source: S) -> Result + fn parse<'a, S>( + config: &Self::Config, + codemap: &ArcCodemap, + errors: &'a mut (dyn ErrorReceiver + 'a), + source: S, + ) -> Result where S: Source; /// Implemented by each parser, which should parse the token stream and produce a T - fn parse_tokens(tokens: S) -> Result + fn parse_tokens<'a, S>( + errors: &'a mut (dyn ErrorReceiver + 'a), + tokens: S, + ) -> Result where S: IntoIterator; } diff --git a/util/libeir_util_parse_listing/src/parser.rs b/util/libeir_util_parse_listing/src/parser.rs index 38fddd0..849618c 100644 --- a/util/libeir_util_parse_listing/src/parser.rs +++ b/util/libeir_util_parse_listing/src/parser.rs @@ -1,7 +1,5 @@ -use std::sync::{Arc, RwLock}; - -use libeir_util_parse::{Scanner, Parse, ParserConfig, Source, SourceError}; -use libeir_diagnostics::{CodeMap, ByteIndex}; +use libeir_util_parse::{Scanner, Parse, Source, SourceError, ErrorReceiver, ToDiagnostic, ArcCodemap}; +use libeir_diagnostics::{Diagnostic, ByteIndex}; use super::ast; use super::token::{Lexer, Token}; @@ -33,69 +31,75 @@ impl From> for ParseError { } } -pub struct ParseConfig { - pub codemap: Arc>, -} -impl ParserConfig for ParseConfig { - fn codemap(&self) -> &Arc> { - &self.codemap - } -} -impl Default for ParseConfig { - fn default() -> Self { - ParseConfig { - codemap: Arc::new(RwLock::new(CodeMap::new())), - } +impl ToDiagnostic for ParseError { + fn to_diagnostic(&self) -> Diagnostic { + unimplemented!() } } impl Parse for ast::Root { type Parser = grammar::RootParser; type Error = ParseError; - type Config = ParseConfig; + type Config = (); type Token = Result<(ByteIndex, Token, ByteIndex), ()>; fn file_map_error(_err: SourceError) -> Self::Error { unimplemented!() } - fn parse(_config: &ParseConfig, source: S) -> Result + fn parse( + _config: &(), + _codemap: &ArcCodemap, + errors: &mut dyn ErrorReceiver, + source: S, + ) -> Result where S: Source, { let scanner = Scanner::new(source); let lexer = Lexer::new(scanner); - Self::parse_tokens(lexer) + Self::parse_tokens(errors, lexer) } - fn parse_tokens(tokens: S) -> Result + fn parse_tokens( + errors: &mut dyn ErrorReceiver, + tokens: S, + ) -> Result where S: IntoIterator, { - Self::Parser::new() - .parse(tokens) - .map_err(|e| e.into()) + match Self::Parser::new().parse(tokens) { + Ok(inner) => Ok(inner), + Err(err) => { + errors.error(err.into()); + Err(()) + }, + } } } #[cfg(test)] mod test { - use libeir_util_parse::{Parser, Parse}; - use super::{ParseConfig, ParseError}; + use libeir_util_parse::{Parser, Parse, Errors, ArcCodemap}; + use super::ParseError; use super::ast::Root; fn parse<'a, T>(input: &'a str) -> T where - T: Parse + T: Parse { - let config = ParseConfig::default(); - let parser = Parser::new(config); - let err = match parser.parse_string::<&'a str, T>(input) { + let codemap = ArcCodemap::default(); + + let parser = Parser::new(()); + let mut errors = Errors::new(); + match parser.parse_string::<&'a str, T>(&mut errors, &codemap, input) { Ok(ast) => return ast, - Err(err) => err, + Err(()) => { + errors.print(&codemap); + panic!(); + }, }; - panic!("{:?}", err); } #[test] diff --git a/util/libeir_util_pattern_compiler/Cargo.toml b/util/libeir_util_pattern_compiler/Cargo.toml index ab67e0e..83eadcc 100644 --- a/util/libeir_util_pattern_compiler/Cargo.toml +++ b/util/libeir_util_pattern_compiler/Cargo.toml @@ -18,3 +18,5 @@ petgraph = "0.4" derivative = "1.0" prettytable-rs = { version = "0.8", optional = true } itertools = "0.8.0" + +log = "0.4" diff --git a/util/libeir_util_pattern_compiler/src/lib.rs b/util/libeir_util_pattern_compiler/src/lib.rs index 09b301a..3ea54f4 100644 --- a/util/libeir_util_pattern_compiler/src/lib.rs +++ b/util/libeir_util_pattern_compiler/src/lib.rs @@ -19,6 +19,11 @@ pub mod simple_pattern; pub use ::petgraph::graph::NodeIndex; +#[cfg(feature = "debug_table_print")] +use log::trace; +#[cfg(feature = "debug_table_print")] +const TARGET: &'static str = "pattern_compiler"; + #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] struct LeafId(usize); @@ -72,10 +77,12 @@ fn matrix_to_decision_tree

(parent: cfg::CfgNodeIndex, #[cfg(feature = "debug_table_print")] { + let mut buf = String::new(); for _ in 0..level { - print!(" =="); + write!(buf, " =="); } - println!(" MATRIX AT LEVEL {}", level); + write!(buf, " MATRIX AT LEVEL {}", level); + trace!(target: TARGET, "{}", buf); } let edge = cfg::CfgEdge { diff --git a/util/libeir_util_pattern_compiler/src/matrix.rs b/util/libeir_util_pattern_compiler/src/matrix.rs index f7a3a34..aa9f3f2 100644 --- a/util/libeir_util_pattern_compiler/src/matrix.rs +++ b/util/libeir_util_pattern_compiler/src/matrix.rs @@ -3,6 +3,12 @@ use std::collections::{ HashMap, HashSet }; #[cfg(feature = "debug_table_print")] use prettytable::Table; + +#[cfg(feature = "debug_table_print")] +use log::trace; +#[cfg(feature = "debug_table_print")] +use super::TARGET; + use super::LeafId; use super::pattern::PatternProvider; @@ -148,9 +154,9 @@ impl

MatchMatrix

where P: PatternProvider { { #[cfg(feature = "debug_table_print")] { - println!("Specialize variable #{} on {:?}", variable, on); - println!("{}", self.to_table(&ctx.pattern)); - println!("binds: {:#?}", self.leaf_bindings); + trace!(target: TARGET, "Specialize variable #{} on {:?}", variable, on); + trace!(target: TARGET, "{}", self.to_table(&ctx.pattern)); + trace!(target: TARGET, "binds: {:#?}", self.leaf_bindings); } // 1. Determine how we want to handle the different clauses diff --git a/util/libeir_util_prof/Cargo.toml b/util/libeir_util_prof/Cargo.toml index 97bec77..19c83b8 100644 --- a/util/libeir_util_prof/Cargo.toml +++ b/util/libeir_util_prof/Cargo.toml @@ -8,4 +8,4 @@ license = "MIT OR Apache-2.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -bumpalo = "2.6" +bumpalo = { git = "https://github.com/hansihe/bumpalo", branch = "nightly_alloc", features = ["nightly", "collections"] } diff --git a/util/scoped_cell/Cargo.toml b/util/scoped_cell/Cargo.toml new file mode 100644 index 0000000..6e1bb74 --- /dev/null +++ b/util/scoped_cell/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "scoped_cell" +version = "0.1.0" +authors = ["Hans Elias B. Josephsen "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/util/scoped_cell/src/lib.rs b/util/scoped_cell/src/lib.rs new file mode 100644 index 0000000..7619a9f --- /dev/null +++ b/util/scoped_cell/src/lib.rs @@ -0,0 +1,295 @@ +use std::alloc::{Layout, alloc, dealloc}; +use std::marker::PhantomData; +use std::ptr::NonNull; + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct BorrowMutError; + +struct ScopedCellGuard<'a, T: ?Sized>(&'a ScopedCell); +impl<'a, T: ?Sized> Drop for ScopedCellGuard<'a, T> { + fn drop(&mut self) { + unsafe { + let inner = self.0.inner_mut(); + debug_assert!(inner.active); + inner.active = false; + } + } +} + +pub struct ScopedCell { + ptr: NonNull>, + //phantom: PhantomData>, +} + + +impl ScopedCell { + + pub fn borrow_mut(&self, fun: F) -> R + where + F: FnOnce(&mut T) -> R, + { + unsafe { + let inner_ptr; + { + let inner = self.inner_mut(); + if inner.active { + panic!("ScopedCell already borrowed"); + } + if inner.inner.is_none() { + panic!("Tried to borrow ScopedCell outside of creating scope"); + } + inner.active = true; + inner_ptr = inner.inner.unwrap(); + } + + // This guard will take care of setting active to false when we + // leave the inner scope. + let _guard = ScopedCellGuard(self); + + fun(&mut *inner_ptr.as_ptr()) + } + } + + pub fn try_borrow_mut(&self, fun: F) -> Result + where + F: FnOnce(&mut T) -> R, + { + unsafe { + let inner_ptr; + { + let inner = self.inner_mut(); + if inner.active { + return Err(BorrowMutError); + } + if inner.inner.is_none() { + return Err(BorrowMutError); + } + inner.active = true; + inner_ptr = inner.inner.unwrap(); + } + + // This guard will take care of setting active to false when we + // leave the inner scope. + let _guard = ScopedCellGuard(self); + + Ok(fun(&mut *inner_ptr.as_ptr())) + } + } + + /// # Safety + /// This may only be called by internal functions. + /// A reference to this may only be held while control flow is controlled by the scoped cell. + /// Only one reference to inner may be obtained at the time. + unsafe fn inner_mut(&self) -> &mut ScopedCellInner { + &mut *(self.ptr.as_ptr() as *mut _) + } + +} + +impl Clone for ScopedCell { + fn clone(&self) -> Self { + unsafe { + let inner = self.inner_mut(); + inner.references += 1; + } + ScopedCell { + ptr: self.ptr, + //phantom: PhantomData, + } + } +} +impl Drop for ScopedCell { + fn drop(&mut self) { + unsafe { + let is_zero; + let active; + { + let inner = self.inner_mut(); + inner.references -= 1; + is_zero = inner.references == 0; + active = inner.active; + } + + if is_zero { + debug_assert!(!active); + dealloc(self.ptr.as_ptr().cast(), Layout::for_value(self.ptr.as_ref())) + } + } + } +} + +pub struct ScopedCellInner { + /// Number of `ScopedCell`s that exist at any given time. + /// This is incremented on creation/cloning of a new `ScopedCell`, and + /// decremented in the drop implementation. + /// When this reaches 0, this struct is deallocated. + references: usize, + /// When this is true, a `&mut T` exists + active: bool, + /// Pointer to the thing this ScopedCell references. + /// If this is `Some`, the creator guard of this scoped cell is still in + /// scope. + /// If this is `None`, the creator guard has been dropped and any future + /// borrow attempts will fail. + inner: Option>, +} + +/// The guard must not be dropped while a borrow of a related cell is in +/// progress. If this happens, the whole process will be aborted. +pub fn new<'a, T: ?Sized + 'a>(value: &'a mut T) -> ScopedCellCreatorGuard<'a, T> { + // Because we are using the value reference in a `PhantomData`, the + // reference will be concidered used, while not actually existing in a + // usable form until this guard stuct is dropped. + // + // This should make things sound when we create a mutable reference from + // the pointer we have stored in `ScopedCellInner`, because that can only + // be done while the actual value reference is tied up in the PhantomData + // of this guard. + + unsafe { + let inner_layout = Layout::new::>(); + let inner_ptr_u8 = alloc(inner_layout); + + let inner_ptr = NonNull::new(inner_ptr_u8 as *mut _).unwrap(); + std::ptr::write(inner_ptr.as_ptr(), ScopedCellInner { + references: 1, + active: false, + inner: Some(NonNull::new(value as *mut _).unwrap()), + }); + + let cell = ScopedCell { + ptr: inner_ptr, + //phantom: PhantomData, + }; + + ScopedCellCreatorGuard { + cell, + life: PhantomData, + } + } +} + +pub struct ScopedCellCreatorGuard<'a, T: ?Sized> { + cell: ScopedCell, + life: PhantomData<&'a mut T>, +} +impl<'a, T: ?Sized> Drop for ScopedCellCreatorGuard<'a, T> { + fn drop(&mut self) { + unsafe { + let inner = self.cell.inner_mut(); + if inner.active { + println!("FATAL: ScopedCell borrow active while ScopedCellCreatorGuard was dropped"); + std::process::abort(); + } + inner.inner = None; + } + } +} +impl<'a, T: ?Sized> ScopedCellCreatorGuard<'a, T> { + pub fn clone_cell(&self) -> ScopedCell { + self.cell.clone() + } +} + +pub fn scoped_cell(value: &mut T, fun: F) -> R +where + F: FnOnce(ScopedCell) -> R, +{ + let guard = new(value); + + let cell = guard.clone_cell(); + fun(cell) +} + +#[cfg(test)] +mod tests { + use super::{new, scoped_cell}; + + #[test] + fn creation() { + let mut a: u32 = 0; + scoped_cell(&mut a, |_ac| { + }); + } + + #[test] + fn basic_usage() { + let mut a: u32 = 0; + + fn inner(a: &mut u32) { + scoped_cell(a, |ac| { + let ac2 = ac.clone(); + + ac.borrow_mut(|v| { + *v += 1; + }); + + ac2.borrow_mut(|v| { + *v += 1; + }); + }); + } + + inner(&mut a); + + assert!(a == 2); + } + + //#[test] + //fn miri_fail() { + // use std::marker::PhantomData; + + // struct Guard<'a>(PhantomData<&'a u8>); + // fn new<'a>(_val: &'a mut u8) -> Guard<'a> { + // Guard(PhantomData) + // } + + // let mut a = 0u8; + // let a_ptr = &mut a as *mut _; + + // // Should be sound: + // { + // let guard = new(&mut a); + + // // Compiler is aware that `a` is borrowed mutably at this point, but + // // no actual reference exists. Therefore there will only be one active + // // mutable reference when we create one from the pointer. + + // let inner_ref = unsafe { &mut *a_ptr }; + // *inner_ref += 1u8; + // std::mem::drop(inner_ref); + + // std::mem::drop(guard); + // } + //} + + #[test] + fn raw_usage() { + let mut a: u32 = 0; + let aco; + { + let sc = new(&mut a); + let ac1 = sc.clone_cell(); + let ac2 = sc.clone_cell(); + aco = ac2.clone(); + ac1.borrow_mut(|_i| { + assert!(ac2.try_borrow_mut(|_i| ()).is_err()); + }); + } + assert!(aco.try_borrow_mut(|_i| ()).is_err()); + } + + #[test] + fn double_borrow_fails() { + let mut a: u32 = 0; + + scoped_cell(&mut a, |ac| { + let ac2 = ac.clone(); + ac.borrow_mut(|_i| { + assert!(ac2.try_borrow_mut(|_i| ()).is_err()); + }); + }); + + } + +}