Skip to content

Commit 69ca370

Browse files
committed
Support eval-up-to on tuple destructure variables
1 parent 7d3e19e commit 69ca370

File tree

2 files changed

+84
-1
lines changed

2 files changed

+84
-1
lines changed

src/eval.rs

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -498,7 +498,7 @@ pub(crate) fn eval_up_to(
498498
// destructuring tuple, we want to return the tuple element and
499499
// position.
500500

501-
match item {
501+
let mut res = match item {
502502
ToplevelItem::Def(def) => match &def.2 {
503503
Definition_::Fun(name_sym, fun_info) => {
504504
eval_toplevel_defs(&[item.clone()], env);
@@ -564,7 +564,79 @@ pub(crate) fn eval_up_to(
564564
Err(e) => Some(Err(e)),
565565
}
566566
}
567+
};
568+
569+
// If the user asked for a single position in a tuple
570+
// destructuring, handle it here.
571+
if let Some(Ok((value, pos))) = &mut res {
572+
if let Some((var_value, var_pos)) = let_var_pos(value, items, &syn_ids) {
573+
*value = var_value;
574+
*pos = var_pos;
575+
}
576+
}
577+
578+
res
579+
}
580+
581+
/// If the AstId corresponded to a let variable or a destructuring let
582+
/// variable, return the inner value and position.
583+
///
584+
/// ```garden
585+
/// let (x, y) = (1, 2)
586+
/// // ^
587+
/// ```
588+
///
589+
/// In this example, we want the position to be the position of `y`,
590+
/// and the value to be `2`, not `(1, 2)`.
591+
fn let_var_pos(
592+
value: &Value,
593+
items: &[ToplevelItem],
594+
ast_ids: &[AstId],
595+
) -> Option<(Value, Position)> {
596+
let innermost_id = ast_ids.last()?;
597+
598+
for syn_id in ast_ids.iter().rev() {
599+
let Some(expr) = find_expr_of_id(items, syn_id.id()) else {
600+
continue;
601+
};
602+
603+
let Expression_::Let(let_dest, _, _) = &expr.expr_ else {
604+
continue;
605+
};
606+
607+
match let_dest {
608+
LetDestination::Symbol(symbol) => {
609+
if &AstId::Sym(symbol.id) != innermost_id {
610+
return None;
611+
}
612+
613+
return Some((value.clone(), symbol.position.clone()));
614+
}
615+
LetDestination::Destructure(symbols) => {
616+
let mut matching_sym_i: Option<usize> = None;
617+
for (i, symbol) in symbols.iter().enumerate() {
618+
if &AstId::Sym(symbol.id) == innermost_id {
619+
matching_sym_i = Some(i);
620+
break;
621+
}
622+
}
623+
let matching_sym_i = matching_sym_i?;
624+
625+
match value {
626+
Value::Tuple { items, .. } => {
627+
let tuple_item = items.get(matching_sym_i)?;
628+
return Some((
629+
tuple_item.clone(),
630+
symbols[matching_sym_i].position.clone(),
631+
));
632+
}
633+
_ => return None,
634+
}
635+
}
636+
};
567637
}
638+
639+
None
568640
}
569641

570642
/// Helper for starting evaluation with a function call. Used when
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
let aa = (1, 2)
3+
let (x, y) = aa
4+
// ^
5+
}
6+
7+
fun main(_) {}
8+
9+
// args: test-eval-up-to
10+
// expected stdout: src/test_files/eval_up_to/tuple_destructure.gdn:3: 1
11+

0 commit comments

Comments
 (0)