View Source Document

oozlybub-and-murphy.markdown

Oozlybub and Murphy

Language version 1.1

Overview

This document describes a new programming language. The name of this language is Oozlybub and Murphy. Despite appearances, this name refers to a single language. The majority of the language is named Oozlybub. The fact that the language is not entirely named Oozlybub is named Murphy. Deal with it.

For the sake of providing an "olde tyme esoterickal de-sign", the language combines several unusual features, including multiple interleaved parse streams, infinitely long variable names, gratuitously strong typing, and only-conjectural Turing completeness. While no implementation of the language exists as of this writing, it is thought to be sufficiently consistent to be implementable, modulo any errors in this docunemt.

In places the language may resemble SMITH and Quylthulg, but this was not intended, and the similarities are purely emergent.

Program Structure

A Oozlybub and Murphy program consists of a number of variables and a number of objects called dynasts. A Oozlybub and Murphy program text consists of multiple parse streams. Each parse stream contains zero or more variable declarations, and optionally a single dynast.

Parse Streams

A parse stream is just a segment, possibly non-contiguous, of the text of a Oozlybub and Murphy program. A program starts out with a single parse stream, but certain parse stream manipulation pragmas can change this. These pragmas have the form {@x} and have a similar syntactic status as comments; they can appear anywhere except inside a lexeme.

Parse streams are arranged as a ring (a cyclic doubly linked list.) When parsing of the program text begins initially, there is already a single pre-created parse stream. When the program text ends, all parse streams which may be active are deleted.

The meanings of the pragmas are:

Deleting a parse stream while it contains an unfinished syntactic construct is a syntax error, just as an end-of-file in that circumstance would be in most other languages.

Providing a concrete example of parse streams in action will be difficult in the absence of defined syntax for the rest of Oozlybub and Murphy, so we will, for the purposes of the following demonstration only, pretend that the contents of a parse stream is a sentence of English. Here is how three parse streams might be managed:

The quick {@+}brown{@>}Now is the time{@<}fox{@<} for all good men to {@+}{@>}Wherefore art thou {@>} jumped over {@>}{@>}Romeo?{@-} come to the aid of {@>}the lazy dog's tail.{@-}their country.{@-}

Variables

All variables are declared in a block at the beginning of a parse stream. If there is also a dynast in that stream, the variables are private to that dynast; otherwise they are global and shared by all dynasts. (Defined in 1.1) Any dynamically created dynast gets its own private copies of any private variables the original dynast had; they will initially hold the values they had in the original, but they are not shared.

The name of a variable in Oozlybub and Murphy is not a fixed, finite-length string of symbols, as you would find in other programming languages. No sir! In Oozlybub and Murphy, each variable is named by a possibly-infinite set of strings (over the alphanumeric-plus-spaces alphabet [a-zA-Z0-9 ]), at least one of which must be infinitely long. (New in 1.1: spaces [but no other kinds of whitespace] are allowed in these strings.)

To accomodate this method of identifying a variable, in Oozlybub and Murphy programs, which are finite, variables are identified using regular expressions which match their set of names. An equivalence class of regular expressions is a set of all regular expressions which accept exactly the same set of strings; each equivalence class of regular expressions refers to the same, unique Oozlybub and Murphy variable.

(In case you wonder about the implementability of this: Checking that two regular expressions are equivalent is decidable: we convert them both to NFAs, then to DFAs, then minimize those DFAs, then check if the transition graphs of those DFAs are isomorphic. Checking that the regular expression accepts at least one infinitely-long string is also decidable: just look for a cycle in the DFA's graph.)

Note that these identifier-sets need not be disjoint. /ma*/ and /mb*/ are distinct variables, even though both contain the string m. (Note also that we are fudging slightly on how we consider to have described an infinitely long name; technically we would want to have a Büchi automaton that specifies an unending repetition with ^ω^ instead of *. But the distinction is subtle enough in this context that we're gonna let it slide.)

Syntax for giving a variable name is fairly straightforward: it is delimited on either side by / symbols; the alphanumeric symbols are literals; textual concatenation is regular expression sequencing, | is alteration, ( and ) increase precedence, and * is Kleene asteration (zero or more occurrences). Asteration has higher precedence than sequencing, which has higher precedence than alteration. Because none of these operators is alphanumeric nor a space, no escaping scheme needs to be installed.

Variables are declared with the following syntax (i and a are the types of the variables, described in the next section):

VARIABLES ARE i /pp*/, i /qq*/, a /(0|1)*/.

This declares an integer variable identified by the names {p, pp, ppp, ...}, an integer variable identified by the names {q, qq, qqq, ...}, and an array variable identified by the names of all strings of 0's and 1's.

When not in wimpmode (see below), any regular expression which denotes a variable may not be literally repeated anywhere else in the program. So in the above example, it would not be legal to refer to /pp*/ further down in the program; an equivalent regular expression, such as /p|ppp*/ or /p*p/ or /pp*|pp*|pp*/ would have to be used instead.

Types

Oozlybub and Murphy is a statically-typed language, in that variables as well as values have types, and a value of one type cannot be stored in a variable of another type. The types of values, however, are not entirely disjoint, as we will see, and special considerations may arise for checking and conversion because of this.

The basic types are:

Wimpmode

(New in 1.1) An Oozlybub and Murphy program is in wimpmode if it declares a global variable of integer type which matches the string am a wimp, for example:

VARIABLES ARE i /am *a *wimp/.

Certain language constructs, noted in this document as such, are only permissible in wimpmode. If they are used in a program in which wimpmode is not in effect, a compile-time error shall occur and the program shall not be executed.

Dynasts

Each dynast is labeled with a positive integer and contains an expression. Only one dynast may be denoted in any given parse stream, but dynasts may also be created dynamically during program execution.

Program execution begins at the lowest-numbered dynast that exists in the initial program. When a dynast is executed, the expression of that dynast is evaluated for its side-effects. If there is a dynast labelled with the next higher integer (i.e. the successor of the label of the current dynast), execution continues with that dynast; otherwise, the program halts. Once a dynast has been executed, it continues to exist until the program halts, but it may never be executed again.

Evaluation of an expression may have side-effects, including writing characters to an output channel, reading characters from an input channel, altering the value of a variable, and creating a new dynast.

Dynasts are written with the syntax dynast(label) <-> expr. A concrete example follows:

dynast(100) <-> for each prime /p*/ below 1000 do write (./p*|p/+1.)

TRIVIA PORTION OF SHOW

WHO WAS IT FAMOUS MAN THAT SAID THIS?

contestant enters lightning round now

Expressions

In the following, the letter preceding -expr or -var indicates the expected type, if any, of that expression or variable. Where the expressions listed below are infix expressions, they are listed from highest to lowest precedence. Unless noted otherwise, subexpressions are evaluated left to right.

Grammar

This section attempts to capture and summarize the syntax rules (for a single parse stream) described above, using an EBNF-like syntax extended with a few ad-hoc annotations that I don't feel like explaining right now.

ParseStream  ::= VarDeclBlock {DynastLit}.
VarDeclBlock ::= "VARIABLES ARE" VarDecl {"," VarDecl} ".".
VarDecl      ::= TypeSpec VarName.
TypeSpec     ::= "i" | "p" | "a" | "b" | "t" | "z" | "c".
VarName      ::= "/" Pattern "/".
Pattern      ::= {[a-zA-Z0-9 ]}
               | Pattern "|" Pattern                    /* ignoring precedence here */
               | Pattern "*"                            /* and here */
               | "(" Pattern ")".
DynastLit    ::= "dynast" "(" Gumber ")" "<->" Expr.
Expr         ::= Expr1[c] {"then" Expr1 | ",then" Expr1[i]}.
Expr1        ::= Expr2[c] {"or" Expr2[c]}.
Expr2        ::= Expr3[b] {"and" Expr3[b]}.
Expr3        ::= Expr4[i] {"+" Expr4[i]}.
Expr4        ::= Expr5[i] {"*" Expr5[i]}.
Expr5        ::= Expr6[a] {"?" Expr6[i]}.
Expr6        ::= Prim[a] {"[" Expr[i] "]"} [":=" Expr[i]].
Prim         ::= {"("}* "." Expr "." {")"}*             /* remember the Fibonacci rule! */
               | VarName [":=" Expr]
               | Gumber
               | "#myself#"
               | "minus" Expr[i]
               | "write" Expr[i]
               | "#read#"
               | "not?" Expr[z]
               | "if?" Expr[b]
               | "cvt?" Expr[c]
               | "to?" Expr[t]
               | "P?" Expr[i]
               | "exists/dynast" Expr[i]
               | "copy/dynast" Expr[i] "," Expr[p] "," Expr[p]
               | "create/countably/many/dynasts"
                   Expr[i] "," Expr[i]
               | "do" Expr
               | "for" "each" "prime" VarName "below"
                   Expr[i] "do" Expr[i].
Gumber       ::= {[0-9]}.

Boolean Idioms

Here we show how we can get any value of any of the b, t, z, and c types, without any constants or variables with known values of these types.

VARIABLES ARE b /b*/.
zero = /b*|b/ and not? to? cvt? if? /b*|b*/
true = not? zero
go = if? true
yes = cvt? go
one = to? yes
false = not? one
nogo = if? false
no = cvt? nogo

Computational Class

Because the single in-dynast looping construct, for each prime below, is always a finite loop, the execution of any fixed number of dynasts cannot be Turing-complete. We must create new dynasts at runtime, and continue execution in them, if we want any chance at being Turing-complete. We demonstrate this by showing an example of a (conjecturally) infinite loop in Oozlybub and Murphy, an idiom which will doubtless come in handy in real programs.

VARIABLES ARE p /p*/, p /q*/.
dynast(3) <->
  (. do (. if? not? exists/dynast 5 ,then
       create/countably/many/dynasts #myself#, 5 .) .) ,then
  (. for each prime /p*|p/ below #myself#+2 do
       for each prime /q*|q/ below /p*|pp/+1 do
         if? not? exists/dynast /p*|p|p/+/q*|q|q/ ,then
           copy/dynast #myself#, /p*|ppp/, /q*|qqq/ .)

As you can see, the ability to loop indefinitely in Oozlybub and Murphy hinges on whether Goldbach's Conjecture is true or not. Looping forever requires creating an unbounded number of new dynasts. We can create all the odd-numbered dynasts at once, but that won't be enough to loop forever, as we must proceed to the next highest numbered dynast after executing a dynast. So we must create new dynasts with successively higher even integer labels, and these can only be created by summing two primes. So, if Goldbach's conjecture is false, then there is some even number greater than two which is not the sum of two primes; thus there is some dynast that cannot be created by a running Oozlybub and Murphy program, thus it is not possible to loop forever in Oozlybub and Murphy, thus Oozlybub and Murphy is not Turing-complete (because it cannot simulate any Turing machine that loops forever.)

It should not however be difficult to show that Oozlybub and Murphy is Turing-complete under the assumption that Goldbach's Conjecture is true. If Goldbach's Conjecture is true, then the above program is an infinite loop. We need only add to it appropriate conditional instructions to, say, simulate the execution of an arbitrarily-chosen Turing machine. An array can serve as the tape, and an integer can serve as the head. Another integer can serve as the state of the finite control. The integer can be tested against various fixed integers by establishing an array for each of these fixed integers and using the ? operator against each in turn; each branch can mutate the tape, tape head, and finite control as desired. The program can halt by neglecting to create a new even dynast to execute next, or by trying to create a dynast with a label that already exists.

Happy FLIMPING,
Chris Pressey
December 1, 2010
Evanston, Illinois, USA