さすがにもう縮まないのでネタバレ

最終的に1525バイトになった。劇的に縮められる新機軸はもうなさそうなのでネタバレ

データは以下のようにひとつの関数にエンコードする。すなわち「w → Ww」「W→WWw」「v→WWWw」になる。

  let.dat = abs o1 do
    let.o2 = o1(o1) # w
    let.o3 = o1(o2) # W
    let.o4 = o1(o3) # v
    let.o5 = o4(o4) # w
    let.o6 = o4(o5) # W
    let.o7 = o4(o6) # v
  end

スタックの少し前にある関数にスタックのトップを適用すると、スタック位置の差によって「wWv」のいずれかが出力される。

よって、oN は次のように動作しないといけない。

o3(o3) -> (out w); o4   |    (out Ww);   o4
o2(o3) -> (out W); o4   |    (out WWw);  o4
o1(o3) -> (out v); o4   |    (out WWWw); o4

エンコード前のデータを出力するときは左の結果、エンコード後のデータなら右。

ふたつの動作はほとんど同じなので、各々トリプルを作ってdatをスキャンする関数に渡せばよい
ラムダ計算ではトリプルはこうやって作る。

  let.triplet = abs a, b, c, f do
    f(a, b, c)
  end
  let.tr1 = triplet(out_w, out_W, out_v)
  let.tr2 = triplet(out_Ww, out_WWw, out_WWWw)

  scan_dat(tr1)  # エンコード前のデータが出力される
  scan_dat(tr2)  # エンコード後のデータが出力される

あとはscan_datをどう作るかである。

oNはoMを受け取ったら、(M-N)%3を計算して、o(M+1)を返さないといけないので、
とりあえずは

  let.oN = abs my, tr, n, oM do
    ... 
    (exec_tr tr (m_n_mod3 (get_m oM) n)))
    ...
    (my my tr (succ (get_m oM)))
  end

こういう感じになると思う。
ここで、問題になるのは

o3 == (oN oN tr 3)
o2 == (oN oN tr 2)

という感じに、oNとoMの中身は同じ関数なので、oMからMをとってくる機能もこの関数の中にいれないといけないのである。
これが今回一番難しいところだ。