mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-01 07:41:12 +00:00
Introduce Pattern
This commit is contained in:
parent
89c71e9c1d
commit
91aac9a86e
3 changed files with 53 additions and 32 deletions
10
src/expr.rs
10
src/expr.rs
|
@ -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,
|
||||||
|
|
55
src/parse.rs
55
src/parse.rs
|
@ -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>
|
||||||
|
|
|
@ -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()))),
|
||||||
"")
|
"")
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue