Unassignable-ABCDXYZ equivalency proof
Converting ABCDXYZ to :≠ is trivial. Below is a way to convert :≠ to ABCDXYZ.
For each object in a :≠ program, there are three things to do:
- Replace its declaration with one that declares just
ABCD
object(s). - Replace its definition with definitions for each new
ABCD
object. - Replace method calls on it with ones on the new
ABCD
object(s).
Once this is done, it is trivial to convert to ABCDXYZ... nearly.
:≠ allows initial values; ABCDXYZ sets everything to A. So we add an object named (say) unao_init
, which sets every other object's initial value, then does main->call;
; this becomes object number 0 in ABCDXYZ.
And io->output(c)
is converted into "c
.
Types
Each type will be dealt with separately:
ABCD
function
integer
unat_io
-- made up name forio
's typeunat_bit
-- new type to help convertinteger
s (see below)
(Names starting with una
are reserved for use of the implementation. Here, the prefix unat_
is used for new types and prefix unao_
for new objects.)
ABCD
Nothing to do.
unat_io
Cannot declare anything of this type.
function
Let f be a function
object. Then:
Declarations
function f = activated; ==> ABCD unao_fun_f = B; function f = deactivated; ==> ABCD unao_fun_f = C;
Definition
f { run { statements } } ==> unao_fun_f { event { statements } }
Method calls
f -> activate; ==> unao_fun_f -> Z; f -> deactivate; ==> unao_fun_f -> Y; f -> call; ==> unao_fun_f -> X, Z;
(Note: obj -> m1, m2;
is shorthand for obj -> m1; obj -> m2;
.)
unat_bit
A unat_bit
is a one-bit integer
, with the following methods:
bit->toggle; Toggle bit. bit->increment; Toggle bit. If it becomes clear, run the carry event. bit->decrement; Toggle bit. If it becomes set, run the borrow event. bit->loop; Run the iterate event. bit->loopifset; If currently set, run the iterateifset event.
A unat_bit
is implemented with 4 ABCD objects, named (poorly and unimaginatively) a, b, c, and d:
a handles the carry
event.
b handles the borrow
event.
c handles the iterate
event.
d handles the iterateifset
event.
a, b and d's values are updated in tandem when the bit is toggled (c stays in state B).
Declarations
unat_bit bit = init;
becomes (A_or_C = A if init==set, C if init==clear):
ABCD unao_bita_bit = A_or_C; ABCD unao_bitb_bit = A_or_C; ABCD unao_bitc_bit = B; ABCD unao_bitd_bit = A_or_C;
Definitions
bit { carry { stmts_carry } borrow { stmts_borrow } iterate { stmts_iterate } iterateifset { stmts_iterateifset } }
becomes
unao_bita_bit { event { stmts_carry } } unao_bitb_bit { event { stmts_borrow } } unao_bitc_bit { event { stmts_iterate } } unao_bitd_bit { event { stmts_iterateifset } }
Method calls
bit->toggle ==> unao_bita_bit->X, Y; unao_bitb_bit->X, Y; unao_bitd_bit->X, Y; bit->increment ==> unao_bita_bit->X, X; unao_bitb_bit->X, Y; unao_bitd_bit->X, Y; bit->decrement ==> unao_bita_bit->X, Y; unao_bitb_bit->Y, Y; unao_bitd_bit->X, Y; bit->loop ==> unao_bitc_bit->X, Z; bit->loopifset ==> unao_bitd_bit->X, X, X, Y;
integer
Let x be an integer
object. First, it is converted into individual unat_bits
, which can be converted into ABCD
s as above.
Declaration
integer x(max) = init;
is converted to
unat_bit unao_i0_x = init0 unat_bit unao_i1_x = init1 unat_bit unao_i2_x = init2 unat_bit unao_i3_x = init3 ... unat_bit unao_i(n-1)_x = init(n-1)
(where n is number of bits in max, and initk is kth bit of init.)
Definition
x { overflow { stmts_overflow } underflow { stmts_underflow } iterate { stmts_iterate } }
is converted to
unao_i0_x { carry { unao_i1_x->increment; } borrow { unao_i1_x->decrement; } iterate { stmts_iterate } iterateifset { stmts_iterate } } unao_i1_x { carry { unao_i2_x->increment; } borrow { unao_i2_x->decrement; } iterate { unao_i0_x->loop, loop; } iterateifset { unao_i0_x->loop, loop; } } ... unao_ik_x { carry { unao_i(k+1)_x->increment; } borrow { unao_i(k+1)_x->decrement; } iterate { unao_i(k-1)_x->loop, loop; } iterateifset { unao_i(k-1)_x->loop, loop; } } ... unao_i(n-2)_x { carry { unao_i(n-1)_x->increment; } borrow { unao_i(n-1)_x->decrement; } iterate { unao_i(n-3)_x->loop, loop; } iterateifset { unao_i(n-3)_x->loop, loop; } } unao_i(n-1)_x { carry { stmts_overflow } borrow { stmts_underflow } iterate { unao_i(n-2)_x->loop, loop; } iterateifset { unao_i(n-2)_x->loop, loop; } }
Method calls
x -> increment(r); x -> decrement(r);
become (where r = 2 ** k)
unao_ik_x -> increment; unao_ik_x -> decrement;
and
x -> loop;
becomes
unao_i0_x -> loopifset; unao_i1_x -> loopifset; unao_i2_x -> loopifset; ... unao_i(n-1)_x -> loopifset;
How does all this work?
When increment
ing/decrement
ing x, it increment
s/decrement
s the bit required, and if there is a carry/borrow, the next highest bit is increment
ed/decrement
ed. loop
ing on x will loopifset
on each of its bits. Each set bit k of x then performs x.iterate 2 ** k times, by loop
ing the next-lower-order bit twice.
Example
Say x = 6 (dec) = 0110 (bin). So
x -> loop;
becomes
unao_i0_x -> loopifset; unao_i1_x -> loopifset; unao_i2_x -> loopifset; unao_i3_x -> loopifset;
The 0th loopifset
does nothing.
The 1st loopifset
runs x.iterate 2 times.
The 2nd loopifset
runs x.iterate 4 times.
The 3rd loopifset
does nothing.
In all, x.iterate is run 6 times.