Skip to content

Commit

Permalink
Let else
Browse files Browse the repository at this point in the history
  • Loading branch information
ecton committed Feb 13, 2024
1 parent 06b3a8b commit 9108f7a
Show file tree
Hide file tree
Showing 6 changed files with 89 additions and 9 deletions.
6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,6 @@ unsafe_code = "forbid"
name = "harness"
harness = false

[profile.release]
debug = true
lto = true
# [profile.release]
# debug = true
# lto = true
66 changes: 64 additions & 2 deletions src/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1081,9 +1081,19 @@ impl<'a> Scope<'a> {
self.compiler.code.label(pattern_mismatch);

if let Some(r#else) = &decl.r#else {
self.compile_expression(r#else, OpDestination::Void);
match refutable {
Refutability::Refutable => todo!("ensure all branches in else exit"),
Refutability::Irrefutable => {}
Refutability::Refutable => {
if let Err(range) = Self::check_all_branches_diverge(r#else) {
self.compiler
.errors
.push(Ranged::new(range, Error::LetElseMustDiverge));
}
}
Refutability::Irrefutable => self
.compiler
.errors
.push(Ranged::new(r#else.1, Error::ElseOnIrrefutablePattern)),
}
} else {
self.compiler.code.throw(FaultKind::PatternMismatch);
Expand All @@ -1093,6 +1103,56 @@ impl<'a> Scope<'a> {
self.compiler.code.copy(true, dest);
}

fn check_all_branches_diverge(expr: &Ranged<Expression>) -> Result<(), SourceRange> {
match &expr.0 {
Expression::Break(_) | Expression::Return(_) | Expression::Continue(_) => Ok(()),
Expression::If(if_expr) => {
let Some(when_false) = &if_expr.when_false else {
return Err(expr.1);
};
Self::check_all_branches_diverge(&if_expr.when_true)?;

Self::check_all_branches_diverge(when_false)
}
Expression::Block(block) => Self::check_all_branches_diverge(&block.body),
Expression::Binary(binary) if binary.kind == syntax::BinaryKind::Chain => {
Self::check_all_branches_diverge(&binary.left)
.or_else(|_| Self::check_all_branches_diverge(&binary.right))
}
Expression::Try(_) => {
todo!("try isn't implemented yet")
}
Expression::Loop(loop_expr) => {
let conditional = match &loop_expr.kind {
LoopKind::Infinite => false,
LoopKind::While(_) | LoopKind::TailWhile(_) | LoopKind::For { .. } => true,
};

if !conditional {
return Err(expr.1);
}

Self::check_all_branches_diverge(&loop_expr.block.body)
}

Expression::Literal(_)
| Expression::Lookup(_)
| Expression::Match(_)
| Expression::TryOrNil(_)
| Expression::Map(_)
| Expression::List(_)
| Expression::Tuple(_)
| Expression::Call(_)
| Expression::Index(_)
| Expression::Assign(_)
| Expression::Unary(_)
| Expression::Binary(_)
| Expression::Module(_)
| Expression::Function(_)
| Expression::Variable(_) => Err(expr.1),
}
}

fn declare_local(&mut self, name: Symbol, mutable: bool, value: Stack, dest: OpDestination) {
self.compiler.code.copy(value, dest);
let previous_declaration = self
Expand Down Expand Up @@ -1453,6 +1513,8 @@ pub enum Error {
ExpectedBlock,
NameAlreadyBound,
OrPatternBindingsMismatch,
ElseOnIrrefutablePattern,
LetElseMustDiverge,
Syntax(syntax::Error),
}

Expand Down
10 changes: 9 additions & 1 deletion src/syntax.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2071,14 +2071,22 @@ fn parse_variable(
Expression::Literal(Literal::Nil),
)
};

let r#else = if tokens.peek_token() == Some(Token::Identifier(Symbol::else_symbol().clone())) {
tokens.next()?;
Some(config.parse_expression(tokens)?)
} else {
None
};

Ok(tokens.ranged(
start..,
Expression::Variable(Box::new(Variable {
publish,
mutable,
pattern,
r#else: None,
value,
r#else,
})),
))
}
Expand Down
4 changes: 2 additions & 2 deletions src/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -619,7 +619,7 @@ impl Vm {
Ordering::Equal => return Ok(StepResult::Complete),
Ordering::Greater => return Err(Fault::InvalidInstructionAddress),
};
println!("Executing {instruction:?}");
// println!("Executing {instruction:?}");
let next_instruction = StepResult::from(address.checked_add(1));
let result = match instruction {
LoadedOp::Return => return Ok(StepResult::Complete),
Expand Down Expand Up @@ -754,7 +754,7 @@ impl Vm {
loaded.op1,
loaded.op2,
loaded.dest,
|vm, lhs, rhs| dbg!(lhs.matches(vm, &rhs).map(Value::Bool)),
|vm, lhs, rhs| lhs.matches(vm, &rhs).map(Value::Bool),
),
LoadedOp::BitwiseAnd(loaded) => self.op_binop(
code_index,
Expand Down
10 changes: 10 additions & 0 deletions tests/cases/match.rsn
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,14 @@ try_mismatch: {
false => 0,
}?"#,
output: Nil,
}

let_irrefutable: {
src: r#"let foo = 1 else { return }"#,
output: Error([Ranged(ElseOnIrrefutablePattern, SourceRange { start: 17, length: 10 })]),
}

let_else_doesnt_diverge: {
src: r#"let 1 = true else { true }"#,
output: Error([Ranged(LetElseMustDiverge, SourceRange { start: 20, length: 4 })]),
}
2 changes: 1 addition & 1 deletion tests/harness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use serde::Deserialize;

fn main() {
let filter = std::env::args().nth(1).unwrap_or_default();
// let filter = String::from("chain_range_short_circuit");
// let filter = String::from("let_irrefutable");
for entry in std::fs::read_dir("tests/cases").unwrap() {
let entry = entry.unwrap().path();
if entry.extension().map_or(false, |ext| ext == "rsn") {
Expand Down

0 comments on commit 9108f7a

Please sign in to comment.