Skip to content

Commit

Permalink
feat: re-add switch-case
Browse files Browse the repository at this point in the history
  • Loading branch information
jac3km4 committed Nov 28, 2022
1 parent 7cb3ec7 commit b8ea485
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 15 deletions.
36 changes: 35 additions & 1 deletion compiler/src/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,41 @@ impl<'ctx, 'id> CodeGen<'ctx, 'id> {
Expr::Seq(seq) => {
self.assemble_seq(seq, pool, cache, exit);
}
Expr::Switch(_, _, _, _) => todo!(),
Expr::Switch(scrutinee, cases, default, scrutinee_type, _) => {
let first_case_label = self.new_label();
let mut next_case_label = self.new_label();
let exit_label = self.new_label();

let scrutinee_type = cache.alloc_type(&scrutinee_type.simplify(self.repo), self.repo, pool);
self.emit(Instr::Switch(scrutinee_type, first_case_label));
self.assemble(*scrutinee, pool, cache);
self.emit_label(first_case_label);

let mut case_iter = cases.into_iter().peekable();
while case_iter.peek().is_some() {
let body_label = self.new_label();

for case in &mut case_iter {
self.emit_label(next_case_label);
next_case_label = self.new_label();
self.emit(Instr::SwitchLabel(next_case_label, body_label));
self.assemble(case.matcher, pool, cache);

if !case.body.exprs.iter().all(Expr::is_empty) {
self.emit_label(body_label);
self.assemble_seq(case.body, pool, cache, Some(exit_label));
break;
}
}
}
self.emit_label(next_case_label);

if let Some(body) = default {
self.emit(Instr::SwitchDefault);
self.assemble_seq(body, pool, cache, Some(exit_label));
}
self.emit_label(exit_label);
}
Expr::If(condition, if_, else_, _) => {
let else_label = self.new_label();
self.emit(Instr::JumpIfFalse(else_label));
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -423,7 +423,7 @@ peg::parser! {

rule switch() -> Expr<SourceAst>
= pos:pos() keyword("switch") _ matcher:expr() _ "{" _ cases:(case() ** _) _ default:default()? _ "}" _ ";"? end:pos()
{ Expr::Switch(Box::new(matcher), cases, default, Span::new(pos, end)) }
{ Expr::Switch(Box::new(matcher), cases, default, (), Span::new(pos, end)) }

rule case() -> SwitchCase<SourceAst>
= keyword("case") _ matcher:expr() _ ":" _ body:seq()
Expand Down
36 changes: 26 additions & 10 deletions compiler/src/typer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use enum_as_inner::EnumAsInner;
use hashbrown::{hash_map, HashMap, HashSet};
use itertools::{izip, Itertools};
use redscript::ast::{
Constant, Expr, ExprKind, Ident, Literal, Param, Seq, SourceAst, Span, TypeName, TypeParam, Variance
Constant, Expr, ExprKind, Ident, Literal, Param, Seq, SourceAst, Span, SwitchCase, TypeName, TypeParam, Variance
};
use redscript::bytecode::Intrinsic;
use redscript::Str;
Expand Down Expand Up @@ -303,7 +303,25 @@ impl<'ctx, 'id> Typer<'ctx, 'id> {
Ok((Expr::Return(expr, *span), InferType::UNIT))
}
Expr::Seq(seq) => Ok((Expr::Seq(self.typeck_seq(seq, locals)), InferType::UNIT)),
Expr::Switch(_, _, _, _) => todo!(),
Expr::Switch(scrutinee, cases, default, _, span) => {
let (scrutinee, scrutinee_type) = self.typeck(scrutinee, locals)?;
let cases = cases
.iter()
.map(|case| {
let (matcher, typ) = self.typeck(&case.matcher, locals)?;
typ.constrain(&scrutinee_type, self.repo).with_span(*span)?;
let body = self.typeck_seq(&case.body, &mut locals.introduce_scope());
Ok(SwitchCase { matcher, body })
})
.try_collect()?;
let default = default
.as_ref()
.map(|body| self.typeck_seq(&body, &mut locals.introduce_scope()));
Ok((
Expr::Switch(scrutinee.into(), cases, default, scrutinee_type, *span),
InferType::UNIT,
))
}
Expr::If(cond, if_, else_, span) => {
let (cond, cond_type) = self.typeck(cond, locals)?;
cond_type
Expand Down Expand Up @@ -1083,14 +1101,12 @@ impl<'id> Mono<'id> {
(Self::Bottom, _) | (_, Type::Top | Type::Var(_)) => true,
(Self::Prim(lhs), Type::Prim(rhs)) => lhs == rhs,
(Self::Data(lhs), Type::Data(rhs)) if lhs.id == rhs.id => true,
(lhs, Type::Data(rhs)) => {
match (lhs.ref_type(), rhs.id.ref_type(), &rhs.args[..]) {
(Some((_, lhs)), Some(_), [rhs]) => lhs.is_same_shape(rhs),
(Some((_, lhs)), None, _) => lhs.is_same_shape(typ),
(None, Some(_), [rhs]) => self.is_same_shape(rhs),
_ => false,
}
}
(lhs, Type::Data(rhs)) => match (lhs.ref_type(), rhs.id.ref_type(), &rhs.args[..]) {
(Some((_, lhs)), Some(_), [rhs]) => lhs.is_same_shape(rhs),
(Some((_, lhs)), None, _) => lhs.is_same_shape(typ),
(None, Some(_), [rhs]) => self.is_same_shape(rhs),
_ => false,
},
_ => false,
}
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ macro_rules! visit_expr {
Expr::Seq(seq) => {
(&$($mut)? seq.exprs).into_iter().for_each(|e| $self.$fun(e));
}
Expr::Switch(matched, cases, default, _) => {
Expr::Switch(matched, cases, default, _, _) => {
$self.$fun(matched);
for case in &$($mut)? cases[..] {
$self.$fun(&$($mut)? case.matcher);
Expand Down
4 changes: 2 additions & 2 deletions core/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ where
New(N::Class, Box<[Self]>, Span),
Return(Option<Box<Self>>, Span),
Seq(Seq<N>),
Switch(Box<Self>, Vec<SwitchCase<N>>, Option<Seq<N>>, Span),
Switch(Box<Self>, Vec<SwitchCase<N>>, Option<Seq<N>>, N::Inferred, Span),
Goto(Target, Span),
If(Box<Self>, Seq<N>, Option<Seq<N>>, Span),
Conditional(Box<Self>, Box<Self>, Box<Self>, Span),
Expand Down Expand Up @@ -72,7 +72,7 @@ where
| Expr::ArrayElem(_, _, _, span)
| Expr::New(_, _, span)
| Expr::Return(_, span)
| Expr::Switch(_, _, _, span)
| Expr::Switch(_, _, _, _, span)
| Expr::Goto(_, span)
| Expr::If(_, _, _, span)
| Expr::Conditional(_, _, _, span)
Expand Down

0 comments on commit b8ea485

Please sign in to comment.