An introduction to the project-k FORTH kernel

project-k is a very small FORTH programming language kernel supporting Javascript and Python open-sourced on GitHub https://github.com/hcchengithub/project-k. We are going to use this FORTH kernel to build our own tiny FORTH programming language system.

Read only

Read this tutorial on GitHub https://github.com/hcchengithub/project-k/blob/master/notebooks/tutor.ipynb

How to play

Use an online Jupyter Notebook, I recommend notebooks.ai while , Microsoft Azure Notebooks, and more are available out there, you don't need to install anything. Click [Download Zip] form GitHub project-k. We only need this notebook, notebooks\tutor.ipynb, and project-k source code file for Python, projectk.py that has only 20k bytes includes a lot of comments. Choose an online Jupyter notebook you like, create an acount, upload the minimum two files, double click or run this notebook tutor.ipynb and start playing.

Import the FORTH kernel

The python statement below imports projectk.py and gives it an arbitrary nick name vm. As shown below we can see that vm is a python module and it is an instance of projectk.py which is our FORTH kernel.


In [1]:
# In case you are not familiar with Jupyter Notebook, click here and press Ctrl+Enter to run this cell.
import projectk as vm
vm


Out[1]:
<module 'projectk' from 'C:\\Users\\hcche\\Downloads\\projectk.py'>

Python standard function dir(obj) gets all member names of an object. Lest's see what are in the FORTH kernel vm:


In [2]:
print(dir(vm))


['EXIT', 'RET', 'Word', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'code', 'comma', 'compiling', 'context', 'context_word_list', 'current', 'current_word_list', 'dictate', 'dictionary', 'docode', 'doendcode', 'endcode', 'execute', 'genfunc', 'genxt', 'here', 'inner', 'ip', 'isReDef', 'last', 'major_version', 'name', 'newhelp', 'newname', 'newxt', 'nextstring', 'nexttoken', 'ntib', 'order', 'outer', 'phaseA', 'phaseB', 'pop', 'push', 're', 'reset', 'rpop', 'rstack', 'rtos', 'stack', 'stop', 'sys', 'tib', 'tick', 'tos', 'vm', 'vocs', 'wordhash', 'words']

I only want you to see that there are very few properties and methods in this FORTH kernel object and many of them are conventional FORTH tokens like code, endcode, comma, compiling, dictionary, here, last, stack, pop, push, tos, rpop, rstack, rtos, tib, ntib, tick, and words.

Now let's play

The property vm.stack is the FORTH data stack which is empty at first.


In [3]:
vm.stack


Out[3]:
[]

vm.dictate() method is the way project-k VM receives your commands (a string). It actually is also the way we feed it an entire FORTH source code file. Everything given to vm.dictate() is like a command line you type to the FORTH system as simple as only a number:


In [4]:
vm.dictate("123")
vm.stack


Out[4]:
[123]

The first line above dictates project-k VM to push 123 onto the data stack and the second line views the data stack. We can even cascade these two lines into one:


In [5]:
vm.dictate("456").stack


Out[5]:
[123, 456]

because vm.dictate() returns the vm object itself.

project-k VM knows only two words 'code' and 'end-code' at first

Let's define a FORTH command (or 'word') that prints "Hello World!!":


In [6]:
vm.dictate("code hi! print('Hello World!!') end-code");  # define the "hi!" comamnd where print() is a standard python function
vm.dictate("hi!");


Hello World!!

Did you know what have we done? We defined a new FORTH code word! By the way, we can use any character in a word name except white spaces. This is a FORTH convention.

Define the 'words' command to view all words

I'd like to see what are all the words we have so far. The FORTH command 'words' is what we want now but this tiny FORTH system does not have it yet. We have to define it:


In [7]:
vm.dictate("code words print([w.name for w in vm.words['forth'][1:]]) end-code")
vm.dictate("words");


['code', 'end-code', 'hi!', 'words']

In the above definition the vm.words is a python dictionary (not FORTH dictionary) defined in the project-k VM object as a property which is something like an array of all recent words in the recent vocabulary named forth which is the only one vocabulary comes with the FORTH kernel. Where a FORTH 'vocabulary' is simply a key in python dictionary key:value pair. We have only 4 words so far as the words new command show above. Where 'code' and 'end-code' are built-in in the FORTH kernel; 'hi!' and 'words' were defined above.

Define '+' and conventional FORTH words '.s' , and 's"'

Next exercise is to define some more FORTH words.


In [8]:
vm.dictate("code + push(pop(1)+pop()) end-code");  # pop two operands from FORTH data stack and push back the result  
vm.dictate("code .s print(stack) end-code");  # print the FORTH data stack
vm.dictate('code s" push(nexttoken(\'"\'));nexttoken() end-code');  # get a string 
vm.dictate('words');  # list all recent words


['code', 'end-code', 'hi!', 'words', '+', '.s', 's"']

This example demonstrates how to use built-in methods push(), pop(), nexttoken() and the stack property (or global variable). As shown in above definitions, we can omit vm. so vm.push, vm.stack are simplified to push, stack because code ... end-code definitions are right in the VM name space. Now let's try these new words:


In [9]:
vm.stack = []  # clear the data stack
vm.dictate(' s" Forth "')  # get the string 'Forth '
vm.dictate(' s" is the easist "')  # get the string 'is the easist '
vm.dictate(' s" programming langage."')  # get the string 'programing language.'
vm.dictate('.s');  # view the data stack


['Forth ', 'is the easist ', 'programming langage.']

In [10]:
print(vm.dictate('+').stack)  # concatenate top two strings
print(vm.dictate('+').stack)  # concatenate the reset


['Forth ', 'is the easist programming langage.']
['Forth is the easist programming langage.']

The + command can certainly concatenate strings together and also can add numbers because Python's + operator works that way. Please try it with integers and floating point numbers:


In [11]:
print(vm.dictate('123 456 + ').pop());  # Push 123, push 456, add them 
print(vm.dictate('1.23 45.6 + ').pop());


579
46.83

If you want to see more examples like how to define if,else,then,for,next,begin,again and more please refer to https://github.com/hcchengithub/peforth and find peforth.f source code file.

Below tables list and explain all project-k FORTH kernel properties and methods.

Global variables and Built-in functions

Global variables (properties of the project-k object) and built-in functions (methods of the project-k object) can be seen and used in FORTH code ... end-code word definitions. You are encouraged to read the projectk.py source code directly. It's short, interesting and with a lot of comments.

Global variables

No. Global variable initial definition Description
1 vm The project-k module object.
2 wordhash = {} Forth words of recent active vocabularies. Find and get the word object through its name at the highest speed.
3 RET=None The 'ret' instruction code. It marks the end of a colon word.
4 EXIT="" The 'exit' instruction code. Same effect as 'ret' but used in colon definitions (instead of at the end of them).
5 stop = False The flag to stop the outer loop.
6 newname = "" The last new word's name.
7 newxt = function() The last new word's executable.

Above variables may not found in a traditional Forth system. Following ones are common Forth global variables you probably are very familiar with already .

No. Global variable initial definition Description
8 compiling=False The conventional flag of the Forth system state which is either compiling or interpreting.
9 ip=0 The instruction pointer. Always points to the next word when in the inner loop.
10 stack = [] The data stack.
11 rstack = [] The return stack.
12 vocs = [] The vocabulary list. e.g. ['forth','assembler', ...]
13 words = {} The word-list. e.g. words['forth'][], words['assembler'][], ... etc.
14 current = "forth" The current definition of word-list or vocabulary.
15 context = "forth" The recent top priority word-list or vocabulary.
16 order = [context] Active vocabularies and their priority order.
17 dictionary=[]; dictionary[0]=0; The Forth VM memory.
18 here=1 Index of the Form VM memory. Next free address.
19 tib="" The conventional Forth system's Terminal Input Buffer string.
20 ntib=0 The index of TIB string.

Built-in functions

No. Built-in function Description
1 dictate("commands") An exported method of the project-k VM. This is where the Forth VM receives commands from the outside world.
2 Word(a[]) The common constructor of all Forth words.
3 nextstring("delimitor") Low level tool to get next string from TIB.
4 nexttoken("delimitor") High level tool to get next string from TIB.
5 panic("msg", bool:severe) Prints the error message.
6 reset(void) Reset the Forth VM to avoid hanging up the computer.
7 isReDef("name") Check if the new word is a re-defined.
8 mytypeof(x) N/A, only needed in projectk.js
9 inner(entry, bool:resuming) The loop that runs through a colon definition as fast as possible.
10 outer(entry) The loop that walks through the command string from dictate().

Above functions may not be seen in a traditional Forth system and that's all of them. Following ones are from common Forth words you probably are very familiar with already.

No. Built-in function Description
11 current_word_list(void) Gets words[current].
12 context_word_list(void) Gets words[context].
13 tick("name") Find the word object through the given word name.
14 comma(x) Comile x into the dictionary.
15 execute(x) Execte one word. x can be a word name or a word object or simply a function.
16 tos(void or index) Get a value from the data stack w/o removing it. tos() or tos(0) is the Top of the data stack.
17 rtos(void or index) Similar to tos() but works on the return stack.
18 pop(void or index) Get and consume a value from the data stack. pop() or pop(0) is the Top of the data stack.
19 push(data,volid or index) Push a value into the data stack. push(data) or push(data,0) adds the value to the Top of the data stack.
20 type("s") N/A, for projectk.js only
21 last(void) Gets the last defined Forth word.

May the FORTH be with you!

H.C. Chen @ FigTaiwan
hcchen5600@gmail.com

─ The End ─