summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLeo Tenenbaum <pommicket@gmail.com>2019-11-04 17:40:11 -0500
committerLeo Tenenbaum <pommicket@gmail.com>2019-11-04 17:40:11 -0500
commit16db3a715f4af41c9c66952d459ffed723e186fc (patch)
tree80383e9b3b05582da8b6eec51bf4673604f539ce
parent376fe21bba246b0bdbd90b9a4101e15c5411b505 (diff)
each loops!
-rw-r--r--cgen.c152
-rw-r--r--decls_cgen.c13
-rw-r--r--eval.c122
-rw-r--r--identifiers.c14
-rw-r--r--main.c8
-rw-r--r--parse.c143
-rw-r--r--scope.c45
-rw-r--r--test.toc53
-rw-r--r--tokenizer.c10
-rw-r--r--typedefs_cgen.c13
-rw-r--r--types.c284
-rw-r--r--types.h48
12 files changed, 757 insertions, 148 deletions
diff --git a/cgen.c b/cgen.c
index cea6bfa..d8afe05 100644
--- a/cgen.c
+++ b/cgen.c
@@ -1,6 +1,6 @@
static bool cgen_stmt(CGenerator *g, Statement *s);
-#define CGEN_BLOCK_FLAG_NOENTER 0x01 /* should cgen_block actually enter and exit the block? */
-#define CGEN_BLOCK_FLAG_NOBRACES 0x02 /* should it use braces? */
+#define CGEN_BLOCK_NOENTER 0x01 /* should cgen_block actually enter and exit the block? */
+#define CGEN_BLOCK_NOBRACES 0x02 /* should it use braces? */
static bool cgen_block(CGenerator *g, Block *b, const char *ret_name, uint16_t flags);
static bool cgen_expr_pre(CGenerator *g, Expression *e);
static bool cgen_expr(CGenerator *g, Expression *e);
@@ -9,6 +9,7 @@ static bool cgen_type_pre(CGenerator *g, Type *t, Location where);
static bool cgen_type_post(CGenerator *g, Type *t, Location where);
static bool cgen_decl(CGenerator *g, Declaration *d);
static bool cgen_ret(CGenerator *g, Expression *ret);
+static bool cgen_val(CGenerator *g, Value *v, Type *t, Location where);
static bool cgen_val_ptr(CGenerator *g, void *v, Type *t, Location where);
static void cgen_create(CGenerator *g, FILE *out, Identifiers *ids, Evaluator *ev) {
@@ -19,6 +20,7 @@ static void cgen_create(CGenerator *g, FILE *out, Identifiers *ids, Evaluator *e
g->will_indent = true;
g->indent_lvl = 0;
g->anon_fns = NULL;
+ g->idents = ids;
}
static bool cgen_block_enter(CGenerator *g, Block *b) {
@@ -121,6 +123,7 @@ static void cgen_ident(CGenerator *g, Identifier i) {
}
}
+
static char *cgen_ident_to_str(Identifier i) {
return ident_to_str(i);
}
@@ -131,6 +134,12 @@ static inline void cgen_ident_id_to_str(char *buffer, IdentID id) {
snprintf(buffer, 32, "a%lu_", (unsigned long)id);
}
+static inline Identifier cgen_ident_id_to_ident(CGenerator *g, IdentID id) {
+ char s[32];
+ cgen_ident_id_to_str(s, id);
+ return ident_get(g->idents, s);
+}
+
static bool cgen_type_pre(CGenerator *g, Type *t, Location where) {
assert(t->flags & TYPE_FLAG_RESOLVED);
switch (t->kind) {
@@ -500,6 +509,9 @@ static bool cgen_set_tuple(CGenerator *g, Expression *exprs, Identifier *idents,
case EXPR_WHILE:
prefix_id = to->while_.c.id;
goto prefixed;
+ case EXPR_EACH:
+ prefix_id = to->each.c.id;
+ goto prefixed;
prefixed:
for (unsigned long i = 0; i < (unsigned long)arr_len(to->type.tuple); i++) {
cgen_write(g, "(");
@@ -596,6 +608,7 @@ static bool cgen_expr_pre(CGenerator *g, Expression *e) {
switch (e->kind) {
case EXPR_IF:
case EXPR_WHILE:
+ case EXPR_EACH:
case EXPR_BLOCK: {
id = g->ident_counter++;
@@ -658,6 +671,125 @@ static bool cgen_expr_pre(CGenerator *g, Expression *e) {
if (!cgen_block(g, &w->body, ret_name, 0))
return false;
} break;
+ case EXPR_EACH: {
+ EachExpr *ea = &e->each;
+ int is_range = ea->flags & EACH_IS_RANGE;
+ IdentID slice_id;
+ IdentID from_id;
+ if (!is_range && ea->of->type.kind == TYPE_SLICE) {
+ /* pre-generate the slice */
+ slice_id = g->ident_counter++;
+ cgen_write(g, "slice_ ");
+ cgen_ident_id(g, slice_id);
+ cgen_write(g, " = ");
+ if (!cgen_expr(g, ea->of)) return false;
+ cgen_write(g, "; ");
+ } else if (is_range) {
+ /* pre-generate from */
+ from_id = g->ident_counter++;
+ if (!cgen_type_pre(g, &ea->type, e->where)) return false;
+ cgen_write(g, " ");
+ cgen_ident_id(g, from_id);
+ if (!cgen_type_post(g, &ea->type, e->where)) return false;
+ cgen_write(g, " = ");
+ if (!cgen_expr(g, ea->range.from))
+ return false;
+ cgen_write(g, "; ");
+ }
+ ea->c.id = id;
+ if (ea->index == NULL)
+ ea->c.index_id = g->ident_counter++;
+ if (!each_enter(e, 0)) return false;
+ cgen_write(g, "for (i64 ");
+ if (ea->index)
+ cgen_ident(g, ea->index);
+ else
+ cgen_ident_id(g, ea->c.index_id);
+ cgen_write(g, " = 0; ");
+ if (!(is_range && !ea->range.to)) {
+ if (ea->index)
+ cgen_ident(g, ea->index);
+ else
+ cgen_ident_id(g, ea->c.index_id);
+ if (is_range) {
+ bool positive_step
+ = ea->range.stepval == NULL || val_is_nonnegative(ea->range.stepval, &ea->type);
+ cgen_write(g, " %c= ", positive_step ? '<' : '>');
+ if (!cgen_expr(g, ea->range.to))
+ return false;
+ cgen_write(g, "-");
+ cgen_ident_id(g, from_id);
+ } else {
+ cgen_write(g, " < ");
+ switch (ea->of->type.kind) {
+ case TYPE_ARR:
+ cgen_write(g, "%lu", (unsigned long)ea->of->type.arr.n);
+ break;
+ case TYPE_SLICE:
+ cgen_ident_id(g, slice_id);
+ cgen_write(g, ".len");
+ break;
+ default: assert(0); break;
+ }
+ }
+ }
+ cgen_write(g, "; ");
+ if (ea->index)
+ cgen_ident(g, ea->index);
+ else
+ cgen_ident_id(g, ea->c.index_id);
+ cgen_write(g, " += ");
+ if (is_range && ea->range.stepval) {
+ if (!cgen_val(g, ea->range.stepval, &ea->type, e->where))
+ return false;
+ } else {
+ cgen_write(g, "1");
+ }
+ cgen_write(g, ") {");
+ cgen_nl(g);
+ if (ea->value) {
+ if (is_range) {
+ if (!cgen_type_pre(g, &ea->type, e->where)) return false;
+ cgen_write(g, " ");
+ cgen_ident(g, ea->value);
+ if (!cgen_type_post(g, &ea->type, e->where)) return false;
+ cgen_write(g, " = ");
+ cgen_ident_id(g, from_id);
+ cgen_write(g, " + ");
+ if (ea->index)
+ cgen_ident(g, ea->index);
+ else
+ cgen_ident_id(g, ea->c.index_id);
+ cgen_write(g, ";");
+ cgen_nl(g);
+ } else {
+ /* necessary for iterating over, e.g., an array of arrays */
+ Expression set;
+ set.flags = 0;
+ set.kind = EXPR_IDENT;
+ set.ident = ea->value;
+
+ Expression to;
+ to.flags = 0;
+ to.kind = EXPR_BINARY_OP;
+ to.binary.op = BINARY_AT_INDEX;
+ to.binary.lhs = ea->of;
+ to.binary.rhs = err_malloc(sizeof(Expression));
+ to.binary.rhs->flags = 0;
+ to.binary.rhs->kind = EXPR_IDENT;
+ if (ea->index)
+ to.binary.rhs->ident = ea->index;
+ else
+ to.binary.rhs->ident = cgen_ident_id_to_ident(g, ea->c.id);
+ if (!cgen_set(g, &set, NULL, &to, NULL))
+ return false;
+ }
+ }
+ if (!cgen_block(g, &ea->body, ret_name, CGEN_BLOCK_NOBRACES))
+ return false;
+ cgen_write(g, "}");
+ each_exit(e);
+ } break;
case EXPR_BLOCK:
e->block_ret_id = id;
if (!cgen_block(g, &e->block, ret_name, 0))
@@ -969,6 +1101,10 @@ static bool cgen_expr(CGenerator *g, Expression *e) {
if (e->type.kind != TYPE_VOID)
cgen_ident_id(g, e->block_ret_id);
break;
+ case EXPR_EACH:
+ if (e->type.kind != TYPE_VOID)
+ cgen_ident_id(g, e->each.c.id);
+ break;
case EXPR_CALL:
if (cgen_uses_ptr(&e->type)) {
cgen_ident_id(g, e->call.c.id);
@@ -1047,11 +1183,11 @@ static bool cgen_expr(CGenerator *g, Expression *e) {
functions always call with NULL as ret_name, even if they use out params, for now
at least.
*/
-static bool cgen_block(CGenerator *g, Block *b, const char *ret_name, uint16_t flags) {
+static bool cgen_block(CGenerator *g, Block *b, const char *ret_name, U16 flags) {
Block *prev = g->block;
- if (!(flags & CGEN_BLOCK_FLAG_NOBRACES))
+ if (!(flags & CGEN_BLOCK_NOBRACES))
cgen_write(g, "{");
- if (!(flags & CGEN_BLOCK_FLAG_NOENTER))
+ if (!(flags & CGEN_BLOCK_NOENTER))
if (!cgen_block_enter(g, b))
return false;
cgen_nl(g);
@@ -1068,9 +1204,9 @@ static bool cgen_block(CGenerator *g, Block *b, const char *ret_name, uint16_t f
}
cgen_nl(g);
}
- if (!(flags & CGEN_BLOCK_FLAG_NOENTER))
+ if (!(flags & CGEN_BLOCK_NOENTER))
cgen_block_exit(g, prev);
- if (!(flags & CGEN_BLOCK_FLAG_NOBRACES))
+ if (!(flags & CGEN_BLOCK_NOBRACES))
cgen_write(g, "}");
return true;
}
@@ -1118,7 +1254,7 @@ static bool cgen_fn(CGenerator *g, FnExpr *f, Location where) {
return false;
}
if (!cgen_block_enter(g, &f->body)) return false;
- if (!cgen_block(g, &f->body, NULL, CGEN_BLOCK_FLAG_NOENTER | CGEN_BLOCK_FLAG_NOBRACES))
+ if (!cgen_block(g, &f->body, NULL, CGEN_BLOCK_NOENTER | CGEN_BLOCK_NOBRACES))
return false;
if (f->ret_decls) {
if (cgen_uses_ptr(&f->ret_type)) {
diff --git a/decls_cgen.c b/decls_cgen.c
index 6a3a635..86ca5f8 100644
--- a/decls_cgen.c
+++ b/decls_cgen.c
@@ -47,6 +47,19 @@ static bool cgen_decls_expr(CGenerator *g, Expression *e) {
if (!cgen_decls_block(g, &e->while_.body))
return false;
break;
+ case EXPR_EACH: {
+ EachExpr *ea = &e->each;
+ if (ea->flags & EACH_IS_RANGE) {
+ if (!cgen_decls_expr(g, ea->range.from))
+ return false;
+ if (ea->range.to && !cgen_decls_expr(g, ea->range.to))
+ return false;
+ /* step is a value, not an expression */
+ } else {
+ if (!cgen_decls_expr(g, ea->of))
+ return false;
+ }
+ } break;
case EXPR_TUPLE:
arr_foreach(e->tuple, Expression, x)
if (!cgen_decls_expr(g, x))
diff --git a/eval.c b/eval.c
index e4b6676..0430cbd 100644
--- a/eval.c
+++ b/eval.c
@@ -179,6 +179,8 @@ static bool val_truthiness(Value *v, Type *t) {
return false;
}
+
+
static I64 val_to_i64(Value *v, BuiltinType v_type) {
switch (v_type) {
case BUILTIN_I8: return (I64)v->i8;
@@ -195,6 +197,8 @@ static I64 val_to_i64(Value *v, BuiltinType v_type) {
return 0;
}
+
+
static U64 val_to_u64(Value *v, BuiltinType v_type) {
if (v_type == BUILTIN_U64) return v->u64;
return (U64)val_to_i64(v, v_type);
@@ -229,6 +233,106 @@ static void u64_to_val(Value *v, BuiltinType v_type, U64 x) {
i64_to_val(v, v_type, (I64)x);
}
+
+static bool val_is_nonnegative(Value *v, Type *t) {
+ assert(t->kind == TYPE_BUILTIN);
+ if (!type_builtin_is_signed(t->builtin))
+ return true;
+ return val_to_i64(v, t->builtin) >= 0;
+}
+
+static void fprint_val_ptr(FILE *f, void *p, Type *t) {
+ switch (t->kind) {
+ case TYPE_VOID:
+ fprintf(f, "(void)");
+ break;
+ case TYPE_UNKNOWN:
+ fprintf(f, "???");
+ break;
+ case TYPE_BUILTIN:
+ switch (t->builtin) {
+ case BUILTIN_I8: fprintf(f, "%"PRId8, *(I8 *)p); break;
+ case BUILTIN_U8: fprintf(f, "%"PRIu8, *(U8 *)p); break;
+ case BUILTIN_I16: fprintf(f, "%"PRId16, *(I16 *)p); break;
+ case BUILTIN_U16: fprintf(f, "%"PRIu16, *(U16 *)p); break;
+ case BUILTIN_I32: fprintf(f, "%"PRId32, *(I32 *)p); break;
+ case BUILTIN_U32: fprintf(f, "%"PRIu32, *(U32 *)p); break;
+ case BUILTIN_I64: fprintf(f, "%"PRId64, *(I64 *)p); break;
+ case BUILTIN_U64: fprintf(f, "%"PRIu64, *(U64 *)p); break;
+ case BUILTIN_F32: fprintf(f, F32_FMT, *(F32 *)p); break;
+ case BUILTIN_F64: fprintf(f, F64_FMT, *(F64 *)p); break;
+ case BUILTIN_CHAR: fprintf(f, "'%c'", *(char *)p); break;
+ case BUILTIN_BOOL: fprintf(f, "%s", *(bool *)p ? "true" : "false"); break;
+ }
+ break;
+ case TYPE_FN:
+ fprintf(f, "<function @ %p>", (void *)*(FnExpr **)p);
+ break;
+ case TYPE_TUPLE:
+ fprintf(f, "<tuple>");
+ break;
+ case TYPE_ARR: {
+ fprintf(f, "["); /* TODO: change? when array initializers are added */
+ size_t n = t->arr.n;
+ if (n > 5) n = 5;
+ for (size_t i = 0; i < n; i++) {
+ if (i) fprintf(f, ", ");
+ fprint_val_ptr(f, *(char **)p + i * compiler_sizeof(t->arr.of), t->arr.of);
+ }
+ if (t->arr.n > n) {
+ fprintf(f, ", ...");
+ }
+ fprintf(f, "]");
+ } break;
+ case TYPE_PTR:
+ fprintf(f, "<pointer: %p>", *(void **)p);
+ break;
+ case TYPE_SLICE: {
+ fprintf(f, "["); /* TODO: change? when slice initializers are added */
+ Slice slice = *(Slice *)p;
+ I64 n = slice.n;
+ if (n > 5) n = 5;
+ for (I64 i = 0; i < n; i++) {
+ if (i) fprintf(f, ", ");
+ fprint_val_ptr(f, (char *)slice.data + i * (I64)compiler_sizeof(t->arr.of), t->arr.of);
+ }
+ if (slice.n > n) {
+ fprintf(f, ", ...");
+ }
+ fprintf(f, "]");
+ } break;
+ case TYPE_TYPE:
+ fprint_type(f, *(Type **)p);
+ break;
+ case TYPE_USER:
+ fprint_val_ptr(f, p, type_user_underlying(t));
+ break;
+ case TYPE_STRUCT:
+ fprintf(f, "["); /* TODO: change? when struct initializers are added */
+ arr_foreach(t->struc.fields, Field, fi) {
+ if (fi != t->struc.fields)
+ fprintf(f, ", ");
+ fprint_ident(f, fi->name);
+ fprintf(f, ": ");
+ fprint_val_ptr(f, *(char **)p + fi->offset, fi->type);
+ }
+ fprintf(f, "]");
+ break;
+ }
+}
+
+static void fprint_val(FILE *f, Value *v, Type *t) {
+ if (t->kind == TYPE_TUPLE) {
+ fprintf(f, "(");
+ for (size_t i = 0; i < arr_len(t->tuple); i++) {
+ fprint_val(f, &v->tuple[i], &t->tuple[i]);
+ }
+ fprintf(f, ")");
+ } else {
+ fprint_val_ptr(f, v, t);
+ }
+}
+
/*
IMPORTANT: Only pass an evaluator if you want it to use its allocator.
Otherwise, pass NULL.
@@ -680,7 +784,7 @@ static bool eval_address_of(Evaluator *ev, Expression *e, void **ptr) {
switch (e->kind) {
case EXPR_IDENT: {
IdentDecl *id = ident_decl(e->ident);
- if (!(id->flags & IDECL_FLAG_HAS_VAL)) {
+ if (!(id->flags & IDECL_HAS_VAL)) {
err_print(e->where, "Cannot take address of run time variable at compile time.");
return false;
}
@@ -735,7 +839,7 @@ static bool eval_set(Evaluator *ev, Expression *set, Value *to) {
switch (set->kind) {
case EXPR_IDENT: {
IdentDecl *id = ident_decl(set->ident);
- if (!(id->flags & IDECL_FLAG_HAS_VAL)) {
+ if (!(id->flags & IDECL_HAS_VAL)) {
err_print(set->where, "Cannot set value of run time variable at compile time.");
return false;
}
@@ -1043,6 +1147,14 @@ static bool eval_expr(Evaluator *ev, Expression *e, Value *v) {
if (!eval_block(ev, &w->body, &e->type, v)) return false;
}
} break;
+ case EXPR_EACH: {
+ EachExpr *ea = &e->each;
+ /* TODO: ASDF */
+ if (ea->flags & EACH_IS_RANGE) {
+
+ } else {
+ }
+ } break;
case EXPR_BLOCK:
if (!eval_block(ev, &e->block, &e->type, v)) return false;
break;
@@ -1069,7 +1181,7 @@ static bool eval_expr(Evaluator *ev, Expression *e, Value *v) {
Declaration *d = idecl->decl;
if (!types_decl(ev->typer, d)) return false;
assert(d->type.flags & TYPE_FLAG_RESOLVED);
- if (idecl->flags & IDECL_FLAG_HAS_VAL) {
+ if (idecl->flags & IDECL_HAS_VAL) {
*v = idecl->val;
} else if (d->flags & DECL_FLAG_CONST) {
if (!(d->flags & DECL_FLAG_FOUND_VAL)) {
@@ -1166,7 +1278,7 @@ static bool eval_expr(Evaluator *ev, Expression *e, Value *v) {
Type *type = p->type.kind == TYPE_TUPLE ? &p->type.tuple[idx++] : &p->type;
IdentDecl *id = ident_decl(*i);
val_copy(NULL, &id->val, &args[arg], type);
- id->flags |= IDECL_FLAG_HAS_VAL;
+ id->flags |= IDECL_HAS_VAL;
arg++;
}
}
@@ -1270,7 +1382,7 @@ static bool eval_decl(Evaluator *ev, Declaration *d) {
}
}
index++;
- id->flags |= IDECL_FLAG_HAS_VAL;
+ id->flags |= IDECL_HAS_VAL;
}
}
return true;
diff --git a/identifiers.c b/identifiers.c
index 030ec76..3cce0d1 100644
--- a/identifiers.c
+++ b/identifiers.c
@@ -149,6 +149,7 @@ static void ident_add_decl(Identifier i, struct Declaration *d, struct Block *b)
id_decl->decl = d;
id_decl->scope = b;
id_decl->flags = 0;
+ id_decl->kind = IDECL_DECL;
}
static IdentDecl *ident_decl(Identifier i) {
@@ -225,3 +226,16 @@ static bool ident_eq_str(Identifier i, const char *s) {
if (i->parent) return false; /* not at root */
return true;
}
+
+static Location idecl_where(IdentDecl *id) {
+
+ switch (id->kind) {
+ case IDECL_DECL:
+ return id->decl->where;
+ case IDECL_EXPR:
+ return id->expr->where;
+ }
+ assert(0);
+ Location def = {0};
+ return def;
+}
diff --git a/main.c b/main.c
index 671a4e3..deb038e 100644
--- a/main.c
+++ b/main.c
@@ -1,7 +1,10 @@
/*
TODO:
-make sure futurely/currently-declared types are *only* used by pointer
-for
+each
+compile-time each
+each pointer
+compile-time each pointer
+prevent each x := x
+=, -=, *=, /=
compile-time arguments
don't allow while {3; 5} (once break is added)
@@ -9,6 +12,7 @@ any odd number of "s for a string
modifiable strings:
s := ["sakjdfhkjh ksjdahfkjsd ahs ahdf hsdaf khsadkjfh"];
unicode variable names (cgen support)
+make sure futurely/currently-declared types are only used by pointer/slice
allow omission of trailing ; in foo @= fn() {}?
*/
#include "toc.c"
diff --git a/parse.c b/parse.c
index ce01ba6..9a05082 100644
--- a/parse.c
+++ b/parse.c
@@ -12,6 +12,7 @@ static const char *expr_kind_to_str(ExprKind k) {
case EXPR_LITERAL_CHAR: return "character literal";
case EXPR_IF: return "if expression";
case EXPR_WHILE: return "while expression";
+ case EXPR_EACH: return "each expression";
case EXPR_CALL: return "function call";
case EXPR_C: return "c code";
case EXPR_DSIZEOF: return "#sizeof";
@@ -255,11 +256,14 @@ static inline Expression *parser_new_expr(Parser *p) {
return parser_malloc(p, sizeof(Expression));
}
-#define EXPR_CAN_END_WITH_COMMA 0x01 /* a comma could end the expression */
-#define EXPR_CAN_END_WITH_LBRACE 0x02
-#define EXPR_CAN_END_WITH_COLON 0x04
+typedef enum {
+ EXPR_CAN_END_WITH_COMMA = 0x01, /* a comma could end the expression */
+ EXPR_CAN_END_WITH_LBRACE = 0x02,
+ EXPR_CAN_END_WITH_COLON = 0x04,
+ EXPR_CAN_END_WITH_DOTDOT = 0x08
+} ExprEndFlags;
/* is_vbs can be NULL */
-static Token *expr_find_end(Parser *p, U16 flags, bool *is_vbs) {
+static Token *expr_find_end(Parser *p, ExprEndFlags flags, bool *is_vbs) {
Tokenizer *t = p->tokr;
int paren_level = 0;
int brace_level = 0;
@@ -313,6 +317,10 @@ static Token *expr_find_end(Parser *p, U16 flags, bool *is_vbs) {
return token;
could_be_vbs = true;
break;
+ case KW_DOTDOT:
+ if (brace_level == 0 && square_level == 0 && paren_level == 0 && (flags & EXPR_CAN_END_WITH_DOTDOT))
+ return token;
+ break;
case KW_COLON:
if ((flags & EXPR_CAN_END_WITH_COLON)
&& brace_level == 0 && square_level == 0 && paren_level == 0)
@@ -1050,6 +1058,94 @@ static bool parse_expr(Parser *p, Expression *e, Token *end) {
if (!parse_block(p, &w->body)) return false;
return true;
}
+ case KW_EACH: {
+ e->kind = EXPR_EACH;
+ EachExpr *ea = &e->each;
+ ea->flags = 0;
+ ea->value = NULL;
+ ea->index = NULL;
+ t->token++;
+ if (t->token->kind == TOKEN_IDENT) {
+ ea->value = t->token->ident;
+ if (ident_eq_str(ea->value, "_")) /* ignore value */
+ ea->value = NULL;
+ t->token++;
+ if (token_is_kw(t->token, KW_COMMA)) {
+ t->token++;
+ if (t->token->kind == TOKEN_IDENT) {
+ ea->index = t->token->ident;
+ if (ident_eq_str(ea->index, "_")) /* ignore index */
+ ea->index = NULL;
+ } else {
+ tokr_err(t, "Expected identifier after , in each statement.");
+ return false;
+ }
+ }
+ }
+ if (token_is_kw(t->token, KW_AT)) {
+ tokr_err(t, "The variable(s) in a for loop cannot be constant.");
+ return false;
+ }
+ if (!token_is_kw(t->token, KW_COLON)) {
+ tokr_err(t, "Expected : following identifiers in for statement.");
+ return false;
+ }
+ t->token++;
+ if (!token_is_kw(t->token, KW_EQ)) {
+ ea->flags |= EACH_ANNOTATED_TYPE;
+ if (!parse_type(p, &ea->type))
+ return false;
+ if (!token_is_kw(t->token, KW_EQ)) {
+ tokr_err(t, "Expected = in for statement.");
+ return false;
+ }
+ }
+ t->token++;
+ Token *first_end = expr_find_end(p, EXPR_CAN_END_WITH_COMMA|EXPR_CAN_END_WITH_DOTDOT|EXPR_CAN_END_WITH_LBRACE, NULL);
+ Expression *first = parser_new_expr(p);
+ if (!parse_expr(p, first, first_end))
+ return false;
+ if (token_is_kw(first_end, KW_LBRACE)) {
+ ea->of = first;
+ } else if (token_is_kw(first_end, KW_DOTDOT) || token_is_kw(first_end, KW_COMMA)) {
+ ea->flags |= EACH_IS_RANGE;
+ ea->range.from = first;
+ if (token_is_kw(first_end, KW_COMMA)) {
+ /* step */
+ t->token++;
+ ea->range.step = parser_new_expr(p);
+ Token *step_end = expr_find_end(p, EXPR_CAN_END_WITH_LBRACE|EXPR_CAN_END_WITH_DOTDOT, NULL);
+ if (!parse_expr(p, ea->range.step, step_end))
+ return false;
+ if (!token_is_kw(step_end, KW_DOTDOT)) {
+ err_print(step_end->where, "Expected .. to follow step in for statement.");
+ return false;
+ }
+ } else {
+ ea->range.step = NULL;
+ }
+ t->token++; /* move past .. */
+ if (token_is_kw(t->token, KW_LBRACE)) {
+ ea->range.to = NULL; /* infinite loop! */
+ } else {
+ ea->range.to = parser_new_expr(p);
+ Token *to_end = expr_find_end(p, EXPR_CAN_END_WITH_LBRACE, NULL);
+ if (!parse_expr(p, ea->range.to, to_end))
+ return false;
+ if (!token_is_kw(t->token, KW_LBRACE)) {
+ tokr_err(t, "Expected { to open body of for statement.");
+ return false;
+ }
+ }
+ } else {
+ err_print(first_end->where, "Expected { or .. to follow expression in for statement.");
+ return false;
+ }
+
+ if (!parse_block(p, &ea->body))
+ return false;
+ return true;
+ }
default: break;
}
@@ -1831,6 +1927,8 @@ static void fprint_arg_exprs(FILE *out, Expression *args) {
fprintf(out, ")");
}
+static void fprint_val(FILE *f, Value *v, Type *t);
+
static void fprint_expr(FILE *out, Expression *e) {
PARSE_PRINT_LOCATION(e->where);
switch (e->kind) {
@@ -1902,6 +2000,43 @@ static void fprint_expr(FILE *out, Expression *e) {
if (e->while_.cond) fprint_expr(out, e->while_.cond);
fprint_block(out, &e->while_.body);
break;
+ case EXPR_EACH: {
+ EachExpr *ea = &e->each;
+ fprintf(out, "each ");
+ if (ea->index) {
+ fprint_ident(out, ea->index);
+ } else fprintf(out, "_");
+ fprintf(out, ", ");
+ if (ea->value) {
+ fprint_ident(out, ea->value);
+ } else fprintf(out, "_");
+ fprintf(out, " :");
+ if (ea->flags & EACH_ANNOTATED_TYPE)
+ fprint_type(out, &ea->type);
+ fprintf(out, "= ");
+ if (ea->flags & EACH_IS_RANGE) {
+ fprint_expr(out, ea->range.from);
+ if (parse_printing_after_types) {
+ if (ea->range.stepval) {
+ fprintf(out, ",");
+ fprint_val(out, ea->range.stepval, &ea->type);
+ }
+ } else {
+ if (ea->range.step) {
+ fprintf(out, ",");
+ fprint_expr(out, ea->range.step);
+ }
+ }
+ fprintf(out, "..");
+ if (ea->range.to) {
+ fprint_expr(out, ea->range.to);
+ }
+ fprintf(out, " ");
+ } else {
+ fprint_expr(out, ea->of);
+ }
+ fprint_block(out, &ea->body);
+ } break;
case EXPR_CALL:
fprint_expr(out, e->call.fn);
if (parse_printing_after_types) {
diff --git a/scope.c b/scope.c
index 42e446a..35721d0 100644
--- a/scope.c
+++ b/scope.c
@@ -6,10 +6,10 @@ static void val_free(Value *v, Type *t);
static bool add_ident_decls(Block *b, Declaration *d, U32 flags) {
bool ret = true;
arr_foreach(d->idents, Identifier, ident) {
- IdentDecl **decls = &(*ident)->decls;
- if ((flags & SCOPE_FLAG_CHECK_REDECL) && arr_len(*decls)) {
+ IdentDecl *decls = (*ident)->decls;
+ if ((flags & SCOPE_FLAG_CHECK_REDECL) && arr_len(decls)) {
/* check that it hasn't been declared in this block */
- IdentDecl *prev = arr_last(*decls);
+ IdentDecl *prev = arr_last(decls);
if (prev->scope == b) {
err_print(d->where, "Re-declaration of identifier in the same block.");
info_print(prev->decl->where, "Previous declaration was here.");
@@ -30,7 +30,7 @@ static void remove_ident_decls(Block *b, Declaration *d) {
IdentDecl **decls = &id_info->decls;
IdentDecl *last_decl = arr_last(*decls);
if (last_decl && last_decl->scope == b) {
- if ((last_decl->flags & IDECL_FLAG_HAS_VAL)
+ if ((last_decl->flags & IDECL_HAS_VAL)
/* don't free const vals (there's only one per decl) */
&& !(last_decl->decl->flags & DECL_FLAG_CONST)) {
val_free(&last_decl->val, is_tuple ? &d->type.tuple[i++] : &d->type);
@@ -63,11 +63,14 @@ static void block_exit(Block *b, Statement *stmts) {
}
/* does NOT enter function's block body */
-static void fn_enter(FnExpr *f, U32 flags) {
+static bool fn_enter(FnExpr *f, U32 flags) {
arr_foreach(f->params, Declaration, decl)
- add_ident_decls(&f->body, decl, flags);
+ if (!add_ident_decls(&f->body, decl, flags))
+ return false;
arr_foreach(f->ret_decls, Declaration, decl)
- add_ident_decls(&f->body, decl, flags);
+ if (!add_ident_decls(&f->body, decl, flags))
+ return false;
+ return true;
}
static void fn_exit(FnExpr *f) {
@@ -76,3 +79,31 @@ static void fn_exit(FnExpr *f) {
arr_foreach(f->ret_decls, Declaration, decl)
remove_ident_decls(&f->body, decl);
}
+
+static bool each_enter(Expression *e, U32 flags) {
+ assert(e->kind == EXPR_EACH);
+ EachExpr *ea = &e->each;
+ if (ea->index && ea->index == ea->value) {
+ err_print(e->where, "The identifier for the index of an each loop must be different from the identifier for the value.");
+ return false;
+ }
+ if (ea->index) {
+ IdentDecl *id = arr_add(&ea->index->decls);
+ id->flags = 0;
+ id->kind = IDECL_EXPR;
+ id->scope = &ea->body;
+ id->expr = e;
+ }
+ if (ea->value) {
+ IdentDecl *id = arr_add(&ea->value->decls);
+ id->flags = 0;
+ id->kind = IDECL_EXPR;
+ id->scope = &ea->body;
+ id->expr = e;
+ }
+ return true;
+}
+
+static void each_exit(Expression *e) {
+ assert(e->kind == EXPR_EACH);
+}
diff --git a/test.toc b/test.toc
index 00cd372..cce5421 100644
--- a/test.toc
+++ b/test.toc
@@ -1,55 +1,16 @@
+
puti @= fn(x: int) {
#C("printf(\"%ld\\n\", (long)x);
");
};
-total @= fn() int {
- t := 0;
- x := new(int,10);
- i := 0;
- while i < x.len {
- x[i] = i;
- i = i + 1;
- }
-
- x.len = 5;
- i = 0;
- while i < x.len {
- t = t + x[i];
- i = i + 1;
- }
- t
-};
-
-Foo @= struct {
- x: &Bar;
-};
-
-Bar @= struct {
- y : &Foo;
-};
main @= fn() {
- f: Foo;
- b: Bar;
- f.x = &b;
- b.y = &f;
-
- Baz @= struct { a : &Quux; };
- Quux @= struct { b: &Baz; };
- B : Baz;
- q : Quux;
- B.a = &q;
- q.b = &B;
-
- Wow @= struct { w: &Wow; };
- w: Wow;
- w.w = &w;
-
- puti(total());
- X @= total();
- puti(X);
- puti(#sizeof(int));
- puti(#alignof(int));
+ each i := 1..10 {
+ puti(i);
+ }
+ each i := 10,-1..1 {
+ puti(i);
+ }
};
diff --git a/tokenizer.c b/tokenizer.c
index a5eb3ce..828b3d4 100644
--- a/tokenizer.c
+++ b/tokenizer.c
@@ -1,8 +1,8 @@
static const char *keywords[KW_COUNT] =
{";", ":", "@", ",", "(", ")", "{", "}", "[", "]", "==", "!=", "<=", "<", ">=", ">",
- "+", "-", "*", "!", "&", "/", ".",
+ "+", "-", "*", "!", "&", "/", "..", ".",
"=",
- "if", "elif", "else", "while", "return", "fn", "as",
+ "if", "elif", "else", "while", "each", "return", "fn", "as",
"new", "del", "struct",
"int", "i8", "i16", "i32", "i64",
"u8", "u16", "u32", "u64", "float", "f32", "f64",
@@ -273,7 +273,6 @@ static bool tokenize_string(Tokenizer *t, char *str) {
}
/* check if it's a number */
-
if (isdigit(*t->s)) {
/* it's a numeric literal */
int base = 10;
@@ -308,6 +307,10 @@ static bool tokenize_string(Tokenizer *t, char *str) {
while (1) {
if (*t->s == '.') {
+ if (t->s[1] == '.') {
+ /* .. (not a decimal point; end the number here) */
+ break;
+ }
if (n.kind == NUM_LITERAL_FLOAT) {
tokenization_err(t, "Double . in number.");
goto err;
@@ -389,6 +392,7 @@ static bool tokenize_string(Tokenizer *t, char *str) {
}
tokr_nextchar(t);
}
+
token->kind = TOKEN_LITERAL_NUM;
token->num = n;
continue;
diff --git a/typedefs_cgen.c b/typedefs_cgen.c
index 2051acb..937be4a 100644
--- a/typedefs_cgen.c
+++ b/typedefs_cgen.c
@@ -56,6 +56,19 @@ static bool typedefs_expr(CGenerator *g, Expression *e) {
if (!typedefs_block(g, &e->while_.body))
return false;
break;
+ case EXPR_EACH: {
+ EachExpr *ea = &e->each;
+ if (ea->flags & EACH_IS_RANGE) {
+ if (!typedefs_expr(g, ea->range.from))
+ return false;
+ if (ea->range.to && !typedefs_expr(g, ea->range.to))
+ return false;
+ /* step is a value, not an expression */
+ } else {
+ if (!typedefs_expr(g, ea->of))
+ return false;
+ }
+ } break;
case EXPR_TUPLE:
arr_foreach(e->tuple, Expression, x)
if (!typedefs_expr(g, x))
diff --git a/types.c b/types.c
index 7756585..7b8e9fb 100644
--- a/types.c
+++ b/types.c
@@ -102,12 +102,15 @@ static bool expr_arr_must_mut(Expression *e) {
switch (e->kind) {
case EXPR_IDENT: {
IdentDecl *idecl = ident_decl(e->ident);
- Declaration *d = idecl->decl;
- if (d->flags & DECL_FLAG_CONST) {
- err_print(e->where, "Cannot modify a constant array.");
- return false;
+ if (idecl->kind == IDECL_DECL) {
+ Declaration *d = idecl->decl;
+ if (d->flags & DECL_FLAG_CONST) {
+ err_print(e->where, "Cannot modify a constant array.");
+ return false;
+ }
}
- } return true;
+ return true;
+ }
case EXPR_CAST:
case EXPR_CALL:
case EXPR_NEW:
@@ -119,6 +122,8 @@ static bool expr_arr_must_mut(Expression *e) {
case EXPR_WHILE:
assert(e->while_.body.ret_expr);
return expr_arr_must_mut(e->while_.body.ret_expr);
+ case EXPR_EACH:
+ return expr_arr_must_mut(e->each.body.ret_expr);
case EXPR_IF:
for (IfExpr *i = &e->if_; i; i->next_elif ? i = &i->next_elif->if_ : (i = NULL)) {
assert(i->body.ret_expr);
@@ -155,12 +160,15 @@ static bool expr_must_lval(Expression *e) {
case EXPR_IDENT: {
IdentDecl *id_decl = ident_decl(e->ident);
assert(id_decl);
- Declaration *d = id_decl->decl;
- if (d->flags & DECL_FLAG_CONST) {
- char *istr = ident_to_str(e->ident);
- err_print(e->where, "Use of constant %s as a non-constant expression.", istr);
- info_print(d->where, "%s was declared here.", istr);
- return false;
+ if (id_decl->kind == IDECL_DECL) {
+ Declaration *d = id_decl->decl;
+ if (d->flags & DECL_FLAG_CONST) {
+ char *istr = ident_to_str(e->ident);
+ err_print(e->where, "Use of constant %s as a non-constant expression.", istr);
+ info_print(d->where, "%s was declared here.", istr);
+ return false;
+ }
+
}
return true;
}
@@ -203,6 +211,7 @@ static bool expr_must_lval(Expression *e) {
case EXPR_LITERAL_BOOL:
case EXPR_IF:
case EXPR_WHILE:
+ case EXPR_EACH:
case EXPR_CALL:
case EXPR_C:
case EXPR_DALIGNOF:
@@ -243,6 +252,7 @@ static bool type_of_fn(Typer *tr, Expression *e, Type *t) {
}
static bool type_of_ident(Typer *tr, Location where, Identifier i, Type *t) {
+ t->flags = 0;
IdentDecl *decl = ident_decl(i);
if (!decl) {
char *s = ident_to_str(i);
@@ -250,73 +260,93 @@ static bool type_of_ident(Typer *tr, Location where, Identifier i, Type *t) {
free(s);
return false;
}
- Declaration *d = decl->decl;
- bool captured = false;
- if (decl->scope != NULL)
- for (Block *block = tr->block; block != decl->scope; block = block->parent) {
- if (block->flags & BLOCK_FLAG_FN) {
- captured = true;
- break;
+ switch (decl->kind) {
+ case IDECL_DECL: {
+ Declaration *d = decl->decl;
+ bool captured = false;
+ if (decl->scope != NULL)
+ for (Block *block = tr->block; block != decl->scope; block = block->parent) {
+ if (block->flags & BLOCK_FLAG_FN) {
+ captured = true;
+ break;
+ }
}
+ if (captured && !(d->flags & DECL_FLAG_CONST)) {
+ err_print(where, "Variables cannot be captured into inner functions (but constants can).");
+ return false;
}
- if (captured && !(d->flags & DECL_FLAG_CONST)) {
- err_print(where, "Variables cannot be captured into inner functions (but constants can).");
- return false;
- }
- /* are we inside this declaration? */
- typedef Declaration *DeclarationPtr;
- arr_foreach(tr->in_decls, DeclarationPtr, in_decl) {
- if (d == *in_decl) {
- assert(d->flags & DECL_FLAG_HAS_EXPR); /* we can only be in decls with an expr */
- if (d->expr.kind != EXPR_FN) { /* it's okay if a function references itself */
- /* if we've complained about it before when we were figuring out the type, don't complain again */
- if (!(d->flags & DECL_FLAG_ERRORED_ABOUT_SELF_REFERENCE)) {
- char *s = ident_to_str(i);
- err_print(where, "Use of identifier %s within its own declaration.", s);
- free(s);
- info_print(d->where, "Declaration was here.");
- d->flags |= DECL_FLAG_ERRORED_ABOUT_SELF_REFERENCE;
+ /* are we inside this declaration? */
+ typedef Declaration *DeclarationPtr;
+ arr_foreach(tr->in_decls, DeclarationPtr, in_decl) {
+ if (d == *in_decl) {
+ assert(d->flags & DECL_FLAG_HAS_EXPR); /* we can only be in decls with an expr */
+ if (d->expr.kind != EXPR_FN) { /* it's okay if a function references itself */
+ /* if we've complained about it before when we were figuring out the type, don't complain again */
+ if (!(d->flags & DECL_FLAG_ERRORED_ABOUT_SELF_REFERENCE)) {
+ char *s = ident_to_str(i);
+ err_print(where, "Use of identifier %s within its own declaration.", s);
+ free(s);
+ info_print(d->where, "Declaration was here.");
+ d->flags |= DECL_FLAG_ERRORED_ABOUT_SELF_REFERENCE;
+ }
+ return false;
}
- return false;
}
}
- }
- if (d->flags & DECL_FLAG_FOUND_TYPE) {
- if (d->type.kind == TYPE_TUPLE) {
- /* get correct item in tuple */
- long index = 0;
- arr_foreach(d->idents, Identifier, decl_i) {
- if (*decl_i == i) {
- break;
+ if (d->flags & DECL_FLAG_FOUND_TYPE) {
+ if (d->type.kind == TYPE_TUPLE) {
+ /* get correct item in tuple */
+ long index = 0;
+ arr_foreach(d->idents, Identifier, decl_i) {
+ if (*decl_i == i) {
+ break;
+ }
+ index++;
+ assert(index < (long)arr_len(d->idents)); /* identifier got its declaration set to here, but it's not here */
}
- index++;
- assert(index < (long)arr_len(d->idents)); /* identifier got its declaration set to here, but it's not here */
+ *t = d->type.tuple[index];
+ } else {
+ *t = d->type;
}
- *t = d->type.tuple[index];
- } else {
- *t = d->type;
- }
- return true;
- } else {
- if ((d->flags & DECL_FLAG_HAS_EXPR) && (d->expr.kind == EXPR_FN)) {
- /* allow using a function before declaring it */
- if (!type_of_fn(tr, &d->expr, t)) return false;
return true;
} else {
- if (location_after(d->where, where)) {
- char *s = ident_to_str(i);
- err_print(where, "Use of identifier %s before its declaration.\nNote that it is only possible to use a constant function before it is directly declared (e.g. x @= fn() {}).", s);
- info_print(d->where, "%s will be declared here.", s);
- free(s);
+ if ((d->flags & DECL_FLAG_HAS_EXPR) && (d->expr.kind == EXPR_FN)) {
+ /* allow using a function before declaring it */
+ if (!type_of_fn(tr, &d->expr, t)) return false;
+ return true;
} else {
- /* let's type the declaration, and redo this (for evaling future functions) */
- if (!types_decl(tr, d)) return false;
- return type_of_ident(tr, where, i, t);
+ if (location_after(d->where, where)) {
+ char *s = ident_to_str(i);
+ err_print(where, "Use of identifier %s before its declaration.\nNote that it is only possible to use a constant function before it is directly declared (e.g. x @= fn() {}).", s);
+ info_print(d->where, "%s will be declared here.", s);
+ free(s);
+ } else {
+ /* let's type the declaration, and redo this (for evaling future functions) */
+ if (!types_decl(tr, d)) return false;
+ return type_of_ident(tr, where, i, t);
+ }
+ return false;
}
- return false;
}
+ } break;
+ case IDECL_EXPR: {
+ Expression *e = decl->expr;
+ switch (e->kind) {
+ case EXPR_EACH:
+ if (i == e->each.index) {
+ t->kind = TYPE_BUILTIN;
+ t->builtin = BUILTIN_I64;
+ } else {
+ assert(i == e->each.value);
+ *t = e->each.type;
+ }
+ break;
+ default: assert(0); return false;
+ }
+ } break;
}
+ return true;
}
/* fixes the type (replaces [5+3]int with [8]int, etc.) */
@@ -389,6 +419,7 @@ static bool type_resolve(Typer *tr, Type *t, Location where) {
free(s);
return false;
}
+ assert(idecl->kind == IDECL_DECL);
Declaration *decl = idecl->decl;
/* now, type the declaration (in case we are using it before its declaration) */
if (!types_decl(tr, decl))
@@ -558,7 +589,8 @@ static bool types_expr(Typer *tr, Expression *e) {
tr->ret_type = t->fn.types[0];
}
tr->can_ret = true;
- fn_enter(f, SCOPE_FLAG_CHECK_REDECL);
+ if (!fn_enter(f, SCOPE_FLAG_CHECK_REDECL))
+ return false;
bool block_success = true;
block_success = types_block(tr, &e->fn.body);
fn_exit(f);
@@ -634,6 +666,116 @@ static bool types_expr(Typer *tr, Expression *e) {
t->kind = TYPE_BUILTIN;
t->builtin = BUILTIN_CHAR;
break;
+ case EXPR_EACH: {
+ EachExpr *ea = &e->each;
+ if (ea->flags & EACH_IS_RANGE) {
+ /* TODO: allow user-defined numerical types */
+ if (!types_expr(tr, ea->range.from)) return false;
+ {
+ Type *ft = &ea->range.from->type;
+ if (ft->kind != TYPE_BUILTIN || !type_builtin_is_numerical(ft->builtin)) {
+ char *s = type_to_str(ft);
+ err_print(e->where, "from expression of each must be a builtin numerical type, not %s", s);
+ free(s);
+ }
+ }
+ if (ea->range.step) {
+ if (!types_expr(tr, ea->range.step)) return false;
+ Type *st = &ea->range.step->type;
+ if (st->kind != TYPE_BUILTIN || !type_builtin_is_numerical(st->builtin)) {
+ char *s = type_to_str(st);
+ err_print(e->where, "step expression of each must be a builtin numerical type, not %s", s);
+ free(s);
+ }
+ }
+ if (ea->range.to) {
+ if (!types_expr(tr, ea->range.to)) return false;
+ Type *tt = &ea->range.to->type;
+ if (tt->kind != TYPE_BUILTIN || !type_builtin_is_numerical(tt->builtin)) {
+ char *s = type_to_str(tt);
+ err_print(e->where, "to expression of each must be a builtin numerical type, not %s", s);
+ free(s);
+ }
+ }
+
+ if (!(ea->flags & EACH_ANNOTATED_TYPE)) {
+ ea->type = ea->range.from->type;
+ }
+
+ if (!type_eq(&ea->type, &ea->range.from->type)) {
+ char *exp = type_to_str(&ea->type);
+ char *got = type_to_str(&ea->range.from->type);
+ err_print(e->where, "Type of each does not match the type of the from expression. Expected %s, but got %s.", exp, got);
+ free(exp); free(got);
+ return false;
+ }
+
+ if (ea->range.step && !type_eq(&ea->type, &ea->range.step->type)) {
+ char *exp = type_to_str(&ea->type);
+ char *got = type_to_str(&ea->range.step->type);
+ err_print(e->where, "Type of each does not match the type of the step expression. Expected %s, but got %s.", exp, got);
+ free(exp); free(got);
+ return false;
+ }
+
+ if ((ea->type.flags & TYPE_FLAG_FLEXIBLE) && ea->range.step)
+ ea->type = ea->range.step->type;
+
+ if (ea->range.to && !type_eq(&ea->type, &ea->range.to->type)) {
+ char *exp = type_to_str(&ea->type);
+ char *got = type_to_str(&ea->range.to->type);
+ err_print(e->where, "Type of each does not match the type of the to expression. Expected %s, but got %s.", exp, got);
+ free(exp); free(got);
+ return false;
+ }
+
+ if ((ea->type.flags & TYPE_FLAG_FLEXIBLE) && ea->range.to)
+ ea->type = ea->range.to->type;
+
+ } else {
+ if (!types_expr(tr, ea->of))
+ return false;
+ Type *of_type = NULL;
+ switch (ea->of->type.kind) {
+ case TYPE_SLICE:
+ of_type = ea->of->type.slice;
+ break;
+ case TYPE_ARR:
+ of_type = ea->of->type.arr.of;
+ break;
+ default: {
+ char *s = type_to_str(&ea->of->type);
+ err_print(e->where, "Cannot iterate over non-array non-slice type %s.", s);
+ free(s);
+ return false;
+ }
+ }
+ if (ea->flags & EACH_ANNOTATED_TYPE) {
+ if (!type_eq(of_type, &ea->type)) {
+ char *exp = type_to_str(of_type);
+ char *got = type_to_str(&ea->type);
+ err_print(e->where, "Expected to iterate over type %s, but it was annotated as iterating over type %s.");
+ free(exp); free(got);
+ return false;
+ }
+ } else ea->type = *of_type;
+ }
+ if ((ea->flags & EACH_IS_RANGE) && ea->range.step) {
+ Value *stepval = typer_malloc(tr, sizeof *ea->range.stepval);
+ if (!eval_expr(tr->evalr, ea->range.step, stepval)) {
+ info_print(ea->range.step->where, "Note that the step of an each loop must be a compile-time constant.");
+ return false;
+ }
+ ea->range.stepval = stepval;
+ }
+ if (!each_enter(e, SCOPE_FLAG_CHECK_REDECL)) return false;
+ if (!types_block(tr, &ea->body)) return false;
+ each_exit(e);
+ if (ea->body.ret_expr)
+ *t = ea->body.ret_expr->type;
+ else
+ t->kind = TYPE_VOID;
+ } break;
case EXPR_IDENT: {
if (!type_of_ident(tr, e->where, e->ident, t)) return false;
} break;
@@ -785,10 +927,12 @@ static bool types_expr(Typer *tr, Expression *e) {
if (f->kind == EXPR_IDENT) {
IdentDecl *decl = ident_decl(f->ident);
assert(decl);
- if (decl->decl->flags & DECL_FLAG_HAS_EXPR) {
- Expression *expr = &decl->decl->expr;
- if (expr->kind == EXPR_FN)
- fn_decl = &decl->decl->expr.fn;
+ if (decl->kind == IDECL_DECL) {
+ if (decl->decl->flags & DECL_FLAG_HAS_EXPR) {
+ Expression *expr = &decl->decl->expr;
+ if (expr->kind == EXPR_FN)
+ fn_decl = &decl->decl->expr.fn;
+ }
}
}
if (!fn_decl && nargs != nparams) {
@@ -819,7 +963,7 @@ static bool types_expr(Typer *tr, Expression *e) {
char *s = ident_to_str(args[p].name);
err_print(args[p].where, "Argument '%s' does not appear in declaration of function.", s);
free(s);
- info_print(ident_decl(f->ident)->decl->where, "Declaration is here.");
+ info_print(idecl_where(ident_decl(f->ident)), "Declaration is here.");
return false;
}
new_args[arg_index] = args[p].val;
diff --git a/types.h b/types.h
index 0313691..d81ea05 100644
--- a/types.h
+++ b/types.h
@@ -94,12 +94,22 @@ typedef union Value {
struct Type *type;
} Value;
-#define IDECL_FLAG_HAS_VAL 0x01
+#define IDECL_HAS_VAL 0x01
+
+typedef enum {
+ IDECL_DECL,
+ IDECL_EXPR
+} IdentDeclKind;
+
typedef struct {
- struct Declaration *decl;
+ union {
+ struct Declaration *decl;
+ struct Expression *expr; /* for example, this identifier is declared in an each expression */
+ };
struct Block *scope; /* NULL for file scope */
Value val;
- uint16_t flags;
+ IdentDeclKind kind;
+ U16 flags;
} IdentDecl;
/*
@@ -166,6 +176,7 @@ typedef enum {
KW_EXCLAMATION,
KW_AMPERSAND,
KW_SLASH,
+ KW_DOTDOT,
KW_DOT,
KW_EQ,
KW_LAST_SYMBOL = KW_EQ, /* last one entirely consisting of symbols */
@@ -173,6 +184,7 @@ typedef enum {
KW_ELIF,
KW_ELSE,
KW_WHILE,
+ KW_EACH,
KW_RETURN,
KW_FN,
KW_AS,
@@ -340,6 +352,7 @@ typedef enum {
EXPR_UNARY_OP,
EXPR_IF,
EXPR_WHILE,
+ EXPR_EACH,
EXPR_FN,
EXPR_CAST,
EXPR_NEW,
@@ -406,6 +419,33 @@ typedef struct {
Block body;
} WhileExpr;
+
+#define EACH_IS_RANGE 0x01
+#define EACH_ANNOTATED_TYPE 0x02
+
+typedef struct EachExpr {
+ U16 flags;
+ struct {
+ IdentID id;
+ IdentID index_id; /* only set if index is NULL */
+ } c;
+ Type type;
+ Identifier index; /* NULL = no index */
+ Identifier value; /* NULL = no value */
+ Block body;
+ union {
+ struct {
+ struct Expression *from;
+ struct Expression *to;
+ union {
+ struct Expression *step;
+ Value *stepval;
+ };
+ } range;
+ struct Expression *of;
+ };
+} EachExpr;
+
typedef struct FnExpr {
struct Declaration *params; /* declarations of the parameters to this function */
struct Declaration *ret_decls; /* array of decls, if this has named return values. otherwise, NULL */
@@ -478,6 +518,7 @@ typedef struct Expression {
} del;
IfExpr if_;
WhileExpr while_;
+ EachExpr each;
FnExpr fn;
CastExpr cast;
SliceExpr slice;
@@ -587,4 +628,5 @@ typedef struct {
Expression **anon_fns; /* array of pointers to expressions of anonymous functions */
Evaluator *evalr;
Identifier main_ident;
+ Identifiers *idents;
} CGenerator;