misc/kforth: Fix a bunch of bugs from part 0x08.
This commit is contained in:
parent
960587baa3
commit
cf3a0f426c
|
@ -1,7 +1,7 @@
|
|||
Write You a Forth, 0x08
|
||||
-----------------------
|
||||
|
||||
:date: 2018-03-01 19:31
|
||||
:date: 2018-03-05 21:42
|
||||
:tags: wyaf, forth
|
||||
|
||||
After reading some more in Threaded Interpreted Languages (TIL_ from now on),
|
||||
|
@ -199,7 +199,7 @@ Reading TIL has given me some new ideas on how to implement words::
|
|||
#endif /* __KF_WORD_H__ */
|
||||
|
||||
The codeword is the big changer here. I've put a native evaluator and
|
||||
a codeword executor in the ``eval`` files:
|
||||
a codeword executor in the ``eval`` files::
|
||||
|
||||
#ifndef __KF_EVAL_H__
|
||||
#define __KF_EVAL_H__
|
||||
|
@ -238,6 +238,8 @@ The implementations of these are short::
|
|||
|
||||
``nexec`` just casts its target to a void function and calls it.
|
||||
|
||||
::
|
||||
|
||||
void
|
||||
nexec(uintptr_t target)
|
||||
{
|
||||
|
@ -248,6 +250,8 @@ The implementations of these are short::
|
|||
is the executor, and the next is the start of the code body. In the
|
||||
case of native execution, this is a pointer to a function.
|
||||
|
||||
::
|
||||
|
||||
void
|
||||
cwexec(uintptr_t entry)
|
||||
{
|
||||
|
@ -317,7 +321,7 @@ adds a new word to the dictionary, and the second attempts to look
|
|||
up a word by name and execute it::
|
||||
|
||||
void
|
||||
append_word(const char *name, const uint8_t len, void(*target)(void))
|
||||
append_native_word(const char *name, const uint8_t len, void(*target)(void))
|
||||
{
|
||||
store_native(dict+last, name, len, target);
|
||||
}
|
||||
|
@ -340,7 +344,42 @@ up a word by name and execute it::
|
|||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Actually, now that I think about it, maybe I should also add in a function
|
||||
to return a uintptr_t to the word, too. Should this point to the header or
|
||||
to the body? My first instinct is to point to the header and have the caller
|
||||
(me) use ``word_body`` to get the actual body. That being said, however,
|
||||
we already have the useful information from the header (namely, the name and
|
||||
length); the link is only useful for the search phase. Following this logic
|
||||
means that ``lookup`` will return a pointer to the body. So say we all::
|
||||
|
||||
bool
|
||||
lookup(const char *name, const uint8_t len, uintptr_t *ptr)
|
||||
{
|
||||
size_t offset = 0;
|
||||
size_t body = 0;
|
||||
while (true) {
|
||||
if (!match_word(dict+offset, name, len)) {
|
||||
if ((offset = word_link(dict+offset)) == 0) {
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
body = word_body(dict+offset);
|
||||
*ptr = (uintptr_t)(dict + offset + body);
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
The rest of the functions in the header (all of which are publicly
|
||||
visible) are made available for use later. Maybe (but let's be honest,
|
||||
probably not) I'll go back later and make these functions private.
|
||||
|
||||
The first such function stores a native (built-in) word. This is what
|
||||
``append_native_word`` is built around::
|
||||
|
||||
void
|
||||
store_native(uint8_t *entry, const char *name, const uint8_t len, void(*target)(void))
|
||||
{
|
||||
|
@ -358,6 +397,9 @@ up a word by name and execute it::
|
|||
memcpy(entry + sizeof(uintptr_t), (uint8_t *)(&target_p), sizeof(uintptr_t));
|
||||
}
|
||||
|
||||
The rest of the functions are utility functions. ``match_word`` is used
|
||||
to... match words::
|
||||
|
||||
bool
|
||||
match_word(uint8_t *entry, const char *name, const uint8_t len)
|
||||
{
|
||||
|
@ -371,6 +413,10 @@ up a word by name and execute it::
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
Finally, ``word_link`` returns the offset to the next function (e.g. so
|
||||
as to be able to do ``entry+offset``) and ``word_body`` returns the offset
|
||||
to the body of the word::
|
||||
|
||||
size_t
|
||||
word_link(uint8_t *entry)
|
||||
|
@ -390,3 +436,6 @@ up a word by name and execute it::
|
|||
return 2 + entry[0] + sizeof(size_t);
|
||||
}
|
||||
|
||||
|
||||
That about wraps up this chunk of work. Next to maybe start porting builtins? I
|
||||
also need to rewrite the parser and I/O layer.
|
||||
|
|
16
kf.c
16
kf.c
|
@ -4,6 +4,7 @@
|
|||
#include "word.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
void
|
||||
|
@ -18,11 +19,18 @@ main(void)
|
|||
dstack_push(2);
|
||||
dstack_push(3);
|
||||
|
||||
uint8_t arena[128] = {0};
|
||||
uintptr_t arena_p = (uintptr_t)arena;
|
||||
store_native(arena, "hello", 5, hello);
|
||||
append_native_word("hello", 5, hello);
|
||||
uintptr_t hwb = 0;
|
||||
|
||||
cwexec(arena_p);
|
||||
if (!lookup("hello", 5, &hwb)) {
|
||||
fprintf(stderr, "failed to lookup 'hello'\n");
|
||||
exit(1);
|
||||
}
|
||||
printf("hello: 0x%lx\n", (unsigned long)hwb);
|
||||
if (!execute("hello", 5)) {
|
||||
fprintf(stderr, "failed to execute 'hello'\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
printf("finished\n");
|
||||
}
|
||||
|
|
30
word.c
30
word.c
|
@ -8,7 +8,7 @@ static uint8_t dict[DICT_SIZE] = {0};
|
|||
static size_t last = 0;
|
||||
|
||||
void
|
||||
append_word(const char *name, const uint8_t len, void(*target)(void))
|
||||
append_native_word(const char *name, const uint8_t len, void(*target)(void))
|
||||
{
|
||||
store_native(dict+last, name, len, target);
|
||||
}
|
||||
|
@ -32,11 +32,32 @@ execute(const char *name, const uint8_t len)
|
|||
}
|
||||
}
|
||||
|
||||
bool
|
||||
lookup(const char *name, const uint8_t len, uintptr_t *ptr)
|
||||
{
|
||||
size_t offset = 0;
|
||||
size_t body = 0;
|
||||
while (true) {
|
||||
if (!match_word(dict+offset, name, len)) {
|
||||
if ((offset = word_link(dict+offset)) == 0) {
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
body = word_body(dict+offset);
|
||||
*ptr = (uintptr_t)(dict + offset + body);
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
store_native(uint8_t *entry, const char *name, const uint8_t len, void(*target)(void))
|
||||
{
|
||||
uintptr_t target_p = (uintptr_t)target;
|
||||
size_t link = 2 + len + (2 * sizeof(uintptr_t));
|
||||
size_t offset = 2 + len + sizeof(size_t);
|
||||
size_t link = offset + (2 * sizeof(uintptr_t));
|
||||
|
||||
/* write the header */
|
||||
entry[0] = len;
|
||||
|
@ -45,8 +66,9 @@ store_native(uint8_t *entry, const char *name, const uint8_t len, void(*target)(
|
|||
memcpy(entry+2+len, &link, sizeof(link));
|
||||
|
||||
/* write the native executor codeword and the function pointer */
|
||||
memcpy(entry, (uint8_t *)(&nexec_p), sizeof(uintptr_t));
|
||||
memcpy(entry + sizeof(uintptr_t), (uint8_t *)(&target_p), sizeof(uintptr_t));
|
||||
memcpy(entry+offset, (uint8_t *)(&nexec_p), sizeof(uintptr_t));
|
||||
offset += sizeof(uintptr_t);
|
||||
memcpy(entry+offset, (uint8_t *)(&target_p), sizeof(uintptr_t));
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
3
word.h
3
word.h
|
@ -19,6 +19,9 @@
|
|||
* The body of a native word points to a function that's compiled in already.
|
||||
*/
|
||||
|
||||
void append_native_word(const char *, const uint8_t, void(*)(void));
|
||||
bool execute(const char *, const uint8_t);
|
||||
bool lookup(const char *, const uint8_t, uintptr_t *);
|
||||
|
||||
/*
|
||||
* store_native writes a new dictionary entry for a native-compiled
|
||||
|
|
Loading…
Reference in New Issue