phorth is a bootstrapped forth-like language where instead of writing our
primitive words in some assembler, we have chosen to write them in a mix of
CPython bytecode and C++*. By using a superset of CPython bytecode, we may
interpret our phorth programs with an unmodified version of CPython using
the same interpreter loop that handles normal python code objects.
* C++ is used where the VM is too restrictive. This is mainly used to handle dynamic jumps which there is no opcode for in CPython.
phorth exists because I wanted to attempt to use the CPython virtual machine
for a language that was not Python. Forth seemed like a decent candidate because
of the stack based nature of the VM; however, the CPython machine's model is
radically different which led to a lot of fun hacks later.
phorth is designed to run inside of a single self modifying CPython code
object. The CPython data stack is not global, instead a new data stack is
created for each code object that is being executed. This means that there is no
simple way to write words like nip or over as Python function because
they just do stack manipulation. To get around this problem, I decided to make a
phorth 'context' a single code object. This means that phorth gets a
single data stack to be managed by the interpreter. I can also take advantage of
all of CPython instructions that work on the stack. This means that nip can
be defined in terms of CPython instructions as ROT_TWO, POP_TOP. Some
words can even be implemented as single CPython instructions, for example:
drop is just POP_TOP!
Because I am not using CPython's control stack, this is implemented as a list
stored as a local variable of the frame. The local is accessed through two
functions push_return_addr and pop_return_addr which may only be called
from a phorth context. These function inspect the calling stack frame and
manipulate the values as needed.
Some terrible things needed to happen to make this work. The whole project is hacks that are threaded together to do something cute, but below I will list some of my favorites.
Probably the most terrible of all of the hacks is that I needed to be able to
mutate the code object as it was running. This is because I treat the
co_code, which is a bytes object, as a large mutable memory segment
which acts as the phorth context's addressable memory space. In Python there
is no way to mutate bytes, which is good because they are supposed to be
immutable; (un)fortunatly there are no such restrictions in the CPython C API.
This allows us to define new words at runtime which is critical for a Forth. Forth is very deeply tied to the repl and interactive experience so we needed some way to define words on the fly.
CPython has no need for a computed jump instruction so the VM does not have
one. This is totally reasonable for Python because, obviously, Python can
compiled in such a way that a computed goto is not needed. In phorth, I
don't have as much static information so I needed to be able to jump to an
address in the code object dynamically. To implement this the phorth context
is actually a generator. This means that the code object has the
CO_GENERATOR flag set and uses YIELD_VALUE instructions to pause control
flow. The phorth context is yields int* objects which are one less than
the address to jump to. The reason it is one less that the address to jump to is
that it really works by setting context.gi_frame.f_lasti (in C++ again, this
is not mutable in Python) to the value yielded. This means that when execution
resumes, it will resume at the new location. I decided to yield the lasti
instead of the actual target because in most places the location is computed as
an offset from the current instruction pointer meaning I could subtract one from
the offset there and save another subtraction in the runner. I didn't want to
have all of my jump targets start with a POP_TOP so using the normal
generator next code would not work. The runner reimplements most of the
next function with special handling to manage the lasti assignment and
reenter the code without pushing any values onto the stack.
* There is a special case of yielding None which means resume execution on
the next instruction. For control flow this is a NOP; however, yielding
causes the state of the gi_frame object to get synced with the internal
state of PyEval_EvalFrameEx. This is needed to access the f_stacktop
inside some of the primitive phorth words defined in C++.
Direct threaded code is a model where functions are layed out as a list of
addresses of other functions, starting with a call to some machinery that starts
executing the thread. All functions need to end in some next function that
will jump to the next function in the thread.
There is a little more complexity to the computed jump machinery described
above. The context may yield a negative number which says, "derefence the
absolute value yielded and jump there, also decrement the value by 2 and push it
onto the control stack". This instruction is pretty complicated but it is
designed to make the direct threaded code model simpler. The docol procedure
starts the threading by yielding the inverse of the next instruction. This will
be seen by the runner which will dereference the value, jumping to the function
whose address appeared after the docol. It will also push addr - 2, or
really the next word's address onto the control stack. Each word defined in
phorth also ends in some exit procedure which just pops the top value
off the control stack and throws it away because it points to the address after
the function ends, afterwards it yields the top value of the control stack like
normal. This is basically using the control stack as a stack of instruction
pointers for each thread.
One side effect of this is that the co_code is really a superset of the
CPython bytecode because there are a lot of bytes that are not valid
instructions. This means that dis of the phorth context will often fail
once some words are defined.
Out of the box phorth comes with many words defined. The names are mostly taken
from forth with many omitted and some added. This list is nowhere near the list
of words required to be a compliant forth, but phorth is not aiming for
that. Like Python, words that start with _ are pseudo private, or meant for
debugging. This includes _dis which prints the output of dis on the
phorth context and _cstack which prints the control (return) stack.
Words starting with py:: are meant to help interface with the CPython
virtual machine. For example, py::getattr pops a string and an object from
the stack and calls getattr.
> words
[<Word '!': addr=412, immediate=False>,
<Word '&': addr=585, immediate=False>,
<Word "'": addr=327, immediate=False>,
<Word '(': addr=813, immediate=True>,
<Word '*': addr=637, immediate=False>,
<Word '+': addr=541, immediate=False>,
<Word ',': addr=218, immediate=False>,
<Word '-': addr=559, immediate=False>,
<Word '-rot': addr=1133, immediate=False>,
<Word '.': addr=509, immediate=False>,
<Word '.s': addr=458, immediate=False>,
<Word '/': addr=623, immediate=False>,
<Word '/mod': addr=472, immediate=False>,
<Word '0<': addr=927, immediate=False>,
<Word '0=': addr=945, immediate=False>,
<Word '0>': addr=963, immediate=False>,
<Word '0branch': addr=448, immediate=True>,
<Word '1+': addr=981, immediate=False>,
<Word '1-': addr=999, immediate=False>,
<Word '2*': addr=1017, immediate=False>,
<Word '2+': addr=1035, immediate=False>,
<Word '2-': addr=1053, immediate=False>,
<Word '2/': addr=1071, immediate=False>,
<Word '2drop': addr=1089, immediate=False>,
<Word '2dup': addr=551, immediate=False>,
<Word ':': addr=641, immediate=False>,
<Word ';': addr=775, immediate=True>,
<Word '<': addr=535, immediate=False>,
<Word '<<': addr=563, immediate=False>,
<Word '<=': addr=545, immediate=False>,
<Word '<>': addr=529, immediate=False>,
<Word '=': addr=519, immediate=False>,
<Word '>': addr=627, immediate=False>,
<Word '>=': addr=605, immediate=False>,
<Word '>>': addr=615, immediate=False>,
<Word '>cfa': addr=188, immediate=False>,
<Word '?': addr=1105, immediate=False>,
<Word '@': addr=392, immediate=False>,
<Word '[': addr=309, immediate=False>,
<Word ']': addr=318, immediate=True>,
<Word '^': addr=595, immediate=False>,
<Word '_cstack': addr=599, immediate=False>,
<Word '_dis': addr=261, immediate=False>,
<Word 'b!': addr=423, immediate=False>,
<Word 'b,': addr=231, immediate=False>,
<Word 'b@': addr=402, immediate=False>,
<Word 'branch': addr=440, immediate=True>,
<Word 'bye': addr=482, immediate=False>,
<Word 'create': addr=296, immediate=False>,
<Word 'drop': addr=493, immediate=False>,
<Word 'dup': addr=505, immediate=False>,
<Word 'exit': addr=765, immediate=False>,
<Word 'false': addr=579, immediate=False>,
<Word 'find': addr=244, immediate=False>,
<Word 'here': addr=573, immediate=False>,
<Word 'immediate': addr=801, immediate=False>,
<Word 'latest': addr=513, immediate=False>,
<Word 'matmul': addr=555, immediate=False>,
<Word 'mod': addr=633, immediate=False>,
<Word 'nip': addr=488, immediate=False>,
<Word 'none': addr=567, immediate=False>,
<Word 'noop': addr=1121, immediate=False>,
<Word 'nop': addr=501, immediate=False>,
<Word 'over': addr=434, immediate=False>,
<Word 'py::call': addr=851, immediate=False>,
<Word 'py::getattr': addr=841, immediate=False>,
<Word 'py::import': addr=831, immediate=False>,
<Word 'rot': addr=497, immediate=False>,
<Word 'swap': addr=611, immediate=False>,
<Word 'true': addr=589, immediate=False>,
<Word 'tuck': addr=1149, immediate=False>,
<Word 'word': addr=172, immediate=False>,
<Word 'words': addr=280, immediate=False>,
<Word 'xor': addr=619, immediate=False>,
<Word '|': addr=525, immediate=False>]
This is the disassembly of a base phorth context before any new words are
defined (including those in stdlib.fs). This context uses 1000 bytes of
addressable memory, which does not leave much room for user defined words. This
is not even enough to hold the whole stdlib. Some key points are that the whole
context is wrapped in a try/except to catch any errors, report them, clear
the data and control stacks, and then jump back to the top of the repl. This
allows users to mistype words and not have the program crash. Also remember
that YIELD_VALUE instructions mean jmp. There is a large segment of
NOP instructions towards the bottom (I have stripped most of them) which is
the free memory space, or memory that is not used to define the
interpreter. This is where new words will be stored or can be used as mutable
memory by the program. The size of this space is configurable with the
-m/--memory flag on the command line. It defaults to the max addressable
memory size of 2 ** 16 - 1
1 >> 0 SETUP_EXCEPT 982 (to 985)
>> 3 LOAD_CONST 0 (<built-in function push_return_addr>)
6 CALL_FUNCTION 0 (0 positional, 0 keyword pair)
9 POP_TOP
10 JUMP_ABSOLUTE 172
13 DUP_TOP
14 DUP_TOP
15 LOAD_CONST 0 (<built-in function push_return_addr>)
18 CALL_FUNCTION 0 (0 positional, 0 keyword pair)
21 POP_TOP
22 JUMP_ABSOLUTE 244
25 DUP_TOP
26 LOAD_CONST 1 (None)
29 COMPARE_OP 8 (is)
32 POP_JUMP_IF_TRUE 87
35 ROT_THREE
36 POP_TOP
37 POP_TOP
38 DUP_TOP
39 LOAD_ATTR 0 (addr)
42 LOAD_CONST 2 (True)
45 BINARY_SUBTRACT
46 LOAD_FAST 0 (immediate)
49 POP_JUMP_IF_TRUE 72
52 ROT_TWO
53 LOAD_ATTR 2 (immediate)
56 POP_JUMP_IF_TRUE 74
59 LOAD_CONST 0 (<built-in function push_return_addr>)
62 CALL_FUNCTION 0 (0 positional, 0 keyword pair)
65 POP_TOP
66 JUMP_ABSOLUTE 218
69 JUMP_ABSOLUTE 3
>> 72 ROT_TWO
73 POP_TOP
>> 74 LOAD_CONST 0 (<built-in function push_return_addr>)
77 CALL_FUNCTION 0 (0 positional, 0 keyword pair)
80 POP_TOP
81 YIELD_VALUE
82 NOP
83 NOP
84 JUMP_ABSOLUTE 3
>> 87 POP_TOP
88 LOAD_CONST 3 (<function process_lit at 0x7f05c8228620>)
91 ROT_TWO
92 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
95 DUP_TOP
96 LOAD_CONST 4 (NotImplemented)
99 COMPARE_OP 8 (is)
102 POP_JUMP_IF_TRUE 145
105 ROT_TWO
106 POP_TOP
107 LOAD_FAST 0 (immediate)
110 POP_JUMP_IF_TRUE 3
113 LOAD_CONST 5 (<built-in function append_lit>)
116 ROT_TWO
117 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
120 LOAD_CONST 6 (<built-in function comma_impl>)
123 LOAD_CONST 7 (155)
126 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
129 STORE_FAST 1 (here)
132 LOAD_CONST 6 (<built-in function comma_impl>)
135 ROT_TWO
136 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
139 STORE_FAST 1 (here)
142 JUMP_ABSOLUTE 3
>> 145 POP_TOP
146 LOAD_CONST 8 (<class 'phorth.code.UnknownWord'>)
149 ROT_TWO
150 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
153 RAISE_VARARGS 1
156 LOAD_CONST 9 (<built-in function lit_impl>)
159 LOAD_CONST 10 (<built-in function pop_return_addr>)
162 CALL_FUNCTION 0 (0 positional, 0 keyword pair)
165 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
168 UNPACK_SEQUENCE 2
171 YIELD_VALUE
>> 172 LOAD_CONST 11 (functools.partial(<built-in function next>, <generator object read_words at 0x7f05c8223db0>))
175 CALL_FUNCTION 0 (0 positional, 0 keyword pair)
178 JUMP_ABSOLUTE 181
>> 181 LOAD_CONST 10 (<built-in function pop_return_addr>)
184 CALL_FUNCTION 0 (0 positional, 0 keyword pair)
187 YIELD_VALUE
>> 188 DUP_TOP
189 LOAD_CONST 12 (<class 'phorth.Word'>)
192 LOAD_CONST 13 (<built-in function isinstance>)
195 ROT_THREE
196 CALL_FUNCTION 2 (2 positional, 0 keyword pair)
199 POP_JUMP_IF_FALSE 208
202 LOAD_ATTR 0 (addr)
205 JUMP_ABSOLUTE 181
>> 208 LOAD_CONST 14 (<class 'phorth.code.NotAWord'>)
211 ROT_TWO
212 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
215 RAISE_VARARGS 1
>> 218 LOAD_CONST 6 (<built-in function comma_impl>)
221 ROT_TWO
222 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
225 STORE_FAST 1 (here)
228 JUMP_ABSOLUTE 181
>> 231 LOAD_CONST 15 (<built-in function bcomma_impl>)
234 ROT_TWO
235 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
238 STORE_FAST 1 (here)
241 JUMP_ABSOLUTE 181
>> 244 LOAD_CONST 16 (<built-in function find_impl>)
247 ROT_TWO
248 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
251 JUMP_ABSOLUTE 181
254 LOAD_CONST 17 (<built-in function docol_impl>)
257 CALL_FUNCTION 0 (0 positional, 0 keyword pair)
260 YIELD_VALUE
261 LOAD_CONST 18 (<function dis at 0x7f05ce428378>)
264 LOAD_CONST 19 (<built-in function _getframe>)
267 CALL_FUNCTION 0 (0 positional, 0 keyword pair)
270 LOAD_ATTR 1 (f_code)
273 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
276 POP_TOP
277 JUMP_ABSOLUTE 181
280 LOAD_CONST 20 (<toolz.functoolz.Compose object at 0x7f05c81cd978>)
283 LOAD_CONST 21 (<built-in function globals>)
286 CALL_FUNCTION 0 (0 positional, 0 keyword pair)
289 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
292 POP_TOP
293 JUMP_ABSOLUTE 181
>> 296 LOAD_CONST 22 (<built-in function create_impl>)
299 ROT_TWO
300 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
303 STORE_FAST 2 (latest)
306 JUMP_ABSOLUTE 181
>> 309 LOAD_CONST 23 (False)
312 STORE_FAST 0 (immediate)
315 JUMP_ABSOLUTE 181
>> 318 LOAD_CONST 2 (True)
321 STORE_FAST 0 (immediate)
324 JUMP_ABSOLUTE 181
327 LOAD_CONST 0 (<built-in function push_return_addr>)
330 CALL_FUNCTION 0 (0 positional, 0 keyword pair)
333 POP_TOP
334 JUMP_ABSOLUTE 172
337 DUP_TOP
338 LOAD_CONST 0 (<built-in function push_return_addr>)
341 CALL_FUNCTION 0 (0 positional, 0 keyword pair)
344 POP_TOP
345 JUMP_ABSOLUTE 244
348 DUP_TOP
349 LOAD_CONST 1 (None)
352 COMPARE_OP 8 (is)
355 POP_JUMP_IF_TRUE 381
358 ROT_TWO
359 POP_TOP
360 LOAD_CONST 0 (<built-in function push_return_addr>)
363 CALL_FUNCTION 0 (0 positional, 0 keyword pair)
366 POP_TOP
367 JUMP_ABSOLUTE 188
370 JUMP_ABSOLUTE 181
373 POP_JUMP_IF_TRUE 381
376 ROT_TWO
377 POP_TOP
378 JUMP_ABSOLUTE 181
>> 381 POP_TOP
382 LOAD_CONST 8 (<class 'phorth.code.UnknownWord'>)
385 ROT_TWO
386 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
389 RAISE_VARARGS 1
392 LOAD_CONST 24 (<built-in function read_impl>)
395 ROT_TWO
396 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
399 JUMP_ABSOLUTE 181
402 LOAD_CONST 25 (<built-in function bread_impl>)
405 ROT_TWO
406 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
409 JUMP_ABSOLUTE 181
412 LOAD_CONST 26 (<built-in function write_impl>)
415 ROT_THREE
416 CALL_FUNCTION 2 (2 positional, 0 keyword pair)
419 POP_TOP
420 JUMP_ABSOLUTE 181
423 LOAD_CONST 27 (<built-in function bwrite_impl>)
426 ROT_THREE
427 CALL_FUNCTION 2 (2 positional, 0 keyword pair)
430 POP_TOP
431 JUMP_ABSOLUTE 181
434 ROT_TWO
435 DUP_TOP
436 ROT_THREE
437 JUMP_ABSOLUTE 181
>> 440 LOAD_CONST 28 (<built-in function branch_impl>)
443 ROT_TWO
444 CALL_FUNCTION 0 (0 positional, 0 keyword pair)
447 YIELD_VALUE
448 LOAD_CONST 23 (False)
451 COMPARE_OP 2 (==)
454 POP_JUMP_IF_TRUE 440
457 YIELD_VALUE
458 LOAD_CONST 1 (None)
461 YIELD_VALUE
462 LOAD_CONST 29 (<built-in function print_stack_impl>)
465 CALL_FUNCTION 0 (0 positional, 0 keyword pair)
468 POP_TOP
469 JUMP_ABSOLUTE 181
472 LOAD_CONST 30 (<built-in function divmod>)
475 ROT_THREE
476 CALL_FUNCTION 0 (0 positional, 0 keyword pair)
479 JUMP_ABSOLUTE 181
482 LOAD_CONST 31 (Done())
485 RAISE_VARARGS 1
488 ROT_TWO
489 POP_TOP
490 JUMP_ABSOLUTE 181
493 LOAD_CONST 1 (None)
496 JUMP_ABSOLUTE 181
499 COMPARE_OP 2 (==)
502 JUMP_ABSOLUTE 181
505 COMPARE_OP 1 (<=)
508 JUMP_ABSOLUTE 181
511 BINARY_MULTIPLY
512 JUMP_ABSOLUTE 181
515 PRINT_EXPR
516 JUMP_ABSOLUTE 181
519 BINARY_ADD
520 JUMP_ABSOLUTE 181
523 BINARY_SUBTRACT
524 JUMP_ABSOLUTE 181
527 ROT_TWO
528 JUMP_ABSOLUTE 181
531 BINARY_LSHIFT
532 JUMP_ABSOLUTE 181
535 LOAD_FAST 3 (cstack)
538 JUMP_ABSOLUTE 181
541 BINARY_XOR
542 JUMP_ABSOLUTE 181
545 DUP_TOP
546 JUMP_ABSOLUTE 181
549 COMPARE_OP 0 (<)
552 JUMP_ABSOLUTE 181
555 BINARY_POWER
556 JUMP_ABSOLUTE 181
559 LOAD_CONST 2 (True)
562 JUMP_ABSOLUTE 181
565 BINARY_MODULO
566 JUMP_ABSOLUTE 181
569 BINARY_AND
570 JUMP_ABSOLUTE 181
573 LOAD_CONST 23 (False)
576 JUMP_ABSOLUTE 181
579 NOP
580 JUMP_ABSOLUTE 181
583 COMPARE_OP 5 (>=)
586 JUMP_ABSOLUTE 181
589 DUP_TOP_TWO
590 JUMP_ABSOLUTE 181
593 POP_TOP
594 JUMP_ABSOLUTE 181
597 COMPARE_OP 3 (!=)
600 JUMP_ABSOLUTE 181
603 LOAD_FAST 1 (here)
606 JUMP_ABSOLUTE 181
609 ROT_THREE
610 JUMP_ABSOLUTE 181
613 BINARY_RSHIFT
614 JUMP_ABSOLUTE 181
617 BINARY_OR
618 JUMP_ABSOLUTE 181
621 LOAD_FAST 2 (latest)
624 JUMP_ABSOLUTE 181
627 COMPARE_OP 4 (>)
630 JUMP_ABSOLUTE 181
633 BINARY_TRUE_DIVIDE
634 JUMP_ABSOLUTE 181
637 BINARY_MATRIX_MULTIPLY
638 JUMP_ABSOLUTE 181
641 LOAD_CONST 0 (<built-in function push_return_addr>)
644 CALL_FUNCTION 0 (0 positional, 0 keyword pair)
647 POP_TOP
648 JUMP_ABSOLUTE 172
651 LOAD_CONST 0 (<built-in function push_return_addr>)
654 CALL_FUNCTION 0 (0 positional, 0 keyword pair)
657 POP_TOP
658 JUMP_ABSOLUTE 296
661 LOAD_CONST 32 (100)
664 LOAD_CONST 0 (<built-in function push_return_addr>)
667 CALL_FUNCTION 0 (0 positional, 0 keyword pair)
670 POP_TOP
671 JUMP_ABSOLUTE 231
674 LOAD_CONST 23 (False)
677 LOAD_CONST 0 (<built-in function push_return_addr>)
680 CALL_FUNCTION 0 (0 positional, 0 keyword pair)
683 POP_TOP
684 JUMP_ABSOLUTE 218
687 LOAD_CONST 33 (131)
690 LOAD_CONST 0 (<built-in function push_return_addr>)
693 CALL_FUNCTION 0 (0 positional, 0 keyword pair)
696 POP_TOP
697 JUMP_ABSOLUTE 231
700 LOAD_CONST 23 (False)
703 LOAD_CONST 0 (<built-in function push_return_addr>)
706 CALL_FUNCTION 0 (0 positional, 0 keyword pair)
709 POP_TOP
710 JUMP_ABSOLUTE 218
713 LOAD_CONST 2 (True)
716 LOAD_CONST 0 (<built-in function push_return_addr>)
719 CALL_FUNCTION 0 (0 positional, 0 keyword pair)
722 POP_TOP
723 JUMP_ABSOLUTE 231
726 LOAD_CONST 34 (113)
729 LOAD_CONST 0 (<built-in function push_return_addr>)
732 CALL_FUNCTION 0 (0 positional, 0 keyword pair)
735 POP_TOP
736 JUMP_ABSOLUTE 231
739 LOAD_CONST 35 (254)
742 LOAD_CONST 0 (<built-in function push_return_addr>)
745 CALL_FUNCTION 0 (0 positional, 0 keyword pair)
748 POP_TOP
749 JUMP_ABSOLUTE 218
752 LOAD_CONST 0 (<built-in function push_return_addr>)
755 CALL_FUNCTION 0 (0 positional, 0 keyword pair)
758 POP_TOP
759 JUMP_ABSOLUTE 309
762 JUMP_ABSOLUTE 181
765 LOAD_CONST 36 (<function license_impl at 0x7f05c8228840>)
768 CALL_FUNCTION 0 (0 positional, 0 keyword pair)
771 POP_TOP
772 JUMP_ABSOLUTE 181
775 LOAD_CONST 10 (<built-in function pop_return_addr>)
778 CALL_FUNCTION 0 (0 positional, 0 keyword pair)
781 POP_TOP
782 JUMP_ABSOLUTE 181
785 LOAD_CONST 37 (774)
788 LOAD_CONST 0 (<built-in function push_return_addr>)
791 CALL_FUNCTION 0 (0 positional, 0 keyword pair)
794 POP_TOP
795 JUMP_ABSOLUTE 218
798 LOAD_CONST 0 (<built-in function push_return_addr>)
801 CALL_FUNCTION 0 (0 positional, 0 keyword pair)
804 POP_TOP
805 JUMP_ABSOLUTE 318
808 JUMP_ABSOLUTE 181
811 LOAD_CONST 2 (True)
814 LOAD_FAST 2 (latest)
817 STORE_ATTR 2 (immediate)
820 JUMP_ABSOLUTE 181
>> 823 LOAD_CONST 38 (')')
826 LOAD_CONST 11 (functools.partial(<built-in function next>, <generator object read_words at 0x7f05c8223db0>))
829 CALL_FUNCTION 0 (0 positional, 0 keyword pair)
832 COMPARE_OP 2 (==)
835 POP_JUMP_IF_FALSE 823
838 JUMP_ABSOLUTE 181
841 LOAD_CONST 39 (<built-in function __import__>)
844 ROT_TWO
845 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
848 JUMP_ABSOLUTE 181
851 LOAD_CONST 40 (<built-in function getattr>)
854 ROT_THREE
855 CALL_FUNCTION 2 (2 positional, 0 keyword pair)
858 JUMP_ABSOLUTE 181
861 DUP_TOP
862 LOAD_CONST 23 (False)
865 COMPARE_OP 0 (<)
868 POP_JUMP_IF_FALSE 886
871 LOAD_CONST 41 ('nargs must be >= 0; got %s')
874 ROT_TWO
875 BINARY_MODULO
876 LOAD_CONST 42 (<class 'ValueError'>)
879 ROT_TWO
880 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
883 RAISE_VARARGS 1
>> 886 BUILD_LIST 0
889 ROT_THREE
890 ROT_THREE
891 LIST_APPEND 1
894 STORE_FAST 6 (tmp)
>> 897 DUP_TOP
898 LOAD_CONST 23 (False)
901 COMPARE_OP 2 (==)
904 POP_JUMP_IF_TRUE 924
907 LOAD_CONST 2 (True)
910 ROT_TWO
911 BINARY_SUBTRACT
912 LOAD_FAST 6 (tmp)
915 ROT_THREE
916 ROT_THREE
917 LIST_APPEND 1
920 POP_TOP
921 JUMP_ABSOLUTE 897
>> 924 POP_TOP
925 LOAD_CONST 43 (<function py_call_impl at 0x7f05c82287b8>)
928 LOAD_FAST 6 (tmp)
931 CALL_FUNCTION_VAR 0 (0 positional, 0 keyword pair)
934 JUMP_ABSOLUTE 181
937 NOP
938 NOP
...
This is where the program's free memory goes. New words will go
in this segment.
...
983 NOP
984 NOP
>> 985 POP_TOP
986 ROT_TWO
987 POP_TOP
988 LOAD_CONST 44 (<function handle_exception at 0x7f05c82282f0>)
991 ROT_TWO
992 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
995 POP_TOP
996 POP_EXCEPT
997 JUMP_ABSOLUTE 0
phorth is built with codetransformer which is a library for
manipulating CPython bytecode. It is normally used for defining trasformations
on bytecode produced by the CPython compiler; however, here it is used for the
richer definition of an instruction and the assembler.
The command line interface is built with click. Click is by far my favorite cli parsing library and I would encourage anyone building a cli to use it.
phorth is free software, available under the terms of the GNU General
Public License, version 2 or later. For
more information, see LICENSE.