% crochet

/// Concatenates two pieces of text
command (X is unsafe-arbitrary-text) ++ (Y is unsafe-arbitrary-text) = 
  "[X][Y]"
test
  assert ("" ++ "") flatten-into-plain-text =:= "";
  assert ("" ++ "abc") flatten-into-plain-text =:= "abc";
  assert ("abc" ++ "") flatten-into-plain-text =:= "abc";
  assert ("abc" ++ "def") flatten-into-plain-text =:= "abcdef";
end

/// Converts an interpolation to its normal form
command (X is interpolation) normalise
  = foreign interpolation.normalise(X)
test
  assert "abc[1]def" normalise =:= "abc[1]def";
  assert ("a[1]b" ++ "d[1]f") normalise =:= "a[1]bd[1]f";
end

/// Concatenates interpolations and texts
command (X is interpolation) ++ (Y is interpolation) =
  foreign interpolation.concat(X, Y)
test
  assert ("[1]a[2]x" ++ "y[3]b[4]") normalise =:= "[1]a[2]xy[3]b[4]";
end

command (X is unsafe-arbitrary-text) ++ (Y is interpolation) do
  "[X]" ++ Y;
test
  assert ("abc" ++ "d[1]f") =:= "["abc"]d[1]f";
end

command (X is interpolation) ++ (Y is unsafe-arbitrary-text) do
  X ++ "[Y]";
test
  assert ("a[1]c" ++ "def") =:= "a[1]c["def"]";
end

/// Returns all parts of an interpolation
command (X is interpolation) parts = 
  foreign interpolation.parts(X)
test
  assert ("abc" as interpolation) parts =:= ["abc"];
  assert "abc[1]def" parts =:= ["abc", 1, "def"];
  assert "[1][2]" parts =:= [1, 2];
end

/// Returns all dynamic parts of an interpolation
command (X is interpolation) holes =
  foreign interpolation.holes(X)
test
  assert ("abc" as interpolation) holes =:= ["abc"];
  assert ("a[1]c" ++ "abc") holes =:= [1, "abc"];
  assert "abc[1]def" holes =:= [1];
  assert "[1][2]" holes =:= [1, 2];
end

/// Returns a static textual representation of the interpolation
command (X is interpolation) static-text =
  foreign interpolation.static-text(X)
test
  assert ("abc" as interpolation) static-text =:= "\[_\]";
  assert ("a[1]c" ++ "abc") static-text =:= "a\[_\]c\[_\]";
  assert "abc[1]def" static-text =:= "abc\[_\]def";
  assert "[1][2]" static-text =:= "\[_\]\[_\]";
end

local singleton text-concat;

/// If given an interpolation consisting only of pieces of text, returns
/// the flattened piece of text that combines all of them together.
command (X is interpolation) flatten-into-plain-text do
  X parts
    | fold-from: "" with: (text-concat left: _ right: _);
test
  // Trusted text begets trusted text
  let Trusted-A = "A";
  let Trusted-B = "B";
  assert "[Trusted-A][Trusted-B]" flatten-into-plain-text === "AB";
  assert "[Trusted-A][Trusted-B]" flatten-into-plain-text is dynamic-text;

  // Untrusted text begets untrusted text
  let Untrusted-A = taint make-untrusted: Trusted-A;
  let Untrusted-B = taint make-untrusted: Trusted-B;
  assert (untaint make-trusted: "[Trusted-A][Untrusted-B]" flatten-into-plain-text) === "AB";
  assert "[Trusted-A][Untrusted-B]" flatten-into-plain-text is untrusted-text;
  assert (untaint make-trusted: "[Untrusted-A][Trusted-B]" flatten-into-plain-text) === "AB";
  assert "[Untrusted-A][Trusted-B]" flatten-into-plain-text is untrusted-text;
  assert (untaint make-trusted: "[Untrusted-A][Untrusted-B]" flatten-into-plain-text) === "AB";
  assert "[Untrusted-A][Untrusted-B]" flatten-into-plain-text is untrusted-text;
  
  // Interpolation is flattened recursively
  let AB = "[Trusted-A][Trusted-B]";
  let ABAB = "[AB][AB]";
  let CABABC = "C[ABAB]C";
  assert CABABC flatten-into-plain-text === "CABABC";
end

command text-concat left: (L is untrusted-text) right: (R is text) =
  foreign text.untrusted-concat(L, R);

command text-concat left: (L is text) right: (R is untrusted-text) =
  foreign text.untrusted-concat(L, R);

command text-concat left: (L is untrusted-text) right: (R is untrusted-text) =
  foreign text.untrusted-concat(L, R);

command text-concat left: (L is text) right: (R is text) =
  foreign text.trusted-concat(L, R);

command text-concat left: (L is interpolation) right: R =
  text-concat left: L flatten-into-plain-text right: R;

command text-concat left: L right: (R is interpolation) =
  text-concat left: L right: R flatten-into-plain-text;
