mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-01 07:41:12 +00:00
Add variants
This commit is contained in:
parent
1aaab2dc17
commit
7aab332650
3 changed files with 91 additions and 14 deletions
26
src/eval.rs
26
src/eval.rs
|
@ -27,11 +27,6 @@ pub fn scoped_eval(expr: Expr, vars: &HashMap<String, Rc<Expr>>) -> Expr {
|
||||||
} else {
|
} else {
|
||||||
// Create a new scope containing the new declaration.
|
// Create a new scope containing the new declaration.
|
||||||
let mut new_vars = (*vars).clone();
|
let mut new_vars = (*vars).clone();
|
||||||
// TODO traverse definition searching for Closure exprs. If we
|
|
||||||
// find any, traverse their bodies and resolve as many Var
|
|
||||||
// and Func exprs as we can using current vars map. (If we
|
|
||||||
// don't find the value in the vars map, np - keep going.)
|
|
||||||
// In this way, we have inlined "closing over" those vars!
|
|
||||||
new_vars.insert(name, Rc::new(scoped_eval(*definition, vars)));
|
new_vars.insert(name, Rc::new(scoped_eval(*definition, vars)));
|
||||||
|
|
||||||
// Evaluate in_expr with that new scope's variables.
|
// Evaluate in_expr with that new scope's variables.
|
||||||
|
@ -39,6 +34,10 @@ pub fn scoped_eval(expr: Expr, vars: &HashMap<String, Rc<Expr>>) -> Expr {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
Let(Variant(name, patterns), definition, in_expr) => {
|
||||||
|
panic!("Pattern matching on variants is not yet supported!");
|
||||||
|
},
|
||||||
|
|
||||||
Let(Underscore, definition, in_expr) => {
|
Let(Underscore, definition, in_expr) => {
|
||||||
// Faithfully eval this, but discard its result.
|
// Faithfully eval this, but discard its result.
|
||||||
scoped_eval(*definition, vars);
|
scoped_eval(*definition, vars);
|
||||||
|
@ -56,6 +55,18 @@ pub fn scoped_eval(expr: Expr, vars: &HashMap<String, Rc<Expr>>) -> Expr {
|
||||||
scoped_eval(Apply(Box::new(func_expr), args), vars)
|
scoped_eval(Apply(Box::new(func_expr), args), vars)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
ApplyVariant(_, None) => expr, // This is all we do - for now...
|
||||||
|
|
||||||
|
ApplyVariant(name, Some(args)) => {
|
||||||
|
let mut evaluated_args = Vec::with_capacity(args.len());
|
||||||
|
|
||||||
|
for arg in args {
|
||||||
|
evaluated_args.push(scoped_eval(arg, vars))
|
||||||
|
}
|
||||||
|
|
||||||
|
ApplyVariant(name, Some(evaluated_args))
|
||||||
|
}
|
||||||
|
|
||||||
Apply(func_expr, args) => {
|
Apply(func_expr, args) => {
|
||||||
match *func_expr.clone() {
|
match *func_expr.clone() {
|
||||||
Closure(arg_patterns, body) => {
|
Closure(arg_patterns, body) => {
|
||||||
|
@ -69,6 +80,11 @@ pub fn scoped_eval(expr: Expr, vars: &HashMap<String, Rc<Expr>>) -> Expr {
|
||||||
Identifier(name) => {
|
Identifier(name) => {
|
||||||
let new_val = scoped_eval((*args.get(index).unwrap()).clone(), vars);
|
let new_val = scoped_eval((*args.get(index).unwrap()).clone(), vars);
|
||||||
|
|
||||||
|
new_vars.insert(name.clone(), Rc::new(new_val));
|
||||||
|
}
|
||||||
|
Variant(name, patterns) => {
|
||||||
|
let new_val = scoped_eval((*args.get(index).unwrap()).clone(), vars);
|
||||||
|
|
||||||
new_vars.insert(name.clone(), Rc::new(new_val));
|
new_vars.insert(name.clone(), Rc::new(new_val));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,10 @@ pub enum Expr {
|
||||||
Operator(Box<Expr>, Operator, Box<Expr>),
|
Operator(Box<Expr>, Operator, Box<Expr>),
|
||||||
Closure(Vec<Pattern>, Box<Expr>),
|
Closure(Vec<Pattern>, Box<Expr>),
|
||||||
|
|
||||||
|
// Sum Types
|
||||||
|
ApplyVariant(String, Option<Vec<Expr>>),
|
||||||
|
|
||||||
|
|
||||||
If(Box<Expr>, Box<Expr>, Box<Expr>),
|
If(Box<Expr>, Box<Expr>, Box<Expr>),
|
||||||
Error(Problem),
|
Error(Problem),
|
||||||
}
|
}
|
||||||
|
@ -80,6 +84,7 @@ pub enum Problem {
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub enum Pattern {
|
pub enum Pattern {
|
||||||
Identifier(String),
|
Identifier(String),
|
||||||
|
Variant(String, Option<Vec<Pattern>>),
|
||||||
Underscore
|
Underscore
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
74
src/parse.rs
74
src/parse.rs
|
@ -21,7 +21,6 @@ pub fn parse_string(string: &str) -> Result<Expr, combine::easy::Errors<char, &s
|
||||||
expr().skip(eof()).easy_parse(parse_state).map(|( expr, _ )| expr)
|
expr().skip(eof()).easy_parse(parse_state).map(|( expr, _ )| expr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn expr<I>() -> impl Parser<Input = I, Output = Expr>
|
pub fn expr<I>() -> 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>
|
||||||
|
@ -132,6 +131,7 @@ parser! {
|
||||||
if_expr(min_indent),
|
if_expr(min_indent),
|
||||||
closure(min_indent),
|
closure(min_indent),
|
||||||
let_expr(min_indent),
|
let_expr(min_indent),
|
||||||
|
apply_variant(min_indent),
|
||||||
func_or_var(min_indent),
|
func_or_var(min_indent),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
@ -261,7 +261,7 @@ 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(
|
||||||
pattern().and(indentation())
|
pattern(min_indent).and(indentation())
|
||||||
.skip(whitespace())
|
.skip(whitespace())
|
||||||
.and(
|
.and(
|
||||||
char('=').with(indentation())
|
char('=').with(indentation())
|
||||||
|
@ -313,7 +313,7 @@ where I: Stream<Item = char, Position = IndentablePosition>,
|
||||||
// TODO patterns must be separated by commas!
|
// TODO patterns must be separated by commas!
|
||||||
between(char('|'), char('|'),
|
between(char('|'), char('|'),
|
||||||
sep_by1(
|
sep_by1(
|
||||||
pattern(),
|
pattern(min_indent),
|
||||||
char(',').skip(indented_whitespaces(min_indent))
|
char(',').skip(indented_whitespaces(min_indent))
|
||||||
))
|
))
|
||||||
.and(whitespace1().with(expr_body(min_indent)))
|
.and(whitespace1().with(expr_body(min_indent)))
|
||||||
|
@ -322,14 +322,69 @@ where I: Stream<Item = char, Position = IndentablePosition>,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pattern<I>() -> impl Parser<Input = I, Output = Pattern>
|
parser! {
|
||||||
|
#[inline(always)]
|
||||||
|
fn pattern[I](min_indent_ref: i32)(I) -> Pattern
|
||||||
|
where [ I: Stream<Item = char, Position = IndentablePosition> ]
|
||||||
|
{
|
||||||
|
let min_indent = *min_indent_ref;
|
||||||
|
|
||||||
|
choice((
|
||||||
|
char('_').map(|_| Pattern::Underscore),
|
||||||
|
ident().map(|name| Pattern::Identifier(name)),
|
||||||
|
match_variant(min_indent)
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn apply_variant<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>
|
||||||
{
|
{
|
||||||
choice((
|
variant_name()
|
||||||
char('_').map(|_| Pattern::Underscore),
|
.and(optional(attempt(function_application(min_indent))))
|
||||||
ident().map(|name| Pattern::Identifier(name))
|
.map(|(name, opt_args): (String, Option<Vec<Expr>>)|
|
||||||
))
|
// Use optional(sep_by1()) over sep_by() to avoid
|
||||||
|
// allocating a Vec in case the variant is empty
|
||||||
|
Expr::ApplyVariant(name, opt_args)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn match_variant<I>(min_indent: i32) -> impl Parser<Input = I, Output = Pattern>
|
||||||
|
where I: Stream<Item = char, Position = IndentablePosition>,
|
||||||
|
I::Error: ParseError<I::Item, I::Range, I::Position>
|
||||||
|
{
|
||||||
|
variant_name()
|
||||||
|
.and(optional(attempt(
|
||||||
|
sep_by1(
|
||||||
|
pattern(min_indent),
|
||||||
|
char(',').skip(indented_whitespaces(min_indent))
|
||||||
|
))))
|
||||||
|
.map(|(name, opt_args): (String, Option<Vec<Pattern>>)|
|
||||||
|
// Use optional(sep_by1()) over sep_by() to avoid
|
||||||
|
// allocating a Vec in case the variant is empty
|
||||||
|
Pattern::Variant(name, opt_args)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn variant_name<I>() -> impl Parser<Input = I, Output = String>
|
||||||
|
where I: Stream<Item = char, Position = IndentablePosition>,
|
||||||
|
I::Error: ParseError<I::Item, I::Range, I::Position>
|
||||||
|
{
|
||||||
|
// Variants must begin with an uppercase letter, but can have any
|
||||||
|
// combination of letters or numbers afterwards.
|
||||||
|
// No underscores, dashes, or apostrophes.
|
||||||
|
many1::<Vec<_>, _>(alpha_num())
|
||||||
|
.then(|chars: Vec<char>| {
|
||||||
|
let valid_start_char = chars[0].is_uppercase();
|
||||||
|
|
||||||
|
if valid_start_char {
|
||||||
|
value(chars.into_iter().collect()).right()
|
||||||
|
} else {
|
||||||
|
unexpected_any("First character in an identifier that was not a lowercase letter").left()
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ident<I>() -> impl Parser<Input = I, Output = String>
|
pub fn ident<I>() -> impl Parser<Input = I, Output = String>
|
||||||
|
@ -342,9 +397,10 @@ where I: Stream<Item = char, Position = IndentablePosition>,
|
||||||
many1::<Vec<_>, _>(alpha_num())
|
many1::<Vec<_>, _>(alpha_num())
|
||||||
.then(|chars: Vec<char>| {
|
.then(|chars: Vec<char>| {
|
||||||
let valid_start_char = chars[0].is_lowercase();
|
let valid_start_char = chars[0].is_lowercase();
|
||||||
let ident_str:String = chars.into_iter().collect();
|
|
||||||
|
|
||||||
if valid_start_char {
|
if valid_start_char {
|
||||||
|
let ident_str:String = chars.into_iter().collect();
|
||||||
|
|
||||||
match ident_str.as_str() {
|
match ident_str.as_str() {
|
||||||
"if" => unexpected_any("Reserved keyword `if`").left(),
|
"if" => unexpected_any("Reserved keyword `if`").left(),
|
||||||
"then" => unexpected_any("Reserved keyword `then`").left(),
|
"then" => unexpected_any("Reserved keyword `then`").left(),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue