Add variants

This commit is contained in:
Richard Feldman 2019-05-27 22:00:30 -04:00
parent 1aaab2dc17
commit 7aab332650
3 changed files with 91 additions and 14 deletions

View file

@ -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));
} }
} }

View file

@ -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
} }

View file

@ -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(),