#include #include #include #include size_t GID = 0; typedef struct { void **data; size_t length; } list_t; list_t *list_new(void) { list_t *list = malloc(sizeof(list_t)); list->data = NULL; list->length = 0; return list; } void list_push(list_t *l, void *v) { size_t i = l->length++; l->data = realloc(l->data, l->length * sizeof(void *)); l->data[i] = v; } void *list_pop(list_t *l) { if (!l->length) return NULL; return l->data[--l->length]; } void *list_index(list_t *l, ssize_t index) { if (!l->length) return NULL; if (index < 0) index += ((ssize_t)l->length); if (index < 0 || index >= l->length) return NULL; return l->data[index]; } void list_set(list_t *l, ssize_t index, void *v) { if (!l->length) return; if (index < 0) index += ((ssize_t)l->length); if (index < 0 || index >= l->length) return; l->data[index] = v; } typedef struct { size_t *data; size_t length; } stack_t; stack_t *stack_new(void) { stack_t *stack = malloc(sizeof(list_t)); stack->data = NULL; stack->length = 0; return stack; } void stack_push(stack_t *l, size_t v) { size_t i = l->length++; l->data = realloc(l->data, l->length * sizeof(size_t)); l->data[i] = v; } size_t stack_pop(stack_t *l) { if (!l->length) return 0; return l->data[--l->length]; } struct entry_t { char *key; void *value; }; struct table_t { struct entry_t *entries; size_t used; size_t capacity; }; typedef struct entry_t entry_t; typedef struct table_t table_t; table_t *table_new() { table_t *table = malloc(sizeof(table_t)); table->used = 0; table->capacity = 32; table->entries = calloc(table->capacity, sizeof(entry_t)); return table; } unsigned long ht_hash(const char* key) { unsigned long hash = 5381; int c; while ((c = *key++)) hash = ((hash << 5) + hash) + c; return hash; } void *table_get(table_t *table, char *key) { if (!table->used) return NULL; unsigned long hash = ht_hash(key); size_t index = hash % table->capacity; size_t i = index; while (table->entries[i].key) { if (strcmp(table->entries[i].key, key) == 0) return table->entries[i].value; i++; if (i >= table->capacity) i = 0; if (i == index) break; } return NULL; } static void table_entry_set(entry_t *entries, char *key, void *value, size_t capacity, size_t *used) { unsigned long hash = ht_hash(key); size_t index = hash % capacity; size_t i = index; while (entries[i].key) { if (strcmp(entries[i].key, key) == 0) { entries[i].value = value; return; } i++; if (i >= capacity) i = 0; if (i == index) break; } if (used) (*used)++; entries[i].key = key; entries[i].value = value; } table_t *table_set(table_t *table, char *key, void *value) { if (table->used >= table->capacity) { size_t capacity = table->capacity + 32; entry_t *entries = calloc(capacity, sizeof(entry_t)); for (size_t i = 0; i < table->capacity; i++) { entry_t entry = table->entries[i]; if (entry.key) table_entry_set(entries, entry.key, entry.value, capacity, NULL); } table->entries = entries; table->capacity = capacity; } table_entry_set(table->entries, key, value, table->capacity, &table->used); return table; } #define table_iterate(table, code) \ { \ if ((table)->used) { \ size_t i = 0; \ while (i < (table)->capacity) { \ entry_t entry = (table)->entries[i]; \ if (entry.key) { \ code; \ } \ i++; \ } \ } \ } typedef struct { char *str; size_t size; } buffer_t; buffer_t *buffer_new(void) { buffer_t *buf = malloc(sizeof(buffer_t)); buf->str = NULL; buf->size = 0; return buf; } void buffer_append(buffer_t *buf, char c) { buf->size++; void *p = malloc(sizeof(char) * buf->size); if (buf->str) memcpy(p, buf->str, buf->size - 1); buf->str = p; buf->str[buf->size - 1] = c; } char *buffer_read(buffer_t *buf) { if (buf->size == 0 || buf->str[buf->size - 1]) buffer_append(buf, 0); return buf->str; } void buffer_appends(buffer_t *buf, char *s) { for (size_t i = 0; i < strlen(s); i++) buffer_append(buf, s[i]); } void buffer_appendb(buffer_t *dst, buffer_t *src) { for (size_t i = 0; i < src->size; i++) buffer_append(dst, src->str[i]); } void buffer_fmt(buffer_t *buf, const char *fmt, ...) { va_list args; va_start(args, fmt); size_t size = vsnprintf(NULL, 0, fmt, args); char *str = malloc(sizeof(char) * (size + 1)); vsnprintf(str, size + 1, fmt, args); va_end(args); buffer_appends(buf, str); } typedef struct { enum { T_EOF, T_NUMBER, T_STRING, T_NAME, T_VAR, T_LET, T_IF, T_ELSE, T_ELIF, T_FOR, T_OF, T_BREAK, T_CONTINUE, T_PASS, T_FUNC, T_USE, T_RETURN, T_DEFER, T_REQUIRE, T_TRY, T_CATCH, T_THROW, T_GOTO, T_IS, T_IN, T_LPAR, T_RPAR, T_LSB, T_RSB, T_LCB, T_RCB, T_EQUALS, T_NOTEQUALS, T_PLUSASSIGN, T_MINUSASSIGN, T_SLASHASSIGN, T_STARASSIGN, T_SLASHSLASHASSIGN, T_PERCENTASSIGN, T_BARBAR, T_ANDAND, T_STARSTAR, T_PLUSPLUS, T_MINUSMINUS, T_SLASHSLASH, T_PLUS, T_MINUS, T_QM, T_COLON, T_BAR, T_AND, T_LT, T_LTLT, T_GT, T_GTGT, T_LE, T_GE, T_STAR, T_SLASH, T_PERCENT, T_COMMA, T_DOT, T_BANG, T_RAISE, T_TILDE, T_INLINE, T_ASSIGN, T_SEMI } tag; char *text; size_t fi; size_t pos; } token_t; token_t *token(int tag, char *text) { token_t *tok = malloc(sizeof(token_t)); tok->tag = tag; tok->text = text; return tok; } #define TK(tk) (token(T_##tk, NULL)) #define WS() while (source[*pos] == ' ' || source[*pos] == '\t' || source[*pos] == '\n' || source[*pos] == '\r') { (*pos)++; } void consume_ignored(char *source, size_t *pos) { WS(); while (source[*pos] == '#') { (*pos)++; for (;;) { if (!source[*pos]) break; if (source[*pos] == '\n') { (*pos)++; break; } (*pos)++; } WS(); } } list_t *FILES; list_t *REQUIRED; int is_required(char *path) { for (size_t i = 0; i < REQUIRED->length; i++) if (strcmp(REQUIRED->data[i], path) == 0) return 1; return 0; } void traverse(char *source, size_t pos, size_t *line, size_t *col) { *line = 1; *col = 1; for (size_t i = 0; i < pos; i++) { if (source[i] == '\n') { (*line)++; (*col) = 1; } else (*col)++; } } void format_error(char *filename, char *source, size_t pos, char *fmt, ...) { size_t line, col; traverse(source, pos, &line, &col); va_list args; va_start(args, fmt); fprintf(stderr, "%s (%zu:%zu): ", filename, line, col); vfprintf(stderr, fmt, args); fputc('\n', stderr); va_end(args); } #define GETFNAME(fi) ((char *)((list_t *)list_index(FILES, fi))->data[0]) #define GETSRC(fi) ((char *)((list_t *)list_index(FILES, fi))->data[1]) #define LEX_ERROR(fmt, ...) { format_error(GETFNAME(-1), source, *pos, fmt, ##__VA_ARGS__); exit(1); } token_t *next_token(char *source, size_t *pos) { if (!source[*pos]) return token(T_EOF, NULL); if (source[*pos] == '"' || source[*pos] == '\'') { char term = source[(*pos)++]; buffer_t *text = buffer_new(); while (source[*pos] != term) { if (!source[*pos]) LEX_ERROR("unterminated string literal"); char c = source[(*pos)++]; if (c == '\\') { char nc = source[(*pos)++]; if (!nc) continue; switch (nc) { case 'n': buffer_appends(text, "\\n"); break; case 't': buffer_appends(text, "\\t"); break; case 'r': buffer_appends(text, "\\r"); break; case 'b': buffer_appends(text, "\\b"); break; case 'e': buffer_appends(text, "\\e"); break; case 's': buffer_appends(text, " "); break; case '"': buffer_appends(text, "\\\""); break; case '\\': buffer_appends(text, "\\\\"); break; default: buffer_append(text, nc); break; } continue; } if (c == '"' || c == '\\') buffer_append(text, '\\'); buffer_append(text, c); } (*pos)++; return token(T_STRING, buffer_read(text)); } else if (isdigit(source[*pos])) { buffer_t *number = buffer_new(); int dot = 0; int sub = 0; do { buffer_append(number, source[(*pos)++]); if (!dot && source[*pos] == '.') { buffer_append(number, source[(*pos)++]); if (!isdigit(source[*pos])) LEX_ERROR("illegal number literal (missing part after floating point)"); dot = 1; } else if (!sub && source[*pos] == '.') { (*pos)++; sub = 1; } else if (sub) sub = 0; } while (isdigit(source[*pos])); return token(T_NUMBER, buffer_read(number)); } else if (isalpha(source[*pos]) || source[*pos] == '_') { buffer_t *text = buffer_new(); do { buffer_append(text, source[(*pos)++]); } while (isalpha(source[*pos]) || source[*pos] == '_' || isdigit(source[*pos])); char *name = buffer_read(text); if (strcmp(name, "var") == 0) return TK(VAR); else if (strcmp(name, "let") == 0) return TK(LET); else if (strcmp(name, "if") == 0) return TK(IF); else if (strcmp(name, "else") == 0) return TK(ELSE); else if (strcmp(name, "elif") == 0) return TK(ELIF); else if (strcmp(name, "for") == 0) return TK(FOR); else if (strcmp(name, "break") == 0) return TK(BREAK); else if (strcmp(name, "continue") == 0) return TK(CONTINUE); else if (strcmp(name, "func") == 0) return TK(FUNC); else if (strcmp(name, "use") == 0) return TK(USE); else if (strcmp(name, "return") == 0) return TK(RETURN); else if (strcmp(name, "defer") == 0) return TK(DEFER); else if (strcmp(name, "pass") == 0) return TK(PASS); else if (strcmp(name, "require") == 0) return TK(REQUIRE); else if (strcmp(name, "try") == 0) return TK(TRY); else if (strcmp(name, "catch") == 0) return TK(CATCH); else if (strcmp(name, "throw") == 0) return TK(THROW); else if (strcmp(name, "goto") == 0) return TK(GOTO); else if (strcmp(name, "is") == 0) return TK(IS); else if (strcmp(name, "in") == 0) return TK(IN); else if (strcmp(name, "of") == 0) return TK(OF); else if (strcmp(name, "inline") == 0) return TK(INLINE); return token(T_NAME, name); } else if (strncmp(&source[*pos], "==", 2) == 0 && ++(*pos) && ++(*pos)) return TK(EQUALS); else if (strncmp(&source[*pos], "!=", 2) == 0 && ++(*pos) && ++(*pos)) return TK(NOTEQUALS); else if (strncmp(&source[*pos], "+=", 2) == 0 && ++(*pos) && ++(*pos)) return TK(PLUSASSIGN); else if (strncmp(&source[*pos], "-=", 2) == 0 && ++(*pos) && ++(*pos)) return TK(MINUSASSIGN); else if (strncmp(&source[*pos], "*=", 2) == 0 && ++(*pos) && ++(*pos)) return TK(STARASSIGN); else if (strncmp(&source[*pos], "/=", 2) == 0 && ++(*pos) && ++(*pos)) return TK(SLASHASSIGN); else if (strncmp(&source[*pos], "//=", 3) == 0 && ++(*pos) && ++(*pos) && ++(*pos)) return TK(SLASHSLASHASSIGN); else if (strncmp(&source[*pos], "%=", 2) == 0 && ++(*pos) && ++(*pos)) return TK(PERCENTASSIGN); else if (strncmp(&source[*pos], "||", 2) == 0 && ++(*pos) && ++(*pos)) return TK(BARBAR); else if (strncmp(&source[*pos], "&&", 2) == 0 && ++(*pos) && ++(*pos)) return TK(ANDAND); else if (strncmp(&source[*pos], "++", 2) == 0 && ++(*pos) && ++(*pos)) return TK(PLUSPLUS); else if (strncmp(&source[*pos], "--", 2) == 0 && ++(*pos) && ++(*pos)) return TK(MINUSMINUS); else if (strncmp(&source[*pos], "//", 2) == 0 && ++(*pos) && ++(*pos)) return TK(SLASHSLASH); else if (strncmp(&source[*pos], "**", 2) == 0 && ++(*pos) && ++(*pos)) return TK(STARSTAR); else if (strncmp(&source[*pos], "<<", 2) == 0 && ++(*pos) && ++(*pos)) return TK(LTLT); else if (strncmp(&source[*pos], ">>", 2) == 0 && ++(*pos) && ++(*pos)) return TK(GTGT); else if (strncmp(&source[*pos], "<=", 2) == 0 && ++(*pos) && ++(*pos)) return TK(LE); else if (strncmp(&source[*pos], ">=", 2) == 0 && ++(*pos) && ++(*pos)) return TK(GE); else if (source[*pos] == '(' && ++(*pos)) return TK(LPAR); else if (source[*pos] == ')' && ++(*pos)) return TK(RPAR); else if (source[*pos] == '[' && ++(*pos)) return TK(LSB); else if (source[*pos] == ']' && ++(*pos)) return TK(RSB); else if (source[*pos] == '{' && ++(*pos)) return TK(LCB); else if (source[*pos] == '}' && ++(*pos)) return TK(RCB); else if (source[*pos] == '+' && ++(*pos)) return TK(PLUS); else if (source[*pos] == '-' && ++(*pos)) return TK(MINUS); else if (source[*pos] == '*' && ++(*pos)) return TK(STAR); else if (source[*pos] == '/' && ++(*pos)) return TK(SLASH); else if (source[*pos] == '%' && ++(*pos)) return TK(PERCENT); else if (source[*pos] == '?' && ++(*pos)) return TK(QM); else if (source[*pos] == ':' && ++(*pos)) return TK(COLON); else if (source[*pos] == '=' && ++(*pos)) return TK(ASSIGN); else if (source[*pos] == ';' && ++(*pos)) return TK(SEMI); else if (source[*pos] == ',' && ++(*pos)) return TK(COMMA); else if (source[*pos] == '.' && ++(*pos)) return TK(DOT); else if (source[*pos] == '<' && ++(*pos)) return TK(LT); else if (source[*pos] == '>' && ++(*pos)) return TK(GT); else if (source[*pos] == '!' && ++(*pos)) return TK(BANG); else if (source[*pos] == '|' && ++(*pos)) return TK(BAR); else if (source[*pos] == '&' && ++(*pos)) return TK(AND); else if (source[*pos] == '^' && ++(*pos)) return TK(RAISE); else if (source[*pos] == '~' && ++(*pos)) return TK(TILDE); LEX_ERROR("unexpected input") } list_t *tokenize(char *source) { size_t pos = 0; list_t *toks = list_new(); do { consume_ignored(source, &pos); size_t tok_pos = pos; token_t *tok = next_token(source, &pos); tok->fi = FILES->length-1; tok->pos = tok_pos; list_push(toks, tok); if (tok->tag == T_EOF) break; } while (1); return toks; } struct _node_t { enum { N_PROGRAM, N_EXPRSTMT, N_BLOCK, N_NOT, N_NEGATE, N_BNOT, N_LITERAL, N_LIST, N_TUPLE, N_NILTUPLE, N_TABLE, N_CALL, N_MEMBER, N_INDEX, N_ADD, N_SUB, N_MUL, N_DIV, N_IDIV, N_MOD, N_POW, N_SHL, N_SHR, N_XOR, N_BOR, N_BAND, N_ASSIGN, N_ASSIGN_ADD, N_ASSIGN_SUB, N_ASSIGN_MUL, N_ASSIGN_DIV, N_ASSIGN_IDIV, N_ASSIGN_MOD, N_ASSIGN_POW, N_EQUALS, N_NOTEQUALS, N_IS, N_IN, N_NOTIS, N_NOTIN, N_LT, N_GT, N_LE, N_GE, N_INC, N_DEC, N_VAR, N_LET, N_IF, N_FOR, N_FOROF, N_BREAK, N_CONTINUE, N_FUNCDEF, N_RETURN, N_DEFER, N_PASS, N_REQUIRE, N_TRY, N_THROW, N_LABEL, N_GOTO, N_INLINE, N_IFEXPR, N_FUNCEXPR, N_LOGOR, N_LOGAND, } tag; struct _node_t *a; struct _node_t *b; struct _node_t *c; struct _node_t *d; list_t *l; table_t *h; table_t *h2; token_t *t; size_t fi; size_t pos; }; typedef struct _node_t node_t; node_t *node_pos(node_t *node, size_t fi, size_t pos) { node->fi = fi; node->pos = pos; return node; } node_t *nodet(int tag, token_t *t) { node_t *node = malloc(sizeof(node_t)); node->tag = tag; node->t = t; return node; } #define NODET(n, a) (node_pos(nodet(N_##n, (a)), ((token_t *)tokens->data[(*pos)>0?(*pos)-1:(*pos)])->fi, ((token_t *)tokens->data[(*pos)>0?(*pos)-1:(*pos)])->pos)) node_t *nodel(int tag, list_t *l) { node_t *node = malloc(sizeof(node_t)); node->tag = tag; node->l = l; return node; } #define NODEL(n, a) (node_pos(nodel(N_##n, (a)), ((token_t *)tokens->data[(*pos)>0?(*pos)-1:(*pos)])->fi, ((token_t *)tokens->data[(*pos)>0?(*pos)-1:(*pos)])->pos)) node_t *nodeh(int tag, table_t *h) { node_t *node = malloc(sizeof(node_t)); node->tag = tag; node->h = h; return node; } #define NODEH(n, a) (node_pos(nodeh(N_##n, (a)), ((token_t *)tokens->data[(*pos)>0?(*pos)-1:(*pos)])->fi, ((token_t *)tokens->data[(*pos)>0?(*pos)-1:(*pos)])->pos)) node_t *node0(int tag) { node_t *node = malloc(sizeof(node_t)); node->tag = tag; return node; } #define NODE0(n) (node_pos(node0(N_##n), ((token_t *)tokens->data[(*pos)>0?(*pos)-1:(*pos)])->fi, ((token_t *)tokens->data[(*pos)>0?(*pos)-1:(*pos)])->pos)) node_t *node1(int tag, node_t *a) { node_t *node = malloc(sizeof(node_t)); node->tag = tag; node->a = a; return node; } #define NODE1(n, a) (node_pos(node1(N_##n, (a)), ((token_t *)tokens->data[(*pos)>0?(*pos)-1:(*pos)])->fi, ((token_t *)tokens->data[(*pos)>0?(*pos)-1:(*pos)])->pos)) node_t *node1l(int tag, node_t *a, list_t *l) { node_t *node = malloc(sizeof(node_t)); node->tag = tag; node->a = a; node->l = l; return node; } #define NODE1l(n, a, l) (node_pos(node1l(N_##n, (a), (l)), ((token_t *)tokens->data[(*pos)>0?(*pos)-1:(*pos)])->fi, ((token_t *)tokens->data[(*pos)>0?(*pos)-1:(*pos)])->pos)) node_t *node1t(int tag, node_t *a, token_t *t) { node_t *node = malloc(sizeof(node_t)); node->tag = tag; node->a = a; node->t = t; return node; } #define NODE1t(n, a, t) (node_pos(node1t(N_##n, (a), (t)), ((token_t *)tokens->data[(*pos)>0?(*pos)-1:(*pos)])->fi, ((token_t *)tokens->data[(*pos)>0?(*pos)-1:(*pos)])->pos)) node_t *node2(int tag, node_t *a, node_t *b) { node_t *node = malloc(sizeof(node_t)); node->tag = tag; node->a = a; node->b = b; return node; } #define NODE2(n, a, b) (node_pos(node2(N_##n, (a), (b)), ((token_t *)tokens->data[(*pos)>0?(*pos)-1:(*pos)])->fi, ((token_t *)tokens->data[(*pos)>0?(*pos)-1:(*pos)])->pos)) node_t *node2t(int tag, node_t *a, node_t *b, token_t *t) { node_t *node = malloc(sizeof(node_t)); node->tag = tag; node->a = a; node->b = b; node->t = t; return node; } #define NODE2t(n, a, b, c) (node_pos(node2t(N_##n, (a), (b), (c)), ((token_t *)tokens->data[(*pos)>0?(*pos)-1:(*pos)])->fi, ((token_t *)tokens->data[(*pos)>0?(*pos)-1:(*pos)])->pos)) node_t *node3(int tag, node_t *a, node_t *b, node_t *c) { node_t *node = malloc(sizeof(node_t)); node->tag = tag; node->a = a; node->b = b; node->c = c; return node; } #define NODE3(n, a, b, c) (node_pos(node3(N_##n, (a), (b), (c)), ((token_t *)tokens->data[(*pos)>0?(*pos)-1:(*pos)])->fi, ((token_t *)tokens->data[(*pos)>0?(*pos)-1:(*pos)])->pos)) node_t *node4(int tag, node_t *a, node_t *b, node_t *c, node_t *d) { node_t *node = malloc(sizeof(node_t)); node->tag = tag; node->a = a; node->b = b; node->c = c; node->d = d; return node; } #define NODE4(n, a, b, c, d) (node_pos(node4(N_##n, (a), (b), (c), (d)), ((token_t *)tokens->data[(*pos)>0?(*pos)-1:(*pos)])->fi, ((token_t *)tokens->data[(*pos)>0?(*pos)-1:(*pos)])->pos)) node_t *nodef(int tag, token_t *name, table_t *params, table_t *captured, node_t *body) { node_t *node = malloc(sizeof(node_t)); node->tag = tag; node->t = name; node->h = params; node->h2 = captured; node->a = body; return node; } #define NODEF(n, a, b, c, d) (node_pos(nodef(N_##n, (a), (b), (c), (d)), ((token_t *)tokens->data[(*pos)>0?(*pos)-1:(*pos)])->fi, ((token_t *)tokens->data[(*pos)>0?(*pos)-1:(*pos)])->pos)) #define AT(tk) (*pos < tokens->length && ((token_t *)tokens->data[*pos])->tag == T_##tk) #define ATP(tk, p) ((*pos)+p < tokens->length && ((token_t *)tokens->data[(*pos)+p])->tag == T_##tk) #define MATCH(tk) (AT(tk) && ++(*pos)) #define PARSE_ERROR(fmt, ...) { format_error(GETFNAME(((token_t *)tokens->data[(*pos)>0?(*pos)-1:(*pos)])->fi), GETSRC(((token_t *)tokens->data[(*pos)>0?(*pos)-1:(*pos)])->fi), ((token_t *)tokens->data[(*pos)>0?(*pos)-1:(*pos)])->pos, fmt, ##__VA_ARGS__); exit(1); } #define EXPECT(tk, s) { if (!MATCH(tk)) PARSE_ERROR("expected %s", (s)); } node_t *parse_expr(list_t *tokens, size_t *pos); list_t *parse_sequence(list_t *tokens, size_t *pos, int term) { list_t *seq = list_new(); do { if (term != -1 && *pos < tokens->length && ((token_t *)tokens->data[*pos])->tag == term) break; list_push(seq, parse_expr(tokens, pos)); } while (MATCH(COMMA)); return seq; } node_t *parse_func(list_t *tokens, size_t *pos, int is_expr); node_t *parse_primary(list_t *tokens, size_t *pos) { if (MATCH(FUNC)) return parse_func(tokens, pos, 1); else if (MATCH(LPAR)) { if (MATCH(RPAR)) return NODE0(NILTUPLE); node_t *a = parse_expr(tokens, pos); if (MATCH(COMMA)) { list_t *l = list_new(); list_push(l, a); if (!AT(RPAR)) do { node_t *n = parse_expr(tokens, pos); list_push(l, n); } while (MATCH(COMMA)); a = NODEL(TUPLE, l); } EXPECT(RPAR, ")"); return a; } else if (MATCH(LSB)) { list_t *a = parse_sequence(tokens, pos, T_RSB); EXPECT(RSB, "]"); return NODEL(LIST, a); } else if (MATCH(LCB)) { table_t *table = table_new(); do { if (AT(RCB)) break; if (!AT(NAME) && !AT(STRING)) PARSE_ERROR("expected identifier or string"); char *key = ((token_t *)tokens->data[(*pos)++])->text; EXPECT(COLON, ":"); node_t *val = parse_expr(tokens, pos); table_set(table, key, val); } while (MATCH(COMMA)); EXPECT(RCB, "}"); return NODEH(TABLE, table); } else if (MATCH(NUMBER) || MATCH(STRING) || MATCH(NAME)) return NODET(LITERAL, tokens->data[(*pos)-1]); PARSE_ERROR("expected expression"); return NULL; } size_t get_lineno(token_t *tok) { size_t line, col; traverse(GETSRC(tok->fi), tok->pos, &line, &col); return line; } #define CLIFF (get_lineno(((token_t *)tokens->data[(*pos)>0?(*pos)-1:(*pos)])) != get_lineno(((token_t *)tokens->data[(*pos)>=tokens->length?tokens->length-1:(*pos)]))) node_t *parse_call(list_t *tokens, size_t *pos) { node_t *a = parse_primary(tokens, pos); do { if (!CLIFF && MATCH(LPAR)) { list_t *b = NULL; if (!AT(RPAR)) b = parse_sequence(tokens, pos, -1); EXPECT(RPAR, ")"); a = NODE1l(CALL, a, b); continue; } else if (!CLIFF && MATCH(LSB)) { node_t *b = parse_expr(tokens, pos); EXPECT(RSB, "]"); a = NODE2(INDEX, a, b); continue; } else if (!CLIFF && MATCH(DOT)) { if (!AT(NAME)) PARSE_ERROR("expected identifier after ."); a = NODE1t(MEMBER, a, tokens->data[(*pos)++]); continue; } break; } while (1); return a; } node_t *parse_postfix(list_t *tokens, size_t *pos) { node_t *a = parse_call(tokens, pos); if (CLIFF) return a; if (MATCH(PLUSPLUS)) return NODE1(INC, a); else if (MATCH(MINUSMINUS)) return NODE1(DEC, a); return a; } node_t *parse_unary(list_t *tokens, size_t *pos) { if (MATCH(MINUS)) { node_t *a = parse_unary(tokens, pos); return NODE1(NEGATE, a); } else if (MATCH(BANG)) { node_t *a = parse_unary(tokens, pos); return NODE1(NOT, a); } else if (MATCH(TILDE)) { node_t *a = parse_unary(tokens, pos); return NODE1(BNOT, a); } return parse_postfix(tokens, pos); } node_t *parse_pow(list_t *tokens, size_t *pos) { node_t *a = parse_unary(tokens, pos); do { if (MATCH(STARSTAR)) { node_t *b = parse_unary(tokens, pos); a = NODE2(POW, a, b); continue; } break; } while (1); return a; } node_t *parse_mul(list_t *tokens, size_t *pos) { node_t *a = parse_pow(tokens, pos); do { if (MATCH(STAR)) { node_t *b = parse_pow(tokens, pos); a = NODE2(MUL, a, b); continue; } else if (MATCH(SLASH)) { node_t *b = parse_pow(tokens, pos); a = NODE2(DIV, a, b); continue; } else if (MATCH(SLASHSLASH)) { node_t *b = parse_pow(tokens, pos); a = NODE2(IDIV, a, b); continue; } else if (MATCH(PERCENT)) { node_t *b = parse_pow(tokens, pos); a = NODE2(MOD, a, b); continue; } break; } while (1); return a; } node_t *parse_add(list_t *tokens, size_t *pos) { node_t *a = parse_mul(tokens, pos); do { if (MATCH(PLUS)) { node_t *b = parse_mul(tokens, pos); a = NODE2(ADD, a, b); continue; } else if (MATCH(MINUS)) { node_t *b = parse_mul(tokens, pos); a = NODE2(SUB, a, b); continue; } break; } while (1); return a; } node_t *parse_shift(list_t *tokens, size_t *pos) { node_t *a = parse_add(tokens, pos); do { if (MATCH(LTLT)) { node_t *b = parse_add(tokens, pos); a = NODE2(SHL, a, b); continue; } else if (MATCH(GTGT)) { node_t *b = parse_add(tokens, pos); a = NODE2(SHR, a, b); continue; } break; } while (1); return a; } node_t *parse_relation(list_t *tokens, size_t *pos) { node_t *a = parse_shift(tokens, pos); do { if (MATCH(LT)) { node_t *b = parse_shift(tokens, pos); a = NODE2(LT, a, b); continue; } else if (MATCH(GT)) { node_t *b = parse_shift(tokens, pos); a = NODE2(GT, a, b); continue; } else if (MATCH(LE)) { node_t *b = parse_shift(tokens, pos); a = NODE2(LE, a, b); continue; } else if (MATCH(GE)) { node_t *b = parse_shift(tokens, pos); a = NODE2(GE, a, b); continue; } break; } while (1); return a; } node_t *parse_equality(list_t *tokens, size_t *pos) { node_t *a = parse_relation(tokens, pos); do { if (MATCH(EQUALS)) { node_t *b = parse_relation(tokens, pos); a = NODE2(EQUALS, a, b); continue; } else if (MATCH(NOTEQUALS)) { node_t *b = parse_relation(tokens, pos); a = NODE2(NOTEQUALS, a, b); continue; } else if (MATCH(IS)) { node_t *b = parse_relation(tokens, pos); a = NODE2(IS, a, b); continue; } else if (AT(BANG) && ATP(IS, 1)) { EXPECT(BANG, "!"); EXPECT(IS, "is"); node_t *b = parse_relation(tokens, pos); a = NODE2(NOTIS, a, b); continue; } else if (MATCH(IN)) { node_t *b = parse_relation(tokens, pos); a = NODE2(IN, a, b); continue; } else if (AT(BANG) && ATP(IN, 1)) { EXPECT(BANG, "!"); EXPECT(IN, "in"); node_t *b = parse_relation(tokens, pos); a = NODE2(NOTIN, a, b); continue; } break; } while (1); return a; } node_t *parse_bitand(list_t *tokens, size_t *pos) { node_t *a = parse_equality(tokens, pos); while (MATCH(AND)) { node_t *b = parse_equality(tokens, pos); a = NODE2(BAND, a, b); } return a; } node_t *parse_bitxor(list_t *tokens, size_t *pos) { node_t *a = parse_bitand(tokens, pos); while (MATCH(RAISE)) { node_t *b = parse_bitand(tokens, pos); a = NODE2(XOR, a, b); } return a; } node_t *parse_bitor(list_t *tokens, size_t *pos) { node_t *a = parse_bitxor(tokens, pos); while (MATCH(BAR)) { node_t *b = parse_bitxor(tokens, pos); a = NODE2(BOR, a, b); } return a; } node_t *parse_logand(list_t *tokens, size_t *pos) { node_t *a = parse_bitor(tokens, pos); if (MATCH(ANDAND)) { node_t *b = parse_logand(tokens, pos); return NODE2(LOGAND, a, b); } return a; } node_t *parse_logor(list_t *tokens, size_t *pos) { node_t *a = parse_logand(tokens, pos); if (MATCH(BARBAR)) { node_t *b = parse_logor(tokens, pos); return NODE2(LOGOR, a, b); } return a; } node_t *parse_assignment(list_t *tokens, size_t *pos); node_t *parse_conditional(list_t *tokens, size_t *pos) { node_t *a = parse_logor(tokens, pos); if (MATCH(QM)) { node_t *b = parse_assignment(tokens, pos); EXPECT(COLON, ":"); node_t *c = parse_assignment(tokens, pos); return NODE3(IFEXPR, a, b, c); } return a; } node_t *parse_assignment(list_t *tokens, size_t *pos) { node_t *a = parse_conditional(tokens, pos); if (MATCH(ASSIGN)) { node_t *b = parse_assignment(tokens, pos); return NODE2(ASSIGN, a, b); } else if (MATCH(PLUSASSIGN)) { node_t *b = parse_assignment(tokens, pos); return NODE2(ASSIGN_ADD, a, b); } else if (MATCH(MINUSASSIGN)) { node_t *b = parse_assignment(tokens, pos); return NODE2(ASSIGN_SUB, a, b); } else if (MATCH(STARASSIGN)) { node_t *b = parse_assignment(tokens, pos); return NODE2(ASSIGN_MUL, a, b); } else if (MATCH(SLASHASSIGN)) { node_t *b = parse_assignment(tokens, pos); return NODE2(ASSIGN_DIV, a, b); } else if (MATCH(SLASHSLASHASSIGN)) { node_t *b = parse_assignment(tokens, pos); return NODE2(ASSIGN_IDIV, a, b); } else if (MATCH(PERCENTASSIGN)) { node_t *b = parse_assignment(tokens, pos); return NODE2(ASSIGN_MOD, a, b); } return a; } node_t *parse_expr(list_t *tokens, size_t *pos) { return parse_assignment(tokens, pos); } node_t *parse_stmt(list_t *tokens, size_t *pos); node_t *parse_block(list_t *tokens, size_t *pos) { EXPECT(LCB, "{"); list_t *stmts = list_new(); while (!AT(EOF) && !AT(RCB)) list_push(stmts, parse_stmt(tokens, pos)); EXPECT(RCB, "}"); return NODEL(PROGRAM, stmts); } #define BLOCK() (CLIFF||MATCH(COLON)?parse_stmt(tokens, pos):parse_block(tokens, pos)) node_t *parse_if(list_t *tokens, size_t *pos) { node_t *a = parse_expr(tokens, pos); node_t *b = BLOCK(); node_t *c = NULL; if (MATCH(ELSE)) c = BLOCK(); else if (MATCH(ELIF)) c = parse_if(tokens, pos); return NODE3(IF, a, b, c); } node_t *parse_var(list_t *tokens, size_t *pos, int is_let) { table_t *h = table_new(); do { if(!AT(NAME)) PARSE_ERROR("expected identifier"); char *k = ((token_t *)tokens->data[(*pos)++])->text; node_t *v = NULL; if (is_let) { EXPECT(ASSIGN, "="); v = parse_expr(tokens, pos); } else if (MATCH(ASSIGN)) v = parse_expr(tokens, pos); table_set(h, k, v); } while (MATCH(COMMA)); if (is_let) return NODEH(LET, h); return NODEH(VAR, h); } node_t *parse_func(list_t *tokens, size_t *pos, int is_expr) { token_t *name = NULL; if (!is_expr) { if(!AT(NAME)) PARSE_ERROR("expected identifier"); name = tokens->data[(*pos)++]; } EXPECT(LPAR, "("); table_t *params = NULL; if (!AT(RPAR)) { int flag = 0; params = table_new(); size_t argc = 0; do { if(!AT(NAME)) PARSE_ERROR("expected identifier"); char *l = ((token_t *)tokens->data[(*pos)++])->text; node_t *r = NULL; if (!flag && AT(ASSIGN)) flag = 1; if (flag) { EXPECT(ASSIGN, "="); r = parse_expr(tokens, pos); } list_t *pair = list_new(); size_t *argcp = malloc(sizeof(size_t)); memcpy(argcp, &argc, sizeof(size_t)); argc++; list_push(pair, argcp); list_push(pair, r); table_set(params, l, pair); } while (MATCH(COMMA)); } EXPECT(RPAR, ")"); table_t *captured = NULL; if (MATCH(USE)) { EXPECT(RPAR, "("); captured = table_new(); do { if(!AT(NAME)) PARSE_ERROR("expected identifier"); token_t *name = tokens->data[(*pos)++]; table_set(captured, name->text, NODET(LITERAL, name)); } while (MATCH(COMMA)); EXPECT(RPAR, ")"); } node_t *body = BLOCK(); if (is_expr) return NODEF(FUNCEXPR, NULL, params, captured, body); return NODEF(FUNCDEF, name, params, captured, body); } node_t *parse_stmt(list_t *tokens, size_t *pos) { if (MATCH(LCB)) { list_t *stmts = list_new(); while (!AT(EOF) && !AT(RCB)) { node_t *n = parse_stmt(tokens, pos); MATCH(SEMI); list_push(stmts, n); } EXPECT(RCB, "}"); return NODEL(BLOCK, stmts); } else if (MATCH(VAR)) return parse_var(tokens, pos, 0); else if (MATCH(LET)) return parse_var(tokens, pos, 1); else if (MATCH(IF)) return parse_if(tokens, pos); else if (MATCH(FOR)) { node_t *a = NULL; node_t *b = NULL; node_t *c = NULL; if (!AT(LCB) && !AT(COLON) && !CLIFF) { if (MATCH(VAR)) { if (AT(NAME) && ATP(OF, 1)) { token_t *t = tokens->data[(*pos)++]; EXPECT(OF, "of"); a = parse_expr(tokens, pos); b = BLOCK(); return NODE2t(FOROF, a, b, t); } a = parse_var(tokens, pos, 0); EXPECT(SEMI, ";"); b = parse_expr(tokens, pos); EXPECT(SEMI, ";"); c = parse_expr(tokens, pos); } else a = parse_expr(tokens, pos); } node_t *d = BLOCK(); return NODE4(FOR, a, b, c, d); } else if (MATCH(BREAK)) return NODE0(BREAK); else if (MATCH(CONTINUE)) return NODE0(CONTINUE); else if (MATCH(FUNC)) return parse_func(tokens, pos, 0); else if (MATCH(RETURN)) { node_t *a = NULL; if (!AT(RCB) && !AT(EOF) && !CLIFF) a = parse_expr(tokens, pos); return NODE1(RETURN, a); } else if (MATCH(DEFER)) { node_t *a; if (AT(LCB)) a = BLOCK(); else a = parse_stmt(tokens, pos); return NODE1(DEFER, a); } else if (MATCH(PASS)) return NODE0(PASS); else if (MATCH(TRY)) { node_t *a = BLOCK(); token_t *t = NULL; EXPECT(CATCH, "catch"); if (!AT(COLON) && !AT(LCB) && !CLIFF) { if (!AT(NAME)) PARSE_ERROR("expected identifier"); t = tokens->data[(*pos)++]; } node_t *b = BLOCK(); return NODE2t(TRY, a, b, t); } else if (MATCH(THROW)) { node_t *a = NULL; if (!CLIFF) a = parse_expr(tokens, pos); return NODE1(THROW, a); } else if (MATCH(GOTO)) { if(!AT(NAME)) PARSE_ERROR("expected identifier"); token_t *t = tokens->data[(*pos)++]; return NODET(GOTO, t); } else if (AT(NAME) && ATP(COLON, 1) && !CLIFF) { token_t *t = tokens->data[(*pos)++]; EXPECT(COLON, ":"); return NODET(LABEL, t); } else if (MATCH(INLINE)) { if (!AT(STRING)) PARSE_ERROR("expected string"); token_t *t = tokens->data[(*pos)++]; return NODET(INLINE, t); } node_t *n = parse_expr(tokens, pos); return NODE1(EXPRSTMT, n); } node_t *parse_program(list_t *tokens, size_t *pos) { if (AT(EOF)) PARSE_ERROR("empty program"); list_t *stmts = list_new(); int flag = 0; while (!AT(EOF) && *pos < tokens->length) { node_t *n; if (MATCH(REQUIRE)) { if (flag) PARSE_ERROR("misplaced require statement") if (!AT(STRING)) PARSE_ERROR("expected string"); token_t *path = tokens->data[(*pos)++]; n = NODET(REQUIRE, path); } else { n = parse_stmt(tokens, pos); flag = 1; } MATCH(SEMI); list_push(stmts, n); } return NODEL(PROGRAM, stmts); } node_t *parse(char *source) { size_t pos = 0; return parse_program(tokenize(source), &pos); } #define NEWGID() size_t gid = GID++ #define EMIT(fmt, ...) buffer_fmt(buf, (fmt), ##__VA_ARGS__); #define BINOP(s) { EMIT("qi_" s "(state, "); compile_node(gbuf, buf, ctx, lstk, lbl, node->a); EMIT(", "); compile_node(gbuf, buf, ctx, lstk, lbl, node->b); EMIT(")"); } #define UNOP(s) { EMIT("qi_" s "(state, "); compile_node(gbuf, buf, ctx, lstk, lbl, node->a); EMIT(")"); } #define ASSIGN(lhs, rhs) {\ if ((lhs)->tag == N_LITERAL && (lhs)->t->tag == T_NAME) {\ EMIT("qi_set(state, false, \"%s\", ", (lhs)->t->text);\ rhs;\ EMIT(")");\ } else if ((lhs)->tag == N_INDEX) {\ EMIT("qi_index_set(state, false, ");\ compile_node(gbuf, buf, ctx, lstk, lbl, (lhs)->a);\ EMIT(", ");\ compile_node(gbuf, buf, ctx, lstk, lbl, (lhs)->b);\ EMIT(", ");\ rhs;\ EMIT(")");\ } else if ((lhs)->tag == N_MEMBER) {\ EMIT("qi_index_set(state, false, ");\ compile_node(gbuf, buf, ctx, lstk, lbl, (lhs)->a);\ EMIT(", qi_make_string(state, \"%s\"), ", (lhs)->t->text);\ rhs;\ EMIT(")");\ } else COMPILE_ERROR("illegal assignment left-hand side");\ } #define COMPASSIGN(lhs, s, rhs) {\ ASSIGN(node->a, {\ EMIT("qi_%s(state, ", s);\ compile_node(gbuf, buf, ctx, lstk, lbl, (lhs));\ EMIT(", ");\ rhs;\ EMIT(")");\ });\ } #define COMPILE_ERROR(fmt, ...) { format_error(GETFNAME(node->fi), GETSRC(node->fi), node->pos, fmt, ##__VA_ARGS__); exit(1); } void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, list_t *lbl, node_t *node); void compile_list(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, list_t *lbl, list_t *seq) { if (!seq || seq->length < 1) { EMIT("NULL"); return; } buffer_t *tbuf = buffer_new(); NEWGID(); buffer_fmt(tbuf, "qi_list_t *__list%d(qi_state_t *state) {\n", gid); buffer_fmt(tbuf, "qi_list_t *list = qi_list_make();\n"); for (size_t i = 0; i < seq->length; i++) { buffer_fmt(tbuf, "qi_list_push(list, "); compile_node(gbuf, tbuf, ctx, lstk, lbl, seq->data[i]); buffer_fmt(tbuf, ");\n"); } buffer_fmt(tbuf, "return list;\n"); buffer_fmt(tbuf, "}\n"); buffer_appendb(gbuf, tbuf); EMIT("__list%d(state)", gid); } void compile_table(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, list_t *lbl, table_t *table) { if (!table || table->used < 1) { EMIT("NULL"); return; } buffer_t *tbuf = buffer_new(); NEWGID(); buffer_fmt(tbuf, "qi_table_t *__table%d(qi_state_t *state) {\n", gid); buffer_fmt(tbuf, "qi_table_t *table = qi_table_make();\n"); table_iterate(table, { buffer_fmt(tbuf, "qi_table_set(table, \"%s\", ", entry.key); compile_node(gbuf, tbuf, ctx, lstk, lbl, entry.value); buffer_fmt(tbuf, ");\n"); }); buffer_fmt(tbuf, "return table;\n"); buffer_fmt(tbuf, "}\n"); buffer_appendb(gbuf, tbuf); EMIT("__table%d(state)", gid); } #define CTXPUSH(s) list_push(ctx, (s)) #define CTXPOP() list_pop(ctx) int in_context(list_t *ctx, char *s) { if (!ctx->length) return 0; for (ssize_t i = ctx->length - 1; i >= 0; i--) { if (strcmp(ctx->data[i], "gap") == 0) break; else if (strcmp(ctx->data[i], s) == 0) return 1; } return 0; } size_t count_ctxs(list_t *ctx, char *s) { if (!ctx->length) return 0; size_t k = 0; for (ssize_t i = ctx->length - 1; i >= 0; i--) { if (strcmp(ctx->data[i], "gap") == 0) break; else if (strcmp(ctx->data[i], s) == 0) k++; } return k; } #define INCTX(s) (in_context(ctx, (s))) #define SCOPESK (count_ctxs(ctx, "scope")) #define TRAPSK (count_ctxs(ctx, "trap")) #define LPUSH(i) stack_push(lstk, (i)) #define LPOP() stack_pop(lstk) #define LID (lstk->data[lstk->length-1]) #define LBPUSH() list_push(lbl, table_new()) #define LBPOP() list_pop(lbl) char *tempvar() { NEWGID(); char *s = malloc(sizeof(char) * 64); snprintf(s, 64, "__temp%zu", gid); return s; } void compile_func(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, list_t *lbl, node_t *node) { NEWGID(); buffer_t *tbuf = buffer_new(); buffer_fmt(tbuf, "qi_value_t *__func%d(qi_state_t *state, qi_size_t pargc, qi_list_t *pargs) {\n", gid); LBPUSH(); CTXPUSH("gap"); CTXPUSH("func"); size_t optargc = 0; if (node->h) { table_iterate(node->h, { list_t *pair = entry.value; size_t argc = *(size_t *)pair->data[0]; if (pair->data[1]) { optargc++; buffer_fmt(tbuf, "qi_set(state, false, \"%s\", pargc >= %d? qi_list_index(pargs, %d): ", entry.key, argc+1, argc); compile_node(gbuf, tbuf, ctx, lstk, lbl, pair->data[1]); buffer_fmt(tbuf, ");\n"); } else buffer_fmt(tbuf, "qi_set(state, false, \"%s\", qi_list_index(pargs, %d));\n", entry.key, argc); argc++; }); } compile_node(gbuf, tbuf, ctx, lstk, lbl, node->a); CTXPOP(); CTXPOP(); LBPOP(); buffer_fmt(tbuf, "return state->nil;\n"); buffer_fmt(tbuf, "}\n"); buffer_appendb(gbuf, tbuf); tbuf = buffer_new(); buffer_fmt(tbuf, "qi_make_function(state, \"%s\", %d, __func%d, ", node->t? node->t->text: "", !node->h? 0: (node->h->used - optargc), gid); compile_table(gbuf, tbuf, ctx, lstk, lbl, node->h2); buffer_fmt(tbuf, ")"); if (node->tag == N_FUNCEXPR) { buffer_appendb(buf, tbuf); return; } EMIT("qi_set(state, false, \"%s\", ", node->t->text); buffer_appendb(buf, tbuf); EMIT(");"); } void compile_block(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, list_t *lbl, list_t *block) { for (size_t i = 0; i < block->length; i++) { node_t *n = block->data[i]; if (n->tag == N_FUNCDEF) { compile_func(gbuf, buf, ctx, lstk, lbl, n); EMIT("\n"); } else if (n->tag == N_VAR || n->tag == N_LET) { table_iterate(n->h, { EMIT("qi_%s(state, \"%s\", ", n->tag == N_LET? "decl_const": "decl", entry.key); if (entry.value) compile_node(gbuf, buf, ctx, lstk, lbl, entry.value); else EMIT("state->nil"); EMIT(");\n"); }); } } for (size_t i = 0; i < block->length; i++) { compile_node(gbuf, buf, ctx, lstk, lbl, block->data[i]); EMIT("\n"); } } const char *STD[][2] = { {"std", "func exit(c) {\n" " if type(c) != \"number\":\n" " throw \"expected first argument to be: number, but got: \" + type(c)\n" " inline 'int code = qi_get(state, \"c\")->value.number'\n" " inline 'exit(code)'\n" "}\n" "func head(l): return l[0]\n" "func die(msg, c=1) {\n" " println(msg)\n" " exit(c)\n" "}\n" "let SEEK_SET = 0, SEEK_CUR = 1, SEEK_END = 2\n" "func frewind(file)\n" " fseek(file, 0, SEEK_SET)\n" "func file_read(filename) {\n" " var file = fopen(filename, \"r\")\n" " defer fclose(file)\n" " fseek(file, 0, SEEK_END)\n" " let size = ftell(file)\n" " frewind(file)\n" " return str(fread(file, size))\n" "}\n" }, {NULL, NULL} }; char *unescape(char *s) { buffer_t *buf = buffer_new(); for (size_t i = 0; i < strlen(s); i++) { char c = s[i]; if (c == '\\') { char nc = s[i+1]; if (!nc) continue; switch (nc) { case 'n': buffer_append(buf, '\n'); break; default: buffer_append(buf, nc); break; } i++; } else buffer_append(buf, c); } return buffer_read(buf); } void compile_into(char *source, buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, list_t *lbl); void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, list_t *lbl, node_t *node) { switch (node->tag) { case N_PROGRAM: compile_block(gbuf, buf, ctx, lstk, lbl, node->l); break; case N_EXPRSTMT: EMIT("(void)("); compile_node(gbuf, buf, ctx, lstk, lbl, node->a); EMIT(");"); break; case N_BLOCK: LBPUSH(); CTXPUSH("scope"); EMIT("qi_new_scope(state);\n"); compile_block(gbuf, buf, ctx, lstk, lbl, node->l); EMIT("qi_old_scope(state);"); CTXPOP(); LBPOP(); break; case N_LITERAL: switch (node->t->tag) { case T_NUMBER: EMIT("qi_make_number(state, %s)", node->t->text); break; case T_STRING: if (!*(node->t->text)) { EMIT("state->empty_string"); } else { EMIT("qi_make_string(state, \"%s\")", node->t->text); } break; case T_NAME: EMIT("qi_get(state, \"%s\")", node->t->text); break; default: COMPILE_ERROR("not yet implemented"); } break; case N_LIST: EMIT("qi_make_list(state, "); compile_list(gbuf, buf, ctx, lstk, lbl, node->l); EMIT(")"); break; case N_TUPLE: EMIT("qi_make_tuple(state, "); compile_list(gbuf, buf, ctx, lstk, lbl, node->l); EMIT(")"); break; case N_NILTUPLE: EMIT("state->empty_tuple"); break; case N_TABLE: EMIT("qi_make_table(state, "); compile_table(gbuf, buf, ctx, lstk, lbl, node->h); EMIT(")"); break; case N_CALL: EMIT("qi_call(state, "); compile_node(gbuf, buf, ctx, lstk, lbl, node->a); EMIT(", "); compile_list(gbuf, buf, ctx, lstk, lbl, node->l); EMIT(")"); break; case N_MEMBER: EMIT("qi_index(state, "); compile_node(gbuf, buf, ctx, lstk, lbl, node->a); EMIT(", qi_make_string(state, \"%s\"))", node->t->text); break; case N_INDEX: EMIT("qi_index(state, "); compile_node(gbuf, buf, ctx, lstk, lbl, node->a); EMIT(", "); compile_node(gbuf, buf, ctx, lstk, lbl, node->b); EMIT(")"); break; case N_ASSIGN: ASSIGN(node->a, compile_node(gbuf, buf, ctx, lstk, lbl, node->b)); break; case N_ASSIGN_ADD: COMPASSIGN(node->a, "add", compile_node(gbuf, buf, ctx, lstk, lbl, node->b)); break; case N_ASSIGN_SUB: COMPASSIGN(node->a, "sub", compile_node(gbuf, buf, ctx, lstk, lbl, node->b)); break; case N_ASSIGN_MUL: COMPASSIGN(node->a, "mul", compile_node(gbuf, buf, ctx, lstk, lbl, node->b)); break; case N_ASSIGN_DIV: COMPASSIGN(node->a, "div", compile_node(gbuf, buf, ctx, lstk, lbl, node->b)); break; case N_ASSIGN_IDIV: COMPASSIGN(node->a, "idiv", compile_node(gbuf, buf, ctx, lstk, lbl, node->b)); break; case N_ASSIGN_MOD: COMPASSIGN(node->a, "mod", compile_node(gbuf, buf, ctx, lstk, lbl, node->b)); break; case N_ASSIGN_POW: COMPASSIGN(node->a, "pow", compile_node(gbuf, buf, ctx, lstk, lbl, node->b)); break; case N_INC: COMPASSIGN(node->a, "add", EMIT("state->one")); break; case N_DEC: COMPASSIGN(node->a, "sub", EMIT("state->one")); break; case N_VAR: break; case N_LET: break; case N_IF: EMIT("if (_qi_truthy(state, "); compile_node(gbuf, buf, ctx, lstk, lbl, node->a); EMIT(")) {\n"); CTXPUSH("scope"); EMIT("qi_new_scope(state);\n"); compile_node(gbuf, buf, ctx, lstk, lbl, node->b); EMIT("qi_old_scope(state);\n"); CTXPOP(); if (node->c) { EMIT("} else {\n"); CTXPUSH("scope"); EMIT("qi_new_scope(state);\n"); compile_node(gbuf, buf, ctx, lstk, lbl, node->c); EMIT("qi_old_scope(state);\n"); CTXPOP(); } EMIT("}"); break; case N_FOR: { NEWGID(); CTXPUSH("scope"); EMIT("qi_new_scope(state);\n"); if (!node->a) { EMIT("for (;;) {\n"); } else if (node->a && !node->b) { EMIT("while (_qi_truthy(state, "); compile_node(gbuf, buf, ctx, lstk, lbl, node->a); EMIT(")) {\n"); } else { compile_node(gbuf, buf, ctx, lstk, lbl, node->a); EMIT("while (_qi_truthy(state, "); compile_node(gbuf, buf, ctx, lstk, lbl, node->b); EMIT(")) {\n"); } LPUSH(gid); CTXPUSH("for"); compile_node(gbuf, buf, ctx, lstk, lbl, node->d); CTXPOP(); LPOP(); if (node->c) compile_node(gbuf, buf, ctx, lstk, lbl, node->c); EMIT("__continue%d:;\n", gid); EMIT("}\n"); EMIT("__break%d:;\n", gid); EMIT("qi_old_scope(state);\n"); CTXPOP(); } break; case N_FOROF: { NEWGID(); char *varname = tempvar(); CTXPUSH("scope"); EMIT("qi_new_scope(state);\n"); EMIT("qi_value_t *%s = qi_iter(state, ", varname); compile_node(gbuf, buf, ctx, lstk, lbl, node->a); EMIT(");\n"); EMIT("qi_decl(state, \"%s\", state->nil);\n", node->t->text); EMIT("for (qi_size_t length = _qi_length(state, %s), i = 0; i < length; i++) {\n", varname); EMIT("qi_set(state, false, \"%s\", qi_index(state, %s, qi_make_number(state, i)));\n", node->t->text, varname); LPUSH(gid); CTXPUSH("for"); compile_node(gbuf, buf, ctx, lstk, lbl, node->b); CTXPOP(); LPOP(); EMIT("__continue%d:;\n", gid); EMIT("}\n"); EMIT("__break%d:;\n", gid); EMIT("qi_old_scope(state);\n"); CTXPOP(); } break; case N_BREAK: if (!INCTX("for")) COMPILE_ERROR("break outside of a loop"); EMIT("goto __break%d;", LID); break; case N_CONTINUE: if (!INCTX("for")) COMPILE_ERROR("continue outside of a loop"); EMIT("goto __continue%d;", LID); break; case N_DEFER: { NEWGID(); buffer_t *tbuf = buffer_new(); buffer_fmt(tbuf, "void __defer%d(qi_state_t *state) {\n", gid); LBPUSH(); CTXPUSH("gap"); compile_node(gbuf, tbuf, ctx, lstk, lbl, node->a); CTXPOP(); LBPOP(); buffer_fmt(tbuf, "\n"); buffer_fmt(tbuf, "}\n"); buffer_appendb(gbuf, tbuf); EMIT("qi_add_defer(state, -1, __defer%d);", gid); } break; case N_RETURN: if (!INCTX("func")) COMPILE_ERROR("return outside of a function"); for (size_t i = 0; i < SCOPESK; i++) EMIT("qi_old_scope(state);\n"); for (size_t i = 0; i < TRAPSK; i++) EMIT("qi_unset_trap(state, trap);\n"); EMIT("return "); if (node->a) compile_node(gbuf, buf, ctx, lstk, lbl, node->a); else EMIT("state->nil"); EMIT(";"); break; case N_FUNCDEF: break; case N_PASS: break; case N_TRY: CTXPUSH("scope"); EMIT("qi_new_scope(state);\n"); EMIT("qi_try(state, {\n"); compile_node(gbuf, buf, ctx, lstk, lbl, node->a); EMIT("}, {\n"); if (node->t) EMIT("qi_decl(state, \"%s\", trap->value);\n", node->t->text); CTXPUSH("trap"); compile_node(gbuf, buf, ctx, lstk, lbl, node->b); CTXPOP(); EMIT("}, NULL);\n"); EMIT("qi_new_scope(state);"); CTXPOP(); break; case N_THROW: EMIT("qi_throw(state, "); if (node->a) compile_node(gbuf, buf, ctx, lstk, lbl, node->a); else { EMIT("state->nil"); } EMIT(");"); break; case N_LABEL: { char *label = node->t->text; table_iterate((table_t *)list_index(lbl, -1), { if (strcmp(entry.key, label) == 0) { COMPILE_ERROR("duplicated label: '%s'", label); } }); NEWGID(); EMIT("__label%d:;", gid); size_t *n = malloc(sizeof(size_t)); memcpy(n, &gid, sizeof(size_t)); table_set(list_index(lbl, -1), label, n); } break; case N_GOTO: { ssize_t gid = -1; char *label = node->t->text; table_iterate((table_t *)list_index(lbl, -1), { if (strcmp(entry.key, label) == 0) { gid = *(size_t *)entry.value; break; } }); if (gid < 0) COMPILE_ERROR("undefined label: '%s'", label); EMIT("goto __label%d;", gid); } break; case N_REQUIRE: { char *source = NULL; char *path = unescape(node->t->text); for (size_t i = 0; STD[i][0]; i++) { if (strcmp(path, STD[i][0]) == 0) { source = (char *)STD[i][1]; break; } } if (is_required(path)) break; if (!source) { FILE *fd = fopen(path, "rb"); if (!fd) COMPILE_ERROR("'%s' is not a valid file path or a builtin library name", path); buffer_t *fbuf = buffer_new(); for (;;) { char line[512]; if (!fgets(line, sizeof(line), fd)) break; buffer_appends(fbuf, line); } source = buffer_read(fbuf); path = realpath(path, NULL); } list_t *pair = list_new(); list_push(pair, path); list_push(pair, source); list_push(FILES, pair); compile_into(source, gbuf, buf, ctx, lstk, lbl); list_pop(FILES); list_push(REQUIRED, path); } break; case N_IFEXPR: EMIT("(_qi_truthy(state, "); compile_node(gbuf, buf, ctx, lstk, lbl, node->a); EMIT(")? "); compile_node(gbuf, buf, ctx, lstk, lbl, node->b); EMIT(": "); compile_node(gbuf, buf, ctx, lstk, lbl, node->c); EMIT(")"); break; case N_FUNCEXPR: compile_func(gbuf, buf, ctx, lstk, lbl, node); break; case N_EQUALS: BINOP("equals"); break; case N_NOTEQUALS: BINOP("not_equals"); break; case N_IS: BINOP("is"); break; case N_NOTIS: BINOP("not_is"); break; case N_IN: BINOP("in"); break; case N_NOTIN: BINOP("not_in"); break; case N_LT: BINOP("lt"); break; case N_GT: BINOP("gt"); break; case N_LE: BINOP("le"); break; case N_GE: BINOP("ge"); break; case N_ADD: BINOP("add"); break; case N_SUB: BINOP("sub"); break; case N_MUL: BINOP("mul"); break; case N_DIV: BINOP("div"); break; case N_IDIV: BINOP("idiv"); break; case N_MOD: BINOP("mod"); break; case N_POW: BINOP("pow"); break; case N_SHL: BINOP("shl"); break; case N_SHR: BINOP("shr"); break; case N_XOR: BINOP("xor"); break; case N_BOR: BINOP("bor"); break; case N_BAND: BINOP("band"); break; case N_NEGATE: UNOP("negate"); break; case N_NOT: UNOP("not"); break; case N_BNOT: UNOP("bnot"); break; case N_INLINE: EMIT("%s;", unescape(node->t->text)); break; default: COMPILE_ERROR("not yet implemented"); } } void compile_into(char *source, buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, list_t *lbl) { node_t *n = parse(source); compile_node(gbuf, buf, ctx, lstk, lbl, n); } char *compile(char *source) { list_t *ctx = list_new(); stack_t *lstk = stack_new(); list_t *lbl = list_new(); LBPUSH(); buffer_t *gbuf = buffer_new(); buffer_appends(gbuf, "#include \n"); buffer_t *buf = buffer_new(); compile_into(source, gbuf, buf, ctx, lstk, lbl); buffer_t *rbuf = buffer_new(); buffer_appendb(rbuf, gbuf); buffer_appends(rbuf, "int main(int argc, char **argv) {\n"); buffer_appends(rbuf, "qi_state_t *state;\n"); buffer_appends(rbuf, "qi_state_init(&state);\n"); buffer_appendb(rbuf, buf); buffer_appends(rbuf, "qi_old_scope(state);\n"); buffer_appends(rbuf, "qi_finalize();\n"); buffer_appends(rbuf, "return 0;\n"); buffer_appends(rbuf, "}\n"); return buffer_read(rbuf); } char *compile_file(char *filename, FILE *fd) { buffer_t *buf = buffer_new(); for (;;) { char line[512]; if (!fgets(line, sizeof(line), fd)) break; buffer_appends(buf, line); } char *source = buffer_read(buf); list_t *pair = list_new(); list_push(pair, filename); list_push(pair, source); list_push(FILES, pair); char *out = compile(source); list_pop(FILES); return out; } int main(int argc, char **argv) { FILES = list_new(); REQUIRED = list_new(); char *out = compile_file("", stdin); fwrite(out, sizeof(char), strlen(out), stdout); return 0; }