diff options
author | JJ | 2023-04-11 21:10:31 +0000 |
---|---|---|
committer | JJ | 2023-04-11 21:10:31 +0000 |
commit | 0c980d4575a210f0ff4835500bc0bee315b66a10 (patch) | |
tree | 1ac2ce91e3664b9aa53a5830ff8bc76732df1ef3 | |
parent | ff43d1b9ac0b3fbb34cc7adb82504186e868f03d (diff) |
add support for function types to parse_lambda
-rw-r--r-- | src/parser.rs | 50 | ||||
-rw-r--r-- | tests/test_parser.rs | 56 |
2 files changed, 86 insertions, 20 deletions
diff --git a/src/parser.rs b/src/parser.rs index 2cb2734..7739a85 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -29,11 +29,11 @@ pub fn parse_lambda(input: &str) -> Result<Expression, peg::error::ParseError<pe // this is kinda awful, i miss my simple nim pegs peg::parser! { grammar lambda() for str { - rule identifier() -> String + rule ident() -> String = i:['a'..='z' | 'A'..='Z' | '0'..='9']+ { i.iter().collect::<String>() } - rule constant() -> Expression + rule cons() -> Expression = p:"-"? c:['0'..='9']+ { let value = c.iter().collect::<String>().parse::<Value>().unwrap(); Expression::Constant { @@ -56,55 +56,69 @@ pub fn parse_lambda(input: &str) -> Result<Expression, peg::error::ParseError<pe rule boolean() -> Type = k:"bool" {Type::Boolean} rule natural() -> Type = k:"nat" {Type::Natural} rule integer() -> Type = k:"int" {Type::Integer} + // fixme: brackets are necessary here + rule function() -> Type = "(" f:kind() " "* "->" " "* t:kind() ")" { + Type::Function { from: Box::new(f), to: Box::new(t) } + } rule kind() -> Type - = k:(empty() / unit() / boolean() / natural() / integer()) { + = k:(function() / empty() / unit() / boolean() / natural() / integer()) { k } - rule annotation() -> Expression - = e:(conditional() / abstraction() / application() / constant() / variable()) " "* ":" " "* k:kind() { + rule ann() -> Expression + = e:(bracketed() / (cond() / abs() / app() / cons() / var())) " "* ":" " "* k:kind() { Expression::Annotation { expr: Box::new(e), kind: k } } - rule variable() -> Expression - = v:identifier() { + rule var() -> Expression + = v:ident() { Expression::Variable { id: v } } - rule abstraction() -> Expression - = ("λ" / "lambda ") " "* p:identifier() " "* "." " "* f:expression() { + rule abs() -> Expression + = ("λ" / "lambda ") " "* p:ident() " "* "." " "* f:expr() { Expression::Abstraction { param: p, func: Box::new(f) } } // fixme: more cases should parse, but how? - rule application() -> Expression - = "(" f:(annotation() / abstraction()) ")" " "* a:expression() { + rule app() -> Expression + = "(" f:expr() ")" " "* a:expr() { Expression::Application { func: Box::new(f), arg: Box::new(a) } } - rule conditional() -> Expression - = "if" " "+ c:expression() " "+ "then" " "+ t:expression() " "+ "else" " "+ e:expression() { + rule cond() -> Expression + = "if" " "+ c:expr() " "+ "then" " "+ t:expr() " "+ "else" " "+ e:expr() { Expression::Conditional { if_cond: Box::new(c), if_then: Box::new(t), if_else: Box::new(e) } } - pub rule expression() -> Expression - = e:(conditional() / annotation() / abstraction() / application() / constant() / variable()) { + rule unbracketed() -> Expression + = e:(cond() / ann() / abs() / app() / cons() / var()) { + e + } + rule bracketed() -> Expression + = "(" " "* e:(cond() / ann() / abs() / app() / cons() / var()) " "* ")" { + e + } + pub rule expr() -> Expression + // what the fuck + // why doesn't = " "* e:(unbracketed() / bracketed()) " "* work + = e:(unbracketed() / bracketed()) { e } - pub rule ast() -> Vec<Expression> - = expression() ** ("\n"+) + // pub rule ast() -> Vec<Expression> + // = expr() ** ("\n"+) } } - return lambda::expression(input.trim()); + return lambda::expr(input.trim()); } /// Converts a whitespace-indented language into a regular bracketed language for matching with PEGs diff --git a/tests/test_parser.rs b/tests/test_parser.rs index b096597..c619745 100644 --- a/tests/test_parser.rs +++ b/tests/test_parser.rs @@ -1,3 +1,5 @@ +#![allow(non_upper_case_globals)] + use chrysanthemum::ast::*; use chrysanthemum::parser::*; use chrysanthemum::util::*; @@ -26,7 +28,7 @@ fn test_simple_expressions() { assert_eq!(parse_lambda("λ x.y"), Ok(Abs("x", Var("y")))); assert_eq!(parse_lambda("λx.y"), Ok(Abs("x", Var("y")))); assert_eq!(parse_lambda("lambda x . y"), Ok(Abs("x", Var("y")))); - assert!(parse_lambda("(λx.y)").is_err()); // fixme: should be fine + assert_eq!(parse_lambda("(λx.y)"), Ok(Abs("x", Var("y")))); assert_eq!(parse_lambda("(λx.y) x"), Ok(App(Abs("x", Var("y")), Var("x")))); assert_eq!(parse_lambda("(λx.y) x"), Ok(App(Abs("x", Var("y")), Var("x")))); assert_eq!(parse_lambda("if x then y else z"), Ok(Cond(Var("x"), Var("y"), Var("z")))); @@ -40,7 +42,57 @@ fn test_complex_expressions() { } #[test] -fn test_file() { +fn test_complex_annotations() { + assert_eq!(parse_lambda("(lambda x . y) : int"), Ok(Ann(Abs("x", Var("y")), Type::Integer))); + assert_eq!(parse_lambda("((lambda x. y): (int -> int)) 413: int"), Ok(App(Ann(Abs("x", Var("y")), Type::Function { from: Box::new(Type::Integer), to: Box::new(Type::Integer) }), Ann(Const(413, Type::Empty), Type::Integer)))); + assert_eq!(parse_lambda("if 0: bool then 1: bool else 2: int"), Ok(Cond(Ann(Const(0, Type::Empty), Type::Boolean), Ann(Const(1, Type::Empty), Type::Boolean), Ann(Const(2, Type::Empty), Type::Integer)))); + assert_eq!(parse_lambda("(lambda x. if x then 1: bool else 0: bool): (int -> bool)"), Ok(Ann(Abs("x", Cond(Var("x"), Ann(Const(1, Type::Empty), Type::Boolean), Ann(Const(0, Type::Empty), Type::Boolean))), Type::Function { from: Box::new(Type::Integer), to: Box::new(Type::Boolean) }))); + assert_eq!(parse_lambda("(lambda x. if x then 1: int else 0: int): (bool -> int)"), Ok(Ann(Abs("x", Cond(Var("x"), Ann(Const(1, Type::Empty), Type::Integer), Ann(Const(0, Type::Empty), Type::Integer))), Type::Function { from: Box::new(Type::Boolean), to: Box::new(Type::Integer) }))); + assert_eq!(parse_lambda("(lambda x. if x then 0 else 1): (bool -> bool)"), Ok(Ann(Abs("x", Cond(Var("x"), Const(0, Type::Empty), Const(1, Type::Empty))), Type::Function { from: Box::new(Type::Boolean), to: Box::new(Type::Boolean) }))); +} + +const program: &'static str = +"func foo() = + bar + if this: + that + else: + this +hello +foo +bar +baz +func foo = + this + if that: + then this +"; + +const lexed: &'static str = +"func foo() = { + bar; + if this: { + that; + } + else: { + this; + } } +hello; +foo; +bar; +baz; +func foo = { + this; + if that: { + then this; + } +}"; +#[test] +fn test_lexer() { + let result = lex(program); + assert!(result.is_ok()); + assert_eq!(result.unwrap(), lexed); +} |