ni (a <> b) == ni a <> ni b§
Ni is a stack-based, concatenative programming language. Here's how it works:
A program is a list of values.
A value is either:
- an integer:
- a double:
- a boolean:
- a character:
- a string:
- a symbol:
- a list of values:
[a b c ...],
- or an environment.
A program is evaluated by converting every value to an action and running the actions, one after another, inside a context.
A context consists of a stack of values (the stack) and a stack of environments.
An environment is a map from symbols to actions.
A literal value pushes itself onto the stack.
Everything that is not a symbol is a literal value.
A symbol prefixed with a backslash (
\) is literal (the backslash is removed).
A symbol prefixed with a dollar sign (
$) pops a literal value from the stack and binds it to that symbol (with the dollar sign removed) in the topmost environment. A dollar sign on its own pops a value from the stack without binding it to a symbol.
Any other symbol has the action bound to it in the topmost environment in which it is bound.
The following program prints the first ten Fibonacci numbers:
\fib [ $n 0 1 [ $x $y x y x + ] n times const ] define 0 $i [ i fib print i increment $i ] 10 times
The following primitives are available.
Unless otherwise specified,
f means that
a from the stack, then pops
b from the stack, then pushes the result onto the stack, if any.
actionin the current context. Lists are evaluated as programs. Symbols are evaluated by making them into a singleton list, for convenience.
definebinds the symbol
nameto the action of evaluating
namein the topmost environment.
newcreates a new empty environment with name
envonto the stack of environments.
unusepops an environment from the stack of environments and pushes it onto the stack.
/=are the usual equality and inequality functions, respectively. Equality is defined trivially on all values except environments: two environments are equal if they have the same name.
orare the usual boolean operations.
cond yes no
^are the usual addition, subtraction, multiplication, division and exponentiation operations, respectively, defined on both integers and doubles.
+is also concatenation on strings and lists.
ais the empty string or list.
vto the list or string
unconsbreaks the list or string
vsinto its head and its tail, pushes its tail onto the stack, then pushes its head onto the stack.
vto standard output. Strings and characters are printed literally, other values are converted to their string representation.
printStackprints the stack, represented as a list, to standard output.
getCharreads a character from standard input.
getLinereads a line from standard input.
exitterminates the program successfully.
src/stdlib.ni is evaluated when Ni starts.
Comments are introduced with
#, and extend to the end of the line.
Numbers are parsed as an optional plus or minus sign, a (possibly empty) integer part, optionally followed by a dot and a (possibly empty) fractional part. One of the integer part or the fractional part must be non-empty. If a dot is present, the number is a Double, otherwise it is an Integer.
Character and string literals are parsed as Haskell literals.
Symbols are any sequence of non-whitespace, non-
 characters that fail to parse as any other type of value.
Environments have an output representation (
<environment NAME>), but no input representation.
Ni's syntax and core principles were largely suggested to me by nitrix.