Valid
	XHTML 1.1! Valid CSS!
Created 2003-10-01   Modified 2010-04-11
Chelton Evans

proj rpn User Manual home

Command Line Basics

rpn is a stack based programming language. Data, functions and programs are pushed onto the current data stack. Functions are evaluated in Reverse Polish Notation hence the name rpn. The last element pushed to the stack has position 0. So counting on the data stack starts from 0. Whenever an element is pushed onto the stack the other elements position is increased by one.

Consider adding two 2 + 3 = 5 from an empty stack.

2     <Enter> 

results in something like this.

size=1 depth=2 
0: 2

Firstly size=1 displays the size of the current data stack. depth displays the program stack depth and can be looked at later.

3     <Enter> 
size=2 depth=2 
1: 2 
0: 3 

Reverse polish notation is the natural way computers evaluate. For maths this means + is added to the data stack. The order is important as the function reads the first two arguments from the data stack. The data stack is used to pass the arguments.

size=2 depth=2 
1: 2 
0: 3 
+     <Enter>
size=1 depth=2 
0: 5

For this example I have assumed that + evaluated itself on the stack. This programming language supports two modes, evaluating an object relative to the current stack or placing the object on the stack(deferred evaluation).

In deferred evaluation mode the stack would have looked like this.

size=3 depth=2 
2: 2 
1: 3 
0: + 

Each element in the stack is numbered from the most recent 0 onwards. To copy an element already in the stack to the front use & operator. This operator is useful because it allows you to reach into the stack.

size=3 depth=2 
2: 3.141592 
1: cat 
0: 2 
2 &   <Enter>
size=4 depth=2 
3: 3.141592 
2: cat 
1: 2 
0: 3.141592 

Data Types

Concrete Data Types

  • Integers
  • Reals
  • Complex Numbers
  • Strings
  • Programs
  • Functions

When entering input the interpreter determines the type and evaluates it relative to the current stack and evaluation mode.

Some functions do conversions eg dividing an integer can return a real.

A dictionary holds the names of functions. If an input is not a number or function it is interpreted as a string.

A string can be declared explicitly by quoting the text. The quotes can not be part of the string. eg

"A silly string"   <Enter> 
0: "A silly string" 

While the quotes appear in the display they are not part of the string, but are for rpn to interpret as a string and not interpret the white space as meaning to separate the tokens.

To avoid interpretation text can be quoted to force a string variable onto the stack. eg "/" for the forward slash and not division.

To enter a program, enclose input within {   } . For example { HelloWorld } is a program containing a string. Evaluating it places the HelloWorld string onto the stack.

Complex numbers are entered in cartesian form. eg

(1.,3.)  <Enter> 
0: (1.00000000000000000000,3.00000000000000000000)

Maths Functions

For historical reasons the word functions has a few different meanings. For maths people a function takes one or more values and returns a value. Maths functions are easy to build in rpn.
eg   f(x) = x*x+1.

These are implemented with the program data structure. The stack itself is used to pass the x value.dup is short for duplicate so you get two x's on the stack.

{ dup * 1. + } 

To input the function enter the following. There are many ways to do this, here is one.

{ #dup #* 1. #+ @rev } 

If we want to evaluate f(1.5), f(2.5), f(3.5) on the stack here is a way to do it.

0: { dup * 1. + }   3: { dup * 1.00000000000000000000 + }
 1.5  1 &           2: 3.25000000000000000000
 2.5  2 &           1: 7.25000000000000000000
                    0: 13.25000000000000000000
 3.5  3 & 

Alternatively have the function as a local variable.

1: { dup * 1. + } 
0: f 
  var 

  1.5 f & 
  2.5 f &
  3.5 f &

Here is the same program written without dup using the maths power operator instead. The order of operations is important. When I was in grade 7 this was the drill.

Brackets first include Powers and Roots
Of next
Multiplication and Division in the order they ocurre,
and finally addition and subtraction in the order they ocure.
{ 2. ^ * 1. + } 

Angular Units

Every calculator has degrees, radians and grads along with polar, spherical and radians coordinate system modes, what does rpn have?

All of rpn's angles are in radians. This is because they are the natural units from a computational perspective. To facilitate conversion between radians and degrees use r->d and d->r.

The coordinate system is rectangular however there are functions to go from polar to rectangular and back.

The building blocks are there so you can easily build a convenience function yourself. For example I would like a function that reads in polar numbers in degrees and can print out a complex number in polar degrees.

pr={ d->r p->c } 
 pc={ dup abs 1 & arg r->d } 
5. 20. pr &  - to enter polar in degrees, 
 pc & - to display in polar coordinates.

Pointers

Pointers are the most crucial data types because they are the basis of the programming language - without them programs could not be written effectively.

A pointer is something which points to something else. If you have data it has to have a location. The location is the pointer. And this is why pointers are fundamental to computer science.

Variables

Variables are a type of pointer. Support for storing and retrieving variables is provided. eg

32 hat var - creates a variable named hat which stores the value of 32.

hat &
hat rcl
hat  ->

- evaluates hat on the stack.
- recalls the variables contents to the stack.
- recalls the actual object as a pointer to the stack, if modified so is the original.

A named variable is accessed through a string argument. Most types of pointers support named variables.

The Address & Operator

As can be seen from maths functions the & operator is very useful. & combines accessing the variable and evaluating it on the stack.

Since a copy of the original variable is make the operator is in general non-destructive of the original variable.

What this means is that I expect & to be the most heavily used operator because its simple.

Its brother is rcl which accesses the variables but does not evaluate.

rpn accesses variables through strings. Most other languages upon encountering a variable do a lookup. For example if I place "x" on the stack and a variable x exists evaluate x on the stack instead.

For rpn its not clear as to which possibility of the variable is meant. Do you mean "x" the string, a pointer to the variable x or a copy of the variable x.

To remove this confusion the user is responsible for how a string is interpreted. The interpreter is not to interpret a string as a variable, only an operator is to do that. The & operator says evaluate the variables contents to the stack. This improved the language because it made clear that operators are fully responsible for interpretation and need to overload string for variable interpretation. Some do and some don't.

Indexed Pointers

[] creates an index into the stack and ->[] gets a pointer into the stack relative to the index.

A pointer that is accessed with an integer relative to some other pointer. In rpn the stack size keeps changing so indexing into the stack from the bottom is not easy because the stack is in constant change here. However counting from the top is another story.

Provided you do not corrupt above the index into a stack a indexed stack pointer will keep track of the position in the stack that was originally refered to.

Out of Range, Non-existant and Inconsistant Pointers

For stability rpn guarantees that all pointers accessed will be valid or ignored.

  • With indexes operations that are out of range ignored.

  • For pointers to objects if you destroy the original object the pointer remains valid because the object is only truely destroyed when there are no other objects pointing to it (reference counting on all objects implemented).

  • When a session is saved the evaluated pointers are realized. The pointers themselves were not saved and are effectively destroyed. In their place the pointers data is written.

    The pointer object -> is saved and restored across sessions. But when evaluated rpn does not keep track of the original's location. This may not be unique, rpn guarantees only valid references. An evaluated pointer will not be pointing to the same object between sessions.

    An exception is the indexed pointer [] which was made to be saved across sessions because it indexes the local stack so its meaning is always clear.

Second Class Pointers

These pointers always point to their original source. Even if this gets annihilated it still exists because a pointer is pointing to it. These are literally pointers to the original object, which always exists if it has a pointer to it.

Pointers allow the user to operate into the stack, and not just on the first elements. When evaluated a pointer evaluates to what its pointing at. So a pointer to an integer returns an integer. However if this is modified so is the original.

2: 849
1: 23 
0: 34 
2: 849 
1: 23 
0: 34 
 2 -> 
3: 849 
2: 23 
1: 34 
0: 849 
3: 849 
2: 23 
1: 34 
0: 849
 1 + 
3: 850 
2: 23 
1: 34 
0: 850 

Motivations for pointers comes from considering how to implement algorithms in rpn. For example adding two vectors on the stack.

5: 1698
4: 23 
3: 34 
2: 904 
1: -494 
0: 3 
 3 -> swap + drop 
 3 -> swap + drop 
 3 -> swap + drop 
2: 2602
1: -471 
0: 37 

A weakness in the language is that the vectors references were not constant and generally programmers should not have to deal with this.
   eg consider the vector operation in C++.   v1[k] += v2[k] .

As a consequence of the above vectors pointing into the stack were implemented. Its another type of pointer. Instead of a pointer to the data, its a pointer to the position in the stack. However the position is not counted from the start but from the top of the data stack. As data is added this reference stays constant provided the user doesn't delete in the stack and invalidate the reference.

The beauty in this is that we can have indexes pointing into the data stack which generally do not change as the stack size changes over time. This is great for writing algorithms.

3 v1 var[]   creates a variable called v1 which indexes into the current stack at position 3.

0 v1 &[] copies the value at that position to the front of the stack.

2 v1 ->[] returns an object which is also a pointer to the data at position v1 + 2 in the data stack.

Now the programmer can write a more traditional algorithm for adding the two vectors from the previous example.

0 v2 [] 3 v1 [] 
0 v1 ->[] 0 v2 ->[] + drop 
1 v1 ->[] 1 v2 ->[] + drop 
2 v1 ->[] 2 v2 ->[] + drop 
3 dropn

or

0 v [] 
3 v ->[] 0 v ->[] + drop 
4 v ->[] 1 v ->[] + drop 
5 v ->[] 2 v ->[] + drop 
3 dropn

rpn was designed so that you can not crash the program when accessing a pointer out of range. This is a robustness issue and design decisions were made to ensure that this never happens.

Third Class Pointers

These could be viewed as pointers resulting from an ordering of operations.

In math a+b is equal to b+a because the order of addition is not important. In general the order determines which variable will store the result of the operation.

a b + would in general store the result in a's memory location.

Promotions and Order

Conversions between rpninteger and rpnreal are supported.
eg   Dividing two integers returns a real if the division is not exact.
Adding an integer and a real returns a real.

However rpn is order driven.   a b +   doesn't necessarily equal   b a +   because the result is stored at the first arguments memory location, except when promotion occurs where the new promoted data element is placed on the stack.

In general the first argument is modified and the second discarded in a binary operation. Except where this doesn't make sense as in a promotion.

Since rpn supports pointers a variable may not be assigned to as you expected. However if you are not using pointers this isn't an issue. Conversions were supported to make the language more friendly.

From a practical perspective be aware of what types you put on the stack and you can use the ordering.
eg for integer and real addition put the real first and then the integer before addition. This ensures the real is the same variable as before. This is a feature of the rpn calculator.

There are explicit functions to querie an ojbects type and convert between the types. See isinteger , isreal , iscomplex , isstring , isprogram , integer , real , string .

Evaluation Modes

There are two modes of evaluation: immediate evaluation and deferred evaluation. In immediate mode the function is immediately evaluated onto the stack. In deferred mode the object is pushed onto the stack. Strings and numbers are invariant(do not change) when evaluated, but functions and programs have both forms of evaluation.

Before in immediate evaluation mode:

1: hat 
dup

After:

2: hat 
1: hat 

Before in deferred evaluation mode:

1: hat 
dup

After:

2: hat
1: dup

Temporarily change the evaluation mode

When in immediate mode there is a command to temporarily defer function evaluation by prefixing the command with a # character. eg #dup places the dup function onto the stack. The # command is good when building programs in immediate mode.

Similarly in deferred mode there is a command to temporarily evaluate a function by prefixing the command with the @ character.

The first object on the stack can be explicitly evaluated using the eval function. To guarantee evaluation use @eval.

Persistent change in the evaluation mode

The evaluation mode is persistent and is the users responsibility. Its a state, and a way of computing. You could evaluate directly or build a program to do the same job then evaluate it. So often the programmer wishes to switch between evaluation modes.

This is further needed because even when building a program the programmer can still evaluate on the stack - the distinction between the program and the stack is meaningless here as they are both the same, except the program is nested down one more layer.

@+ and @- were implemented to meet the switching between modes by permanently setting and un-setting the evaluation mode respectively.

Input Manipulators

#    - instant deferred evaluation
@    - immediate evaluation
"    - interpret the word as a string

Place in front of command without whitespace between them.

Nodes

Nodes are rpn's local file system. The were named nodes and not directories so that there would be no confusion between the file system of the local computer and rpn's file system.

Node Hierarchy

One of the aims of the language was to support a tree structure where users variables and programs can be stored and manipulated. A node hierarchy was implemented which is a tree of nodes which in the language are implemented as programs.

Unix like commands are available for manipulating the node tree. eg cp, pwd, pushd, popd, mv .

The root node / is the base node of the tree. Since this is also the symbol for division its interpreted as such when placed on the stack. Directories are processed as strings so the string / has to be placed on the stack to mean the root node and not the division operator. Entering "/ forces the string on the stack. This problem does not accure for other paths - only the lone root node.

"/   <Enter> 
0: / 
img01.png
Node hierarchy diagram

Nodes are persistent and can store user variables so can be used to build environments. eg Collections of useful programs.

See homesave to save the node hierachy. When rpn is first run it looks for the file rpnhome.txt in the directory where rpn was run from. If it exists the file is read and interpreted as if the user had typed in the commands themselves. This restores the state as before.

The exception being pointers which have their variables realizes(values written).

Nodes and Programs

A program has its own data stack and variable stack. rpn also has a current node state.

img02.png
Program and Node Composition diagram

Programs are the core data structure in the language. A program has its own data stack. It also has its own variable stack. Since a variable can also be a program, a program can form a hierarchy of nodes.

When a variable is created through var it is stored in the current node.

The primary data stack is the current programs data stack. Functions are evaluated on the primary data stack. Nodes can form a hierarchy

When a variable is created through var it is stored in the current node. To make a new node (or branch) , create an empty program and make it a variable.

{ } d1 var 

Programs can be edited. Move into the program and edit the stack directly. Use pushd to edit programs.

1: {  a var b var  a b + b }
pushd

There are two ways to move about directories.

  1. pushd popd
  2. cd

When a program is entered it pushes itself on the program stack. When a variable is searched for the top to the bottom of the program stack is searched for the first matching variable name. pushd, popd allow exact scoping of variables and also cyclic paths. In contrast cd is always a tree(non-cyclic) with each decedent possibly overloading its parents variables.

cd clears the program stack and loads the variables in its path from home. If successful it guarantees the program stack state and is useful for this alone.

Utility functions like ls, pwd, tree help navigate directories. cd can be used with relative paths, as can mv and cp.

The rpnhome.txt file is loaded if it exists. The file stores rpn's directories.

The environment can be permanently saved and brought back through save and load commands. Firstly save overwrites an existing file with the same name, storing in the same directory where the executable was launched.

Further save saves from the current directory onward, so if you are not in / that will not be saved.

By default rpnhome.txt is loaded on startup if it exists, preserving rpn's state between sessions. See the user defined functions section for a persistence solution.

Programming - what you need to Know

Compilation and Execution

On of the strangest things about this language is compilation and execution are not separate, because when you are writing a program you are still evaluating on the stack.

This may not appear exciting but is. For example local variables can be initialized, other functions/ programs called and results used. Many other languages such as Lisp can do this, I'm just new to it.

However in rpn the consequence of such flexibility is that the order of the program is reversed. If something is input in unevaluated form it needs to be reversed. You could of-course put it in the right way to begin with but this makes even the most trivial programming task a feat.

For these reasons rev and prev were implemented to reverse the stack and program respectively. For example

Before:

{ @- a var b var a b + b @rev } 

After:

{ a var b var a b + b }

Or if I forgot to do the reverse at the end. Before:

0 : { b + b a var b var a } 
@prev

After

0: { a var b var a b + b }

The evaluation mode needed to be switched off with @- so that the functions would be placed on the stack and not evaluated. Since this is persistent the mode will need to be switched back on with @+.

Local Variables

Passing variables to functions is done via the stack. Support for parameter passing through the stack exists in the language with the use of local variables.

Local are variables local to the program being evaluated and will not collide with global variables( variables which have been defined further up the tree ). The local variable hides the global variable.

Have the local variables initialized in reverse order to the stack. Consider the following program.

Input:    a b c       Output:    ab ac bc

There are 3 input variables which would be harder to manage without named variables. The following builds a function to do this.

{ 
  c var b var a var 
  a & b & * 
  a & c & * 
  b & c & * 
}

Local variables can improve the look of the program.

Local variables are overloaded. Creating another variable with an existing name does not overwrite the existing variable, but hides it.

Even within the same node/program there can exist multiple variables with the same name. The most resent one will be in scope.

You can remove a variable with the rm command which in a sense is the reverse of var.

for loop

Problem:   Sum the numbers 1 to 6 for the answer of 21.

Create and initialize variables.

0 i var
0 s var
{ i & 5 <= }
{ s -> i -> ++ + drop }
for

For the result ls to view the s variables contents, which hold the sum.

Notice the use of the pointers. + changes the first argument which is s. So s is updated with the new value.

Consider writing the loop function in a more structured way. The -> of i can be removed because ++ does the pointer access. The program need only be evaluated returning the result to the callers stack.

{
  0 s var 0 i var 
  { i & 5 <= }
  { s -> i ++ + drop } 
  for 
  s & 
}

Compare with C++ code

{ 
  int s = 0; 
  for ( int i = 1; i<=5; ++i ) 
  s += i; 
}

I have given C++ code so that you can compare the code written in rpn and C++. rpn is not as conscise as C++. Unlike C++ in rpn you have to be aware that you are programming on the stack but they are different environments. rpn is not as easy to read, but the intent can easily be seen with a slightly longer look at the source code.

With pointers more strange code emerges. The named variables can be eliminated with the variables being directly on the stack. The catch is that you have to be sure you are pointing to the right variable because the stack size changes.

1: 0 
0: 0 
{ 1 -> 5 <= } { 0 -> 2 -> ++ + drop } for

0: holds the sum and   1:   the counter.

The for loop consumes the programs. When the test evaluates it evaluates itself on the stack, leaving an integer. If that is 0 if failed the test else passed. The for program consumes the integer leaving the stack with the reference state.

The body evaluates itself on the stack grabbing a pointer to the sum, then a pointer to the counter. Notice that it had to adjust because the program itself put an integer on the stack so its reference increased by one. ...

1: 6 
0: 21 

Traditionally people move away from assembler or directly passing information using the stack because its harder to recognize what the code is. However I believe that the last example is very readable and simpler than with named variables. he success of the code was the use of pointers by rpn. I have not seen any other language do this.

Accessing variables on the stack is quicker than accessing named variables because strings and dictionary lookup are minimized. Operating in defered evaluation mode still compiles so writing code resolves or interprets the code as a list of types. Thus in general all the code is compiled, despite rpn being an interpretive language.

Infinite Looping

The for loop was difficult because if the test statement never returned false the loop would go forever.

This is most often a programmers mistake eg forgetting to increment the counter. Therefore a counter was put into the for loop to terminate after about 1000000 iterations with an error message(see rpnfunc.cpp to edit this number).

pushd popd cd

Programs and nodes are the same and are evaluated on the same stack. So what happens when you mismatch pushd and popd?

The worst case is when you can find yourself not in a node but a program. Your location does not appear right, the stack is different. Call popd to restore the stack.

Another situation is when pushd fails because the directory does not exist. The command does not say if it worked or not it just does or does not do the job. rpn is not a strict language. A strict language may return the status result after each function call, which is then tested or discarded. This is not what I wanted the language to do. The idea is to write the code so it works. If it does strange things fix it.

So if pushing the node/directory fails poping it will if you are not in the root node, because popd is equivalent to .. cd. So you will move up one directory.

Since pushd and popd are not unique in behaviour they do not have to be matched. For example

g1 cd ..... popd
g1 pushd ..... popd
g1 pushd ..... .. cd

The difference between cd and pushd is that the cd adjusts the stack up or down relative to the path. pushd adds a path to the stack independent of the path.

You could use this to change a variables scope.

eg - assuming successful command evaluation
../../d4 cd the stack depth is decreased by one because minus two directories and then plus one.
../../d4 pushd increased the stack depth by one.

pushd can be used to change the scope. For example if a variable had been overloaded (in scope) you could pushd to the variables directory and it would be in scope again.

You can manage the program stack by watching the stack depth count and current node/directory.

p@ - Program Evaluation

This is the hardest part of the rpn language with the question how do you interpret a program?

You read it and follow the commands? Since rpn places programs on the stack, in default mode it evaluates a program on the the callers data stack by fixing that stack reference and then going along each command in the program and evaluating it on the stack.

While this is generally a good thing, when you wish to change the directory thereby changing the location and hence the data stack is still where you called the program from garbage results.

eg
Let /d3 be a node and the current node is /.
{ d3 cd h1 } evaluated on the stack changes to /d3 but places h1 in / and not /d3.

If I had entered the same commands from the keyboard h1 would be placed in /d3 's data stack.

There are a few issues here. Firstly without a radical design change there is no one solution to the problem. rpn evaluated like this to support named local variables. However wysiwig or the need to have a programming language where what you keyed in exactly by hand could be put in a program with the exact same output was a key requirement, especially as the language doesn't have debugging tools it relies on this technique. There is no one solution.

So after much thought(going mad) I identified the two different ways that a program can be evaluated.

  1. Placing the program on the program stack.   [bit 0 of the state field]
  2. Having the data stack constant or relative to the current location.   [bit 1 of the state field]

Each program was given a state field which is an integer representing switches that control how evaluation ocurres. So I do not have to decide how to evaluate a program, you do.

A default setting for programs of 3 where both bits are on/high was implemented, the same evaluation that gave the garbage result is the default. Here the program has its own local variables. If bit0 - the pushing of the program onto the stack is turned off you can not access local variables because the programs scope does not exist. Instead the calling programs scope will be used.

For that magical code where what you entered is eactly what you get set the program state to 0.

All this is controlled through one function p@ . One of the calls queries and the other sets the state.

States 0 and 3 are the main states with 0 being the most primative form of evaluation - it just reads and excecutes the commands in sequence.

User Defined Functions

When you can tailor something it feels comfortable. dictadd allows the user to add their own functions to the dictionary. This is really bootstrapping because rather than write functions in C++ and compile them into the system you can use rpn instead.

For example I have a homesave function to save my home directory. If it wasn't this simple it would have been implemented as a C++ rpnfunction.

homesave={ dec "/" pushd rpnhome.txt save popd } 

dec makes sure that integers are written in base 10. Then move into the root node and write the nodes/directories, returning to where you called the function from.

So I recommend implementing minimal functionality in the C++ code and implementing the rest in rpn's language.

Managing user-defined functions also means that you may wish to delete them. User-defined functions are stored in /bin. When load loads a file it looks for variables in /bin and initializes them as user defined functions. dictadd adds variables to the /bin directory to permanently store the functions.

If you wish to remove a user defined function it has to be deleted from the bin directory. To do this use rm . However because a user-defined function doesn't let you access its name (it evaluates the function or pushes it to the stack), the name has to be forced onto the sack without evaluation.

"name forces a string called name to be placed on the stack. Then use rm from /bin to remove the variable.

Finally homesave to save the current state, exit and restart the program to be free of the definition, or call load to re-initialize the interpreter. In general I just remove it, when the program is quit the function naturally dies.

Extending rpn in C++

rpn can be extended at the source code level. The source code can be modified any way you like. However as this is an application language support for user defined functions at the source code level was implemented.

By deriving from rpnfunction clients can extend rpn. Follow the conventions of other rpn functions eg do not delete but dec() data on the stack.

Once a function is defined it needs to be added to the interpreters dictionary. After the interpreter is initialized call addtodictionary< T >(); in rawinterpreter.h where T is the derived rpnfunction type.

@-    - set the deferred evaluation mode
@+    - set the immediate evaluation mode

Commands

var   isvar   rm   rcl   &   =   ->   []   ->[] 

eval   ifthen   thenif   ifthenelse   thenelseif   for   clear   clearvar   clearboth   dup   dupn   rev   rot   rotn   | swap   \ drop   \i delete   dropn   size   stateevalis   stateevalset   stateevalunset   load   save   dictadd   isstring   isinteger   isreal   iscomplex   isprogram   isvector   string   integer   real   ascii   insert   interp   quit  

ls   tree   pwd   cd   mv   cp   pushd   popd   depthd   ispath   path!  

p@   pnew   pnew!   prev   pstream  

push   pop   pushn   popn   size2  

+   -   *   /   ++   - -   <   <=   >   >=   ==   !  

neg   abs   sin   cos   tan   asin   acos   atan   sinh   cosh   tanh   floor   ceil   log   log10   exp   sqrt   ^   factorial   mod   gcd  

binary   oct   dec   hex   not   xor   or   and   shl   shr  

conj   arg   imag   d->r   r->d   p->c   complex   norm  

The commands are grouped by type or perspective. For example all the program commands operate on programs on a stack.

  1. Variable and pointers
  2. General
  3. Node/directory
  4. Program
  5. Tempory data stack
  6. Maths functions
  7. Integer Displays and Bit Manipulations
  8. Complex number functions
0:   x
push
0:  
Pushes x to ds2 data stack.
0:  
pop
0:   x
Moves the object from ds2 to the current data stack.
n: anything
...
0:   n - integer
pushn
0:  
Pushes n objects to the second data stack.
0:   n - integer
popn
n-1: anything
...
0:   anything
Moves n objects from the second data stack to the primary data stack.
0:  
size2
0:   n - integer
Finds the size of the second data stack.
0:   x
eval
0:  
Evaluate x on the stack. For example execute a program built on the stack.
1:   number - result of test
0:   x
ifthen
0:  
Evaluate x if true.
1:   x
0:   number - result of test
thenif
0:  
Evaluate x if true. This is equivalent to swap ifthen and was implemented to help the programmer reduce another instruction.
2:   number - result of test
1:   a
0:   b
ifthenelse
0:  
Evaluate a if true else evaluate b.
2:   a
1:   b
0:   number - result of test
thenelseif
0:  
Evaluate a if true else evaluate b.
...
0:   x
clear
0:  
Clears the data stack.
...
0:   x
clearvar
Clears the variable stack - the current nodes variables. It does not effect the data stack.
...
0:   x
clearboth
0:  
Clears the data stack and variable stack.
0:   x
dup
1:   x
0:   x
Duplicate x on the stack.
3:   a
2:   hat
1:   2.7
0:   3
3 dupn
6:   a
5:   hat
4:   2.7
3:   3
2:   hat
1:   2.7
0:   3
Duplicates or copies the first n elements of the stack to the front of the stack.
n-1:   n
2:   ...
1:   2
0:   1
rev
n-1:   1
2:   ...
1:   n-1
0:   n
Reverse the data stack.
2:   a
1:   b
0:   c
rot
2:   b
1:   c
0:   a
Rotate the first three objects, :2 comes down.
n+1:   n
...
3:   2
2:   1
1:   integer n
0:   integer k
rotn
 
Rotate the first n objects k times, the top element comes down when k is positive. Rotation in opposite direction with negative k.
1:   b
0:   a
swap

or

1:   b
0:   a
|
1:   a
0:   b
Swap the first two objects. The symbolic version | was added for convenience.
0:   a
\

or
0:   a
drop
0:  



Drops or removes the first element form the stack. However because it is so tedious to type in drop the back-slash symbol \ was implemented as the drop operator too. The drop operator has two names.
...
3:   hat
2:   a
1:   1
0:   sin
2 \i

or
2 delete
...
2:   hat
1:   1
0:   sin
Deletes the i'th element from the stack.
n:   n
...
1:   1
0:   n - integer
dropn
0:  
Drops or removes the first n elements form the stack.
0:   number
!
0:   0 or 1
Logical not operation.
1:   x
0:   string - name
var
0:  
Stores x as a variable in the current program.
0:   string - name
isvar
1:   string
0:   integer
Does the variable exist?
0:   string - name
rm
0:  
Removes the first occurrence of the variable.
0:   string - name
rcl
or

0: integer - index
rcl
0:   x
Recalls the variable's contents to the stack.
0:   string - name
&

or

0: integer - index
&

or

0: program
&
0:  

Evaluates to the front of the stack.

The string version copies and evaluates the variable's contents on the stack.

The integer version access the stack copying the variable to the front and evaluating it.

The program version simply evaluates the program.
1: x
0:   string - name
=

or

  ...
a b =   where a,b are integers
0:   x

or

a'th position replaced by a pointer from the b'th position
There are two separate versions. The string version replaces the variable's contents. The integer version needs to integers to point into the stack. the first index is overwritten by the second in a pointer(not value) sense.
0:   string - name
++
0:   x
Increment the variable by one. The variable is returned to the stack.
0:   25 - number
++
0:   26
Increment the number by one.
0:   string - name
- -
0:   x
Decrement the variable by one. The variable is returned to the stack.
0:   25 - number
- -
0:   24
Decrement the number by one.
0:  
ls
0:  
{ id=value ... }
List the current programs variables.
0:  
tree
.
   d2
      h2
         k2
         k1
      h1
   d1
Recursively display the current programs/directories from the current program/directory.
0:  
pwd
0:   /d1
Prints the current directory to the stack.
0:   /d1/h1
path!
0:   { / d1 h1 }
Toggle between a string representation and a program with a list of strings.
0:   d1 - string
pushd
0:  
Changes to the directory called d1. A directory is a program that is a user variable.
0:  
popd
0:  
Pops the current directory. Advise: Be careful when building programs as they share the same stack!
0:  
depthd
0:   integer
Returns the program/directory stack depth.
1:   number
0:   number
+
0:   number
Add two numbers 1: and 0: and store in 1:, delete 0:.
1:   number
0:   number
-
0:   number
Subtract two numbers.
1:   number
0:   number
*
0:   number
Multiply two numbers.
1:   number
0:   number
/
0:   number
Divide two numbers.
1:   number
0:   number
<
0:   integer
Less than function.
1:   number
0:   number
<=
0:   integer
Less than or equal to function.
1:   number
0:   number
>
0:   integer
Greater than function.
1:   number
0:   number
>=
0:   integer
Greater than or equal to function.
1:   number
0:   number
==
0:   integer
Equality function.
1:   program - test
0:   program - body
for
0:  
For loop. The body is evaluated at least once. See for loop example .
1:   program
0:   n - integer
forn

or

1:   n - integer
0:   program
forn
0:  
Counting from 0 to n-1, the count is passed onto the stack and the program evaluated.

0 { + } 5 forn   counts from 0 to 4.
1:     { } - program
0:   0..3 - integer
p@

or

0:     { } - program
bit 0 is the program being placed on the program stack.
bit 1 is setting the program to evaluate on the calling programs stack - the data stack is constant.

The integer case sets the program to the integer state. The program case gets the integer state of the program and puts it on the stack.

n:   any type
...
2:   any type
1:   any type
0:   integer n
pnew
0:   program
Build a program from the stack.
n:   any type
...
0:   { } - program
pnew!
1: ...
0:   integer n - size of program
Decompose a program to its individual elements and place on the stack. The reverse of pnew.
0:   program
prev
0:   program
Reverse a programs data stack.
0:  
stateevalis
0:   integer 0 or 1
Query the evaluation mode.
0:  
stateevalset
0:  
Sets the evaluation mode to on - immediate evaluation. Generally use @+ instead.
0:  
stateevalunset
0:  
Sets the evaluation mode to off - deferred evaluation. Generally use @- instead.
3:   d
2:   c
1:   b
0:   a
size
4:   d
...
1:   a
0:   4
Query the data stack's size.
0:   string - filename
load
...
1:   anything
0:   anything
Loads the file into the current program/directory, interpreting it as an rpn program.
0:   string - filename
save
0:  
Saves the current program tree to a file. [overwrites existing file]
1:   { program }
0:   string - filename
dictadd
0:  
User defined functions: adds the program to the dictionary. The program is stored in /bin.

You can edit the program too.

In /bin , "name -> pushd   Edit program...   popd .

Since the dictionary holds a pointer to the program editing has immediate effect.

However the program remains in the dictionary until the dictionary is reloaded. So deleting(rm) the variable in /bin does not remove the program from the dictionary, but next time the dictionary is loaded the program is no longer there and hence is removed.
0:   x
isstring
0:   integer - 1 or 0
Is the first element of a string type?
0:   x
isinteger
0:   integer - 1 or 0
Is the first element of a integer type?
0:   x
isreal
0:   integer - 1 or 0
Is the first element of a real type?
0:   x
iscomplex
0:   integer - 1 or 0
Is the first element of a complex type?
0:   x
isprogram
0:   integer - 1 or 0
Is the first element of a program type?
0:   x
isvector
0:   integer - 1 or 0
Is the first element of a vector type? <TODO> Implement this function in rpn.
0:   path - string
ispath
1:   path - string
0:   integer - 1 or 0
Does the path exist?
0:   ../crap
cd
0:  
If possible moves to the specified directory. The program and its parents variables are visible.
1:   path - string
0:   variable name - string
mv
0:  
Moves the variable to the location if possible.
1:   path - string
0:   variable name - string
cp
0:  
Copies the variable to the location if possible.
1:   x - anything
0:   i - integer index
insert
i:   x
    ...
1:   ...
0:   ...
Inserts x into the stack at i, counting from zero before x.
0:   x - string
ascii


or

0:   x - integer
ascii
0:   x - integer or string
Toggles conversion of the ascii character between an integer and the first character in the string.
    ...
1:   ...
0:   i - integer index or string variable name
->
    ...
1:   ...
0:   -> value
Creates a pointer. Modifying a pointer modifies the original too.

If 0: is an integer creates a pointer into the stack. If 0: is a string creates a pointer to a variable.

0:   i - integer index
[]

or

1:   i - integer index
0:   string - anything
[]
Make a indexed pointer. An indexed pointer is a pointer into the stack, counting from the top. Unlike -> which points to data the index pointer holds an integer index into the stack.

Elements can be accessed relative to the indexed pointer by ->[] operator.

The string version makes the indexed pointer and stores it as a variable. The non-string places the indexed pointer on the stack.

1:   i - integer index
0:   indexed pointer
->[]

or

1:   indexed pointer
0:   i - integer index
->[]

or

1:   i - integer index
0:   string
->[]
    ...
1:   ...
0:   -> value
Make a pointer relative to the index.

i is added to the index, positive is up.

The string version expects a name of a variable that is a index.

eg The program creates a named indexed pointer into the stack and at a later point addresses the stack relative to the reference.

1:   { data }
0:   { action }
pstream
    ...
2:   { data }
1:   { action }
0:   { result }

Process a stream. The { action } is a program that is evaluated on each element in the data in sequential order.

eg
1:   { 3 5 -7 -9 2 0 11 }
0:   { dup 0 <= thenif }
pstream

produced
0: { 0 -9 -7 }

  ...
0: "1. 3. 5. * * "
interp
  ...
0:   15.000000000

The string is interpreted as a input.

For example a program could be stored as a string and interpreted when needed. Here the limitations of saving programs that use pointers could be avoided by saving the program as a string between sessions.

" { 1 k @var { k @-> #++ @rev } inc @var } "


0:   ...
quit

The program is immediately terminated.

0:   number
string
0:   - string
Convert the number to a string.
0:   number or string
integer
0:   - integer
Convert the number to an integer. Information can be lost. eg converting from 2.5 to 2.
0:   number or string
real
0:   - real
Convert to a real.
0:   number
neg
0:   -number
Change the sign on the number.
0:   -23
abs
0:   23
Return the absolute value of the number.
0:   rpnreal
sin
0:   rpnreal
sin(x)
0:   rpnreal
cos
0:   rpnreal
cos(x)
0:   rpnreal
tan
0:   rpnreal
tan(x)
0:   rpnreal
asin
0:   rpnreal
asin(x)
0:   rpnreal
acos
0:   rpnreal
acos(x)
0:   rpnreal
atan
0:   rpnreal
atan(x)
0:   rpnreal
sinh
0:   rpnreal
sinh(x)
0:   rpnreal
cosh
0:   rpnreal
cosh(x)
0:   rpnreal
tanh
0:   rpnreal
tanh(x)
0:   27.3
floor
0:   27.0
Returns the integer part of a number. Here 27.3 = 27.0 + 0.3 . Compare this with ceil which is equivalent to floor(x) + 1 except when x is an integer.
0:   27.3
ceil
0:   28.0
ceil is short for ceiling. Both floor and ceil have no effect when the argument is already an integer.
0:   rpnreal
log
0:   rpnreal
log and exp are each others inverses. log refers to the natural logarithm in base e.
0:   rpnreal
log10
0:   rpnreal
Logarithm in base 10.
0:   rpnreal, rpninteger or rpncomplex
sqrt
0:   rpnreal or rpncomplex
The square root function.
1:   x - number
0:   y - number
^
0:   number
Power function. ie x^y
0:   rpninteger
factorial
0:   rnpinteger
Compute the factorial of a number. ie n!
1:   x - integer
0:   b - integer
mod
0:   integer
Modulus operation. Return the remainder after division of the first integer by the second. ie
    x mod b
1:   a - integer
0:   b - integer
gcd
0:   integer
Compute the greatest common divisor between two integers. ie
    (a,b)
5:   2
4:   4
3:   8
2:   10
1:   15
0:   -20
binary
5:   0000000000000000000000000000010
4:   0000000000000000000000000000100
3:   0000000000000000000000000001000
2:   0000000000000000000000000001010
1:   0000000000000000000000000001111
0:   1111111111111111111111111101100
Displays integers in the binary base.
5:   2
4:   4
3:   8
2:   10
1:   15
0:   -20
oct
5:   2
4:   4
3:   10
2:   12
1:   17
0:   37777777754
Displays integers in the octal base.
5:   2
4:   4
3:   8
2:   10
1:   15
0:   -20
hex
5:   2
4:   4
3:   8
2:   a
1:   f
0:   ffffffec
Displays integers in the hexadecimal base.
5:   2
4:   4
3:   8
2:   a
1:   f
0:   ffffffec
dec
5:   2
4:   4
3:   8
2:   10
1:   15
0:   -20
Displays integers in the decimal base.
0:   0000000000000000000000000000001
not
0:   1111111111111111111111111111110
Bitwise not on integer.
1:   0000000000000000000000000101011
0:   0000000000000000000000000001001
xor
0:   0000000000000000000000000100010
Bitwise xor on integer.
1:   0000000000000000000000000101011
0:   0000000000000000000000000001001
and
0:   0000000000000000000000000001001
Bitwise and on integer.
1:   0000000000000000000000000101011
0:   0000000000000000000000000001001
or
0:   0000000000000000000000000101011
Bitwise or on integer.
1:   0000000000000000000000000000111
0:   0000000000000000000000000000010
shl
0:   0000000000000000000000000011100
Bitwise shift left on integer.
1:   0000000000000000000000000000111
0:   0000000000000000000000000000010
shl
0:   0000000000000000000000000000001
Bitwise shift right on integer.
0:   (1.00000000000000000000,2.00000000000000000000)
conj
0:   (1.00000000000000000000,-2.00000000000000000000)
Multiplies the imaginary component by negative 1.
0:   (1.00000000000000000000,2.00000000000000000000)
imag
0:   2.00000000000000000000
Gets the imaginary component.
0:   (1.00000000000000000000,2.00000000000000000000)
arg
0:   1.10714871779409050297
Gets the complex argument in radians.
0:   rpnreal
d->r
0:   rpnreal
Multiply by π/180.
0:   rpnreal
r->d
0:   rpnreal
Multiply by 180./π
1:  r - rpnreal
0:  theta - rpnreal
p->c
0:   rpncomplex
Make a complex number from a polar number on the stack. The first argument is the magnitude and the second is the angle in radians.
1:   x - rpnreal or integer
0:   y - rpnreal or integer
complex
0:   rpncomplex
Make a complex number from a rectangular number on the stack. (x,y)
0:   (1.00000000000000000000,2.00000000000000000000)
norm
0:   5.00000000000000000000
Gets the norm or abs squared. A distance measure.
(x,y) -> x^2+y^2

Command Conventions and Perspectives

Logic Queries

Logic queries do not alter the argument and return true or false corresponding to 1 or 0. The function starts with is.

ispath  isvector isinteger isreal

Perspectives

Different perspectives for algorithm development are important. Consider editing a program { } . You could edit it from the outside or step into it and edit it from inside.

The inside of a program is its data stack. Most of the functions apply to the current nodes data stack, so effectively they are from the inside perspective.

Acknowledging that the outside perspective is useful, functions were added to operate on programs only.

prev p-> p& prcl psize pnew! 

pnew is an exception because it has an outside perspective.

Let functions on programs start with p for program, but should not be confused with other commands also starting with p.

Complements

Inverses or complements are each others reverse. eg 0 ! ! is 0. Let reverse functions start with !.

path, path!
pnew, pnew!

Directories/Nodes

Commands that operate with directories end in d. But so not to confuse directories with the local computers file system, I have called them nodes. Notice that the command names have parallels with Unix. Indeed Unix has influenced this programming language here.

Example of directory/node commands. eg

pushd popd depthd pwd cd 

Object Oriented Programming

Is rpn an object oriented programming language? The short answer is yes - it can be.

Firstly the standard use of rpn is as a functional language where you move around the nodes and call functions that operate on the nodes data. The functions are static in that they do something on data, - the functions themselves do not change. This is not Object Orientated Programming (henceforth abbreviated to OOP).

What OOP is is open to interpretation, so the following is my opinion on what its about, so even if you think you know what it is its worth having a listen to what I have to say.

The combining of data and function together is the single biggest advancement in software and enabled the software revolution. It forms hierachies and associates meaning by functions to data. In short functions and data together in the one unit are grouped.

Other technologies evolved (virtual functions), but the software revolution in theory could have continued without them.

Since this is a user manual I will spare you as much as possible from thinking and narrow my thoughts to OOP in rpn.

Consider { } as an OOP object

The rpnprogram { } data type meets the requirements of grouping functions and data together.

{ } has variables or a state and a body which is executed when the program is behaving like a function.

The language also provides features to move into a program and effect its state by evaluating its variables and editing its stack. The variables themselves can be function and provide the objects interface.

The tree structure of rpn programs