Skip to content

Commit

Permalink
Add optional condition to match cases
Browse files Browse the repository at this point in the history
  • Loading branch information
fpottbaecker committed Mar 28, 2024
1 parent 3b237b7 commit 8e38c46
Show file tree
Hide file tree
Showing 21 changed files with 421 additions and 97 deletions.
1 change: 1 addition & 0 deletions compiler/formatter/src/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -742,6 +742,7 @@ pub fn format_cst<'a>(
}
CstKind::MatchCase {
pattern,
condition: _, // TODO: format match case conditions
arrow,
body,
} => {
Expand Down
19 changes: 17 additions & 2 deletions compiler/frontend/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ pub struct Match {
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub struct MatchCase {
pub pattern: Box<Ast>,
pub condition: Option<Box<Ast>>,
pub body: Vec<Ast>,
}
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
Expand Down Expand Up @@ -248,7 +249,14 @@ impl FindAst for Match {
}
impl FindAst for MatchCase {
fn find(&self, id: &Id) -> Option<&Ast> {
self.pattern.find(id).or_else(|| self.body.find(id))
self.pattern
.find(id)
.or_else(|| {
self.condition
.as_ref()
.and_then(|box condition| condition.find(id))
})
.or_else(|| self.body.find(id))
}
}
impl FindAst for OrPattern {
Expand Down Expand Up @@ -352,8 +360,15 @@ impl CollectErrors for Ast {
expression.collect_errors(errors);
cases.collect_errors(errors);
}
AstKind::MatchCase(MatchCase { pattern, body }) => {
AstKind::MatchCase(MatchCase {
pattern,
condition,
body,
}) => {
pattern.collect_errors(errors);
if let Some(box condition) = condition {
condition.collect_errors(errors);
}
body.collect_errors(errors);
}
AstKind::OrPattern(OrPattern(patterns)) => {
Expand Down
67 changes: 49 additions & 18 deletions compiler/frontend/src/ast_to_hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -385,34 +385,65 @@ impl Context<'_> {
cases
.iter()
.map(|case| match &case.kind {
AstKind::MatchCase(MatchCase { box pattern, body }) => {
AstKind::MatchCase(MatchCase {
box pattern,
condition,
body,
}) => {
let (pattern, pattern_identifiers) = scope.lower_pattern(pattern);

let (body, ()) = scope.with_scope(None, |scope| {
for (name, (ast_id, identifier_id)) in
pattern_identifiers.clone()
{
scope.push(
ast_id,
Expression::PatternIdentifierReference(identifier_id),
name.clone(),
);
}
scope.compile(body.as_ref());
});

(pattern, body)
let (outer_body, (condition_body, body)) =
scope.with_scope(None, |scope| {
for (name, (ast_id, identifier_id)) in
pattern_identifiers.clone()
{
scope.push(
ast_id,
Expression::PatternIdentifierReference(
identifier_id,
),
name.clone(),
);
}

let condition = condition.as_ref().map(|box condition| {
scope
.with_scope(match_id.clone(), |condition_scope| {
condition_scope.compile_single(condition);
})
.0
});

let (body, ()) =
scope.with_scope(match_id.clone(), |scope| {
scope.compile(body);
});

(condition, body)
});

hir::MatchCase {
pattern,
identifier_expressions: outer_body,
condition: condition_body,
body,
}
}
AstKind::Error { errors } => {
let pattern = Pattern::Error {
errors: errors.clone(),
};

let (body, ()) = scope.with_scope(None, |scope| {
scope.compile(&[]);
let (body, inner_body) = scope.with_scope(None, |scope| {
scope.with_scope(match_id.clone(), |scope| {}).0
});

(pattern, body)
hir::MatchCase {
pattern,
identifier_expressions: body,
condition: None,
body: inner_body,
}
}
_ => unreachable!("Expected match case in match cases, got {case:?}."),
})
Expand Down
1 change: 1 addition & 0 deletions compiler/frontend/src/cst/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pub enum CstError {
ListNotClosed,
MatchCaseMissesArrow,
MatchCaseMissesBody,
MatchCaseMissesCondition,
MatchMissesCases,
OpeningParenthesisMissesExpression,
OrPatternMissesRight,
Expand Down
10 changes: 9 additions & 1 deletion compiler/frontend/src/cst/is_multiline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,17 @@ impl<D> IsMultiline for CstKind<D> {
} => expression.is_multiline() || percent.is_multiline() || cases.is_multiline(),
Self::MatchCase {
pattern,
condition,
arrow,
body,
} => pattern.is_multiline() || arrow.is_multiline() || body.is_multiline(),
} => {
pattern.is_multiline()
|| condition.as_deref().map_or(false, |(comma, condition)| {
comma.is_multiline() || condition.is_multiline()
})
|| arrow.is_multiline()
|| body.is_multiline()
}
Self::Function {
opening_curly_brace,
parameters_and_arrow,
Expand Down
14 changes: 13 additions & 1 deletion compiler/frontend/src/cst/kind.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use super::{Cst, CstData, CstError};
use crate::rich_ir::{RichIrBuilder, ToRichIr, TokenType};
use enumset::EnumSet;
use num_bigint::{BigInt, BigUint};
use std::fmt::{self, Display, Formatter};
use std::fmt::{self, Display, Formatter, Pointer};
use strum_macros::EnumIs;

#[derive(Clone, Debug, EnumIs, Eq, Hash, PartialEq)]
Expand Down Expand Up @@ -106,6 +106,7 @@ pub enum CstKind<D = CstData> {
},
MatchCase {
pattern: Box<Cst<D>>,
condition: Option<MatchCaseWithComma<D>>,
arrow: Box<Cst<D>>,
body: Vec<Cst<D>>,
},
Expand All @@ -130,6 +131,7 @@ pub enum IntRadix {
Binary,
Hexadecimal,
}
pub type MatchCaseWithComma<D> = Box<(Cst<D>, Cst<D>)>;
pub type FunctionParametersAndArrow<D> = (Vec<Cst<D>>, Box<Cst<D>>);

impl<D> CstKind<D> {
Expand Down Expand Up @@ -291,10 +293,14 @@ impl<D> CstKind<D> {
}
Self::MatchCase {
pattern,
condition,
arrow,
body,
} => {
let mut children = vec![pattern.as_ref(), arrow.as_ref()];
if let Some(box (comma, condition)) = condition {
children.extend([&comma, &condition]);
}
children.extend(body);
children
}
Expand Down Expand Up @@ -507,11 +513,16 @@ impl<D> Display for CstKind<D> {
}
Self::MatchCase {
pattern,
condition,
arrow,
body,
} => {
pattern.fmt(f)?;
arrow.fmt(f)?;
if let Some(box (comma, condition)) = condition {
comma.fmt(f)?;
condition.fmt(f)?;
}
for expression in body {
expression.fmt(f)?;
}
Expand Down Expand Up @@ -904,6 +915,7 @@ where
Self::MatchCase {
pattern,
arrow,
condition,
body,
} => {
builder.push_cst_kind("MatchCase", |builder| {
Expand Down
14 changes: 14 additions & 0 deletions compiler/frontend/src/cst/tree_with_ids.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,10 +138,16 @@ impl TreeWithIds for Cst {
.or_else(|| cases.find(id)),
CstKind::MatchCase {
pattern,
condition,
arrow,
body,
} => pattern
.find(id)
.or_else(|| {
condition.as_deref().and_then(|(comma, condition)| {
comma.find(id).or_else(|| condition.find(id))
})
})
.or_else(|| arrow.find(id))
.or_else(|| body.find(id)),
CstKind::Function {
Expand Down Expand Up @@ -329,11 +335,19 @@ impl TreeWithIds for Cst {
),
CstKind::MatchCase {
pattern,
condition,
arrow,
body,
} => (
pattern
.find_by_offset(offset)
.or_else(|| {
condition.as_deref().and_then(|(comma, condition)| {
comma
.find_by_offset(offset)
.or_else(|| condition.find_by_offset(offset))
})
})
.or_else(|| arrow.find_by_offset(offset))
.or_else(|| body.find_by_offset(offset)),
false,
Expand Down
7 changes: 7 additions & 0 deletions compiler/frontend/src/cst/unwrap_whitespace_and_comment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,10 +144,17 @@ impl<D: Clone> UnwrapWhitespaceAndComment for Cst<D> {
},
CstKind::MatchCase {
pattern,
condition,
arrow,
body,
} => CstKind::MatchCase {
pattern: pattern.unwrap_whitespace_and_comment(),
condition: condition.as_deref().map(|(comma, condition)| {
Box::new((
comma.unwrap_whitespace_and_comment(),
condition.unwrap_whitespace_and_comment(),
))
}),
arrow: arrow.unwrap_whitespace_and_comment(),
body: body.unwrap_whitespace_and_comment(),
},
Expand Down
23 changes: 18 additions & 5 deletions compiler/frontend/src/cst_to_ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -584,23 +584,36 @@ impl LoweringContext {
}
CstKind::MatchCase {
pattern,
arrow: _,
condition,
arrow,
body,
} => {
if lowering_type != LoweringType::Expression {
return self.create_ast_for_invalid_expression_in_pattern(cst);
};

let mut errors = vec![];
let pattern = self.lower_cst(pattern, LoweringType::Pattern);

// TODO: handle error in arrow
let condition = condition
.as_ref()
.map(|box (_, condition)| self.lower_cst(condition, LoweringType::Expression));

if let CstKind::Error {
unparsable_input: _,
error,
} = arrow.kind
{
errors.push(self.create_error(arrow, error));
}

let body = self.lower_csts(body);

self.create_ast(
cst.data.id,
self.create_errors_or_ast(
cst,
errors,
MatchCase {
pattern: Box::new(pattern),
condition: condition.map(Box::new),
body,
},
)
Expand Down
1 change: 1 addition & 0 deletions compiler/frontend/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ impl Display for CompilerErrorPayload {
CstError::MatchMissesCases => "This match misses cases to match against.",
CstError::MatchCaseMissesArrow => "This match case misses an arrow.",
CstError::MatchCaseMissesBody => "This match case misses a body to run.",
CstError::MatchCaseMissesCondition => "This match case condition is empty.",
CstError::OpeningParenthesisMissesExpression => {
"Here's an opening parenthesis without an expression after it."
}
Expand Down
Loading

0 comments on commit 8e38c46

Please sign in to comment.