txlyre 3 dagen geleden
bovenliggende
commit
eef20e00b1
1 gewijzigde bestanden met toevoegingen van 294 en 95 verwijderingen
  1. 294 95
      qic.c

+ 294 - 95
qic.c

@@ -347,6 +347,7 @@ typedef struct {
     T_TILDE,
 
     T_INLINE,
+    T_HEADER,
 
     T_ASSIGN,
     T_SEMI
@@ -628,6 +629,8 @@ token_t *next_token(char *source, size_t *pos) {
       return TK(OF);
     else if (strcmp(name, "inline") == 0)
       return TK(INLINE);
+    else if (strcmp(name, "header") == 0)
+      return TK(HEADER);
 
     return token(T_NAME, name);
   } else if (strncmp(&source[*pos], "==", 2) == 0 && ++(*pos) && ++(*pos))
@@ -834,6 +837,7 @@ struct _node_t {
     N_CLASS,
 
     N_INLINE,
+    N_HEADER,
 
     N_IFEXPR,
     N_FUNCEXPR,
@@ -1674,7 +1678,7 @@ node_t *parse_func(list_t *tokens, size_t *pos, int is_expr) {
 
   int colon = AT(COLON);
 
-  node_t *body = BLOCK();
+  node_t *body = !colon && !AT(LCB)? parse_stmt(tokens, pos): BLOCK();
 
   if (colon && body->tag == N_EXPRSTMT)
     body = NODE1(RETURN, body->a);
@@ -1901,7 +1905,7 @@ node_t *parse_stmt(list_t *tokens, size_t *pos) {
   } else if (MATCH(THROW)) {
     node_t *a = NULL;
 
-    if (!CLIFF)
+    if (!CLIFF && !AT(COMMA) && !AT(RPAR) && !AT(RCB))
       a = parse_expr(tokens, pos);
 
     return NODE1(THROW, a);
@@ -2007,6 +2011,16 @@ node_t *parse_program(list_t *tokens, size_t *pos) {
       token_t *path = tokens->data[(*pos)++];
 
       n = NODET(REQUIRE, path);
+    } else if (MATCH(HEADER)) {
+      if (flag)
+        PARSE_ERROR("misplaced header statement")
+
+      if (!AT(STRING))
+        PARSE_ERROR("expected string");
+
+      token_t *text = tokens->data[(*pos)++];
+
+      n = NODET(HEADER, text);
     } else { n = parse_stmt(tokens, pos); flag = 1; }
 
     MATCH(SEMI);
@@ -2026,8 +2040,8 @@ 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, 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 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) {\
   if ((lhs)->tag == N_LITERAL && (lhs)->t->tag == T_NAME) {\
     EMIT("qi_set(state, false, \"%s\", ", (lhs)->t->text);\
@@ -2035,15 +2049,15 @@ node_t *parse(char *source) {
     EMIT(")");\
   } else if ((lhs)->tag == N_INDEX) {\
     EMIT("qi_index_set(state, false, ");\
-    compile_node(gbuf, buf, ctx, lstk, lbl, (lhs)->a);\
+    compile_node(gbuf, buf, ctx, lstk, sstk, lbl, (lhs)->a);\
     EMIT(", ");\
-    compile_node(gbuf, buf, ctx, lstk, lbl, (lhs)->b);\
+    compile_node(gbuf, buf, ctx, lstk, sstk, 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);\
+    compile_node(gbuf, buf, ctx, lstk, sstk, lbl, (lhs)->a);\
     EMIT(", qi_make_string(state, \"%s\"), ", (lhs)->t->text);\
     rhs;\
     EMIT(")");\
@@ -2052,7 +2066,7 @@ node_t *parse(char *source) {
 #define COMPASSIGN(lhs, s, rhs) {\
   ASSIGN(node->a, {\
     EMIT("qi_%s(state, ", s);\
-    compile_node(gbuf, buf, ctx, lstk, lbl, (lhs));\
+    compile_node(gbuf, buf, ctx, lstk, sstk, lbl, (lhs));\
     EMIT(", ");\
     rhs;\
     EMIT(")");\
@@ -2060,9 +2074,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, list_t *lbl, node_t *node);
+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_list(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, list_t *lbl, list_t *seq) {
+void compile_list(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, stack_t *sstk, list_t *lbl, list_t *seq) {
   if (!seq || seq->length < 1) {
     EMIT("NULL");
 
@@ -2079,7 +2093,7 @@ void compile_list(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, lis
   for (size_t i = 0; i < seq->length; i++) {
     buffer_fmt(tbuf, "qi_list_data(list, %d) = ", i);
 
-    compile_node(gbuf, tbuf, ctx, lstk, lbl, seq->data[i]);
+    compile_node(gbuf, tbuf, ctx, lstk, sstk, lbl, seq->data[i]);
 
     buffer_fmt(tbuf, ";\n");
   }
@@ -2092,7 +2106,7 @@ void compile_list(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, lis
   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) {
+void compile_table(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, stack_t *sstk, list_t *lbl, table_t *table) {
   if (!table || table->used < 1) {
     EMIT("NULL");
 
@@ -2109,7 +2123,7 @@ void compile_table(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, li
   table_iterate(table, {
     buffer_fmt(tbuf, "qi_table_set(table, \"%s\", ", entry.key);
 
-    compile_node(gbuf, tbuf, ctx, lstk, lbl, entry.value);
+    compile_node(gbuf, tbuf, ctx, lstk, sstk, lbl, entry.value);
 
     buffer_fmt(tbuf, ");\n");
   });
@@ -2139,6 +2153,20 @@ int in_context(list_t *ctx, char *s) {
   return 0;
 }
 
+int in_switch(list_t *ctx) {
+  if (!ctx->length)
+    return 0;
+
+  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;
+}
+
 size_t count_ctxs(list_t *ctx, char *s) {
   if (!ctx->length)
     return 0;
@@ -2163,6 +2191,10 @@ size_t count_ctxs(list_t *ctx, char *s) {
 #define LPOP() stack_pop(lstk)
 #define LID (lstk->data[lstk->length-1])
 
+#define SPUSH(i) stack_push(sstk, (i))
+#define SPOP() stack_pop(sstk)
+#define SID (sstk->data[sstk->length-1])
+
 #define LBPUSH() list_push(lbl, table_new())
 #define LBPOP() list_pop(lbl)
 
@@ -2175,7 +2207,7 @@ char *tempvar() {
   return s;
 }
 
-void compile_func(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, list_t *lbl, node_t *node, char *name) {
+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) {
   NEWGID();
 
   buffer_t *tbuf = buffer_new();
@@ -2198,7 +2230,7 @@ void compile_func(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, lis
         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, lbl, pair->data[1]);
+        compile_node(gbuf, tbuf, ctx, 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);
@@ -2207,7 +2239,7 @@ void compile_func(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, lis
     });
   }
 
-  compile_node(gbuf, tbuf, ctx, lstk, lbl, node->a);
+  compile_node(gbuf, tbuf, ctx, lstk, sstk, lbl, node->a);
 
   CTXPOP();
   CTXPOP();
@@ -2221,7 +2253,7 @@ void compile_func(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, lis
   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, lbl, node->h2);
+  compile_table(gbuf, tbuf, ctx, lstk, sstk, lbl, node->h2);
   buffer_fmt(tbuf, ")");
 
   if (node->tag == N_FUNCEXPR) {
@@ -2237,7 +2269,7 @@ void compile_func(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, lis
 
 table_t *CONSTANTS;
 
-void compile_block(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, list_t *lbl, list_t *block, int toplevel) {
+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) {
   for (size_t i = 0; i < block->length; i++) {
     node_t *node = block->data[i];
 
@@ -2254,7 +2286,7 @@ void compile_block(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, li
         table_set(CONSTANTS, name, entry.value);
       });
     } else if (node->tag == N_FUNCDEF) {
-      compile_func(gbuf, buf, ctx, lstk, lbl, node, NULL);
+      compile_func(gbuf, buf, ctx, lstk, sstk, lbl, node, NULL);
 
       EMIT("\n");
     } else if (node->tag == N_CLASS) {
@@ -2288,9 +2320,9 @@ void compile_block(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, li
 
         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, lbl, triple->data[2], buffer_read(methodname));
+          compile_func(gbuf, tbuf, ctx, lstk, sstk, lbl, triple->data[2], buffer_read(methodname));
         else
-          compile_node(gbuf, tbuf, ctx, lstk, lbl, triple->data[2]);
+          compile_node(gbuf, tbuf, ctx, lstk, sstk, lbl, triple->data[2]);
         buffer_fmt(tbuf, ");\n");
       }
 
@@ -2326,7 +2358,7 @@ void compile_block(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, li
     if (n->tag == N_FUNCDEF)
       continue;
 
-    compile_node(gbuf, buf, ctx, lstk, lbl, n);
+    compile_node(gbuf, buf, ctx, lstk, sstk, lbl, n);
 
     EMIT("\n");
   }
@@ -2613,10 +2645,10 @@ const char *STD[][2] = {
     "    return bytes(r)\n"
     "  return r\n"
     "}\n"
-    "set_pseudomethod(\"list.reduce\", reduce)\n"
-    "set_pseudomethod(\"tuple.reduce\", reduce)\n"
-    "set_pseudomethod(\"string.reduce\", reduce)\n"
-    "set_pseudomethod(\"bytes.reduce\", reduce)\n"
+    "set_pseudomethod(\"list.reduce\", func (xs, f): reduce(f, xs))\n"
+    "set_pseudomethod(\"tuple.reduce\", func (xs, f): reduce(f, xs))\n"
+    "set_pseudomethod(\"string.reduce\", func (xs, f): reduce(f, xs))\n"
+    "set_pseudomethod(\"bytes.reduce\", func (xs, f): reduce(f, xs))\n"
     "func sum(xs)\n"
     "  return reduce(func (x, y): x + y, xs)\n"
     "set_pseudomethod(\"list.sum\", sum)\n"
@@ -2643,10 +2675,10 @@ const char *STD[][2] = {
     "    return bytes(r)\n"
     "  return r\n"
     "}\n"
-    "set_pseudomethod(\"list.map\", map)\n"
-    "set_pseudomethod(\"tuple.map\", map)\n"
-    "set_pseudomethod(\"string.map\", map)\n"
-    "set_pseudomethod(\"bytes.map\", map)\n"
+    "set_pseudomethod(\"list.map\", func (xs, f): map(f, xs))\n"
+    "set_pseudomethod(\"tuple.map\", func (xs, f): map(f, xs))\n"
+    "set_pseudomethod(\"string.map\", func (xs, f): map(f, xs))\n"
+    "set_pseudomethod(\"bytes.map\", func (xs, f): map(f, xs))\n"
     "func filter(f, xs) {\n"
     "  if type(f) != \"function\"\n"
     "    throw \"expected first argument to be: function, but got: \" + type(f)\n"
@@ -2666,10 +2698,10 @@ const char *STD[][2] = {
     "    return bytes(r)\n"
     "  return r\n"
     "}\n"
-    "set_pseudomethod(\"list.filter\", filter)\n"
-    "set_pseudomethod(\"tuple.filter\", filter)\n"
-    "set_pseudomethod(\"string.filter\", filter)\n"
-    "set_pseudomethod(\"bytes.filter\", filter)\n"
+    "set_pseudomethod(\"list.filter\", func (xs, f): filter(f, xs))\n"
+    "set_pseudomethod(\"tuple.filter\", func (xs, f): filter(f, xs))\n"
+    "set_pseudomethod(\"string.filter\", func (xs, f): filter(f, xs))\n"
+    "set_pseudomethod(\"bytes.filter\", func (xs, f): filter(f, xs))\n"
     "func str_index(s, w) {\n"
     "  if s == \"\" || w == \"\"\n"
     "    return -1\n"
@@ -2715,6 +2747,16 @@ const char *STD[][2] = {
     "  return str_lstrip(str_rstrip(s, cs), cs)\n"
     "}\n"
     "set_pseudomethod(\"string.strip\", str_strip)\n"
+    "func table_get(t, k, d=nil) {\n"
+    "  if type(t) != \"table\"\n"
+    "    throw \"expected first argument to be: table, but got: \" + type(t)\n"
+    "  if type(k) != \"string\"\n"
+    "    throw \"expected second argument to be: string, but got: \" + type(k)\n"
+    "  if k !in t\n"
+    "    return d\n"
+    "  return t[k]\n"
+    "}\n"
+    "set_pseudomethod(\"table.get\", table_get)\n"
     "func zip() {\n"
     "  if !arguments\n"
     "    return []\n"
@@ -2734,6 +2776,18 @@ const char *STD[][2] = {
     "    return zip(table_keys(l), table_values(l))\n"
     "  else\n"
     "    return zip(range(len(l)), l)\n"
+    "func str_toupper(s) {\n"
+    "  if type(s) != \"string\"\n"
+    "    throw \"expected first argument to be: string, but got: \" + type(c)\n"
+    "  return map(func (c): c >= 'a' && c <= 'z'? chr(ord(c) - 32): c, s)\n"
+    "}\n"
+    "set_pseudomethod(\"string.toupper\", str_toupper)\n"
+    "func str_tolower(s) {\n"
+    "  if type(s) != \"string\"\n"
+    "    throw \"expected first argument to be: string, but got: \" + type(c)\n"
+    "  return map(func (c): c >= 'A' && c <= 'Z'? chr(ord(c) + 32): c, s)\n"
+    "}\n"
+    "set_pseudomethod(\"string.tolower\", str_tolower)\n"
     "func Object(t, p=nil): return p !is nil? set_meta_table(p, get_meta_table(p) + t): set_meta_table({}, t)\n"
     "func is_object(o): return has_meta_table(o)\n"
     "func __class_wrapper(n, p, t, mt): return Object({\n"
@@ -2761,6 +2815,66 @@ const char *STD[][2] = {
     "        return obj\n"
     "    }\n"
     "})\n"
+    "func format(s) {\n"
+    "  if type(s) != \"string\"\n"
+    "    throw \"expected first argument to be: string, but got: \" + type(s)\n"
+    "  var r = \"\"\n"
+    "  var n = 1\n"
+    "  for var i = 0; i < len(s); i++\n"
+    "    switch s[i] {\n"
+    "      case '_'\n"
+    "        if i+1 < len(s) && s[i+1] == '_' {\n"
+    "          r += '_'\n"
+    "          i++\n"
+    "          continue\n"
+    "        }\n"
+    "        r += repr(arguments[n++])\n"
+    "        break\n"
+    "      default\n"
+    "        r += s[i]\n"
+    "    }\n"
+    "  return r\n"
+    "}\n"
+    "set_pseudomethod(\"string.format\", format)\n"
+    "func formatl(s, l) {\n"
+    "  if type(s) != \"string\"\n"
+    "    throw \"expected first argument to be: string, but got: \" + type(s)\n"
+    "  if type(l) != \"list\"\n"
+    "    throw \"expected second argument to be: list, but got: \" + type(l)\n"
+    "  return func_call(str_format, [s] + l)\n"
+    "}\n"
+    "set_pseudomethod(\"string.formatl\", formatl)\n"
+    "func formatd(s, t) {\n"
+    "  if type(s) != \"string\"\n"
+    "    throw \"expected first argument to be: string, but got: \" + type(s)\n"
+    "  var r = \"\"\n"
+    "  var n = 1\n"
+    "  for var i = 0; i < len(s); i++\n"
+    "    switch s[i] {\n"
+    "      case '{'\n"
+    "        if i+1 < len(s) && s[i+1] == '{' {\n"
+    "          r += '{'\n"
+    "          i++\n"
+    "          continue\n"
+    "        }\n"
+    "        var k = ''\n"
+    "        i++\n"
+    "        for i < len(s) && s[i] != '}'\n"
+    "          k += s[i++]\n"
+    "        if i >= len(s) || s[i] != '}'\n"
+    "          throw \"unmatched { in format specifier\"\n"
+    "        if !k\n"
+    "          throw \"empty format key\"\n"
+    "        r += repr(t[k])\n"
+    "        break\n"
+    "      default\n"
+    "        r += s[i]\n"
+    "    }\n"
+    "  return r\n"
+    "}\n"
+    "set_pseudomethod(\"string.formatd\", formatd)\n"
+    "func getch() return chr(fgetc(STDIN))\n"
+    "func putch(c) fputc(STDOUT, c)\n"
     "func getline()\n"
     "  return fgets(STDIN, 256)\n"
     "func input() {\n"
@@ -2805,6 +2919,85 @@ const char *STD[][2] = {
     "}\n"
   },
 
+  {"thread",
+    "func thread_create(fn, args=[]) {\n"
+    "  if type(fn) != \"function\"\n"
+    "    throw \"expected first argument to be: function, but got: \" + type(fn)\n"
+    "  if type(args) != \"list\"\n"
+    "    throw \"expected second argument to be: list, but got: \" + type(args)\n"
+    "  inline `qi_value_t *args = qi_get(state, \"args\")`\n"
+    "  inline `qi_list_t *list`\n"
+    "  inline `LOCKED(args, {list = qi_list_copy(args->value.list);})`\n"
+    "  inline `void *td = qi_thread_create(state, qi_get(state, \"fn\"), list)`\n"
+    "  inline `qi_decl(state, \"td\", qi_make_data(state, 'T', td))`\n"
+    "  return Object({\n"
+    "    __str: func (this): \"<Thread>\",\n"
+    "    _td: td,\n"
+    "    joined: false,\n"
+    "    join: func (this) {\n"
+    "      if this.joined\n"
+    "        return\n"
+    "      var td = this._td\n"
+    "      inline `qi_value_t *ret = qi_thread_join(state, qi_get_data(state, 'T', qi_get(state, \"td\")))`\n"
+    "      this.joined = true\n"
+    "      inline `return ret`\n"
+    "    }\n"
+    "  })\n"
+    "}\n"
+    "func Thread(fn, args=[]): Object({\n"
+    "  __str: func (this) use (fn, args): format(\"<Thread _ _>\", fn, args),\n"
+    "  _thread: nil,\n"
+    "  start: func (this) use (fn, args) {\n"
+    "    if this._thread !is nil\n"
+    "      return\n"
+    "    this._thread = thread_create(fn, args)\n"
+    "    return this\n"
+    "  },\n"
+    "  join: func (this) {\n"
+    "    if this._thread !is nil\n"
+    "      return this._thread.join()\n"
+    "  },\n"
+    "  is_joined: func (this): this._thread.joined\n"
+    "})\n"
+    "func Lock(acquired=false) {\n"
+    "  inline `void *lock = qi_lock_create()`\n"
+    "  inline `if (!lock) qi_throw_format(state, \"lock init failed\")`\n"
+    "  inline `qi_decl(state, \"lock\", qi_make_data(state, 'L', lock))`\n"
+    "  var l = Object({\n"
+    "    __fin: func (this) {\n"
+    "      var lock = this._lock\n"
+    "      this._lock = nil\n"
+    "      inline `qi_lock_destroy(qi_get_data(state, 'L', qi_get(state, \"lock\")))`\n"
+    "    },\n"
+    "    __str: func (this): \"<Lock>\",\n"
+    "    _lock: lock,\n"
+    "    acquired: func (this) {\n"
+    "      var lock = this._lock\n"
+    "      inline `return qi_make_boolean(state, qi_lock_acquired(qi_get_data(state, 'L', qi_get(state, \"lock\"))))`\n"
+    "    },\n"
+    "    acquire: func (this) {\n"
+    "      var lock = this._lock\n"
+    "      inline `qi_lock_acquire(qi_get_data(state, 'L', qi_get(state, \"lock\")))`\n"
+    "    },\n"
+    "    tryacquire: func (this) {\n"
+    "      var lock = this._lock\n"
+    "      inline `return qi_make_boolean(state, qi_lock_tryacquire(qi_get_data(state, 'L', qi_get(state, \"lock\"))))`\n"
+    "    },\n"
+    "    release: func (this) {\n"
+    "      var lock = this._lock\n"
+    "      inline `qi_lock_release(qi_get_data(state, 'L', qi_get(state, \"lock\")))`\n"
+    "    }\n"
+    "  })\n"
+    "  if acquired\n"
+    "    l.acquire()\n"
+    "  return l\n"
+    "}\n"
+    "func thread_exit(ret=nil)\n"
+    "  inline `qi_thread_exit(state, qi_get(state, \"ret\"))`\n"
+    "func is_main_thread()\n"
+    "  inline `return qi_make_boolean(state, qi_thread_main())`\n"
+  },
+
   {"str",
     "let STR_LETTERS = \"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\"\n"
     "let STR_ASCII_LC = \"abcdefghijklmnopqrstuvwxyz\"\n"
@@ -2941,9 +3134,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, list_t *lbl);
+void compile_into(char *source, buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, stack_t *sstk, list_t *lbl);
 
-int require_once(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, list_t *lbl, char *path) {
+int require_once(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, stack_t *sstk, list_t *lbl, char *path) {
   char *source = NULL;
 
   for (size_t i = 0; STD[i][0]; i++) {
@@ -2983,7 +3176,7 @@ int require_once(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, list
 
   list_push(FILES, pair);
 
-  compile_into(source, gbuf, buf, ctx, lstk, lbl);
+  compile_into(source, gbuf, buf, ctx, lstk, sstk, lbl);
 
   list_pop(FILES);
   list_push(REQUIRED, path);
@@ -2991,15 +3184,17 @@ int require_once(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, list
   return 0;
 }
 
-void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, list_t *lbl, node_t *node) {
+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) {
   switch (node->tag) {
     case N_TOPLEVEL: case N_PROGRAM:
-      compile_block(gbuf, buf, ctx, lstk, lbl, node->l, node->tag == N_TOPLEVEL);
+      compile_block(gbuf, buf, ctx, lstk, sstk, lbl, node->l, node->tag == N_TOPLEVEL);
       break;
 
     case N_EXPRSTMT:
       EMIT("(void)(");
-      compile_node(gbuf, buf, ctx, lstk, lbl, node->a);
+      compile_node(gbuf, buf, ctx, lstk, sstk, lbl, node->a);
       EMIT(");");
       break;
 
@@ -3007,7 +3202,7 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, lis
       LBPUSH();
       CTXPUSH("scope");
       EMIT("qi_new_scope(state);\n");
-      compile_block(gbuf, buf, ctx, lstk, lbl, node->l, 0);
+      compile_block(gbuf, buf, ctx, lstk, sstk, lbl, node->l, 0);
       EMIT("qi_old_scope(state);");
       CTXPOP();
       LBPOP();
@@ -3052,7 +3247,7 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, lis
           node_t *n = table_get(CONSTANTS, name);
 
           if (n)
-            compile_node(gbuf, buf, ctx, lstk, lbl, n);
+            compile_node(gbuf, buf, ctx, lstk, sstk, lbl, n);
           else
             EMIT("qi_get(state, \"%s\")", name);
           } break;
@@ -3064,13 +3259,13 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, lis
 
     case N_LIST:
       EMIT("qi_make_list(state, ");
-      compile_list(gbuf, buf, ctx, lstk, lbl, node->l);
+      compile_list(gbuf, buf, ctx, lstk, sstk, lbl, node->l);
       EMIT(")");
       break;
 
     case N_TUPLE:
       EMIT("qi_make_tuple(state, ");
-      compile_list(gbuf, buf, ctx, lstk, lbl, node->l);
+      compile_list(gbuf, buf, ctx, lstk, sstk, lbl, node->l);
       EMIT(")");
       break;
 
@@ -3078,43 +3273,43 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, lis
 
     case N_TABLE:
       EMIT("qi_make_table(state, ");
-      compile_table(gbuf, buf, ctx, lstk, lbl, node->h);
+      compile_table(gbuf, buf, ctx, lstk, sstk, lbl, node->h);
       EMIT(")");
       break;
 
     case N_CALL:
       EMIT("qi_call(state, ");
-      compile_node(gbuf, buf, ctx, lstk, lbl, node->a);
+      compile_node(gbuf, buf, ctx, lstk, sstk, lbl, node->a);
       EMIT(", ");
-      compile_list(gbuf, buf, ctx, lstk, lbl, node->l);
+      compile_list(gbuf, buf, ctx, lstk, sstk, lbl, node->l);
       EMIT(")");
       break;
 
     case N_MEMBER:
       EMIT("qi_index(state, ");
-      compile_node(gbuf, buf, ctx, lstk, lbl, node->a);
+      compile_node(gbuf, buf, ctx, 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, lbl, node->a);
+      compile_node(gbuf, buf, ctx, lstk, sstk, lbl, node->a);
       EMIT(", ");
-      compile_node(gbuf, buf, ctx, lstk, lbl, node->b);
+      compile_node(gbuf, buf, ctx, lstk, sstk, lbl, node->b);
       EMIT(")");
       break;
 
-    case N_ASSIGN: ASSIGN(node->a, compile_node(gbuf, buf, ctx, lstk, lbl, node->b)); break;
+    case N_ASSIGN: ASSIGN(node->a, compile_node(gbuf, buf, ctx, lstk, sstk, 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_ASSIGN_BOR: COMPASSIGN(node->a, "bor", compile_node(gbuf, buf, ctx, lstk, lbl, node->b)); break;
-    case N_ASSIGN_BAND: COMPASSIGN(node->a, "band", compile_node(gbuf, buf, ctx, lstk, lbl, node->b)); break;
+    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;
 
     case N_INC: 
       COMPASSIGN(node->a, "add", EMIT("state->one"));
@@ -3129,7 +3324,7 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, lis
         EMIT("qi_%s(state, \"%s\", ", node->tag == N_LET? "decl_const": "decl", entry.key);
         
         if (entry.value)
-          compile_node(gbuf, buf, ctx, lstk, lbl, entry.value);
+          compile_node(gbuf, buf, ctx, lstk, sstk, lbl, entry.value);
         else EMIT("state->nil");
 
         EMIT(");\n");
@@ -3140,7 +3335,7 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, lis
       char *varname = tempvar();
 
       EMIT("qi_value_t *%s = ", varname);
-      compile_node(gbuf, buf, ctx, lstk, lbl, node->a);
+      compile_node(gbuf, buf, ctx, lstk, sstk, lbl, node->a);
       EMIT(";\n");
 
       for (size_t i = 0; i < node->l->length; i++)
@@ -3155,12 +3350,12 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, lis
 
     case N_IF:
       EMIT("if (_qi_truthy(state, ");
-      compile_node(gbuf, buf, ctx, lstk, lbl, node->a);
+      compile_node(gbuf, buf, ctx, lstk, sstk, lbl, node->a);
       EMIT(")) {\n");
-      compile_node(gbuf, buf, ctx, lstk, lbl, node->b);
+      compile_node(gbuf, buf, ctx, lstk, sstk, lbl, node->b);
       if (node->c) {
         EMIT("} else {\n");
-        compile_node(gbuf, buf, ctx, lstk, lbl, node->c);
+        compile_node(gbuf, buf, ctx, lstk, sstk, lbl, node->c);
       }
       EMIT("}");
       break;
@@ -3170,7 +3365,7 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, lis
       char *varname = tempvar();
 
       EMIT("qi_value_t *%s = ", varname);
-      compile_node(gbuf, buf, ctx, lstk, lbl, node->a);
+      compile_node(gbuf, buf, ctx, lstk, sstk, lbl, node->a);
       EMIT(";\n");
 
       for (size_t i = 0; i < node->l->length; i++) {
@@ -3178,7 +3373,7 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, lis
         char *label = tempvar();
 
         EMIT("if (_qi_equals(state, %s, ", varname);
-        compile_node(gbuf, buf, ctx, lstk, lbl, pair->data[0]);
+        compile_node(gbuf, buf, ctx, lstk, sstk, lbl, pair->data[0]);
         EMIT(")) goto %s;\n", label);
 
         pair->data[0] = label;
@@ -3186,7 +3381,7 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, lis
 
       EMIT("goto __default%d;\n", gid); 
 
-      LPUSH(gid);
+      SPUSH(gid);
       CTXPUSH("switch");
 
       for (size_t i = 0; i < node->l->length; i++) {
@@ -3195,16 +3390,16 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, lis
         node_t *block = pair->data[1];
 
         EMIT("%s:;\n", label);
-        compile_node(gbuf, buf, ctx, lstk, lbl, block);
+        compile_node(gbuf, buf, ctx, lstk, sstk, lbl, block);
       }
 
       EMIT("__default%d:;\n", gid);
 
       if (node->b)
-        compile_node(gbuf, buf, ctx, lstk, lbl, node->b);
+        compile_node(gbuf, buf, ctx, lstk, sstk, lbl, node->b);
 
       CTXPOP();
-      LPOP();
+      SPOP();
 
       EMIT("__break%d:;\n", gid);
       } break;
@@ -3216,23 +3411,23 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, lis
         EMIT("for (;;) {\n");
       } else if (node->a && !node->b) {
         EMIT("while (_qi_truthy(state, ");
-        compile_node(gbuf, buf, ctx, lstk, lbl, node->a);
+        compile_node(gbuf, buf, ctx, lstk, sstk, lbl, node->a);
         EMIT(")) {\n");
       } else {
-        compile_node(gbuf, buf, ctx, lstk, lbl, node->a);
+        compile_node(gbuf, buf, ctx, lstk, sstk, lbl, node->a);
         EMIT("while (_qi_truthy(state, ");
-        compile_node(gbuf, buf, ctx, lstk, lbl, node->b);
+        compile_node(gbuf, buf, ctx, lstk, sstk, lbl, node->b);
         EMIT(")) {\n");
       }
 
       LPUSH(gid);
       CTXPUSH("for");
-      compile_node(gbuf, buf, ctx, lstk, lbl, node->d);
+      compile_node(gbuf, buf, ctx, lstk, sstk, lbl, node->d);
 
       EMIT("__continue%d:;\n", gid);
 
       if (node->c) {
-        compile_node(gbuf, buf, ctx, lstk, lbl, node->c);
+        compile_node(gbuf, buf, ctx, lstk, sstk, lbl, node->c);
 
         EMIT(";\n");
       }
@@ -3250,7 +3445,7 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, lis
       char *varname = tempvar();
 
       EMIT("qi_value_t *%s = qi_iter(state, ", varname);
-      compile_node(gbuf, buf, ctx, lstk, lbl, node->a);
+      compile_node(gbuf, buf, ctx, lstk, sstk, lbl, node->a);
       EMIT(");\n");
 
       if (node->tag == N_FOROFVAR) {
@@ -3270,7 +3465,7 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, lis
 
       LPUSH(gid);
       CTXPUSH("for");
-      compile_node(gbuf, buf, ctx, lstk, lbl, node->b);
+      compile_node(gbuf, buf, ctx, lstk, sstk, lbl, node->b);
       CTXPOP();
       LPOP();
 
@@ -3284,7 +3479,7 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, lis
       if (!INCTX("for") && !INCTX("switch"))
         COMPILE_ERROR("break outside of a loop or a switch");
 
-      EMIT("goto __break%d;", LID);
+      EMIT("goto __break%d;", in_switch(ctx)? SID: LID);
       break;
 
     case N_CONTINUE:
@@ -3303,7 +3498,7 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, lis
 
       LBPUSH();
       CTXPUSH("gap");
-      compile_node(gbuf, tbuf, ctx, lstk, lbl, node->a);
+      compile_node(gbuf, tbuf, ctx, lstk, sstk, lbl, node->a);
       CTXPOP();
       LBPOP();
 
@@ -3328,7 +3523,7 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, lis
       EMIT("return ");
 
       if (node->a)
-        compile_node(gbuf, buf, ctx, lstk, lbl, node->a);
+        compile_node(gbuf, buf, ctx, lstk, sstk, lbl, node->a);
       else EMIT("state->nil");
 
       EMIT(";");
@@ -3340,12 +3535,12 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, lis
 
     case N_TRY:
       EMIT("qi_try(state, {\n");
-      compile_node(gbuf, buf, ctx, lstk, lbl, node->a);
+      compile_node(gbuf, buf, ctx, 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, lbl, node->b);
+      compile_node(gbuf, buf, ctx, lstk, sstk, lbl, node->b);
       CTXPOP();
       EMIT("}, NULL);\n");
       break;
@@ -3353,7 +3548,7 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, lis
     case N_THROW:
       EMIT("qi_throw(state, ");
       if (node->a)
-        compile_node(gbuf, buf, ctx, lstk, lbl, node->a);
+        compile_node(gbuf, buf, ctx, lstk, sstk, lbl, node->a);
       else {
         EMIT("state->nil");
       }
@@ -3378,22 +3573,22 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, lis
     case N_REQUIRE: {
       char *path = unescape(node->t->text);
 
-      if (require_once(gbuf, buf, ctx, lstk, lbl, path) < 0)
+      if (require_once(gbuf, buf, ctx, 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, lbl, node->a);
+      compile_node(gbuf, buf, ctx, lstk, sstk, lbl, node->a);
       EMIT(")? ");
-      compile_node(gbuf, buf, ctx, lstk, lbl, node->b);
+      compile_node(gbuf, buf, ctx, lstk, sstk, lbl, node->b);
       EMIT(": ");
-      compile_node(gbuf, buf, ctx, lstk, lbl, node->c);
+      compile_node(gbuf, buf, ctx, lstk, sstk, lbl, node->c);
       EMIT(")");
       break;
 
     case N_FUNCEXPR:
-      compile_func(gbuf, buf, ctx, lstk, lbl, node, NULL);
+      compile_func(gbuf, buf, ctx, lstk, sstk, lbl, node, NULL);
       break;
 
     case N_EQUALS:
@@ -3505,35 +3700,38 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, lis
       break;
 
     case N_INLINE: EMIT("%s;", unescape(node->t->text)); break;
+    case N_HEADER: buffer_fmt(HBUF, "%s\n", 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) {
+void compile_into(char *source, buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, stack_t *sstk, list_t *lbl) {
   node_t *n = parse(source);
 
-  compile_node(gbuf, buf, ctx, lstk, lbl, n);
+  compile_node(gbuf, buf, ctx, lstk, sstk, lbl, n);
 }
 
 char *compile(char *source) {
   list_t *ctx = list_new();
   stack_t *lstk = stack_new();
+  stack_t *sstk = stack_new();
   list_t *lbl = list_new();
   LBPUSH();
   
   buffer_t *gbuf = buffer_new();
 
-  buffer_appends(gbuf, "#include <qirt.h>\n");
-
   buffer_t *buf = buffer_new();
 
-  require_once(gbuf, buf, ctx, lstk, lbl, "std");
-  compile_into(source, gbuf, buf, ctx, lstk, lbl);
+  require_once(gbuf, buf, ctx, lstk, sstk, lbl, "std");
+  compile_into(source, gbuf, buf, ctx, lstk, sstk, lbl);
 
   buffer_t *rbuf = buffer_new();
 
+  buffer_appends(rbuf, "#include <qirt.h>\n");
+  buffer_appendb(rbuf, HBUF);
+
   buffer_appendb(rbuf, gbuf);
 
   buffer_appends(rbuf, "int main(int argc, char **argv) {\n");
@@ -3579,6 +3777,7 @@ int main(int argc, char **argv) {
   FILES = list_new();
   REQUIRED = list_new();
   CONSTANTS = table_new();
+  HBUF = buffer_new();
 
   genmathlib();