Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 4 additions & 14 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ members = [
"ast-sanitizer",
"debug-fir",
"typecheck",
"recursive_typecheck",
# "recursive_typecheck",
"fire",
"xrepl",
"xrepl", "append_bind",
]

[dependencies]
Expand All @@ -45,7 +45,7 @@ loop_desugar = { path = "loop_desugar" }
builtins = { path = "builtins" }
xparser = { path = "xparser" }
typecheck = { path = "typecheck" }
recursive_typecheck = { path = "recursive_typecheck" }
# recursive_typecheck = { path = "recursive_typecheck" }
fire = { path = "fire" }
xrepl = { path = "xrepl" }
structopt = "0.3"
Expand Down
55 changes: 34 additions & 21 deletions error/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,39 @@ pub struct ErrorHandler {
file: PathBuf,
}

pub trait Emitter {
fn emit(&self);
}

impl Emitter for Error {
fn emit(&self) {
match &self.kind {
ErrKind::Multiple(errs) => errs.iter().for_each(|e| e.emit()),
ErrKind::Hint => self.emit_hint(),
ErrKind::Sanitizer => self.emit_sanity_error(),
_ => {
if let Some(loc) = &self.loc {
self.emit_full_loc(loc);
} else if let Some(msg) = &self.msg {
eprintln!("{msg}")
}

if let Some(first_hint) = self.hints.first() {
first_hint.emit_hint();
}

self.hints.iter().skip(1).for_each(|hint| hint.emit_hint());
}
}
}
}

impl Emitter for Vec<Error> {
fn emit(&self) {
self.iter().for_each(|err| err.emit())
}
}

impl ErrorHandler {
/// Emit all the errors contained in a handler
pub fn emit(&self) {
Expand Down Expand Up @@ -76,6 +109,7 @@ pub enum ErrKind {
IO,
Debug,
UTF8,
// FIXME: Remove
Multiple(Vec<Error>),
}

Expand Down Expand Up @@ -181,27 +215,6 @@ impl Error {
}
}

pub fn emit(&self) {
match &self.kind {
ErrKind::Multiple(errs) => errs.iter().for_each(|e| e.emit()),
ErrKind::Hint => self.emit_hint(),
ErrKind::Sanitizer => self.emit_sanity_error(),
_ => {
if let Some(loc) = &self.loc {
self.emit_full_loc(loc);
} else if let Some(msg) = &self.msg {
eprintln!("{msg}")
}

if let Some(first_hint) = self.hints.first() {
first_hint.emit_hint();
}

self.hints.iter().skip(1).for_each(|hint| hint.emit_hint());
}
}
}

/// Emit a debug interpreter error - this is only useful for debugging the
/// interpreter itself
pub fn emit_debug(&self) {
Expand Down
51 changes: 50 additions & 1 deletion fir/src/iter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,62 @@ pub use traverse::Traversal;

use crate::Fir;

// we want the API to look something like this

// let fir = fir.pass1().bind(Pass2::pass).bind(Pass3::pass)?;
// same as
// let fir = fir.pass1().bind(|fir| fir.pass2()).bind(|fir| fir.pass3())?;

pub trait AccumulateBind<T, E> {
fn bind<F>(self, bind_fn: F) -> Result<Fir<T>, IncompleteFir<T, E>>
where
F: Fn(Fir<T>) -> Result<Fir<T>, IncompleteFir<T, E>>;
}

impl<T, E> AccumulateBind<T, E> for Result<Fir<T>, IncompleteFir<T, E>> {
fn bind<F>(self, bind_fn: F) -> Result<Fir<T>, IncompleteFir<T, E>>
where
F: Fn(Fir<T>) -> Result<Fir<T>, IncompleteFir<T, E>>,
{
match self {
Ok(t) => bind_fn(t),
Err(IncompleteFir { carcass, mut errs }) => {
let res = bind_fn(carcass);

let (carcass, errs) = match res {
Ok(fir) => (fir, errs),
Err(IncompleteFir {
carcass,
errs: new_errs,
}) => {
new_errs.into_iter().for_each(|e| errs.push(e));

(carcass, errs)
}
};

Err(IncompleteFir { carcass, errs })
}
}
}
}

impl<T, E> AccumulateBind<T, E> for Fir<T> {
fn bind<F>(self, bind_fn: F) -> Result<Fir<T>, IncompleteFir<T, E>>
where
F: Fn(Fir<T>) -> Result<Fir<T>, IncompleteFir<T, E>>,
{
bind_fn(self)
}
}

/// An incomplete [`Fir`]. This is returned by the various mappers within this module. The
/// incomplete [`Fir`] contains all of the nodes for which the mapping operation succeeded,
/// which enables you to still perform error reporting, hinting, or to keep going with more
/// operations. You can also access the errors emitted by the mapping pass. This vector of
/// errors is guaranteed to not be empty.
#[derive(Debug)]
pub struct Incomplete<T, E> {
pub struct IncompleteFir<T, E> {
pub carcass: Fir<T>,
pub errs: Vec<E>,
}
Expand Down
6 changes: 3 additions & 3 deletions fir/src/iter/mapper.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{Fir, Incomplete, Kind, Node, OriginIdx, RefIdx};
use crate::{Fir, IncompleteFir, Kind, Node, OriginIdx, RefIdx};

pub trait Mapper<T, U: From<T>, E> {
fn map_constant(&mut self, data: T, origin: OriginIdx, constant: RefIdx) -> Result<Node<U>, E> {
Expand Down Expand Up @@ -276,7 +276,7 @@ pub trait Mapper<T, U: From<T>, E> {
/// In the [`Err`] case, this returns an incomplete [`Fir`] which contains
/// all valid mapped nodes. This allows an interpreter to keep trying
/// passes and emit as many errors as possible
fn map(&mut self, fir: Fir<T>) -> Result<Fir<U>, Incomplete<U, E>> {
fn map(&mut self, fir: Fir<T>) -> Result<Fir<U>, IncompleteFir<U, E>> {
let (fir, errs) =
fir.nodes
.into_values()
Expand All @@ -294,7 +294,7 @@ pub trait Mapper<T, U: From<T>, E> {
if errs.is_empty() {
Ok(fir)
} else {
Err(Incomplete { carcass: fir, errs })
Err(IncompleteFir { carcass: fir, errs })
}
}
}
6 changes: 3 additions & 3 deletions fir/src/iter/multi_mapper.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{Fir, Incomplete, Kind, Node, OriginIdx, RefIdx};
use crate::{Fir, IncompleteFir, Kind, Node, OriginIdx, RefIdx};

pub trait MultiMapper<T, U: Default + From<T>, E> {
/// Each implementer of [`Pass`] should keep its own [`OriginIdx`] counter in order to supply the [`Fir`]
Expand Down Expand Up @@ -286,7 +286,7 @@ pub trait MultiMapper<T, U: Default + From<T>, E> {
/// In the [`Err`] case, this returns an incomplete [`Fir`] which contains
/// all valid mapped nodes. This allows an interpreter to keep trying
/// passes and emit as many errors as possible
fn multi_map(&mut self, fir: Fir<T>) -> Result<Fir<U>, Incomplete<U, E>> {
fn multi_map(&mut self, fir: Fir<T>) -> Result<Fir<U>, IncompleteFir<U, E>> {
let (fir, errs) = fir.nodes.into_values().fold(
(Fir::default(), Vec::new()),
|(new_fir, mut errs), node| match self.map_node(node) {
Expand All @@ -305,7 +305,7 @@ pub trait MultiMapper<T, U: Default + From<T>, E> {
if errs.is_empty() {
Ok(fir)
} else {
Err(Incomplete { carcass: fir, errs })
Err(IncompleteFir { carcass: fir, errs })
}
}
}
6 changes: 3 additions & 3 deletions fir/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ use std::{collections::BTreeMap, ops::Index};
mod checks;
pub mod iter;

pub use iter::{Fallible, Incomplete, Mapper, MultiMapper, Traversal};
pub use iter::{Fallible, IncompleteFir, Mapper, MultiMapper, Traversal};

/// A reference to another [`Node`] in the [`Fir`]. These references can be either resolved or unresolved, based
/// on the state of the [`Fir`].
Expand Down Expand Up @@ -294,7 +294,7 @@ pub trait Pass<T: Debug, U: Debug, E> {
fn post_condition(fir: &Fir<U>);

/// The actual pass algorithm which transforms the [`Fir`] and returns a new one.
fn transform(&mut self, fir: Fir<T>) -> Result<Fir<U>, E>;
fn transform(&mut self, fir: Fir<T>) -> Result<Fir<U>, IncompleteFir<T, E>>;

/// The [`pass`] function is implemented by default in the [`Pass`] trait. It calls into the
/// [`pre_condition`] function, then executes the [`transform`] one, before finally executing
Expand All @@ -305,7 +305,7 @@ pub trait Pass<T: Debug, U: Debug, E> {
/// The fallible traits in the Fir's `iter` module return an instance of the [`Incomplete`] type
/// on error. Should you want to return this incomplete [`Fir`], make sure to return it in the
/// [`Ok`] case. This trait is to be used as high level interface into your [`Fir`] pass.
fn pass(&mut self, fir: Fir<T>) -> Result<Fir<U>, E> {
fn pass(&mut self, fir: Fir<T>) -> Result<Fir<U>, IncompleteFir<T, E>> {
// FIXME: Add a #[cfg(not(release))] here
Self::pre_condition(&fir);

Expand Down
57 changes: 37 additions & 20 deletions interpreter/jinko.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ mod repl;
use colored::Colorize;

use builtins::AppendAstBuiltins;
use fire::instance::Instance;
use fire::Interpret;
use error::Emitter;
use fir::iter::AccumulateBind;
use fir::IncompleteFir;
use fire::{instance::Instance, Interpret};
use flatten::{FlattenAst, FlattenData};
use include_code::IncludeCode;
use loop_desugar::DesugarLoops;
Expand Down Expand Up @@ -111,9 +113,10 @@ fn experimental_pipeline(input: &str, file: &Path) -> InteractResult {
($res:expr) => {
match $res {
Ok(inner) => inner,
Err(e) => {
e.emit();
return Err(Error::new(ErrKind::Context));
Err(Incomplete { carcass, errs }) => {
errs.emit();

carcass
}
}
};
Expand Down Expand Up @@ -142,25 +145,39 @@ fn experimental_pipeline(input: &str, file: &Path) -> InteractResult {
};

let fir = ast.flatten();
FirDebug::default()
.header("flattened")
.show_data(data_fmt)
.display(&fir);

let fir = x_try!(fir.name_resolve());
FirDebug::default()
.header("name_resolved")
.show_data(data_fmt)
.display(&fir);
let result = ast
.flatten()
.bind(|fir| {
FirDebug::default()
.header("flattened")
.show_data(data_fmt)
.display(&fir);

fir.name_resolve()
})
.bind(|fir| {
FirDebug::default()
.header("name_resolved")
.show_data(data_fmt)
.display(&fir);

let fir = x_try!(fir.type_check());
let result = fir.interpret();
fir.type_check()
})
.bind(Interpret::interpret)?;

let exit_code = match result {
// convert `true` to `0` and `false` to `1`
Some(Instance::Bool(b)) => !b as i32,
Some(Instance::Int(inner)) => inner as i32,
_ => 0,
Ok(instance) => match instance {
// convert `true` to `0` and `false` to `1`
Some(Instance::Bool(b)) => !b as i32,
Some(Instance::Int(inner)) => inner as i32,
_ => 0,
},
Err(IncompleteFir { errs, .. }) => {
errs.emit();

1
}
};

process::exit(exit_code);
Expand Down
Loading