More syntax changes

This commit is contained in:
Richard Feldman 2019-05-30 10:11:30 -04:00
parent 9895e0c0ae
commit 352ab5ed34
4 changed files with 81 additions and 49 deletions

View file

@ -1,64 +1,69 @@
succeed = (val) -> Success val succeed = (val) ->
Success val
fail = (val) -> Failure val fail = (val) ->
Failure val
echo = (str) -> Echo str, succeed, fail echo = (str) ->
Echo str, succeed, fail
read = Read succeed, fail readInput =
Read succeed, fail
map = (convert, task) -> map = (convert, task) ->
await task, (output) -> after task, (output) ->
succeed (convert output) succeed (convert output)
mapErr = (convert, task) -> mapErr = (convert, task) ->
fallback task, (err) -> fallback task, (err) ->
fail (convert err) fail (convert err)
await = (task, cont) -> after = (task, cont) ->
match task case task
when Success val then cont val when Success val then cont val
when Failure val then Failure val when Failure val then Failure val
when Echo str, prevCont, onFailure then when Echo str, prevCont, onFailure then
Echo str, Echo str,
(({}) -> await (prevCont {}), cont) ({} -> after (prevCont {}), cont),
((ioErr) -> await (onFailure ioErr), cont) (ioErr -> after (onFailure ioErr), cont)
when Read prevCont, onFailure then
Read
((str) -> await (prevCont str), cont)
((ioErr) -> await (onFailure ioErr), cont)
when Read prevCont, onFailure then
Read
(str -> after (prevCont str), cont),
(ioErr -> after (onFailure ioErr), cont)
fallback = (task, onFailure) -> fallback = (task, onFailure) ->
match task case task
when Success val then Success val when Success val then Success val
when Failure val then onFailure val when Failure val then onFailure val
when Echo str, cont, prevOnFailure then when Echo str, cont, prevOnFailure then
Echo str Echo str
(({}) -> fallback (cont {}), onFailure) ({} -> fallback (cont {}), onFailure),
((ioErr) -> fallback (prevOnFailure ioErr), onFailure) (ioErr -> fallback (prevOnFailure ioErr), onFailure)
when Read cont, prevOnFailure then when Read cont, prevOnFailure then
Read Read
((str) -> fallback (cont str), onFailure) (str -> fallback (cont str), onFailure),
((ioErr) -> fallback (prevOnFailure ioErr), onFailure) (ioErr -> fallback (prevOnFailure ioErr), onFailure)
demo = demo =
await (echo "Enter first name"), ({}) -> after (echo "Enter first name"), ({}) ->
await read, (firstName) -> after readInput, (firstName) ->
await (echo "Enter last name"), ({}) -> after (echo "Enter last name"), ({}) ->
await read, (lastName) -> after readInput, (lastName) ->
echo "Your name is: \(firstName) \(lastName)" fullName = "\(firstName) \(lastName)"
echo "Your name is: \(fullName)"
demo demo

View file

@ -31,7 +31,7 @@ fn problem(prob: Problem) -> Evaluated {
pub fn scoped_eval(expr: Expr, vars: &Scope) -> Evaluated { pub fn scoped_eval(expr: Expr, vars: &Scope) -> Evaluated {
match expr { match expr {
// Primitives need no further evaluation // Primitives need no further evaluation
Error(_) | Int(_) | Str(_) | Frac(_, _) | Char(_) | Bool(_) | Closure(_, _) => Evaluated(expr), Error(_) | Int(_) | Str(_) | Frac(_, _) | Char(_) | Bool(_) | Closure(_, _) | Expr::EmptyRecord => Evaluated(expr),
// Resolve variable names // Resolve variable names
Var(name) => match vars.get(&name) { Var(name) => match vars.get(&name) {
@ -72,6 +72,14 @@ pub fn scoped_eval(expr: Expr, vars: &Scope) -> Evaluated {
scoped_eval(*in_expr, vars) scoped_eval(*in_expr, vars)
}, },
Let(Pattern::EmptyRecord, definition, in_expr) => {
// Faithfully eval this, but discard its result.
scoped_eval(*definition, &vars);
// Actually use this part.
scoped_eval(*in_expr, vars)
},
Func(name, args) => { Func(name, args) => {
let func_expr = match vars.get(&name) { let func_expr = match vars.get(&name) {
Some(resolved) => { Some(resolved) => {
@ -93,8 +101,8 @@ pub fn scoped_eval(expr: Expr, vars: &Scope) -> Evaluated {
eval_apply(scoped_eval(*func_expr, vars), args, vars) eval_apply(scoped_eval(*func_expr, vars), args, vars)
}, },
Match(condition, branches) => { Case(condition, branches) => {
eval_match(scoped_eval(*condition, vars), branches, vars) eval_case(scoped_eval(*condition, vars), branches, vars)
}, },
Operator(left_arg, op, right_arg) => { Operator(left_arg, op, right_arg) => {
@ -225,7 +233,7 @@ fn eval_operator(Evaluated(left_expr): &Evaluated, op: Operator, Evaluated(right
} }
#[inline(always)] #[inline(always)]
fn eval_match (condition: Evaluated, branches: SmallVec<[(Pattern, Box<Expr>); 4]>, vars: &Scope) -> Evaluated { fn eval_case (condition: Evaluated, branches: SmallVec<[(Pattern, Box<Expr>); 4]>, vars: &Scope) -> Evaluated {
let Evaluated(ref evaluated_expr) = condition; let Evaluated(ref evaluated_expr) = condition;
for (pattern, definition) in branches { for (pattern, definition) in branches {
@ -254,6 +262,14 @@ fn pattern_match(evaluated: Evaluated, pattern: Pattern, vars: &mut Scope) -> Re
// Underscore matches anything, and records no new vars. // Underscore matches anything, and records no new vars.
Ok(()) Ok(())
}, },
Pattern::EmptyRecord => {
match evaluated {
Evaluated(Expr::EmptyRecord) => Ok(()),
Evaluated(expr) => Err(TypeMismatch(
format!("Wanted a `{}`, but was given `{}`.", "{}", expr)
))
}
},
Variant(pattern_variant_name, opt_pattern_contents) => { Variant(pattern_variant_name, opt_pattern_contents) => {
match evaluated { match evaluated {
Evaluated(ApplyVariant(applied_variant_name, opt_applied_contents)) => { Evaluated(ApplyVariant(applied_variant_name, opt_applied_contents)) => {

View file

@ -23,9 +23,12 @@ pub enum Expr {
// Sum Types // Sum Types
ApplyVariant(String, Option<Vec<Expr>>), ApplyVariant(String, Option<Vec<Expr>>),
// Product Types
EmptyRecord,
// Conditionals // Conditionals
If(Box<Expr>, Box<Expr>, Box<Expr>), If(Box<Expr>, Box<Expr>, Box<Expr>),
Match(Box<Expr>, SmallVec<[(Pattern, Box<Expr>); 4]>), Case(Box<Expr>, SmallVec<[(Pattern, Box<Expr>); 4]>),
// Error // Error
Error(Problem), Error(Problem),
@ -104,6 +107,7 @@ pub enum Problem {
pub enum Pattern { pub enum Pattern {
Identifier(String), Identifier(String),
Variant(String, Option<Vec<Pattern>>), Variant(String, Option<Vec<Pattern>>),
EmptyRecord,
Underscore Underscore
} }

View file

@ -123,7 +123,9 @@ parser! {
let min_indent = *min_indent_ref; let min_indent = *min_indent_ref;
choice(( choice((
closure(min_indent),
parenthetical_expr(min_indent), parenthetical_expr(min_indent),
string("{}").with(value(Expr::EmptyRecord)),
string("True").with(value(Expr::Bool(true))), string("True").with(value(Expr::Bool(true))),
string("False").with(value(Expr::Bool(false))), string("False").with(value(Expr::Bool(false))),
string_literal(), string_literal(),
@ -131,7 +133,6 @@ parser! {
char_literal(), char_literal(),
if_expr(min_indent), if_expr(min_indent),
match_expr(min_indent), match_expr(min_indent),
closure(min_indent),
let_expr(min_indent), let_expr(min_indent),
apply_variant(min_indent), apply_variant(min_indent),
func_or_var(min_indent), func_or_var(min_indent),
@ -204,7 +205,7 @@ pub fn match_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("match").skip(indented_whitespaces1(min_indent)) string("case").skip(indented_whitespaces1(min_indent))
.with(expr_body(min_indent)).skip(indented_whitespaces1(min_indent)) .with(expr_body(min_indent)).skip(indented_whitespaces1(min_indent))
.and( .and(
many::<SmallVec<_>, _>( many::<SmallVec<_>, _>(
@ -219,7 +220,7 @@ where I: Stream<Item = char, Position = IndentablePosition>,
// TODO handle this more gracefully // TODO handle this more gracefully
panic!("encountered match-expression with no branches!") panic!("encountered match-expression with no branches!")
} else { } else {
Expr::Match(Box::new(conditional), branches) Expr::Case(Box::new(conditional), branches)
} }
) )
} }
@ -336,15 +337,20 @@ 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 patterns must be separated by commas! // TODO patterns must be separated by commas!
between(char('|'), char('|'), attempt(
between(char('('), char(')'),
sep_by1( sep_by1(
pattern(min_indent), pattern(min_indent),
char(',').skip(indented_whitespaces(min_indent)) char(',').skip(indented_whitespaces(min_indent))
)) ))
.and(whitespace1().with(expr_body(min_indent))) .skip(indented_whitespaces1(min_indent))
.map(|(patterns, closure_body)| { .skip(string("->"))
Expr::Closure(patterns, Box::new(closure_body)) .skip(indented_whitespaces1(min_indent))
}) )
.and(expr_body(min_indent))
.map(|(patterns, closure_body)| {
Expr::Closure(patterns, Box::new(closure_body))
})
} }
parser! { parser! {
@ -356,6 +362,7 @@ parser! {
choice(( choice((
char('_').map(|_| Pattern::Underscore), char('_').map(|_| Pattern::Underscore),
string("{}").map(|_| Pattern::EmptyRecord),
ident().map(|name| Pattern::Identifier(name)), ident().map(|name| Pattern::Identifier(name)),
match_variant(min_indent) match_variant(min_indent)
)) ))
@ -420,7 +427,7 @@ where I: Stream<Item = char, Position = IndentablePosition>,
"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(),
"else" => unexpected_any("Reserved keyword `else`").left(), "else" => unexpected_any("Reserved keyword `else`").left(),
"match" => unexpected_any("Reserved keyword `match`").left(), "case" => unexpected_any("Reserved keyword `case`").left(),
"when" => unexpected_any("Reserved keyword `when`").left(), "when" => unexpected_any("Reserved keyword `when`").left(),
_ => value(ident_str).right() _ => value(ident_str).right()
} }