PingPong Reference

General

PingPong interprets programs written in postfix notation. All instructions (see Instruction Reference) are the same size of one character and operate on a stack. All characters which are not instructions are pushed to this stack after they are converted to the PingPong Character Code. PingPong is a two dimensional language (see Control Flow) which operates on a matrix (called PingPong-Space) containing code and data. Code and data are treated equivalently which makes self modifying code possible. The matrix contains 32-bit signed integer cells and the stack contains 64-bit signed integer cells. All cells can be interpreted as characters or as numbers depending on the operation.

How Source Code is loaded into PingPong-Space

Initially the PingPong-Space is filled with character code PPCCT #226 (ASCII #160), indicating that there is no program (noP). The interpreter reads the source file, converts all characters to the PingPong Character Code and writes them into the space starting at position (0,0). Each character fills one cell of the PingPong-Space (also the Tab) and all cells which are out of the bounds of the source file remain unchanged. The character ASCII #160 (noP) in the source file indicates that the PingPong-Space should not be altered at this position. This makes no difference here, but there are API functions to load additional files into the space at execution time. Here, the character ASCII #160 in the loaded file leaves the original data untouched, it's a kind of mask.

The loading mechanism described above is called "overloading" (or "overriding" in terms of OOP). There is a second type of loading files called "underloading" (or "underriding") described in the API-Reference.

It is assumed that the source code is ISO-8859-1 encoded.

Control Flow

The execution of a program starts at position (0,0), corresponding to the first character in the first line of the source code. The direction of execution is initially set to "left-to-right". The code is executed sequentially in the current direction.

Reflecting instruction ("|", "_", "/", "\") change the direction of execution just like a PingPong ball would be reflected when hitting one of those characters.

When the instruction pointer leaves PingPong-Space, it comes out on the opposite side again. So the PingPong-Space is something like a closed torus (it wraps around).

Stacks

PingPong has two stacks: the default stack and the HelpStack®. When you push data or perform operations, you always work with the default stack. There are two operations to modify the Help Stack®: "`" and "'" are used to move a value from the stack to the Help Stack® and back again. Moving from one stack to the other means popping from the source stack and pushing the same value to the target stack.

The stack's sizes are only limited by the memory of the host system. If the memory goes low, the interpreter terminates. If you try to pop a value from an empty stack, you'll always get 0 without any underflow. Note that this means that you will not be able to detect if a stack is empty or not.

I/O Streams

Binary streams are neither supported nor specified right now.

Character streams convert characters from ASCII to PPCCT and back, depending on the operation. When reading, you can rely that each line break is converted to a single line feed automatically. A line break may be a single line feed or a carriage return followed by a line feed. Note that "CR LF CR <sth. else>" is converted to "LF CR <sth. else>". When writing to a stream you should only use line feeds to be platform compatible, but here no conversion is done.
Additionally, character streams have the ability to read and write decimal numbers which are converted from/to strings automatically. The reading of a decimal number is implemented as following: The Stream is scanned for the first non-whitespace character, then the following characters are concatenated to a string until the next whitespace (space, tab). A line break, the character PPCCT #226 or the end of the stream always terminates reading. Then the string is converted into a 64-bit signed integer. The default value is zero if conversion fails, or the string is empty.

The Context

Using parenthesis "(", ")" you can create and close contexts. When a context is created the interpreter pushes the old context's data to its context stack. Then a new context is initialized with the current instruction pointer and the heights of the stack and the Help Stack®. From now on all memory accesses and procedure calls will take addresses relative to the position where the current context was created.

When closing a context, the next context is popped from the context stack, if it is not empty. In all cases this will set the heights of the stacks to their initial heights at context creation. This is done by discarding values to decrement the stacks' heights, or by pushing zeros to increment it.

Initially the program runs in a default context. This context's memory offsets and stack heights are zero.

Threads

PingPong supports threads. Threads can be created using "{" - this will cause the current thread to skip the following instruction while the newly created thread will execute it. All stacks (default stack, help stack and context stack), the current context, the input/output streams, the allocated resources (see API) and the execution direction are copied to the new thread.
"}" will terminate the thread hitting this instruction. If this is the last thread the whole program will terminate.

Each resource has a reference counter, counting the number of threads being able to access the resource. Forking a thread will increment the reference counter of all owned resources and terminating a thread will decrement it. If the counter reaches zero, the resource is closed automatically.

When terminating a program using "@" the current thread will be stopped and all other threads, if existing, are notified to stop as soon as possible. If not all threads stop within a specific period of time the program shoots all remaining threads and terminates abnormally - this can happen if some threads are stuck in blocking io-operations.

Using mutexes and events threads can be synchronized (see API-reference)

PingPong Character Code Table

The PingPong character code has the following properties:

Complete PPCCT

The stacks and the PingPong-Space always contain PPCCT encoded characters and all IO-operations take or return PPCCT codes. When a text file is loaded into PingPong-Space or a character is read from a stream, it is automatically converted to PPCCT (so you can write your source code in ASCII). Also when writing characters to streams, they are converted to ASCII. So the only situation you will notice PPCCT is when you calculate with character codes.
Using this character mapping, the character codes of numbers correspond with their values, so no distinction has to be made between numbers and characters in the source code. Also capital letters can be used to represent integer values between 10 and 35, for example.

All integer values which cannot be pushed directly can be created out of smaller, pushable values. For example #138 (= line feed) can be pushed like this: N6* because "N"=23 and 6*23=138.

Instruction Reference

codepopspushesdescription
+b aa+badd
-b aa-bsubtract
*b aa×bmultiply
%b aa÷b (a mod b)divide and calculate remainder. is also defined for b = 0
&b aa and bperforms a bitwise "and"
~a¬apushes the 1's complement of a
°a pops one value and discards it
"aa adublicates the value on top of the stack
^b ab aexchanges the two topmost values on the stack
`aa(h)moves a value from the standard-stack to the Help Stack®
'a(h)amoves a value from the Help Stack® to the standard-stack
<  skips the next instruction if the top value is less than zero
>  skips the next instruction if the top value is greater than zero
=  skips the next instruction if the top value is exactly zero
#  skips the next instruction
$  the next character will be pushed even if it is an instruction
¤i pops a value i from the stack and executes it like an instruction
, creads the character c from the current input stream (c is converted to PPCCT). c is -1 is the stream is closed
; areads the number a from the current input stream, entered in decimal digits terminated by return
.c writes the character c to the current output stream (c is converted to ASCII first)
:a writes the number a to the current output stream, converted to decimal digits without newline
?x ycreads the value c from mem(x,y)
!c x y writes the value c to mem(x,y)
[sp x yip.y ip.x ip.sppushes the instruction pointer and the current PingPong-Space and jumps to (x,y) in the space "sp". the target instruction will be executed.
]sp x y jumps to (x,y) in the PingPong-Space "sp". the target instruction will not be executed (ip will move first).
(  creates a new context (see context)
)  closes the current context, resetting the stacks to their initial size
{  creates a new thread which starts with the next instruction. the current thread skips the next instruction (see Threads).
}a terminates the current thread. if this is the last thread, the program terminates with exit code a. note that 0 is popped when the stack is empty.
§f argsresultscalls the API function with the number f. the args and results depend on the called function
|  reflects the direction of execution horizontally; traversed vertically it acts as a nOp (see Control Flow)
_  reflects the direction of execution vertically; traversed horizontally it acts as a nOp
/  reflects the direction of execution like a PingPong racket
\  reflects the direction of execution like a PingPong racket
@a terminates the current program with exit code a. note that 0 is popped when the stack is empty.
SP  nOp (no Operation)
NBSP  noP (no Program, PPCCT #226, ASCII #160). Does essentially the same as nOp but it indicates that there is no source code at this position. this is important for under- and overwriting (see API for loading source files).

All other characters are instructions to push themselves.

API Reference

API functions can be invoked using the "§" instruction after pushing the needed arguments and the function number.
Some API functions take strings as argument, this is indicated by an asterisk after the parameter name. In PingPong strings are a sequence of characters terminated by a negative value.

Some functions return handles or take them as arguments. Those handles are numbers, identifying resources for the current thread. Other threads are not able to access the resource (not even with that number). The only way to share resources between threads is to create them before forking, because forking makes an exact copy of a thread, including all open resources.

General Functions

numberargumentsresultsdescription
-1HND closes the specified object for all threads and releases all threads blocking on it (if possible).
0HND closes the specified object just for this thread, so that other threads still can use it. If all threads close an object this way, the object will be destroyed

Input/Output Streaming Functions

All io-functions take the access mode and a resource locator as argument and return the handle to the opened resource or a negative error code. Arguments with asterisk are strings terminated by a negative number.
The access modes are: 0 = none, 1 = read, 2 = write, 3 = read and write.
If the resource is opened for reading, the stdin of the current thread will be set to the resource's input stream. The same applies to stdout and the output stream. Use function 15 to explicitly set the stdin and stdout streams to a resource. Resource number zero is always the console the program is running in.

numberargumentsresultsdescription
1mode filename*HNDOpens a file for reading/writing.
2mode url*HNDOpens the resource specified by an URL.
4mode x y dirHNDOpens a stream to access the PingPong-Space starting at (x, y) in the given direction. values for dir: 0=UP, 1=RIGHT, 2=DOWN, 3=LEFT
8mode address*HNDOpens a socket connection. The address must have the following format: "1.1.1.1:80".
9portHNDCreates a TCP-IP server to wait for incoming connections on the specified port (see the following instructions on how to use the server object).
10mode ServerHNDHNDAccepts a single incoming connection from a TCP-IP server object. Returns a handle to the socket.
11mode ServerHNDHNDAccepts multiple incoming connections from a TCP-IP server object. Each accepted connection creates a new thread starting at the following instructions. The newly created thread gets a handle to the socket. If the listening thread is interrupted will continue execution after skipping one instruction.
15mode HND Sets the default input and/or output streams (depends on mode) to the specified resource.

Thread Synchronization

For thread synchronization two methods are supported: Mutexes and Events. A mutex is an object that can be owned by a single thread at a time, all other threads trying to acquire the mutex will wait for the owning thread to release it or to terminate. Then one of the waiting threads will take control over the mutex. A thread can acquire the same mutex multiple times, but it has to release it the same number of times.
Events can be used to let a number of threads wait for something to happen (nomen est omen). They have an internal state (triggered or non triggered) which can be set, reset or pulsed (set and reset in one) by any thread. In the moment the state switches to triggered, all threads waiting for the event will be released. Using the single pulse you can also just release one waiting thread which will not affect the trigger state of the event. The order of release is not specified.
Both, events and mutexes are represented by the same type of object (sync-object), the real behaviour of the sync-object depends on the way you use it.

numberargumentsresultsdescription
16 HNDcreates a sync-object which is not triggered and not owned by any thread.
17 HNDcreates a sync-object which is triggered and owned by the calling thread.
18HND get mutex
19HND release mutex
24HND set event. releases all threads waiting for this event. further calls to wait will not block.
25HND reset event. further calls to wait will block.
26HND pulse event. combines set and reset atomically.
27HND single pulse. releases only one thread waiting for the event.
28HND wait for event
32time sleeps the specified time in milliseconds.

Over- and Underwriting, PingPong-Space creation

This API is used to load text files into the PingPong-Space. This is done in the same way like the interpreter loads the program's source into the space and can therefore be used to load either data or program code.
Underwriting loads a file into the current space, but it does not overwrite existing code. It changes only positions with noP's.
Overwriting loads a file and replaces existing code but only on positions where there is no noP in the loaded file.

numberargumentsresultsdescription
48filename* x y Overwrites the PingPong-Space with the textfile starting at the given position. All characters are converted to PPCCT.
49filename* x y Underwrites the PingPong-Space with the textfile starting at the given position. All characters are converted to PPCCT.
50filename*SpaceHNDCreates a new PingPong-Space, loads the file into it and returns a handle to the space. <not yet implemented>
©2001, Michael Wurm. Legals