Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor Transform String Resugar Term Scott #714

Merged
merged 7 commits into from
Sep 17, 2024
287 changes: 101 additions & 186 deletions src/fun/transform/resugar_string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,237 +4,152 @@ use crate::{
};

impl Term {
/// Converts lambda-encoded strings ending with String/nil to a string literals.
/// Converts lambda-encoded strings ending with String/nil to string literals.
pub fn resugar_strings(&mut self, adt_encoding: AdtEncoding) {
match adt_encoding {
AdtEncoding::Scott => self.resugar_strings_scott(),
AdtEncoding::NumScott => self.resugar_strings_num_scott(),
AdtEncoding::Scott => self.try_resugar_strings_with(Self::resugar_strings_scott),
AdtEncoding::NumScott => self.try_resugar_strings_with(Self::resugar_strings_num_scott),
}
}

/// Converts num-scott-encoded strings ending with String/nil to a string literals.
fn resugar_strings_num_scott(&mut self) {
/// Converts encoded strings to string literals using the provided extraction function.
fn try_resugar_strings_with(&mut self, extract_fn: fn(&Term) -> Option<(char, &Term)>) {
maybe_grow(|| {
// Search for a String/cons pattern in the term and try to build a string from that point on.
// If successful, replace the term with the string.
// If not, keep as-is.

// Nil: String/nil
if let Term::Ref { nam } = self {
if nam == builtins::SNIL {
*self = Term::str("");
// Try to resugar nil or cons patterns. If unsuccessful, recurse into child terms.
if !self.try_resugar_strings_nil() && !self.try_resugar_strings_cons(extract_fn) {
for child in self.children_mut() {
child.try_resugar_strings_with(extract_fn);
}
}
// Cons: @x (x CONS_TAG <num> <str>)
if let Term::Lam { tag: Tag::Static, pat, bod } = self {
if let Pattern::Var(Some(var_lam)) = pat.as_mut() {
if let Term::App { tag: Tag::Static, fun, arg: tail } = bod.as_mut() {
if let Term::App { tag: Tag::Static, fun, arg: head } = fun.as_mut() {
if let Term::App { tag: Tag::Static, fun, arg } = fun.as_mut() {
if let Term::Var { nam: var_app } = fun.as_mut() {
if let Term::Ref { nam: Name(nam) } = arg.as_mut() {
if let Term::Num { val: Num::U24(head) } = head.as_mut() {
if var_lam == var_app && nam == builtins::SCONS_TAG_REF {
let head = char::from_u32(*head).unwrap_or(char::REPLACEMENT_CHARACTER);
if let Some(str) = build_string_num_scott(tail, head.to_string()) {
*self = Term::str(&str);
} else {
// Not a string term, keep as-is.
}
}
}
}
}
}
}
})
}

/// Attempts to resugar a nil term (String/nil) to an empty string literal.
fn try_resugar_strings_nil(&mut self) -> bool {
matches!(self, Term::Ref { nam } if nam == builtins::SNIL).then(|| *self = Term::str("")).is_some()
}

/// Attempts to resugar a cons term to a string literal.
fn try_resugar_strings_cons(&mut self, extract_fn: fn(&Term) -> Option<(char, &Term)>) -> bool {
self
.try_resugar_strings_cons_with(extract_fn)
.or_else(|| self.try_resugar_strings_cons_common())
.map(|str| *self = Term::str(&str))
.is_some()
}

/// Attempts to resugar a cons term using the provided extraction function.
fn try_resugar_strings_cons_with(&self, extract_fn: fn(&Term) -> Option<(char, &Term)>) -> Option<String> {
extract_fn(self)
.and_then(|(head_char, tail)| Self::build_strings_common(tail, head_char.to_string(), extract_fn))
}

/// Attempts to resugar a cons term using the common extraction method.
fn try_resugar_strings_cons_common(&self) -> Option<String> {
if let Term::App { tag: Tag::Static, fun, arg: tail } = self {
if let Term::App { tag: Tag::Static, fun: inner_fun, arg: head } = fun.as_ref() {
if let (Term::Ref { nam }, Term::Num { val: Num::U24(head_val) }) =
(inner_fun.as_ref(), head.as_ref())
{
if nam == builtins::SCONS {
let head_char = char::from_u32(*head_val).unwrap_or(char::REPLACEMENT_CHARACTER);
return Self::build_strings_common(tail, head_char.to_string(), Self::extract_strings_common);
}
}
}
// Cons: (String/cons <num> <str>)
if let Term::App { tag: Tag::Static, fun, arg: tail } = self {
if let Term::App { tag: Tag::Static, fun, arg: head } = fun.as_mut() {
if let Term::Ref { nam } = fun.as_mut() {
if let Term::Num { val: Num::U24(head) } = head.as_mut() {
if nam == builtins::SCONS {
let head = char::from_u32(*head).unwrap_or(char::REPLACEMENT_CHARACTER);
if let Some(str) = build_string_num_scott(tail, head.to_string()) {
*self = Term::str(&str);
} else {
// Not a string term, keep as-is.
}
}
}
}
None
}

/// Builds a string from a term structure using the provided extraction function.
fn build_strings_common(
term: &Term,
mut s: String,
extract_fn: fn(&Term) -> Option<(char, &Term)>,
) -> Option<String> {
maybe_grow(|| {
let mut current = term;
loop {
match current {
// If we reach a nil term, we've completed the string
Term::Ref { nam } if nam == builtins::SNIL => return Some(s),
_ => {
// Extract the next character and continue building the string
let (head, next) = extract_fn(current).or_else(|| Self::extract_strings_common(current))?;
s.push(head);
current = next;
}
}
}

for child in self.children_mut() {
child.resugar_strings_num_scott();
}
})
}

/// Converts scott-encoded strings ending with String/nil to a string literals.
fn resugar_strings_scott(&mut self) {
maybe_grow(|| {
// Search for a String/cons pattern in the term and try to build a string from that point on.
// If successful, replace the term with the string.
// If not, keep as-is.

// Nil: String/nil
if let Term::Ref { nam } = self {
if nam == builtins::SNIL {
*self = Term::str("");
}
}
// Cons: @* @c (c <num> <str>)
if let Term::Lam { tag: Tag::Static, pat, bod } = self {
if let Pattern::Var(None) = pat.as_mut() {
if let Term::Lam { tag: Tag::Static, pat, bod } = bod.as_mut() {
if let Pattern::Var(Some(var_lam)) = pat.as_mut() {
if let Term::App { tag: Tag::Static, fun, arg: tail } = bod.as_mut() {
if let Term::App { tag: Tag::Static, fun, arg: head } = fun.as_mut() {
if let Term::Var { nam: var_app } = fun.as_mut() {
if let Term::Num { val: Num::U24(head) } = head.as_mut() {
if var_lam == var_app {
let head = char::from_u32(*head).unwrap_or(char::REPLACEMENT_CHARACTER);
if let Some(str) = build_string_scott(tail, head.to_string()) {
*self = Term::str(&str);
} else {
// Not a string term, keep as-is.
}
}
}
/// Extracts a character and the remaining term from a Scott-encoded string term.
/// The structure of this function mimics the shape of the AST for easier visualization.
fn resugar_strings_scott(term: &Term) -> Option<(char, &Term)> {
if let Term::Lam { tag: Tag::Static, pat: outer_pat, bod } = term {
if let Pattern::Var(None) = outer_pat.as_ref() {
if let Term::Lam { tag: Tag::Static, pat: inner_pat, bod: inner_bod } = bod.as_ref() {
if let Pattern::Var(Some(var_lam)) = inner_pat.as_ref() {
if let Term::App { tag: Tag::Static, fun, arg: tail } = inner_bod.as_ref() {
if let Term::App { tag: Tag::Static, fun: inner_fun, arg: head } = fun.as_ref() {
if let (Term::Var { nam: var_app }, Term::Num { val: Num::U24(head_val) }) =
(inner_fun.as_ref(), head.as_ref())
{
if var_lam == var_app {
let head_char = char::from_u32(*head_val).unwrap_or(char::REPLACEMENT_CHARACTER);
return Some((head_char, tail));
}
}
}
}
}
}
}
// Cons: (String/cons <num> <str>)
if let Term::App { tag: Tag::Static, fun, arg: tail } = self {
if let Term::App { tag: Tag::Static, fun, arg: head } = fun.as_mut() {
if let Term::Ref { nam } = fun.as_mut() {
if let Term::Num { val: Num::U24(head) } = head.as_mut() {
if nam == builtins::SCONS {
let head = char::from_u32(*head).unwrap_or(char::REPLACEMENT_CHARACTER);
if let Some(str) = build_string_num_scott(tail, head.to_string()) {
*self = Term::str(&str);
} else {
// Not a string term, keep as-is.
}
}
}
}
}
}

for child in self.children_mut() {
child.resugar_strings_scott();
}
})
}
None
}
}

fn build_string_num_scott(term: &Term, mut s: String) -> Option<String> {
maybe_grow(|| {
// Nil: String/nil
if let Term::Ref { nam } = term {
if nam == builtins::SNIL {
return Some(s);
}
}
// Cons: @x (x CONS_TAG <num> <str>)
/// Extracts a character and the remaining term from a NumScott-encoded string term.
/// The structure of this function mimics the shape of the AST for easier visualization.
fn resugar_strings_num_scott(term: &Term) -> Option<(char, &Term)> {
if let Term::Lam { tag: Tag::Static, pat, bod } = term {
if let Pattern::Var(Some(var_lam)) = pat.as_ref() {
if let Term::App { tag: Tag::Static, fun, arg: tail } = bod.as_ref() {
if let Term::App { tag: Tag::Static, fun, arg: head } = fun.as_ref() {
if let Term::App { tag: Tag::Static, fun, arg } = fun.as_ref() {
if let Term::Var { nam: var_app } = fun.as_ref() {
if let Term::Ref { nam } = arg.as_ref() {
if let Term::Num { val: Num::U24(head) } = head.as_ref() {
if var_lam == var_app && nam == builtins::SCONS_TAG_REF {
// New string character, append and recurse
let head = char::from_u32(*head).unwrap_or(char::REPLACEMENT_CHARACTER);
s.push(head);
return build_string_num_scott(tail, s);
}
}
if let (
Term::Var { nam: var_app },
Term::Ref { nam: Name(ref_nam) },
Term::Num { val: Num::U24(head_val) },
) = (fun.as_ref(), arg.as_ref(), head.as_ref())
{
if var_lam == var_app && ref_nam == builtins::SCONS_TAG_REF {
let head_char = char::from_u32(*head_val).unwrap_or(char::REPLACEMENT_CHARACTER);
return Some((head_char, tail));
}
}
}
}
}
}
}
// Cons: (String/cons <num> <str>)
if let Term::App { tag: Tag::Static, fun, arg: tail } = term {
if let Term::App { tag: Tag::Static, fun, arg: head } = fun.as_ref() {
if let Term::Ref { nam } = fun.as_ref() {
if let Term::Num { val: Num::U24(head) } = head.as_ref() {
if nam == builtins::SCONS {
// New string character, append and recurse
let head = char::from_u32(*head).unwrap_or(char::REPLACEMENT_CHARACTER);
s.push(head);
return build_string_num_scott(tail, s);
}
}
}
}
}
// Not a string term, stop
None
})
}
}

fn build_string_scott(term: &Term, mut s: String) -> Option<String> {
maybe_grow(|| {
// Nil: String/nil
if let Term::Ref { nam } = term {
if nam == builtins::SNIL {
return Some(s);
}
}
// Cons: @* @c (c <num> <str>)
if let Term::Lam { tag: Tag::Static, pat, bod } = term {
if let Pattern::Var(None) = pat.as_ref() {
if let Term::Lam { tag: Tag::Static, pat, bod } = bod.as_ref() {
if let Pattern::Var(Some(var_lam)) = pat.as_ref() {
if let Term::App { tag: Tag::Static, fun, arg: tail } = bod.as_ref() {
if let Term::App { tag: Tag::Static, fun, arg: head } = fun.as_ref() {
if let Term::Var { nam: var_app } = fun.as_ref() {
if let Term::Num { val: Num::U24(head) } = head.as_ref() {
if var_lam == var_app {
// New string character, append and recurse
let head = char::from_u32(*head).unwrap_or(char::REPLACEMENT_CHARACTER);
s.push(head);
return build_string_scott(tail, s);
}
}
}
}
}
}
}
}
}
// Cons: (String/cons <num> <str>)
/// Extracts a character and the remaining term from a common-encoded string term.
/// The structure of this function mimics the shape of the AST for easier visualization.
fn extract_strings_common(term: &Term) -> Option<(char, &Term)> {
if let Term::App { tag: Tag::Static, fun, arg: tail } = term {
if let Term::App { tag: Tag::Static, fun, arg: head } = fun.as_ref() {
if let Term::Ref { nam } = fun.as_ref() {
if let Term::Num { val: Num::U24(head) } = head.as_ref() {
if nam == builtins::SCONS {
// New string character, append and recurse
let head = char::from_u32(*head).unwrap_or(char::REPLACEMENT_CHARACTER);
s.push(head);
return build_string_scott(tail, s);
}
if let (Term::Ref { nam }, Term::Num { val: Num::U24(head_val) }) = (fun.as_ref(), head.as_ref()) {
if nam == builtins::SCONS {
let head_char = char::from_u32(*head_val).unwrap_or(char::REPLACEMENT_CHARACTER);
return Some((head_char, tail));
}
}
}
}
// Not a string term, stop
None
})
}
}
Loading