Open In Colab

Tools to help write programs

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 onesharp
from onesharp.tools.sanity import sanity
from onesharp.tools.ones import ones
from onesharp.programs.clear import clear
from onesharp.programs.move import move
from onesharp.programs.copy import copy
from onesharp.programs.successor import successor
from onesharp.programs.compare import compare
from onesharp.programs.add import add

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 = w_1 w_2 \cdots w_n \]

in R1 and reverses it.
Here is a flowchart for the program which we’ll write: Our program will work as follows. It processes the letters in \(w\) in order, using a loop that involves cases on R1. At the end of the \(i\)th pass through the loop, we’ll have \(w_{i+1}\cdots w_n\) in R1, and its prefix \(w_{i}\cdots w_2 w_1\) will be in R2, but it will appear there reversed. Thus, after \(n\) passes through the loop, the desired reversal will be in R2. This is how the flowchart ends.

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 \(w_{i+1}\). If that first symbol is a \(\one\), we write the same symbol \(\one\) 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 \(w_{i}\cdots w_2 w_1\) before this iteration, it now has \(w_{i+1} w_{i}\cdots w_2 w_1\). The same thing happens if the first symbol in R1 was \(\hash\). Finally, if R1 was empty, we would go to the bottom of the flowchart. In this case, R2 would have \(w_{n}\cdots w_2 w_1\). (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', 'one_found', 'hash_found'],
    ['one_found','111#'],
    ['goto', 'move_phase'],
    ['hash_found',  '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 \(\one\hash\) 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 \(\one\hash^5\), 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 \(\one\) 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.

Workflow

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) make an “idea” for the desired program using the flowchart, an “idea” with special requirements that we discuss below

(d) call a Python program called sanity on the “idea” to get a \(\one\hash\) 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####'

The inputs to sanity

In order to use the tool, we have clarify what we mean by segments and labels.

What may go into a segment?

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 \(\one\hash\) code surrounded by single quotes, or a Python expression that denotes a snippet of \(\one\hash\) code

labels

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 ‘#, 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, another derivation of the “diag” program which we have seen earlier.

dg_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)] 
]
dg = sanity(diag_idea)
onesharp(dg,['11#'])
'1#1#1##11#'

Examples of segments:

['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)]

How does Sanity work?

We aren’t going to discuss this at length (now), but you can read the Python code for it by examining the input box at the top. Eventually this code will be documented well-enough so that it can be read. The overall idea is to work with parsed programs, the Python lists that we obtained with the function parse that we saw earlier. The program sanity consists of manipulations of parses, followed by an application of unparse which returns us to a \(\one\hash\) program.

Further programs and exercises

# 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

Here is a derivation of a progam that takes the predecessor of a number written in backwards binary (bb) notation.

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#'])
'##'
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####'