From b7b3e0868b9eef1018f87bd1c81c0112abaf94f4 Mon Sep 17 00:00:00 2001 From: Leo Tenenbaum Date: Mon, 16 Mar 2020 16:26:13 -0400 Subject: break, continue --- cgen.c | 4 ++-- eval.c | 38 ++++++++++++++++++++++++++++++++++---- main.c | 7 +++---- test.toc | 20 +++++++++++++++++--- tests/control_flow.toc | 31 +++++++++++++++++++++++++++++++ tests/control_flow_expected | 2 ++ tests/io.toc | 16 ++++++---------- tests/test.sh | 3 ++- types.c | 1 + types.h | 4 +++- 10 files changed, 101 insertions(+), 25 deletions(-) create mode 100644 tests/control_flow.toc create mode 100644 tests/control_flow_expected 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; -- cgit v1.2.3