aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorgingerBill <gingerBill@users.noreply.github.com>2021-08-16 12:03:43 +0100
committerGitHub <noreply@github.com>2021-08-16 12:03:43 +0100
commit5f072591ba70abf6df59f4ed9372649e7ebda710 (patch)
tree8c9d02259b75be22736d65d4a595ee933a26a259 /src
parentd62f189d7261160a2d3c66bfba2fda182a863fbd (diff)
parente3fef2dadef2b10342222f0314583f6f53e39ff4 (diff)
Merge pull request #1082 from odin-lang/or_else-or_return-operator
`or_else` and `or_return` operators
Diffstat (limited to 'src')
-rw-r--r--src/check_builtin.cpp91
-rw-r--r--src/check_expr.cpp151
-rw-r--r--src/check_stmt.cpp10
-rw-r--r--src/checker_builtin_procs.hpp4
-rw-r--r--src/llvm_backend_expr.cpp8
-rw-r--r--src/llvm_backend_proc.cpp3
-rw-r--r--src/llvm_backend_utility.cpp42
-rw-r--r--src/parser.cpp62
-rw-r--r--src/parser.hpp2
-rw-r--r--src/parser_pos.cpp4
-rw-r--r--src/tokenizer.cpp7
11 files changed, 315 insertions, 69 deletions
diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp
index 846bea296..e1615ce87 100644
--- a/src/check_builtin.cpp
+++ b/src/check_builtin.cpp
@@ -48,6 +48,17 @@ BuiltinTypeIsProc *builtin_type_is_procs[BuiltinProc__type_simple_boolean_end -
};
+void check_or_else_right_type(CheckerContext *c, Ast *expr, String const &name, Type *right_type) {
+ if (right_type == nullptr) {
+ return;
+ }
+ if (!is_type_boolean(right_type) && !type_has_nil(right_type)) {
+ gbString str = type_to_string(right_type);
+ error(expr, "'%.*s' expects an \"optional ok\" like value, or an n-valued expression where the last value is either a boolean or can be compared against 'nil', got %s", LIT(name), str);
+ gb_string_free(str);
+ }
+}
+
void check_or_else_split_types(CheckerContext *c, Operand *x, String const &name, Type **left_type_, Type **right_type_) {
Type *left_type = nullptr;
Type *right_type = nullptr;
@@ -70,15 +81,11 @@ void check_or_else_split_types(CheckerContext *c, Operand *x, String const &name
if (left_type_) *left_type_ = left_type;
if (right_type_) *right_type_ = right_type;
- if (!is_type_boolean(right_type)) {
- gbString str = type_to_string(right_type);
- error(x->expr, "'%.*s' expects an \"optional ok\" like value, got %s", LIT(name), str);
- gb_string_free(str);
- }
+ check_or_else_right_type(c, x->expr, name, right_type);
}
-void check_try_expr_no_value_error(CheckerContext *c, String const &name, Operand const &x, Type *type_hint) {
+void check_or_else_expr_no_value_error(CheckerContext *c, String const &name, Operand const &x, Type *type_hint) {
// TODO(bill): better error message
gbString t = type_to_string(x.type);
error(x.expr, "'%.*s' does not return a value, value is of type %s", LIT(name), t);
@@ -108,6 +115,33 @@ void check_try_expr_no_value_error(CheckerContext *c, String const &name, Operan
}
+void check_or_return_split_types(CheckerContext *c, Operand *x, String const &name, Type **left_type_, Type **right_type_) {
+ Type *left_type = nullptr;
+ Type *right_type = nullptr;
+ if (x->type->kind == Type_Tuple) {
+ auto const &vars = x->type->Tuple.variables;
+ auto lhs = array_slice(vars, 0, vars.count-1);
+ auto rhs = vars[vars.count-1];
+ if (lhs.count == 1) {
+ left_type = lhs[0]->type;
+ } else if (lhs.count != 0) {
+ left_type = alloc_type_tuple();
+ left_type->Tuple.variables = array_make_from_ptr(lhs.data, lhs.count, lhs.count);
+ }
+
+ right_type = rhs->type;
+ } else {
+ check_promote_optional_ok(c, x, &left_type, &right_type);
+ }
+
+ if (left_type_) *left_type_ = left_type;
+ if (right_type_) *right_type_ = right_type;
+
+ check_or_else_right_type(c, x->expr, name, right_type);
+}
+
+
+
bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 id, Type *type_hint) {
ast_node(ce, CallExpr, call);
if (ce->inlining != ProcInlining_none) {
@@ -145,10 +179,6 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
// NOTE(bill): The first arg may be a Type, this will be checked case by case
break;
- case BuiltinProc_or_else:
- // NOTE(bill): The arguments may be multi-expr
- break;
-
case BuiltinProc_DIRECTIVE: {
ast_node(bd, BasicDirective, ce->proc);
String name = bd->name.string;
@@ -1800,47 +1830,6 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
break;
}
- case BuiltinProc_or_else: {
- GB_ASSERT(ce->args.count == 2);
- Ast *arg = ce->args[0];
- Ast *default_value = ce->args[1];
-
- Operand x = {};
- Operand y = {};
- check_multi_expr_with_type_hint(c, &x, arg, type_hint);
- if (x.mode == Addressing_Invalid) {
- operand->mode = Addressing_Value;
- operand->type = t_invalid;
- return false;
- }
-
- check_multi_expr_with_type_hint(c, &y, default_value, x.type);
- error_operand_no_value(&y);
- if (y.mode == Addressing_Invalid) {
- operand->mode = Addressing_Value;
- operand->type = t_invalid;
- return false;
- }
-
- Type *left_type = nullptr;
- Type *right_type = nullptr;
- check_or_else_split_types(c, &x, builtin_name, &left_type, &right_type);
- add_type_and_value(&c->checker->info, arg, x.mode, x.type, x.value);
-
- if (left_type != nullptr) {
- check_assignment(c, &y, left_type, builtin_name);
- } else {
- check_try_expr_no_value_error(c, builtin_name, x, type_hint);
- }
-
- if (left_type == nullptr) {
- left_type = t_invalid;
- }
- operand->mode = Addressing_Value;
- operand->type = left_type;
- return true;
- }
-
case BuiltinProc_simd_vector: {
Operand x = {};
Operand y = {};
diff --git a/src/check_expr.cpp b/src/check_expr.cpp
index f01e9a328..30f44a59f 100644
--- a/src/check_expr.cpp
+++ b/src/check_expr.cpp
@@ -114,6 +114,11 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
void check_promote_optional_ok(CheckerContext *c, Operand *x, Type **val_type_, Type **ok_type_);
+void check_or_else_right_type(CheckerContext *c, Ast *expr, String const &name, Type *right_type);
+void check_or_else_split_types(CheckerContext *c, Operand *x, String const &name, Type **left_type_, Type **right_type_);
+void check_or_else_expr_no_value_error(CheckerContext *c, String const &name, Operand const &x, Type *type_hint);
+void check_or_return_split_types(CheckerContext *c, Operand *x, String const &name, Type **left_type_, Type **right_type_);
+
Entity *entity_from_expr(Ast *expr) {
expr = unparen_expr(expr);
switch (expr->kind) {
@@ -2960,6 +2965,25 @@ void update_untyped_expr_type(CheckerContext *c, Ast *e, Type *type, bool final)
update_untyped_expr_type(c, te->y, type, final);
case_end;
+ case_ast_node(ore, OrReturnExpr, e);
+ if (old->value.kind != ExactValue_Invalid) {
+ // See above note in UnaryExpr case
+ break;
+ }
+
+ update_untyped_expr_type(c, ore->expr, type, final);
+ case_end;
+
+ case_ast_node(oee, OrElseExpr, e);
+ if (old->value.kind != ExactValue_Invalid) {
+ // See above note in UnaryExpr case
+ break;
+ }
+
+ update_untyped_expr_type(c, oee->x, type, final);
+ update_untyped_expr_type(c, oee->y, type, final);
+ case_end;
+
case_ast_node(pe, ParenExpr, e);
update_untyped_expr_type(c, pe->expr, type, final);
case_end;
@@ -6602,6 +6626,123 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
}
case_end;
+ case_ast_node(oe, OrElseExpr, node);
+ String name = oe->token.string;
+ Ast *arg = oe->x;
+ Ast *default_value = oe->y;
+
+ Operand x = {};
+ Operand y = {};
+ check_multi_expr_with_type_hint(c, &x, arg, type_hint);
+ if (x.mode == Addressing_Invalid) {
+ o->mode = Addressing_Value;
+ o->type = t_invalid;
+ o->expr = node;
+ return Expr_Expr;
+ }
+
+ check_multi_expr_with_type_hint(c, &y, default_value, x.type);
+ error_operand_no_value(&y);
+ if (y.mode == Addressing_Invalid) {
+ o->mode = Addressing_Value;
+ o->type = t_invalid;
+ o->expr = node;
+ return Expr_Expr;
+ }
+
+ Type *left_type = nullptr;
+ Type *right_type = nullptr;
+ check_or_else_split_types(c, &x, name, &left_type, &right_type);
+ add_type_and_value(&c->checker->info, arg, x.mode, x.type, x.value);
+
+ if (left_type != nullptr) {
+ check_assignment(c, &y, left_type, name);
+ } else {
+ check_or_else_expr_no_value_error(c, name, x, type_hint);
+ }
+
+ if (left_type == nullptr) {
+ left_type = t_invalid;
+ }
+ o->mode = Addressing_Value;
+ o->type = left_type;
+ o->expr = node;
+ return Expr_Expr;
+ case_end;
+
+ case_ast_node(re, OrReturnExpr, node);
+ String name = re->token.string;
+ Operand x = {};
+ check_multi_expr_with_type_hint(c, &x, re->expr, type_hint);
+ if (x.mode == Addressing_Invalid) {
+ o->mode = Addressing_Value;
+ o->type = t_invalid;
+ o->expr = node;
+ return Expr_Expr;
+ }
+
+ Type *left_type = nullptr;
+ Type *right_type = nullptr;
+ check_or_return_split_types(c, &x, name, &left_type, &right_type);
+ add_type_and_value(&c->checker->info, re->expr, x.mode, x.type, x.value);
+
+ if (right_type == nullptr) {
+ check_or_else_expr_no_value_error(c, name, x, type_hint);
+ } else {
+ Type *proc_type = base_type(c->curr_proc_sig);
+ GB_ASSERT(proc_type->kind == Type_Proc);
+ Type *result_type = proc_type->Proc.results;
+ if (result_type == nullptr) {
+ error(node, "'%.*s' requires the current procedure to have at least one return value", LIT(name));
+ } else {
+ GB_ASSERT(result_type->kind == Type_Tuple);
+
+ auto const &vars = result_type->Tuple.variables;
+ Type *end_type = vars[vars.count-1]->type;
+
+ if (vars.count > 1) {
+ if (!proc_type->Proc.has_named_results) {
+ error(node, "'%.*s' within a procedure with more than 1 return value requires that the return values are named, allowing for early return", LIT(name));
+ }
+ }
+
+ Operand rhs = {};
+ rhs.type = right_type;
+ rhs.mode = Addressing_Value;
+
+ // TODO(bill): better error message
+ if (!check_is_assignable_to(c, &rhs, end_type)) {
+ gbString a = type_to_string(right_type);
+ gbString b = type_to_string(end_type);
+ gbString ret_type = type_to_string(result_type);
+ error(node, "Cannot assign end value of type '%s' to '%s' in '%.*s'", a, b, LIT(name));
+ if (vars.count == 1) {
+ error_line("\tProcedure return value type: %s\n", ret_type);
+ } else {
+ error_line("\tProcedure return value types: (%s)\n", ret_type);
+ }
+ gb_string_free(ret_type);
+ gb_string_free(b);
+ gb_string_free(a);
+ }
+ }
+ }
+
+ o->expr = node;
+ o->type = left_type;
+ if (left_type != nullptr) {
+ o->mode = Addressing_Value;
+ } else {
+ o->mode = Addressing_NoValue;
+ }
+
+ if (c->curr_proc_sig == nullptr) {
+ error(node, "'%.*s' can only be used within a procedure", LIT(name));
+ }
+
+ return Expr_Expr;
+ case_end;
+
case_ast_node(cl, CompoundLit, node);
Type *type = type_hint;
if (type != nullptr && is_type_untyped(type)) {
@@ -8565,6 +8706,16 @@ gbString write_expr_to_string(gbString str, Ast *node, bool shorthand) {
str = write_expr_to_string(str, te->y, shorthand);
case_end;
+ case_ast_node(oe, OrElseExpr, node);
+ str = write_expr_to_string(str, oe->x, shorthand);
+ str = gb_string_appendc(str, " or_else ");
+ str = write_expr_to_string(str, oe->y, shorthand);
+ case_end;
+
+ case_ast_node(oe, OrReturnExpr, node);
+ str = write_expr_to_string(str, oe->expr, shorthand);
+ str = gb_string_appendc(str, " or_return");
+ case_end;
case_ast_node(pe, ParenExpr, node);
str = gb_string_append_rune(str, '(');
diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp
index 236b5a9f5..504c23d53 100644
--- a/src/check_stmt.cpp
+++ b/src/check_stmt.cpp
@@ -1480,8 +1480,10 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
if (kind == Expr_Stmt) {
return;
}
- if (operand.expr->kind == Ast_CallExpr) {
- AstCallExpr *ce = &operand.expr->CallExpr;
+ Ast *expr = strip_or_return_expr(operand.expr);
+
+ if (expr->kind == Ast_CallExpr) {
+ AstCallExpr *ce = &expr->CallExpr;
Type *t = type_of_expr(ce->proc);
if (is_type_proc(t)) {
if (t->Proc.require_results) {
@@ -1491,8 +1493,8 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
}
}
return;
- } else if (operand.expr->kind == Ast_SelectorCallExpr) {
- AstSelectorCallExpr *se = &operand.expr->SelectorCallExpr;
+ } else if (expr->kind == Ast_SelectorCallExpr) {
+ AstSelectorCallExpr *se = &expr->SelectorCallExpr;
ast_node(ce, CallExpr, se->call);
Type *t = type_of_expr(ce->proc);
if (is_type_proc(t)) {
diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp
index a25657abe..57b5d7eb9 100644
--- a/src/checker_builtin_procs.hpp
+++ b/src/checker_builtin_procs.hpp
@@ -33,8 +33,6 @@ enum BuiltinProcId {
BuiltinProc_soa_zip,
BuiltinProc_soa_unzip,
- BuiltinProc_or_else,
-
BuiltinProc_DIRECTIVE, // NOTE(bill): This is used for specialized hash-prefixed procedures
// "Intrinsics"
@@ -265,8 +263,6 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("soa_zip"), 1, true, Expr_Expr, BuiltinProcPkg_builtin},
{STR_LIT("soa_unzip"), 1, false, Expr_Expr, BuiltinProcPkg_builtin},
- {STR_LIT("or_else"), 2, false, Expr_Expr, BuiltinProcPkg_builtin},
-
{STR_LIT(""), 0, true, Expr_Expr, BuiltinProcPkg_builtin}, // DIRECTIVE
diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp
index 1c5f03722..e6bb35c0c 100644
--- a/src/llvm_backend_expr.cpp
+++ b/src/llvm_backend_expr.cpp
@@ -2305,6 +2305,14 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) {
}
case_end;
+ case_ast_node(oe, OrElseExpr, expr);
+ return lb_emit_or_else(p, oe->x, oe->y, tv);
+ case_end;
+
+ case_ast_node(oe, OrReturnExpr, expr);
+ return lb_emit_or_return(p, oe->expr, tv);
+ case_end;
+
case_ast_node(ta, TypeAssertion, expr);
TokenPos pos = ast_token(expr).pos;
Type *type = tv.type;
diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp
index 5b922de4b..03b052c71 100644
--- a/src/llvm_backend_proc.cpp
+++ b/src/llvm_backend_proc.cpp
@@ -1252,9 +1252,6 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
case BuiltinProc_soa_unzip:
return lb_soa_unzip(p, ce, tv);
- case BuiltinProc_or_else:
- return lb_emit_or_else(p, ce->args[0], ce->args[1], tv);
-
// "Intrinsics"
case BuiltinProc_alloca:
diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp
index 5fc5389c9..406b4fc2e 100644
--- a/src/llvm_backend_utility.cpp
+++ b/src/llvm_backend_utility.cpp
@@ -364,6 +364,48 @@ lbValue lb_emit_or_else(lbProcedure *p, Ast *arg, Ast *else_expr, TypeAndValue c
return res;
}
+void lb_build_return_stmt(lbProcedure *p, Slice<Ast *> const &return_results);
+void lb_build_return_stmt_internal(lbProcedure *p, lbValue const &res);
+
+lbValue lb_emit_or_return(lbProcedure *p, Ast *arg, TypeAndValue const &tv) {
+ lbValue lhs = {};
+ lbValue rhs = {};
+ lb_emit_try_lhs_rhs(p, arg, tv, &lhs, &rhs);
+
+ lbBlock *return_block = lb_create_block(p, "or_return.return");
+ lbBlock *continue_block = lb_create_block(p, "or_return.continue");
+
+ lb_emit_if(p, lb_emit_try_has_value(p, rhs), continue_block, return_block);
+ lb_start_block(p, return_block);
+ {
+ Type *proc_type = base_type(p->type);
+ Type *results = proc_type->Proc.results;
+ GB_ASSERT(results != nullptr && results->kind == Type_Tuple);
+ TypeTuple *tuple = &results->Tuple;
+
+ GB_ASSERT(tuple->variables.count != 0);
+
+ Entity *end_entity = tuple->variables[tuple->variables.count-1];
+ rhs = lb_emit_conv(p, rhs, end_entity->type);
+ if (p->type->Proc.has_named_results) {
+ GB_ASSERT(end_entity->token.string.len != 0);
+
+ // NOTE(bill): store the named values before returning
+ lbValue found = map_must_get(&p->module->values, hash_entity(end_entity));
+ lb_emit_store(p, found, rhs);
+
+ lb_build_return_stmt(p, {});
+ } else {
+ GB_ASSERT(tuple->variables.count == 1);
+ lb_build_return_stmt_internal(p, rhs);
+ }
+ }
+ lb_start_block(p, continue_block);
+ if (tv.type != nullptr) {
+ return lb_emit_conv(p, lhs, tv.type);
+ }
+ return {};
+}
void lb_emit_increment(lbProcedure *p, lbValue addr) {
diff --git a/src/parser.cpp b/src/parser.cpp
index 893ce9891..8f3ffb8cc 100644
--- a/src/parser.cpp
+++ b/src/parser.cpp
@@ -187,6 +187,13 @@ Ast *clone_ast(Ast *node) {
n->TernaryWhenExpr.cond = clone_ast(n->TernaryWhenExpr.cond);
n->TernaryWhenExpr.y = clone_ast(n->TernaryWhenExpr.y);
break;
+ case Ast_OrElseExpr:
+ n->OrElseExpr.x = clone_ast(n->OrElseExpr.x);
+ n->OrElseExpr.y = clone_ast(n->OrElseExpr.y);
+ break;
+ case Ast_OrReturnExpr:
+ n->OrReturnExpr.expr = clone_ast(n->OrReturnExpr.expr);
+ break;
case Ast_TypeAssertion:
n->TypeAssertion.expr = clone_ast(n->TypeAssertion.expr);
n->TypeAssertion.type = clone_ast(n->TypeAssertion.type);
@@ -685,6 +692,21 @@ Ast *ast_ternary_when_expr(AstFile *f, Ast *x, Ast *cond, Ast *y) {
return result;
}
+Ast *ast_or_else_expr(AstFile *f, Ast *x, Token const &token, Ast *y) {
+ Ast *result = alloc_ast_node(f, Ast_OrElseExpr);
+ result->OrElseExpr.x = x;
+ result->OrElseExpr.token = token;
+ result->OrElseExpr.y = y;
+ return result;
+}
+
+Ast *ast_or_return_expr(AstFile *f, Ast *expr, Token const &token) {
+ Ast *result = alloc_ast_node(f, Ast_OrReturnExpr);
+ result->OrReturnExpr.expr = expr;
+ result->OrReturnExpr.token = token;
+ return result;
+}
+
Ast *ast_type_assertion(AstFile *f, Ast *expr, Token dot, Ast *type) {
Ast *result = alloc_ast_node(f, Ast_TypeAssertion);
result->TypeAssertion.expr = expr;
@@ -1340,6 +1362,8 @@ Token expect_operator(AstFile *f) {
// okay
} else if (prev.kind == Token_if || prev.kind == Token_when) {
// okay
+ } else if (prev.kind == Token_or_else || prev.kind == Token_or_return) {
+ // okay
} else if (!gb_is_between(prev.kind, Token__OperatorBegin+1, Token__OperatorEnd-1)) {
String p = token_to_string(prev);
syntax_error(f->curr_token, "Expected an operator, got '%.*s'",
@@ -1674,6 +1698,22 @@ Ast *unselector_expr(Ast *node) {
return node;
}
+Ast *strip_or_return_expr(Ast *node) {
+ for (;;) {
+ if (node == nullptr) {
+ return node;
+ }
+ if (node->kind == Ast_OrReturnExpr) {
+ node = node->OrReturnExpr.expr;
+ } else if (node->kind == Ast_ParenExpr) {
+ node = node->ParenExpr.expr;
+ } else {
+ return node;
+ }
+ }
+}
+
+
Ast *parse_value(AstFile *f);
Array<Ast *> parse_element_list(AstFile *f) {
@@ -1892,7 +1932,7 @@ bool ast_on_same_line(Ast *x, Ast *y) {
Ast *parse_force_inlining_operand(AstFile *f, Token token) {
Ast *expr = parse_unary_expr(f, false);
- Ast *e = unparen_expr(expr);
+ Ast *e = strip_or_return_expr(expr);
if (e->kind != Ast_ProcLit && e->kind != Ast_CallExpr) {
syntax_error(expr, "%.*s must be followed by a procedure literal or call, got %.*s", LIT(token.string), LIT(ast_strings[expr->kind]));
return ast_bad_expr(f, token, f->curr_token);
@@ -2777,6 +2817,10 @@ Ast *parse_atom_expr(AstFile *f, Ast *operand, bool lhs) {
operand = ast_deref_expr(f, operand, expect_token(f, Token_Pointer));
break;
+ case Token_or_return:
+ operand = ast_or_return_expr(f, operand, expect_token(f, Token_or_return));
+ break;
+
case Token_OpenBrace:
if (!lhs && is_literal_type(operand) && f->expr_level >= 0) {
operand = parse_literal_value(f, operand);
@@ -2870,6 +2914,7 @@ i32 token_precedence(AstFile *f, TokenKind t) {
case Token_Question:
case Token_if:
case Token_when:
+ case Token_or_else:
return 1;
case Token_Ellipsis:
case Token_RangeFull:
@@ -2924,14 +2969,16 @@ Ast *parse_binary_expr(AstFile *f, bool lhs, i32 prec_in) {
// NOTE(bill): This will also catch operators that are not valid "binary" operators
break;
}
- if (op.kind == Token_if || op.kind == Token_when) {
- Token prev = f->prev_token;
+ Token prev = f->prev_token;
+ switch (op.kind) {
+ case Token_if:
+ case Token_when:
if (prev.pos.line < op.pos.line) {
// NOTE(bill): Check to see if the `if` or `when` is on the same line of the `lhs` condition
- break;
+ goto loop_end;
}
+ break;
}
-
expect_operator(f); // NOTE(bill): error checks too
if (op.kind == Token_Question) {
@@ -2955,6 +3002,10 @@ Ast *parse_binary_expr(AstFile *f, bool lhs, i32 prec_in) {
Token tok_else = expect_token(f, Token_else);
Ast *y = parse_expr(f, lhs);
expr = ast_ternary_when_expr(f, x, cond, y);
+ } else if (op.kind == Token_or_else) {
+ Ast *x = expr;
+ Ast *y = parse_expr(f, lhs);
+ expr = ast_or_else_expr(f, x, op, y);
} else {
Ast *right = parse_binary_expr(f, false, prec+1);
if (right == nullptr) {
@@ -2965,6 +3016,7 @@ Ast *parse_binary_expr(AstFile *f, bool lhs, i32 prec_in) {
lhs = false;
}
+ loop_end:;
}
return expr;
}
diff --git a/src/parser.hpp b/src/parser.hpp
index c284ec586..579ec5b79 100644
--- a/src/parser.hpp
+++ b/src/parser.hpp
@@ -385,6 +385,8 @@ AST_KIND(_ExprBegin, "", bool) \
AST_KIND(FieldValue, "field value", struct { Token eq; Ast *field, *value; }) \
AST_KIND(TernaryIfExpr, "ternary if expression", struct { Ast *x, *cond, *y; }) \
AST_KIND(TernaryWhenExpr, "ternary when expression", struct { Ast *x, *cond, *y; }) \
+ AST_KIND(OrElseExpr, "or_else expression", struct { Ast *x; Token token; Ast *y; }) \
+ AST_KIND(OrReturnExpr, "or_return expression", struct { Ast *expr; Token token; }) \
AST_KIND(TypeAssertion, "type assertion", struct { \
Ast *expr; \
Token dot; \
diff --git a/src/parser_pos.cpp b/src/parser_pos.cpp
index 921836afe..0f2ac438a 100644
--- a/src/parser_pos.cpp
+++ b/src/parser_pos.cpp
@@ -41,6 +41,8 @@ Token ast_token(Ast *node) {
case Ast_DerefExpr: return node->DerefExpr.op;
case Ast_TernaryIfExpr: return ast_token(node->TernaryIfExpr.x);
case Ast_TernaryWhenExpr: return ast_token(node->TernaryWhenExpr.x);
+ case Ast_OrElseExpr: return ast_token(node->OrElseExpr.x);
+ case Ast_OrReturnExpr: return ast_token(node->OrReturnExpr.expr);
case Ast_TypeAssertion: return ast_token(node->TypeAssertion.expr);
case Ast_TypeCast: return node->TypeCast.token;
case Ast_AutoCast: return node->AutoCast.token;
@@ -175,6 +177,8 @@ Token ast_end_token(Ast *node) {
case Ast_DerefExpr: return node->DerefExpr.op;
case Ast_TernaryIfExpr: return ast_end_token(node->TernaryIfExpr.y);
case Ast_TernaryWhenExpr: return ast_end_token(node->TernaryWhenExpr.y);
+ case Ast_OrElseExpr: return ast_end_token(node->OrElseExpr.y);
+ case Ast_OrReturnExpr: return node->OrReturnExpr.token;
case Ast_TypeAssertion: return ast_end_token(node->TypeAssertion.type);
case Ast_TypeCast: return ast_end_token(node->TypeCast.expr);
case Ast_AutoCast: return ast_end_token(node->AutoCast.expr);
diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp
index e33c945bc..8772890d9 100644
--- a/src/tokenizer.cpp
+++ b/src/tokenizer.cpp
@@ -113,10 +113,12 @@ TOKEN_KIND(Token__KeywordBegin, ""), \
TOKEN_KIND(Token_transmute, "transmute"), \
TOKEN_KIND(Token_distinct, "distinct"), \
TOKEN_KIND(Token_using, "using"), \
- TOKEN_KIND(Token_inline, "inline"), \
- TOKEN_KIND(Token_no_inline, "no_inline"), \
TOKEN_KIND(Token_context, "context"), \
+ TOKEN_KIND(Token_or_else, "or_else"), \
+ TOKEN_KIND(Token_or_return, "or_return"), \
TOKEN_KIND(Token_asm, "asm"), \
+ TOKEN_KIND(Token_inline, "inline"), \
+ TOKEN_KIND(Token_no_inline, "no_inline"), \
TOKEN_KIND(Token__KeywordEnd, ""), \
TOKEN_KIND(Token_Count, "")
@@ -1508,6 +1510,7 @@ semicolon_check:;
case Token_continue:
case Token_fallthrough:
case Token_return:
+ case Token_or_return:
/*fallthrough*/
case Token_Integer:
case Token_Float: