From Esolang
Jump to navigation Jump to search

In 96, each command consists of only one character, and each printable ASCII character is a command. Printable ASCII characters are those in the range 32-126, plus newline: that's 96 of them (this doesn't make a great name: if you have any idea, please tell me).

Commands are context insensitive: they do not take arguments, and the only factor that may modify their execution is an execution error.

96 is more tolerant than any other language, since any ASCII text is a valid program: there's no possible "syntax errors". Nonetheless, it's Turing-complete, has I/O, and is designed to be usable.

Data structure

96 uses a memory pointer, an instruction pointer, a stack of marks (used by loops to save the starting position), and an accumulator (ACC). Whenever a command returns a value, ACC is set to that value.

There's exactly 26 possible user-defined variables, labeled from a to z. Each one of these variables is an infinite array of positive or zero integers, with index starting at 0. All elements of these arrays start as undefined (otherwise it would use an infinite amount of memory). Whenever the memory pointer hovers an undefined element, this element is set to 0.

At the beginning of the execution, the memory pointer is on the first element of array a, instruction pointer is (of course) 0, the stack of marks is empty, and ACC is 0.


Modify current element:

+        increment current element
-        decrement current element
.        current element is set to 0
0...9    set current element to 10*itself+digit 
@        set current element to ACC

Note that if current element is 0, any number will set it to this number (e.g. 42 will set it to (0*10+4)*10+2=42)

Memory pointer control:

a...z    go to the first element of corresponding array
,        go to the next element of current array
'        go to the previous element of current array
#        go to element n of current array, where n is the content of current element
_        go to the first zero element of current array (it is set to 0)

(Imagine arrays as columns, with first element on the top: ' goes up and , goes down)

Control structures:

( )      if ACC=0 then *inside*
( ; )    if ACC=0 then *left side* else *right side*
[ ]      infinite loop (there's ways to exit: see next section)
{ }      undefined

Unary operators:

^        return ACC+1 (i.e. increment ACC)
|        return ACC-1 (decrement)
Space    return 0
:        return the value of current element

Binary operators: they return the result of ACC *op* current element

&        addition
=        subtraction (absolute value)
*        multiplication
/        euclidean division (quotient)
%        euclidean division (remainder)
\        inverted quotient (current element / ACC)
`        inverted remainder

Test operators: they return 0 (i.e. true) iff ACC *op* current element

=        equals (this is the same as subtraction, because |a-b|=0 iff a=b)
<        is lower than (strict, returns 1 if false)
>        is greater than (idem)


?        input text: if only digits were input, return the numeral they represent, else, fill current array with ASCII representation of input characters. 
"        output ASCII characters signified by numbers in current array
$        output ACC as a numeral, followed by a space

With ? and ", End of String is encoded by a zero element (NULL character. If user enters n characters, they will fill elements [0..n-1], element n will be set to 0, and elements after n are not affected.

? deals with any input starting with 0 as non-numerical (this is to tell apart 0123 from 123).

Advanced commands:

~        switches the values of current element and ACC
!        execute the command corresponding to the character signified by ACC
A-Z      define a function, or call it

To define functions, put this at the beginning of the program:


Commands inside the definitions will be skipped in the first time. Then, any use of a capital letter will call the corresponding function, executing the commands up to the new line, and then resume the execution after the function call. Functions may call each other, or even themselves.

Alternative syntaxes

Control structures and functions do what they are meant to do when used with the correct syntax. However, remember that each character composing the control structure is a command in itself. Using any of these commands unmatched or crossing (e.g. [ (])) is not a syntax error. It may have strange effects, but if used carefully, it has predictable and desired effects.

Loops are based on the mark stack:

[        sets a mark right after itself
]        instruction pointer goes to the last mark set

If no mark is set, ] has no effect.

If/else structures are based on error-management:

(        causes an error if ACC is non-zero
;        causes an error
)        nothing (see below)

When an error occurs, parenthesis count (PC) is set to 0, then instructions are skipped, except:

(        increment PC
;        if PC=0, resume execution
)        if PC=0, resume execution ; else decrement PC
]        remove the last mark set

In a (  ; ) structure: if ACC is 0, ( does nothing, left side is executed, then ; causes an error, right side is skipped and ) resumes execution. If ACC is non-0, ( causes an error, left-side is skipped, ; resumes execution, right side is executed, and ) does nothing. Parenthesis count allows nested if/else structures to work properly.

If an error occurs for another reason, it is dealt with in the same way. Other error reasons are:

/        (if current cell is zero)
%        (idem)
-        (idem)
\        (if ACC is zero)
`        (idem)
|        (idem)
'        (if memory pointer is on the first element of an array)
.        (idem)

An error may be used to exit infinite loops (actually, this is the only way). The modified ] command allows nested loops to work properly.

Functions are based on the mark stack, just as loops !

A-Z      set a mark immediately after this letter, then go to the first occurrence of this letter in the program
newline  instruction pointer goes to the last mark set, which is removed

These are the same kind of marks as those used by [ ]. However, newline won't allow loops, since they remove the mark. Just as with ], if no mark is set, newline has no effect.

The stack will handle correctly multiple function call, unless there is unmatched [ or ] in one of the functions.

And now, some examples of outlandish uses of these commands:

newline         not significant if no mark is set: phew, you can format your program
[  (])          repeat *inside* until ACC is non-0
[-+  ];         while current element is not 0, do *inside*
_[.];           clears an array (elements after the first that was already undefined are not affected) 
]               used inside a function, exits the function and resume the execution from the function call (this won't remove the mark set by the function call)
;];             remove the last mark set
[  newline       if no other marks are set, *inside* is executed exactly twice
[[...[  newline  same, but *inside* is repeated 2^n times, where n is the number of [
;    ;           skip *inside*: used for function definition and comments

Warning: if you want to use a capital letter as a function, do not use it in a comment before you defined the function. Do not use unmatched parenthesis, or semicolons that are not inside parenthesis, in comments or function. ] is also a problem if the mark stack is not empty.

The infinitely many ways to use improper syntax is part of the stylishness of this language.

Some programs

Cat program:

; [?"] ;
[?(";$ )]

Remember that if user inputs a numeral, ? returns it instead of modifying the array. In the first version, this will cause " to output again the previous entry. In the second version, this will trigger the "else" part: $ will output the numeral correctly, and space will reset ACC to 0.

Hello world:

;H   e   l   l   o  , " "  w   o   r   l   d  !;


44 ,,58 ,36 ,34 ,44 ,91 ,91 ,91 ,91 ,91 ,34 ,44 ,58 ,36 ,10 ,95 ,57 ,55 ,34 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,a:$",[[[[[",:$

Funny infinite loop:


! will execute the command corresponding to the character signified by 33. This command is ! ...

Fibonacci sequence:


At the beginning of the n'th iteration of the loop, a contains the n'th element and ACC contains the (n-1)'th element (0'th element is 0). ACC is set to a+ACC, i.e. the (n+1)'th element, then ACC and a are switched so that ACC still contains the smaller element.

Powers of 2:

;2>[$*] works as well;

Input n, powers of n:


If user doesn't enter a numeral (or enters 0 or a blank entry), this will output 1 0 0 0 ... You could use [?(]) instead of ? to ask a non-zero value to the user.

Input n, factorial n:


After ?+~, ACC holds 1 and pointed cell holds the input. [-+...]; loops until pointed element is zero. At each iteration, ACC is multiplied by pointed element, which is decremented.

Prime numbers:


Inner loop outputs n iff it has no divisors between 2 and it's square root, i.e. iff it is prime (square root of n isn't actually computed: instead, the condition (n>d*d) is checked at each iteration). The main loop increments n by 1 at each iteration. Thus any prime number will eventually be output, given enough time.

Prime numbers (saved in a list):


First element of p is used as an index. Further elements are ascending prime numbers. Inner loop outputs n iff it has no prime divisor between 2 and it's square root. This is enough to make sure n is prime, and needs less divisions than in the first algorithm. However, this one needs not only infinite time but also infinite space in order to output all prime numbers.

Brainfuck equivalences

Brainfuck  96
+	   +
-	   -
>	   ,
<	   '
[	   [-+
]	   ];

This proves the Turing-completeness of 96. There's no strict equivalence for I/O, but this doesn't affect computational class. Unlike Brainfuck, 96 has no syntax error: this may lead to undefined behavior instead of a crash, but a working program will work the same way.


User:Marinus wrote an interpreter: it is here. It is under testing.