aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore5
-rw-r--r--Makefile12
-rw-r--r--README.org4
-rw-r--r--better_string.c65
-rw-r--r--better_string.h28
-rw-r--r--macros.c8
-rw-r--r--macros.h80
-rw-r--r--main.c62
-rw-r--r--parser.c735
-rw-r--r--parser.h102
-rwxr-xr-xuforthbin0 -> 26680 bytes
11 files changed, 1101 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ddb7c5c
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+.DS_Store
+.idea
+*.log
+tmp/
+stem
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..5cc8982
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,12 @@
+##
+# Project Title
+#
+# @file
+# @version 0.1
+
+all:
+ cc *.c -o stem
+
+clean:
+ rm stem
+# end
diff --git a/README.org b/README.org
new file mode 100644
index 0000000..89c2314
--- /dev/null
+++ b/README.org
@@ -0,0 +1,4 @@
+#+title: Uforth
+
+* uForth
+uForth aims to be a small implementation of something like the forth programming language.
diff --git a/better_string.c b/better_string.c
new file mode 100644
index 0000000..b3bc931
--- /dev/null
+++ b/better_string.c
@@ -0,0 +1,65 @@
+#include "better_string.h"
+#include "macros.h"
+#include <stdlib.h>
+#include <string.h>
+
+string_t *init_string(char *s) {
+ string_t *str = malloc(sizeof(string_t));
+ if (str == NULL)
+ die("malloc in init_string");
+ /* If we pass in NULL we assume that we want to append things to this in the
+ * future */
+ if (s == NULL) {
+ str->bufsize = 10;
+ str->value = calloc(str->bufsize, sizeof(char));
+ if (str->value == NULL)
+ die("calloc in init_string");
+ str->length = 0;
+ return str;
+ }
+ /* Otherwise we want the initial value to be equal to the parameter */
+ str->length = strlen(s);
+ str->bufsize = 2 * strlen(s);
+ str->value = calloc(str->bufsize, sizeof(char));
+ if (str->value == NULL)
+ die("calloc in init_string");
+ strcpy(str->value, s);
+ return str;
+}
+
+string_t *string_copy(string_t *s) { return init_string(s->value); }
+
+/* stackoverflow code */
+void *realloc_zero(void *pBuffer, size_t oldSize, size_t newSize) {
+ void *pNew = realloc(pBuffer, newSize);
+ if (pNew == NULL)
+ die("realloc on realloc_zero");
+ if (newSize > oldSize && pNew) {
+ size_t diff = newSize - oldSize;
+ void *pStart = ((char *)pNew) + oldSize;
+ memset(pStart, 0, diff);
+ }
+ return pNew;
+}
+
+void string_concat(string_t *s1, string_t *s2) {
+ for (int i = 0; i < s2->length; i++) {
+ string_append(s1, s2->value[i]);
+ }
+}
+
+void string_append(string_t *s, char c) {
+ char str[2] = {c, '\0'};
+ int oldsize = s->bufsize;
+ if (s->bufsize - 1 <= s->length) {
+ s->bufsize = s->bufsize * 2;
+ s->value = realloc_zero(s->value, oldsize, s->bufsize);
+ }
+ s->length++;
+ strcat(s->value, str);
+}
+
+void string_free(string_t *s) {
+ free(s->value);
+ free(s);
+}
diff --git a/better_string.h b/better_string.h
new file mode 100644
index 0000000..03bc6f6
--- /dev/null
+++ b/better_string.h
@@ -0,0 +1,28 @@
+#ifndef BETTER_STRING_H
+#define BETTER_STRING_H
+#include <stdlib.h>
+/* We want better strings to realloc less and keep track of the length of the
+ * string so we don't have to call strlen every time */
+typedef struct STRING_STRUCT {
+ /* length of string */
+ size_t length;
+ /* Size of current value buffer */
+ size_t bufsize;
+ char *value;
+} string_t;
+
+/* Allocates memory for new string */
+string_t *init_string(char *value);
+
+/* Copies string to another string */
+string_t *string_copy(string_t *s);
+
+/* Concatenates a string_t type and a char * type */
+void string_concat(string_t *s1, string_t *s2);
+
+/* Appends single characters */
+void string_append(string_t *s, char c);
+
+/* Frees space for string */
+void string_free(string_t *s);
+#endif
diff --git a/macros.c b/macros.c
new file mode 100644
index 0000000..e17e113
--- /dev/null
+++ b/macros.c
@@ -0,0 +1,8 @@
+#include "macros.h"
+#include <stdio.h>
+#include <stdlib.h>
+
+void die(char *message) {
+ fprintf(stderr, "ERROR: %s\n", message);
+ exit(1);
+}
diff --git a/macros.h b/macros.h
new file mode 100644
index 0000000..0fc94c8
--- /dev/null
+++ b/macros.h
@@ -0,0 +1,80 @@
+#ifndef MACROS_H
+#define MACROS_H
+#define BLK "\e[0;30m"
+#define RED "\e[0;31m"
+#define GRN "\e[0;32m"
+#define YEL "\e[0;33m"
+#define BLU "\e[0;34m"
+#define MAG "\e[0;35m"
+#define CYN "\e[0;36m"
+#define WHT "\e[0;37m"
+
+/* Regular bold text */
+#define BBLK "\e[1;30m"
+#define BRED "\e[1;31m"
+#define BGRN "\e[1;32m"
+#define BYEL "\e[1;33m"
+#define BBLU "\e[1;34m"
+#define BMAG "\e[1;35m"
+#define BCYN "\e[1;36m"
+#define BWHT "\e[1;37m"
+
+/* Regular underline text */
+#define UBLK "\e[4;30m"
+#define URED "\e[4;31m"
+#define UGRN "\e[4;32m"
+#define UYEL "\e[4;33m"
+#define UBLU "\e[4;34m"
+#define UMAG "\e[4;35m"
+#define UCYN "\e[4;36m"
+#define UWHT "\e[4;37m"
+
+/* Regular background */
+#define BLKB "\e[40m"
+#define REDB "\e[41m"
+#define GRNB "\e[42m"
+#define YELB "\e[43m"
+#define BLUB "\e[44m"
+#define MAGB "\e[45m"
+#define CYNB "\e[46m"
+#define WHTB "\e[47m"
+
+/* High intensty background */
+#define BLKHB "\e[0;100m"
+#define REDHB "\e[0;101m"
+#define GRNHB "\e[0;102m"
+#define YELHB "\e[0;103m"
+#define BLUHB "\e[0;104m"
+#define MAGHB "\e[0;105m"
+#define CYNHB "\e[0;106m"
+#define WHTHB "\e[0;107m"
+
+/* High intensty text */
+#define HBLK "\e[0;90m"
+#define HRED "\e[0;91m"
+#define HGRN "\e[0;92m"
+#define HYEL "\e[0;93m"
+#define HBLU "\e[0;94m"
+#define HMAG "\e[0;95m"
+#define HCYN "\e[0;96m"
+#define HWHT "\e[0;97m"
+
+/* Bold high intensity text */
+#define BHBLK "\e[1;90m"
+#define BHRED "\e[1;91m"
+#define BHGRN "\e[1;92m"
+#define BHYEL "\e[1;93m"
+#define BHBLU "\e[1;94m"
+#define BHMAG "\e[1;95m"
+#define BHCYN "\e[1;96m"
+#define BHWHT "\e[1;97m"
+
+/* Reset */
+#define reset "\e[0m"
+#define CRESET "\e[0m"
+#define COLOR_RESET "\e[0m"
+
+/* Die in case of catastrophic memory problem */
+void die(char *message);
+
+#endif
diff --git a/main.c b/main.c
new file mode 100644
index 0000000..34bad00
--- /dev/null
+++ b/main.c
@@ -0,0 +1,62 @@
+#include "parser.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define MAX 50
+#define JUSTDO(a) \
+ if (!(a)) { \
+ perror(#a); \
+ exit(1); \
+ }
+
+extern ht_t *WORD_TABLE;
+extern array_t *STACK;
+extern char *INBUF;
+extern parser_t *PARSER;
+
+/** char *get_line FILE *f
+ * reads an arbitrarily long line of text or until EOF
+ * caller must free the pointer returned after use
+ */
+char *get_line(FILE *f) {
+ int len = MAX;
+ char buf[MAX], *e = NULL, *ret;
+ JUSTDO(ret = calloc(MAX, 1));
+ while (fgets(buf, MAX, f)) {
+ if (len - strlen(ret) < MAX)
+ JUSTDO(ret = realloc(ret, len *= 2));
+ strcat(ret, buf);
+ if ((e = strrchr(ret, '\n')))
+ break;
+ }
+ if (e)
+ *e = '\0';
+ return ret;
+}
+
+int main(int argc, char **argv) {
+ PARSER = init_parser("");
+ value_t *v;
+ STACK = init_array(10);
+ WORD_TABLE = init_ht(500);
+
+ printf("exit to exit REPL;\n");
+ while (1) {
+ printf("> ");
+ INBUF = get_line(stdin);
+ parser_reset(PARSER, INBUF);
+ while (1) {
+ v = parser_get_next(PARSER);
+ if (v == NULL)
+ break;
+ eval(v);
+ }
+ free(INBUF);
+ }
+ ht_free(WORD_TABLE);
+ array_free(STACK);
+ free(PARSER);
+ free(INBUF);
+ return 0;
+}
diff --git a/parser.c b/parser.c
new file mode 100644
index 0000000..cee5e3b
--- /dev/null
+++ b/parser.c
@@ -0,0 +1,735 @@
+#include "parser.h"
+#include "macros.h"
+#include <ctype.h>
+#include <math.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+array_t *STACK;
+ht_t *WORD_TABLE;
+char *INBUF;
+parser_t *PARSER;
+
+array_t *init_array(size_t size) {
+ array_t *a = calloc(1, sizeof(array_t));
+ a->size = 0;
+ a->capacity = size;
+ a->items = calloc(a->capacity, sizeof(value_t *));
+ return a;
+}
+
+void array_append(array_t *a, value_t *v) {
+ if (a->size >= a->capacity - 2) {
+ a->capacity = a->capacity * 2;
+ a->items = realloc(a->items, a->capacity * sizeof(value_t *));
+ }
+ a->items[a->size] = v;
+ a->size++;
+}
+
+value_t *array_pop(array_t *a) {
+ if (a->size > 0) {
+ value_t *v = a->items[a->size - 1];
+ a->size--;
+ return v;
+ }
+ return NULL;
+}
+
+void array_extend(array_t *a, array_t *b) {
+ for (int i = 0; i < b->size; i++) {
+ array_append(a, b->items[i]);
+ }
+}
+
+void array_free(array_t *a) {
+ for (int i = 0; i < a->size; i++) {
+ value_free(a->items[i]);
+ }
+ free(a->items);
+ free(a);
+}
+
+array_t *array_copy(array_t *a) {
+ array_t *b = calloc(1, sizeof(array_t));
+ b->size = a->size;
+ b->capacity = a->capacity;
+ b->items = calloc(b->capacity, sizeof(value_t *));
+ for (int i = 0; i < a->size; i++) {
+ b->items[i] = value_copy(a->items[i]);
+ }
+ return b;
+}
+
+value_t *init_value(int type) {
+ value_t *v = calloc(1, sizeof(value_t));
+ v->type = type;
+ v->escaped = false;
+ return v;
+}
+
+value_t *value_copy(value_t *v) {
+ value_t *a = init_value(VINT);
+ a->type = v->type;
+ if (v->type == VINT || v->type == VFLOAT) {
+ a->int_float = v->int_float;
+ } else if (v->type == VSTR || v->type == VWORD) {
+ a->str_word = string_copy(v->str_word);
+ } else if (v->type == VQUOTE) {
+ a->quote = array_copy(v->quote);
+ }
+ return a;
+}
+
+void value_free(value_t *v) {
+ if (v->type == VSTR || v->type == VSTR || v->type == VWORD) {
+ string_free(v->str_word);
+ }
+ if (v->type == VQUOTE) {
+ array_free(v->quote);
+ }
+ free(v);
+}
+
+parser_t *init_parser(char *source) {
+ parser_t *p = calloc(1, sizeof(parser_t));
+ p->i = 0;
+ p->source = source;
+ p->c = source[0];
+ return p;
+}
+
+void parser_reset(parser_t *p, char *source) {
+ p->source = source;
+ p->i = 0;
+ p->c = source[0];
+}
+
+void parser_move(parser_t *p) {
+ if (p->i < strlen(p->source) && p->c != '\0') {
+ p->i++;
+ p->c = p->source[p->i];
+ }
+}
+
+void parser_skip_whitespace(parser_t *p) {
+ while (isspace(p->c)) {
+ parser_move(p);
+ }
+}
+
+value_t *parse_string(parser_t *p) {
+ value_t *retv = init_value(VSTR);
+ parser_move(p);
+ string_t *s = init_string(NULL);
+ while (p->c != '"' && p->c != '\0') {
+ string_append(s, p->c);
+ parser_move(p);
+ }
+ parser_move(p);
+ retv->str_word = s;
+ return retv;
+}
+
+value_t *parse_quote(parser_t *p) {
+ value_t *retv = init_value(VQUOTE);
+ retv->quote = init_array(10);
+ parser_move(p);
+ parser_skip_whitespace(p);
+ while (p->c != ']') {
+ array_append(retv->quote, parser_get_next(p));
+ parser_skip_whitespace(p);
+ }
+ parser_move(p);
+ return retv;
+}
+
+void parser_error(parser_t *p) { exit(1); }
+
+value_t *parse_word(parser_t *p) {
+ value_t *retv = init_value(VWORD);
+ string_t *s = init_string(NULL);
+ if (p->c == '\\') {
+ retv->escaped = true;
+ parser_move(p);
+ if (isspace(p->c) || p->c == '\0') {
+ parser_error(p);
+ }
+ }
+ while (!isspace(p->c) && p->c != '\0') {
+ string_append(s, p->c);
+ parser_move(p);
+ }
+ retv->str_word = s;
+ return retv;
+}
+
+value_t *parse_num(parser_t *p) {
+ value_t *retv;
+ string_t *s = init_string(NULL);
+ bool is_float = false;
+ while (isdigit(p->c) || p->c == '.') {
+ if (p->c == '.')
+ is_float = true;
+ string_append(s, p->c);
+ parser_move(p);
+ }
+ if (is_float)
+ retv = init_value(VFLOAT);
+ else
+ retv = init_value(VINT);
+ retv->int_float = atof(s->value);
+ string_free(s);
+ return retv;
+}
+
+value_t *parser_get_next(parser_t *p) {
+ parser_skip_whitespace(p);
+ if (isdigit(p->c)) {
+ return parse_num(p);
+ }
+ switch (p->c) {
+ case '"':
+ return parse_string(p);
+ case '[':
+ return parse_quote(p);
+ case '\0':
+ return NULL;
+ default:
+ return parse_word(p);
+ }
+}
+
+node_t *init_node(string_t *key, value_t *value) {
+ node_t *n = calloc(1, sizeof(node_t));
+ n->key = key;
+ n->value = value;
+ n->next = NULL;
+ return n;
+}
+
+void node_free(node_t *n) {
+ string_free(n->key);
+ value_free(n->value);
+ free(n);
+}
+
+sll_t *init_sll() {
+ sll_t *l = calloc(1, sizeof(sll_t));
+ l->size = 0;
+ l->head = NULL;
+ return l;
+}
+
+void sll_add(sll_t *l, string_t *s, value_t *v) {
+ if (l->head == NULL) {
+ node_t *n = init_node(s, v);
+ l->head = n;
+ l->size++;
+ return;
+ }
+ node_t *cur = l->head;
+ while (cur->next != NULL) {
+ if (strcmp(s->value, cur->value->str_word->value) == 0) {
+ value_free(cur->value);
+ string_free(s);
+ cur->value = v;
+ return;
+ }
+ cur = cur->next;
+ }
+ if (strcmp(s->value, cur->value->str_word->value) == 0) {
+ value_free(cur->value);
+ string_free(s);
+ cur->value = v;
+ return;
+ }
+ node_t *n = init_node(s, v);
+ cur->next = n;
+}
+
+value_t *sll_get(sll_t *l, string_t *k) {
+ if (l->head == NULL)
+ return NULL;
+ node_t *cur = l->head;
+ while (cur != NULL) {
+ if (strcmp(k->value, cur->key->value) == 0)
+ return cur->value;
+ cur = cur->next;
+ }
+ return NULL;
+}
+/* TODO */
+void sll_free(sll_t *l) {
+ node_t *cur = l->head;
+ node_t *tmp;
+ while (cur != NULL) {
+ tmp = cur;
+ cur = cur->next;
+ node_free(tmp);
+ }
+ free(l);
+}
+
+ht_t *init_ht(size_t size) {
+ ht_t *h = calloc(1, sizeof(ht_t));
+ h->size = size;
+ h->buckets = calloc(h->size, sizeof(sll_t *));
+ for (int i = 0; i < size; i++) {
+ h->buckets[i] = init_sll();
+ }
+ return h;
+}
+
+void ht_add(ht_t *h, string_t *key, value_t *v) {
+ sll_add(h->buckets[hash(h, key->value)], key, v);
+}
+
+value_t *ht_get(ht_t *h, string_t *key) {
+ return sll_get(h->buckets[hash(h, key->value)], key);
+}
+
+void ht_free(ht_t *h) {
+ for (int i = 0; i < h->size; i++) {
+ sll_free(h->buckets[i]);
+ }
+ free(h->buckets);
+ free(h);
+}
+
+/* DJB2 HASH FUNCTION */
+unsigned long hash(ht_t *h, char *key) {
+ unsigned long hash = 5381;
+ int c;
+
+ while (c = *key++)
+ hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
+
+ return hash % h->size;
+}
+
+void print_value(value_t *v) {
+ switch (v->type) {
+ case VINT:
+ case VFLOAT:
+ printf("%Lf\n", v->int_float);
+ break;
+ case VSTR:
+ printf("%s\n", v->str_word->value);
+ break;
+ case VWORD:
+ printf("W: %s\n", v->str_word->value);
+ break;
+ case VQUOTE:
+ printf("Q:\n");
+ for (int i = 0; i < v->quote->size; i++) {
+ print_value(v->quote->items[i]);
+ }
+ break;
+ }
+}
+
+void eval_error() { exit(1); }
+
+bool eval_builtins(value_t *v) {
+ char *str = v->str_word->value;
+ value_t *v1;
+ value_t *v2;
+ value_t *v3;
+ value_t *retval;
+ if (strcmp(str, "func") == 0) {
+ v2 = array_pop(STACK);
+ if (v2 == NULL) {
+ eval_error();
+ }
+ v1 = array_pop(STACK);
+ if (v1 == NULL) {
+ array_append(STACK, v2);
+ eval_error();
+ }
+ if (v1->type != VWORD)
+ eval_error();
+ ht_add(WORD_TABLE, string_copy(v1->str_word), v2);
+ value_free(v1);
+ } else if (strcmp(str, "+") == 0) {
+ v2 = array_pop(STACK);
+ if (v2 == NULL) {
+ eval_error();
+ }
+ v1 = array_pop(STACK);
+ if (v1 == NULL) {
+ array_append(STACK, v2);
+ eval_error();
+ }
+ retval = init_value(VFLOAT);
+ if (v1->type == VINT && v2->type == VINT) {
+ retval->type = VINT;
+ }
+ retval->int_float = v1->int_float + v2->int_float;
+ array_append(STACK, retval);
+ value_free(v1);
+ value_free(v2);
+ } else if (strcmp(str, "-") == 0) {
+ v2 = array_pop(STACK);
+ if (v2 == NULL) {
+ eval_error();
+ }
+ v1 = array_pop(STACK);
+ if (v1 == NULL) {
+ array_append(STACK, v2);
+ eval_error();
+ }
+ retval = init_value(VFLOAT);
+ if (v1->type == VINT && v2->type == VINT) {
+ retval->type = VINT;
+ }
+ retval->int_float = v1->int_float - v2->int_float;
+ array_append(STACK, retval);
+ value_free(v1);
+ value_free(v2);
+ } else if (strcmp(str, "/") == 0) {
+ v2 = array_pop(STACK);
+ if (v2 == NULL) {
+ eval_error();
+ }
+ v1 = array_pop(STACK);
+ if (v1 == NULL) {
+ array_append(STACK, v2);
+ eval_error();
+ }
+ retval = init_value(VFLOAT);
+ if (v1->type == VINT && v2->type == VINT) {
+ retval->type = VINT;
+ }
+ retval->int_float = v1->int_float / v2->int_float;
+ array_append(STACK, retval);
+ value_free(v1);
+ value_free(v2);
+ } else if (strcmp(str, "*") == 0) {
+ v2 = array_pop(STACK);
+ if (v2 == NULL) {
+ eval_error();
+ }
+ v1 = array_pop(STACK);
+ if (v1 == NULL) {
+ array_append(STACK, v2);
+ eval_error();
+ }
+ retval = init_value(VFLOAT);
+ if (v1->type == VINT && v2->type == VINT) {
+ retval->type = VINT;
+ }
+ retval->int_float = v1->int_float * v2->int_float;
+ array_append(STACK, retval);
+ value_free(v1);
+ value_free(v2);
+ } else if (strcmp(str, "pow") == 0) {
+ v2 = array_pop(STACK);
+ if (v2 == NULL) {
+ eval_error();
+ }
+ v1 = array_pop(STACK);
+ if (v1 == NULL) {
+ array_append(STACK, v2);
+ eval_error();
+ }
+ retval = init_value(VFLOAT);
+ if (v1->type == VINT && v2->type == VINT) {
+ retval->type = VINT;
+ }
+ retval->int_float = powl(v1->int_float, v2->int_float);
+ array_append(STACK, retval);
+ value_free(v1);
+ value_free(v2);
+ } else if (strcmp(str, "eval") == 0) {
+ v1 = array_pop(STACK);
+ if (v1 == NULL) {
+ eval_error();
+ }
+ if (v1->type == VQUOTE) {
+ for (int i = 0; i < v1->quote->size; i++) {
+ eval(value_copy(v1->quote->items[i]));
+ }
+ value_free(v1);
+ } else {
+ eval(v1);
+ }
+ } else if (strcmp(str, ".") == 0) {
+ v1 = array_pop(STACK);
+ if (v1 == NULL) {
+ eval_error();
+ }
+ print_value(v1);
+ value_free(v1);
+ } else if (strcmp(str, "?") == 0) {
+ for (int i = 0; i < STACK->size; i++) {
+ print_value(STACK->items[i]);
+ }
+ } else if (strcmp(str, "sin") == 0) {
+ v1 = array_pop(STACK);
+ if (v1 == NULL)
+ eval_error();
+ retval = init_value(VFLOAT);
+ retval->int_float = sinhl(v1->int_float);
+ array_append(STACK, retval);
+ value_free(v1);
+ } else if (strcmp(str, "cos") == 0) {
+ v1 = array_pop(STACK);
+ if (v1 == NULL)
+ eval_error();
+ retval = init_value(VFLOAT);
+ retval->int_float = coshl(v1->int_float);
+ array_append(STACK, retval);
+ value_free(v1);
+ } else if (strcmp(str, "exp") == 0) {
+ v1 = array_pop(STACK);
+ if (v1 == NULL)
+ eval_error();
+ retval = init_value(VFLOAT);
+ retval->int_float = expl(v1->int_float);
+ array_append(STACK, retval);
+ value_free(v1);
+ } else if (strcmp(str, "floor") == 0) {
+ v1 = array_pop(STACK);
+ if (v1 == NULL)
+ eval_error();
+ retval = init_value(VFLOAT);
+ retval->int_float = floor(v1->int_float);
+ array_append(STACK, retval);
+ value_free(v1);
+ } else if (strcmp(str, "ceil") == 0) {
+ v1 = array_pop(STACK);
+ if (v1 == NULL)
+ eval_error();
+ retval = init_value(VFLOAT);
+ retval->int_float = ceil(v1->int_float);
+ array_append(STACK, retval);
+ value_free(v1);
+ } else if (strcmp(str, "ln") == 0) {
+ v1 = array_pop(STACK);
+ if (v1 == NULL)
+ eval_error();
+ retval = init_value(VFLOAT);
+ retval->int_float = logl(v1->int_float);
+ array_append(STACK, retval);
+ value_free(v1);
+ } else if (strcmp(str, "ssize") == 0) {
+ retval = init_value(VINT);
+ retval->int_float = STACK->size;
+ array_append(STACK, retval);
+ } else if (strcmp(str, "compose") == 0) {
+ v2 = array_pop(STACK);
+ if (v2 == NULL)
+ eval_error();
+ v1 = array_pop(STACK);
+ if (v1 == NULL)
+ eval_error();
+
+ if (v2->type == VSTR && v1->type == VSTR) {
+ retval = init_value(VSTR);
+ string_concat(v1->str_word, v2->str_word);
+ retval->str_word = string_copy(v1->str_word);
+ value_free(v1);
+ value_free(v2);
+ } else if (v2->type == VWORD && v1->type == VWORD) {
+ retval = init_value(VWORD);
+ string_concat(v1->str_word, v2->str_word);
+ retval->str_word = string_copy(v1->str_word);
+ value_free(v1);
+ value_free(v2);
+ } else if (v2->type == VQUOTE && v1->type == VQUOTE) {
+ retval = v1;
+ array_extend(v1->quote, v2->quote);
+ free(v2->quote);
+ free(v2);
+ } else {
+ eval_error();
+ }
+ array_append(STACK, retval);
+ } else if (strcmp(str, "wtostr") == 0) {
+ v1 = array_pop(STACK);
+ if (v1 == NULL)
+ eval_error();
+
+ if (v1->type != VWORD)
+ eval_error();
+ v1->type = VSTR;
+ array_append(STACK, v1);
+ } else if (strcmp(str, "=") == 0) {
+ v2 = array_pop(STACK);
+ if (v2 == NULL)
+ eval_error();
+ v1 = array_pop(STACK);
+ if (v1 == NULL)
+ eval_error();
+
+ retval = init_value(VINT);
+ if (v1->type == VSTR && v2->type == VSTR ||
+ v1->type == VWORD && v2->type == VWORD) {
+ retval->int_float = strcmp(v1->str_word->value, v2->str_word->value) == 0;
+ } else if ((v1->type == VINT || v1->type == VFLOAT) &&
+ (v2->type == VINT || v2->type == VFLOAT)) {
+ retval->int_float = v1->int_float == v2->int_float;
+ } else {
+ eval_error();
+ }
+ array_append(STACK, retval);
+ value_free(v1);
+ value_free(v2);
+ } else if (strcmp(str, "if") == 0) {
+ v3 = array_pop(STACK);
+ if (v3 == NULL)
+ eval_error();
+ v2 = array_pop(STACK);
+ if (v2 == NULL)
+ eval_error();
+ v1 = array_pop(STACK);
+ if (v1 == NULL)
+ eval_error();
+
+ if (v3->type != VINT)
+ eval_error();
+
+ if (v3->int_float) {
+ if (v1->type == VQUOTE) {
+ for (int i = 0; i < v1->quote->size; i++) {
+ eval(value_copy(v1->quote->items[i]));
+ }
+ value_free(v1);
+ } else {
+ eval(v1);
+ }
+ value_free(v2);
+ } else {
+ if (v2->type == VQUOTE) {
+ for (int i = 0; i < v2->quote->size; i++) {
+ eval(value_copy(v2->quote->items[i]));
+ }
+ value_free(v2);
+ } else {
+ eval(v2);
+ }
+ value_free(v1);
+ }
+ value_free(v3);
+ } else if (strcmp(str, "clear") == 0) {
+ for (int i = 0; i < STACK->size; i++) {
+ value_free(array_pop(STACK));
+ }
+ } else if (strcmp(str, "curry") == 0) {
+ v2 = array_pop(STACK);
+ if (v2 == NULL)
+ eval_error();
+ v1 = array_pop(STACK);
+ if (v1 == NULL)
+ eval_error();
+
+ if (v2->type != VQUOTE)
+ eval_error();
+
+ array_append(v2->quote, v1);
+ array_append(STACK, v2);
+ } else if (strcmp(str, "len") == 0) {
+ v1 = array_pop(STACK);
+ if (v1 == NULL)
+ eval_error();
+
+ retval = init_value(VINT);
+ if (v1->type == VINT || v1->type == VFLOAT) {
+ retval->int_float = 1;
+ } else if (v1->type == VSTR || v1->type == VWORD) {
+ retval->int_float = strlen(v1->str_word->value);
+ } else if (v1->type == VQUOTE) {
+ retval->int_float = v1->quote->size;
+ }
+ array_append(STACK, v1);
+ array_append(STACK, retval);
+ } else if (strcmp(str, "dup") == 0) {
+ v1 = array_pop(STACK);
+ if (v1 == NULL)
+ eval_error();
+
+ retval = value_copy(v1);
+ array_append(STACK, v1);
+ array_append(STACK, retval);
+ } else if (strcmp(str, "exit") == 0) {
+ ht_free(WORD_TABLE);
+ array_free(STACK);
+ free(INBUF);
+ free(PARSER);
+ value_free(v);
+ exit(1);
+ } else if (strcmp(str, "vat") == 0) {
+ v2 = array_pop(STACK);
+ if (v2 == NULL)
+ eval_error();
+ v1 = array_pop(STACK);
+ if (v1 == NULL)
+ eval_error();
+
+ if (v1->type != VINT) {
+ eval_error();
+ }
+ if (v2->type == VINT || v2->type == VFLOAT) {
+ eval_error();
+ }
+
+ if (v2->type == VQUOTE) {
+ if (v2->quote->size <= v1->int_float) {
+ eval_error();
+ }
+ array_append(STACK, v2);
+ array_append(STACK, value_copy(v2->quote->items[(int)v1->int_float]));
+ value_free(v1);
+ } else {
+ eval_error();
+ }
+ } else {
+ return false;
+ }
+ value_free(v);
+ return true;
+}
+
+bool eval_ht(value_t *v) {
+ value_t *func = ht_get(WORD_TABLE, v->str_word);
+ if (func == NULL)
+ return false;
+ if (func->type == VQUOTE) {
+ for (int i = 0; i < func->quote->size; i++) {
+ eval(value_copy(func->quote->items[i]));
+ }
+ } else {
+ eval(value_copy(func));
+ }
+ value_free(v);
+ return true;
+}
+
+void eval(value_t *v) {
+ switch (v->type) {
+ case VINT:
+ case VFLOAT:
+ case VSTR:
+ case VQUOTE:
+ array_append(STACK, v);
+ printf("OK\n");
+ break;
+ case VWORD:
+ if (v->escaped) {
+ v->escaped = false;
+ array_append(STACK, v);
+ printf("OK\n");
+ } else {
+ if (!eval_builtins(v)) {
+ if (!eval_ht(v)) {
+ array_append(STACK, v);
+ printf("OK\n");
+ }
+ }
+ }
+ break;
+ }
+}
diff --git a/parser.h b/parser.h
new file mode 100644
index 0000000..168c7e9
--- /dev/null
+++ b/parser.h
@@ -0,0 +1,102 @@
+#ifndef PARSER_H_
+#define PARSER_H_
+#include "better_string.h"
+#include <stdbool.h>
+#include <stdlib.h>
+
+typedef struct VALUE_STRUCT value_t;
+typedef struct ARRAY_STRUCT array_t;
+
+struct ARRAY_STRUCT {
+ value_t **items;
+ size_t size;
+ size_t capacity;
+};
+
+struct VALUE_STRUCT {
+ enum { VWORD, VINT, VFLOAT, VSTR, VQUOTE } type;
+ union {
+ long double int_float;
+ array_t *quote;
+ string_t *str_word;
+ };
+ bool escaped;
+};
+
+typedef struct PARSER_STRUCT {
+ char *source;
+ int i;
+ char c;
+} parser_t;
+
+typedef struct NODE_STRUCT {
+ string_t *key;
+ value_t *value;
+ struct NODE_STRUCT *next;
+} node_t;
+
+typedef struct {
+ node_t *head;
+ size_t size;
+} sll_t;
+
+typedef struct {
+ sll_t **buckets;
+ size_t size;
+} ht_t;
+
+array_t *init_array(size_t size);
+
+void array_append(array_t *a, value_t *v);
+
+value_t *array_pop(array_t *a);
+
+array_t *array_copy(array_t *a);
+
+void array_free(array_t *a);
+
+value_t *init_value(int type);
+
+value_t *value_copy(value_t *v);
+
+void value_free(value_t *v);
+
+parser_t *init_parser(char *source);
+
+void parser_reset(parser_t *p, char *source);
+
+void parser_move(parser_t *p);
+
+void parser_skip_whitespace(parser_t *p);
+
+value_t *parser_get_next(parser_t *p);
+
+node_t *init_node(string_t *key, value_t *v);
+
+sll_t *init_sll();
+
+void sll_add(sll_t *l, string_t *key, value_t *v);
+
+value_t *sll_get(sll_t *l, string_t *key);
+
+void sll_free(sll_t *l);
+
+ht_t *init_ht(size_t size);
+
+void ht_add(ht_t *h, string_t *key, value_t *v);
+
+value_t *ht_get(ht_t *h, string_t *key);
+
+void ht_free(ht_t *h);
+
+unsigned long hash(ht_t *h, char *key);
+
+bool eval_builtins(value_t *v);
+
+bool eval_ht(value_t *v);
+
+bool eval_quote(value_t *v);
+
+void eval(value_t *v);
+
+#endif // PARSER_H_
diff --git a/uforth b/uforth
new file mode 100755
index 0000000..fc38117
--- /dev/null
+++ b/uforth
Binary files differ