|
@@ -434,7 +434,7 @@ token_t *next_token(char *source, size_t *pos) {
|
|
if (!source[*pos])
|
|
if (!source[*pos])
|
|
return token(T_EOF, NULL);
|
|
return token(T_EOF, NULL);
|
|
|
|
|
|
- if (source[*pos] == '"' || source[*pos] == '\'') {
|
|
|
|
|
|
+ if (source[*pos] == '"' || source[*pos] == '\'' || source[*pos] == '`') {
|
|
char term = source[(*pos)++];
|
|
char term = source[(*pos)++];
|
|
|
|
|
|
buffer_t *text = buffer_new();
|
|
buffer_t *text = buffer_new();
|
|
@@ -444,8 +444,10 @@ token_t *next_token(char *source, size_t *pos) {
|
|
LEX_ERROR("unterminated string literal");
|
|
LEX_ERROR("unterminated string literal");
|
|
|
|
|
|
char c = source[(*pos)++];
|
|
char c = source[(*pos)++];
|
|
|
|
+ if (c == '\n' && term != '`')
|
|
|
|
+ LEX_ERROR("unterminated string literal");
|
|
|
|
|
|
- if (c == '\\') {
|
|
|
|
|
|
+ if (term != '`' && c == '\\') {
|
|
char nc = source[(*pos)++];
|
|
char nc = source[(*pos)++];
|
|
|
|
|
|
if (!nc)
|
|
if (!nc)
|
|
@@ -484,6 +486,10 @@ token_t *next_token(char *source, size_t *pos) {
|
|
buffer_appends(text, "\\\\");
|
|
buffer_appends(text, "\\\\");
|
|
break;
|
|
break;
|
|
|
|
|
|
|
|
+ case '\n':
|
|
|
|
+ buffer_appends(text, "\\n");
|
|
|
|
+ break;
|
|
|
|
+
|
|
default:
|
|
default:
|
|
buffer_append(text, nc);
|
|
buffer_append(text, nc);
|
|
break;
|
|
break;
|
|
@@ -494,6 +500,8 @@ token_t *next_token(char *source, size_t *pos) {
|
|
|
|
|
|
if (c == '"' || c == '\\')
|
|
if (c == '"' || c == '\\')
|
|
buffer_append(text, '\\');
|
|
buffer_append(text, '\\');
|
|
|
|
+ else if (c == '\n')
|
|
|
|
+ buffer_appends(text, "\\n");
|
|
|
|
|
|
buffer_append(text, c);
|
|
buffer_append(text, c);
|
|
}
|
|
}
|
|
@@ -501,13 +509,22 @@ token_t *next_token(char *source, size_t *pos) {
|
|
(*pos)++;
|
|
(*pos)++;
|
|
|
|
|
|
return token(T_STRING, buffer_read(text));
|
|
return token(T_STRING, buffer_read(text));
|
|
- } else if (isdigit(source[*pos])) {
|
|
|
|
|
|
+ } else if ((source[*pos] == '.' && isdigit(source[(*pos)+1])) || isdigit(source[*pos])) {
|
|
buffer_t *number = buffer_new();
|
|
buffer_t *number = buffer_new();
|
|
int dot = 0;
|
|
int dot = 0;
|
|
int sub = 0;
|
|
int sub = 0;
|
|
|
|
+ int skip = 0;
|
|
|
|
+
|
|
|
|
+ if (source[*pos] == '.') {
|
|
|
|
+ buffer_append(number, '0');
|
|
|
|
+
|
|
|
|
+ skip = 1;
|
|
|
|
+ }
|
|
|
|
|
|
do {
|
|
do {
|
|
- buffer_append(number, source[(*pos)++]);
|
|
|
|
|
|
+ if (skip) skip = 0;
|
|
|
|
+ else
|
|
|
|
+ buffer_append(number, source[(*pos)++]);
|
|
|
|
|
|
if (!dot && source[*pos] == '.') {
|
|
if (!dot && source[*pos] == '.') {
|
|
buffer_append(number, source[(*pos)++]);
|
|
buffer_append(number, source[(*pos)++]);
|
|
@@ -516,9 +533,12 @@ token_t *next_token(char *source, size_t *pos) {
|
|
LEX_ERROR("illegal number literal (missing part after floating point)");
|
|
LEX_ERROR("illegal number literal (missing part after floating point)");
|
|
|
|
|
|
dot = 1;
|
|
dot = 1;
|
|
- } else if (!sub && source[*pos] == '.') {
|
|
|
|
|
|
+ } else if (!sub && source[*pos] == '_') {
|
|
(*pos)++;
|
|
(*pos)++;
|
|
|
|
|
|
|
|
+ if (!isdigit(source[*pos]))
|
|
|
|
+ LEX_ERROR("illegal number literal (missing part after underscore)");
|
|
|
|
+
|
|
sub = 1;
|
|
sub = 1;
|
|
} else if (sub) sub = 0;
|
|
} else if (sub) sub = 0;
|
|
} while (isdigit(source[*pos]));
|
|
} while (isdigit(source[*pos]));
|
|
@@ -1541,8 +1561,13 @@ node_t *parse_func(list_t *tokens, size_t *pos, int is_expr) {
|
|
EXPECT(RPAR, ")");
|
|
EXPECT(RPAR, ")");
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ int colon = AT(COLON);
|
|
|
|
+
|
|
node_t *body = BLOCK();
|
|
node_t *body = BLOCK();
|
|
|
|
|
|
|
|
+ if (colon && body->tag == N_EXPRSTMT)
|
|
|
|
+ body = NODE1(RETURN, body->a);
|
|
|
|
+
|
|
if (is_expr)
|
|
if (is_expr)
|
|
return NODEF(FUNCEXPR, NULL, params, captured, body);
|
|
return NODEF(FUNCEXPR, NULL, params, captured, body);
|
|
|
|
|
|
@@ -1940,7 +1965,11 @@ void compile_block(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, li
|
|
}
|
|
}
|
|
|
|
|
|
for (size_t i = 0; i < block->length; i++) {
|
|
for (size_t i = 0; i < block->length; i++) {
|
|
- compile_node(gbuf, buf, ctx, lstk, lbl, block->data[i]);
|
|
|
|
|
|
+ node_t *n = block->data[i];
|
|
|
|
+ if (n->tag == N_FUNCDEF || n->tag == N_VAR || n->tag == N_LET)
|
|
|
|
+ continue;
|
|
|
|
+
|
|
|
|
+ compile_node(gbuf, buf, ctx, lstk, lbl, n);
|
|
|
|
|
|
EMIT("\n");
|
|
EMIT("\n");
|
|
}
|
|
}
|
|
@@ -1949,10 +1978,10 @@ void compile_block(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, li
|
|
const char *STD[][2] = {
|
|
const char *STD[][2] = {
|
|
{"std",
|
|
{"std",
|
|
"func exit(c) {\n"
|
|
"func exit(c) {\n"
|
|
- " if type(c) != \"number\":\n"
|
|
|
|
|
|
+ " if type(c) != \"number\"\n"
|
|
" throw \"expected first argument to be: number, but got: \" + type(c)\n"
|
|
" throw \"expected first argument to be: number, but got: \" + type(c)\n"
|
|
- " inline 'int code = qi_get(state, \"c\")->value.number'\n"
|
|
|
|
- " inline 'exit(code)'\n"
|
|
|
|
|
|
+ " inline `int code = qi_get(state, \"c\")->value.number`\n"
|
|
|
|
+ " inline `exit(code)`\n"
|
|
"}\n"
|
|
"}\n"
|
|
"func head(l): return l[0]\n"
|
|
"func head(l): return l[0]\n"
|
|
"func die(msg, c=1) {\n"
|
|
"func die(msg, c=1) {\n"
|
|
@@ -1961,7 +1990,7 @@ const char *STD[][2] = {
|
|
"}\n"
|
|
"}\n"
|
|
"let SEEK_SET = 0, SEEK_CUR = 1, SEEK_END = 2\n"
|
|
"let SEEK_SET = 0, SEEK_CUR = 1, SEEK_END = 2\n"
|
|
"func frewind(file)\n"
|
|
"func frewind(file)\n"
|
|
- " fseek(file, 0, SEEK_SET)\n"
|
|
|
|
|
|
+ " return fseek(file, 0, SEEK_SET)\n"
|
|
"func file_read(filename) {\n"
|
|
"func file_read(filename) {\n"
|
|
" var file = fopen(filename, \"r\")\n"
|
|
" var file = fopen(filename, \"r\")\n"
|
|
" defer fclose(file)\n"
|
|
" defer fclose(file)\n"
|
|
@@ -1970,6 +1999,20 @@ const char *STD[][2] = {
|
|
" frewind(file)\n"
|
|
" frewind(file)\n"
|
|
" return str(fread(file, size))\n"
|
|
" return str(fread(file, size))\n"
|
|
"}\n"
|
|
"}\n"
|
|
|
|
+ "func is_defined(name) {\n"
|
|
|
|
+ " if type(name) != \"string\"\n"
|
|
|
|
+ " throw \"expected first argument to be: string, but got: \" + type(name)\n"
|
|
|
|
+ " inline `bool b = qi_find(state, qi_get(state, \"name\")->value.string) != NULL`\n"
|
|
|
|
+ " inline `return qi_make_boolean(state, b)`\n"
|
|
|
|
+ "}\n"
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ {"str",
|
|
|
|
+ "let STR_LETTERS = \"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\"\n"
|
|
|
|
+ "let STR_ASCII_LC = \"abcdefghijklmnopqrstuvwxyz\"\n"
|
|
|
|
+ "let STR_ASCII_UC = \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\"\n"
|
|
|
|
+ "let STR_DIGITS = \"0123456789\"\n"
|
|
|
|
+ "func is_char(c): return type(c) == \"string\" && len(c) == 1\n"
|
|
},
|
|
},
|
|
|
|
|
|
{NULL, NULL}
|
|
{NULL, NULL}
|
|
@@ -2005,6 +2048,54 @@ char *unescape(char *s) {
|
|
|
|
|
|
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, list_t *lbl);
|
|
|
|
|
|
|
|
+int require_once(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, list_t *lbl, char *path) {
|
|
|
|
+ char *source = NULL;
|
|
|
|
+
|
|
|
|
+ for (size_t i = 0; STD[i][0]; i++) {
|
|
|
|
+ if (strcmp(path, STD[i][0]) == 0) {
|
|
|
|
+ source = (char *)STD[i][1];
|
|
|
|
+
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (is_required(path))
|
|
|
|
+ return 1;
|
|
|
|
+
|
|
|
|
+ if (!source) {
|
|
|
|
+ FILE *fd = fopen(path, "rb");
|
|
|
|
+ if (!fd)
|
|
|
|
+ return -1;
|
|
|
|
+
|
|
|
|
+ buffer_t *fbuf = buffer_new();
|
|
|
|
+
|
|
|
|
+ for (;;) {
|
|
|
|
+ char line[512];
|
|
|
|
+
|
|
|
|
+ if (!fgets(line, sizeof(line), fd))
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ buffer_appends(fbuf, line);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ source = buffer_read(fbuf);
|
|
|
|
+ path = realpath(path, NULL);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ list_t *pair = list_new();
|
|
|
|
+ list_push(pair, path);
|
|
|
|
+ list_push(pair, source);
|
|
|
|
+
|
|
|
|
+ list_push(FILES, pair);
|
|
|
|
+
|
|
|
|
+ compile_into(source, gbuf, buf, ctx, lstk, lbl);
|
|
|
|
+
|
|
|
|
+ list_pop(FILES);
|
|
|
|
+ list_push(REQUIRED, path);
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
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, list_t *lbl, node_t *node) {
|
|
switch (node->tag) {
|
|
switch (node->tag) {
|
|
case N_PROGRAM:
|
|
case N_PROGRAM:
|
|
@@ -2117,18 +2208,10 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, lis
|
|
EMIT("if (_qi_truthy(state, ");
|
|
EMIT("if (_qi_truthy(state, ");
|
|
compile_node(gbuf, buf, ctx, lstk, lbl, node->a);
|
|
compile_node(gbuf, buf, ctx, lstk, lbl, node->a);
|
|
EMIT(")) {\n");
|
|
EMIT(")) {\n");
|
|
- CTXPUSH("scope");
|
|
|
|
- EMIT("qi_new_scope(state);\n");
|
|
|
|
compile_node(gbuf, buf, ctx, lstk, lbl, node->b);
|
|
compile_node(gbuf, buf, ctx, lstk, lbl, node->b);
|
|
- EMIT("qi_old_scope(state);\n");
|
|
|
|
- CTXPOP();
|
|
|
|
if (node->c) {
|
|
if (node->c) {
|
|
EMIT("} else {\n");
|
|
EMIT("} else {\n");
|
|
- CTXPUSH("scope");
|
|
|
|
- EMIT("qi_new_scope(state);\n");
|
|
|
|
compile_node(gbuf, buf, ctx, lstk, lbl, node->c);
|
|
compile_node(gbuf, buf, ctx, lstk, lbl, node->c);
|
|
- EMIT("qi_old_scope(state);\n");
|
|
|
|
- CTXPOP();
|
|
|
|
}
|
|
}
|
|
EMIT("}");
|
|
EMIT("}");
|
|
break;
|
|
break;
|
|
@@ -2136,9 +2219,6 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, lis
|
|
case N_FOR: {
|
|
case N_FOR: {
|
|
NEWGID();
|
|
NEWGID();
|
|
|
|
|
|
- CTXPUSH("scope");
|
|
|
|
- EMIT("qi_new_scope(state);\n");
|
|
|
|
-
|
|
|
|
if (!node->a) {
|
|
if (!node->a) {
|
|
EMIT("for (;;) {\n");
|
|
EMIT("for (;;) {\n");
|
|
} else if (node->a && !node->b) {
|
|
} else if (node->a && !node->b) {
|
|
@@ -2165,22 +2245,19 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, lis
|
|
EMIT("}\n");
|
|
EMIT("}\n");
|
|
|
|
|
|
EMIT("__break%d:;\n", gid);
|
|
EMIT("__break%d:;\n", gid);
|
|
-
|
|
|
|
- EMIT("qi_old_scope(state);\n");
|
|
|
|
- CTXPOP();
|
|
|
|
} break;
|
|
} break;
|
|
|
|
|
|
case N_FOROF: {
|
|
case N_FOROF: {
|
|
NEWGID();
|
|
NEWGID();
|
|
char *varname = tempvar();
|
|
char *varname = tempvar();
|
|
|
|
|
|
- CTXPUSH("scope");
|
|
|
|
- EMIT("qi_new_scope(state);\n");
|
|
|
|
-
|
|
|
|
EMIT("qi_value_t *%s = qi_iter(state, ", varname);
|
|
EMIT("qi_value_t *%s = qi_iter(state, ", varname);
|
|
compile_node(gbuf, buf, ctx, lstk, lbl, node->a);
|
|
compile_node(gbuf, buf, ctx, lstk, lbl, node->a);
|
|
EMIT(");\n");
|
|
EMIT(");\n");
|
|
|
|
|
|
|
|
+ CTXPUSH("scope");
|
|
|
|
+ EMIT("qi_new_scope(state);\n");
|
|
|
|
+
|
|
EMIT("qi_decl(state, \"%s\", state->nil);\n", node->t->text);
|
|
EMIT("qi_decl(state, \"%s\", state->nil);\n", node->t->text);
|
|
|
|
|
|
EMIT("for (qi_size_t length = _qi_length(state, %s), i = 0; i < length; i++) {\n", varname);
|
|
EMIT("for (qi_size_t length = _qi_length(state, %s), i = 0; i < length; i++) {\n", varname);
|
|
@@ -2197,7 +2274,7 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, lis
|
|
|
|
|
|
EMIT("__break%d:;\n", gid);
|
|
EMIT("__break%d:;\n", gid);
|
|
|
|
|
|
- EMIT("qi_old_scope(state);\n");
|
|
|
|
|
|
+ EMIT("qi_old_scope(state);");
|
|
CTXPOP();
|
|
CTXPOP();
|
|
} break;
|
|
} break;
|
|
|
|
|
|
@@ -2259,8 +2336,6 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, lis
|
|
case N_PASS: break;
|
|
case N_PASS: break;
|
|
|
|
|
|
case N_TRY:
|
|
case N_TRY:
|
|
- CTXPUSH("scope");
|
|
|
|
- EMIT("qi_new_scope(state);\n");
|
|
|
|
EMIT("qi_try(state, {\n");
|
|
EMIT("qi_try(state, {\n");
|
|
compile_node(gbuf, buf, ctx, lstk, lbl, node->a);
|
|
compile_node(gbuf, buf, ctx, lstk, lbl, node->a);
|
|
EMIT("}, {\n");
|
|
EMIT("}, {\n");
|
|
@@ -2270,8 +2345,6 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, lis
|
|
compile_node(gbuf, buf, ctx, lstk, lbl, node->b);
|
|
compile_node(gbuf, buf, ctx, lstk, lbl, node->b);
|
|
CTXPOP();
|
|
CTXPOP();
|
|
EMIT("}, NULL);\n");
|
|
EMIT("}, NULL);\n");
|
|
- EMIT("qi_new_scope(state);");
|
|
|
|
- CTXPOP();
|
|
|
|
break;
|
|
break;
|
|
|
|
|
|
case N_THROW:
|
|
case N_THROW:
|
|
@@ -2322,50 +2395,10 @@ void compile_node(buffer_t *gbuf, buffer_t *buf, list_t *ctx, stack_t *lstk, lis
|
|
} break;
|
|
} break;
|
|
|
|
|
|
case N_REQUIRE: {
|
|
case N_REQUIRE: {
|
|
- char *source = NULL;
|
|
|
|
-
|
|
|
|
char *path = unescape(node->t->text);
|
|
char *path = unescape(node->t->text);
|
|
- for (size_t i = 0; STD[i][0]; i++) {
|
|
|
|
- if (strcmp(path, STD[i][0]) == 0) {
|
|
|
|
- source = (char *)STD[i][1];
|
|
|
|
-
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (is_required(path))
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- if (!source) {
|
|
|
|
- FILE *fd = fopen(path, "rb");
|
|
|
|
- if (!fd)
|
|
|
|
- COMPILE_ERROR("'%s' is not a valid file path or a builtin library name", path);
|
|
|
|
-
|
|
|
|
- buffer_t *fbuf = buffer_new();
|
|
|
|
-
|
|
|
|
- for (;;) {
|
|
|
|
- char line[512];
|
|
|
|
-
|
|
|
|
- if (!fgets(line, sizeof(line), fd))
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- buffer_appends(fbuf, line);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- source = buffer_read(fbuf);
|
|
|
|
- path = realpath(path, NULL);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- list_t *pair = list_new();
|
|
|
|
- list_push(pair, path);
|
|
|
|
- list_push(pair, source);
|
|
|
|
-
|
|
|
|
- list_push(FILES, pair);
|
|
|
|
-
|
|
|
|
- compile_into(source, gbuf, buf, ctx, lstk, lbl);
|
|
|
|
|
|
|
|
- list_pop(FILES);
|
|
|
|
- list_push(REQUIRED, path);
|
|
|
|
|
|
+ if (require_once(gbuf, buf, ctx, lstk, lbl, path) < 0)
|
|
|
|
+ COMPILE_ERROR("'%s' is not a valid file path or a builtin library name", path);
|
|
} break;
|
|
} break;
|
|
|
|
|
|
case N_IFEXPR:
|
|
case N_IFEXPR:
|
|
@@ -2507,6 +2540,7 @@ char *compile(char *source) {
|
|
|
|
|
|
buffer_t *buf = buffer_new();
|
|
buffer_t *buf = buffer_new();
|
|
|
|
|
|
|
|
+ require_once(gbuf, buf, ctx, lstk, lbl, "std");
|
|
compile_into(source, gbuf, buf, ctx, lstk, lbl);
|
|
compile_into(source, gbuf, buf, ctx, lstk, lbl);
|
|
|
|
|
|
buffer_t *rbuf = buffer_new();
|
|
buffer_t *rbuf = buffer_new();
|