diff options
author | Griffin Smith <root@gws.fyi> | 2021-03-13T18·12-0500 |
---|---|---|
committer | Griffin Smith <root@gws.fyi> | 2021-03-13T18·12-0500 |
commit | f8beda81fbe8d04883aee71ff4ea078f897c6de4 (patch) | |
tree | ad61046d7e86c8a71381ee6b936fcd46ec3a89ac /src/parser | |
parent | 3dff189499af1ddd60d8fc128b794d15f1cb19ae (diff) |
Allow exprs+bindings to optionally be ascripted
Diffstat (limited to '')
-rw-r--r-- | src/parser/expr.rs | 143 | ||||
-rw-r--r-- | src/parser/mod.rs | 2 | ||||
-rw-r--r-- | src/parser/type_.rs | 104 |
3 files changed, 224 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") + } + ) + } + } } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 3e162d449320..af7dff6ff213 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -5,9 +5,11 @@ use nom::{alt, char, complete, do_parse, many0, named, separated_list0, tag}; #[macro_use] mod macros; mod expr; +mod type_; use crate::ast::{Decl, Fun, Ident}; pub use expr::expr; +pub use type_::type_; pub type Error = nom::Err<nom::error::Error<String>>; diff --git a/src/parser/type_.rs b/src/parser/type_.rs new file mode 100644 index 000000000000..076df7d6bd55 --- /dev/null +++ b/src/parser/type_.rs @@ -0,0 +1,104 @@ +use nom::character::complete::{multispace0, multispace1}; +use nom::{alt, delimited, do_parse, map, named, opt, separated_list0, tag, terminated, tuple}; + +use crate::ast::{FunctionType, Type}; + +named!(function_type(&str) -> Type, do_parse!( + tag!("fn") + >> multispace1 + >> args: map!(opt!(terminated!(separated_list0!( + tuple!( + multispace0, + tag!(","), + multispace0 + ), + type_ + ), multispace1)), |args| args.unwrap_or_default()) + >> tag!("->") + >> multispace1 + >> ret: type_ + >> (Type::Function(FunctionType { + args, + ret: Box::new(ret) + })) +)); + +named!(pub type_(&str) -> Type, alt!( + tag!("int") => { |_| Type::Int } | + tag!("float") => { |_| Type::Float } | + tag!("bool") => { |_| Type::Bool } | + function_type | + delimited!( + tuple!(tag!("("), multispace0), + type_, + tuple!(tag!(")"), multispace0) + ) +)); + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn simple_types() { + assert_eq!(test_parse!(type_, "int"), Type::Int); + assert_eq!(test_parse!(type_, "float"), Type::Float); + assert_eq!(test_parse!(type_, "bool"), Type::Bool); + } + + #[test] + fn no_arg_fn_type() { + assert_eq!( + test_parse!(type_, "fn -> int"), + Type::Function(FunctionType { + args: vec![], + ret: Box::new(Type::Int) + }) + ); + } + + #[test] + fn fn_type_with_args() { + assert_eq!( + test_parse!(type_, "fn int, bool -> int"), + Type::Function(FunctionType { + args: vec![Type::Int, Type::Bool], + ret: Box::new(Type::Int) + }) + ); + } + + #[test] + fn fn_taking_fn() { + assert_eq!( + test_parse!(type_, "fn fn int, bool -> bool, float -> float"), + Type::Function(FunctionType { + args: vec![ + Type::Function(FunctionType { + args: vec![Type::Int, Type::Bool], + ret: Box::new(Type::Bool) + }), + Type::Float + ], + ret: Box::new(Type::Float) + }) + ) + } + + #[test] + fn parenthesized() { + assert_eq!( + test_parse!(type_, "fn (fn int, bool -> bool), float -> float"), + Type::Function(FunctionType { + args: vec![ + Type::Function(FunctionType { + args: vec![Type::Int, Type::Bool], + ret: Box::new(Type::Bool) + }), + Type::Float + ], + ret: Box::new(Type::Float) + }) + ) + } +} |