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

View file

@ -31,7 +31,7 @@ fn problem(prob: Problem) -> Evaluated {
pub fn scoped_eval(expr: Expr, vars: &Scope) -> Evaluated {
match expr {
// 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
Var(name) => match vars.get(&name) {
@ -72,6 +72,14 @@ pub fn scoped_eval(expr: Expr, vars: &Scope) -> Evaluated {
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) => {
let func_expr = match vars.get(&name) {
Some(resolved) => {
@ -93,8 +101,8 @@ pub fn scoped_eval(expr: Expr, vars: &Scope) -> Evaluated {
eval_apply(scoped_eval(*func_expr, vars), args, vars)
},
Match(condition, branches) => {
eval_match(scoped_eval(*condition, vars), branches, vars)
Case(condition, branches) => {
eval_case(scoped_eval(*condition, vars), branches, vars)
},
Operator(left_arg, op, right_arg) => {
@ -225,7 +233,7 @@ fn eval_operator(Evaluated(left_expr): &Evaluated, op: Operator, Evaluated(right
}
#[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;
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.
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) => {
match evaluated {
Evaluated(ApplyVariant(applied_variant_name, opt_applied_contents)) => {

View file

@ -23,9 +23,12 @@ pub enum Expr {
// Sum Types
ApplyVariant(String, Option<Vec<Expr>>),
// Product Types
EmptyRecord,
// Conditionals
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(Problem),
@ -104,6 +107,7 @@ pub enum Problem {
pub enum Pattern {
Identifier(String),
Variant(String, Option<Vec<Pattern>>),
EmptyRecord,
Underscore
}

View file

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