txlyre 1 hari lalu
induk
melakukan
3a9cd38784
1 mengubah file dengan 289 tambahan dan 125 penghapusan
  1. 289 125
      qic.c

+ 289 - 125
qic.c

@@ -345,6 +345,7 @@ typedef struct {
     T_BANG,
     T_RAISE,
     T_TILDE,
+    T_AT,
 
     T_INLINE,
     T_HEADER,
@@ -723,6 +724,8 @@ token_t *next_token(char *source, size_t *pos) {
     return TK(RAISE);
   else if (source[*pos] == '~' && ++(*pos))
     return TK(TILDE);
+  else if (source[*pos] == '@' && ++(*pos))
+    return TK(AT);
 
   LEX_ERROR("unexpected input")
 }
@@ -814,7 +817,6 @@ struct _node_t {
     N_LET,
     N_VARUNPACK,
     N_LETUNPACK,
-    N_UNPACK,
     N_CONST,
     N_IF,
     N_SWITCH,
@@ -857,6 +859,7 @@ struct _node_t {
   table_t *h2;
 
   token_t *t;
+  token_t *t2;
 
   size_t fi;
   size_t pos;
@@ -1039,6 +1042,10 @@ node_t *nodef(int tag, token_t *name, table_t *params, table_t *captured, node_t
 
 #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 LABELLOOP(n)\
+  node_t *ln = (n);\
+  ln->t2 = label;
+
 #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))
@@ -1580,7 +1587,7 @@ node_t *parse_var(list_t *tokens, size_t *pos, int is_let) {
 }
 
 node_t *parse_unpack(list_t *tokens, size_t *pos, int tag) {
-  EXPECT(LPAR, "(");
+  EXPECT(LSB, "[");
 
   list_t *l = list_new();
 
@@ -1591,17 +1598,15 @@ node_t *parse_unpack(list_t *tokens, size_t *pos, int tag) {
     list_push(l, ((token_t *)tokens->data[(*pos)++])->text);
   } while (MATCH(COMMA));
 
-  EXPECT(RPAR, ")");
+  EXPECT(RSB, "]");
   EXPECT(ASSIGN, "=");
 
   node_t *a = parse_expr(tokens, pos);
 
-  if (tag == N_VARUNPACK)
-    return NODE1l(VARUNPACK, a, l);
-  else if (tag == N_LETUNPACK)
+  if (tag == N_LETUNPACK)
     return NODE1l(LETUNPACK, a, l);
 
-  return NODE1l(UNPACK, a, l);
+  return NODE1l(VARUNPACK, a, l);
 }
 
 node_t *parse_func(list_t *tokens, size_t *pos, int is_expr) {
@@ -1705,18 +1710,16 @@ node_t *parse_stmt(list_t *tokens, size_t *pos) {
 
     return NODEL(BLOCK, stmts);
   } else if (MATCH(VAR)) {
-    if (AT(LPAR))
+    if (AT(LSB))
       return parse_unpack(tokens, pos, N_VARUNPACK);
 
     return parse_var(tokens, pos, 0);
   } else if (MATCH(LET)) {
-    if (AT(LPAR))
+    if (AT(LSB))
       return parse_unpack(tokens, pos, N_LETUNPACK);
 
     return parse_var(tokens, pos, 1);
-  } else if (MATCH(UNPACK))
-    return parse_unpack(tokens, pos, N_UNPACK);
-  else if (MATCH(CONST)) {
+  } else if (MATCH(CONST)) {
     table_t *h = table_new();
 
     do {
@@ -1742,6 +1745,15 @@ node_t *parse_stmt(list_t *tokens, size_t *pos) {
   } else if (MATCH(IF))
     return parse_if(tokens, pos);
   else if (MATCH(SWITCH)) {
+    token_t *label = NULL;
+
+    if (MATCH(AT)) {
+      if(!AT(NAME))
+        PARSE_ERROR("expected identifier");
+
+      label = tokens->data[(*pos)++];
+    }
+
     node_t *a = parse_expr(tokens, pos);
 
     EXPECT(LCB, "{");
@@ -1787,14 +1799,25 @@ node_t *parse_stmt(list_t *tokens, size_t *pos) {
     if (!cases->length && !b)
       PARSE_ERROR("empty switch statement");
 
-    return NODE2l(SWITCH, a, b, cases);
+    LABELLOOP(NODE2l(SWITCH, a, b, cases));
+
+    return ln;
   } else if (MATCH(FOR)) {
+    token_t *label = NULL;
+
+    if (MATCH(AT)) {
+      if(!AT(NAME))
+        PARSE_ERROR("expected identifier");
+
+      label = tokens->data[(*pos)++];
+    }
+
     node_t *a = NULL;
     node_t *b = NULL;
     node_t *c = NULL;
 
     if (!AT(LCB) && !AT(COLON) && !CLIFF) {
-      if (MATCH(LPAR)) {
+      if (MATCH(LSB)) {
         list_t *l = list_new();
 
         do {
@@ -1804,13 +1827,15 @@ node_t *parse_stmt(list_t *tokens, size_t *pos) {
           list_push(l, ((token_t *)tokens->data[(*pos)++])->text);
         } while (MATCH(COMMA));
 
-        EXPECT(RPAR, ")");
+        EXPECT(RSB, "]");
         EXPECT(OF, "of");
 
         a = parse_expr(tokens, pos);
         b = BLOCK();
 
-        return NODE2l(FOROFUNPACK, a, b, l);
+        LABELLOOP(NODE2l(FOROFUNPACK, a, b, l));
+
+        return ln;
       }
 
       if (AT(NAME) && ATP(OF, 1)) {
@@ -1821,11 +1846,13 @@ node_t *parse_stmt(list_t *tokens, size_t *pos) {
         a = parse_expr(tokens, pos);
         b = BLOCK();
 
-        return NODE2t(FOROF, a, b, t);
+        LABELLOOP(NODE2t(FOROF, a, b, t));
+
+        return ln;
       }
 
       if (MATCH(VAR)) {
-        if (MATCH(LPAR)) {
+        if (MATCH(LSB)) {
           list_t *l = list_new();
 
           do {
@@ -1835,13 +1862,15 @@ node_t *parse_stmt(list_t *tokens, size_t *pos) {
             list_push(l, ((token_t *)tokens->data[(*pos)++])->text);
           } while (MATCH(COMMA));
 
-          EXPECT(RPAR, ")");
+          EXPECT(RCB, "]");
           EXPECT(OF, "of");
 
           a = parse_expr(tokens, pos);
           b = BLOCK();
 
-          return NODE2l(FOROFVARUNPACK, a, b, l);
+          LABELLOOP(NODE2l(FOROFVARUNPACK, a, b, l));
+
+          return ln;
         }
 
         if (AT(NAME) && ATP(OF, 1)) {
@@ -1852,7 +1881,9 @@ node_t *parse_stmt(list_t *tokens, size_t *pos) {
           a = parse_expr(tokens, pos);
           b = BLOCK();
 
-          return NODE2t(FOROFVAR, a, b, t);
+          LABELLOOP(NODE2t(FOROFVAR, a, b, t));
+
+          return ln;
         }
 
         a = parse_var(tokens, pos, 0);
@@ -1865,14 +1896,16 @@ node_t *parse_stmt(list_t *tokens, size_t *pos) {
 
     node_t *d = BLOCK();
 
-    return NODE4(FOR, a, b, c, d);
+    LABELLOOP(NODE4(FOR, a, b, c, d));
+
+    return ln;
   } else if (MATCH(BREAK)) {
-    if (AT(NUMBER) && !CLIFF)
+    if ((AT(NUMBER) || AT(NAME)) && !CLIFF)
       return NODET(BREAK, tokens->data[(*pos)++]);
 
     return NODE0(BREAK);
   } else if (MATCH(CONTINUE)) {
-    if (AT(NUMBER) && !CLIFF)
+    if ((AT(NUMBER) || AT(NAME)) && !CLIFF)
       return NODET(CONTINUE, tokens->data[(*pos)++]);
 
     return NODE0(CONTINUE);
@@ -2048,33 +2081,34 @@ node_t *parse(char *source) {
 #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, sstk, lbl, node->a); EMIT(", "); compile_node(gbuf, buf, ctx, lstk, sstk, lbl, node->b); EMIT(")"); }
-#define UNOP(s) { EMIT("qi_" s "(state, "); compile_node(gbuf, buf, ctx, lstk, sstk, lbl, node->a); EMIT(")"); }
-#define ASSIGN(lhs, rhs) {\
+#define BINOP(s) { EMIT("qi_" s "(state, "); compile_node(gbuf, buf, ctx, ltab, lstk, sstk, lbl, node->a); EMIT(", "); compile_node(gbuf, buf, ctx, ltab, lstk, sstk, lbl, node->b); EMIT(")"); }
+#define UNOP(s) { EMIT("qi_" s "(state, "); compile_node(gbuf, buf, ctx, ltab, lstk, sstk, lbl, node->a); EMIT(")"); }
+#define ASSIGNIN(buf, lhs, rhs) {\
   if ((lhs)->tag == N_LITERAL && (lhs)->t->tag == T_NAME) {\
-    EMIT("qi_set(state, false, \"%s\", ", (lhs)->t->text);\
+    buffer_fmt(buf, "qi_set(state, false, \"%s\", ", (lhs)->t->text);\
     rhs;\
-    EMIT(")");\
+    buffer_fmt(buf, ")");\
   } else if ((lhs)->tag == N_INDEX) {\
-    EMIT("qi_index_set(state, false, ");\
-    compile_node(gbuf, buf, ctx, lstk, sstk, lbl, (lhs)->a);\
-    EMIT(", ");\
-    compile_node(gbuf, buf, ctx, lstk, sstk, lbl, (lhs)->b);\
-    EMIT(", ");\
+    buffer_fmt(buf, "qi_index_set(state, false, ");\
+    compile_node(gbuf, buf, ctx, ltab, lstk, sstk, lbl, (lhs)->a);\
+    buffer_fmt(buf, ", ");\
+    compile_node(gbuf, buf, ctx, ltab, lstk, sstk, lbl, (lhs)->b);\
+    buffer_fmt(buf, ", ");\
     rhs;\
-    EMIT(")");\
+    buffer_fmt(buf, ")");\
   } else if ((lhs)->tag == N_MEMBER) {\
-    EMIT("qi_index_set(state, false, ");\
-    compile_node(gbuf, buf, ctx, lstk, sstk, lbl, (lhs)->a);\
-    EMIT(", qi_make_string(state, \"%s\"), ", (lhs)->t->text);\
+    buffer_fmt(buf, "qi_index_set(state, false, ");\
+    compile_node(gbuf, buf, ctx, ltab, lstk, sstk, lbl, (lhs)->a);\
+    buffer_fmt(buf, ", qi_make_string(state, \"%s\"), ", (lhs)->t->text);\
     rhs;\
-    EMIT(")");\
+    buffer_fmt(buf, ")");\
   } else COMPILE_ERROR("illegal assignment left-hand side");\
 }
+#define ASSIGN(lhs, rhs) ASSIGNIN(buf, lhs, rhs)
 #define COMPASSIGN(lhs, s, rhs) {\
   ASSIGN(node->a, {\
     EMIT("qi_%s(state, ", s);\
-    compile_node(gbuf, buf, ctx, lstk, sstk, lbl, (lhs));\
+    compile_node(gbuf, buf, ctx, ltab, lstk, sstk, lbl, (lhs));\
     EMIT(", ");\
     rhs;\
     EMIT(")");\
@@ -2082,9 +2116,9 @@ node_t *parse(char *source) {
 }
 #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, stack_t *sstk, list_t *lbl, node_t *node);
+void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, table_t *ltab, stack_t *lstk, stack_t *sstk, list_t *lbl, node_t *node);
 
-void compile_list(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, stack_t *sstk, list_t *lbl, list_t *seq) {
+void compile_list(buffer_t *gbuf, buffer_t *buf, list_t *ctx, table_t *ltab, stack_t *lstk, stack_t *sstk, list_t *lbl, list_t *seq) {
   if (!seq || seq->length < 1) {
     EMIT("NULL");
 
@@ -2101,7 +2135,7 @@ void compile_list(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, sta
   for (size_t i = 0; i < seq->length; i++) {
     buffer_fmt(tbuf, "qi_list_data(list, %d) = ", i);
 
-    compile_node(gbuf, tbuf, ctx, lstk, sstk, lbl, seq->data[i]);
+    compile_node(gbuf, tbuf, ctx, ltab, lstk, sstk, lbl, seq->data[i]);
 
     buffer_fmt(tbuf, ";\n");
   }
@@ -2114,7 +2148,7 @@ void compile_list(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, sta
   EMIT("__list%d(state)", gid);
 }
 
-void compile_table(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, stack_t *sstk, list_t *lbl, table_t *table) {
+void compile_table(buffer_t *gbuf, buffer_t *buf, list_t *ctx, table_t *ltab, stack_t *lstk, stack_t *sstk, list_t *lbl, table_t *table) {
   if (!table || table->used < 1) {
     EMIT("NULL");
 
@@ -2131,7 +2165,7 @@ void compile_table(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, st
   table_iterate(table, {
     buffer_fmt(tbuf, "qi_table_set(table, \"%s\", ", entry.key);
 
-    compile_node(gbuf, tbuf, ctx, lstk, sstk, lbl, entry.value);
+    compile_node(gbuf, tbuf, ctx, ltab, lstk, sstk, lbl, entry.value);
 
     buffer_fmt(tbuf, ");\n");
   });
@@ -2215,7 +2249,7 @@ char *tempvar() {
   return s;
 }
 
-void compile_func(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, stack_t *sstk, list_t *lbl, node_t *node, char *name) {
+void compile_func(buffer_t *gbuf, buffer_t *buf, list_t *ctx, table_t *ltab, stack_t *lstk, stack_t *sstk, list_t *lbl, node_t *node, char *name) {
   NEWGID();
 
   buffer_t *tbuf = buffer_new();
@@ -2238,7 +2272,7 @@ void compile_func(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, sta
         optargc++;
 
         buffer_fmt(tbuf, "qi_decl(state, \"%s\", pargc >= %d? qi_list_index(pargs, %d): ", entry.key, argc+1, argc);
-        compile_node(gbuf, tbuf, ctx, lstk, sstk, lbl, pair->data[1]);
+        compile_node(gbuf, tbuf, ctx, ltab, lstk, sstk, lbl, pair->data[1]);
         buffer_fmt(tbuf, ");\n");
       } else
         buffer_fmt(tbuf, "qi_decl(state, \"%s\", qi_list_index(pargs, %d));\n", entry.key, argc);
@@ -2247,7 +2281,7 @@ void compile_func(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, sta
     });
   }
 
-  compile_node(gbuf, tbuf, ctx, lstk, sstk, lbl, node->a);
+  compile_node(gbuf, tbuf, ctx, ltab, lstk, sstk, lbl, node->a);
 
   CTXPOP();
   CTXPOP();
@@ -2261,7 +2295,7 @@ void compile_func(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, sta
   tbuf = buffer_new();
 
   buffer_fmt(tbuf, "qi_make_function(state, \"%s\", %d, __func%d, ", name? name: node->t? node->t->text: "<anon>", !node->h? 0: (node->h->used - optargc), gid);
-  compile_table(gbuf, tbuf, ctx, lstk, sstk, lbl, node->h2);
+  compile_table(gbuf, tbuf, ctx, ltab, lstk, sstk, lbl, node->h2);
   buffer_fmt(tbuf, ")");
 
   if (node->tag == N_FUNCEXPR) {
@@ -2277,7 +2311,7 @@ void compile_func(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, sta
 
 table_t *CONSTANTS;
 
-void compile_block(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, stack_t *sstk, list_t *lbl, list_t *block, int toplevel) {
+void compile_block(buffer_t *gbuf, buffer_t *buf, list_t *ctx, table_t *ltab, stack_t *lstk, stack_t *sstk, list_t *lbl, list_t *block, int toplevel) {
   for (size_t i = 0; i < block->length; i++) {
     node_t *node = block->data[i];
 
@@ -2294,7 +2328,7 @@ void compile_block(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, st
         table_set(CONSTANTS, name, entry.value);
       });
     } else if (node->tag == N_FUNCDEF) {
-      compile_func(gbuf, buf, ctx, lstk, sstk, lbl, node, NULL);
+      compile_func(gbuf, buf, ctx, ltab, lstk, sstk, lbl, node, NULL);
 
       EMIT("\n");
     } else if (node->tag == N_CLASS) {
@@ -2328,9 +2362,9 @@ void compile_block(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, st
 
         buffer_fmt(tbuf, "qi_table_set(%s, \"%s\", ", triple->data[1] != NULL? "table": "metatable", t->text);
         if (triple->data[1] == NULL)
-          compile_func(gbuf, tbuf, ctx, lstk, sstk, lbl, triple->data[2], buffer_read(methodname));
+          compile_func(gbuf, tbuf, ctx, ltab, lstk, sstk, lbl, triple->data[2], buffer_read(methodname));
         else
-          compile_node(gbuf, tbuf, ctx, lstk, sstk, lbl, triple->data[2]);
+          compile_node(gbuf, tbuf, ctx, ltab, lstk, sstk, lbl, triple->data[2]);
         buffer_fmt(tbuf, ");\n");
       }
 
@@ -2366,7 +2400,7 @@ void compile_block(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, st
     if (n->tag == N_FUNCDEF)
       continue;
 
-    compile_node(gbuf, buf, ctx, lstk, sstk, lbl, n);
+    compile_node(gbuf, buf, ctx, ltab, lstk, sstk, lbl, n);
 
     EMIT("\n");
   }
@@ -3037,6 +3071,7 @@ const char *STD[][2] = {
   },
 
   {"time",
+    "header `#include <time.h>`\n"
     "func time() {\n"
     "  inline `unsigned long long ts = time(NULL)`\n"
     "  inline `return qi_make_number(state, ts)`\n"
@@ -3164,9 +3199,9 @@ char *unescape(char *s) {
   return buffer_read(buf);
 }
 
-void compile_into(char *source, buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, stack_t *sstk, list_t *lbl);
+void compile_into(char *source, buffer_t *gbuf, buffer_t *buf, list_t *ctx, table_t *ltab, stack_t *lstk, stack_t *sstk, list_t *lbl);
 
-int require_once(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, stack_t *sstk, list_t *lbl, char *path) {
+int require_once(buffer_t *gbuf, buffer_t *buf, list_t *ctx, table_t *ltab, stack_t *lstk, stack_t *sstk, list_t *lbl, char *path) {
   char *source = NULL;
 
   for (size_t i = 0; STD[i][0]; i++) {
@@ -3206,7 +3241,7 @@ int require_once(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, stac
 
   list_push(FILES, pair);
 
-  compile_into(source, gbuf, buf, ctx, lstk, sstk, lbl);
+  compile_into(source, gbuf, buf, ctx, ltab, lstk, sstk, lbl);
 
   list_pop(FILES);
   list_push(REQUIRED, path);
@@ -3216,15 +3251,15 @@ int require_once(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, stac
 
 buffer_t *HBUF;
 
-void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, stack_t *sstk, list_t *lbl, node_t *node) {
+void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, table_t *ltab, stack_t *lstk, stack_t *sstk, list_t *lbl, node_t *node) {
   switch (node->tag) {
     case N_TOPLEVEL: case N_PROGRAM:
-      compile_block(gbuf, buf, ctx, lstk, sstk, lbl, node->l, node->tag == N_TOPLEVEL);
+      compile_block(gbuf, buf, ctx, ltab, lstk, sstk, lbl, node->l, node->tag == N_TOPLEVEL);
       break;
 
     case N_EXPRSTMT:
       EMIT("(void)(");
-      compile_node(gbuf, buf, ctx, lstk, sstk, lbl, node->a);
+      compile_node(gbuf, buf, ctx, ltab, lstk, sstk, lbl, node->a);
       EMIT(");");
       break;
 
@@ -3232,7 +3267,7 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, sta
       LBPUSH();
       CTXPUSH("scope");
       EMIT("qi_new_scope(state);\n");
-      compile_block(gbuf, buf, ctx, lstk, sstk, lbl, node->l, 0);
+      compile_block(gbuf, buf, ctx, ltab, lstk, sstk, lbl, node->l, 0);
       EMIT("qi_old_scope(state);");
       CTXPOP();
       LBPOP();
@@ -3277,7 +3312,7 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, sta
           node_t *n = table_get(CONSTANTS, name);
 
           if (n)
-            compile_node(gbuf, buf, ctx, lstk, sstk, lbl, n);
+            compile_node(gbuf, buf, ctx, ltab, lstk, sstk, lbl, n);
           else
             EMIT("qi_get(state, \"%s\")", name);
           } break;
@@ -3289,13 +3324,13 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, sta
 
     case N_LIST:
       EMIT("qi_make_list(state, ");
-      compile_list(gbuf, buf, ctx, lstk, sstk, lbl, node->l);
+      compile_list(gbuf, buf, ctx, ltab, lstk, sstk, lbl, node->l);
       EMIT(")");
       break;
 
     case N_TUPLE:
       EMIT("qi_make_tuple(state, ");
-      compile_list(gbuf, buf, ctx, lstk, sstk, lbl, node->l);
+      compile_list(gbuf, buf, ctx, ltab, lstk, sstk, lbl, node->l);
       EMIT(")");
       break;
 
@@ -3303,43 +3338,113 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, sta
 
     case N_TABLE:
       EMIT("qi_make_table(state, ");
-      compile_table(gbuf, buf, ctx, lstk, sstk, lbl, node->h);
+      compile_table(gbuf, buf, ctx, ltab, lstk, sstk, lbl, node->h);
       EMIT(")");
       break;
 
     case N_CALL:
       EMIT("qi_call(state, ");
-      compile_node(gbuf, buf, ctx, lstk, sstk, lbl, node->a);
+      compile_node(gbuf, buf, ctx, ltab, lstk, sstk, lbl, node->a);
       EMIT(", ");
-      compile_list(gbuf, buf, ctx, lstk, sstk, lbl, node->l);
+      compile_list(gbuf, buf, ctx, ltab, lstk, sstk, lbl, node->l);
       EMIT(")");
       break;
 
     case N_MEMBER:
       EMIT("qi_index(state, ");
-      compile_node(gbuf, buf, ctx, lstk, sstk, lbl, node->a);
+      compile_node(gbuf, buf, ctx, ltab, lstk, sstk, 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, sstk, lbl, node->a);
+      compile_node(gbuf, buf, ctx, ltab, lstk, sstk, lbl, node->a);
       EMIT(", ");
-      compile_node(gbuf, buf, ctx, lstk, sstk, lbl, node->b);
+      compile_node(gbuf, buf, ctx, ltab, lstk, sstk, lbl, node->b);
       EMIT(")");
       break;
 
-    case N_ASSIGN: ASSIGN(node->a, compile_node(gbuf, buf, ctx, lstk, sstk, lbl, node->b)); break;
+    case N_ASSIGN:
+      if (node->a->tag == N_LIST) {
+        if (node->a->l->length < 2)
+          COMPILE_ERROR("illegal unpack assignment left-hand side");
+
+        NEWGID();
+
+        buffer_t *tbuf = buffer_new();
+
+        buffer_fmt(tbuf, "inline static qi_value_t *__assign%d(qi_state_t *state) {\n", gid);
+   
+        char *varname = tempvar();
+
+        buffer_fmt(tbuf, "qi_value_t *%s = ", varname);
+        compile_node(gbuf, tbuf, ctx, ltab, lstk, sstk, lbl, node->b);
+        buffer_fmt(tbuf, ";\n");
+
+        for (size_t i = 0; i < node->a->l->length; i++) {
+          ASSIGNIN(tbuf, (node_t *)node->a->l->data[i], buffer_fmt(tbuf, "qi_index(state, %s, qi_make_number(state, %d))", varname, i));
+          buffer_fmt(tbuf, ";\n");
+        }
+
+        buffer_fmt(tbuf, "return %s;\n}\n", varname);
 
-    case N_ASSIGN_ADD: COMPASSIGN(node->a, "add", compile_node(gbuf, buf, ctx, lstk, sstk, lbl, node->b)); break;
-    case N_ASSIGN_SUB: COMPASSIGN(node->a, "sub", compile_node(gbuf, buf, ctx, lstk, sstk, lbl, node->b)); break;
-    case N_ASSIGN_MUL: COMPASSIGN(node->a, "mul", compile_node(gbuf, buf, ctx, lstk, sstk, lbl, node->b)); break;
-    case N_ASSIGN_DIV: COMPASSIGN(node->a, "div", compile_node(gbuf, buf, ctx, lstk, sstk, lbl, node->b)); break;
-    case N_ASSIGN_IDIV: COMPASSIGN(node->a, "idiv", compile_node(gbuf, buf, ctx, lstk, sstk, lbl, node->b)); break;
-    case N_ASSIGN_MOD: COMPASSIGN(node->a, "mod", compile_node(gbuf, buf, ctx, lstk, sstk, lbl, node->b)); break;
-    case N_ASSIGN_POW: COMPASSIGN(node->a, "pow", compile_node(gbuf, buf, ctx, lstk, sstk, lbl, node->b)); break;
-    case N_ASSIGN_BOR: COMPASSIGN(node->a, "bor", compile_node(gbuf, buf, ctx, lstk, sstk, lbl, node->b)); break;
-    case N_ASSIGN_BAND: COMPASSIGN(node->a, "band", compile_node(gbuf, buf, ctx, lstk, sstk, lbl, node->b)); break;
+        buffer_appendb(gbuf, tbuf);
+
+        EMIT("__assign%d(state)", gid);
+
+        break;
+      } else if (node->a->tag == N_TUPLE) {
+        if (node->a->l->length < 2)
+          COMPILE_ERROR("illegal multiple assignment left-hand side");
+
+        if (node->b->tag != N_TUPLE || node->b->l->length < 2 || node->a->l->length != node->b->l->length)
+          COMPILE_ERROR("illegal multiple assignment right-hand side");
+
+        NEWGID();
+
+        buffer_t *tbuf = buffer_new();
+
+        buffer_fmt(tbuf, "qi_value_t *__assign%d(qi_state_t *state) {\n", gid);
+
+        list_t *vals = list_new();
+        for (size_t i = 0; i < node->b->l->length; i++) {
+          char *varname = tempvar();
+
+          buffer_fmt(tbuf, "qi_value_t *%s = ", varname);
+          compile_node(gbuf, tbuf, ctx, ltab, lstk, sstk, lbl, node->b->l->data[i]);
+          buffer_fmt(tbuf, ";\n");
+
+          list_push(vals, varname);
+        }
+
+        for (size_t i = 0; i < node->a->l->length; i++) {
+          char *varname = vals->data[i];
+
+          ASSIGNIN(tbuf, (node_t *)node->a->l->data[i], buffer_fmt(tbuf, "%s", varname));
+          buffer_fmt(tbuf, ";\n");
+        }
+
+        buffer_fmt(tbuf, "return %s;\n}\n", vals->data[vals->length-1]);
+
+        buffer_appendb(gbuf, tbuf);
+
+        EMIT("__assign%d(state)", gid);
+
+        break;
+      }
+
+      ASSIGN(node->a, compile_node(gbuf, buf, ctx, ltab, lstk, sstk, lbl, node->b));
+    break;
+
+    case N_ASSIGN_ADD: COMPASSIGN(node->a, "add", compile_node(gbuf, buf, ctx, ltab, lstk, sstk, lbl, node->b)); break;
+    case N_ASSIGN_SUB: COMPASSIGN(node->a, "sub", compile_node(gbuf, buf, ctx, ltab, lstk, sstk, lbl, node->b)); break;
+    case N_ASSIGN_MUL: COMPASSIGN(node->a, "mul", compile_node(gbuf, buf, ctx, ltab, lstk, sstk, lbl, node->b)); break;
+    case N_ASSIGN_DIV: COMPASSIGN(node->a, "div", compile_node(gbuf, buf, ctx, ltab, lstk, sstk, lbl, node->b)); break;
+    case N_ASSIGN_IDIV: COMPASSIGN(node->a, "idiv", compile_node(gbuf, buf, ctx, ltab, lstk, sstk, lbl, node->b)); break;
+    case N_ASSIGN_MOD: COMPASSIGN(node->a, "mod", compile_node(gbuf, buf, ctx, ltab, lstk, sstk, lbl, node->b)); break;
+    case N_ASSIGN_POW: COMPASSIGN(node->a, "pow", compile_node(gbuf, buf, ctx, ltab, lstk, sstk, lbl, node->b)); break;
+    case N_ASSIGN_BOR: COMPASSIGN(node->a, "bor", compile_node(gbuf, buf, ctx, ltab, lstk, sstk, lbl, node->b)); break;
+    case N_ASSIGN_BAND: COMPASSIGN(node->a, "band", compile_node(gbuf, buf, ctx, ltab, lstk, sstk, lbl, node->b)); break;
 
     case N_INC: 
       COMPASSIGN(node->a, "add", EMIT("state->one"));
@@ -3354,48 +3459,57 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, sta
         EMIT("qi_%s(state, \"%s\", ", node->tag == N_LET? "decl_const": "decl", entry.key);
         
         if (entry.value)
-          compile_node(gbuf, buf, ctx, lstk, sstk, lbl, entry.value);
+          compile_node(gbuf, buf, ctx, ltab, lstk, sstk, lbl, entry.value);
         else EMIT("state->nil");
 
         EMIT(");\n");
       });
     break;
 
-    case N_UNPACK: case N_VARUNPACK: case N_LETUNPACK: {
+    case N_VARUNPACK: case N_LETUNPACK: {
       char *varname = tempvar();
 
       EMIT("qi_value_t *%s = ", varname);
-      compile_node(gbuf, buf, ctx, lstk, sstk, lbl, node->a);
+      compile_node(gbuf, buf, ctx, ltab, lstk, sstk, lbl, node->a);
       EMIT(";\n");
 
       for (size_t i = 0; i < node->l->length; i++)
-        if (node->tag == N_UNPACK) {
-          EMIT("qi_set(state, false, \"%s\", qi_index(state, %s, qi_make_number(state, %d)));\n", node->l->data[i], varname, i);
-        } else {
-          EMIT("%s(state, \"%s\", qi_index(state, %s, qi_make_number(state, %d)));\n", node->tag == N_VARUNPACK? "qi_decl": "qi_decl_const", node->l->data[i], varname, i);
-        }
+        EMIT("%s(state, \"%s\", qi_index(state, %s, qi_make_number(state, %d)));\n", node->tag == N_VARUNPACK? "qi_decl": "qi_decl_const", node->l->data[i], varname, i);
     } break;
 
     case N_CONST:break;
 
     case N_IF:
       EMIT("if (_qi_truthy(state, ");
-      compile_node(gbuf, buf, ctx, lstk, sstk, lbl, node->a);
+      compile_node(gbuf, buf, ctx, ltab, lstk, sstk, lbl, node->a);
       EMIT(")) {\n");
-      compile_node(gbuf, buf, ctx, lstk, sstk, lbl, node->b);
+      compile_node(gbuf, buf, ctx, ltab, lstk, sstk, lbl, node->b);
       if (node->c) {
         EMIT("} else {\n");
-        compile_node(gbuf, buf, ctx, lstk, sstk, lbl, node->c);
+        compile_node(gbuf, buf, ctx, ltab, lstk, sstk, lbl, node->c);
       }
       EMIT("}");
       break;
 
     case N_SWITCH: {
       NEWGID();
+
+      if (node->t2) {
+        if (table_get(ltab, node->t2->text))
+          COMPILE_ERROR("redeclaration of loop label: '%s'", node->t2->text);
+
+        stack_t *pair = stack_new();
+
+        stack_push(pair, 1);
+        stack_push(pair, gid);
+
+        table_set(ltab, node->t2->text, pair);
+      }
+
       char *varname = tempvar();
 
       EMIT("qi_value_t *%s = ", varname);
-      compile_node(gbuf, buf, ctx, lstk, sstk, lbl, node->a);
+      compile_node(gbuf, buf, ctx, ltab, lstk, sstk, lbl, node->a);
       EMIT(";\n");
 
       for (size_t i = 0; i < node->l->length; i++) {
@@ -3403,7 +3517,7 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, sta
         char *label = tempvar();
 
         EMIT("if (_qi_equals(state, %s, ", varname);
-        compile_node(gbuf, buf, ctx, lstk, sstk, lbl, pair->data[0]);
+        compile_node(gbuf, buf, ctx, ltab, lstk, sstk, lbl, pair->data[0]);
         EMIT(")) goto %s;\n", label);
 
         pair->data[0] = label;
@@ -3420,13 +3534,13 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, sta
         node_t *block = pair->data[1];
 
         EMIT("%s:;\n", label);
-        compile_node(gbuf, buf, ctx, lstk, sstk, lbl, block);
+        compile_node(gbuf, buf, ctx, ltab, lstk, sstk, lbl, block);
       }
 
       EMIT("__default%d:;\n", gid);
 
       if (node->b)
-        compile_node(gbuf, buf, ctx, lstk, sstk, lbl, node->b);
+        compile_node(gbuf, buf, ctx, ltab, lstk, sstk, lbl, node->b);
 
       CTXPOP();
       SPOP();
@@ -3437,27 +3551,39 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, sta
     case N_FOR: {
       NEWGID();
 
+      if (node->t2) {
+        if (table_get(ltab, node->t2->text))
+          COMPILE_ERROR("redeclaration of loop label: '%s'", node->t2->text);
+
+        stack_t *pair = stack_new();
+
+        stack_push(pair, 0);
+        stack_push(pair, gid);
+
+        table_set(ltab, node->t2->text, pair);
+      }
+
       if (!node->a) {
         EMIT("for (;;) {\n");
       } else if (node->a && !node->b) {
         EMIT("while (_qi_truthy(state, ");
-        compile_node(gbuf, buf, ctx, lstk, sstk, lbl, node->a);
+        compile_node(gbuf, buf, ctx, ltab, lstk, sstk, lbl, node->a);
         EMIT(")) {\n");
       } else {
-        compile_node(gbuf, buf, ctx, lstk, sstk, lbl, node->a);
+        compile_node(gbuf, buf, ctx, ltab, lstk, sstk, lbl, node->a);
         EMIT("while (_qi_truthy(state, ");
-        compile_node(gbuf, buf, ctx, lstk, sstk, lbl, node->b);
+        compile_node(gbuf, buf, ctx, ltab, lstk, sstk, lbl, node->b);
         EMIT(")) {\n");
       }
 
       LPUSH(gid);
       CTXPUSH("for");
-      compile_node(gbuf, buf, ctx, lstk, sstk, lbl, node->d);
+      compile_node(gbuf, buf, ctx, ltab, lstk, sstk, lbl, node->d);
 
       EMIT("__continue%d:;\n", gid);
 
       if (node->c) {
-        compile_node(gbuf, buf, ctx, lstk, sstk, lbl, node->c);
+        compile_node(gbuf, buf, ctx, ltab, lstk, sstk, lbl, node->c);
 
         EMIT(";\n");
       }
@@ -3472,10 +3598,23 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, sta
 
     case N_FOROF: case N_FOROFVAR: case N_FOROFUNPACK: case N_FOROFVARUNPACK: {
       NEWGID();
+
+      if (node->t2) {
+        if (table_get(ltab, node->t2->text))
+          COMPILE_ERROR("redeclaration of loop label: '%s'", node->t2->text);
+
+        stack_t *pair = stack_new();
+
+        stack_push(pair, 0);
+        stack_push(pair, gid);
+
+        table_set(ltab, node->t2->text, pair);
+      }
+
       char *varname = tempvar();
 
       EMIT("qi_value_t *%s = qi_iter(state, ", varname);
-      compile_node(gbuf, buf, ctx, lstk, sstk, lbl, node->a);
+      compile_node(gbuf, buf, ctx, ltab, lstk, sstk, lbl, node->a);
       EMIT(");\n");
 
       if (node->tag == N_FOROFVAR) {
@@ -3495,7 +3634,7 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, sta
 
       LPUSH(gid);
       CTXPUSH("for");
-      compile_node(gbuf, buf, ctx, lstk, sstk, lbl, node->b);
+      compile_node(gbuf, buf, ctx, ltab, lstk, sstk, lbl, node->b);
       CTXPOP();
       LPOP();
 
@@ -3510,13 +3649,24 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, sta
         COMPILE_ERROR("break outside of a loop or a switch");
 
       if (node->t) {
-        unsigned int offset = abs(atoi(node->t->text)) + 1;
+        if (node->t->tag == T_NUMBER) {
+          unsigned int offset = abs(atoi(node->t->text)) + 1;
 
-        if (offset < 2 || offset > lstk->length)
-          COMPILE_ERROR("%d is not a valid break loop offset", offset-1);
+          if (offset < 2 || offset > lstk->length)
+            COMPILE_ERROR("%d is not a valid break loop offset", offset-1);
+
+          EMIT("goto __break%d;", lstk->data[lstk->length-offset]);
+        } else {
+          char *label = node->t->text;
 
-        EMIT("goto __break%d;", lstk->data[lstk->length-offset]);
+          stack_t *pair = table_get(ltab, label);
 
+          if (!pair)
+            COMPILE_ERROR("undefined loop label: '%s'", label);
+
+          EMIT("goto __break%d;", pair->data[1]);
+        }
+        
         break;
       }
 
@@ -3528,13 +3678,27 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, sta
         COMPILE_ERROR("continue outside of a loop");
 
       if (node->t) {
-        unsigned int offset = abs(atoi(node->t->text)) + 1;
+        if (node->t->tag == T_NUMBER) {
+          unsigned int offset = abs(atoi(node->t->text)) + 1;
+
+          if (offset < 2 || offset > lstk->length)
+            COMPILE_ERROR("%d is not a valid break loop offset", offset-1);
+
+          EMIT("goto __continue%d;", lstk->data[lstk->length-offset]);
+        } else {
+          char *label = node->t->text;
+
+          stack_t *pair = table_get(ltab, label);
 
-        if (offset < 2 || offset > lstk->length)
-          COMPILE_ERROR("%d is not a valid continue loop offset", offset-1);
+          if (pair->data[1])
+            COMPILE_ERROR("continue on a switch loop label: '%s'", label);
 
-        EMIT("goto __continue%d;", lstk->data[lstk->length-offset]);
+          if (!pair)
+            COMPILE_ERROR("undefined loop label: '%s'", label);
 
+          EMIT("goto __continue%d;", pair->data[1]);
+        }
+        
         break;
       }
 
@@ -3550,7 +3714,7 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, sta
 
       LBPUSH();
       CTXPUSH("gap");
-      compile_node(gbuf, tbuf, ctx, lstk, sstk, lbl, node->a);
+      compile_node(gbuf, tbuf, ctx, ltab, lstk, sstk, lbl, node->a);
       CTXPOP();
       LBPOP();
 
@@ -3575,7 +3739,7 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, sta
       EMIT("return ");
 
       if (node->a)
-        compile_node(gbuf, buf, ctx, lstk, sstk, lbl, node->a);
+        compile_node(gbuf, buf, ctx, ltab, lstk, sstk, lbl, node->a);
       else EMIT("state->nil");
 
       EMIT(";");
@@ -3587,12 +3751,12 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, sta
 
     case N_TRY:
       EMIT("qi_try(state, {\n");
-      compile_node(gbuf, buf, ctx, lstk, sstk, lbl, node->a);
+      compile_node(gbuf, buf, ctx, ltab, lstk, sstk, 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, sstk, lbl, node->b);
+      compile_node(gbuf, buf, ctx, ltab, lstk, sstk, lbl, node->b);
       CTXPOP();
       EMIT("}, NULL);\n");
       break;
@@ -3600,7 +3764,7 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, sta
     case N_THROW:
       EMIT("qi_throw(state, ");
       if (node->a)
-        compile_node(gbuf, buf, ctx, lstk, sstk, lbl, node->a);
+        compile_node(gbuf, buf, ctx, ltab, lstk, sstk, lbl, node->a);
       else {
         EMIT("state->nil");
       }
@@ -3625,22 +3789,22 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, sta
     case N_REQUIRE: {
       char *path = unescape(node->t->text);
 
-      if (require_once(gbuf, buf, ctx, lstk, sstk, lbl, path) < 0)
+      if (require_once(gbuf, buf, ctx, ltab, lstk, sstk, lbl, path) < 0)
         COMPILE_ERROR("'%s' is not a valid file path or a builtin library name", path);
       } break;
 
     case N_IFEXPR:
       EMIT("(_qi_truthy(state, ");
-      compile_node(gbuf, buf, ctx, lstk, sstk, lbl, node->a);
+      compile_node(gbuf, buf, ctx, ltab, lstk, sstk, lbl, node->a);
       EMIT(")? ");
-      compile_node(gbuf, buf, ctx, lstk, sstk, lbl, node->b);
+      compile_node(gbuf, buf, ctx, ltab, lstk, sstk, lbl, node->b);
       EMIT(": ");
-      compile_node(gbuf, buf, ctx, lstk, sstk, lbl, node->c);
+      compile_node(gbuf, buf, ctx, ltab, lstk, sstk, lbl, node->c);
       EMIT(")");
       break;
 
     case N_FUNCEXPR:
-      compile_func(gbuf, buf, ctx, lstk, sstk, lbl, node, NULL);
+      compile_func(gbuf, buf, ctx, ltab, lstk, sstk, lbl, node, NULL);
       break;
 
     case N_EQUALS:
@@ -3759,25 +3923,25 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, sta
   }
 }
 
-void compile_into(char *source, buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, stack_t *sstk, list_t *lbl) {
+void compile_into(char *source, buffer_t *gbuf, buffer_t *buf, list_t *ctx, table_t *ltab, stack_t *lstk, stack_t *sstk, list_t *lbl) {
   node_t *n = parse(source);
 
-  compile_node(gbuf, buf, ctx, lstk, sstk, lbl, n);
+  compile_node(gbuf, buf, ctx, ltab, lstk, sstk, lbl, n);
 }
 
 char *compile(char *source) {
   list_t *ctx = list_new();
+  table_t *ltab = table_new();
   stack_t *lstk = stack_new();
   stack_t *sstk = stack_new();
   list_t *lbl = list_new();
   LBPUSH();
   
   buffer_t *gbuf = buffer_new();
-
   buffer_t *buf = buffer_new();
 
-  require_once(gbuf, buf, ctx, lstk, sstk, lbl, "std");
-  compile_into(source, gbuf, buf, ctx, lstk, sstk, lbl);
+  require_once(gbuf, buf, ctx, ltab, lstk, sstk, lbl, "std");
+  compile_into(source, gbuf, buf, ctx, ltab, lstk, sstk, lbl);
 
   buffer_t *rbuf = buffer_new();