From fba2b99b65d5023675a3f270adbc87ef0b0ede8f Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Thu, 23 May 2024 12:40:21 -0400 Subject: [PATCH] Support 'while when' --- compile.c | 2 +- parse.c | 10 +++++++++- test/enums.tm | 6 ++++++ typecheck.c | 4 +++- 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/compile.c b/compile.c index 9be60d7..f3a1330 100644 --- a/compile.c +++ b/compile.c @@ -623,7 +623,7 @@ CORD compile_statement(env_t *env, ast_t *ast) CORD body = compile_statement(scope, while_->body); if (loop_ctx.skip_label) body = CORD_all(body, "\n", loop_ctx.skip_label, ": continue;"); - CORD loop = CORD_all("while (", compile(scope, while_->condition), ") {\n\t", body, "\n}"); + CORD loop = CORD_all("while (", while_->condition ? compile(scope, while_->condition) : "yes", ") {\n\t", body, "\n}"); if (loop_ctx.stop_label) loop = CORD_all(loop, "\n", loop_ctx.stop_label, ":;"); return loop; diff --git a/parse.c b/parse.c index 28e91b2..75aff30 100644 --- a/parse.c +++ b/parse.c @@ -983,10 +983,18 @@ PARSER(parse_while) { // while condition [do] [] body const char *start = pos; if (!match_word(&pos, "while")) return NULL; + + const char *tmp = pos; + // Shorthand form: `while when ...` + if (match_word(&tmp, "when")) { + ast_t *when = expect(ctx, start, &pos, parse_when, "I expected a 'when' block after this"); + if (!when->__data.When.else_body) when->__data.When.else_body = NewAST(ctx->file, pos, pos, Stop); + return NewAST(ctx->file, start, pos, While, .body=when); + } ast_t *condition = expect(ctx, start, &pos, parse_expr, "I don't see a viable condition for this 'while'"); expect_str(ctx, start, &pos, ":", "I expected a ':' here"); ast_t *body = expect(ctx, start, &pos, parse_opt_indented_block, "I expected a body for this 'while'"); - const char *tmp = pos; + tmp = pos; whitespace(&tmp); return NewAST(ctx->file, start, pos, While, .condition=condition, .body=body); } diff --git a/test/enums.tm b/test/enums.tm index 9f70041..553fcf8 100644 --- a/test/enums.tm +++ b/test/enums.tm @@ -55,3 +55,9 @@ func main(): >> choose_text(Foo.Last("XX")) = "else: Foo.Last(t=\"XX\")" + i := 1 + cases := [Foo.One(1), Foo.One(2), Foo.Zero] + while when cases[i] is One(x): + >> x + i += 1 + diff --git a/typecheck.c b/typecheck.c index 217937e..9964c9d 100644 --- a/typecheck.c +++ b/typecheck.c @@ -948,7 +948,9 @@ type_t *get_type(env_t *env, ast_t *ast) break; } } - if (!any_unhandled) + // HACK: `while when ...` is handled by the parser adding an implicit + // `else: stop`, which has an empty source code span. + if (!any_unhandled && when->else_body->end > when->else_body->start) code_err(when->else_body, "This 'else' block will never run because every tag is handled"); type_t *else_t = get_type(env, when->else_body);