aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJJ2023-04-11 21:10:31 +0000
committerJJ2023-04-11 21:10:31 +0000
commit0c980d4575a210f0ff4835500bc0bee315b66a10 (patch)
tree1ac2ce91e3664b9aa53a5830ff8bc76732df1ef3
parentff43d1b9ac0b3fbb34cc7adb82504186e868f03d (diff)
add support for function types to parse_lambda
-rw-r--r--src/parser.rs50
-rw-r--r--tests/test_parser.rs56
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);
+}