diff --git a/src/ast/ast.rs b/src/ast/ast.rs index b18fb6070692a9a4fe2cec834a4a4995aaa12147..9daa62ab72963ca4ff3e6fd4258579bcd6df589b 100644 --- a/src/ast/ast.rs +++ b/src/ast/ast.rs @@ -5,7 +5,7 @@ use std::fmt; pub type Id = String; // println!("{:?}", ..) -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum NumOrId { Num(i32), Id(Id), @@ -24,7 +24,7 @@ impl fmt::Display for NumOrId { pub type Stmts = Vec<Stmt>; -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum Stmt { Let(Id, NumOrId), If(Id, Stmts, Option<Stmts>), diff --git a/src/ast/main.rs b/src/ast/main.rs index 825a7ff3e4a83504fcc4fdcebde9ba51fc0b3ec5..a669a9b140452b948a7a82bf49cb4bf816c12b97 100644 --- a/src/ast/main.rs +++ b/src/ast/main.rs @@ -30,20 +30,73 @@ fn parse_num_or_id() { type Error = String; -fn type_check(stmts: &Stmts) -> Result<(), Error> { - let r: Result<(), Error> = stmts.iter().try_for_each(|i| match i { - Stmt::Let(_, _) => { - println!("let"); - Ok(()) +fn type_check(stmts: &Stmts) -> Result<Id, Error> { + let mut last_id = "".to_string(); + let mut err: Option<Stmt> = None; + stmts.iter().for_each(|stmt| match stmt { + Stmt::Let(id, _) => { + println!("let {} ...", id); + last_id = id.to_string(); } Stmt::If(_, _, __) => { - println!("if"); - Err("error in if".to_string())? + println!("if has an error"); + err = Some(stmt.clone()); } }); println!("here we can do something before returning"); - r + 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(()) + } + Stmt::If(_, _, __) => { + println!("if has an error"); + 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"); + 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] fn test_stmts() { @@ -53,5 +106,7 @@ fn test_stmts() { Stmt::If("b".to_string(), Stmts::new(), None), 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)); }