calcit 0.12.30

Interpreter and js codegen for Calcit
Documentation
use crate::util::string::has_ns_part;

pub(super) fn escape_var(name: &str) -> String {
  if has_ns_part(name) {
    unreachable!("Invalid variable name `{}`, use `escape_ns_var` instead", name);
  }
  match name {
    "if" => String::from("_IF_"),
    "do" => String::from("_DO_"),
    "else" => String::from("_ELSE_"),
    "let" => String::from("_LET_"),
    "case" => String::from("_CASE_"),
    "-" => String::from("_SUB_"),
    _ => name
      .replace('-', "_")
      .replace('.', "_DOT_")
      .replace('?', "_$q_")
      .replace('+', "_ADD_")
      .replace('^', "_CRT_")
      .replace('*', "_$s_")
      .replace('&', "_$n_")
      .replace("{}", "_$M_")
      .replace("[]", "_$L_")
      .replace('{', "_CURL_")
      .replace('}', "_CURR_")
      .replace('\'', "_SQUO_")
      .replace('[', "_SQRL_")
      .replace(']', "_SQRR_")
      .replace('!', "_$x_")
      .replace('%', "_PCT_")
      .replace('/', "_SLSH_")
      .replace('=', "_$e_")
      .replace('>', "_GT_")
      .replace('<', "_LT_")
      .replace(':', "_$o_")
      .replace(';', "_SCOL_")
      .replace('#', "_SHA_")
      .replace('\\', "_BSL_"),
  }
}

pub(super) fn escape_cirru_str(s: &str) -> String {
  let mut result = String::from('"');
  for c in s.chars() {
    match c {
      '\\' => result.push_str("\\\\"),
      '\"' => result.push_str("\\\""),
      '\n' => result.push_str("\\n"),
      '\t' => result.push_str("\\t"),
      _ => result.push(c),
    }
  }
  result.push('"');
  result
}

pub(crate) fn unescape_var(name: &str) -> String {
  match name {
    "_IF_" => return String::from("if"),
    "_DO_" => return String::from("do"),
    "_ELSE_" => return String::from("else"),
    "_LET_" => return String::from("let"),
    "_CASE_" => return String::from("case"),
    "_SUB_" => return String::from("-"),
    _ => {}
  }

  let mut decoded = name.to_string();
  for (from, to) in [
    ("_$q_", "?"),
    ("_ADD_", "+"),
    ("_CRT_", "^"),
    ("_$s_", "*"),
    ("_$n_", "&"),
    ("_$M_", "{}"),
    ("_$L_", "[]"),
    ("_CURL_", "{"),
    ("_CURR_", "}"),
    ("_SQUO_", "'"),
    ("_SQRL_", "["),
    ("_SQRR_", "]"),
    ("_$x_", "!"),
    ("_PCT_", "%"),
    ("_SLSH_", "/"),
    ("_$e_", "="),
    ("_GT_", ">"),
    ("_LT_", "<"),
    ("_$o_", ":"),
    ("_SCOL_", ";"),
    ("_SHA_", "#"),
    ("_BSL_", "\\"),
    ("_DOT_", "."),
  ] {
    decoded = decoded.replace(from, to);
  }

  decoded
}

#[cfg(test)]
mod tests {
  use super::*;

  #[test]
  fn escapes_keywords_and_symbols() {
    assert_eq!(escape_var("if"), "_IF_");
    assert_eq!(escape_var("a-b?"), "a_b_$q_");
    assert_eq!(escape_var("[]"), "_$L_");
  }

  #[test]
  fn escapes_cirru_string_chars() {
    assert_eq!(escape_cirru_str("a\nb\t\\\""), "\"a\\nb\\t\\\\\\\"\"");
  }

  #[test]
  fn unescapes_symbols_best_effort() {
    assert_eq!(unescape_var("a_b_$q_"), "a_b?");
    assert_eq!(unescape_var("_DOT_"), ".");
    assert_eq!(unescape_var("_IF_"), "if");
  }
}