diff --git a/Makefile b/Makefile index 6b15ec0..4a09afc 100644 --- a/Makefile +++ b/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) diff --git a/dict.cc b/dict.cc new file mode 100644 index 0000000..53ad8cd --- /dev/null +++ b/dict.cc @@ -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; +} \ No newline at end of file diff --git a/dict.h b/dict.h new file mode 100644 index 0000000..c63181b --- /dev/null +++ b/dict.h @@ -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__ \ No newline at end of file diff --git a/doc/index.rst b/doc/index.rst index d313229..08a7242 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -10,6 +10,7 @@ Contents: part-0x02 part-0x03 part-0x04 + part-0x05 Indices and tables ================== diff --git a/doc/part-0x05.rst b/doc/part-0x05.rst new file mode 100644 index 0000000..f2e0193 --- /dev/null +++ b/doc/part-0x05.rst @@ -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 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. \ No newline at end of file diff --git a/io.cc b/io.cc index a4d70b8..77e0e2a 100644 --- a/io.cc +++ b/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); } \ No newline at end of file diff --git a/io.h b/io.h index 4f957a6..38bf51b 100644 --- a/io.h +++ b/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__ \ No newline at end of file diff --git a/kforth.cc b/kforth.cc index 77c7320..10d8aa2 100644 --- a/kforth.cc +++ b/kforth.cc @@ -1,6 +1,7 @@ +#include "dict.h" #include "io.h" #include "parser.h" -#include "stack.h" +#include "system.h" #include #include @@ -11,33 +12,33 @@ static char ok[] = "ok.\n"; static char bye[] = "bye"; - -// dstack is the data stack. -static Stack 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; } \ No newline at end of file diff --git a/linux/io.cc b/linux/io.cc index aeef8bb..51015e1 100644 --- a/linux/io.cc +++ b/linux/io.cc @@ -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; diff --git a/parser.cc b/parser.cc index 6a94687..f7e35f4 100644 --- a/parser.cc +++ b/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 &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 &s) } if (token->token[i] == '-') { + if (token->length == 1) { + return false; + } i++; sign = true; } @@ -96,14 +114,15 @@ parse_num(struct Token *token, Stack &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; } \ No newline at end of file diff --git a/parser.h b/parser.h index 4cdef1d..af2f5a7 100644 --- a/parser.h +++ b/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 &); +bool parse_num(struct Token *, KF_INT *); #endif // __KF_PARSER_H__ diff --git a/stack.h b/stack.h index e23fe9a..849b710 100644 --- a/stack.h +++ b/stack.h @@ -7,9 +7,10 @@ template 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::push(T val) // pop returns false if there was a stack underflow. template bool -Stack::pop(T &val) +Stack::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. diff --git a/system.h b/system.h new file mode 100644 index 0000000..77413d3 --- /dev/null +++ b/system.h @@ -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 dstack; + IO *interface; +} System; + + +#endif // __KF_CORE_H__ \ No newline at end of file diff --git a/word.cc b/word.cc new file mode 100644 index 0000000..399b275 --- /dev/null +++ b/word.cc @@ -0,0 +1,39 @@ +#include "defs.h" +#include "parser.h" +#include "system.h" +#include "word.h" + +#include + + +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; +} \ No newline at end of file diff --git a/word.h b/word.h new file mode 100644 index 0000000..84b7684 --- /dev/null +++ b/word.h @@ -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__ \ No newline at end of file