Skip to content

Commit 971f18f

Browse files
authored
Term::has_free_variables: check if an expression has free variables (#54)
* feat: Term::has_free_variables() * fix: has_free_variables considers Var(0) free * fix: has_free_vars was broken under applications
1 parent 221c85c commit 971f18f

File tree

1 file changed

+42
-0
lines changed

1 file changed

+42
-0
lines changed

src/term.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,32 @@ impl Term {
445445
_ => false,
446446
}
447447
}
448+
449+
/// Returns `true` if `self` has any free vairables.
450+
///
451+
/// # Example
452+
/// ```
453+
/// use lambda_calculus::*;
454+
///
455+
/// let with_freevar = abs(Var(2)); // λ 2
456+
/// let without_freevar = abs(Var(1)); // λ 1
457+
///
458+
/// assert!(with_freevar.has_free_variables());
459+
/// assert!(!without_freevar.has_free_variables());
460+
pub fn has_free_variables(&self) -> bool {
461+
self.has_free_variables_helper(0)
462+
}
463+
464+
fn has_free_variables_helper(&self, depth: usize) -> bool {
465+
match self {
466+
Var(x) => *x > depth || *x == 0,
467+
Abs(p) => p.has_free_variables_helper(depth + 1),
468+
App(p_boxed) => {
469+
let (ref f, ref a) = **p_boxed;
470+
f.has_free_variables_helper(depth) || a.has_free_variables_helper(depth)
471+
}
472+
}
473+
}
448474
}
449475

450476
/// Wraps a `Term` in an `Abs`traction. Consumes its argument.
@@ -713,4 +739,20 @@ mod tests {
713739
assert!(app(abs(Var(1)), Var(1)).is_isomorphic_to(&app(abs(Var(1)), Var(1))));
714740
assert!(!app(abs(Var(1)), Var(1)).is_isomorphic_to(&app(Var(2), abs(Var(1)))));
715741
}
742+
743+
#[test]
744+
fn has_free_variables() {
745+
assert!(!(abs(Var(1)).has_free_variables()));
746+
assert!(abs(Var(2)).has_free_variables());
747+
assert!(app(abs(Var(2)), Var(1)).has_free_variables());
748+
assert!(app(abs(Var(2)), abs(Var(1))).has_free_variables());
749+
assert!(app(abs(Var(1)), abs(Var(2))).has_free_variables());
750+
assert!(!app(abs(Var(1)), abs(Var(1))).has_free_variables());
751+
assert!(!(abs(app(
752+
abs(app(Var(2), app(Var(1), Var(1)))),
753+
abs(app(Var(2), app(Var(1), Var(1)))),
754+
)))
755+
.has_free_variables());
756+
assert!((Var(0)).has_free_variables());
757+
}
716758
}

0 commit comments

Comments
 (0)