diff options
author | Přemysl Janouch <p.janouch@gmail.com> | 2017-05-30 02:43:42 +0200 |
---|---|---|
committer | Přemysl Janouch <p.janouch@gmail.com> | 2017-05-30 02:43:42 +0200 |
commit | 8518c1a58f63d8a1fc992616480c6202335815e8 (patch) | |
tree | 1821fc070e7db0c817cc239e1becb28e97304b50 | |
parent | d579e68051ac757c530f753b02fbf18ed1aa41b3 (diff) | |
download | ell-8518c1a58f63d8a1fc992616480c6202335815e8.tar.gz ell-8518c1a58f63d8a1fc992616480c6202335815e8.tar.xz ell-8518c1a58f63d8a1fc992616480c6202335815e8.zip |
The great librarificating rename
-rw-r--r-- | ell.c | 1243 | ||||
-rw-r--r-- | interpreter.c | 38 | ||||
-rw-r--r-- | repl.c | 52 |
3 files changed, 675 insertions, 658 deletions
@@ -27,19 +27,19 @@ #include <setjmp.h> #if defined __GNUC__ -#define ATTRIBUTE_PRINTF(x, y) __attribute__ ((format (printf, x, y))) +#define ELL_ATTRIBUTE_PRINTF(x, y) __attribute__ ((format (printf, x, y))) #else // ! __GNUC__ -#define ATTRIBUTE_PRINTF(x, y) +#define ELL_ATTRIBUTE_PRINTF(x, y) #endif // ! __GNUC__ -#define N_ELEMENTS(a) (sizeof (a) / sizeof ((a)[0])) +#define ELL_N_ELEMENTS(a) (sizeof (a) / sizeof ((a)[0])) // --- Utilities --------------------------------------------------------------- -static char *format (const char *format, ...) ATTRIBUTE_PRINTF (1, 2); +static char *ell_format (const char *format, ...) ELL_ATTRIBUTE_PRINTF (1, 2); static char * -vformat (const char *format, va_list ap) { +ell_vformat (const char *format, va_list ap) { va_list aq; va_copy (aq, ap); int size = vsnprintf (NULL, 0, format, aq); @@ -56,26 +56,26 @@ vformat (const char *format, va_list ap) { } static char * -format (const char *format, ...) { +ell_format (const char *format, ...) { va_list ap; va_start (ap, format); - char *result = vformat (format, ap); + char *result = ell_vformat (format, ap); va_end (ap); return result; } // --- Generic buffer ---------------------------------------------------------- -struct buffer { +struct ell_buffer { char *s; ///< Buffer data size_t alloc, len; ///< Number of bytes allocated and used bool memory_failure; ///< Memory allocation failed }; -#define BUFFER_INITIALIZER { NULL, 0, 0, false } +#define ELL_BUFFER_INITIALIZER { NULL, 0, 0, false } static bool -buffer_append (struct buffer *self, const void *s, size_t n) { +ell_buffer_append (struct ell_buffer *self, const void *s, size_t n) { if (self->memory_failure) return false; @@ -95,50 +95,50 @@ buffer_append (struct buffer *self, const void *s, size_t n) { } inline static bool -buffer_append_c (struct buffer *self, char c) { - return buffer_append (self, &c, 1); +ell_buffer_append_c (struct ell_buffer *self, char c) { + return ell_buffer_append (self, &c, 1); } -// --- Data items -------------------------------------------------------------- +// --- Values ------------------------------------------------------------------ -enum item_type { ITEM_STRING, ITEM_LIST }; +enum ell_v_type { ELL_STRING, ELL_LIST }; -struct item { - enum item_type type; ///< The type of this object - struct item *next; ///< Next item on the list/stack +struct ell_v { + enum ell_v_type type; ///< The type of this value + struct ell_v *next; ///< Next value in sequence - struct item *head; ///< The head of the list - size_t len; ///< Length of "value" (sans '\0') - char value[]; ///< The null-terminated string value + struct ell_v *head; ///< The head of the list + size_t len; ///< Length of "string" (sans '\0') + char string[]; ///< The null-terminated string value }; -static void item_free_list (struct item *); -static struct item *new_clone_list (const struct item *); +static void ell_free_seq (struct ell_v *); +static struct ell_v *ell_clone_seq (const struct ell_v *); static void -item_free (struct item *item) { - item_free_list (item->head); - free (item); +ell_free_v (struct ell_v *v) { + ell_free_seq (v->head); + free (v); } static void -item_free_list (struct item *item) { - while (item) { - struct item *link = item; - item = item->next; - item_free (link); +ell_free_seq (struct ell_v *v) { + while (v) { + struct ell_v *link = v; + v = v->next; + ell_free_v (link); } } -static struct item * -new_clone (const struct item *item) { - size_t size = sizeof *item + item->len + 1; - struct item *clone = malloc (size); +static struct ell_v * +ell_clone (const struct ell_v *v) { + size_t size = sizeof *v + v->len + 1; + struct ell_v *clone = malloc (size); if (!clone) return NULL; - memcpy (clone, item, size); - if (clone->head && !(clone->head = new_clone_list (clone->head))) { + memcpy (clone, v, size); + if (clone->head && !(clone->head = ell_clone_seq (clone->head))) { free (clone); return NULL; } @@ -146,12 +146,12 @@ new_clone (const struct item *item) { return clone; } -static struct item * -new_clone_list (const struct item *item) { - struct item *head = NULL; - for (struct item **out = &head; item; item = item->next) { - if (!(*out = new_clone (item))) { - item_free_list (head); +static struct ell_v * +ell_clone_seq (const struct ell_v *v) { + struct ell_v *head = NULL; + for (struct ell_v **out = &head; v; v = v->next) { + if (!(*out = ell_clone (v))) { + ell_free_seq (head); return NULL; } out = &(*out)->next; @@ -159,70 +159,71 @@ new_clone_list (const struct item *item) { return head; } -static struct item * -new_string (const char *s, size_t len) { - struct item *item = calloc (1, sizeof *item + len + 1); - if (!item) +static struct ell_v * +ell_string (const char *s, size_t len) { + struct ell_v *v = calloc (1, sizeof *v + len + 1); + if (!v) return NULL; - item->type = ITEM_STRING; - item->len = len; - memcpy (item->value, s, len); - return item; + v->type = ELL_STRING; + v->len = len; + memcpy (v->string, s, len); + return v; } -static struct item * -new_list (struct item *head) { - struct item *item = calloc (1, sizeof *item + 1); - if (!item) { - item_free_list (head); +static struct ell_v * +ell_list (struct ell_v *head) { + struct ell_v *v = calloc (1, sizeof *v + 1); + if (!v) { + ell_free_seq (head); return NULL; } - item->type = ITEM_LIST; - item->head = head; - return item; + v->type = ELL_LIST; + v->head = head; + return v; } // --- Lexer ------------------------------------------------------------------- -enum token { T_ABORT, T_LPAREN, T_RPAREN, T_LBRACKET, T_RBRACKET, - T_LBRACE, T_RBRACE, T_STRING, T_NEWLINE, T_AT }; - -static const char *token_names[] = { - [T_ABORT] = "end of input", - [T_LPAREN] = "left parenthesis", - [T_RPAREN] = "right parenthesis", - [T_LBRACKET] = "left bracket", - [T_RBRACKET] = "right bracket", - [T_LBRACE] = "left brace", - [T_RBRACE] = "right brace", - [T_STRING] = "string", - [T_NEWLINE] = "newline", - [T_AT] = "at symbol", +enum ell_token { ELLT_ABORT, ELLT_LPAREN, ELLT_RPAREN, + ELLT_LBRACKET, ELLT_RBRACKET, ELLT_LBRACE, ELLT_RBRACE, + ELLT_STRING, ELLT_NEWLINE, ELLT_AT }; + +static const char *ell_token_names[] = { + [ELLT_ABORT] = "end of input", + [ELLT_LPAREN] = "left parenthesis", + [ELLT_RPAREN] = "right parenthesis", + [ELLT_LBRACKET] = "left bracket", + [ELLT_RBRACKET] = "right bracket", + [ELLT_LBRACE] = "left brace", + [ELLT_RBRACE] = "right brace", + [ELLT_STRING] = "string", + [ELLT_NEWLINE] = "newline", + [ELLT_AT] = "at symbol", }; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -struct lexer { +struct ell_lexer { const unsigned char *p; ///< Current position in input size_t len; ///< How many bytes of input are left unsigned line, column; ///< Current line and column - struct buffer string; ///< Parsed string value + struct ell_buffer string; ///< Parsed string value }; static void -lexer_init (struct lexer *self, const char *p, size_t len) { - *self = (struct lexer) { .p = (const unsigned char *) p, .len = len }; +ell_lexer_init (struct ell_lexer *self, const char *p, size_t len) { + *self = (struct ell_lexer) { .p = (const unsigned char *) p, .len = len }; } static void -lexer_free (struct lexer *self) { +ell_lexer_free (struct ell_lexer *self) { free (self->string.s); } static int -lexer_advance (struct lexer *self) { +ell_lexer_advance (struct ell_lexer *self) { int c = *self->p++; if (c == '\n') { self->column = 0; @@ -235,109 +236,119 @@ lexer_advance (struct lexer *self) { } static bool -lexer_hexa_escape (struct lexer *self, struct buffer *output) { - const char *alphabet = "0123456789abcdef", *h, *l; - if (!self->len || !(h = strchr (alphabet, tolower (lexer_advance (self)))) - || !self->len || !(l = strchr (alphabet, tolower (lexer_advance (self))))) +ell_lexer_hexa_escape (struct ell_lexer *self, struct ell_buffer *output) { + const char *abc = "0123456789abcdef", *h, *l; + if (!self->len || !(h = strchr (abc, tolower (ell_lexer_advance (self)))) + || !self->len || !(l = strchr (abc, tolower (ell_lexer_advance (self))))) return false; - buffer_append_c (output, (h - alphabet) << 4 | (l - alphabet)); + ell_buffer_append_c (output, (h - abc) << 4 | (l - abc)); return true; } -enum { LEXER_STRING_QUOTE = '\'', LEXER_ESCAPE = '\\', LEXER_COMMENT = '#' }; -static bool lexer_is_whitespace (int c) { return !c || c == ' ' || c == '\t'; } +enum { + ELL_LEXER_STRING_QUOTE = '\'', + ELL_LEXER_ESCAPE = '\\', + ELL_LEXER_COMMENT = '#' +}; + +static bool ell_lexer_is_whitespace (int c) { + return !c || c == ' ' || c == '\t'; +} -static unsigned char lexer_escapes[256] = { - [LEXER_STRING_QUOTE] = LEXER_STRING_QUOTE, [LEXER_ESCAPE] = LEXER_ESCAPE, +static unsigned char ell_lexer_escapes[256] = { + [ELL_LEXER_STRING_QUOTE] = ELL_LEXER_STRING_QUOTE, + [ELL_LEXER_ESCAPE] = ELL_LEXER_ESCAPE, ['a'] = '\a', ['b'] = '\b', ['n'] = '\n', ['r'] = '\r', ['t'] = '\t', }; static const char * -lexer_escape_sequence (struct lexer *self, struct buffer *output) { +ell_lexer_escape_sequence (struct ell_lexer *self, struct ell_buffer *output) { if (!self->len) return "premature end of escape sequence"; - int c = lexer_advance (self); + int c = ell_lexer_advance (self); if (c == 'x') { - if (lexer_hexa_escape (self, output)) + if (ell_lexer_hexa_escape (self, output)) return NULL; return "invalid hexadecimal escape"; } - if (!(c = lexer_escapes[c])) + if (!(c = ell_lexer_escapes[c])) return "unknown escape sequence"; - buffer_append_c (output, c); + ell_buffer_append_c (output, c); return NULL; } static const char * -lexer_string (struct lexer *self, struct buffer *output) { +ell_lexer_string (struct ell_lexer *self, struct ell_buffer *output) { int c; const char *e = NULL; while (self->len) { - if ((c = lexer_advance (self)) == LEXER_STRING_QUOTE) + if ((c = ell_lexer_advance (self)) == ELL_LEXER_STRING_QUOTE) return NULL; - if (c != LEXER_ESCAPE) - buffer_append_c (output, c); - else if ((e = lexer_escape_sequence (self, output))) + if (c != ELL_LEXER_ESCAPE) + ell_buffer_append_c (output, c); + else if ((e = ell_lexer_escape_sequence (self, output))) return e; } return "premature end of string"; } -static enum token lexer_tokens[256] = { - ['('] = T_LPAREN, [')'] = T_RPAREN, ['['] = T_LBRACKET, [']'] = T_RBRACKET, - ['{'] = T_LBRACE, ['}'] = T_RBRACE, [';'] = T_NEWLINE, ['\n'] = T_NEWLINE, - ['@'] = T_AT, [LEXER_STRING_QUOTE] = T_STRING, +static enum ell_token ell_lexer_tokens[256] = { + ['('] = ELLT_LPAREN, [')'] = ELLT_RPAREN, + ['['] = ELLT_LBRACKET, [']'] = ELLT_RBRACKET, + ['{'] = ELLT_LBRACE, ['}'] = ELLT_RBRACE, + [';'] = ELLT_NEWLINE, ['\n'] = ELLT_NEWLINE, + ['@'] = ELLT_AT, [ELL_LEXER_STRING_QUOTE] = ELLT_STRING, }; -static enum token -lexer_next (struct lexer *self, const char **e) { - while (self->len && lexer_is_whitespace (*self->p)) - lexer_advance (self); +static enum ell_token +ell_lexer_next (struct ell_lexer *self, const char **e) { + while (self->len && ell_lexer_is_whitespace (*self->p)) + ell_lexer_advance (self); if (!self->len) - return T_ABORT; + return ELLT_ABORT; free (self->string.s); - self->string = (struct buffer) BUFFER_INITIALIZER; + self->string = (struct ell_buffer) ELL_BUFFER_INITIALIZER; - int c = lexer_advance (self); - if (c == LEXER_COMMENT) { + int c = ell_lexer_advance (self); + if (c == ELL_LEXER_COMMENT) { while (self->len) - if (lexer_advance (self) == '\n') - return T_NEWLINE; - return T_ABORT; + if (ell_lexer_advance (self) == '\n') + return ELLT_NEWLINE; + return ELLT_ABORT; } - enum token token = lexer_tokens[c]; + enum ell_token token = ell_lexer_tokens[c]; if (!token) { - buffer_append_c (&self->string, c); - while (self->len && !lexer_is_whitespace (*self->p) - && !lexer_tokens[*self->p]) - buffer_append_c (&self->string, lexer_advance (self)); - return T_STRING; + ell_buffer_append_c (&self->string, c); + while (self->len && !ell_lexer_is_whitespace (*self->p) + && !ell_lexer_tokens[*self->p]) + ell_buffer_append_c (&self->string, ell_lexer_advance (self)); + return ELLT_STRING; } - if (token == T_STRING - && (*e = lexer_string (self, &self->string))) - return T_ABORT; + if (token == ELLT_STRING + && (*e = ell_lexer_string (self, &self->string))) + return ELLT_ABORT; return token; } -static char *lexer_errorf (struct lexer *self, const char *fmt, ...) - ATTRIBUTE_PRINTF (2, 3); +static char *ell_lexer_errorf (struct ell_lexer *self, const char *fmt, ...) + ELL_ATTRIBUTE_PRINTF (2, 3); static char * -lexer_errorf (struct lexer *self, const char *fmt, ...) { +ell_lexer_errorf (struct ell_lexer *self, const char *fmt, ...) { va_list ap; va_start (ap, fmt); - char *description = vformat (fmt, ap); + char *description = ell_vformat (fmt, ap); va_end (ap); if (!description) return NULL; - char *e = format ("at or before line %u, column %u: %s", + char *e = ell_format ("at or before line %u, column %u: %s", self->line + 1, self->column + 1, description); free (description); return e; @@ -345,56 +356,56 @@ lexer_errorf (struct lexer *self, const char *fmt, ...) { // --- Printing ---------------------------------------------------------------- -static void print_item_list (struct item *item); +static void ell_print_seq (struct ell_v *v); static bool -print_string_needs_quoting (struct item *s) { +ell_print_string_needs_quoting (struct ell_v *s) { for (size_t i = 0; i < s->len; i++) { - unsigned char c = s->value[i]; - if (lexer_is_whitespace (c) || lexer_tokens[c] - || c == LEXER_ESCAPE || c < 32) + unsigned char c = s->string[i]; + if (ell_lexer_is_whitespace (c) || ell_lexer_tokens[c] + || c == ELL_LEXER_ESCAPE || c < 32) return true; } return s->len == 0; } static bool -print_string (struct item *s) { - if (s->type != ITEM_STRING) +ell_print_string (struct ell_v *s) { + if (s->type != ELL_STRING) return false; - if (!print_string_needs_quoting (s)) { - printf ("%s", s->value); + if (!ell_print_string_needs_quoting (s)) { + printf ("%s", s->string); return true; } - putchar (LEXER_STRING_QUOTE); + putchar (ELL_LEXER_STRING_QUOTE); for (size_t i = 0; i < s->len; i++) { - unsigned char c = s->value[i]; + unsigned char c = s->string[i]; if (c < 32) printf ("\\x%02x", c); - else if (c == LEXER_ESCAPE || c == LEXER_STRING_QUOTE) + else if (c == ELL_LEXER_ESCAPE || c == ELL_LEXER_STRING_QUOTE) printf ("\\%c", c); else putchar (c); } - putchar (LEXER_STRING_QUOTE); + putchar (ELL_LEXER_STRING_QUOTE); return true; } static bool -print_block (struct item *list) { - if (!list->head || strcmp (list->head->value, "block")) +ell_print_block (struct ell_v *list) { + if (!list->head || strcmp (list->head->string, "block")) return false; list = list->head->next; - for (struct item *line = list; line; line = line->next) - if (line->type != ITEM_LIST) + for (struct ell_v *line = list; line; line = line->next) + if (line->type != ELL_LIST) return false; putchar ('{'); - for (struct item *line = list; line; line = line->next) { + for (struct ell_v *line = list; line; line = line->next) { putchar (' '); - print_item_list (line->head); + ell_print_seq (line->head); putchar (line->next ? ';' : ' '); } putchar ('}'); @@ -402,106 +413,106 @@ print_block (struct item *list) { } static bool -print_set (struct item *list) { - if (!list->head || strcmp (list->head->value, "set") +ell_print_set (struct ell_v *list) { + if (!list->head || strcmp (list->head->string, "set") || !list->head->next || list->head->next->next) return false; putchar ('@'); - print_item_list (list->head->next); + ell_print_seq (list->head->next); return true; } static bool -print_list (struct item *list) { - if (!list->head || strcmp (list->head->value, "list")) +ell_print_list (struct ell_v *list) { + if (!list->head || strcmp (list->head->string, "list")) return false; putchar ('['); - print_item_list (list->head->next); + ell_print_seq (list->head->next); putchar (']'); return true; } static void -print_item (struct item *item) { - if (print_string (item) - || print_block (item) - || print_set (item) - || print_list (item)) +ell_print_v (struct ell_v *v) { + if (ell_print_string (v) + || ell_print_block (v) + || ell_print_set (v) + || ell_print_list (v)) return; putchar ('('); - print_item_list (item->head); + ell_print_seq (v->head); putchar (')'); } static void -print_item_list (struct item *item) { - for (; item; item = item->next) { - print_item (item); - if (item->next) +ell_print_seq (struct ell_v *v) { + for (; v; v = v->next) { + ell_print_v (v); + if (v->next) putchar (' '); } } // --- Parsing ----------------------------------------------------------------- -struct parser { - struct lexer lexer; ///< Tokenizer +struct ell_parser { + struct ell_lexer lexer; ///< Tokenizer char *error; ///< Tokenizer error - enum token token; ///< Current token in the lexer + enum ell_token token; ///< Current token in the lexer bool replace_token; ///< Replace the token bool memory_failure; ///< Memory allocation failed }; static void -parser_init (struct parser *self, const char *script, size_t len) { - memset (self, 0, sizeof *self); - lexer_init (&self->lexer, script, len); +ell_parser_init (struct ell_parser *p, const char *script, size_t len) { + memset (p, 0, sizeof *p); + ell_lexer_init (&p->lexer, script, len); // As reading in tokens may cause exceptions, we wait for the first peek() - // to replace the initial T_ABORT. - self->replace_token = true; + // to replace the initial ELLT_ABORT. + p->replace_token = true; } static void -parser_free (struct parser *self) { - lexer_free (&self->lexer); - if (self->error) - free (self->error); +ell_parser_free (struct ell_parser *p) { + ell_lexer_free (&p->lexer); + if (p->error) + free (p->error); } -static enum token -parser_peek (struct parser *self, jmp_buf out) { - if (self->replace_token) { +static enum ell_token +ell_parser_peek (struct ell_parser *p, jmp_buf out) { + if (p->replace_token) { const char *e = NULL; - self->token = lexer_next (&self->lexer, &e); + p->token = ell_lexer_next (&p->lexer, &e); if (e) { - self->memory_failure = - !(self->error = lexer_errorf (&self->lexer, "%s", e)); + p->memory_failure = + !(p->error = ell_lexer_errorf (&p->lexer, "%s", e)); longjmp (out, 1); } - if (self->token == T_STRING && self->lexer.string.memory_failure) + if (p->token == ELLT_STRING && p->lexer.string.memory_failure) longjmp (out, 1); - self->replace_token = false; + p->replace_token = false; } - return self->token; + return p->token; } static bool -parser_accept (struct parser *self, enum token token, jmp_buf out) { - return self->replace_token = (parser_peek (self, out) == token); +ell_parser_accept (struct ell_parser *p, enum ell_token token, jmp_buf out) { + return p->replace_token = (ell_parser_peek (p, out) == token); } static void -parser_expect (struct parser *self, enum token token, jmp_buf out) { - if (parser_accept (self, token, out)) +ell_parser_expect (struct ell_parser *p, enum ell_token token, jmp_buf out) { + if (ell_parser_accept (p, token, out)) return; - self->memory_failure = !(self->error = lexer_errorf (&self->lexer, + p->memory_failure = !(p->error = ell_lexer_errorf (&p->lexer, "unexpected `%s', expected `%s'", - token_names[self->token], token_names[token])); + ell_token_names[p->token], ell_token_names[token])); longjmp (out, 1); } @@ -510,99 +521,99 @@ parser_expect (struct parser *self, enum token token, jmp_buf out) { // We don't need no generator, but a few macros will come in handy. // From time to time C just doesn't have the right features. -#define PEEK() parser_peek (self, err) -#define ACCEPT(token) parser_accept (self, token, err) -#define EXPECT(token) parser_expect (self, token, err) -#define SKIP_NL() do {} while (ACCEPT (T_NEWLINE)) +#define PEEK() ell_parser_peek (p, err) +#define ACCEPT(token) ell_parser_accept (p, token, err) +#define EXPECT(token) ell_parser_expect (p, token, err) +#define SKIP_NL() do {} while (ACCEPT (ELLT_NEWLINE)) -static struct item * -parser_check (struct parser *self, struct item *item, jmp_buf out) { - if (!item) { - self->memory_failure = true; +static struct ell_v * +ell_parser_check (struct ell_parser *p, struct ell_v *v, jmp_buf out) { + if (!v) { + p->memory_failure = true; longjmp (out, 1); } - return item; + return v; } // Beware that this jumps to the "out" buffer directly -#define CHECK(item) parser_check (self, (item), out) +#define CHECK(v) ell_parser_check (p, (v), out) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -static struct item * -parse_prefix_list (struct item *list, const char *name) { - struct item *prefix; - if (!(prefix = new_string (name, strlen (name)))) { - item_free_list (list); +static struct ell_v * +ell_parse_prefix_list (struct ell_v *seq, const char *name) { + struct ell_v *prefix; + if (!(prefix = ell_string (name, strlen (name)))) { + ell_free_seq (seq); return NULL; } - prefix->next = list; - return new_list (prefix); + prefix->next = seq; + return ell_list (prefix); } -static struct item * parse_line (struct parser *self, jmp_buf out); +static struct ell_v * ell_parse_line (struct ell_parser *p, jmp_buf out); -static struct item * -parse_item (struct parser *self, jmp_buf out) { +static struct ell_v * +ell_parse_v (struct ell_parser *p, jmp_buf out) { jmp_buf err; - struct item *volatile result = NULL, *volatile *tail = &result; + struct ell_v *volatile result = NULL, *volatile *tail = &result; if (setjmp (err)) { - item_free_list (result); + ell_free_seq (result); longjmp (out, 1); } SKIP_NL (); - if (ACCEPT (T_STRING)) - return CHECK (new_string - (self->lexer.string.s, self->lexer.string.len)); - if (ACCEPT (T_AT)) { - result = parse_item (self, out); - return CHECK (parse_prefix_list (result, "set")); + if (ACCEPT (ELLT_STRING)) + return CHECK (ell_string + (p->lexer.string.s, p->lexer.string.len)); + if (ACCEPT (ELLT_AT)) { + result = ell_parse_v (p, out); + return CHECK (ell_parse_prefix_list (result, "set")); } - if (ACCEPT (T_LPAREN)) { - while (!ACCEPT (T_RPAREN)) { - tail = &(*tail = parse_item (self, err))->next; + if (ACCEPT (ELLT_LPAREN)) { + while (!ACCEPT (ELLT_RPAREN)) { + tail = &(*tail = ell_parse_v (p, err))->next; SKIP_NL (); } - return CHECK (new_list (result)); + return CHECK (ell_list (result)); } - if (ACCEPT (T_LBRACKET)) { - while (!ACCEPT (T_RBRACKET)) { - tail = &(*tail = parse_item (self, err))->next; + if (ACCEPT (ELLT_LBRACKET)) { + while (!ACCEPT (ELLT_RBRACKET)) { + tail = &(*tail = ell_parse_v (p, err))->next; SKIP_NL (); } - return CHECK (parse_prefix_list (result, "list")); + return CHECK (ell_parse_prefix_list (result, "list")); } - if (ACCEPT (T_LBRACE)) { - while ((*tail = parse_line (self, err))) + if (ACCEPT (ELLT_LBRACE)) { + while ((*tail = ell_parse_line (p, err))) tail = &(*tail)->next; - EXPECT (T_RBRACE); - return CHECK (parse_prefix_list (result, "block")); + EXPECT (ELLT_RBRACE); + return CHECK (ell_parse_prefix_list (result, "block")); } - self->memory_failure = !(self->error = lexer_errorf (&self->lexer, - "unexpected `%s', expected a value", token_names[self->token])); + p->memory_failure = !(p->error = ell_lexer_errorf (&p->lexer, + "unexpected `%s', expected a value", ell_token_names[p->token])); longjmp (out, 1); } -static struct item * -parse_line (struct parser *self, jmp_buf out) { +static struct ell_v * +ell_parse_line (struct ell_parser *p, jmp_buf out) { jmp_buf err; - struct item *volatile result = NULL, *volatile *tail = &result; + struct ell_v *volatile result = NULL, *volatile *tail = &result; if (setjmp (err)) { - item_free_list (result); + ell_free_seq (result); longjmp (out, 1); } - while (PEEK () != T_RBRACE && PEEK () != T_ABORT) { - if (!ACCEPT (T_NEWLINE)) { - tail = &(*tail = parse_item (self, err))->next; + while (PEEK () != ELLT_RBRACE && PEEK () != ELLT_ABORT) { + if (!ACCEPT (ELLT_NEWLINE)) { + tail = &(*tail = ell_parse_v (p, err))->next; } else if (result) { - return CHECK (new_list (result)); + return CHECK (ell_list (result)); } } if (result) - return CHECK (new_list (result)); + return CHECK (ell_list (result)); return NULL; } @@ -614,140 +625,140 @@ parse_line (struct parser *self, jmp_buf out) { #undef SKIP_NL #undef CHECK -static struct item * -parser_run (struct parser *self, const char **e) { +static struct ell_v * +ell_parser_run (struct ell_parser *p, const char **e) { jmp_buf err; - struct item *volatile result = NULL, *volatile *tail = &result; + struct ell_v *volatile result = NULL, *volatile *tail = &result; if (setjmp (err)) { - item_free_list (result); - *e = self->error; - if (self->memory_failure || self->lexer.string.memory_failure) + ell_free_seq (result); + *e = p->error; + if (p->memory_failure || p->lexer.string.memory_failure) *e = "memory allocation failure"; return NULL; } - while ((*tail = parse_line (self, err))) + while ((*tail = ell_parse_line (p, err))) tail = &(*tail)->next; - parser_expect (self, T_ABORT, err); + ell_parser_expect (p, ELLT_ABORT, err); return result; } // --- Runtime ----------------------------------------------------------------- -struct context { - struct item *globals; ///< List of global variables - struct item *scopes; ///< Dynamic scopes from newest - struct native_fn *native; ///< Maps strings to C functions +struct ell { + struct ell_v *globals; ///< List of global variables + struct ell_v *scopes; ///< Dynamic scopes from newest + struct ell_native_fn *native; ///< Maps strings to C functions char *error; ///< Error information bool memory_failure; ///< Memory allocation failure void *user_data; ///< User data }; -typedef bool (*handler_fn) (struct context *, struct item *, struct item **); +typedef bool (*EllHandler) (struct ell *, struct ell_v *, struct ell_v **); -struct native_fn { - struct native_fn *next; ///< The next link in the chain - handler_fn handler; ///< Internal C handler, or NULL +struct ell_native_fn { + struct ell_native_fn *next; ///< The next link in the chain + EllHandler handler; ///< Internal C handler, or NULL char name[]; ///< The name of the function }; static void -context_init (struct context *ctx) { - memset (ctx, 0, sizeof *ctx); +ell_init (struct ell *ell) { + memset (ell, 0, sizeof *ell); } static void -context_free (struct context *ctx) { - struct native_fn *next, *iter; - for (iter = ctx->native; iter; iter = next) { +ell_free (struct ell *ell) { + struct ell_native_fn *next, *iter; + for (iter = ell->native; iter; iter = next) { next = iter->next; free (iter); } - item_free_list (ctx->globals); - item_free_list (ctx->scopes); - free (ctx->error); + ell_free_seq (ell->globals); + ell_free_seq (ell->scopes); + free (ell->error); } static bool -check (struct context *ctx, struct item *item) { - return !(ctx->memory_failure |= !item); +ell_check (struct ell *ell, struct ell_v *v) { + return !(ell->memory_failure |= !v); } -static struct item ** -scope_find (struct item **scope, const char *name) { +static struct ell_v ** +ell_scope_find (struct ell_v **scope, const char *name) { for (; *scope; scope = &(*scope)->next) - if (!strcmp ((*scope)->head->value, name)) + if (!strcmp ((*scope)->head->string, name)) return scope; return NULL; } static bool -scope_prepend (struct context *ctx, struct item **scope, - const char *name, struct item *value) { - struct item *key, *pair; - if (!check (ctx, (key = new_string (name, strlen (name)))) - || !check (ctx, (pair = new_list (key)))) { - item_free_list (value); +ell_scope_prepend (struct ell *ell, struct ell_v **scope, const char *name, + struct ell_v *v) { + struct ell_v *key, *pair; + if (!ell_check (ell, (key = ell_string (name, strlen (name)))) + || !ell_check (ell, (pair = ell_list (key)))) { + ell_free_seq (v); return false; } - key->next = value; + key->next = v; pair->next = *scope; *scope = pair; return true; } -static struct item * -get (struct context *ctx, const char *name) { - struct item **item; - for (struct item *scope = ctx->scopes; scope; scope = scope->next) - if ((item = scope_find (&scope->head, name))) - return (*item)->head->next; - if (!(item = scope_find (&ctx->globals, name))) +static struct ell_v * +ell_get (struct ell *ell, const char *name) { + struct ell_v **place; + for (struct ell_v *scope = ell->scopes; scope; scope = scope->next) + if ((place = ell_scope_find (&scope->head, name))) + return (*place)->head->next; + if (!(place = ell_scope_find (&ell->globals, name))) return NULL; - return (*item)->head->next; + return (*place)->head->next; } static bool -set (struct context *ctx, const char *name, struct item *value) { - struct item **item; - for (struct item *scope = ctx->scopes; scope; scope = scope->next) { - if ((item = scope_find (&scope->head, name))) { - item_free_list ((*item)->head->next); - (*item)->head->next = NULL; - return !value - || check (ctx, ((*item)->head->next = new_clone (value))); +ell_set (struct ell *ell, const char *name, struct ell_v *v) { + struct ell_v **place; + for (struct ell_v *scope = ell->scopes; scope; scope = scope->next) { + if ((place = ell_scope_find (&scope->head, name))) { + ell_free_seq ((*place)->head->next); + (*place)->head->next = NULL; + return !v + || ell_check (ell, ((*place)->head->next = ell_clone (v))); } } // Variables only get deleted by "arg" or from the global scope - if ((item = scope_find (&ctx->globals, name))) { - struct item *tmp = *item; - *item = (*item)->next; - item_free (tmp); + if ((place = ell_scope_find (&ell->globals, name))) { + struct ell_v *tmp = *place; + *place = (*place)->next; + ell_free_v (tmp); } - return !value || scope_prepend (ctx, &ctx->globals, name, value); + return !v || ell_scope_prepend (ell, &ell->globals, name, v); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -static struct native_fn * -native_find (struct context *ctx, const char *name) { - for (struct native_fn *fn = ctx->native; fn; fn = fn->next) +static struct ell_native_fn * +ell_native_find (struct ell *ell, const char *name) { + for (struct ell_native_fn *fn = ell->native; fn; fn = fn->next) if (!strcmp (fn->name, name)) return fn; return NULL; } static bool -native_register (struct context *ctx, const char *name, handler_fn handler) { - struct native_fn *fn = native_find (ctx, name); +ell_native_register (struct ell *ell, const char *name, EllHandler handler) { + struct ell_native_fn *fn = ell_native_find (ell, name); if (!fn) { if (!(fn = calloc (1, sizeof *fn + strlen (name) + 1))) return false; strcpy (fn->name, name); - fn->next = ctx->native; - ctx->native = fn; + fn->next = ell->native; + ell->native = fn; } fn->handler = handler; return true; @@ -756,39 +767,39 @@ native_register (struct context *ctx, const char *name, handler_fn handler) { // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - static bool -set_error (struct context *ctx, const char *format, ...) { +ell_error (struct ell *ell, const char *ell_format, ...) { va_list ap; - va_start (ap, format); - free (ctx->error); - if (!(ctx->error = vformat (format, ap))) - ctx->memory_failure = true; + va_start (ap, ell_format); + free (ell->error); + if (!(ell->error = ell_vformat (ell_format, ap))) + ell->memory_failure = true; va_end (ap); return false; } static bool -can_modify_error (struct context *ctx) { +ell_can_modify_error (struct ell *ell) { // In that case, `error' is NULL and there's nothing else to do anyway. // Errors starting with an underscore are exceptions and would not work // with stack traces generated this way. - return !ctx->memory_failure && ctx->error[0] != '_'; + return !ell->memory_failure && ell->error[0] != '_'; } -static bool execute_statement (struct context *, struct item *, struct item **); -static bool execute_block (struct context *, - struct item *, struct item *, struct item **); +static bool ell_eval_statement (struct ell *, struct ell_v *, struct ell_v **); +static bool ell_eval_block + (struct ell *, struct ell_v *, struct ell_v *, struct ell_v **); static bool -execute_args (struct context *ctx, struct item *args, struct item **result) { +ell_eval_args (struct ell *ell, struct ell_v *args, struct ell_v **result) { size_t i = 0; - struct item *res = NULL, **out = &res; + struct ell_v *res = NULL, **out = &res; for (; args; args = args->next) { - struct item *evaluated = NULL; + struct ell_v *evaluated = NULL; // Arguments should not evaporate, default to a nil value - if (!execute_statement (ctx, args, &evaluated) - || (!evaluated && !check (ctx, (evaluated = new_list (NULL))))) + if (!ell_eval_statement (ell, args, &evaluated) + || (!evaluated && !ell_check (ell, (evaluated = ell_list (NULL))))) goto error; - item_free_list (evaluated->next); + ell_free_seq (evaluated->next); evaluated->next = NULL; out = &(*out = evaluated)->next; i++; @@ -798,60 +809,60 @@ execute_args (struct context *ctx, struct item *args, struct item **result) { error: // Once the code flows like this, at least make some use of it - if (can_modify_error (ctx)) { - char *tmp = ctx->error; - ctx->error = NULL; - set_error (ctx, "(argument %zu) -> %s", i, tmp); + if (ell_can_modify_error (ell)) { + char *tmp = ell->error; + ell->error = NULL; + ell_error (ell, "(argument %zu) -> %s", i, tmp); free (tmp); } - item_free_list (res); + ell_free_seq (res); return false; } static bool -execute_native (struct context *ctx, const char *name, struct item *args, - struct item **result) { - struct native_fn *fn = native_find (ctx, name); +ell_eval_native (struct ell *ell, const char *name, struct ell_v *args, + struct ell_v **result) { + struct ell_native_fn *fn = ell_native_find (ell, name); if (!fn) - return set_error (ctx, "unknown function"); + return ell_error (ell, "unknown function"); - struct item *arguments = NULL; - if (!execute_args (ctx, args, &arguments)) + struct ell_v *arguments = NULL; + if (!ell_eval_args (ell, args, &arguments)) return false; - bool ok = fn->handler (ctx, arguments, result); - item_free_list (arguments); + bool ok = fn->handler (ell, arguments, result); + ell_free_seq (arguments); return ok; } static bool -execute_resolved (struct context *ctx, struct item *body, struct item *args, - struct item **result) { +ell_eval_resolved (struct ell *ell, struct ell_v *body, struct ell_v *args, + struct ell_v **result) { // Resolving names ecursively could be pretty fatal, let's not do that - if (body->type == ITEM_STRING) - return check (ctx, (*result = new_clone (body))); - struct item *arguments = NULL; - return execute_args (ctx, args, &arguments) - && execute_block (ctx, body->head, arguments, result); + if (body->type == ELL_STRING) + return ell_check (ell, (*result = ell_clone (body))); + struct ell_v *arguments = NULL; + return ell_eval_args (ell, args, &arguments) + && ell_eval_block (ell, body->head, arguments, result); } static bool -execute_item (struct context *ctx, struct item *body, struct item **result) { - struct item *args = body->next; - if (body->type == ITEM_STRING) { - const char *name = body->value; +ell_eval_value (struct ell *ell, struct ell_v *body, struct ell_v **result) { + struct ell_v *args = body->next; + if (body->type == ELL_STRING) { + const char *name = body->string; if (!strcmp (name, "block")) - return (!args || check (ctx, (args = new_clone_list (args)))) - && check (ctx, (*result = new_list (args))); - if ((body = get (ctx, name))) - return execute_resolved (ctx, body, args, result); - return execute_native (ctx, name, args, result); + return (!args || ell_check (ell, (args = ell_clone_seq (args)))) + && ell_check (ell, (*result = ell_list (args))); + if ((body = ell_get (ell, name))) + return ell_eval_resolved (ell, body, args, result); + return ell_eval_native (ell, name, args, result); } // When someone tries to call a block directly, we must evaluate it; // e.g. something like `{ choose [@f1 @f2 @f3] } arg1 arg2 arg3`. - struct item *evaluated = NULL; - if (!execute_statement (ctx, body, &evaluated)) + struct ell_v *evaluated = NULL; + if (!ell_eval_statement (ell, body, &evaluated)) return false; // It might a bit confusing that this doesn't evaluate arguments @@ -859,102 +870,102 @@ execute_item (struct context *ctx, struct item *body, struct item **result) { if (!evaluated) return true; - bool ok = execute_resolved (ctx, evaluated, args, result); - item_free_list (evaluated); + bool ok = ell_eval_resolved (ell, evaluated, args, result); + ell_free_seq (evaluated); return ok; } static bool -execute_statement - (struct context *ctx, struct item *statement, struct item **result) { - if (statement->type == ITEM_STRING) - return check (ctx, (*result = new_clone (statement))); +ell_eval_statement + (struct ell *ell, struct ell_v *statement, struct ell_v **result) { + if (statement->type == ELL_STRING) + return ell_check (ell, (*result = ell_clone (statement))); // Executing a nil value results in no value. It's not very different from // calling a block that returns no value--it's for our callers to resolve. if (!statement->head - || execute_item (ctx, statement->head, result)) + || ell_eval_value (ell, statement->head, result)) return true; - item_free_list (*result); + ell_free_seq (*result); *result = NULL; const char *name = "(block)"; - if (statement->head->type == ITEM_STRING) - name = statement->head->value; + if (statement->head->type == ELL_STRING) + name = statement->head->string; - if (can_modify_error (ctx)) { - char *tmp = ctx->error; - ctx->error = NULL; - set_error (ctx, "%s -> %s", name, tmp); + if (ell_can_modify_error (ell)) { + char *tmp = ell->error; + ell->error = NULL; + ell_error (ell, "%s -> %s", name, tmp); free (tmp); } return false; } static bool -args_to_scope (struct context *ctx, struct item *args, struct item **scope) { - if (!check (ctx, (args = new_list (args))) - || !scope_prepend (ctx, scope, "args", args)) +args_to_scope (struct ell *ell, struct ell_v *args, struct ell_v **scope) { + if (!ell_check (ell, (args = ell_list (args))) + || !ell_scope_prepend (ell, scope, "args", args)) return false; size_t i = 0; for (args = args->head; args; args = args->next) { char buf[16] = ""; (void) snprintf (buf, sizeof buf, "%zu", ++i); - struct item *copy = NULL; - if ((args && !check (ctx, (copy = new_clone (args)))) - || !scope_prepend (ctx, scope, buf, copy)) + struct ell_v *copy = NULL; + if ((args && !ell_check (ell, (copy = ell_clone (args)))) + || !ell_scope_prepend (ell, scope, buf, copy)) return false; } - return check (ctx, (*scope = new_list (*scope))); + return ell_check (ell, (*scope = ell_list (*scope))); } /// Execute a block and return whatever the last statement returned, eats args static bool -execute_block (struct context *ctx, struct item *body, struct item *args, - struct item **result) { - struct item *scope = NULL; - if (!args_to_scope (ctx, args, &scope)) { - item_free_list (scope); +ell_eval_block (struct ell *ell, struct ell_v *body, struct ell_v *args, + struct ell_v **result) { + struct ell_v *scope = NULL; + if (!args_to_scope (ell, args, &scope)) { + ell_free_seq (scope); return false; } - scope->next = ctx->scopes; - ctx->scopes = scope; + scope->next = ell->scopes; + ell->scopes = scope; bool ok = true; for (; body; body = body->next) { - item_free_list (*result); + ell_free_seq (*result); *result = NULL; - if (!(ok = execute_statement (ctx, body, result))) + if (!(ok = ell_eval_statement (ell, body, result))) break; } - ctx->scopes = scope->next; - item_free (scope); + ell->scopes = scope->next; + ell_free_v (scope); return ok; } -// --- Runtime library --------------------------------------------------------- +// --- Standard library -------------------------------------------------------- -#define defn(name) static bool name \ - (struct context *ctx, struct item *args, struct item **result) +#define ell_defn(name) static bool name \ + (struct ell *ell, struct ell_v *args, struct ell_v **result) static bool -execute_any (struct context *ctx, struct item *body, struct item *arg, - struct item **result) { - if (body->type == ITEM_STRING) - return check (ctx, (*result = new_clone (body))); - if (arg && !check (ctx, (arg = new_clone (arg)))) +ell_eval_any (struct ell *ell, struct ell_v *body, struct ell_v *arg, + struct ell_v **result) { + if (body->type == ELL_STRING) + return ell_check (ell, (*result = ell_clone (body))); + if (arg && !ell_check (ell, (arg = ell_clone (arg)))) return false; - return execute_block (ctx, body->head, arg, result); + return ell_eval_block (ell, body->head, arg, result); } -static struct item * -new_number (double n) { +static struct ell_v * +ell_number (double n) { char *s; - if (!(s = format ("%f", n))) + if (!(s = ell_format ("%f", n))) return NULL; char *p = strchr (s, 0); @@ -963,34 +974,30 @@ new_number (double n) { if (*p == '.') *p = 0; - struct item *item = new_string (s, strlen (s)); + struct ell_v *v = ell_string (s, strlen (s)); free (s); - return item; + return v; } -static bool -truthy (struct item *item) { - return item && (item->head || item->len); -} - -static struct item * new_boolean (bool b) { return new_string ("1", b); } +static bool ell_truthy (struct ell_v *v) { return v && (v->head || v->len); } +static struct ell_v * ell_boolean (bool b) { return ell_string ("1", b); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -defn (fn_local) { - struct item *names = args; - if (!names || names->type != ITEM_LIST) - return set_error (ctx, "first argument must be a list"); +ell_defn (ell_fn_local) { + struct ell_v *names = args; + if (!names || names->type != ELL_LIST) + return ell_error (ell, "first argument must be a list"); // Duplicates or non-strings don't really matter to us, user's problem - struct item **scope = &ctx->scopes->head; + struct ell_v **scope = &ell->scopes->head; (void) result; - struct item *values = names->next; + struct ell_v *values = names->next; for (names = names->head; names; names = names->next) { - struct item *value = NULL; - if ((values && !check (ctx, (value = new_clone (values)))) - || !scope_prepend (ctx, scope, names->value, value)) + struct ell_v *value = NULL; + if ((values && !ell_check (ell, (value = ell_clone (values)))) + || !ell_scope_prepend (ell, scope, names->string, value)) return false; if (values) values = values->next; @@ -998,317 +1005,346 @@ defn (fn_local) { return true; } -defn (fn_set) { - struct item *name = args; - if (!name || name->type != ITEM_STRING) - return set_error (ctx, "first argument must be string"); +ell_defn (ell_fn_set) { + struct ell_v *name = args; + if (!name || name->type != ELL_STRING) + return ell_error (ell, "first argument must be string"); - struct item *value; - if ((value = name->next)) - return check (ctx, (value = new_clone (value))) - && check (ctx, (*result = new_clone (value))) - && set (ctx, name->value, value); + struct ell_v *v; + if ((v = name->next)) + return ell_check (ell, (v = ell_clone (v))) + && ell_check (ell, (*result = ell_clone (v))) + && ell_set (ell, name->string, v); // We return an empty list for a nil value - if (!(value = get (ctx, name->value))) - return check (ctx, (*result = new_list (NULL))); - return check (ctx, (*result = new_clone (value))); + if (!(v = ell_get (ell, name->string))) + return ell_check (ell, (*result = ell_list (NULL))); + return ell_check (ell, (*result = ell_clone (v))); } -defn (fn_list) { - struct item *values = NULL; - if (args && !check (ctx, (values = new_clone_list (args)))) +ell_defn (ell_fn_list) { + struct ell_v *values = NULL; + if (args && !ell_check (ell, (values = ell_clone_seq (args)))) return false; - return check (ctx, (*result = new_list (values))); + return ell_check (ell, (*result = ell_list (values))); } -defn (fn_values) { - return !args || check (ctx, (*result = new_clone_list (args))); +ell_defn (ell_fn_values) { + return !args || ell_check (ell, (*result = ell_clone_seq (args))); } -defn (fn_if) { - struct item *cond, *body, *keyword; +ell_defn (ell_fn_if) { + struct ell_v *cond, *body, *keyword; for (cond = args; ; cond = keyword->next) { if (!cond) - return set_error (ctx, "missing condition"); + return ell_error (ell, "missing condition"); if (!(body = cond->next)) - return set_error (ctx, "missing body"); + return ell_error (ell, "missing body"); - struct item *res = NULL; - if (!execute_any (ctx, cond, NULL, &res)) + struct ell_v *res = NULL; + if (!ell_eval_any (ell, cond, NULL, &res)) return false; - bool match = truthy (res); - item_free_list (res); + bool match = ell_truthy (res); + ell_free_seq (res); if (match) - return execute_any (ctx, body, NULL, result); + return ell_eval_any (ell, body, NULL, result); if (!(keyword = body->next)) break; - if (keyword->type != ITEM_STRING) - return set_error (ctx, "expected keyword, got list"); + if (keyword->type != ELL_STRING) + return ell_error (ell, "expected keyword, got list"); - if (!strcmp (keyword->value, "else")) { + if (!strcmp (keyword->string, "else")) { if (!(body = keyword->next)) - return set_error (ctx, "missing body"); - return execute_any (ctx, body, NULL, result); + return ell_error (ell, "missing body"); + return ell_eval_any (ell, body, NULL, result); } - if (strcmp (keyword->value, "elif")) - return set_error (ctx, "invalid keyword: %s", keyword->value); + if (strcmp (keyword->string, "elif")) + return ell_error (ell, "invalid keyword: %s", keyword->string); } return true; } -defn (fn_map) { - struct item *body, *values; +ell_defn (ell_fn_map) { + struct ell_v *body, *values; if (!(body = args)) - return set_error (ctx, "first argument must be a function"); - if (!(values = body->next) || values->type != ITEM_LIST) - return set_error (ctx, "second argument must be a list"); - - struct item *res = NULL, **out = &res; - for (struct item *v = values->head; v; v = v->next) { - if (!execute_any (ctx, body, v, out)) { - item_free_list (res); + return ell_error (ell, "first argument must be a function"); + if (!(values = body->next) || values->type != ELL_LIST) + return ell_error (ell, "second argument must be a list"); + + struct ell_v *res = NULL, **out = &res; + for (struct ell_v *v = values->head; v; v = v->next) { + if (!ell_eval_any (ell, body, v, out)) { + ell_free_seq (res); return false; } while (*out) out = &(*out)->next; } - return check (ctx, (*result = new_list (res))); + return ell_check (ell, (*result = ell_list (res))); } -defn (fn_print) { +ell_defn (ell_fn_print) { (void) result; for (; args; args = args->next) { - if (args->type != ITEM_STRING) - print_item (args); - else if (fwrite (args->value, 1, args->len, stdout) != args->len) - return set_error (ctx, "write failed: %s", strerror (errno)); + if (args->type != ELL_STRING) + ell_print_v (args); + else if (fwrite (args->string, 1, args->len, stdout) != args->len) + return ell_error (ell, "write failed: %s", strerror (errno)); } return true; } -defn (fn_concatenate) { - struct buffer buf = BUFFER_INITIALIZER; +ell_defn (ell_fn_cat) { + struct ell_buffer buf = ELL_BUFFER_INITIALIZER; for (; args; args = args->next) { - if (args->type != ITEM_STRING) { + if (args->type != ELL_STRING) { free (buf.s); - return set_error (ctx, "cannot concatenate lists"); + return ell_error (ell, "cannot concatenate lists"); } - buffer_append (&buf, args->value, args->len); + ell_buffer_append (&buf, args->string, args->len); } - bool ok = !(ctx->memory_failure |= buf.memory_failure) - && check (ctx, (*result = new_string (buf.s, buf.len))); + bool ok = !(ell->memory_failure |= buf.memory_failure) + && ell_check (ell, (*result = ell_string (buf.s, buf.len))); free (buf.s); return ok; } -defn (fn_system) { - struct item *command = args; - if (!command || command->type != ITEM_STRING) - return set_error (ctx, "first argument must be string"); +ell_defn (ell_fn_system) { + struct ell_v *command = args; + if (!command || command->type != ELL_STRING) + return ell_error (ell, "first argument must be string"); if (command->next) - return set_error (ctx, "cannot deal with multiple arguments"); - return check (ctx, (*result = new_number (system (command->value)))); + return ell_error (ell, "cannot deal with multiple arguments"); + return ell_check (ell, (*result = ell_number (system (command->string)))); } -defn (fn_parse) { - struct item *body = args; - if (!body || body->type != ITEM_STRING) - return set_error (ctx, "first argument must be string"); +ell_defn (ell_fn_parse) { + struct ell_v *body = args; + if (!body || body->type != ELL_STRING) + return ell_error (ell, "first argument must be string"); - struct parser parser; - parser_init (&parser, args->value, args->len); + struct ell_parser p; + ell_parser_init (&p, args->string, args->len); const char *e = NULL; - bool ok = check (ctx, (*result = new_list (parser_run (&parser, &e)))); + bool ok = ell_check (ell, (*result = ell_list (ell_parser_run (&p, &e)))); if (e) - ok = set_error (ctx, "%s", e); - parser_free (&parser); + ok = ell_error (ell, "%s", e); + ell_parser_free (&p); return ok; } -defn (fn_try) { - struct item *body, *handler; +ell_defn (ell_fn_try) { + struct ell_v *body, *handler; if (!(body = args)) - return set_error (ctx, "first argument must be a function"); + return ell_error (ell, "first argument must be a function"); if (!(handler = body->next)) - return set_error (ctx, "second argument must be a function"); - if (execute_any (ctx, body, NULL, result)) + return ell_error (ell, "second argument must be a function"); + if (ell_eval_any (ell, body, NULL, result)) return true; - struct item *message; - if (ctx->memory_failure - || !check (ctx, (message = new_string (ctx->error, strlen (ctx->error))))) + struct ell_v *msg; + if (ell->memory_failure + || !ell_check (ell, (msg = ell_string (ell->error, strlen (ell->error))))) return false; - free (ctx->error); ctx->error = NULL; - item_free_list (*result); *result = NULL; + free (ell->error); ell->error = NULL; + ell_free_seq (*result); *result = NULL; - bool ok = execute_any (ctx, handler, message, result); - item_free (message); + bool ok = ell_eval_any (ell, handler, msg, result); + ell_free_v (msg); return ok; } -defn (fn_throw) { +ell_defn (ell_fn_throw) { (void) result; - struct item *message = args; - if (!message || message->type != ITEM_STRING) - return set_error (ctx, "first argument must be string"); - return set_error (ctx, message->value); + struct ell_v *message = args; + if (!message || message->type != ELL_STRING) + return ell_error (ell, "first argument must be string"); + return ell_error (ell, message->string); } -defn (fn_plus) { +ell_defn (ell_fn_plus) { double res = 0.0; for (; args; args = args->next) { - if (args->type != ITEM_STRING) - return set_error (ctx, "arguments must be strings"); - res += strtod (args->value, NULL); + if (args->type != ELL_STRING) + return ell_error (ell, "arguments must be strings"); + res += strtod (args->string, NULL); } - return check (ctx, (*result = new_number (res))); + return ell_check (ell, (*result = ell_number (res))); } -defn (fn_minus) { - if (!args || args->type != ITEM_STRING) - return set_error (ctx, "first argument must be string"); - double res = strtod (args->value, NULL); +ell_defn (ell_fn_minus) { + if (!args || args->type != ELL_STRING) + return ell_error (ell, "first argument must be string"); + double res = strtod (args->string, NULL); if (!(args = args->next)) res = -res; for (; args; args = args->next) { - if (args->type != ITEM_STRING) - return set_error (ctx, "arguments must be strings"); - res -= strtod (args->value, NULL); + if (args->type != ELL_STRING) + return ell_error (ell, "arguments must be strings"); + res -= strtod (args->string, NULL); } - return check (ctx, (*result = new_number (res))); + return ell_check (ell, (*result = ell_number (res))); } -defn (fn_multiply) { +ell_defn (ell_fn_multiply) { double res = 1.0; for (; args; args = args->next) { - if (args->type != ITEM_STRING) - return set_error (ctx, "arguments must be strings"); - res *= strtod (args->value, NULL); + if (args->type != ELL_STRING) + return ell_error (ell, "arguments must be strings"); + res *= strtod (args->string, NULL); } - return check (ctx, (*result = new_number (res))); + return ell_check (ell, (*result = ell_number (res))); } -defn (fn_divide) { - if (!args || args->type != ITEM_STRING) - return set_error (ctx, "first argument must be string"); - double res = strtod (args->value, NULL), x; +ell_defn (ell_fn_divide) { + if (!args || args->type != ELL_STRING) + return ell_error (ell, "first argument must be string"); + double res = strtod (args->string, NULL), x; for (args = args->next; args; args = args->next) { - if (args->type != ITEM_STRING) - return set_error (ctx, "arguments must be strings"); - if (!(x = strtod (args->value, NULL))) - return set_error (ctx, "division by zero"); + if (args->type != ELL_STRING) + return ell_error (ell, "arguments must be strings"); + if (!(x = strtod (args->string, NULL))) + return ell_error (ell, "division by zero"); res /= x; } - return check (ctx, (*result = new_number (res))); + return ell_check (ell, (*result = ell_number (res))); } -defn (fn_not) { +ell_defn (ell_fn_not) { if (!args) - return set_error (ctx, "missing argument"); - return check (ctx, (*result = new_boolean (!truthy (args)))); + return ell_error (ell, "missing argument"); + return ell_check (ell, (*result = ell_boolean (!ell_truthy (args)))); } -defn (fn_and) { +ell_defn (ell_fn_and) { if (!args) - return check (ctx, (*result = new_boolean (true))); + return ell_check (ell, (*result = ell_boolean (true))); for (; args; args = args->next) { - item_free_list (*result); + ell_free_seq (*result); *result = NULL; - if (!execute_any (ctx, args, NULL, result)) + if (!ell_eval_any (ell, args, NULL, result)) return false; - if (!truthy (*result)) - return check (ctx, (*result = new_boolean (false))); + if (!ell_truthy (*result)) + return ell_check (ell, (*result = ell_boolean (false))); } return true; } -defn (fn_or) { +ell_defn (ell_fn_or) { for (; args; args = args->next) { - if (!execute_any (ctx, args, NULL, result)) + if (!ell_eval_any (ell, args, NULL, result)) return false; - if (truthy (*result)) + if (ell_truthy (*result)) return true; - item_free_list (*result); + ell_free_seq (*result); *result = NULL; } - return check (ctx, (*result = new_boolean (false))); + return ell_check (ell, (*result = ell_boolean (false))); } -defn (fn_eq) { - struct item *etalon = args; - if (!etalon || etalon->type != ITEM_STRING) - return set_error (ctx, "first argument must be string"); +ell_defn (ell_fn_eq) { + struct ell_v *etalon = args; + if (!etalon || etalon->type != ELL_STRING) + return ell_error (ell, "first argument must be string"); bool res = true; for (args = etalon->next; args; args = args->next) { - if (args->type != ITEM_STRING) - return set_error (ctx, "arguments must be strings"); - if (!(res &= !strcmp (etalon->value, args->value))) + if (args->type != ELL_STRING) + return ell_error (ell, "arguments must be strings"); + if (!(res &= !strcmp (etalon->string, args->string))) break; } - return check (ctx, (*result = new_boolean (res))); + return ell_check (ell, (*result = ell_boolean (res))); } -defn (fn_lt) { - struct item *etalon = args; - if (!etalon || etalon->type != ITEM_STRING) - return set_error (ctx, "first argument must be string"); +ell_defn (ell_fn_lt) { + struct ell_v *etalon = args; + if (!etalon || etalon->type != ELL_STRING) + return ell_error (ell, "first argument must be string"); bool res = true; for (args = etalon->next; args; args = args->next) { - if (args->type != ITEM_STRING) - return set_error (ctx, "arguments must be strings"); - if (!(res &= strcmp (etalon->value, args->value) < 0)) + if (args->type != ELL_STRING) + return ell_error (ell, "arguments must be strings"); + if (!(res &= strcmp (etalon->string, args->string) < 0)) break; etalon = args; } - return check (ctx, (*result = new_boolean (res))); + return ell_check (ell, (*result = ell_boolean (res))); } -defn (fn_equals) { - struct item *etalon = args; - if (!etalon || etalon->type != ITEM_STRING) - return set_error (ctx, "first argument must be string"); +ell_defn (ell_fn_equals) { + struct ell_v *etalon = args; + if (!etalon || etalon->type != ELL_STRING) + return ell_error (ell, "first argument must be string"); bool res = true; for (args = etalon->next; args; args = args->next) { - if (args->type != ITEM_STRING) - return set_error (ctx, "arguments must be strings"); - if (!(res &= strtod (etalon->value, NULL) - == strtod (args->value, NULL))) + if (args->type != ELL_STRING) + return ell_error (ell, "arguments must be strings"); + if (!(res &= strtod (etalon->string, NULL) + == strtod (args->string, NULL))) break; } - return check (ctx, (*result = new_boolean (res))); + return ell_check (ell, (*result = ell_boolean (res))); } -defn (fn_less) { - struct item *etalon = args; - if (!etalon || etalon->type != ITEM_STRING) - return set_error (ctx, "first argument must be string"); +ell_defn (ell_fn_less) { + struct ell_v *etalon = args; + if (!etalon || etalon->type != ELL_STRING) + return ell_error (ell, "first argument must be string"); bool res = true; for (args = etalon->next; args; args = args->next) { - if (args->type != ITEM_STRING) - return set_error (ctx, "arguments must be strings"); - if (!(res &= strtod (etalon->value, NULL) < strtod (args->value, NULL))) + if (args->type != ELL_STRING) + return ell_error (ell, "arguments must be strings"); + if (!(res &= strtod (etalon->string, NULL) + < strtod (args->string, NULL))) break; etalon = args; } - return check (ctx, (*result = new_boolean (res))); + return ell_check (ell, (*result = ell_boolean (res))); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const char init_program[] = +static struct ell_handler_pair { + const char *name; ///< Name of function + EllHandler handler; ///< Handler for the function +} ell_std_native[] = { + { "local", ell_fn_local }, + { "set", ell_fn_set }, + { "list", ell_fn_list }, + { "values", ell_fn_values }, + { "if", ell_fn_if }, + { "map", ell_fn_map }, + { "print", ell_fn_print }, + { "..", ell_fn_cat }, + { "system", ell_fn_system }, + { "parse", ell_fn_parse }, + { "try", ell_fn_try }, + { "throw", ell_fn_throw }, + { "+", ell_fn_plus }, + { "-", ell_fn_minus }, + { "*", ell_fn_multiply }, + { "/", ell_fn_divide }, + { "not", ell_fn_not }, + { "and", ell_fn_and }, + { "or", ell_fn_or }, + { "eq?", ell_fn_eq }, + { "lt?", ell_fn_lt }, + { "=", ell_fn_equals }, + { "<", ell_fn_less }, +}; + +const char ell_std_composed[] = "set unless { if (not (@1)) @2 }\n" - "set filter { local [_body _list] @1 @2\n" - " map { if (@_body @1) { @1 } } @_list }\n" - "set for { local [_list _body] @1 @2\n" - " try { map { @_body @1 } @_list } {\n" - " if (ne? @1 _break) { throw @1 } } }\n" + "set filter { local [_body _list] @1 @2;" + " map { if (@_body @1) { @1 } } @_list }\n" + "set for { local [_list _body] @1 @2;" + " try { map { @_body @1 } @_list } { if (ne? @1 _break) { throw @1 } } }\n" "set break { throw _break }\n" // TODO: we should be able to apply them to all arguments @@ -1318,41 +1354,22 @@ const char init_program[] = "set >= { not (< @1 @2) }\n" "set > { < @2 @1 }\n"; static bool -init_runtime_library (struct context *ctx) { - if (!native_register (ctx, "local", fn_local) - || !native_register (ctx, "set", fn_set) - || !native_register (ctx, "list", fn_list) - || !native_register (ctx, "values", fn_values) - || !native_register (ctx, "if", fn_if) - || !native_register (ctx, "map", fn_map) - || !native_register (ctx, "print", fn_print) - || !native_register (ctx, "..", fn_concatenate) - || !native_register (ctx, "system", fn_system) - || !native_register (ctx, "parse", fn_parse) - || !native_register (ctx, "try", fn_try) - || !native_register (ctx, "throw", fn_throw) - || !native_register (ctx, "+", fn_plus) - || !native_register (ctx, "-", fn_minus) - || !native_register (ctx, "*", fn_multiply) - || !native_register (ctx, "/", fn_divide) - || !native_register (ctx, "not", fn_not) - || !native_register (ctx, "and", fn_and) - || !native_register (ctx, "or", fn_or) - || !native_register (ctx, "eq?", fn_eq) - || !native_register (ctx, "lt?", fn_lt) - || !native_register (ctx, "=", fn_equals) - || !native_register (ctx, "<", fn_less)) - return false; +ell_std_initialize (struct ell *ell) { + for (size_t i = 0; i < ELL_N_ELEMENTS (ell_std_native); i++) { + struct ell_handler_pair *pair = &ell_std_native[i]; + if (!ell_native_register (ell, pair->name, pair->handler)) + return false; + } - struct parser parser; - parser_init (&parser, init_program, sizeof init_program); + struct ell_parser p; + ell_parser_init (&p, ell_std_composed, sizeof ell_std_composed); const char *e = NULL; - struct item *result = NULL; - struct item *program = parser_run (&parser, &e); - bool ok = !e && execute_block (ctx, program, NULL, &result); - parser_free (&parser); - item_free_list (program); - item_free_list (result); + struct ell_v *result = NULL; + struct ell_v *program = ell_parser_run (&p, &e); + bool ok = !e && ell_eval_block (ell, program, NULL, &result); + ell_parser_free (&p); + ell_free_seq (program); + ell_free_seq (result); return ok; } diff --git a/interpreter.c b/interpreter.c index 7be4fc7..c87e0ce 100644 --- a/interpreter.c +++ b/interpreter.c @@ -28,44 +28,44 @@ main (int argc, char *argv[]) { } int c; - struct buffer buf = BUFFER_INITIALIZER; + struct ell_buffer buf = ELL_BUFFER_INITIALIZER; while ((c = fgetc (fp)) != EOF) - buffer_append_c (&buf, c); - buffer_append_c (&buf, 0); + ell_buffer_append_c (&buf, c); + ell_buffer_append_c (&buf, 0); fclose (fp); - struct parser parser; - parser_init (&parser, buf.s, buf.len - 1); + struct ell_parser p; + ell_parser_init (&p, buf.s, buf.len - 1); const char *e = NULL; - struct item *program = parser_run (&parser, &e); + struct ell_v *program = ell_parser_run (&p, &e); free (buf.s); if (e) { printf ("%s: %s\n", "parse error", e); return 1; } - parser_free (&parser); + ell_parser_free (&p); - struct context ctx; - context_init (&ctx); - if (!init_runtime_library (&ctx)) + struct ell ell; + ell_init (&ell); + if (!ell_std_initialize (&ell)) printf ("%s\n", "runtime library initialization failed"); // In this one place we optimistically expect allocation to succeed - struct item *args = NULL, **tail = &args; + struct ell_v *args = NULL, **tail = &args; for (int i = 2; i < argc; i++) - tail = &(*tail = new_string (argv[i], strlen (argv[i])))->next; + tail = &(*tail = ell_string (argv[i], strlen (argv[i])))->next; - struct item *result = NULL; - (void) execute_block (&ctx, program, args, &result); - item_free_list (result); - item_free_list (program); + struct ell_v *result = NULL; + (void) ell_eval_block (&ell, program, args, &result); + ell_free_seq (result); + ell_free_seq (program); - const char *failure = ctx.error; - if (ctx.memory_failure) + const char *failure = ell.error; + if (ell.memory_failure) failure = "memory allocation failure"; if (failure) printf ("%s: %s\n", "runtime error", failure); - context_free (&ctx); + ell_free (&ell); return 0; } @@ -22,23 +22,23 @@ #include <readline/history.h> static void -run (struct context *ctx, struct item *program) { - struct item *result = NULL; - (void) execute_block (ctx, program, NULL, &result); - item_free_list (program); +run (struct ell *ell, struct ell_v *program) { + struct ell_v *result = NULL; + (void) ell_eval_block (ell, program, NULL, &result); + ell_free_seq (program); - const char *failure = ctx->error; - if (ctx->memory_failure) + const char *failure = ell->error; + if (ell->memory_failure) failure = "memory allocation failure"; if (failure) { printf ("\x1b[31m%s: %s\x1b[0m\n", "runtime error", failure); - free (ctx->error); - ctx->error = NULL; - ctx->memory_failure = false; + free (ell->error); + ell->error = NULL; + ell->memory_failure = false; } else { - print_item_list (result); + ell_print_seq (result); putchar ('\n'); - item_free_list (result); + ell_free_seq (result); } } @@ -49,7 +49,7 @@ init_readline (void) { return 0; } -struct context ctx; +static struct ell ell; static char ** complete (const char *text, int start, int end) { @@ -61,18 +61,18 @@ complete (const char *text, int start, int end) { static char *buf[128]; size_t n = 1, len = strlen (text); - for (struct item *item = ctx.globals; item; item = item->next) - if (n < 127 && !strncmp (item->head->value, text, len)) - buf[n++] = format ("%s", item->head->value); - for (struct native_fn *iter = ctx.native; iter; iter = iter->next) + for (struct ell_v *v = ell.globals; v; v = v->next) + if (n < 127 && !strncmp (v->head->string, text, len)) + buf[n++] = ell_format ("%s", v->head->string); + for (struct ell_native_fn *iter = ell.native; iter; iter = iter->next) if (n < 127 && !strncmp (iter->name, text, len)) - buf[n++] = format ("%s", iter->name); + buf[n++] = ell_format ("%s", iter->name); if (n < 2) return NULL; // This never actually completes anything, just shows the options, // we'd have to figure out the longest common prefix - buf[0] = format ("%s", text); + buf[0] = ell_format ("%s", text); buf[n++] = NULL; char **copy = malloc (sizeof *buf * n); @@ -84,8 +84,8 @@ int main (int argc, char *argv[]) { (void) argc; - context_init (&ctx); - if (!init_runtime_library (&ctx)) + ell_init (&ell); + if (!ell_std_initialize (&ell)) printf ("%s\n", "runtime library initialization failed"); using_history (); @@ -96,21 +96,21 @@ main (int argc, char *argv[]) { char *line; while ((line = readline ("> "))) { - struct parser parser; - parser_init (&parser, line, strlen (line)); + struct ell_parser p; + ell_parser_init (&p, line, strlen (line)); add_history (line); const char *e = NULL; - struct item *program = parser_run (&parser, &e); + struct ell_v *program = ell_parser_run (&p, &e); free (line); if (e) printf ("\x1b[31m%s: %s\x1b[0m\n", "parse error", e); else - run (&ctx, program); - parser_free (&parser); + run (&ell, program); + ell_parser_free (&p); } putchar ('\n'); - context_free (&ctx); + ell_free (&ell); return 0; } |