txlyre 1 天之前
父節點
當前提交
5b38fbf6d4
共有 1 個文件被更改,包括 453 次插入121 次删除
  1. 453 121
      qic.c

+ 453 - 121
qic.c

@@ -671,9 +671,12 @@ token_t *tokenize_string(char *source, size_t *pos) {
 
     if (c == '"' || c == '\\' || c == '?')
       buffer_append(text, '\\');
-    else if (c == '\n')
+    else if (c == '\n') {
       buffer_appends(text, "\\n");
 
+      continue;
+    }
+
     buffer_append(text, c);
   }
 
@@ -2496,9 +2499,21 @@ node_t *_parse_stmt(list_t *tokens, size_t *pos, int allow_comma) {
   else if (MATCH(RETURN)) {
     node_t *a = NULL;
 
-    if (!AT(RCB) && !CLIFF)
+    if (!AT(EOF) && !AT(RCB) && !CLIFF) {
       a = parse_expr(tokens, pos);
 
+      if (!CLIFF_AHEAD && MATCH(COMMA)) {
+        list_t *l = list_new();
+        list_push(l, a);
+
+        do {
+          list_push(l, parse_expr(tokens, pos));
+        } while (!CLIFF_AHEAD && MATCH(COMMA));
+
+        a = NODEL(TUPLE, l);
+      }
+    }
+
     return NODE1(RETURN, a);
   } else if (MATCH(DEFER)) {
     node_t *a;
@@ -2574,48 +2589,50 @@ node_t *_parse_stmt(list_t *tokens, size_t *pos, int allow_comma) {
       EXPECT(RPAR, ")");
     }
 
-    EXPECT(LCB, "{");
-
     list_t *triples = list_new();
 
-    for (;;) {
-      int is_out = 0;
-      int is_static = 0;
-
-      if (AT(RCB))
-        break;
-
-      if (MATCH(LET))
-        is_out = 1;
-      else if (MATCH(CONST))
-        is_static = 1;
-
-      if (!AT(NAME))
-        PARSE_ERROR("expected identifier");
-
-      list_t *triple = list_new();
-
-      token_t *t = tokens->data[(*pos)++];
-      list_push(triple, t);
-
-      int *flagp = malloc_checked(sizeof(int));
-      int flag = is_static ? 2 : is_out ? 1 : 0;
-
-      memcpy(flagp, &flag, sizeof(int));
-
-      if (MATCH(ASSIGN)) {
-        list_push(triple, flagp);
-        list_push(triple, parse_expr(tokens, pos));
-      } else {
-        list_push(triple, flagp);
-        list_push(triple, parse_func(tokens, pos, 1));
+    if (!CLIFF && !AT(EOF) && !AT(SEMI)) {
+      EXPECT(LCB, "{");
+  
+      for (;;) {
+        int is_out = 0;
+        int is_static = 0;
+  
+        if (AT(RCB))
+          break;
+  
+        if (MATCH(LET))
+          is_out = 1;
+        else if (MATCH(CONST))
+          is_static = 1;
+  
+        if (!AT(NAME))
+          PARSE_ERROR("expected identifier");
+  
+        list_t *triple = list_new();
+  
+        token_t *t = tokens->data[(*pos)++];
+        list_push(triple, t);
+  
+        int *flagp = malloc_checked(sizeof(int));
+        int flag = is_static ? 2 : is_out ? 1 : 0;
+  
+        memcpy(flagp, &flag, sizeof(int));
+  
+        if (MATCH(ASSIGN)) {
+          list_push(triple, flagp);
+          list_push(triple, parse_expr(tokens, pos));
+        } else {
+          list_push(triple, flagp);
+          list_push(triple, parse_func(tokens, pos, 1));
+        }
+  
+        list_push(triples, triple);
       }
-
-      list_push(triples, triple);
+  
+      EXPECT(RCB, "}");
     }
 
-    EXPECT(RCB, "}");
-
     list_t *pair = list_new();
     list_push(pair, l);
     list_push(pair, triples);
@@ -3081,7 +3098,7 @@ node_t *parse(char *source) {
   }
 
 void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, table_t *ltab,
-                  int_stack_t *lstk, int_stack_t *sstk, list_t *lbl,
+                  list_t *lstk, list_t *sstk, list_t *lbl,
                   node_t *node);
 
 char *tempvar() {
@@ -3094,7 +3111,7 @@ char *tempvar() {
 }
 
 void compile_list(buffer_t *gbuf, buffer_t *buf, list_t *ctx, table_t *ltab,
-                  int_stack_t *lstk, int_stack_t *sstk, list_t *lbl,
+                  list_t *lstk, list_t *sstk, list_t *lbl,
                   list_t *seq) {
   if (!seq || seq->length < 1) {
     EMIT("NULL");
@@ -3162,7 +3179,7 @@ void compile_list(buffer_t *gbuf, buffer_t *buf, list_t *ctx, table_t *ltab,
 }
 
 void compile_table(buffer_t *gbuf, buffer_t *buf, list_t *ctx, table_t *ltab,
-                   int_stack_t *lstk, int_stack_t *sstk, list_t *lbl,
+                   list_t *lstk, list_t *sstk, list_t *lbl,
                    table_t *table) {
   if (!table || table->used < 1) {
     EMIT("NULL");
@@ -3201,12 +3218,11 @@ int in_context(list_t *ctx, char *s) {
   if (!ctx->length)
     return 0;
 
-  for (ssize_t i = ctx->length - 1; i >= 0; i--) {
+  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;
 }
@@ -3215,12 +3231,11 @@ int in_switch(list_t *ctx) {
   if (!ctx->length)
     return 0;
 
-  for (ssize_t i = ctx->length - 1; i >= 0; i--) {
+  for (ssize_t i = ctx->length - 1; i >= 0; i--)
     if (strcmp(ctx->data[i], "gap") == 0 || strcmp(ctx->data[i], "for") == 0)
       break;
     else if (strcmp(ctx->data[i], "switch") == 0)
       return 1;
-  }
 
   return 0;
 }
@@ -3231,12 +3246,11 @@ size_t count_ctxs(list_t *ctx, char *s) {
 
   size_t k = 0;
 
-  for (ssize_t i = ctx->length - 1; i >= 0; i--) {
+  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;
 }
@@ -3247,13 +3261,23 @@ list_t *CONSTANTS;
 #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 LPUSH(i) {\
+  int_stack_t *pair = stack_new();\
+  stack_push(pair, (i));\
+  stack_push(pair, scope_index(ctx));\
+  list_push(lstk, pair);\
+}
+#define LPOP() list_pop(lstk)
+#define LID (((int_stack_t *)lstk->data[lstk->length - 1])->data[0])
 
-#define SPUSH(i) stack_push(sstk, (i))
-#define SPOP() stack_pop(sstk)
-#define SID (sstk->data[sstk->length - 1])
+#define SPUSH(i) {\
+  int_stack_t *pair = stack_new();\
+  stack_push(pair, (i));\
+  stack_push(pair, scope_index(ctx));\
+  list_push(sstk, pair);\
+}
+#define SPOP() list_pop(sstk)
+#define SID (sstk->data[sstk->length - 1]->data[0])
 
 #define LBPUSH() list_push(lbl, table_new())
 #define LBPOP() list_pop(lbl)
@@ -3279,7 +3303,7 @@ void emit_debug(buffer_t *buf, node_t *node) {
 }
 
 void compile_func(buffer_t *gbuf, buffer_t *buf, list_t *ctx, table_t *ltab,
-                  int_stack_t *lstk, int_stack_t *sstk, list_t *lbl,
+                  list_t *lstk, list_t *sstk, list_t *lbl,
                   node_t *node, char *name) {
   NEWGID();
 
@@ -3327,7 +3351,7 @@ void compile_func(buffer_t *gbuf, buffer_t *buf, list_t *ctx, table_t *ltab,
     });
   }
 
-  compile_node(gbuf, tbuf, ctx, table_new(), stack_new(), stack_new(), lbl,
+  compile_node(gbuf, tbuf, ctx, table_new(), list_new(), list_new(), lbl,
                node->a);
 
   list_pop(FUNCNAMES);
@@ -3381,8 +3405,36 @@ node_t *const_get(char *name) {
   return val;
 }
 
+size_t count_scopes(list_t *ctx, size_t offset) {
+  if (ctx->length < 2 || offset >= ctx->length)
+    return 0;
+
+  size_t k = 0;
+
+  for (ssize_t i = ctx->length - 1; i >= 0 && i > offset; i--)
+    if (strcmp(ctx->data[i], "gap") == 0)
+      break;
+    else if (strcmp(ctx->data[i], "scope") == 0)
+      k++;
+
+  return k;
+}
+
+size_t scope_index(list_t *ctx) {
+  if (ctx->length < 2)
+    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], "scope") == 0)
+      return i;
+
+  return 0;
+}
+
 void compile_block(buffer_t *gbuf, buffer_t *buf, list_t *ctx, table_t *ltab,
-                   int_stack_t *lstk, int_stack_t *sstk, list_t *lbl,
+                   list_t *lstk, list_t *sstk, list_t *lbl,
                    list_t *block) {
   for (size_t i = 0; i < block->length; i++) {
     node_t *node = block->data[i];
@@ -3481,10 +3533,11 @@ void compile_block(buffer_t *gbuf, buffer_t *buf, list_t *ctx, table_t *ltab,
 
       NEWGID();
 
-      size_t *n = malloc_checked(sizeof(size_t));
-      memcpy(n, &gid, sizeof(size_t));
+      int_stack_t *pair = stack_new();
+      stack_push(pair, gid);
+      stack_push(pair, scope_index(ctx));
 
-      table_set(list_index(lbl, -1), label, n);
+      table_set(list_index(lbl, -1), label, pair);
     }
   }
 
@@ -3504,6 +3557,198 @@ void compile_block(buffer_t *gbuf, buffer_t *buf, list_t *ctx, table_t *ltab,
 }
 
 const char *STD[][2] = {
+    {"json",
+    "require \"string\"\n"
+    "class JSONError(Error)\n"
+    "func json_tokenize(s) {\n"
+    "  let toks = []\n"
+    "  for var i = 0; i < len(s); i++ {\n"
+    "    if isws(s[i]) {\n"
+    "      continue\n"
+    "    }\n"
+    "    if s[i] in \"[]{}:,\"\n"
+    "      toks.push((s[i], nil))\n"
+    "    elif isalpha(s[i]) {\n"
+    "      var text = \"\"\n"
+    "      for i < len(s) && isalpha(s[i])\n"
+    "        text += s[i++]\n"
+    "      toks.push((\"name\", text))\n"
+    "      i--\n"
+    "    } elif s[i] == '-' || isdigit(s[i]) {\n"
+    "      var text = \"\"\n"
+    "      if s[i] == '-'\n"
+    "        text += s[i++]\n"
+    "      if i >= len(s) || !isdigit(s[i])\n"
+    "        throw JSONError(f\"${i}: malformed number literal\")\n"
+    "      for i < len(s) && isdigit(s[i]) {\n"
+    "        text += s[i++]\n"
+    "        if i < len(s) && s[i] == \".\" && \".\" !in text\n"
+    "          text += s[i++]\n"
+    "      }\n"
+    "      i--\n"
+    "      toks.push((\"number\", text))\n"
+    "    } elif s[i] == '\"' {\n"
+    "      var text = \"\"\n"
+    "      i++\n"
+    "      for i < len(s) && s[i] != '\"'\n"
+    "        text += s[i++]\n"
+    "      if i >= len(s) || s[i] != '\"'\n"
+    "        throw JSONError(f\"#${i}: unterminated string literal\")\n"
+    "      toks.push((\"string\", text))\n"
+    "    } else\n"
+    "      throw JSONError(f\"#${i}: unknown input\")\n"
+    "  }\n"
+    "  return toks\n"
+    "}\n"
+    "func json_parse(toks) {\n"
+    "  func parse_array(toks) {\n"
+    "    let r = []\n"
+    "    if toks && toks[0][0] == ']'\n"
+    "      return r, toks[1:]\n"
+    "    for {\n"
+    "      var e\n"
+    "      [e, toks] = parse_expression(toks)\n"
+    "      r.push(e)\n"
+    "      if !toks || toks[0][0] != ','\n"
+    "        break\n"
+    "      if toks[0][0] == \",\"\n"
+    "        toks = toks[1:]\n"
+    "    }\n"
+    "    if !toks || toks[0][0] != ']'\n"
+    "      throw JSONError(\"missing ]\")\n"
+    "    return r, toks[1:]\n"
+    "  }\n"
+    "  func parse_object(toks) {\n"
+    "    let r = {}\n"
+    "    for {\n"
+    "      var k, e\n"
+    "      if !toks || toks[0][0] != \"string\"\n"
+    "        throw JSONError(\"expected string\")\n"
+    "      k = toks[0][1]\n"
+    "      toks = toks[1:]\n"
+    "      if !toks || toks[0][0] != \":\"\n"
+    "        throw JSONError(\"expected :\")\n"
+    "      toks = toks[1:]\n"
+    "      [e, toks] = parse_expression(toks)\n"
+    "      r[k] = e\n"
+    "      if !toks || toks[0][0] != \",\"\n"
+    "        break\n"
+    "      if toks[0][0] == \",\"\n"
+    "        toks = toks[1:]\n"
+    "    }\n"
+    "    if !toks || toks[0][0] != \"}\"\n"
+    "      throw JSONError(\"missing }\")\n"
+    "    return r, toks[1:]\n"
+    "  }\n"
+    "  func parse_expression(toks) {\n"
+    "    if toks\n"
+    "      switch toks[0][0] {\n"
+    "        case \"{\":\n"
+    "          return parse_object(toks[1:])\n"
+    "        case \"[\":\n"
+    "          return parse_array(toks[1:])\n"
+    "        case \"name\":\n"
+    "          switch toks[0][1] {\n"
+    "            case \"true\":\n"
+    "              return true, toks[1:]\n"
+    "            case \"false\":\n"
+    "              return false, toks[1:]\n"
+    "            case \"null\":\n"
+    "              return nil, toks[1:]\n"
+    "          }\n"
+    "          throw JSONError(f\"illegal name: ${toks[0][1]}\")\n"
+    "        case \"number\":\n"
+    "          return num(toks[0][1]), toks[1:]\n"
+    "        case \"string\":\n"
+    "          return toks[0][1], toks[1:]\n"
+    "      }\n"
+    "    throw JSONError(\"expected expression\")\n"
+    "  }\n"
+    "  if !toks\n"
+    "    throw JSONError(\"empty document\")\n"
+    "  if toks[0][0] !in \"{[\"\n"
+    "    throw JSONError(\"expected { or [\")\n"
+    "  let [r, unconsumed] = parse_expression(toks)\n"
+    "  if unconsumed\n"
+    "    throw JSONError(\"unconsumed input\")\n"
+    "  return r\n"
+    "}\n"
+    "func json_read(s)\n"
+    "  return json_parse(json_tokenize(s))\n"
+    "func json_dump(d) {\n"
+    "  func escape(s) {\n"
+    "    var r = \"\"\n"
+    "    var skip = false\n"
+    "    for var c of s {\n"
+    "      if skip {\n"
+    "        switch c {\n"
+    "          case \"n\":\n"
+    "            r += \"\\n\"\n"
+    "            break\n"
+    "          case \"r\":\n"
+    "            r += \"\\r\"\n"
+    "            break\n"
+    "          case \"t\":\n"
+    "            r += \"\\t\"\n"
+    "            break\n"
+    "          case '\"':\n"
+    "            r += `\\\"`\n"
+    "            break\n"
+    "          case `\\`:\n"
+    "            r += `\\\\`\n"
+    "            break\n"
+    "          default:\n"
+    "            r += c\n"
+    "        }\n"
+    "        skip = false\n"
+    "        continue\n"
+    "      }\n"
+    "      if c == `\\` {\n"
+    "        skip = true\n"
+    "        continue\n"
+    "      }\n"
+    "      r += c\n"
+    "    }\n"
+    "    return r\n"
+    "  }\n"
+    "  switch type(d) {\n"
+    "    case \"nil\":\n"
+    "      return \"null\"\n"
+    "    case \"boolean\":\n"
+    "      return d? \"true\": \"false\"\n"
+    "    case \"number\":\n"
+    "      return str(d)\n"
+    "    case \"string\":\n"
+    "      return \"\\\"\" + escape(d) + \"\\\"\"\n"
+    "    case \"list\":\n"
+    "      var f = true\n"
+    "      var buf = \"[\"\n"
+    "      for var el of d {\n"
+    "        if f\n"
+    "          f = false\n"
+    "        else\n"
+    "          buf += \", \"\n"
+    "        buf += json_dump(el)   \n"
+    "      }\n"
+    "      buf += \"]\"\n"
+    "      return buf\n"
+    "    case \"table\":\n"
+    "      var f = true\n"
+    "      var buf = \"{\"\n"
+    "      for var [k, v] of enumerate(d) {\n"
+    "        if f\n"
+    "          f = false\n"
+    "        else\n"
+    "          buf += \", \"\n"
+    "        buf += f`\"${escape(k)}\": ${json_dump(v)}`\n"
+    "      }\n"
+    "      buf += \"}\"\n"
+    "      return buf\n"
+    "  }\n"
+    "  throw JSONError(f\"cannot serialize ${type(d)} into a JSON\")\n"
+    "}\n"
+    },
+
     {"utf8", "func utf8_chrlen(s) {\n"
              "  s = bytes(s)\n"
              "  var z = len(s)\n"
@@ -3900,11 +4145,11 @@ char *unescape(char *s) {
 }
 
 void compile_into(char *source, buffer_t *gbuf, buffer_t *buf, list_t *ctx,
-                  table_t *ltab, int_stack_t *lstk, int_stack_t *sstk,
+                  table_t *ltab, list_t *lstk, list_t *sstk,
                   list_t *lbl);
 
 int require_once(buffer_t *gbuf, buffer_t *buf, list_t *ctx, table_t *ltab,
-                 int_stack_t *lstk, int_stack_t *sstk, list_t *lbl,
+                 list_t *lstk, list_t *sstk, list_t *lbl,
                  char *path) {
   char *source = NULL;
 
@@ -4758,7 +5003,7 @@ node_t *expand_mvars(node_t *node, node_t *_node, int expr) {
 }
 
 void compile_macro_call(buffer_t *gbuf, buffer_t *buf, list_t *ctx,
-                        table_t *ltab, int_stack_t *lstk, int_stack_t *sstk,
+                        table_t *ltab, list_t *lstk, list_t *sstk,
                         list_t *lbl, node_t *node, int expr) {
   size_t argc = node->l->length;
   char *name = node->t->text;
@@ -4815,7 +5060,7 @@ void compile_macro_call(buffer_t *gbuf, buffer_t *buf, list_t *ctx,
 }
 
 void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, table_t *ltab,
-                  int_stack_t *lstk, int_stack_t *sstk, list_t *lbl,
+                  list_t *lstk, list_t *sstk, list_t *lbl,
                   node_t *node) {
   switch (node->tag) {
   case N_TOPLEVEL:
@@ -5196,7 +5441,7 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, table_t *ltab,
         ASSIGNIN(tbuf, (node_t *)node->a->l->data[i],
                  buffer_fmt(tbuf,
                             "qi_index(state, %s, qi_make_number(state, %d))",
-                            varname, i), falss);
+                            varname, i), false);
         buffer_fmt(tbuf, ";\n");
       }
 
@@ -5355,6 +5600,7 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, table_t *ltab,
 
   case N_IF:
     CTXPUSH("scope");
+
     EMIT("qi_new_scope(state);\n");
     EMIT("if (_qi_truthy(state, ");
     compile_node(gbuf, buf, ctx, ltab, lstk, sstk, lbl, node->a);
@@ -5373,16 +5619,21 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, table_t *ltab,
   case N_SWITCH: {
     NEWGID();
 
+    CTXPUSH("scope");
+
+    EMIT("qi_new_scope(state);\n");
+
     if (node->t2) {
       if (table_get(ltab, node->t2->text))
         COMPILE_ERROR("redeclaration of loop label: '%s'", node->t2->text);
 
-      int_stack_t *pair = stack_new();
+      int_stack_t *triple = stack_new();
 
-      stack_push(pair, 1);
-      stack_push(pair, gid);
+      stack_push(triple, 1);
+      stack_push(triple, gid);
+      stack_push(triple, scope_index(ctx));
 
-      table_set(ltab, node->t2->text, pair);
+      table_set(ltab, node->t2->text, triple);
     }
 
     char *varname = tempvar();
@@ -5410,6 +5661,8 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, table_t *ltab,
     SPUSH(gid);
     CTXPUSH("switch");
 
+    int has_default = 0;
+
     for (size_t i = 0; i < node->l->length; i++) {
       list_t *pair = node->l->data[i];
       char *label = pair->data[0];
@@ -5417,6 +5670,8 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, table_t *ltab,
 
       if (!label) {
         EMIT("__default%d:;\n", gid);
+
+        has_default = 1;
       } else {
         EMIT("%s:;\n", label);
       }
@@ -5424,30 +5679,38 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, table_t *ltab,
       compile_node(gbuf, buf, ctx, ltab, lstk, sstk, lbl, block);
     }
 
+    CTXPOP();
     CTXPOP();
     SPOP();
 
+    if (!has_default) {
+      EMIT("__default%d:;\n", gid);
+    }
+
     EMIT("__break%d:;\n", gid);
+
+    EMIT("qi_old_scope(state);\n");
   } break;
 
   case N_FOR: {
     NEWGID();
 
+    CTXPUSH("scope");
+    EMIT("qi_new_scope(state);\n");
+ 
     if (node->t2) {
       if (table_get(ltab, node->t2->text))
         COMPILE_ERROR("redeclaration of loop label: '%s'", node->t2->text);
 
-      int_stack_t *pair = stack_new();
+      int_stack_t *triple = stack_new();
 
-      stack_push(pair, 0);
-      stack_push(pair, gid);
+      stack_push(triple, 0);
+      stack_push(triple, gid);
+      stack_push(triple, scope_index(ctx));
 
-      table_set(ltab, node->t2->text, pair);
+      table_set(ltab, node->t2->text, triple);
     }
 
-    CTXPUSH("scope");
-    EMIT("qi_new_scope(state);\n");
- 
     if (!node->a) {
       EMIT("for (;;) {\n");
     } else if (node->a && !node->b) {
@@ -5504,21 +5767,22 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, table_t *ltab,
   case N_FOROFVARUNPACK: {
     NEWGID();
 
+    CTXPUSH("scope");
+    EMIT("qi_new_scope(state);\n");
+
     if (node->t2) {
       if (table_get(ltab, node->t2->text))
         COMPILE_ERROR("redeclaration of loop label: '%s'", node->t2->text);
 
-      int_stack_t *pair = stack_new();
+      int_stack_t *triple = stack_new();
 
-      stack_push(pair, 0);
-      stack_push(pair, gid);
+      stack_push(triple, 0);
+      stack_push(triple, gid);
+      stack_push(triple, scope_index(ctx));
 
-      table_set(ltab, node->t2->text, pair);
+      table_set(ltab, node->t2->text, triple);
     }
 
-    CTXPUSH("scope");
-    EMIT("qi_new_scope(state);\n");
-
     char *varname = tempvar();
 
     EMIT("qi_value_t *%s = qi_iter(state, ", varname);
@@ -5571,7 +5835,7 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, table_t *ltab,
     EMIT("qi_old_scope(state);\n");
   } break;
 
-  case N_BREAK:
+  case N_BREAK: {
     if (!INCTX("for") && !INCTX("switch"))
       COMPILE_ERROR("break outside of a loop or a switch");
 
@@ -5582,25 +5846,55 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, table_t *ltab,
         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]);
+        int_stack_t *pair = lstk->data[lstk->length - offset];
+        size_t scopes_k = count_scopes(ctx, pair->data[1]);
+
+        for (size_t i = 0; i < scopes_k; i++) {
+          EMIT("qi_old_scope(state);\n");
+        }
+
+        EMIT("goto __break%d;", pair->data[0]);
       } else {
         char *label = node->t->text;
 
-        int_stack_t *pair = table_get(ltab, label);
+        int_stack_t *triple = table_get(ltab, label);
 
-        if (!pair)
+        if (!triple)
           COMPILE_ERROR("undefined loop label: '%s'", label);
 
-        EMIT("goto __break%d;", pair->data[1]);
+        size_t scopes_k = count_scopes(ctx, triple->data[2]);
+
+        for (size_t i = 0; i < scopes_k; i++) {
+          EMIT("qi_old_scope(state);\n");
+        }
+
+        EMIT("goto __break%d;", triple->data[1]);
       }
 
       break;
     }
 
-    EMIT("goto __break%d;", in_switch(ctx) ? SID : LID);
-    break;
+    size_t gid;
+    int_stack_t *pair;
+
+    if (in_switch(ctx)) {
+      pair = sstk->data[sstk->length - 1];
+      gid = pair->data[0];
+    } else {
+      pair = lstk->data[lstk->length - 1];
+      gid = pair->data[0];
+    }
+   
+    size_t scopes_k = count_scopes(ctx, pair->data[1]);
+
+    for (size_t i = 0; i < scopes_k; i++) {
+      EMIT("qi_old_scope(state);\n");
+    }
 
-  case N_CONTINUE:
+    EMIT("goto __break%d;", gid);
+    } break;
+
+  case N_CONTINUE: {
     if (!INCTX("for"))
       COMPILE_ERROR("continue outside of a loop");
 
@@ -5611,26 +5905,48 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, table_t *ltab,
         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]);
+        int_stack_t *pair = lstk->data[lstk->length - offset];
+        size_t scopes_k = count_scopes(ctx, pair->data[1]);
+
+        for (size_t i = 0; i < scopes_k; i++) {
+          EMIT("qi_old_scope(state);\n");
+        }
+
+        EMIT("goto __continue%d;", pair->data[0]);
       } else {
         char *label = node->t->text;
 
-        int_stack_t *pair = table_get(ltab, label);
+        int_stack_t *triple = table_get(ltab, label);
 
-        if (pair->data[1])
+        if (!triple)
+          COMPILE_ERROR("undefined loop label: '%s'", label);
+
+        if (triple->data[0])
           COMPILE_ERROR("continue on a switch loop label: '%s'", label);
 
-        if (!pair)
-          COMPILE_ERROR("undefined loop label: '%s'", label);
+        size_t scopes_k = count_scopes(ctx, triple->data[2]);
+
+        for (size_t i = 0; i < scopes_k; i++) {
+          EMIT("qi_old_scope(state);\n");
+        }
 
-        EMIT("goto __continue%d;", pair->data[1]);
+        EMIT("goto __continue%d;", triple->data[1]);
       }
 
       break;
     }
+    
+    int_stack_t *pair = lstk->data[lstk->length - 1];
+    size_t gid = pair->data[0];
+   
+    size_t scopes_k = count_scopes(ctx, pair->data[1]);
 
-    EMIT("goto __continue%d;", LID);
-    break;
+    for (size_t i = 0; i < scopes_k; i++) {
+      EMIT("qi_old_scope(state);\n");
+    }
+
+    EMIT("goto __continue%d;", gid);
+    } break;
 
   case N_DEFER: {
     NEWGID();
@@ -5643,7 +5959,7 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, table_t *ltab,
     CTXPUSH("gap");
     list_push(CONSTANTS, table_new());
     list_push(MACROS, table_new());
-    compile_node(gbuf, tbuf, ctx, table_new(), stack_new(), stack_new(), lbl,
+    compile_node(gbuf, tbuf, ctx, table_new(), list_new(), list_new(), lbl,
                  node->a);
     list_pop(MACROS);
     list_pop(CONSTANTS);
@@ -5657,25 +5973,28 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, table_t *ltab,
     EMIT("qi_add_defer(state, -1, __%s%d);", PREFIX, gid);
   } break;
 
-  case N_RETURN:
+  case N_RETURN: {
     if (!INCTX("func"))
       COMPILE_ERROR("return outside of a function");
 
+    char *varname = NULL;
+
+    if (node->a) {
+      varname = tempvar();
+
+      EMIT("qi_value_t *%s = ", varname);
+      compile_node(gbuf, buf, ctx, ltab, lstk, sstk, lbl, node->a);
+      EMIT(";\n");
+    }
+   
     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, ltab, lstk, sstk, lbl, node->a);
-    else
-      EMIT("state->nil");
-
-    EMIT(";");
-    break;
+    EMIT("return %s;", varname? varname: "state->nil");
+    } break;
 
   case N_FUNCDEF:
     break;
@@ -5737,7 +6056,7 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, table_t *ltab,
     LBPUSH();
     CTXPUSH("gap");
     CTXPUSH("trap");
-    compile_node(gbuf, buf, ctx, table_new(), stack_new(), stack_new(), lbl,
+    compile_node(gbuf, buf, ctx, table_new(), list_new(), list_new(), lbl,
                  node->a);
     CTXPOP();
     CTXPOP();
@@ -5747,7 +6066,7 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, table_t *ltab,
       EMIT("qi_decl(state, \"%s\", trap->value);\n", node->t->text);
     LBPUSH();
     CTXPUSH("gap");
-    compile_node(gbuf, buf, ctx, table_new(), stack_new(), stack_new(), lbl,
+    compile_node(gbuf, buf, ctx, table_new(), list_new(), list_new(), lbl,
                  node->b);
     CTXPOP();
     LBPOP();
@@ -5760,7 +6079,7 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, table_t *ltab,
       buffer_fmt(tbuf, "void __%s%d(qi_state_t *state) {\n", PREFIX, gid);
       LBPUSH();
       CTXPUSH("gap");
-      compile_node(gbuf, tbuf, ctx, table_new(), stack_new(), stack_new(), lbl,
+      compile_node(gbuf, tbuf, ctx, table_new(), list_new(), list_new(), lbl,
                    node->c);
       CTXPOP();
       LBPOP();
@@ -5796,18 +6115,29 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, table_t *ltab,
     break;
 
   case N_LABEL: {
-    size_t *gid = table_get(list_index(lbl, -1), node->t->text);
+    int_stack_t *pair = table_get(list_index(lbl, -1), node->t->text);
 
-    EMIT("__label%d:;", *gid);
+    EMIT("__label%d:;", pair->data[0]);
   } break;
 
   case N_GOTO: {
     char *label = node->t->text;
-    size_t *gid = table_get(list_index(lbl, -1), label);
-    if (!gid)
+    int_stack_t *pair = table_get(list_index(lbl, -1), label);
+    if (!pair)
       COMPILE_ERROR("undefined label: '%s'", label);
 
-    EMIT("goto __label%d;", *gid);
+    size_t offset = pair->data[1];
+
+    if (offset >= ctx->length)
+      COMPILE_ERROR("cannot goto inwards");
+
+    size_t scopes_k = count_scopes(ctx, offset);
+
+    for (size_t i = 0; i < scopes_k; i++) {
+      EMIT("qi_old_scope(state);\n");
+    }
+
+    EMIT("goto __label%d;", pair->data[0]);
   } break;
 
   case N_REQUIRE:
@@ -5979,7 +6309,7 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, table_t *ltab,
 }
 
 void compile_into(char *source, buffer_t *gbuf, buffer_t *buf, list_t *ctx,
-                  table_t *ltab, int_stack_t *lstk, int_stack_t *sstk,
+                  table_t *ltab, list_t *lstk, list_t *sstk,
                   list_t *lbl) {
   node_t *n = parse(source);
   if (NEEDS_UTF8 && !is_required("utf8")) {
@@ -6006,9 +6336,11 @@ char *escape(char *s) {
 
 char *compile(char *source, list_t *required) {
   list_t *ctx = list_new();
+  list_push(ctx, "top");
+
   table_t *ltab = table_new();
-  int_stack_t *lstk = stack_new();
-  int_stack_t *sstk = stack_new();
+  list_t *lstk = list_new();
+  list_t *sstk = list_new();
   list_t *lbl = list_new();
   LBPUSH();