Nibl is a Forth VM that aims to be simple to understand, extend and embed; it currently weighs in at around 2kloc.
Nibl requires a C++ compiler and CMake to build.
cd nibl
mkdir build
cd build
cmake ..
make
./nibl help
Nibl v13
Usage: nibl [command] [file1.nl] [file2.nl]
Commands:
dump Dump VM code
eval Evaluate and exit
repl Start REPL
v Print version and exit
Duplicate a.
Remove a.
Swap a and b.
Rotate top three values left.
Rotate top three values right.
Nibl only supports integer arithmetics so far.
c is b added to a.
c is b subtracted from a.
c is a multiplied by b.
c is a divided by b.
c is the remainder from dividing a by b.
Strings are enclosed in double quotes.
b is the string representation of a.
42 to-str
["42"]
b is the integer value parsed from a, or F on failure.
c is the remaining string on success.
"42" parse-int
[42 ""]
"foo" parse-int
[F]
Values may be interpolated into strings by embedding %.
A single % interpolates the top stack value.
1 2 "foo % bar % baz"
["foo 2 bar 1 baz"]
Arbitrary expressions may be interpolated using %{...}.
"foo %{1 2 +} bar"
["foo 3 bar"]
\% may be used to escape interpolation.
42 "foo % bar \%"
["foo 42 bar %"]
Booleans can be either true (T) or false (F).
Any value except F, regardless of type, is true.
b is true if a is false, else false.
c is false if a is false, else the result of evaluating b.
T and: 3
[3]
F and: 3
[F]
c is true if a is true, else the result of evaluating b.
T or: 3
[T]
F or: 3
[3]
c is true if a and b are equal, else false.
1 3 =
[F]
3 3 =
[T]
T F =
[F]
T T =
[T]
c is true if a is less than b, else false.
c is true if a is greater than b, else false.
Skip evaluation until ; or else: is reached or the program ends when a is false.
T if: 1 2; 3
[1 2 3]
F if: 1 2; 3
[3]
T if: 1 else: 2; 3
[1 3]
F if: 1 else: 2; 3
[2 3]
20 dup 10 = if: pop 1 else: dup 20 = if: pop 2 else: pop 3;;
[2]
Values may be bound to names using def:.
def: foo 42
[]
foo
[42]
Functions are first class and may be defined using fun:, and subsequently called using call.
fun: 1 2 3;
[Fun(repl@1:1)]
call
[1 2 3]
Every value has one of the following types:
BoolThe type of booleansCharThe type of charactersFunThe type of functionsIntThe type of numbersLibThe type of librariesMacroThe type of macrosMetaThe type of typesPrimThe type of primitivesStrThe type of strings
b is the type of a.
1 type-of
[Int]
type-of
[Meta]
References may be captured using &.
1 2 3 dup
[1 2 3 3]
1 2 3 &dup
[1 2 3 dup]
Evaluate code in file a.
foo.nl
1 2 3
"foo.nl" load
[1 2 3]
Print a followed by newline to stdout.
Print a to stdout and read line from stdin into b.
Tracing may be toggled using trace.
trace
0 STOP
[]
1 2 3 dup
2 PUSH_INT1 1
4 PUSH_INT1 2
6 PUSH_INT1 3
8 DUP
10 STOP
[1 2 3 3]
test: may be used to write tests, it compares the contents of the stack before and after running the code.
1 2 test: 1 2 3;
Test failed, expected: [1 2], actual: [1 2 3]
[]
1 2 3 test: 1 2 3;
Test ok: [1 2 3]
[]
Nibl comes with a regression test suite.
./nibl ../tests.nl
Test ok: [1 2 2]
...
bench: evaluates its body specified number of repetitions and measures elapsed time in milliseconds.
def: fib fun:
dup 1 > if:
dec dup fib swap
dec fib +;;
[]
1000 bench: 20 fib pop;
[1120]
python bench/fib.py
1004
def: fib fun:
rotr dup 1 > if:
dec rotl dup rotl + rec
else:
1 = if: swap;
pop;;
[]
100000 bench: 100 0 1 fib pop;
[962]
python bench/fibt.py
657