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
|
Write You a Forth, 0x08
|
||||||
-----------------------
|
-----------------------
|
||||||
|
|
||||||
:date: 2018-03-01 19:31
|
:date: 2018-03-05 21:42
|
||||||
:tags: wyaf, forth
|
:tags: wyaf, forth
|
||||||
|
|
||||||
After reading some more in Threaded Interpreted Languages (TIL_ from now on),
|
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__ */
|
#endif /* __KF_WORD_H__ */
|
||||||
|
|
||||||
The codeword is the big changer here. I've put a native evaluator and
|
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__
|
#ifndef __KF_EVAL_H__
|
||||||
#define __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.
|
``nexec`` just casts its target to a void function and calls it.
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
void
|
void
|
||||||
nexec(uintptr_t target)
|
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
|
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.
|
case of native execution, this is a pointer to a function.
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
void
|
void
|
||||||
cwexec(uintptr_t entry)
|
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::
|
up a word by name and execute it::
|
||||||
|
|
||||||
void
|
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);
|
store_native(dict+last, name, len, target);
|
||||||
}
|
}
|
||||||
|
@ -341,6 +345,41 @@ up a word by name and execute it::
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
void
|
||||||
store_native(uint8_t *entry, const char *name, const uint8_t len, void(*target)(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));
|
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
|
bool
|
||||||
match_word(uint8_t *entry, const char *name, const uint8_t len)
|
match_word(uint8_t *entry, const char *name, const uint8_t len)
|
||||||
{
|
{
|
||||||
|
@ -372,6 +414,10 @@ up a word by name and execute it::
|
||||||
return true;
|
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
|
size_t
|
||||||
word_link(uint8_t *entry)
|
word_link(uint8_t *entry)
|
||||||
{
|
{
|
||||||
|
@ -390,3 +436,6 @@ up a word by name and execute it::
|
||||||
return 2 + entry[0] + sizeof(size_t);
|
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 "word.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -18,11 +19,18 @@ main(void)
|
||||||
dstack_push(2);
|
dstack_push(2);
|
||||||
dstack_push(3);
|
dstack_push(3);
|
||||||
|
|
||||||
uint8_t arena[128] = {0};
|
append_native_word("hello", 5, hello);
|
||||||
uintptr_t arena_p = (uintptr_t)arena;
|
uintptr_t hwb = 0;
|
||||||
store_native(arena, "hello", 5, hello);
|
|
||||||
|
|
||||||
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");
|
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;
|
static size_t last = 0;
|
||||||
|
|
||||||
void
|
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);
|
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
|
void
|
||||||
store_native(uint8_t *entry, const char *name, const uint8_t len, void(*target)(void))
|
store_native(uint8_t *entry, const char *name, const uint8_t len, void(*target)(void))
|
||||||
{
|
{
|
||||||
uintptr_t target_p = (uintptr_t)target;
|
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 */
|
/* write the header */
|
||||||
entry[0] = len;
|
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));
|
memcpy(entry+2+len, &link, sizeof(link));
|
||||||
|
|
||||||
/* write the native executor codeword and the function pointer */
|
/* write the native executor codeword and the function pointer */
|
||||||
memcpy(entry, (uint8_t *)(&nexec_p), sizeof(uintptr_t));
|
memcpy(entry+offset, (uint8_t *)(&nexec_p), sizeof(uintptr_t));
|
||||||
memcpy(entry + sizeof(uintptr_t), (uint8_t *)(&target_p), sizeof(uintptr_t));
|
offset += sizeof(uintptr_t);
|
||||||
|
memcpy(entry+offset, (uint8_t *)(&target_p), sizeof(uintptr_t));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
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.
|
* 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
|
* store_native writes a new dictionary entry for a native-compiled
|
||||||
|
|
Loading…
Reference in New Issue