nineties/planckforth

Bootstrapping a Forth interpreter from hand-written tiny ELF binary. Just for fun.

Forth

374

560 commits

updated Aug 18, 2022

See the code

README

PlanckForth: Bootstrapping an Interpreter from Handwritten 1KB Binary

This project aims to bootstrap a Forth interpreter from hand-written tiny (1KB) ELF binary. This is just for fun. No practical use.

How to build

Only xxd is needed to build PlanckForth.

$ git clone https://github.com/nineties/planckforth.git
$ cd planckforth
$ make
xxd -r -c 8 planck.xxd > planck
chmod +x planck

Implementations in other languages are in others.

Hello World

The hello world program at the beginning looks like this.

$ ./planck
kHtketkltkltkotk tkWtkotkrtkltkdtk!tk:k0-tk0k0-Q

After bootstrapping by bootstrap.fs, it looks like this.

$ ./planck < bootstrap.fs
." Hello World!" cr

bootstrap.fs can also take a file as an input program like this.

$ cat example/fib.fs
: fib dup 2 < unless 1- dup recurse swap 1- recurse + then ;
20 fib . cr
$ ./planck < bootstrap.fs example/fib.fs
6765

Running Tests

$ make test

Builtin Words

codenamestack effectsemantics
Qquit( n -- )Exit the process
Ccell( -- n )The size of Cells
h&here( -- a-addr )The address of 'here' cell
l&latest( -- a-addr )The address of 'latest' cell
kkey( -- c )Read character
ttype( c -- )Print character
jjump( -- )Unconditional branch
J0jump( n -- )Jump if a == 0
ffind( c -- xt )Get execution token of c
xexecute( xt -- ... )Run the execution token
@fetch( a-addr -- w )Load value from addr
!store( w a-addr -- )Store value to addr
?cfetch( c-addr -- c )Load byte from addr with sign extension
$cstore( c c-addr -- )Store byte to addr
ddfetch( -- a-addr )Get data stack pointer
Ddstore( a-addr -- )Set data stack pointer
rrfetch( -- a-addr )Get return stack pointer
Rrstore( a-addr -- )Set return stack pointer
idocol( -- a-addr )Get the code pointer of interpreter
eexit( -- )Exit current function
Llit( -- n )Load immediate
Slitstring( -- c-addr )Load string literal
+add( a b -- c )c = (a + b)
-sub( a b -- c )c = (a - b)
*mul( a b -- c )c = (a * b)
/divmod( a b -- c d )c = (a mod b), d = (a / b)
&and( a b -- c )c = (a & b)
|or( a b -- c )c = (a | b)
^xor( a b -- c )c = (a ^ b)
<less( a b -- c )c = (a < b)
uuless( a b -- c )c = (a unsigned< b)
=equal( a b -- c )c = (a == b)
(shl( a b -- c )c = a << b (logical)
)shr( a b -- c )c = a >> b (logical)
%sar( a b -- c )c = a >> b (arithmetic)
vargv( -- a-addr u )argv and argc
Vversion( -- c-addr )Runtime infomation string

Binary Layout

binary layout

Implementations

Implementation of runtimebuildtest status
Handwritten ELF binary for i386-linuxmaketesting i386-linux-handwritten
Cmake ctesting c
Python 3.xmake pythontesting python

Benchmarks

See Wiki/Benchmarks

Contributors

nineties

554 commits

eblanton

3 commits

qartis

2 commits

wasserfuhr

1 commits

nineties/planckforth

Bootstrapping a Forth interpreter from hand-written tiny ELF binary. Just for fun.

Forth

374

560 commits

updated Aug 18, 2022

See the code

README

PlanckForth: Bootstrapping an Interpreter from Handwritten 1KB Binary

This project aims to bootstrap a Forth interpreter from hand-written tiny (1KB) ELF binary. This is just for fun. No practical use.

How to build

Only xxd is needed to build PlanckForth.

$ git clone https://github.com/nineties/planckforth.git
$ cd planckforth
$ make
xxd -r -c 8 planck.xxd > planck
chmod +x planck

Implementations in other languages are in others.

Hello World

The hello world program at the beginning looks like this.

$ ./planck
kHtketkltkltkotk tkWtkotkrtkltkdtk!tk:k0-tk0k0-Q

After bootstrapping by bootstrap.fs, it looks like this.

$ ./planck < bootstrap.fs
." Hello World!" cr

bootstrap.fs can also take a file as an input program like this.

$ cat example/fib.fs
: fib dup 2 < unless 1- dup recurse swap 1- recurse + then ;
20 fib . cr
$ ./planck < bootstrap.fs example/fib.fs
6765

Running Tests

$ make test

Builtin Words

codenamestack effectsemantics
Qquit( n -- )Exit the process
Ccell( -- n )The size of Cells
h&here( -- a-addr )The address of 'here' cell
l&latest( -- a-addr )The address of 'latest' cell
kkey( -- c )Read character
ttype( c -- )Print character
jjump( -- )Unconditional branch
J0jump( n -- )Jump if a == 0
ffind( c -- xt )Get execution token of c
xexecute( xt -- ... )Run the execution token
@fetch( a-addr -- w )Load value from addr
!store( w a-addr -- )Store value to addr
?cfetch( c-addr -- c )Load byte from addr with sign extension
$cstore( c c-addr -- )Store byte to addr
ddfetch( -- a-addr )Get data stack pointer
Ddstore( a-addr -- )Set data stack pointer
rrfetch( -- a-addr )Get return stack pointer
Rrstore( a-addr -- )Set return stack pointer
idocol( -- a-addr )Get the code pointer of interpreter
eexit( -- )Exit current function
Llit( -- n )Load immediate
Slitstring( -- c-addr )Load string literal
+add( a b -- c )c = (a + b)
-sub( a b -- c )c = (a - b)
*mul( a b -- c )c = (a * b)
/divmod( a b -- c d )c = (a mod b), d = (a / b)
&and( a b -- c )c = (a & b)
|or( a b -- c )c = (a | b)
^xor( a b -- c )c = (a ^ b)
<less( a b -- c )c = (a < b)
uuless( a b -- c )c = (a unsigned< b)
=equal( a b -- c )c = (a == b)
(shl( a b -- c )c = a << b (logical)
)shr( a b -- c )c = a >> b (logical)
%sar( a b -- c )c = a >> b (arithmetic)
vargv( -- a-addr u )argv and argc
Vversion( -- c-addr )Runtime infomation string

Binary Layout

binary layout

Implementations

Implementation of runtimebuildtest status
Handwritten ELF binary for i386-linuxmaketesting i386-linux-handwritten
Cmake ctesting c
Python 3.xmake pythontesting python

Benchmarks

See Wiki/Benchmarks

Contributors

nineties

554 commits

eblanton

3 commits

qartis

2 commits

wasserfuhr

1 commits

Languages

Forth

91.2%

Python

4.1%

C

3.4%