diff --git a/src/eval.rs b/src/eval.rs index 010c48ecf9..9da8c19253 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -27,11 +27,6 @@ pub fn scoped_eval(expr: Expr, vars: &HashMap>) -> Expr { } else { // Create a new scope containing the new declaration. 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))); // Evaluate in_expr with that new scope's variables. @@ -39,6 +34,10 @@ pub fn scoped_eval(expr: Expr, vars: &HashMap>) -> Expr { } }, + Let(Variant(name, patterns), definition, in_expr) => { + panic!("Pattern matching on variants is not yet supported!"); + }, + Let(Underscore, definition, in_expr) => { // Faithfully eval this, but discard its result. scoped_eval(*definition, vars); @@ -56,6 +55,18 @@ pub fn scoped_eval(expr: Expr, vars: &HashMap>) -> Expr { 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) => { match *func_expr.clone() { Closure(arg_patterns, body) => { @@ -69,6 +80,11 @@ pub fn scoped_eval(expr: Expr, vars: &HashMap>) -> Expr { Identifier(name) => { 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)); } } diff --git a/src/expr.rs b/src/expr.rs index da2bb12a58..a089a157f6 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -19,6 +19,10 @@ pub enum Expr { Operator(Box, Operator, Box), Closure(Vec, Box), + // Sum Types + ApplyVariant(String, Option>), + + If(Box, Box, Box), Error(Problem), } @@ -80,6 +84,7 @@ pub enum Problem { #[derive(Clone, Debug, PartialEq)] pub enum Pattern { Identifier(String), + Variant(String, Option>), Underscore } diff --git a/src/parse.rs b/src/parse.rs index 31a110ebe7..63f2801844 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -21,7 +21,6 @@ pub fn parse_string(string: &str) -> Result() -> impl Parser where I: Stream, I::Error: ParseError @@ -132,6 +131,7 @@ parser! { if_expr(min_indent), closure(min_indent), let_expr(min_indent), + apply_variant(min_indent), func_or_var(min_indent), )) } @@ -261,7 +261,7 @@ where I: Stream, I::Error: ParseError { attempt( - pattern().and(indentation()) + pattern(min_indent).and(indentation()) .skip(whitespace()) .and( char('=').with(indentation()) @@ -313,7 +313,7 @@ where I: Stream, // TODO patterns must be separated by commas! between(char('|'), char('|'), sep_by1( - pattern(), + pattern(min_indent), char(',').skip(indented_whitespaces(min_indent)) )) .and(whitespace1().with(expr_body(min_indent))) @@ -322,14 +322,69 @@ where I: Stream, }) } -pub fn pattern() -> impl Parser +parser! { + #[inline(always)] + fn pattern[I](min_indent_ref: i32)(I) -> Pattern + where [ I: Stream ] + { + 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(min_indent: i32) -> impl Parser where I: Stream, I::Error: ParseError { - choice(( - char('_').map(|_| Pattern::Underscore), - ident().map(|name| Pattern::Identifier(name)) - )) + variant_name() + .and(optional(attempt(function_application(min_indent)))) + .map(|(name, opt_args): (String, Option>)| + // 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(min_indent: i32) -> impl Parser +where I: Stream, + I::Error: ParseError +{ + variant_name() + .and(optional(attempt( + sep_by1( + pattern(min_indent), + char(',').skip(indented_whitespaces(min_indent)) + )))) + .map(|(name, opt_args): (String, Option>)| + // 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() -> impl Parser +where I: Stream, + I::Error: ParseError +{ + // Variants must begin with an uppercase letter, but can have any + // combination of letters or numbers afterwards. + // No underscores, dashes, or apostrophes. + many1::, _>(alpha_num()) + .then(|chars: Vec| { + 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() -> impl Parser @@ -342,9 +397,10 @@ where I: Stream, many1::, _>(alpha_num()) .then(|chars: Vec| { let valid_start_char = chars[0].is_lowercase(); - let ident_str:String = chars.into_iter().collect(); if valid_start_char { + let ident_str:String = chars.into_iter().collect(); + match ident_str.as_str() { "if" => unexpected_any("Reserved keyword `if`").left(), "then" => unexpected_any("Reserved keyword `then`").left(),