retroforth/doc/Python-VM.md

65 lines
2.3 KiB
Markdown
Raw Normal View History

# Nga in Python
Nga in Python has a number of important differences from the standard
reference implementation in C.
## Cell Size
Typically cells are either 32-bit or 64-bit signed integers. Python
supports infinite precision, so this implementation does not restrict
the cell size.
It should be noted that Retro provides two words (`n:MIN` and `n:MAX`)
for determining the limits. This is used by part of the floating point
device, and may be checked by user code. The Python implementation
reports these as if the cell size is 128 bits. In the standard system,
this will only affect the use of floating point values when encoded to
normal cells.
The use of unlimited precision will be an issue if you try to save
images to run on other VM instances. At this point I'm not implementing
saving of images under the Python implementation, so this isn't a
problem currently, but if you add this, make sure to truncate the
cells to 32 or 64 bits.
## Performance
The Python implementation is slower than the C one. To help compensate
for this, it makes use of a number of techniques.
The VM detects when certain words are being called and will instead
execute native Python functions.
Of note, this currently traps and replaces:
- s:length
- s:eq?
- s:to-number
- d:lookup
String operations are slow, so providing faster length, equality,
and conversion to numbers provides a very noticable performance
improvement.
Dictionary lookups are also slow. In Retro, each token has two
lookups associated with it. First, Retro checks to see if the first
character is a `prefix:` word. If this is not the case, then the
dictionary is searched for the entire token.
The Python implementation now caches the entire dictionary as a
native dict(), and searches this instead. As with string operations,
this provides a substantial improvement.
To keep up to date, the VM also notices when `d:add-header` is
called and adds an entry to the dict before running the Retro word to
actually create the header.
It will also add entries to the dict if the overridden `d:lookup`
fails to find an entry in the cached set.
If you want to override an additional word:
- in `cached_words()`, add an entry to map in the word
- in `execute()` add an `elif` handler for the word
- make sure the `elif` handler ends in `self.ip = self.address.pop()'