aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.org66
-rw-r--r--main.c8
-rw-r--r--parser.c108
3 files changed, 166 insertions, 16 deletions
diff --git a/README.org b/README.org
index 3337be5..3782540 100644
--- a/README.org
+++ b/README.org
@@ -1,4 +1,68 @@
#+title: Stem
* Introduction
-Stem aims to be a small implementation of something like the forth programming language.
+Stem aims to be a small implementation of something like the forth programming language,
+meaning it operates on a virtual stack. There are words, quotes, and literal types in this
+language; this allows for metaprogramming.
+
+** Quickstart
+In this language, words are anything that are not literals (strings, ints, floats), and are
+not the special characters [ and ]. Literals include the types above and they work just
+like in other languages, and quotes are just lists of these words and literals.
+
+Any literal in the language by itself gets pushed onto the stack. For example, the value:
+#+begin_example
+"hello world"
+#+end_example
+gets pushed onto the stack once it is created.
+
+Note that words can also act like literals, but they are different in that you can bind them to funtions:
+#+begin_example
+helloworld [ "hello world" . ] func
+#+end_example
+Where . is a builtin function that pops the first value off the stack and prints it. In this example, the helloworld
+word is pushed onto the stack, then the quote ~[ "hello world" . ]~, which is just an array of these two values. Then,
+the ~func~ builtin is called, which takes these two values off of the stack. As a result, whenever ~helloworld~ is used
+in the future, it is expanded into whatever is in the quote.
+
+a useful way to know what's on the stack:
+#+begin_example
+?
+#+end_example
+is a builtin function that prints everything on the stack, where the very last thing printed is the top of the stack.
+
+*** Quoting and Escaping
+If you want to push a word to the stack after it has been bound, you must escape it:
+#+begin_example
+\helloworld
+#+end_example
+
+quotes are somewhat related to escaping. They allow the language to talk about itself, along with the ~eval~ keyword.
+To get an idea of what you can do with it, consider the following example:
+#+begin_example
+[ hello [ "hello" . ] func ] eval
+#+end_example
+this statement is essentially the same statement as the above, but you can represent the entire code in a quote
+before evaluation. This allows for many possibilities. For example, you may try writing a program that automatically
+names functions and automatically changes what those functions do.
+
+*** Loops
+Looping in this language is done via recursion. Because the language is stack-based, recursion is not more memory efficient
+than looping if using tail recursion. For example, the REPL for this language is implemented like so:
+#+begin_example
+loop [ "> " read evalstr loop ] func loop
+#+end_example
+Where read takes in a string and prints it before reading a value, evalstr evaluates a string, and loop is the function that calls
+itself.
+
+*** Curry, Compose, Qstack, Quote
+These functions are important for manipulating quotes. For example:
+#+begin_example
+[ a b ] 6 5 quote curry compose
+#+end_example
+first turns 5 into ~[ 5 ]~, then curry adds 6 to the end of the quote. Compose takes two quotes and adjoins them together. Qstack
+simply turns everything on the stack into a quote, then puts it on the stack. For example:
+#+begin_example
+1 2 3 4 5 6 7 qstack
+#+end_example
+Returns the quote ~[ 1 2 3 4 5 6 7 ]~.
diff --git a/main.c b/main.c
index 9fd2eee..7ffdccd 100644
--- a/main.c
+++ b/main.c
@@ -33,8 +33,12 @@ int main(int argc, char **argv) {
version();
}
- FILE *fp = fopen(argv[1], "rb");
- ssize_t bytes_read = getdelim(&INBUF, &len, '\0', fp);
+ FILE *FP = fopen(argv[1], "rb");
+ ssize_t bytes_read = getdelim(&INBUF, &len, '\0', FP);
+ if (FP != NULL) {
+ fflush(FP);
+ fclose(FP);
+ }
PARSER = init_parser(INBUF);
STACK = init_array(10);
diff --git a/parser.c b/parser.c
index 7c84008..af7d91f 100644
--- a/parser.c
+++ b/parser.c
@@ -131,9 +131,41 @@ 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);
+ bool escaped = false;
+ while (escaped || p->c != '"' && p->c != '\0') {
+ if (p->c == '\\') {
+ escaped = true;
+ parser_move(p);
+ continue;
+ }
+ if (escaped) {
+ switch (p->c) {
+ case '"':
+ string_append(s, '"');
+ break;
+ case 'n':
+ string_append(s, '\n');
+ break;
+ case 'r':
+ string_append(s, '\r');
+ break;
+ case 't':
+ string_append(s, '\t');
+ break;
+ case '\\':
+ string_append(s, '\\');
+ break;
+ default:
+ string_append(s, p->c);
+ break;
+ }
+ parser_move(p);
+ escaped = false;
+ } else {
+ string_append(s, p->c);
+ parser_move(p);
+ escaped = false;
+ }
}
parser_move(p);
retv->str_word = s;
@@ -520,17 +552,19 @@ bool eval_builtins(value_t *v) {
value_free(v);
return eval_error();
}
- parser_t *tmp_p = init_parser(v1->str_word->value);
- value_t *cur;
+ value_t *TMP_VALUE = v;
+ value_t *TMP_CUR;
+ char *s = malloc(strlen(v1->str_word->value) + 1);
+ strcpy(s, v1->str_word->value);
+ value_free(v1);
+ parser_t *TMP_P = init_parser(s);
while (1) {
- cur = parser_get_next(tmp_p);
- if (cur == NULL)
+ TMP_CUR = parser_get_next(TMP_P);
+ if (TMP_CUR == NULL)
break;
- eval(cur);
+ eval(TMP_CUR);
}
- free(tmp_p);
- value_free(v1);
-
+ free(TMP_P);
} else if (strcmp(str, ".") == 0) {
v1 = array_pop(STACK);
if (v1 == NULL) {
@@ -971,7 +1005,6 @@ bool eval_builtins(value_t *v) {
array_free(STACK);
free(INBUF);
free(PARSER);
- value_free(v);
exit(0);
} else if (strcmp(str, "read") == 0) {
v1 = array_pop(STACK);
@@ -987,9 +1020,58 @@ bool eval_builtins(value_t *v) {
}
printf("%s", v1->str_word->value);
retval = init_value(VSTR);
- retval->str_word = init_string(get_line(stdin));
+ char *a = get_line(stdin);
+ retval->str_word = init_string(a);
+ array_append(STACK, retval);
+ value_free(v1);
+ free(a);
+ } else if (strcmp(str, "fread") == 0) {
+ v1 = array_pop(STACK);
+ if (v1 == NULL) {
+ value_free(v);
+ return eval_error();
+ }
+ if (v1->type != VSTR) {
+ value_free(v);
+ array_append(STACK, v1);
+ return eval_error();
+ }
+ char *val;
+ size_t len;
+ FILE *fp = fopen(v1->str_word->value, "rb");
+ if (!fp) {
+ value_free(v);
+ array_append(STACK, v1);
+ return eval_error();
+ }
+ ssize_t bytes_read = getdelim(&val, &len, '\0', fp);
+ fclose(fp);
+ retval = init_value(VSTR);
+ retval->str_word = init_string(val);
array_append(STACK, retval);
value_free(v1);
+ } else if (strcmp(str, "fwrite") == 0) {
+ v1 = array_pop(STACK);
+ if (v1 == NULL) {
+ value_free(v);
+ return eval_error();
+ }
+ if (v1->type != VSTR) {
+ value_free(v);
+ array_append(STACK, v1);
+ return eval_error();
+ }
+ char *val;
+ size_t len;
+ FILE *fp = fopen(v1->str_word->value, "w+");
+ if (!fp) {
+ value_free(v);
+ array_append(STACK, v1);
+ return eval_error();
+ }
+ fprintf(fp, "%s", v1->str_word->value);
+ value_free(v1);
+ fclose(fp);
} else if (strcmp(str, "vat") == 0) {
v2 = array_pop(STACK);
if (v2 == NULL) {