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,17 +1,21 @@
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)
@ -20,45 +24,46 @@ mapErr = (convert, task) ->
fail (convert err)
await = (task, cont) ->
match task
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)
({} -> after (prevCont {}), cont),
(ioErr -> after (onFailure ioErr), cont)
when Read prevCont, onFailure then
Read
((str) -> await (prevCont str), cont)
((ioErr) -> await (onFailure ioErr), cont)
(str -> after (prevCont str), cont),
(ioErr -> after (onFailure ioErr), cont)
fallback = (task, onFailure) ->
match task
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)
({} -> fallback (cont {}), onFailure),
(ioErr -> fallback (prevOnFailure ioErr), onFailure)
when Read cont, prevOnFailure then
Read
((str) -> fallback (cont str), onFailure)
((ioErr) -> fallback (prevOnFailure ioErr), onFailure)
(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,12 +337,17 @@ 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)))
.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))
})
@ -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()
}