misc/kforth: Start command processing.

This commit is contained in:
Kyle Isom 2018-02-24 22:35:58 -08:00
parent 0a010e4527
commit 14dc525084
15 changed files with 439 additions and 49 deletions

View File

@ -3,6 +3,8 @@ CXXFLAGS := -std=$(CXXSTD) -Wall -Werror -g -O0
OBJS := linux/io.o \
io.o \
parser.o \
word.o \
dict.o \
kforth.o
TARGET := kforth
@ -13,3 +15,7 @@ $(TARGET): $(OBJS)
clean:
rm -f $(OBJS) $(TARGET)
install: $(TARGET)
cp $(TARGET) ~/bin
chmod 0755 ~/bin/$(TARGET)

178
dict.cc Normal file
View File

@ -0,0 +1,178 @@
#include "defs.h"
#include "dict.h"
#include "stack.h"
#include "system.h"
#include "word.h"
static bool
add(System *sys)
{
KF_INT a = 0;
KF_INT b = 0;
if (!sys->dstack.pop(&a)) {
return false;
}
if (!sys->dstack.pop(&b)) {
return false;
}
a += b;
return sys->dstack.push(a);
}
static bool
sub(System *sys)
{
KF_INT a = 0;
KF_INT b = 0;
if (!sys->dstack.pop(&a)) {
return false;
}
if (!sys->dstack.pop(&b)) {
return false;
}
b -= a;
return sys->dstack.push(b);
}
static bool
mul(System *sys)
{
KF_INT a = 0;
KF_INT b = 0;
if (!sys->dstack.pop(&a)) {
return false;
}
if (!sys->dstack.pop(&b)) {
return false;
}
b *= a;
return sys->dstack.push(b);
}
static bool
div(System *sys)
{
KF_INT a = 0;
KF_INT b = 0;
if (!sys->dstack.pop(&a)) {
return false;
}
if (!sys->dstack.pop(&b)) {
return false;
}
b *= a;
return sys->dstack.push(b);
}
static bool
swap(System *sys)
{
KF_INT a = 0;
KF_INT b = 0;
if (!sys->dstack.pop(&a)) {
return false;
}
if (!sys->dstack.pop(&b)) {
return false;
}
if (!sys->dstack.push(a)) {
return false;
}
return sys->dstack.push(b);
}
static bool
rot(System *sys)
{
KF_INT a = 0;
KF_INT b = 0;
KF_INT c = 0;
if (!sys->dstack.pop(&a)) {
return false;
}
if (!sys->dstack.pop(&b)) {
return false;
}
if (!sys->dstack.pop(&c)) {
return false;
}
if (!sys->dstack.push(b)) {
return false;
}
if (!sys->dstack.push(a)) {
return false;
}
return sys->dstack.push(c);
}
// TODO: print multiple per line
static bool
definitions(System *sys)
{
Word *cursor = dict;
char buf[MAX_TOKEN_LENGTH];
size_t buflen = 0;
while (cursor != nullptr) {
cursor->getname(buf, &buflen);
sys->interface->wrln(buf, buflen);
cursor = cursor->next();
}
return true;
}
void
init_dict()
{
dict = new Builtin((const char *)"DEFINITIONS", 11, dict, definitions);
dict = new Builtin((const char *)"+", 1, dict, add);
dict = new Builtin((const char *)"-", 1, dict, sub);
dict = new Builtin((const char *)"*", 1, dict, mul);
dict = new Builtin((const char *)"/", 1, dict, div);
dict = new Builtin((const char *)"SWAP", 4, dict, swap);
dict = new Builtin((const char *)"ROT", 3, dict, rot);
}
LOOKUP
lookup(struct Token *token, System *sys)
{
Word *cursor = dict;
KF_INT n;
if (parse_num(token, &n)) {
if (sys->dstack.push(n)) {
return LOOKUP_OK;
}
return LOOKUP_FAILED;
}
while (cursor != nullptr) {
if (cursor->match(token)) {
if (cursor->eval(sys)) {
return LOOKUP_OK;
}
return LOOKUP_FAILED;
}
cursor = cursor->next();
}
return LOOKUP_NOTFOUND;
}

22
dict.h Normal file
View File

@ -0,0 +1,22 @@
#ifndef __KF_DICT_H__
#define __KF_DICT_H__
#include "defs.h"
#include "parser.h"
#include "system.h"
#include "word.h"
static Word *dict = nullptr;
typedef enum _LOOKUP_ : uint8_t {
LOOKUP_OK = 0, // Lookup executed properly.
LOOKUP_NOTFOUND = 1, // The token isn't in the dictionary.
LOOKUP_FAILED = 2 // The word failed to execute.
} LOOKUP;
void init_dict(void);
LOOKUP lookup(struct Token *, System *);
#endif // __KF_DICT_H__

View File

@ -10,6 +10,7 @@ Contents:
part-0x02
part-0x03
part-0x04
part-0x05
Indices and tables
==================

46
doc/part-0x05.rst Normal file
View File

@ -0,0 +1,46 @@
Write You a Forth, 0x05
-----------------------
:date: 2018-02-24 12:23
:tags: wyaf, forth
Today I need to start actually doing things with tokens. This requires two
things:
1. Some idea of what a word is, and
2. A dictionary of words
I started taking some notes on this previously, and I think there are a few
kinds of words that are possible:
1. Numbers (e.g. defining a variable)
2. Built-in functions
3. Lambda functions (that is, user-defined functions).
Stage 1 really only needs to incorporate #2, so that's what I'll focus on for
now. However, to prepare for the future, I'm going to define a ``Word`` base
class and inherit from there. This interface is going to need to be
stack-aware, so what I've done is define a ``System`` struct in ``system.h``::
#ifndef __KF_CORE_H__
#define __KF_CORE_H__
#include "defs.h"
#include "stack.h"
typedef struct _System {
Stack<KF_INT> dstack;
} System;
#endif // __KF_CORE_H__
This will let me later add in support for the return stack and other things
that might be useful. Other ideas: adding something like an ``errno``
equivalent to indicate the last error, and storing a dictionary of words. This
will need some restructuring, though. Anyways, this works for now.
The Word interface
^^^^^^^^^^^^^^^^^^
Now I can start defining a Word.h.

10
io.cc
View File

@ -6,7 +6,7 @@
static constexpr size_t nbuflen = 11;
void
write_num(IO &interface, KF_INT n)
write_num(IO *interface, KF_INT n)
{
// TODO(kyle): make the size of the buffer depend on the size of
@ -17,8 +17,12 @@ write_num(IO &interface, KF_INT n)
bool neg = n < 0;
if (n < 0) {
interface.wrch('-');
interface->wrch('-');
n = ~n;
if (n == 0) {
neg = false;
n++;
}
}
while (n != 0) {
@ -30,5 +34,5 @@ write_num(IO &interface, KF_INT n)
}
uint8_t buflen = nbuflen - i % nbuflen;
interface.wrbuf(buf+i, buflen);
interface->wrbuf(buf+i, buflen);
}

2
io.h
View File

@ -21,7 +21,7 @@ public:
virtual void wrln(char *buf, size_t len) = 0;
};
void write_num(IO &, KF_INT);
void write_num(IO *, KF_INT);
#endif // __KF_IO_H__

View File

@ -1,6 +1,7 @@
#include "dict.h"
#include "io.h"
#include "parser.h"
#include "stack.h"
#include "system.h"
#include <stdlib.h>
#include <string.h>
@ -11,33 +12,33 @@
static char ok[] = "ok.\n";
static char bye[] = "bye";
// dstack is the data stack.
static Stack<KF_INT> dstack;
static System sys;
static void
write_dstack(IO &interface)
write_dstack()
{
KF_INT tmp;
interface.wrch('<');
for (size_t i = 0; i < dstack.size(); i++) {
sys.interface->wrch('<');
for (size_t i = 0; i < sys.dstack.size(); i++) {
if (i > 0) {
interface.wrch(' ');
sys.interface->wrch(' ');
}
dstack.get(i, tmp);
write_num(interface, tmp);
sys.dstack.get(i, tmp);
write_num(sys.interface, tmp);
}
interface.wrch('>');
sys.interface->wrch('>');
}
static bool
parser(IO &interface, const char *buf, const size_t buflen)
parser(const char *buf, const size_t buflen)
{
static size_t offset = 0;
static struct Token token;
static PARSE_RESULT result = PARSE_FAIL;
static LOOKUP lresult = LOOKUP_FAILED;
static bool stop = false;
offset = 0;
@ -46,50 +47,66 @@ parser(IO &interface, const char *buf, const size_t buflen)
token.length = 0;
while ((result = parse_next(buf, buflen, &offset, &token)) == PARSE_OK) {
interface.wrbuf((char *)"token: ", 7);
interface.wrbuf(token.token, token.length);
interface.wrln((char *)".", 1);
if (!parse_num(&token, dstack)) {
interface.wrln((char *)"failed to parse numeric", 23);
lresult = lookup(&token, &sys);
switch (lresult) {
case LOOKUP_OK:
continue;
case LOOKUP_NOTFOUND:
sys.interface->wrln((char *)"word not found", 15);
stop = true;
break;
case LOOKUP_FAILED:
sys.interface->wrln((char *)"execution failed", 17);
stop = true;
break;
default:
sys.interface->wrln((char *)"*** the world is broken ***", 27);
exit(1);
}
if (stop) {
stop = false;
break;
}
// Temporary hack until the interpreter is working further.
if (match_token(token.token, token.length, bye, 3)) {
interface.wrln((char *)"Goodbye!", 8);
sys.interface->wrln((char *)"Goodbye!", 8);
exit(0);
}
}
switch (result) {
case PARSE_OK:
return false;
case PARSE_EOB:
interface.wrbuf(ok, 4);
sys.interface->wrbuf(ok, 4);
return true;
case PARSE_LEN:
interface.wrln((char *)"parse error: token too long", 27);
sys.interface->wrln((char *)"parse error: token too long", 27);
return false;
case PARSE_FAIL:
interface.wrln((char *)"parser failure", 14);
sys.interface->wrln((char *)"parser failure", 14);
return false;
default:
interface.wrln((char *)"*** the world is broken ***", 27);
sys.interface->wrln((char *)"*** the world is broken ***", 27);
exit(1);
}
}
static void
interpreter(IO &interface)
interpreter()
{
static size_t buflen = 0;
static char linebuf[81];
while (true) {
write_dstack(interface);
interface.wrch('\n');
interface.wrch('?');
interface.wrch(' ');
buflen = interface.rdbuf(linebuf, 80, true, '\n');
parser(interface, linebuf, buflen);
write_dstack();
sys.interface->wrch('\n');
sys.interface->wrch('?');
sys.interface->wrch(' ');
buflen = sys.interface->rdbuf(linebuf, 80, true, '\n');
parser(linebuf, buflen);
}
}
@ -99,10 +116,12 @@ const size_t bannerlen = 19;
int
main(void)
{
init_dict();
#ifdef __linux__
Console interface;
sys.interface = &interface;
#endif
interface.wrbuf(banner, bannerlen);
interpreter(interface);
sys.interface->wrbuf(banner, bannerlen);
interpreter();
return 0;
}

View File

@ -25,6 +25,9 @@ Console::rdbuf(char *buf, size_t len, bool stopat, char stopch)
while (n < len) {
ch = this->rdch();
if (ch == 0x04) {
break;
}
if (stopat && stopch == ch) {
break;

View File

@ -19,7 +19,22 @@ match_token(const char *a, const size_t alen,
return false;
}
return memcmp(a, b, alen) == 0;
for (size_t i = 0; i < alen; i++) {
if (a[i] == b[i]) {
continue;
}
if ((a[i] ^ 0x20) == b[i]) {
continue;
}
if (a[i] == (b[i] ^ 0x20)) {
continue;
}
return false;
}
return true;
}
PARSE_RESULT
@ -72,9 +87,9 @@ parse_next(const char *buf, const size_t length, size_t *offset,
}
bool
parse_num(struct Token *token, Stack<KF_INT> &s)
parse_num(struct Token *token, KF_INT *n)
{
KF_INT n = 0;
KF_INT tmp = 0;
uint8_t i = 0;
bool sign = false;
@ -83,6 +98,9 @@ parse_num(struct Token *token, Stack<KF_INT> &s)
}
if (token->token[i] == '-') {
if (token->length == 1) {
return false;
}
i++;
sign = true;
}
@ -96,14 +114,15 @@ parse_num(struct Token *token, Stack<KF_INT> &s)
return false;
}
n *= 10;
n += (uint8_t)(token->token[i] - '0');
tmp *= 10;
tmp += (uint8_t)(token->token[i] - '0');
i++;
}
if (sign) {
n *= -1;
tmp *= -1;
}
return s.push(n);
*n = tmp;
return true;
}

View File

@ -21,7 +21,7 @@ PARSE_RESULT parse_next(const char *, const size_t, size_t *, struct Token *);
// TODO(kyle): investigate a better return value, e.g. to differentiate between
// stack failures and parse failures.
bool parse_num(struct Token *, Stack<KF_INT> &);
bool parse_num(struct Token *, KF_INT *);
#endif // __KF_PARSER_H__

10
stack.h
View File

@ -7,9 +7,10 @@ template <typename T>
class Stack {
public:
bool push(T val);
bool pop(T &val);
bool pop(T *val);
bool get(size_t, T &);
size_t size(void) { return this->arrlen; };
size_t size(void) { return this->arrlen; }
void clear(void) { this->arrlen = 0; }
private:
T arr[STACK_SIZE];
size_t arrlen;
@ -31,14 +32,15 @@ Stack<T>::push(T val)
// pop returns false if there was a stack underflow.
template <typename T>
bool
Stack<T>::pop(T &val)
Stack<T>::pop(T *val)
{
if (this->arrlen == 0) {
return false;
}
val = this->arr[this->arrlen - 1];
*val = this->arr[this->arrlen - 1];
this->arrlen--;
return true;
}
// get returns false on invalid bounds.

14
system.h Normal file
View File

@ -0,0 +1,14 @@
#ifndef __KF_CORE_H__
#define __KF_CORE_H__
#include "defs.h"
#include "io.h"
#include "stack.h"
typedef struct _System {
Stack<KF_INT> dstack;
IO *interface;
} System;
#endif // __KF_CORE_H__

39
word.cc Normal file
View File

@ -0,0 +1,39 @@
#include "defs.h"
#include "parser.h"
#include "system.h"
#include "word.h"
#include <string.h>
Builtin::Builtin(const char *name, size_t namelen, Word *head, bool (*target)(System *))
: prev(head), fun(target)
{
memcpy(this->name, name, namelen);
this->namelen = namelen;
}
bool
Builtin::eval(System *sys)
{
return this->fun(sys);
}
Word *
Builtin::next()
{
return this->prev;
}
bool
Builtin::match(struct Token *token)
{
return match_token(this->name, this->namelen, token->token, token->length);
}
void
Builtin::getname(char *buf, size_t *buflen)
{
memcpy(buf, this->name, this->namelen);
*buflen = namelen;
}

37
word.h Normal file
View File

@ -0,0 +1,37 @@
#ifndef __KF_WORD_H__
#define __KF_WORD_H__
#include "defs.h"
#include "parser.h"
#include "stack.h"
#include "system.h"
class Word {
public:
virtual ~Word() {};
virtual bool eval(System *) = 0;
virtual Word *next(void) = 0;
virtual bool match(struct Token *) = 0;
virtual void getname(char *, size_t *) = 0;
};
class Builtin : public Word {
public:
~Builtin() {};
Builtin(const char *name, size_t namelen, Word *head, bool (*fun)(System *));
bool eval(System *);
Word *next(void);
bool match(struct Token *);
void getname(char *, size_t *);
private:
char name[MAX_TOKEN_LENGTH];
size_t namelen;
Word *prev;
bool (*fun)(System *);
};
#endif // __KF_WORD_H__