Pigeon Computer 0.1 documentation

Pigeon User Interface

«  Pigeon Firmware   ::   Contents   ::   Appendix A: ATmega328P Definitions  »

Pigeon User Interface

The Pigeon User Interface is a simple but powerful way to interact with your computer. It’s a general system but I’ve customized it here for experimenting and playing with the Pigeon software and hardware.

It consists of a Text window that displays editable text, and a Stack of data items with a Dictionary of named functions that operate on the Stack. In this way it’s very much like the Pigeon Firmware (TODO: linkify) and in fact many simple commands that work on the firmware will also work identically on the PUI (Pigeon User Interface) but the PUI is much more powerful.

For one thing, it never forgets.

The interface stores the current state of the Stack, Dictionary and Text after each command and whenever you change the text and pause for more than two seconds.

You never have to save your work, and you can always go back to any previous place in your history and access it or “restart” from it. And, unlike most web browsers and undo/redo functions, if you go back in history and try a different path the previous path you took is also still remembered, not forgotten, and can be re-accessed or re-visited at any time.

The system never forgets, so you never have to remember to hit “save” and you never have to worry about losing your work. You can’t anymore, the computer protects you.

Stack

Tuple-based persistent stack.

This is too simple to document. It’s almost too simple to implement.

pigeon.xerblin.stack.iterStack(stack)[source]

Iterate through the items on the stack.

pigeon.xerblin.stack.lenStack(stack)[source]

Return the number of items on the stack.

pigeon.xerblin.stack.pick_(stack, n)[source]

Find the nth item on the stack and duplicate it to TOS. (Pick with zero is the same as “dup”.)

pigeon.xerblin.stack.pop(stack, number)[source]

Pop number arguments from stack.

pigeon.xerblin.stack.push(stack, *items)[source]

Push arguments onto a stack.

BTree

This is a simple Binary Tree implementation that uses tuples in such a way as to permit “persistant” usage, i.e. all previous versions of the btree datastructures are retained and available (provided you don’t throw them away yourself.)

The empty tree is represented as an empty tuple. Nodes are a tuple consisting of a key, a value, and two (possible empty) sub-nodes for the lower and higher branches of the tree.

This module defines the following functions:

insert(node, key, value)

get(node, key)

delete(node, key)

Both insert() and delete() return a new tuple that is the result of applying the operation to the existing node. (And both get() and delete() will raise KeyErrors if the key is not in the tree.)

Because of the way that insert() and delete() are written, only as much of the tree is changed as necessary and the rest of it is reused. This provides persistance without using up memory for each version of the tree.

These functions are implemented recursively so they have the potential to raise a RuntimeError if the maximum recursion depth is exceeded. This should only be a problem if used with very large trees. To avoid this issue you can use sys.setrecursionlimit(), but I think I might just rewrite these to not use recursion.

pigeon.xerblin.btree.delete(node, key)[source]

Return a tree with the value (and key) removed or raise KeyError if not found.

pigeon.xerblin.btree.fill_tree(node, items)[source]

Add the (key, value) pairs in items to a btree in a balanced way.

You can balance a tree like so:

tree = fill_tree((), items(tree))

This iterates through the tree and returns a new, balanced tree from its contents.

pigeon.xerblin.btree.get(node, key)[source]

Return the value stored under key or raise KeyError if not found.

pigeon.xerblin.btree.insert(node, key, value)[source]

Return a tree with value stored under key. Replaces old value if any.

pigeon.xerblin.btree.items(node)[source]

Iterate in order over the (key, value) pairs in a tree.

Xerblin Base

This module builds a Xerblin system out of purely “Functional” parts.

An interpreter is represented by a two-tuple that holds a stack and a dictionary.

The stack is a sort of linked list structure while the dictionary is a BTree that maps string names to four kinds of entries:

Functions - These must accept an interpreter, modify it somehow, and return the modified interpreter. They are defined in the library module.

Or, one of three kinds of tuple. The kind of the tuple is indicated by its first member, which is a handler function, and the rest of the tuple consists in its body as indicated:

(SEQUENCE HANDLER, func0, func1, func2)

(BRANCH HANDLER, true_func, false_func)

(LOOP HANDLER, func0, func1, func2)

where any of the functions can be themselves SEQ, BRANCH, LOOP, or plain functions as described above.

Interpretation is done by means of an apply_func(interpreter, function) function that knows how to deal with the above combo-word tuples as well as library word functions.

The first three functions defined in this module are used to build “combo” commands in the UI. There are corresponding commands in the library (NewSeqWord, NewLoopWord, and NewBranchWord) that build tuples with these functions as the first item in the tuple. When apply_func() encounters these tuples the initial handler function is used to “run” the other functions in the tuple.

The three functions are:

handle_sequence

handle_branch

handle_loop

The apply_func() function is used by the handlers and the interpret() function to “run” commands on the interpreter.

Code Documentation

pigeon.xerblin.base.apply_func(I, func)[source]

Given an interpreter and a function or combo-word tuple, apply the function or combo to the interpreter and return the modified interpreter.

pigeon.xerblin.base.handle_branch(I, branch)[source]

Check TOS and do one thing or another depending.

pigeon.xerblin.base.handle_loop(I, loop)[source]

Check TOS and do body if it’s true, repeat.

pigeon.xerblin.base.handle_sequence(I, seq)[source]

Run a sequence and return the modified interpreter.

pigeon.xerblin.base.interpret(I, command)[source]

Given an interpreter and a string command, interpret that string on the interpreter and return the modified interpreter.

World

This module defines two classes: Serializer and World. Serializer deals with saving history protocols to a pickle file and World deals with interacting with an interpreter in a loop.

This module also defines a subclass of World called HistoryListWorld that tracks history in a list object, and a couple of very basic views.

Starting with an initial interpreter (comprised of a stack of nominal data and a dictionary of commands and possibly named state “variables”) the user issues a command to the interpreter which then results in a new state (which can be the same as the initial state.)

The interpreter is embedded in a frame (a World or subclass) which provides view and history and serialization controls.

These frame “meta” controls are several:

Change the view function.

View functions take a state (interpreter, stack and dictionary) and render it somehow for the user.

Implied but not necessary are means for direct manipulation of views to send commands to the interpreter. It may well turn out that tight feedback loops between views and modeled states will require a relaxation of the strict functional style I’m striving for here.

In any event, views take a state and render it.

Views may render as text in a terminal or GUI text widget, “direct” graphics via Pygame, etc., GUI systems such as Tkinter or wxPython, document formats like Postscript or PDF, graphs using matplotlib or other libraries in formats such as PNG or SVG, or web UIs using HTML, CSS, Javascript, and perhaps even ActionScript (Flash).

Examine history and change to previous states.

The chief advantage of storing all history is in the ease of going back and trying something new from a previous point in time.

This requires some means of indexing and displaying the states in your history and your path through them so you can decide when to go back to, and a way to actually go back and set your current state to a previous state and add the transition to the history.

This seems to require another kind of view that can display not just one state time-slice of your history, but also the tree-like path from state to state you took.

(There is an infinite regression here: do you keep the history of the changing of the history? What about the history of the keeping of that history, do you keep that too? And that keeping’s history? Etc... In practice we are likely to make do with only one level of history.)

In addition to simply stepping back and forth in time, we will also want to “pull” data from different history states, possibly in multiple stored pickle files, and combine them in a new synthesized states. We can build new interpreters with the data and commands we need to perform tasks and achieve our goals.

As yet, this script provides only indirect means to manipulate and view history. You can use a HistoryListWorld and manipulate its history attribute, which is a list containing all the states of the system in order starting with the initial state.

Manipulate external save files.

The histories must be stored in python pickle files in some external media (i.e. a disk drive or memory stick) and there are issues of selecting and loading histories and managing the relations between them.

Currently this script provides no direct means to do any of this.

You can pass a file name or open file object to the World as its save_file argument and that will be used to save the pickle data as you use the World object, but other than that you’re on your own.

Code Documentation

class pigeon.xerblin.world.HistoryListWorld(view=<function nullView at 0x2ff0410>, initial=((), ('mul', <function mul at 0x2ff00c8>, ('drop', <function drop at 0x2fe8c08>, ('NewSeqWord', <function NewSeqWord at 0x2fe8de8>, ('NewLoopWord', <function NewLoopWord at 0x2fe8e60>, ('NewBranchWord', <function NewBranchWord at 0x2fe8ed8>, (), ()), ()), ('add', <function add at 0x2fe8f50>, (), ())), ('listwords', <function listwords at 0x2ff0320>, ('inscribe', <function inscribe at 0x2fe8d70>, ('dup', <function dup at 0x2fe8a28>, (), ()), ()), ('lookup', <function lookup at 0x2fe8cf8>, (), ()))), ('sub', <function sub at 0x2ff0050>, ('pickle', <function pickle at 0x2ff0140>, ('pick', <function pick at 0x2fe8b18>, ('over', <function over at 0x2fe8c80>, (), ()), ()), ('rebalance', <function rebalance at 0x2ff0230>, (), ())), ('unpickle', <function unpickle at 0x2ff01b8>, ('tuck', <function tuck at 0x2fe8b90>, ('swap', <function swap at 0x2fe8aa0>, (), ()), ()), ('view', <function view at 0x2ff02a8>, (), ()))))), save_file=None)[source]

Bases: pigeon.xerblin.world.World

A subclass of World that overrides the setCurrentState() and getCurrentState() methods to record states in a history list.

getCurrentState()[source]

Return the current state. Overrides super-class.

setCurrentState(state)[source]

Set current state to passed in state. Overrides super-class.

class pigeon.xerblin.world.Serializer(initial_state, stream)[source]

Combines a Pickler and a file or file-like object to track a linear protocol of state -> command -> resultant-state history.

You instantiate it with an initial state (i.e. your “root” stack and dictionary) and a file(-like object) to save to then you call post() repeatedly with the next command and the resultant stack and dictionary.

The Pickler keeps a memo dict of the objects it has seen and pickled already and when it sees them again it serializes a reference to them rather than the whole object, automatically providing a sort of compression for the persistant datastructures we’re storing in the pickle stream.

You can open the pickle file and call load() repeatedly to get a sequence of state, command, state, ... There will always be an odd number of stored data: the initial state followed by zero or more pairs of command and result state.

post(command, resultant_state)[source]

Serialize command and resultant_state to the pickle stream. Each is saved separately so you can load a command from the stream and examine it without loading its resultant state.

class pigeon.xerblin.world.World(view=<function nullView at 0x2ff0410>, initial=((), ('mul', <function mul at 0x2ff00c8>, ('drop', <function drop at 0x2fe8c08>, ('NewSeqWord', <function NewSeqWord at 0x2fe8de8>, ('NewLoopWord', <function NewLoopWord at 0x2fe8e60>, ('NewBranchWord', <function NewBranchWord at 0x2fe8ed8>, (), ()), ()), ('add', <function add at 0x2fe8f50>, (), ())), ('listwords', <function listwords at 0x2ff0320>, ('inscribe', <function inscribe at 0x2fe8d70>, ('dup', <function dup at 0x2fe8a28>, (), ()), ()), ('lookup', <function lookup at 0x2fe8cf8>, (), ()))), ('sub', <function sub at 0x2ff0050>, ('pickle', <function pickle at 0x2ff0140>, ('pick', <function pick at 0x2fe8b18>, ('over', <function over at 0x2fe8c80>, (), ()), ()), ('rebalance', <function rebalance at 0x2ff0230>, (), ())), ('unpickle', <function unpickle at 0x2ff01b8>, ('tuck', <function tuck at 0x2fe8b90>, ('swap', <function swap at 0x2fe8aa0>, (), ()), ()), ('view', <function view at 0x2ff02a8>, (), ()))))), save_file=None)[source]

Manage an interpreter, a view function, and serialization to a file or file-like object.

This object takes a view function (any callable that accepts an interpreter) and optionally an initial interpreter and a save file. It creates a Serializer object to save commands and results, and it provides a step() method that accepts a command (list of strings) and runs it on the interpreter then saves it and calls the view function.

changeView(view)[source]

Swap the current view function for the one passed, return the old view function. Calls the new view.

getCurrentState()[source]

Return the current state. This method exists mostly to be overridden in subclasses.

setCurrentState(state)[source]

Sets current state to the passed state. This method exists mostly to be overridden in subclasses.

step(command)[source]

Run one command, a list of strings, on the interpreter then save the command and resulting new interpreter to the serialized stream and call the view function on the new interpreter.

view(state)[source]
pigeon.xerblin.world.nullView(I)[source]

“Do nothing” view.

pigeon.xerblin.world.view0(I)[source]

Print the stack to stdout with python’s default formatting.

pigeon.xerblin.world.view1(I)[source]

Print the stack to stdout using the pprint module.

Library of Words

pigeon.xerblin.library.NewBranchWord((stack, dictionary))[source]

Create a new Branch command word. A branch consumes the top item on the stack and does one of two things depending on its “truth” value. Unlike Loops and Sequences which use all the items on the stack, Branch commands only take the top two items. The item on the top of the stack should be a function to use in case of “true” and the second should be a function to use for “false”.

pigeon.xerblin.library.NewLoopWord((stack, dictionary))[source]

This command takes all the items on the stack and puts them into a tuple with the Loop Handler function in front of them.

A Loop consumes the top item on the stack, then depending on it’s “truth” either runs the commands in its tuple and repeats if it’s true or stops looping altogether if it’s false.

Put the first command to run on the stack first, then the second, and so on, so that the last item to run is on the top of the stack when you run this command.

pigeon.xerblin.library.NewSeqWord((stack, dictionary))[source]

This command takes all the items on the stack and puts them into a tuple with the Sequence Handler function in front of them.

The items on the stack should all be commands from the dictionary, either functions or combo commands. You get these by using the “lookup” command on the names of the functions you want.

Put the first command to run on the stack first, then the second, and so on, so that the last item to run is on the top of the stack when you run this command.

pigeon.xerblin.library.add((stack, dictionary))[source]

Add the top two items on the stack and replace them with the sum.

pigeon.xerblin.library.drop((stack, dictionary))[source]

Remove the item on the top of the stack and discard it.

pigeon.xerblin.library.dup((stack, dictionary))[source]

“Duplicate” the top item on the stack.

pigeon.xerblin.library.function(interpreter)

Pretty print the interpreter to stdout.

pigeon.xerblin.library.inscribe((stack, dictionary))[source]

Given a name string on the top of the stack and a “combo” command underneath it (see NewSeqWord, NewLoopWord, and NewBranchWord for how to make combo commands) “inscribe” the combo command into the dictionary under that name, replacing any previous command of that name.

pigeon.xerblin.library.listwords(interpreter)[source]

Print the list of words in the dictionary to stdout.

pigeon.xerblin.library.lookup(interpreter)[source]

Given a name on the top of the stack, look up the named command in the dictionary and put it on the stack in place of the name.

pigeon.xerblin.library.mul((stack, dictionary))[source]

Replace the top two items on the stack with the result of multiplying them together.

pigeon.xerblin.library.over((stack, dictionary))[source]

Put a “duplicate” of the second item down in the stack on the top of the stack. (I.e. second, top, second.)

pigeon.xerblin.library.pick((stack, dictionary))[source]

Takes a number from the stack, counts back that many items (starting from zero for the top item) and puts a “duplicate” of the item found on the top of the stack. (So pick with 0 on the stack is thte same as the command “dup”.)

pigeon.xerblin.library.pickle(interpreter)[source]

Convert the current interpreter to a portable text format (called a “pickle”.)

pigeon.xerblin.library.rebalance((stack, dictionary))[source]

This “rebalances” a dictionary. It makes it more efficient to access commands in the dictionary if you’ve added a lot of new ones.

It’s a good idea to use this command before creating a pickle to save so that the saved pickle’s dictionary is already balanced.

pigeon.xerblin.library.sub((stack, dictionary))[source]

Replace the top two items on the stack with the result of subtracting the top item from the second item.

pigeon.xerblin.library.swap((stack, dictionary))[source]

Reverse the order of the top two items on the stack.

pigeon.xerblin.library.tuck((stack, dictionary))[source]

Put a “duplicate” of the item on the top of the stack just under the second item on the stack. (I.e. top, second, top.)

pigeon.xerblin.library.unpickle(interpreter)[source]

Take a string “pickle” portable text representation of an interpreter and replace the current interpreter with it.

pigeon.xerblin.library.view(interpreter)[source]

Pretty print the interpreter to stdout.

«  Pigeon Firmware   ::   Contents   ::   Appendix A: ATmega328P Definitions  »