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 Jan 6, 2024
1 parent cdc9d23 commit 8ddce7f
Show file tree
Hide file tree
Showing 19 changed files with 401 additions and 117 deletions.
1 change: 1 addition & 0 deletions compiler/formatter/src/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -744,6 +744,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
39 changes: 34 additions & 5 deletions compiler/frontend/src/ast_to_hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,11 @@ impl Context<'_> {
let cases = cases
.iter()
.map(|case| match &case.kind {
AstKind::MatchCase(MatchCase { box pattern, body }) => {
AstKind::MatchCase(MatchCase {
box pattern,
condition,
body,
}) => {
let (pattern, pattern_identifiers) = self.lower_pattern(pattern);

let reset_state = self.start_scope();
Expand All @@ -372,10 +376,25 @@ impl Context<'_> {
name.clone(),
);
}

let condition = condition.as_ref().map(|box condition| {
let condition_reset_state = self.start_scope();
self.compile_single(condition);
self.end_scope(condition_reset_state)
});

let body_reset_state = self.start_scope();
self.compile(body.as_ref());
let body = self.end_scope(reset_state);
let body = self.end_scope(body_reset_state);

(pattern, body)
let identifier_expressions = self.end_scope(reset_state);

hir::MatchCase {
pattern,
identifier_expressions,
condition,
body,
}
}
AstKind::Error { errors } => {
let pattern = Pattern::Error {
Expand All @@ -384,9 +403,19 @@ impl Context<'_> {

let reset_state = self.start_scope();
self.compile(&[]);
let body = self.end_scope(reset_state);

(pattern, body)
let body_reset_state = self.start_scope();
self.compile(&[]);
let body = self.end_scope(body_reset_state);

let identifier_expressions = self.end_scope(reset_state);

hir::MatchCase {
pattern,
identifier_expressions,
condition: None,
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
13 changes: 12 additions & 1 deletion compiler/frontend/src/cst/kind.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use super::{Cst, CstData, CstError};
use num_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 @@ -104,6 +104,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 @@ -128,6 +129,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 @@ -289,10 +291,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 @@ -505,11 +511,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
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 @@ -328,11 +334,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 8ddce7f

Please sign in to comment.