The main tool here is a Python program called Sanity. This tool makes it easier for someone to organize 1# programs and to write them without having to count lines for all of the forward- and backward-transfer statements.
The concept and the name come from Jon Bowman, who once took my class and felt that construction 1# programs by hand was crazy, and that counting all the 1's in a long expression "made his eyeballs bleed."
To start, run the next code cell to install the 1# Python package in your Colab environment. Then run the second code cell to import the functions from the 1# package which are used in this notebook.
!python -m pip install -U setuptools
!python -m pip install -U git+https://github.com/lmoss/onesharp.git@main
from onesharp.interpreter.interpreter import *
from onesharp.interpreter.interpreter import *
As a way to show what the tool does, we'll go through an example. Let's write a program that takes a word
w=w1w2⋯wnin R1 and reverses it.
Here is a flowchart for the program which we'll write:
At the end of the ith iteration, we'll have wi+1⋯wn in R1, and its prefix will be in R2 backwards: wi⋯w2w1.
Let's now consider what happens when we run the (i+1)-st iteration of the loop. We have cases on the first symbol of R1, and we know that this is wi+1. If that first symbol is a 1
, we write it into R3, and then we move R2 onto the end of R3
(emptying out R2) and then R3 is moved back go R2. On the assumption that R2 had wi⋯w2w1 before this iteration,
it now has wi+1wi⋯w2w1. The same thing happens if the first symbol in R1 was #
. Finally, if R1 was empty,
we would go to the bottom of the flowchart. In this case,
R2 would have wn⋯w2w1. (This is what we want; it is the original input reversed.) And the flowchart indicates that we should move R2 back into R1. So we are done.
With this in mind, have a look at the following Python list called 'reverse_idea'. It is a list of 8 arrays, and each of them is of a special form.
reverse_idea = [
['top', 'cases', 1, 'move_back', 'found_a_one', 'found_a_sharp'],
['found_a_one','111#'],
['goto', 'move_phase'],
['found_a_sharp', '111##'],
['goto', 'move_phase'],
['move_phase', move(2,3) + move(3,2)],
['goto', 'top'],
['move_back', move(2,1)]
]
We have here 8 segments. A segment is not the same what we called a line of a 1#
program. As the name suggests, a segment corresponds to a sequence of lines.
For example, segments 6 and 8 each contain move
programs that are bigger than a single instruction. Segments 2, 4 5, and 6 each begin with a label. Labels are strings that other parts of the program could point to. For example, the first segment is a case statement 1#####
, and it also contains the information that if R1 is empty, we should go to whichever segmet has the label "move_phase". (That would be the segment named 'move_stuff_around'.) The first segment also tells us that if R1 begins with 1
we should (delete is and) go to the segment containing 'first-is_one'. Note also that 'goto' is not a label.
Also, note that a segment is not quite the samething as an item in the flowchart. But this is pretty close.
The workflow in this notebook is that you will need to
(a) think about things deeply enough to make a correct flowchart
(b) draw that flowchart, either with pencil and paper or with some tool (as I did here)
(c) call a Python program called sanity
on reverse_idea
to get a 1#
program which you can then run.
# example
rev = sanity(reverse_idea)
# This run 'sanity' on 'reverse_idea', calling the result 'rev'.
# We can refer to it in the rest of this notebook by 'rev'.
# For example we can display our new program
rev
'1#####1111111111111111111111###11###111###111#111###111##1###11#####11111 1###111###111##1111####111#11111 1####111#####11111 1###111###11##1111####11#11111 1####1111111111111111111111####11#####11111 1###111###1##1111####1#11111 1####'
Now the program which we just constructed can be run, as usual:
onesharp(rev,['1####'])
'####1'
# Here is a way to write the program 'clear_1':
sanity([
['top', 'cases',1,'empty', 'one','hash'],
['empty', 'goto', 'end'],
['one','goto', 'top'],
['hash', 'goto', 'top'],
])
'1#####111###111###111###111###11111####111111####'
sanity
¶{admonition}
:class: attention
A segment is a Python list: it must be surrounded by square brackects.
A segment can be snippet of ```1#``` code surrounded by quotes.
A segment can be a Python expression like
```move_3_1 + move_2_1```
that denotes a ```1#`````` word. (These expressions must be defined before you run ```sanity```, or you will get an error.)
A segment can have ```add1``` or ```add#``` followed by a number (without quotes). This number is a register number.
A segment may be the word 'cases' followed by a number and then three labels.
A segment may optionally begin with a *label* like 'top', or 'moveback', and then it either consists of a snippet of ```1#``` code surrounded by single quotes, or a Python expression that denotes a snippet of ```1#`````` code.
{admonition}
:class: attention
A label may be a word of English, and it may have the underscore symbol, but it should not have spaces. It must be surrounded by quotes.
A label must not begin with ```1``` or ```1#```, and it must not be one of the strings 'goto', 'end', 'add1', or 'add#'.
*Labels are optional*, except a "cases" instruction
must have a number and then three labels. The number is for a register. So "cases on register 17" would correspond to
a segment with the number 17 as its third entry.
Another label which may be used is 'goto'.
A use of 'goto' must be followed either by a label or the word 'end'.
Every label used inside a 'cases' or 'goto' statement must be the first label in some segment. Otherwise, ```sanity``` will raise an error.
Here is another example
diag_idea = [
['top','cases',1,'empty', 'one','hash'],
['empty', 'goto', 'moveback'],
['one', 'add1', 2],
['111#111##'],
['goto', 'top'],
['hash', 'add#', 2],
['111#111##111##'],
['goto', 'top'],
['moveback', move(3,1)+move(2,1)]
]
diag = sanity(diag_idea)
onesharp(dg,['11#'])
'1#1#1##11#'
['top','cases',1,'empty', 'one_found','hash_found'],
['empty', 'goto', 'moveback'],
['one_found', 'add1', 2],
['111#111##'],
['goto', 'top'],
['hash_found', 'add#', 2],
['111#111##111##'],
['goto', 'end'],
['moveback', move(3,1)+move(2,1)]
# This code cell contains a Sane program which multiplies the contents of
# registers one and two and stores the product back into register one
sane_multiply = [
[move(1,4)],
['1##'],
[copy(2,5,10)],
['111##'],
[copy(3,6,10)],
[compare(2,3)],
['multiply_loop', 'cases', 2, 'empty', 'one', 'sharp'],
['empty', copy(4,7,10)],
[add(1,4,10)],
[move(7,4)],
[copy(5,2,10)],
[successor(6,10)],
[copy(6,3,10)],
[compare(2,3)],
['goto', 'multiply_loop'],
['one', 'goto', 'epilogue'],
['sharp', 'goto', 'end'], # We shouldn't reach here because cmp shold never
# write sharp into register two
['epilogue', clear(4)],
[clear(5)],
[clear(6)]
]
onesharp_multiply = sanity(sane_multiply)
onesharp(onesharp_multiply, ['11', '1#1']) # 11*1#1 = 1111 <==> 3*5 = 15
'1111'
# This code cell contains a Sane program which exponentiates the contents of
# register one to the power of the contents of register two and stores the
# result back into register one
sane_exponentiate = [
[move(1,14)],
[ones(11)+'#'],
[move(2,12)],
[copy(12,15,20)],
[ones(13)+'##'],
[copy(13,16,20)],
[compare(12,13)],
['exponentiate_loop', 'cases', 12, 'empty', 'one', 'sharp'],
['empty', move(11,1)],
[copy(14,2,20)],
[onesharp_multiply],
[move(1,11)],
[copy(15,12,20)],
[successor(16,20)],
[copy(16,13,20)],
[compare(12,13)],
['goto', 'exponentiate_loop'],
['one', 'goto', 'epilogue'],
['sharp', 'goto', 'end'], # We shouldn't reach here because cmp shold never
# write sharp into register two
['epilogue', move(11,1)],
[clear(14)],
[clear(15)],
[clear(16)]
]
onesharp_exponentiate = sanity(sane_exponentiate)
onesharp(onesharp_exponentiate, ['11', '1#1']) # 11^1#1 = 11##1111 <==> 3^5 = 243
'11##1111'
onesharp(onesharp_exponentiate, ['11', '###1']) # 11^###1 = 1####1#11##11 <==> 3^8 = 6561
# 6561 base 2 is 1100110100001
'1####1#11##11'
pre_pred = [
['top', 'cases', 1, 'first_end', 'first_one', 'first_hash'],
['first_one', 'cases', 1, 'hash_is_it', 'returnA','returnB'],
['hash_is_it', '1##'],
['goto', 'second_end'],
['returnA', '11#11#'],
[move(1,2) + move(2,1)],
['goto', 'end'],
['returnB', '11#11##'],
[move(1,2) + move(2,1)],
['goto', 'end'],
['first_hash', 'cases', 1, 'first_end', 'hash_one', 'hash_hash'],
['hash_one','11##'],
['hash_hash','1###'],
['second_end', '1111#'],
['goto', 'end'],
['first_end', '111#']
]
onesharp(sanity(pre_pred), ['#1'])
This is undefined. The register contents at the end are shown below.
contents | |
---|---|
1 | |
2 | # |
3 | |
4 | 1 |
pred = [
['top','cases', 1, 'a', 'b','c'],
['a', 'goto', 'end'],
['b', 'cases', 1, 'oe', 'oo', 'oh'],
['oe', '1##'],
['goto', 'end'],
['oo', '11#11#'+move(1,2)+move(2,1)],
['goto', 'main'],
['oh', '11#11##'+move(1,2)+move(2,1)],
['goto', 'main'],
['c', 'cases', 1, 'he', 'ho', 'hh'],
['he', '1##'],
['goto', 'end'],
['ho', '11##11#'+move(1,2)+move(2,1)],
['goto', 'main'],
['hh', '11##11##'+move(1,2)+move(2,1)],
['goto', 'main'],
['main', 'cases', 1, 'empty', 'one','hash'],
['empty', move(2,1)],
['goto', 'end'],
['one', '11##'],
[move(1,2) + move(2,1)],
['goto', 'end'],
['hash', '11#'],
['borrowing', 'cases', 1, 'borrowing_empty', 'borrowing_one', 'borrowing_hash'],
['borrowing_empty', move(2,1)],
['goto', 'end'],
['borrowing_one', '11##'],
[move(1,2) + move(2,1)],
['goto', 'end'],
['borrowing_hash', '11#'],
['goto','borrowing']
]
p1 = sanity(pred)
onesharp(p1,['1#'])
'##'
id = [ ['all_h', 'cases', 1, 'a', 'b', 'c'],
['a', '1##'+clear(2)],
['goto', 'end'],
['b', '11#'],
[move(1,2)+move(2,1)],
['goto', 'end'],
['c', '11##'],
['goto', 'all_h']
]
rectify=sanity(id)
onesharp(rectify,['##1'])
'##1'
pr = p1 + rectify
onesharp(pr,['1##'])
'#'
pr
'1#####111###111###111111111111111111111111111111111111111111###11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111###1#####111###1111###11111111111111111111###1##11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111###11#11#1#####11111 1###111###11##1111####11#11111 1####11#####11111 1###111###1##1111####1#11111 1####1111111111111111111111111111111111111111111111111111111111###11#11##1#####11111 1###111###11##1111####11#11111 1####11#####11111 1###111###1##1111####1#11111 1####11111111111111111111111111111111111111111###1#####111###1111###11111111111111111111###1##1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111###11##11#1#####11111 1###111###11##1111####11#11111 1####11#####11111 1###111###1##1111####1#11111 1####111111111111111111###11##11##1#####11111 1###111###11##1111####11#11111 1####11#####11111 1###111###1##1111####1#11111 1####1###1#####111###1111111111###1111111111111111111111111###11#####11111 1###111###1##1111####1#11111 1####111111111111111111111111111111111111111111111111###11##1#####11111 1###111###11##1111####11#11111 1####11#####11111 1###111###1##1111####1#11111 1####11111111111111111111111111111111###11#1#####111###1111111111###1111111111111111111111111###11#####11111 1###111###1##1111####1#11111 1####1111111111111111111###11##1#####11111 1###111###11##1111####11#11111 1####11#####11111 1###111###1##1111####1#11111 1####111###11#11111111111111111111111111111####1#####111###11111111###11111111111111111111111###1##11#####111###11####111####1111111111111111111###11#1#####11111 1###111###11##1111####11#11111 1####11#####11111 1###111###1##1111####1#11111 1####111###11##111111111111111111111111111####'