misc/kforth: Start command processing.
This commit is contained in:
parent
0a010e4527
commit
14dc525084
6
Makefile
6
Makefile
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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__
|
|
@ -10,6 +10,7 @@ Contents:
|
|||
part-0x02
|
||||
part-0x03
|
||||
part-0x04
|
||||
part-0x05
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
|
|
@ -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
10
io.cc
|
@ -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
2
io.h
|
@ -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__
|
81
kforth.cc
81
kforth.cc
|
@ -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);
|
||||
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 (!parse_num(&token, dstack)) {
|
||||
interface.wrln((char *)"failed to parse numeric", 23);
|
||||
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;
|
||||
}
|
|
@ -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;
|
||||
|
|
33
parser.cc
33
parser.cc
|
@ -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;
|
||||
}
|
2
parser.h
2
parser.h
|
@ -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
10
stack.h
|
@ -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.
|
||||
|
|
|
@ -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__
|
|
@ -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;
|
||||
}
|
|
@ -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__
|
Loading…
Reference in New Issue