Skip to content

Commit

Permalink
rework wrapped EvalError variants, add custom Display impl for wrappe…
Browse files Browse the repository at this point in the history
…d variants;
  • Loading branch information
greenhat committed Aug 9, 2023
1 parent 40d95b6 commit e76a90f
Show file tree
Hide file tree
Showing 7 changed files with 156 additions and 66 deletions.
14 changes: 9 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ members = [
"bindings/ergo-lib-wasm",
"bindings/ergo-lib-c-core",
"bindings/ergo-lib-c",
"bindings/ergo-lib-jni"
"bindings/ergo-lib-jni",
]

[workspace.package]
Expand All @@ -35,9 +35,9 @@ ergotree-interpreter = { version = "^0.24.0", path = "./ergotree-interpreter" }
ergo-nipopow = { version = "^0.11", path = "./ergo-nipopow" }
ergo-merkle-tree = { version = "^0.11.0", path = "./ergo-merkle-tree" }
ergo-rest = { version = "^0.9.0", path = "./ergo-rest" }
ergo-lib = { version = "^0.24.0", path = "./ergo-lib"}
ergo-lib = { version = "^0.24.0", path = "./ergo-lib" }
k256 = { version = "0.11", features = ["arithmetic", "ecdsa"] }
elliptic-curve = {version = "0.12", features = [ "ff"]}
elliptic-curve = { version = "0.12", features = ["ff"] }
thiserror = "1"
bounded-vec = { version = "^0.7.0" }
bitvec = { version = "1.0.1" }
Expand All @@ -52,9 +52,12 @@ lazy_static = "1.4"
bs58 = "0.4.0"
base16 = "0.2.1"
base64 = "0.13.0"
indexmap = {version ="1.3.2", features = ["serde"]}
indexmap = { version = "1.3.2", features = ["serde"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = { version = "1.0", features = ["arbitrary_precision", "preserve_order"] }
serde_json = { version = "1.0", features = [
"arbitrary_precision",
"preserve_order",
] }
serde_with = { version = "1.9.1", features = ["json"] }
rand = "0.8.5"
bytes = "1.1"
Expand All @@ -67,6 +70,7 @@ bounded-integer = { version = "^0.5", features = ["types"] }
url = "2.2"
getrandom = { version = "0.2.7" }
itertools = "0.10.3"
miette = { version = "5", features = ["fancy"] }

# dev-dependencies
proptest = { version = "=1.0", default-features = false, features = ["std"] }
Expand Down
1 change: 1 addition & 0 deletions ergotree-interpreter/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ serde_with = { workspace = true, optional = true }
proptest = { workspace = true, optional = true }
scorex_crypto_avltree = "0.1.0"
gf2_192 = { version = "^0.24.0", path = "../gf2_192" }
miette = { workspace = true }

[features]
default = ["json"]
Expand Down
3 changes: 2 additions & 1 deletion ergotree-interpreter/src/eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,8 @@ pub fn reduce_to_crypto(
.print(&mut printer)
.map_err(|e| EvalError::Misc(format!("printer error: {}", e)))?;
let printed_expr_str = printer.get_buf();
inner(&spanned_expr, env, ctx_clone).map_err(|e| e.wrap_with_src(printed_expr_str.to_string()))
inner(&spanned_expr, env, ctx_clone)
.map_err(|e| e.wrap_spanned_with_src(printed_expr_str.to_string()))
}

/// Expects SigmaProp constant value and returns it's value. Otherwise, returns an error.
Expand Down
195 changes: 136 additions & 59 deletions ergotree-interpreter/src/eval/error.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
use miette::miette;
use miette::LabeledSpan;
use std::fmt::Display;

use bounded_vec::BoundedVecOutOfBounds;
use derive_more::TryInto;
use ergotree_ir::ergo_tree::ErgoTreeError;
use ergotree_ir::mir::constant::TryExtractFromError;
use ergotree_ir::serialization::SigmaParsingError;
Expand All @@ -12,7 +17,7 @@ use super::cost_accum::CostError;
use super::env::Env;

/// Interpreter errors
#[derive(Error, PartialEq, Eq, Debug, Clone)]
#[derive(Error, PartialEq, Eq, Debug, Clone, TryInto)]
pub enum EvalError {
/// AVL tree errors
#[error("AvlTree: {0}")]
Expand Down Expand Up @@ -65,58 +70,85 @@ pub enum EvalError {
/// Scorex serialization parsing error
#[error("Serialization parsing error: {0}")]
ScorexParsingError(#[from] ScorexParsingError),
/// Wrapped error with source span and environment
#[error("eval error: {error}, details: {details:?}")]
Wrapped {
/// eval error
error: Box<EvalError>,
/// error details
details: EvalErrorDetails,
},
/// Wrapped error with source span and source code
#[error("eval error: {0}")]
SpannedWithSource(SpannedWithSourceEvalError),
/// Wrapped error with source span
#[error("eval error: {0:?}")]
Spanned(SpannedEvalError),
}

/// Wrapped error with source span
#[derive(PartialEq, Eq, Debug, Clone)]
pub struct EvalErrorDetails {
/// source span
pub struct SpannedEvalError {
/// eval error
error: Box<EvalError>,
/// source span for the expression where error occurred
source_span: SourceSpan,
/// environment after evaluation
/// environment at the time when error occurred
env: Env,
}

/// Wrapped error with source span and source code
#[derive(PartialEq, Eq, Debug, Clone)]
pub struct SpannedWithSourceEvalError {
/// eval error
error: Box<EvalError>,
/// source span for the expression where error occurred
source_span: SourceSpan,
/// environment at the time when error occurred
env: Env,
/// source code
source: Option<String>,
source: String,
}

impl Display for SpannedWithSourceEvalError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
#[allow(clippy::unwrap_used)]
miette::set_hook(Box::new(|_| {
Box::new(
miette::MietteHandlerOpts::new()
.terminal_links(false)
.unicode(false)
.color(false)
.context_lines(5)
.tab_width(2)
.build(),
)
}))
.unwrap();
let err_msg = self.error.to_string();
let report = miette!(
labels = vec![LabeledSpan::at(self.source_span, err_msg,)],
// help = "Help msg",
"Evaluation error"
)
.with_source_code(self.source.clone());
write!(f, "{:?}", report)
}
}

impl EvalError {
/// Wrap eval error with source span
pub fn wrap(self, source_span: SourceSpan, env: Env) -> Self {
EvalError::Wrapped {
EvalError::Spanned(SpannedEvalError {
error: Box::new(self),
details: EvalErrorDetails {
source_span,
env,
source: None,
},
}
source_span,
env,
})
}

/// Wrap eval error with source code
pub fn wrap_with_src(self, source: String) -> Self {
pub fn wrap_spanned_with_src(self, source: String) -> Self {
#[allow(clippy::panic)]
match self {
EvalError::Wrapped { error, details } => EvalError::Wrapped {
error,
details: EvalErrorDetails {
source_span: details.source_span,
env: details.env,
source: Some(source),
},
},
e => EvalError::Wrapped {
error: Box::new(e),
details: EvalErrorDetails {
source_span: SourceSpan::empty(),
env: Env::empty(),
source: Some(source),
},
},
EvalError::Spanned(e) => EvalError::SpannedWithSource(SpannedWithSourceEvalError {
error: e.error,
source_span: e.source_span,
env: e.env,
source,
}),
e => panic!("Expected Spanned, got {:?}", e),
}
}
}
Expand All @@ -129,10 +161,7 @@ impl<T> ExtResultEvalError<T> for Result<T, EvalError> {
fn enrich_err(self, span: SourceSpan, env: Env) -> Result<T, EvalError> {
self.map_err(|e| match e {
// skip already wrapped errors
w @ EvalError::Wrapped {
error: _,
details: _,
} => w,
w @ EvalError::Spanned { .. } => w,
e => e.wrap(span, env),
})
}
Expand All @@ -157,39 +186,76 @@ mod tests {
use sigma_test_util::force_any_val;

use crate::eval::context::Context;
use crate::eval::error::SpannedEvalError;
use crate::eval::error::SpannedWithSourceEvalError;
use crate::eval::tests::try_eval_out;

fn check(expr: Expr, expected_tree: expect_test::Expect) {
let mut w = PosTrackingWriter::new();
let spanned_expr = expr.print(&mut w).unwrap();
dbg!(&spanned_expr);
let ctx = Rc::new(force_any_val::<Context>());
let err_raw = try_eval_out::<i32>(&spanned_expr, ctx).err().unwrap();
// let err = err_raw.wrap_with_src(w.get_buf().to_string());
let err_msg = format!("{:?}", err_raw);
expected_tree.assert_eq(&err_msg);
let err_raw: SpannedEvalError = try_eval_out::<i32>(&spanned_expr, ctx)
.err()
.unwrap()
.try_into()
.unwrap();
let err = SpannedWithSourceEvalError {
error: err_raw.error,
source_span: err_raw.source_span,
env: err_raw.env,
source: w.get_buf().to_string(),
};
expected_tree.assert_eq(&err.to_string());
}

#[ignore = "expect test fails on self-generated string"]
#[test]
fn pretty_binop_div_zero() {
let val_id = 1.into();
let lhs_val_id = 1.into();
let rhs_val_id = 2.into();
let res_val_id = 3.into();
let expr = Expr::BlockValue(
BlockValue {
items: vec![ValDef {
id: val_id,
rhs: Box::new(
BinOp {
kind: ArithOp::Divide.into(),
left: Expr::Const(1i32.into()).into(),
right: Expr::Const(0i32.into()).into(),
}
.into(),
),
}
.into()],
items: vec![
ValDef {
id: lhs_val_id,
rhs: Box::new(Expr::Const(42i32.into())),
}
.into(),
ValDef {
id: rhs_val_id,
rhs: Box::new(Expr::Const(0i32.into())),
}
.into(),
ValDef {
id: res_val_id,
rhs: Box::new(
BinOp {
kind: ArithOp::Divide.into(),
left: Box::new(
ValUse {
val_id: lhs_val_id,
tpe: SType::SInt,
}
.into(),
),
right: Box::new(
ValUse {
val_id: rhs_val_id,
tpe: SType::SInt,
}
.into(),
),
}
.into(),
),
}
.into(),
],
result: Box::new(
ValUse {
val_id,
val_id: res_val_id,
tpe: SType::SInt,
}
.into(),
Expand All @@ -200,6 +266,17 @@ mod tests {
check(
expr,
expect![[r#"
x Evaluation error
,-[1:1]
1 | {
2 | val v1 = 42
3 | val v2 = 0
4 | val v3 = v1 / v2
: ^^^|^^^
: `-- Arithmetic exception: (42) / (0) resulted in exception
5 | v3
6 | }
`----
"#]],
)
}
Expand Down
1 change: 1 addition & 0 deletions ergotree-ir/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ num256 = "0.3.1"
impl-trait-for-tuples = "0.2.0"
strum = "0.21"
strum_macros = "0.21"
miette = { workspace = true }

[features]
default = ["json"]
Expand Down
2 changes: 1 addition & 1 deletion ergotree-ir/src/pretty_printer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ impl Printer for PosTrackingWriter {
}

impl PosTrackingWriter {
const INDENT: usize = 4;
const INDENT: usize = 2;

/// Create new printer
pub fn new() -> Self {
Expand Down
6 changes: 6 additions & 0 deletions ergotree-ir/src/source_span.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ impl SourceSpan {
}
}

impl From<SourceSpan> for miette::SourceSpan {
fn from(value: SourceSpan) -> Self {
miette::SourceSpan::new(value.offset.into(), value.length.into())
}
}

/// Wrapper for Expr with source position
#[derive(PartialEq, Eq, Debug, Clone)]
pub struct Spanned<T> {
Expand Down

0 comments on commit e76a90f

Please sign in to comment.