Introduce Pattern

This commit is contained in:
Richard Feldman 2019-05-22 21:54:46 -04:00
parent 89c71e9c1d
commit 91aac9a86e
3 changed files with 53 additions and 32 deletions

View file

@ -8,7 +8,8 @@ pub enum Expr {
Char(char), Char(char),
Var(String), Var(String),
Let(String, Box<Expr>, Box<Expr>), Let(Pattern, Box<Expr>, Box<Expr>),
Pattern(Pattern),
// Functions // Functions
Func(String, Box<Expr>), Func(String, Box<Expr>),
@ -18,6 +19,13 @@ pub enum Expr {
If(Box<Expr>, Box<Expr>, Box<Expr>), If(Box<Expr>, Box<Expr>, Box<Expr>),
} }
#[derive(Clone, Debug, PartialEq)]
pub enum Pattern {
Identifier(String),
Underscore
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum Operator { pub enum Operator {
Plus, Minus, Star, Slash, DoubleSlash, Equals, Plus, Minus, Star, Slash, DoubleSlash, Equals,

View file

@ -1,5 +1,5 @@
use expr::Operator; use expr::Operator;
use expr::Expr; use expr::{Expr, Pattern};
use std::char; use std::char;
use parse_state::{IndentablePosition}; use parse_state::{IndentablePosition};
@ -60,28 +60,29 @@ where I: Stream<Item = char, Position = IndentablePosition>,
many1::<Vec<_>, _>(choice((char(' '), char('\n')))).with(value(())) many1::<Vec<_>, _>(choice((char(' '), char('\n')))).with(value(()))
} }
fn indented_spaces<I>(min_indent: i32) -> impl Parser<Input = I, Output = ()> fn indented_whitespaces<I>(min_indent: i32) -> impl Parser<Input = I, Output = ()>
where I: Stream<Item = char, Position = IndentablePosition>, where I: Stream<Item = char, Position = IndentablePosition>,
I::Error: ParseError<I::Item, I::Range, I::Position> { I::Error: ParseError<I::Item, I::Range, I::Position> {
many::<Vec<_>, _>(indented_space(min_indent)).with(value(())) many::<Vec<_>, _>(indented_whitespace(min_indent)).with(value(()))
} }
fn indented_spaces1<I>(min_indent: i32) -> impl Parser<Input = I, Output = ()> fn indented_whitespaces1<I>(min_indent: i32) -> impl Parser<Input = I, Output = ()>
where I: Stream<Item = char, Position = IndentablePosition>, where I: Stream<Item = char, Position = IndentablePosition>,
I::Error: ParseError<I::Item, I::Range, I::Position> { I::Error: ParseError<I::Item, I::Range, I::Position> {
// TODO we immediately discard this Vec, so revise this to not use many1 (and thus // TODO we immediately discard this Vec, so revise this to not use many1 (and thus
// not allocate one in the first place) // not allocate one in the first place)
many1::<Vec<_>, _>(indented_space(min_indent)).with(value(())) many1::<Vec<_>, _>(indented_whitespace(min_indent)).with(value(()))
} }
fn indented_space<I>(min_indent: i32) -> impl Parser<Input = I, Output = ()> fn indented_whitespace<I>(min_indent: i32) -> impl Parser<Input = I, Output = ()>
where I: Stream<Item = char, Position = IndentablePosition>, where I: Stream<Item = char, Position = IndentablePosition>,
I::Error: ParseError<I::Item, I::Range, I::Position> { I::Error: ParseError<I::Item, I::Range, I::Position> {
choice(( choice((
char(' ').with(value(())), char(' ').with(value(())),
// If we hit a newline, it must be followed by: // If we hit a newline, it must be followed by:
// * Any number of blank lines (which contain only spaces) //
// * At least min_indent spaces, or else eof() // - Any number of blank lines (which contain only spaces)
// - At least min_indent spaces, or else eof()
char('\n') char('\n')
.skip( .skip(
// TODO we immediately discard this Vec, ... // TODO we immediately discard this Vec, ...
@ -131,10 +132,10 @@ parser! {
// followed by the operator + and another subexpression, 2 // followed by the operator + and another subexpression, 2
optional( optional(
attempt( attempt(
indented_spaces(min_indent) indented_whitespaces(min_indent)
.with(operator()) .with(operator())
.skip(whitespace()) .skip(whitespace())
.skip(indented_spaces(min_indent)) .skip(indented_whitespaces(min_indent))
.and(expr_body(min_indent)) .and(expr_body(min_indent))
) )
) )
@ -153,11 +154,11 @@ pub fn if_expr<I>(min_indent: i32) -> impl Parser<Input = I, Output = Expr>
where I: Stream<Item = char, Position = IndentablePosition>, where I: Stream<Item = char, Position = IndentablePosition>,
I::Error: ParseError<I::Item, I::Range, I::Position> I::Error: ParseError<I::Item, I::Range, I::Position>
{ {
string("if").with(indented_spaces1(min_indent)) string("if").with(indented_whitespaces1(min_indent))
.with(expr_body(min_indent)).skip(indented_spaces1(min_indent)) .with(expr_body(min_indent)).skip(indented_whitespaces1(min_indent))
.skip(string("then")).skip(indented_spaces1(min_indent)) .skip(string("then")).skip(indented_whitespaces1(min_indent))
.and(expr_body(min_indent)).skip(indented_spaces1(min_indent)) .and(expr_body(min_indent)).skip(indented_whitespaces1(min_indent))
.skip(string("else")).skip(indented_spaces1(min_indent)) .skip(string("else")).skip(indented_whitespaces1(min_indent))
.and(expr_body(min_indent)) .and(expr_body(min_indent))
.map(|((conditional, then_branch), else_branch)| .map(|((conditional, then_branch), else_branch)|
Expr::If( Expr::If(
@ -173,13 +174,13 @@ where I: Stream<Item = char, Position = IndentablePosition>,
I::Error: ParseError<I::Item, I::Range, I::Position> I::Error: ParseError<I::Item, I::Range, I::Position>
{ {
between(char('('), char(')'), between(char('('), char(')'),
indented_spaces(min_indent).with(expr_body(min_indent)).skip(indented_spaces(min_indent)) indented_whitespaces(min_indent).with(expr_body(min_indent)).skip(indented_whitespaces(min_indent))
).and( ).and(
// Parenthetical expressions can optionally be followed by // Parenthetical expressions can optionally be followed by
// whitespace and an expr, meaning this is function application! // whitespace and an expr, meaning this is function application!
optional( optional(
attempt( attempt(
indented_spaces1(min_indent) indented_whitespaces1(min_indent)
// Keywords like "then" and "else" are not function application! // Keywords like "then" and "else" are not function application!
.skip(not_followed_by(choice((string("then"), string("else"))))) .skip(not_followed_by(choice((string("then"), string("else")))))
.with(expr_body(min_indent)) .with(expr_body(min_indent))
@ -213,8 +214,8 @@ where I: Stream<Item = char, Position = IndentablePosition>,
I::Error: ParseError<I::Item, I::Range, I::Position> I::Error: ParseError<I::Item, I::Range, I::Position>
{ {
attempt( attempt(
ident().and(indentation()).message("malformed identifier inside declaration") pattern().and(indentation())
.skip(whitespace()).message("whitespace after identifier") .skip(whitespace())
.and( .and(
char('=').with(indentation()) char('=').with(indentation())
// If the "=" after the identifier turns out to be // If the "=" after the identifier turns out to be
@ -223,7 +224,7 @@ where I: Stream<Item = char, Position = IndentablePosition>,
) )
) )
.skip(whitespace()) .skip(whitespace())
.then(move |((var_name, original_indent), equals_sign_indent)| { .then(move |((var_pattern, original_indent), equals_sign_indent)| {
if original_indent < min_indent { if original_indent < min_indent {
unexpected_any("this declaration is outdented too far").left() unexpected_any("this declaration is outdented too far").left()
} else if equals_sign_indent < original_indent /* `<` because '=' should be same indent or greater */ { } else if equals_sign_indent < original_indent /* `<` because '=' should be same indent or greater */ {
@ -236,7 +237,7 @@ where I: Stream<Item = char, Position = IndentablePosition>,
if in_expr_indent != original_indent { if in_expr_indent != original_indent {
unexpected_any("the return expression was indented differently from the original declaration").left() unexpected_any("the return expression was indented differently from the original declaration").left()
} else { } else {
value(Expr::Let(var_name.to_owned(), Box::new(var_expr), Box::new(in_expr))).right() value(Expr::Let(var_pattern.to_owned(), Box::new(var_expr), Box::new(in_expr))).right()
} }
}).right() }).right()
} }
@ -250,7 +251,7 @@ where I: Stream<Item = char, Position = IndentablePosition>,
ident() ident()
.and(optional( .and(optional(
attempt( attempt(
indented_spaces1(min_indent) indented_whitespaces1(min_indent)
.skip(not_followed_by(choice((string("then"), string("else"), string("="))))) .skip(not_followed_by(choice((string("then"), string("else"), string("=")))))
.with(expr_body(min_indent)) .with(expr_body(min_indent))
) )
@ -262,6 +263,16 @@ where I: Stream<Item = char, Position = IndentablePosition>,
) )
} }
pub fn pattern<I>() -> impl Parser<Input = I, Output = Pattern>
where I: Stream<Item = char, Position = IndentablePosition>,
I::Error: ParseError<I::Item, I::Range, I::Position>
{
choice((
char('_').map(|_| Pattern::Underscore),
ident().map(|name| Pattern::Identifier(name))
))
}
pub fn ident<I>() -> impl Parser<Input = I, Output = String> pub fn ident<I>() -> impl Parser<Input = I, Output = String>
where I: Stream<Item = char, Position = IndentablePosition>, where I: Stream<Item = char, Position = IndentablePosition>,
I::Error: ParseError<I::Item, I::Range, I::Position> I::Error: ParseError<I::Item, I::Range, I::Position>

View file

@ -6,7 +6,8 @@ extern crate roc;
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use roc::expr::Expr::*; use roc::expr::Expr::*;
use roc::expr::Expr; use roc::expr::Pattern::*;
use roc::expr::{Expr};
use roc::expr::Operator::*; use roc::expr::Operator::*;
use roc::parse; use roc::parse;
use roc::parse_state::{IndentablePosition}; use roc::parse_state::{IndentablePosition};
@ -616,7 +617,7 @@ mod tests {
// let x = 5 in -10 // let x = 5 in -10
parse_standalone("x = 5\n-10"), parse_standalone("x = 5\n-10"),
Ok(( Ok((
Let("x".to_string(), Box::new(Int(5)), Box::new(Int(-10))), Let(Identifier("x".to_string()), Box::new(Int(5)), Box::new(Int(-10))),
"") "")
) )
); );
@ -625,7 +626,7 @@ mod tests {
// let x = 5 in 10 // let x = 5 in 10
parse_standalone("x=5\n-10"), parse_standalone("x=5\n-10"),
Ok(( Ok((
Let("x".to_string(), Box::new(Int(5)), Box::new(Int(-10))), Let(Identifier("x".to_string()), Box::new(Int(5)), Box::new(Int(-10))),
"") "")
) )
); );
@ -637,7 +638,7 @@ mod tests {
// let x = 5 + 10 in -20 // let x = 5 + 10 in -20
parse_standalone("x =(5 + 10)\n-20"), parse_standalone("x =(5 + 10)\n-20"),
Ok(( Ok((
Let("x".to_string(), Let(Identifier("x".to_string()),
Box::new(Operator(Box::new(Int(5)), Plus, Box::new(Int(10)))), Box::new(Operator(Box::new(Int(5)), Plus, Box::new(Int(10)))),
Box::new(Int(-20))), Box::new(Int(-20))),
"") "")
@ -648,7 +649,7 @@ mod tests {
// let x = 5 + 10 in -20 // let x = 5 + 10 in -20
parse_standalone("x= 5 + 10\n-20"), parse_standalone("x= 5 + 10\n-20"),
Ok(( Ok((
Let("x".to_string(), Let(Identifier("x".to_string()),
Box::new(Operator(Box::new(Int(5)), Plus, Box::new(Int(10)))), Box::new(Operator(Box::new(Int(5)), Plus, Box::new(Int(10)))),
Box::new(Int(-20))), Box::new(Int(-20))),
"") "")
@ -659,7 +660,7 @@ mod tests {
// let x = 5 + 10 in -20 // let x = 5 + 10 in -20
parse_standalone("x=5\n + 10\n-20"), parse_standalone("x=5\n + 10\n-20"),
Ok(( Ok((
Let("x".to_string(), Let(Identifier("x".to_string()),
Box::new(Operator(Box::new(Int(5)), Plus, Box::new(Int(10)))), Box::new(Operator(Box::new(Int(5)), Plus, Box::new(Int(10)))),
Box::new(Int(-20))), Box::new(Int(-20))),
"") "")
@ -681,9 +682,10 @@ mod tests {
// let x = 5 in let y = 12 in 3 // let x = 5 in let y = 12 in 3
parse_standalone("x = 5\ny = 12\n3"), parse_standalone("x = 5\ny = 12\n3"),
Ok(( Ok((
Let("x".to_string(), Box::new(Int(5)), Let(Identifier("x".to_string()),
Box::new(Int(5)),
Box::new( Box::new(
Let("y".to_string(), Box::new(Int(12)), Let(Identifier("y".to_string()), Box::new(Int(12)),
Box::new(Int(3)) Box::new(Int(3))
))), ))),
"") "")
@ -696,7 +698,7 @@ mod tests {
assert_eq!( assert_eq!(
parse_standalone("x=5\nx"), parse_standalone("x=5\nx"),
Ok(( Ok((
Let("x".to_string(), Box::new(Int(5)), Box::new(Var("x".to_string()))), Let(Identifier("x".to_string()), Box::new(Int(5)), Box::new(Var("x".to_string()))),
"") "")
) )
); );