diff options
-rw-r--r-- | README.org | 66 | ||||
-rw-r--r-- | main.c | 8 | ||||
-rw-r--r-- | parser.c | 108 |
3 files changed, 166 insertions, 16 deletions
@@ -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 ]~. @@ -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); @@ -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) { |