Skip to content
Snippets Groups Projects
Commit bcc2b5a4 authored by Per Lindgren's avatar Per Lindgren
Browse files

fallible iterator

parent cac3bbd7
No related branches found
No related tags found
No related merge requests found
...@@ -5,7 +5,7 @@ use std::fmt; ...@@ -5,7 +5,7 @@ use std::fmt;
pub type Id = String; pub type Id = String;
// println!("{:?}", ..) // println!("{:?}", ..)
#[derive(Debug)] #[derive(Debug, Clone)]
pub enum NumOrId { pub enum NumOrId {
Num(i32), Num(i32),
Id(Id), Id(Id),
...@@ -24,7 +24,7 @@ impl fmt::Display for NumOrId { ...@@ -24,7 +24,7 @@ impl fmt::Display for NumOrId {
pub type Stmts = Vec<Stmt>; pub type Stmts = Vec<Stmt>;
#[derive(Debug)] #[derive(Debug, Clone)]
pub enum Stmt { pub enum Stmt {
Let(Id, NumOrId), Let(Id, NumOrId),
If(Id, Stmts, Option<Stmts>), If(Id, Stmts, Option<Stmts>),
......
...@@ -30,20 +30,73 @@ fn parse_num_or_id() { ...@@ -30,20 +30,73 @@ fn parse_num_or_id() {
type Error = String; type Error = String;
fn type_check(stmts: &Stmts) -> Result<(), Error> { fn type_check(stmts: &Stmts) -> Result<Id, Error> {
let r: Result<(), Error> = stmts.iter().try_for_each(|i| match i { let mut last_id = "".to_string();
Stmt::Let(_, _) => { let mut err: Option<Stmt> = None;
println!("let"); stmts.iter().for_each(|stmt| match stmt {
Stmt::Let(id, _) => {
println!("let {} ...", id);
last_id = id.to_string();
}
Stmt::If(_, _, __) => {
println!("if has an error");
err = Some(stmt.clone());
}
});
println!("here we can do something before returning");
match err {
Some(stmt) => Err(format!("error found in statement {:?}", stmt)),
None => Ok(last_id),
}
}
// the above is not ideal, since we have to do a lot of book keeping,
// also we cannot easily "abort", and thus we cont. even after spotting the error we want to return
fn type_check2(stmts: &Stmts) -> Result<Id, Error> {
let mut last_id = "".to_string();
let res: Result<Id, Error> = stmts
.iter()
.try_for_each(|stmt| match stmt {
Stmt::Let(id, _) => {
println!("let {} ...", id);
last_id = id.to_string();
Ok(()) Ok(())
} }
Stmt::If(_, _, __) => { Stmt::If(_, _, __) => {
println!("if"); println!("if has an error");
Err("error in if".to_string())? Err(format!("error found in statement {:?}", stmt))
}
})
.map(|_| Ok(last_id))?;
println!("here we can do something before returning");
res
}
// this is quite good, we can get rid of some book keeping, and we iterate only up to the point where an error occurs.
// we have to convert the Ok(()) into the last Id (using Result::map)
// IMHO Result::map should have been named map_ok, since that is what is does, and symmetric to map_err.
fn type_check3(stmts: &Stmts) -> Result<Id, Error> {
let res =
stmts
.iter()
.try_fold::<_, _, Result<Id, Error>>("".to_string(), |_, stmt| match stmt {
Stmt::Let(id, _) => {
println!("let {} ...", id);
id.to_string();
Ok(id.to_string())
}
Stmt::If(_, _, __) => {
println!("if has an error");
Err(format!("error found in statement {:?}", stmt))
} }
}); });
println!("here we can do something before returning"); println!("here we can do something before returning");
r res
} }
// this is quite good, we can get rid of some book keeping, and we iterate only up to the point where an error occurs.
// we have to convert the Ok(()) into the last Id (using Result::map)
// IMHO Result::map should have been named map_ok, since that is what is does, and symmetric to map_err.
#[test] #[test]
fn test_stmts() { fn test_stmts() {
...@@ -53,5 +106,7 @@ fn test_stmts() { ...@@ -53,5 +106,7 @@ fn test_stmts() {
Stmt::If("b".to_string(), Stmts::new(), None), Stmt::If("b".to_string(), Stmts::new(), None),
Stmt::Let("c".to_string(), NumOrId::Num(3)), Stmt::Let("c".to_string(), NumOrId::Num(3)),
]; ];
type_check(&stmts).unwrap(); println!("type_check {:?}", type_check(&stmts));
println!("type_check2 {:?}", type_check2(&stmts));
println!("type_check3 {:?}", type_check2(&stmts));
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment