summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cgen.c4
-rw-r--r--eval.c38
-rw-r--r--main.c7
-rw-r--r--test.toc20
-rw-r--r--tests/control_flow.toc31
-rw-r--r--tests/control_flow_expected2
-rw-r--r--tests/io.toc16
-rwxr-xr-xtests/test.sh3
-rw-r--r--types.c1
-rw-r--r--types.h4
10 files changed, 101 insertions, 25 deletions
diff --git a/cgen.c b/cgen.c
index 928e0ff..ccbeb7e 100644
--- a/cgen.c
+++ b/cgen.c
@@ -1126,12 +1126,12 @@ static void cgen_expr_pre(CGenerator *g, Expression *e) {
}
}
cgen_block(g, &fo->body, ret_name, CGEN_BLOCK_NOBRACES);
+
+ cgen_write(g, "}}");
if (fo->body.c.break_lbl) {
cgen_lbl(g, fo->body.c.break_lbl);
cgen_writeln(g, ":;");
}
-
- cgen_write(g, "}}");
} break;
case EXPR_BLOCK:
e->cgen.id = id;
diff --git a/eval.c b/eval.c
index 2ca1a23..0ce09a3 100644
--- a/eval.c
+++ b/eval.c
@@ -12,7 +12,7 @@ static Status eval_expr(Evaluator *ev, Expression *e, Value *v);
static Value get_builtin_val(BuiltinVal val);
static void evalr_create(Evaluator *ev, Typer *tr, Allocator *allocr) {
- ev->returning = false;
+ ev->returning = NULL;
ev->typer = tr;
ev->enabled = true;
ev->allocr = allocr;
@@ -1237,7 +1237,7 @@ static Status eval_expr(Evaluator *ev, Expression *e, Value *v) {
if (!eval_expr(ev, i->cond, &cond)) return false;
if (val_truthiness(cond, &i->cond->type)) {
if (!eval_block(ev, &i->body, v)) return false;
- } else if (i->next_elif) {
+ } else if (i->next_elif && !ev->returning) {
if (!eval_expr(ev, i->next_elif, v)) return false;
}
} else {
@@ -1255,6 +1255,13 @@ static Status eval_expr(Evaluator *ev, Expression *e, Value *v) {
break;
}
if (!eval_block(ev, &w->body, v)) return false;
+ if (ev->returning) {
+ if (ev->returning == &w->body) {
+ ev->returning = NULL;
+ if (ev->is_break)
+ break;
+ } else break;
+ }
}
} break;
case EXPR_FOR: {
@@ -1310,6 +1317,14 @@ static Status eval_expr(Evaluator *ev, Expression *e, Value *v) {
if (value_val) *value_val = x;
if (!eval_block(ev, &fo->body, v)) return false;
+
+ if (ev->returning) {
+ if (ev->returning == &fo->body) {
+ ev->returning = NULL;
+ if (ev->is_break)
+ break;
+ } else break;
+ }
if (index_val) {
++index_val->i64;
}
@@ -1356,6 +1371,13 @@ static Status eval_expr(Evaluator *ev, Expression *e, Value *v) {
eval_deref(value_val, ptr, &fo->type);
if (!eval_block(ev, &fo->body, v))
return false;
+ if (ev->returning) {
+ if (ev->returning == &fo->body) {
+ ev->returning = NULL;
+ if (ev->is_break)
+ break;
+ } else break;
+ }
++index->i64;
}
}
@@ -1533,7 +1555,7 @@ static Status eval_expr(Evaluator *ev, Expression *e, Value *v) {
if (ev->returning) {
if (fn->ret_type.kind != TYPE_VOID && !fn->ret_decls)
*v = ev->ret_val;
- ev->returning = false;
+ ev->returning = NULL;
}
arr_foreach(fn->params, Declaration, p)
decl_remove_val(p);
@@ -1673,8 +1695,16 @@ static Status eval_stmt(Evaluator *ev, Statement *stmt) {
return false;
copy_val(NULL, &ev->ret_val, r, &stmt->ret.expr.type);
}
- ev->returning = true;
+ ev->returning = stmt->ret.referring_to;
} break;
+ case STMT_BREAK:
+ ev->returning = stmt->referring_to;
+ ev->is_break = true;
+ break;
+ case STMT_CONT:
+ ev->returning = stmt->referring_to;
+ ev->is_break = false;
+ break;
case STMT_INCLUDE: {
Statement *last_reached = arr_last(stmt->inc.stmts);
arr_foreach(stmt->inc.stmts, Statement, sub) {
diff --git a/main.c b/main.c
index e183839..f4db063 100644
--- a/main.c
+++ b/main.c
@@ -8,13 +8,12 @@
/*
TODO:
-break
-continue
-&&, ||
-don't allow while {3; 5} or for 0..10 { 3; 5 }
+don't allow while/for ret exprs, e.g. for 0..10 { 3; 5 }
make #sizeof always take a Type
sizeof (not #sizeof)
- make new(s) and del functions!
+defer
+&&, ||
start making a standard library... (printf; stringbuilder would be nice to have)
switch
- #fallthrough
diff --git a/test.toc b/test.toc
index 8103f08..9c9d467 100644
--- a/test.toc
+++ b/test.toc
@@ -1,17 +1,31 @@
#include "std/io.toc", io;
-main ::= fn() {
+f ::= fn() int {
+ total := 0;
for i := 1..10 {
if i % 2 == 0 { continue; }
- io.puti(i);
+ total += i;
if i == 7 { break; }
}
i := 0;
while {
i += 1;
- io.puti(i);
+ total += i;
if i == 10 {
break;
}
}
+ while i < 100 {
+ i += 1;
+ if i == 100 {
+ return total;
+ }
+ }
+ 0
}
+
+main ::= fn() {
+ x ::= f();
+ io.puti(x);
+ io.puti(f());
+} \ No newline at end of file
diff --git a/tests/control_flow.toc b/tests/control_flow.toc
new file mode 100644
index 0000000..923a40b
--- /dev/null
+++ b/tests/control_flow.toc
@@ -0,0 +1,31 @@
+#include "io.toc", io;
+
+f ::= fn() int {
+ total := 0;
+ for i := 1..10 {
+ if i % 2 == 0 { continue; }
+ total += i;
+ if i == 7 { break; }
+ }
+ i := 0;
+ while {
+ i += 1;
+ total += i;
+ if i == 10 {
+ break;
+ }
+ }
+ while i < 100 {
+ i += 1;
+ if i == 100 {
+ return total;
+ }
+ }
+ 0
+}
+
+main ::= fn() {
+ x ::= f();
+ io.puti(x);
+ io.puti(f());
+} \ No newline at end of file
diff --git a/tests/control_flow_expected b/tests/control_flow_expected
new file mode 100644
index 0000000..15e58a7
--- /dev/null
+++ b/tests/control_flow_expected
@@ -0,0 +1,2 @@
+71
+71
diff --git a/tests/io.toc b/tests/io.toc
index 9e94147..d3ce467 100644
--- a/tests/io.toc
+++ b/tests/io.toc
@@ -1,19 +1,15 @@
-
putchar ::= #foreign("putchar", "libc.so.6") fn(#C int) #C int;
toc_putchar ::= fn(x: char) {
putchar(x as #C int);
-};
-
-fwrite ::= #foreign("fwrite", "libc.so.6") fn(#C &"void", #C size_t, #C size_t, #C &"void") #C size_t;
+}
-stdout_fwrite ::= fn(data: &u8, size: u64, nmemb: u64) {
- fwrite(data, size as #C size_t, nmemb as #C size_t, #builtin("stdout"));
-};
puts ::= fn(x: []char) {
- stdout_fwrite(&x[0] as &u8, 1, x.len as u64);
+ for c := x {
+ toc_putchar(c);
+ }
toc_putchar('\n');
-};
+}
puti ::= fn(x: int) {
if x < 0 {
@@ -36,4 +32,4 @@ puti ::= fn(x: int) {
}
}
toc_putchar('\n');
-};
+}
diff --git a/tests/test.sh b/tests/test.sh
index d30de7c..4016151 100755
--- a/tests/test.sh
+++ b/tests/test.sh
@@ -1,6 +1,7 @@
#!/bin/bash
tests='bf
+control_flow
arr
arr2
arr3
@@ -26,7 +27,7 @@ compile_c() {
elif [ "$CC" = "tcc" ]; then
EXTRA_FLAGS="-w"
fi
- $CC $CFLAGS $EXTRA_FLAGS -o a.out out.c || exit 1
+ $CC $CFLAGS $EXTRA_FLAGS -Werror -o a.out out.c || exit 1
}
do_tests() {
diff --git a/types.c b/types.c
index f202bca..eca871c 100644
--- a/types.c
+++ b/types.c
@@ -3282,6 +3282,7 @@ static Status types_stmt(Typer *tr, Statement *s) {
err_print(s->where, "return outside of a function.");
return false;
}
+ s->ret.referring_to = &tr->fn->body;
if (s->ret.flags & RET_HAS_EXPR) {
if (tr->fn->ret_type.kind == TYPE_VOID) {
err_print(s->where, "Return value in a void function.");
diff --git a/types.h b/types.h
index 20eb28b..9c6545f 100644
--- a/types.h
+++ b/types.h
@@ -922,6 +922,7 @@ enum {
};
typedef struct Return {
U8 flags;
+ Block *referring_to; /* eval uses this; it's the function body we're returning from */
Expression expr;
} Return;
@@ -1017,7 +1018,8 @@ typedef struct {
typedef struct Evaluator {
Allocator *allocr;
struct Typer *typer;
- bool returning;
+ Block *returning; /* function body from which we are returning OR loop body in which we are continuing/breaking */
+ bool is_break; /* is returning because of a break, as opposed to a continue? */
Value ret_val;
bool enabled;
ForeignFnManager ffmgr;