aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBruce Hill <bruce@bruce-hill.com>2024-09-11 01:48:15 -0400
committerBruce Hill <bruce@bruce-hill.com>2024-09-11 01:48:15 -0400
commit989dc3f4428fd3185753336cb91baf48e0c85ad0 (patch)
tree15eb26b7161f9953039f98625a42f7c68c688c86
parent23209a0aab983501701c62ac87c891309a7d3d58 (diff)
Support `if x := blah: ...`
-rw-r--r--compile.c32
-rw-r--r--parse.c5
-rw-r--r--test/optionals.tm5
3 files changed, 30 insertions, 12 deletions
diff --git a/compile.c b/compile.c
index 5ccfafe3..bfdaa091 100644
--- a/compile.c
+++ b/compile.c
@@ -1196,21 +1196,30 @@ CORD compile_statement(env_t *env, ast_t *ast)
}
case If: {
auto if_ = Match(ast, If);
- type_t *cond_t = get_type(env, if_->condition);
+ ast_t *condition = if_->condition;
+ CORD code = CORD_EMPTY;
+ if (condition->tag == Declare) {
+ env = fresh_scope(env);
+ code = compile_statement(env, condition);
+ bind_statement(env, condition);
+ condition = Match(condition, Declare)->var;
+ }
+
+ type_t *cond_t = get_type(env, condition);
if (cond_t->tag == PointerType) {
- code_err(if_->condition, "This pointer will always be non-null, so it should not be used in a conditional.");
+ code_err(condition, "This pointer will always be non-null, so it should not be used in a conditional.");
} else if (cond_t->tag != BoolType && cond_t->tag != TextType && cond_t->tag != OptionalType) {
- code_err(if_->condition, "Only boolean values, optional pointers, and text can be used in conditionals (this is a %T)", cond_t);
+ code_err(condition, "Only boolean values, optional pointers, and text can be used in conditionals (this is a %T)", cond_t);
}
env_t *truthy_scope = env;
- CORD condition;
+ CORD condition_code;
if (cond_t->tag == TextType) {
- condition = CORD_all("(", compile(env, if_->condition), ").length");
+ condition_code = CORD_all("(", compile(env, condition), ").length");
} else if (cond_t->tag == OptionalType) {
- if (if_->condition->tag == Var) {
+ if (condition->tag == Var) {
truthy_scope = fresh_scope(env);
- const char *varname = Match(if_->condition, Var)->name;
+ const char *varname = Match(condition, Var)->name;
binding_t *b = get_binding(env, varname);
binding_t *nonnull_b = new(binding_t);
*nonnull_b = *b;
@@ -1218,14 +1227,17 @@ CORD compile_statement(env_t *env, ast_t *ast)
nonnull_b->code = compile_optional_into_nonnull(env, b);
set_binding(truthy_scope, varname, nonnull_b);
}
- condition = compile_optional_check(env, if_->condition);
+ condition_code = compile_optional_check(env, condition);
} else {
- condition = compile(env, if_->condition);
+ condition_code = compile(env, condition);
}
- CORD code = CORD_all("if (", condition, ")", compile_statement(truthy_scope, if_->body));
+ code = CORD_all(code, "if (", condition_code, ")", compile_statement(truthy_scope, if_->body));
if (if_->else_body)
code = CORD_all(code, "\nelse ", compile_statement(env, if_->else_body));
+
+ if (if_->condition->tag == Declare)
+ code = CORD_all("{\n", code, "}\n");
return code;
}
case Block: {
diff --git a/parse.c b/parse.c
index 95b05cbc..6191ceaf 100644
--- a/parse.c
+++ b/parse.c
@@ -1003,8 +1003,9 @@ PARSER(parse_if) {
if (!match_word(&pos, "if"))
return NULL;
- ast_t *condition = expect(ctx, start, &pos, parse_expr,
- "I expected to find a condition for this 'if'");
+ ast_t *condition = optional(ctx, &pos, parse_declaration);
+ if (!condition)
+ condition = expect(ctx, start, &pos, parse_expr, "I expected to find a condition for this 'if'");
ast_t *body = expect(ctx, start, &pos, parse_block, "I expected a body for this 'if' statement");
diff --git a/test/optionals.tm b/test/optionals.tm
index d8cbf741..8e24f66d 100644
--- a/test/optionals.tm
+++ b/test/optionals.tm
@@ -121,3 +121,8 @@ func main():
fail("Truthy: $nope")
else: !! Falsey: $nope
+
+ if yep := maybe_int(yes):
+ >> yep
+ = 123 : Int
+ else: fail("Unreachable")