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

+ 291 - 62
qic.c

@@ -270,6 +270,7 @@ typedef struct {
 
     T_NUMBER,
     T_STRING,
+    T_FSTRING,
     T_NAME,
 
     T_VAR,
@@ -349,6 +350,7 @@ typedef struct {
 
     T_INLINE,
     T_HEADER,
+    T_INCLUDE,
 
     T_ASSIGN,
     T_SEMI
@@ -436,85 +438,96 @@ void format_error(char *filename, char *source, size_t pos, char *fmt, ...) {
 
 #define LEX_ERROR(fmt, ...) { format_error(GETFNAME(-1), source, *pos, fmt, ##__VA_ARGS__); exit(1); }
 
-token_t *next_token(char *source, size_t *pos) {
-  if (!source[*pos])
-    return token(T_EOF, NULL);
+token_t *tokenize_string(char *source, size_t *pos) {
+  char term = source[(*pos)++];
 
-  if (source[*pos] == '"' || source[*pos] == '\'' || source[*pos] == '`') {
-    char term = source[(*pos)++];
+  buffer_t *text = buffer_new();
 
-    buffer_t *text = buffer_new();
+  while (source[*pos] != term) {
+    if (!source[*pos])
+      LEX_ERROR("unterminated string literal");
 
-    while (source[*pos] != term) {
-      if (!source[*pos])
-        LEX_ERROR("unterminated string literal");
+    char c = source[(*pos)++];
+    if (c == '\n' && term != '`')
+      LEX_ERROR("unterminated string literal");
 
-      char c = source[(*pos)++];
-      if (c == '\n' && term != '`')
-        LEX_ERROR("unterminated string literal");
+    if (term != '`' && c == '\\') {
+      char nc = source[(*pos)++];
 
-      if (term != '`' && c == '\\') {
-        char nc = source[(*pos)++];
+      if (!nc)
+        continue;
 
-        if (!nc)
-          continue;
+      switch (nc) {
+        case 'n':
+          buffer_appends(text, "\\n");
+          break;
 
-        switch (nc) {
-          case 'n':
-            buffer_appends(text, "\\n");
-            break;
+        case 't':
+          buffer_appends(text, "\\t");
+          break;
 
-          case 't':
-            buffer_appends(text, "\\t");
-            break;
+        case 'r':
+          buffer_appends(text, "\\r");
+          break;
 
-          case 'r':
-            buffer_appends(text, "\\r");
-            break;
+        case 'b':
+          buffer_appends(text, "\\b");
+          break;
 
-          case 'b':
-            buffer_appends(text, "\\b");
-            break;
+        case 'e':
+          buffer_appends(text, "\\e");
+          break;
 
-          case 'e':
-            buffer_appends(text, "\\e");
-            break;
+        case 's':
+          buffer_appends(text, " ");
+          break;
 
-          case 's':
-            buffer_appends(text, " ");
-            break;
+        case '"':
+          buffer_appends(text, "\\\"");
+          break;
 
-          case '"':
-            buffer_appends(text, "\\\"");
-            break;
+        case '\\':
+          buffer_appends(text, "\\\\");
+          break;
 
-          case '\\':
-            buffer_appends(text, "\\\\");
-            break;
+        case '\n':
+          buffer_appends(text, "\\n");
+          break;
 
-          case '\n':
-            buffer_appends(text, "\\n");
-            break;
+        default:
+          buffer_append(text, nc);
+          break;
+      }
 
-          default:
-            buffer_append(text, nc);
-            break;
-        }
+      continue;
+    }
 
-        continue;
-      }
+    if (c == '"' || c == '\\')
+      buffer_append(text, '\\');
+    else if (c == '\n')
+      buffer_appends(text, "\\n");
 
-      if (c == '"' || c == '\\')
-        buffer_append(text, '\\');
-      else if (c == '\n')
-        buffer_appends(text, "\\n");
+    buffer_append(text, c);
+  }
 
-      buffer_append(text, c);
-    }
+  (*pos)++;
+
+  return token(T_STRING, buffer_read(text));
+}
+
+token_t *next_token(char *source, size_t *pos) {
+  if (!source[*pos])
+    return token(T_EOF, NULL);
 
+  if (source[*pos] == '"' || source[*pos] == '\'' || source[*pos] == '`')
+    return tokenize_string(source, pos);
+  else if (source[*pos] == 'f' && (source[(*pos)+1] == '"' || source[(*pos)+1] == '\'' || source[(*pos)+1] == '`')) {
     (*pos)++;
 
-    return token(T_STRING, buffer_read(text));
+    token_t *t = tokenize_string(source, pos);
+    t->tag = T_FSTRING;
+
+    return t;
   } else if (source[*pos] == '0' && (source[(*pos)+1] == 'x' || source[(*pos)+1] == 'b' || source[(*pos)+1] == 'o')) {
     buffer_t *number = buffer_new();
     buffer_append(number, source[(*pos)++]);
@@ -632,6 +645,8 @@ token_t *next_token(char *source, size_t *pos) {
       return TK(INLINE);
     else if (strcmp(name, "header") == 0)
       return TK(HEADER);
+    else if (strcmp(name, "include") == 0)
+      return TK(INCLUDE);
 
     return token(T_NAME, name);
   } else if (strncmp(&source[*pos], "==", 2) == 0 && ++(*pos) && ++(*pos))
@@ -775,6 +790,7 @@ struct _node_t {
     N_STAR,
     N_MEMBER,
     N_INDEX,
+    N_SLICE,
 
     N_ADD,
     N_SUB,
@@ -842,6 +858,7 @@ struct _node_t {
 
     N_INLINE,
     N_HEADER,
+    N_INCLUDE,
 
     N_IFEXPR,
     N_FUNCEXPR,
@@ -1087,6 +1104,8 @@ list_t *parse_sequence(list_t *tokens, size_t *pos, int term) {
 
 node_t *parse_func(list_t *tokens, size_t *pos, int is_expr);
 
+char *unescape(char *s);
+
 node_t *parse_primary(list_t *tokens, size_t *pos) {
   if (MATCH(FUNC))
     return parse_func(tokens, pos, 1);
@@ -1161,19 +1180,21 @@ node_t *parse_primary(list_t *tokens, size_t *pos) {
       if (!AT(NAME) && !AT(STRING))
         PARSE_ERROR("expected identifier or string");
 
+      int is_str = AT(STRING);
+
       char *key = ((token_t *)tokens->data[(*pos)++])->text;
 
       EXPECT(COLON, ":");
 
       node_t *val = parse_expr(tokens, pos);
 
-      table_set(table, key, val);
+      table_set(table, is_str? unescape(key): key, val);
     } while (MATCH(COMMA));
 
     EXPECT(RCB, "}");
 
     return NODEH(TABLE, table);
-  } else if (MATCH(NUMBER) || MATCH(STRING) || MATCH(NAME))
+  } else if (MATCH(NUMBER) || MATCH(STRING) || MATCH(FSTRING) || MATCH(NAME))
     return NODET(LITERAL, tokens->data[(*pos)-1]);
 
   PARSE_ERROR("expected expression");
@@ -1207,8 +1228,29 @@ node_t *parse_call(list_t *tokens, size_t *pos) {
 
       continue;
     } else if (!CLIFF && MATCH(LSB)) {
+      if (MATCH(COLON)) {
+        a = NODE3(SLICE, a, NULL, parse_expr(tokens, pos));
+
+        EXPECT(RSB, "]");
+
+        continue;
+      }
+
       node_t *b = parse_expr(tokens, pos);
 
+      if (MATCH(COLON)) {
+        node_t *c = NULL;
+
+        if (!AT(RSB))
+          c = parse_expr(tokens, pos);
+
+        EXPECT(RSB, "]");
+
+        a = NODE3(SLICE, a, b, c);
+
+        continue;
+      }
+
       EXPECT(RSB, "]");
 
       a = NODE2(INDEX, a, b);
@@ -2112,6 +2154,16 @@ node_t *parse_program(list_t *tokens, size_t *pos) {
       token_t *text = tokens->data[(*pos)++];
 
       n = NODET(HEADER, text);
+    } else if (MATCH(INCLUDE)) {
+      if (flag)
+        PARSE_ERROR("misplaced include statement")
+
+      if (!AT(STRING))
+        PARSE_ERROR("expected string");
+
+      token_t *text = tokens->data[(*pos)++];
+
+      n = NODET(INCLUDE, text);
     } else { n = parse_stmt(tokens, pos); flag = 1; }
 
     MATCH(SEMI);
@@ -2671,6 +2723,7 @@ const char *STD[][2] = {
     "set_pseudomethod(\"list.slice\", slice)\n"
     "set_pseudomethod(\"string.slice\", slice)\n"
     "set_pseudomethod(\"bytes.slice\", slice)\n"
+    "let __slice = slice;\n"
     "func str_startswith(s, p) {\n"
     "  if type(s) != \"string\"\n"
     "    throw \"expected first argument to be: string, but got: \" + type(s)\n"
@@ -3005,6 +3058,9 @@ const char *STD[][2] = {
     "  return str_rstrip(getline(), \"\\n\\r\")\n"
     "}\n"
     "func open(path, mode=\"r\"): fopen(path, mode)\n"
+    "func assert(cond, msg=\"assertion failed\")\n"
+    "  if !cond\n"
+    "    throw msg\n"
   },
 
   {"utf8",
@@ -3042,6 +3098,7 @@ const char *STD[][2] = {
   },
 
   {"thread",
+    "include `pthread.h`\n"
     "func thread_create(fn, args=[]) {\n"
     "  if type(fn) != \"function\"\n"
     "    throw \"expected first argument to be: function, but got: \" + type(fn)\n"
@@ -3149,7 +3206,11 @@ const char *STD[][2] = {
   },
 
   {"time",
-    "header `#include <time.h>`\n"
+    "include `time.h`\n"
+    "include `errno.h`\n"
+    "header `#ifdef _WIN32`\n"
+    "include `Windows.h`\n"
+    "header `#endif`\n"
     "func time() {\n"
     "  inline `unsigned long long ts = time(NULL)`\n"
     "  inline `return qi_make_number(state, ts)`\n"
@@ -3166,6 +3227,23 @@ const char *STD[][2] = {
     "  inline `strftime(buffer, sizeof(buffer), qi_get(state, \"f\")->value.string, &lt)`\n"
     "  inline `return qi_make_string_copy(state, buffer)`\n"
     "}\n"
+    "func usleep(t) {\n"
+    "  if type(t) != \"number\"\n"
+    "    throw \"expected first argument to be: number, but got: \" + type(t)\n"
+    "    \n"
+    "  inline `#ifdef _WIN32`\n"
+    "  inline `Sleep((unsigned long)(qi_get(state, \"t\")->value.number))`\n"
+    "  inline `#else`\n"
+    "  inline `unsigned long ms = (unsigned long)(qi_get(state, \"t\")->value.number)`\n"
+    "  inline `struct timespec ts`\n"
+    "  inline `ts.tv_sec = ms / 1000`\n"
+    "  inline `ts.tv_nsec = (ms % 1000) * 1000000`\n"
+    "  inline `struct timespec r`\n"
+    "  inline `while (nanosleep(&ts, &r) < 0) if (errno == EINTR) ts = r; else break`\n"
+    "  inline `#endif`\n"
+    "}\n"
+    "func sleep(t)\n"
+    "  usleep(t * 1000)\n"
   },
 
   {"random",
@@ -3385,6 +3463,98 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, table_t *ltab, sta
           }
           break;
 
+        case T_FSTRING: {
+          char *text = node->t->text;
+
+          if (!*text) {
+            EMIT("state->empty_string");
+
+            break;
+          }
+
+          list_t *parts = list_new();
+
+          buffer_t *tbuf = buffer_new();
+
+
+          for (size_t i = 0; i < strlen(text); i++) {
+            char c = text[i];
+            if (c == '$' && text[i+1] == '$') {
+              buffer_append(tbuf, '$');
+
+              i++;
+
+              continue;
+            } else if (c == '$' && text[i+1] == '{') {
+              i += 2;
+
+              buffer_t *tbuf2 = buffer_new();
+
+              while (text[i] && text[i] != '}')
+                buffer_append(tbuf2, text[i++]);
+
+              if (text[i] != '}') {
+                i--;
+
+                buffer_appends(tbuf, "${");
+                buffer_appendb(tbuf, tbuf2);
+
+                continue;
+              }
+
+              if (tbuf->size > 0)
+                list_push(parts, nodeb(tbuf));
+
+              tbuf = buffer_new();
+
+              char *source = buffer_read(tbuf2);
+
+              list_t *pair = list_new();
+              list_push(pair, "<fstring>");
+              list_push(pair, source);
+
+              list_push(FILES, pair);
+
+              size_t pos = 0;
+              node_t *n = parse_expr(tokenize(source), &pos);
+
+              list_push(parts, n);
+            } else buffer_append(tbuf, c);
+          }
+
+          if (tbuf->size > 0)
+            list_push(parts, nodeb(tbuf));
+
+          tbuf = buffer_new();
+
+          NEWGID();
+
+          buffer_fmt(tbuf, "inline static qi_value_t *__fstring%d(qi_state_t *state) {\n", gid);
+          buffer_fmt(tbuf, "qi_value_t *str = state->empty_string;\n");
+
+          for (size_t i = 0; i < parts->length; i++) {
+            node_t *part = parts->data[i];
+
+            buffer_fmt(tbuf, "str = qi_add(state, str, ");
+
+            if (part->tag == N_BUFFER)
+              buffer_fmt(tbuf, "qi_make_string(state, \"%s\")", buffer_read(part->buf));
+            else {
+              buffer_fmt(tbuf, "qi_to_string(state, ");
+              compile_node(gbuf, tbuf, ctx, ltab, lstk, sstk, lbl, part);
+              buffer_fmt(tbuf, ")");
+            }
+
+            buffer_fmt(tbuf, ");\n");
+          }
+
+          buffer_fmt(tbuf, "return str;\n}\n");
+
+          buffer_appendb(gbuf, tbuf);
+
+          EMIT("__fstring%d(state)", gid);
+          } break;
+
         case T_NAME: {
           char *name = node->t->text;
           node_t *n = table_get(CONSTANTS, name);
@@ -3411,7 +3581,7 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, table_t *ltab, sta
 
       buffer_t *tbuf = buffer_new();
 
-      buffer_fmt(tbuf, "qi_value_t *__listgen%d(qi_state_t *state) {\n", gid);
+      buffer_fmt(tbuf, "inline static qi_value_t *__listgen%d(qi_state_t *state) {\n", gid);
       buffer_fmt(tbuf, "qi_list_t *list = qi_list_make();\n");
 
       buffer_t *bbuf = buffer_new();
@@ -3469,6 +3639,56 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, table_t *ltab, sta
       EMIT(")");
       break;
 
+    case N_SLICE: {
+      NEWGID();
+
+      buffer_t *tbuf = buffer_new();
+
+      buffer_fmt(tbuf, "inline static qi_value_t *__slice%d(qi_state_t *state) {\n", gid);
+
+      if (node->b && node->c) {
+        buffer_fmt(tbuf, "qi_list_t *pargs = qi_list_make_n(3);\n");
+        buffer_fmt(tbuf, "qi_list_data(pargs, 0) = ");
+        compile_node(gbuf, tbuf, ctx, ltab, lstk, sstk, lbl, node->a);
+        buffer_fmt(tbuf, ";\n");
+
+        buffer_fmt(tbuf, "qi_list_data(pargs, 1) = ");
+        compile_node(gbuf, tbuf, ctx, ltab, lstk, sstk, lbl, node->b);
+        buffer_fmt(tbuf, ";\n");
+
+        buffer_fmt(tbuf, "qi_list_data(pargs, 2) = ");
+        compile_node(gbuf, tbuf, ctx, ltab, lstk, sstk, lbl, node->c);
+        buffer_fmt(tbuf, ";\n");
+      } else if (node->b) {
+        buffer_fmt(tbuf, "qi_list_t *pargs = qi_list_make_n(2);\n");
+        buffer_fmt(tbuf, "qi_list_data(pargs, 0) = ");
+        compile_node(gbuf, tbuf, ctx, ltab, lstk, sstk, lbl, node->a);
+        buffer_fmt(tbuf, ";\n");
+
+        buffer_fmt(tbuf, "qi_list_data(pargs, 1) = ");
+        compile_node(gbuf, tbuf, ctx, ltab, lstk, sstk, lbl, node->b);
+        buffer_fmt(tbuf, ";\n");
+      } else {
+        buffer_fmt(tbuf, "qi_list_t *pargs = qi_list_make_n(3);\n");
+        buffer_fmt(tbuf, "qi_list_data(pargs, 0) = ");
+        compile_node(gbuf, tbuf, ctx, ltab, lstk, sstk, lbl, node->a);
+        buffer_fmt(tbuf, ";\n");
+
+        buffer_fmt(tbuf, "qi_list_data(pargs, 1) = state->zero;\n");
+
+        buffer_fmt(tbuf, "qi_list_data(pargs, 2) = ");
+        compile_node(gbuf, tbuf, ctx, ltab, lstk, sstk, lbl, node->c);
+        buffer_fmt(tbuf, ";\n");
+      }
+
+      buffer_fmt(tbuf, "return qi_call(state, qi_get(state, \"__slice\"), pargs);\n");
+      buffer_fmt(tbuf, "}\n");
+
+      buffer_appendb(gbuf, tbuf);
+
+      EMIT("__slice%d(state)", gid);
+      } break;
+
     case N_ASSIGN:
       if (node->a->tag == N_LIST) {
         if (node->a->l->length < 2)
@@ -4020,8 +4240,17 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, table_t *ltab, sta
       UNOP("bnot");
       break;
 
-    case N_INLINE: EMIT("%s;", unescape(node->t->text)); break;
+    case N_INLINE: {
+      char *text = unescape(node->t->text);
+
+      if (text[0] == '#') {
+        EMIT("%s", text);
+      } else {
+        EMIT("%s;", text);
+      }
+    } break;
     case N_HEADER: buffer_fmt(HBUF, "%s\n", unescape(node->t->text)); break;
+    case N_INCLUDE: buffer_fmt(HBUF, "#include <%s>\n", unescape(node->t->text)); break;
 
     case N_BUFFER: buffer_appendb(buf, node->buf); break;