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

epic fail

parent bfb377f0
No related branches found
No related tags found
No related merge requests found
...@@ -3,29 +3,3 @@ use std::fmt; ...@@ -3,29 +3,3 @@ use std::fmt;
// ast // ast
pub type Id = String; pub type Id = String;
// println!("{:?}", ..)
#[derive(Debug, Clone)]
pub enum NumOrId {
Num(i32),
Id(Id),
}
// println!("{}", ..)
impl fmt::Display for NumOrId {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
NumOrId::Num(i) => write!(f, "{}", i)?,
NumOrId::Id(s) => write!(f, "{}", s)?,
};
Ok(())
}
}
pub type Stmts = Vec<Stmt>;
#[derive(Debug, Clone)]
pub enum Stmt {
Let(Id, NumOrId),
If(Id, Stmts, Option<Stmts>),
}
...@@ -8,137 +8,12 @@ pub mod ast; ...@@ -8,137 +8,12 @@ pub mod ast;
use ast::*; use ast::*;
fn main() { fn main() {
println!("minimal"); let s = "
println!("{:?}", NumOrIdParser::new().parse("123")); {
println!("{:?}", NumOrIdParser::new().parse("a1_a")); let ;
;
println!("{}", NumOrIdParser::new().parse("123").unwrap());
println!("{}", NumOrIdParser::new().parse("a1_a").unwrap());
}
#[test]
fn parse_num_or_id() {
assert_eq!(
format!("{}", NumOrIdParser::new().parse("123").unwrap()),
"123"
);
assert_eq!(
format!("{}", NumOrIdParser::new().parse("a1_a").unwrap()),
"a1_a"
);
}
type Error = String;
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 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(())
}
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("".to_string(), |_, stmt| match stmt {
Stmt::Let(id, _) => {
println!("let {} ...", id);
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 likely as good as it gets using Rust.
// the `try_fold`, aborts on an error directly.
// we need to give an initial value for the accumulator ("".to_string())
// in this case we don't accumulate, so the first closure parameter is discarded (`_`)
// instead we just overwrite the accumatore with the latest seen identifier.
fn type_check4(stmts: &Stmts) -> Result<Id, Error> {
let res = stmts
.iter()
.try_fold("".to_string(), |_, stmt| match stmt {
Stmt::Let(id, _) => {
println!("let {} ...", id);
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");
Ok(res)
}
// A slight alternation av type_check3, here we make an early return
// Notice that the type of `res` has changed.
#[test]
fn test_stmts() {
let stmts = vec![
Stmt::Let("a".to_string(), NumOrId::Num(1)),
Stmt::Let("b".to_string(), NumOrId::Num(2)),
Stmt::If("b".to_string(), Stmts::new(), None),
Stmt::Let("c".to_string(), NumOrId::Num(3)),
];
println!("type_check {:?}", type_check(&stmts));
println!("type_check2 {:?}", type_check2(&stmts));
println!("type_check3 {:?}", type_check3(&stmts));
println!("type_check4 {:?}", type_check4(&stmts));
}
#[test] println!("{:?}", BlockExpressionParser::new().parse(s));
fn test_stmts_without_error() {
let stmts = vec![
Stmt::Let("a".to_string(), NumOrId::Num(1)),
Stmt::Let("b".to_string(), NumOrId::Num(2)),
Stmt::Let("c".to_string(), NumOrId::Num(3)),
];
println!("type_check {:?}", type_check(&stmts));
println!("type_check2 {:?}", type_check2(&stmts));
println!("type_check3 {:?}", type_check3(&stmts));
println!("type_check4 {:?}", type_check4(&stmts));
} }
...@@ -4,15 +4,44 @@ use crate::ast::*; ...@@ -4,15 +4,44 @@ use crate::ast::*;
grammar; grammar;
pub NumOrId: NumOrId = { pub ExpressionWithoutBlock: () = {
Num => NumOrId::Num(<>), LiteralExpression => ()
Id => NumOrId::Id(<>),
} }
pub LiteralExpression = {
Num
Id
}
ExpressionWithBlock: () = {
BlockExpression => ()
}
pub BlockExpression: ()= {
"{" Statements? "}" => ()
}
pub Statements: () = {
Statement+ => (),
Statement+ ExpressionWithoutBlock => (),
ExpressionWithBlock => ()
}
pub Statement: () = {
";" => (),
"let" ";" => (),
ExpressionStatement => ()
}
pub ExpressionStatement: () = {
ExpressionWithoutBlock ";" => (),
ExpressionWithBlock ";"? => ()
}
pub Num: i32 = { pub Num: i32 = {
r"[0-9]+" => i32::from_str(<>).unwrap(), r"[0-9]+" => i32::from_str(<>).unwrap(),
}; };
pub Id: String = { pub Id: String = {
r"([a-z]|[A-Z])([a-z]|[A-Z]|[0-9]|_)*" => String::from_str(<>).unwrap(), r"([a-z]|[A-Z])([a-z]|[A-Z]|[0-9]|_)*" => String::from_str(<>).unwrap(),
}; };
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment