#!/usr/bin/perl
use strict;
use warnings;
sub lam {
{ type
=> 'lam', parameter
=> shift, body
=> shift }; }
sub app {
{ type
=> 'app', callee
=> shift, argument
=> shift }; }
sub var {
{ type
=> 'var', name
=> shift }; }
my $next_alpha_renaming_name = 'a';
sub alpha_rename {
my %methods = (
lam => sub {
my $inner_env = {%$env};
my $new_name = $next_alpha_renaming_name++;
$inner_env->{$term->{parameter}} = $new_name;
lam($new_name, alpha_rename($term->{body}, $inner_env));
},
app => sub {
app(
alpha_rename($term->{callee}, $env),
alpha_rename($term->{argument}, $env),
);
},
var => sub { var($env->{$term->{name}} // $term->{name}); },
);
$methods{$term->{type}}->();
}
sub fmt {
my %methods = (
lam => sub { '(\\' . $term->{parameter} . '.' . fmt($term->{body}) . ')' },
app => sub { fmt($term->{callee}) . ' ' . fmt($term->{argument}) },
var => sub { $term->{name} },
);
$methods{$term->{type}}->();
}
my $term = lam('x', app(var('x'), lam('x', var('x'))));
CORE::say fmt($term);
CORE::say fmt(alpha_rename($term));
IyEvdXNyL2Jpbi9wZXJsCnVzZSBzdHJpY3Q7CnVzZSB3YXJuaW5nczsKCnN1YiBsYW0gewogICAgeyB0eXBlID0+ICdsYW0nLCBwYXJhbWV0ZXIgPT4gc2hpZnQsIGJvZHkgPT4gc2hpZnQgfTsKfQoKc3ViIGFwcCB7CiAgICB7IHR5cGUgPT4gJ2FwcCcsIGNhbGxlZSA9PiBzaGlmdCwgYXJndW1lbnQgPT4gc2hpZnQgfTsKfQoKc3ViIHZhciB7CiAgICB7IHR5cGUgPT4gJ3ZhcicsIG5hbWUgPT4gc2hpZnQgfTsKfQoKbXkgJG5leHRfYWxwaGFfcmVuYW1pbmdfbmFtZSA9ICdhJzsKc3ViIGFscGhhX3JlbmFtZSB7CiAgICBteSAkdGVybSA9IHNoaWZ0OwogICAgbXkgJGVudiA9IHNoaWZ0IC8vIHt9OwogICAgbXkgJW1ldGhvZHMgPSAoCiAgICAgICAgbGFtID0+IHN1YiB7CiAgICAgICAgICAgIG15ICRpbm5lcl9lbnYgPSB7JSRlbnZ9OwogICAgICAgICAgICBteSAkbmV3X25hbWUgPSAkbmV4dF9hbHBoYV9yZW5hbWluZ19uYW1lKys7CiAgICAgICAgICAgICRpbm5lcl9lbnYtPnskdGVybS0+e3BhcmFtZXRlcn19ID0gJG5ld19uYW1lOwogICAgICAgICAgICBsYW0oJG5ld19uYW1lLCBhbHBoYV9yZW5hbWUoJHRlcm0tPntib2R5fSwgJGlubmVyX2VudikpOwogICAgICAgIH0sCiAgICAgICAgYXBwID0+IHN1YiB7CiAgICAgICAgICAgIGFwcCgKICAgICAgICAgICAgICAgIGFscGhhX3JlbmFtZSgkdGVybS0+e2NhbGxlZX0sICRlbnYpLAogICAgICAgICAgICAgICAgYWxwaGFfcmVuYW1lKCR0ZXJtLT57YXJndW1lbnR9LCAkZW52KSwKICAgICAgICAgICAgKTsKICAgICAgICB9LAogICAgICAgIHZhciA9PiBzdWIgeyB2YXIoJGVudi0+eyR0ZXJtLT57bmFtZX19IC8vICR0ZXJtLT57bmFtZX0pOyB9LAogICAgKTsKICAgICRtZXRob2RzeyR0ZXJtLT57dHlwZX19LT4oKTsKfQoKc3ViIGZtdCB7CiAgICBteSAkdGVybSA9IHNoaWZ0OwogICAgbXkgJW1ldGhvZHMgPSAoCiAgICAgICAgbGFtID0+IHN1YiB7ICcoXFwnIC4gJHRlcm0tPntwYXJhbWV0ZXJ9IC4gJy4nIC4gZm10KCR0ZXJtLT57Ym9keX0pIC4gJyknIH0sCiAgICAgICAgYXBwID0+IHN1YiB7IGZtdCgkdGVybS0+e2NhbGxlZX0pIC4gJyAnIC4gZm10KCR0ZXJtLT57YXJndW1lbnR9KSB9LAogICAgICAgIHZhciA9PiBzdWIgeyAkdGVybS0+e25hbWV9IH0sCiAgICApOwogICAgJG1ldGhvZHN7JHRlcm0tPnt0eXBlfX0tPigpOwp9CgpteSAkdGVybSA9IGxhbSgneCcsIGFwcCh2YXIoJ3gnKSwgbGFtKCd4JywgdmFyKCd4JykpKSk7CkNPUkU6OnNheSBmbXQoJHRlcm0pOwpDT1JFOjpzYXkgZm10KGFscGhhX3JlbmFtZSgkdGVybSkpOwo=