A rewritten of: https://github.com/hcchengithub/project-k/wiki/Play-with-the-forth-kernel-on-python
You can play with this article online directly through the jupyter notebook binder: https://mybinder.org/v2/gh/hcchengithub/project-k/master
The small file projectk.py
from the project-k on GitHub at the same directory where you launched this jupyter notebook is the only thing we need. Project-k's purpose is to put the FORTH programming language fundamental components into a small kernel file that bridges FORTH into the host system which is Python here. Obviously projectk.js is for JavaScript. Project-k supports these two host systems so far (2018.3.15).
With the project-k kernel, it takes only 15 minutes to build your own FORTH system. So when you need an user interface to communicate with your machine, FORTH is a remarkable choice.
Now let's create a project-k vm object:
In [1]:
import projectk as vm # vm means 'Virtual Machine'.
The above python statement created an instance of project-k object.
In [2]:
vm.dictate("123").stack
Out[2]:
vm.dictate()
method is the way project-k VM receives your commands (in a multiple lines string). It actually is also the way we feed it an entire Forth source code file. "123"
means: "please push this number into the FORTH data stack". vm.stack
is the Forth VM data stack which was empty at first and now has one item, 123, that we've just put in it. Some methods as vm.dictate()
returns the vm object itself so we can cascade multiple function calls in one line. Therefore the above statement vm.dictate("123").stack
is cascated from the two lines:
vm.dictate("123")
vm.stack
In [3]:
vm.dictate("code hi print('Hello World!!') end-code hi");
A code word is defined between code
and end-code
. The first token after the leading code
, which is 'hi' in this example, is the name of the new FORTH word. All the rests down to the ending end-code
are python statements. Note after end-code
above we execute hi
the new word immediately and it works!
In [4]:
vm.dictate("code words print([w.name for w in words['forth'][1:]]) end-code words");
Where the vm.words
that appears in above definition is project-k vm's word-list that is a common component of a FORTH system. We can see it this way:
In [5]:
vm.words
Out[5]:
In [6]:
vm.dictate("code + push(pop(1)+pop()) end-code");
The vm knows how to do the '+' now, let's try:
In [7]:
vm.stack=[] # clear the data stack
vm.dictate("123 456 +").stack
Out[7]:
FORTH is a programming language of postfix-expression. "123 456 +" means: "Please push 123 into the data stack, please push 456 too. Now please get the top two cells out of the data stack, add them and push the result back to the data stack". The result is 579 left in the data stack.
The common FORTH word to view its data stack is .s
, we can have it by this definition:
In [8]:
vm.dictate("code .s print(stack) end-code");
vm.execute('.s');
Another FORTH word that quotes a text string is s"
that can be defined like this:
In [9]:
vm.dictate('code s" push(nexttoken(\'"\'));nexttoken() end-code');
The FORTH term TIB (terminal input buffer) is simply the string given from vm.dictate('I am the TIB')
. nexttoken()
is a project-k built-in function that gets a quote out of the TIB from the current position to the given delimiter which is "
in the example above. So FORTH strings can now be expressed by s" this is a string"
and that will be pushed to the FORTH data stack. Let's use it:
In [10]:
# Put a string onto the TOS (Top of the data stack)
vm.dictate('s" The wise build bridges, " .s');
In [11]:
vm.dictate('s" while the foolish build barriers." .s');
and according to the +
word we defined above, it can concatenate two strings too. So let's execute +
and check the result on the data stack:
In [12]:
vm.execute('+').execute('.s');
The two strings are correctly concatenated into one.
In [13]:
# list all global functions and global variables seen in a code word definition.
print([propertie for propertie in dir(vm) if not propertie.startswith('__')])
In [14]:
# List only functions out of the aboves
print([method for method in dir(vm) if callable(getattr(vm,method))])
Project-k built-in functions are explained with comments in the projectk.py
source code. View them by using the python help()
function.
In [15]:
help(vm.tos)
help(vm.pop)
help(vm.push)
help(vm.nexttoken)
In [16]:
vm.dictate("code see-locals print(locals()) end-code see-locals");
The _me
object points to the new word itself. For example, this word prints its own name:
In [17]:
vm.dictate("code who-am-I? print('My name is: ' + _me.name) end-code").execute('who-am-I?');
In [18]:
vm.dictate("code see-globals print(globals().keys()) end-code see-globals");
The __name__
is 'projectk'
, as shown below, that indicates that the namespace of this small world is within the FORTH virtual machine. We can't see anything in the outside world unless vm.push()
passes them into the data stack.
In [19]:
vm.dictate("code see__name__ print(globals()['__name__']) end-code see__name__");
For example, this jupyter notebook itself is the main program we are running and through vm.push()
we can pass this information into the FORTH vm and get it back by vm.pop()
:
In [20]:
vm.push(__name__).pop()
vm.push(__IPYTHON__).pop() # see another property from the main program
Out[20]:
Out[20]:
List of all project-k global variables and built-in functions can be found at the end of this page on project-k project wiki on GitHub.
You can start building your own FORTH system now. Don't hesitate to let me know anything that is unclear above.
H.C. Chen @ FitTaiwan
hcchen5600@gmail.com
2018.3.15