Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add match expressions #22

Merged
merged 7 commits into from
Apr 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions crates/ditto-ast/src/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,25 @@ pub enum Expression {
/// The expression to evaluate otherwise.
false_clause: Box<Self>,
},
/// A pattern match.
///
/// ```ditto
/// match some_expr with
/// | Pattern -> another_expr
/// ```
Match {
/// The source span for this expression.
span: Span,

/// The type of the expressions in the `arms`.
match_type: Type,

/// Expression to be matched.
expression: Box<Self>,

/// Patterns to be matched against and their corresponding expressions.
arms: NonEmpty<(Pattern, Self)>,
},
/// A value constructor local to the current module, e.g. `Just` and `Ok`.
LocalConstructor {
/// The source span for this expression.
Expand Down Expand Up @@ -193,6 +212,7 @@ impl Expression {
}
}
Self::If { output_type, .. } => output_type.clone(),
Self::Match { match_type, .. } => match_type.clone(),
Self::LocalConstructor {
constructor_type, ..
} => constructor_type.clone(),
Expand Down Expand Up @@ -220,6 +240,7 @@ impl Expression {
Self::Function { span, .. } => *span,
Self::Call { span, .. } => *span,
Self::If { span, .. } => *span,
Self::Match { span, .. } => *span,
Self::LocalConstructor { span, .. } => *span,
Self::ImportedConstructor { span, .. } => *span,
Self::LocalVariable { span, .. } => *span,
Expand Down Expand Up @@ -296,3 +317,33 @@ impl FunctionBinder {
}
}
}

/// A pattern to be matched.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Pattern {
/// A local constructor pattern.
LocalConstructor {
/// The source span for this pattern.
span: Span,
/// `Just`
constructor: ProperName,
/// Pattern arguments to the constructor.
arguments: Vec<Self>,
},
/// An importedf constructor pattern.
ImportedConstructor {
/// The source span for this pattern.
span: Span,
/// `Maybe.Just`
constructor: FullyQualifiedProperName,
/// Pattern arguments to the constructor.
arguments: Vec<Self>,
},
/// A variable binding pattern.
Variable {
/// The source span for this pattern.
span: Span,
/// Name to bind.
name: Name,
},
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module Test exports (..);

type Foo = Foo;
type Bar = Bar;

nah =
match Foo with
| Bar -> unit;
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@

× types don't unify
╭─[golden:5:1]
5 │
6 │ nah =
7 │ match Foo with
8 │ | Bar -> unit;
· ─┬─
· ╰── here
╰────
help: expected Foo
got Bar
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module Test exports (..);

type Maybe(a) = Just(a) | Nothing;
type Foo = Foo;
type Bar = Bar;

nah = (maybe_foo : Maybe(Foo)) ->
match maybe_foo with
| Just(Bar) -> unit
| Nothing -> unit;
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@

× types don't unify
╭─[golden:6:1]
6 │
7 │ nah = (maybe_foo : Maybe(Foo)) ->
8 │ match maybe_foo with
9 │ | Just(Bar) -> unit
· ─┬─
· ╰── here
10 │ | Nothing -> unit;
╰────
help: expected Foo
got Bar
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module Test exports (..);

type Maybe(a) = Just(a) | Nothing;

nah = (maybe_foo : Maybe(Int)) ->
match maybe_foo with
| Just(a, b) -> unit
| Nothing -> unit;
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@

× wrong number of arguments
╭─[golden:4:1]
4 │
5 │ nah = (maybe_foo : Maybe(Int)) ->
6 │ match maybe_foo with
7 │ | Just(a, b) -> unit
· ─────┬────
· ╰── this expects 1 argument
8 │ | Nothing -> unit;
╰────
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module Test exports (..);

type Maybe(a) = Just(a) | Nothing;

nah = (maybe_foo : Maybe(Int)) ->
match maybe_foo with
| Just -> unit
| Nothing -> unit;
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@

× wrong number of arguments
╭─[golden:4:1]
4 │
5 │ nah = (maybe_foo : Maybe(Int)) ->
6 │ match maybe_foo with
7 │ | Just -> unit
· ──┬─
· ╰── this expects 1 argument
8 │ | Nothing -> unit;
╰────
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module Test exports (..);

type Maybe(a) = Just(a) | Nothing;

nah = (maybe_foo : Maybe(Int)) ->
match maybe_foo with
| Just(n) -> unit
| Nothing(a) -> unit;
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@

× wrong number of arguments
╭─[golden:5:1]
5 │ nah = (maybe_foo : Maybe(Int)) ->
6 │ match maybe_foo with
7 │ | Just(n) -> unit
8 │ | Nothing(a) -> unit;
· ─────┬────
· ╰── this expects no arguments
╰────
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module Test exports (..);

type Maybe(a) = Just(a) | Nothing;

nah = (maybe_foo : Maybe(Int)): Int ->
match maybe_foo with
| Just(n) -> unit
| Nothing -> unit;
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@

× types don't unify
╭─[golden:4:1]
4 │
5 │ nah = (maybe_foo : Maybe(Int)): Int ->
6 │ match maybe_foo with
7 │ | Just(n) -> unit
· ──┬─
· ╰── here
8 │ | Nothing -> unit;
╰────
help: expected Int
got Unit
12 changes: 12 additions & 0 deletions crates/ditto-checker/src/module/value_declarations/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,18 @@ fn toposort_value_declarations(
get_connected_nodes_rec(true_clause, nodes, accum);
get_connected_nodes_rec(false_clause, nodes, accum);
}
Expression::Match {
expression,
head_arm,
tail_arms,
..
} => {
get_connected_nodes_rec(expression, nodes, accum);
get_connected_nodes_rec(&head_arm.expression, nodes, accum);
for tail_arm in tail_arms.iter() {
get_connected_nodes_rec(&tail_arm.expression, nodes, accum);
}
}
Expression::Array(elements) => {
if let Some(ref elements) = elements.value {
elements.iter().for_each(|element| {
Expand Down
26 changes: 23 additions & 3 deletions crates/ditto-checker/src/typechecker/env.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
use super::{common::type_variables, Scheme};
use crate::supply::Supply;
use ditto_ast::{
Expression, FullyQualifiedName, FullyQualifiedProperName, Name, ProperName, QualifiedName,
QualifiedProperName, Span, Type,
Expression, FullyQualifiedName, FullyQualifiedProperName, Name, Pattern, ProperName,
QualifiedName, QualifiedProperName, Span, Type,
};
use std::{
collections::{HashMap, HashSet},
default::Default,
};

#[derive(Default)]
#[derive(Default, Clone)]
pub struct Env {
pub constructors: EnvConstructors,
pub values: EnvValues,
Expand Down Expand Up @@ -157,6 +157,26 @@ impl EnvConstructor {
}
}

// REVIEW should this be `into_pattern`?
pub fn to_pattern(&self, span: Span, arguments: Vec<Pattern>) -> Pattern {
match self {
Self::ModuleConstructor { constructor, .. } => Pattern::LocalConstructor {
span,
constructor: constructor.clone(),
arguments,
},
Self::ImportedConstructor { constructor, .. } => Pattern::ImportedConstructor {
span,
constructor: constructor.clone(),
arguments,
},
}
}

pub fn get_type(&self, supply: &mut Supply) -> Type {
self.get_scheme().instantiate(supply)
}

fn get_scheme(&self) -> Scheme {
match self {
Self::ModuleConstructor {
Expand Down
Loading