about summary refs log tree commit diff
path: root/src/parser/expr.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/parser/expr.rs')
-rw-r--r--src/parser/expr.rs143
1 files changed, 118 insertions, 25 deletions
diff --git a/src/parser/expr.rs b/src/parser/expr.rs
index a42c7a6c765e..2fda3e93fae9 100644
--- a/src/parser/expr.rs
+++ b/src/parser/expr.rs
@@ -1,12 +1,12 @@
 use nom::character::complete::{digit1, multispace0, multispace1};
 use nom::{
-    alt, char, complete, delimited, do_parse, flat_map, many0, map, named, parse_to,
-    separated_list0, separated_list1, tag, tuple,
+    alt, call, char, complete, delimited, do_parse, flat_map, many0, map, named, opt, parse_to,
+    preceded, separated_list0, separated_list1, tag, tuple,
 };
 use pratt::{Affix, Associativity, PrattParser, Precedence};
 
-use crate::ast::{BinaryOperator, Expr, Fun, Ident, Literal, UnaryOperator};
-use crate::parser::ident;
+use crate::ast::{BinaryOperator, Binding, Expr, Fun, Ident, Literal, UnaryOperator};
+use crate::parser::{ident, type_};
 
 #[derive(Debug)]
 enum TokenTree<'a> {
@@ -158,14 +158,20 @@ named!(int(&str) -> Literal, map!(flat_map!(digit1, parse_to!(u64)), Literal::In
 
 named!(literal(&str) -> Expr, map!(alt!(int), Expr::Literal));
 
-named!(binding(&str) -> (Ident, Expr), do_parse!(
+named!(binding(&str) -> Binding, do_parse!(
     multispace0
         >> ident: ident
         >> multispace0
+        >> type_: opt!(preceded!(tuple!(tag!(":"), multispace0), type_))
+        >> multispace0
         >> char!('=')
         >> multispace0
-        >> expr: expr
-        >> (ident, expr)
+        >> body: expr
+        >> (Binding {
+            ident,
+            type_,
+            body
+        })
 ));
 
 named!(let_(&str) -> Expr, do_parse!(
@@ -203,6 +209,25 @@ named!(if_(&str) -> Expr, do_parse! (
 
 named!(ident_expr(&str) -> Expr, map!(ident, Expr::Ident));
 
+fn ascripted<'a>(
+    p: impl Fn(&'a str) -> nom::IResult<&'a str, Expr, nom::error::Error<&'a str>> + 'a,
+) -> impl Fn(&'a str) -> nom::IResult<&str, Expr, nom::error::Error<&'a str>> {
+    move |i| {
+        do_parse!(
+            i,
+            expr: p
+                >> multispace0
+                >> complete!(tag!(":"))
+                >> multispace0
+                >> type_: type_
+                >> (Expr::Ascription {
+                    expr: Box::new(expr),
+                    type_
+                })
+        )
+    }
+}
+
 named!(paren_expr(&str) -> Expr,
        delimited!(complete!(tag!("(")), expr, complete!(tag!(")"))));
 
@@ -251,7 +276,7 @@ named!(call_with_args(&str) -> Expr, do_parse!(
         })
 ));
 
-named!(simple_expr(&str) -> Expr, alt!(
+named!(simple_expr_unascripted(&str) -> Expr, alt!(
     let_ |
     if_ |
     fun_expr |
@@ -259,17 +284,24 @@ named!(simple_expr(&str) -> Expr, alt!(
     ident_expr
 ));
 
+named!(simple_expr(&str) -> Expr, alt!(
+    call!(ascripted(simple_expr_unascripted)) |
+    simple_expr_unascripted
+));
+
 named!(pub expr(&str) -> Expr, alt!(
     no_arg_call |
     call_with_args |
     map!(token_tree, |tt| {
         ExprParser.parse(&mut tt.into_iter()).unwrap()
     }) |
-    simple_expr));
+    simple_expr
+));
 
 #[cfg(test)]
 pub(crate) mod tests {
     use super::*;
+    use crate::ast::Type;
     use std::convert::TryFrom;
     use BinaryOperator::*;
     use Expr::{BinaryOp, If, Let, UnaryOp};
@@ -374,18 +406,20 @@ pub(crate) mod tests {
             res,
             Let {
                 bindings: vec![
-                    (
-                        Ident::try_from("x").unwrap(),
-                        Expr::Literal(Literal::Int(1))
-                    ),
-                    (
-                        Ident::try_from("y").unwrap(),
-                        Expr::BinaryOp {
+                    Binding {
+                        ident: Ident::try_from("x").unwrap(),
+                        type_: None,
+                        body: Expr::Literal(Literal::Int(1))
+                    },
+                    Binding {
+                        ident: Ident::try_from("y").unwrap(),
+                        type_: None,
+                        body: Expr::BinaryOp {
                             lhs: ident_expr("x"),
                             op: Mul,
                             rhs: Box::new(Expr::Literal(Literal::Int(7)))
                         }
-                    )
+                    }
                 ],
                 body: Box::new(Expr::BinaryOp {
                     lhs: Box::new(Expr::BinaryOp {
@@ -448,10 +482,11 @@ pub(crate) mod tests {
             res,
             Expr::Call {
                 fun: Box::new(Expr::Let {
-                    bindings: vec![(
-                        Ident::try_from("x").unwrap(),
-                        Expr::Literal(Literal::Int(1))
-                    )],
+                    bindings: vec![Binding {
+                        ident: Ident::try_from("x").unwrap(),
+                        type_: None,
+                        body: Expr::Literal(Literal::Int(1))
+                    }],
                     body: ident_expr("x")
                 }),
                 args: vec![Expr::Literal(Literal::Int(2))]
@@ -465,13 +500,14 @@ pub(crate) mod tests {
         assert_eq!(
             res,
             Expr::Let {
-                bindings: vec![(
-                    Ident::try_from("id").unwrap(),
-                    Expr::Fun(Box::new(Fun {
+                bindings: vec![Binding {
+                    ident: Ident::try_from("id").unwrap(),
+                    type_: None,
+                    body: Expr::Fun(Box::new(Fun {
                         args: vec![Ident::try_from("x").unwrap()],
                         body: *ident_expr("x")
                     }))
-                )],
+                }],
                 body: Box::new(Expr::Call {
                     fun: ident_expr("id"),
                     args: vec![Expr::Literal(Literal::Int(1))],
@@ -479,4 +515,61 @@ pub(crate) mod tests {
             }
         );
     }
+
+    mod ascriptions {
+        use super::*;
+
+        #[test]
+        fn bare_ascription() {
+            let res = test_parse!(expr, "1: float");
+            assert_eq!(
+                res,
+                Expr::Ascription {
+                    expr: Box::new(Expr::Literal(Literal::Int(1))),
+                    type_: Type::Float
+                }
+            )
+        }
+
+        #[test]
+        fn fn_body_ascription() {
+            let res = test_parse!(expr, "let const_1 = fn x = 1: int in const_1 2");
+            assert_eq!(
+                res,
+                Expr::Let {
+                    bindings: vec![Binding {
+                        ident: Ident::try_from("const_1").unwrap(),
+                        type_: None,
+                        body: Expr::Fun(Box::new(Fun {
+                            args: vec![Ident::try_from("x").unwrap()],
+                            body: Expr::Ascription {
+                                expr: Box::new(Expr::Literal(Literal::Int(1))),
+                                type_: Type::Int,
+                            }
+                        }))
+                    }],
+                    body: Box::new(Expr::Call {
+                        fun: ident_expr("const_1"),
+                        args: vec![Expr::Literal(Literal::Int(2))]
+                    })
+                }
+            )
+        }
+
+        #[test]
+        fn let_binding_ascripted() {
+            let res = test_parse!(expr, "let x: int = 1 in x");
+            assert_eq!(
+                res,
+                Expr::Let {
+                    bindings: vec![Binding {
+                        ident: Ident::try_from("x").unwrap(),
+                        type_: Some(Type::Int),
+                        body: Expr::Literal(Literal::Int(1))
+                    }],
+                    body: ident_expr("x")
+                }
+            )
+        }
+    }
 }