Browse Source

Make `or_else` and `or_return` operators (binary and suffix respectively)

gingerBill 4 years ago
parent
commit
21cbac755e

+ 1 - 1
core/os/os2/errors.odin

@@ -57,7 +57,7 @@ link_error_delete :: proc(lerr: Maybe(Link_Error)) {
 
 
 is_platform_error :: proc(ferr: Error) -> (err: i32, ok: bool) {
-	v := or_else(ferr.(Platform_Error), {});
+	v := ferr.(Platform_Error) or_else {};
 	return v.err, v.err != 0;
 }
 

+ 1 - 1
core/os/os2/file_stream.odin

@@ -13,7 +13,7 @@ error_to_io_error :: proc(ferr: Error) -> io.Error {
 	if ferr == nil {
 		return .None;
 	}
-	return or_else(ferr.(io.Error), .Unknown);
+	return ferr.(io.Error) or_else .Unknown;
 }
 
 

+ 1 - 1
core/testing/runner_windows.odin

@@ -68,7 +68,7 @@ Thread_Os_Specific :: struct {
 thread_create :: proc(procedure: Thread_Proc) -> ^Thread {
 	__windows_thread_entry_proc :: proc "stdcall" (t_: rawptr) -> win32.DWORD {
 		t := (^Thread)(t_);
-		context = or_else(t.init_context.?, runtime.default_context());
+		context = t.init_context.? or_else runtime.default_context();
 
 		t.procedure(t);
 

+ 1 - 1
core/thread/thread_unix.odin

@@ -46,7 +46,7 @@ _create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^
 		t.start_gate = {};
 		t.start_mutex = {};
 
-		context = or_else(t.init_context.?, runtime.default_context());
+		context = t.init_context.? or_else runtime.default_context();
 
 		t.procedure(t);
 

+ 1 - 1
core/thread/thread_windows.odin

@@ -23,7 +23,7 @@ _create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^
 
 	__windows_thread_entry_proc :: proc "stdcall" (t_: rawptr) -> win32.DWORD {
 		t := (^Thread)(t_);
-		context = or_else(t.init_context.?, runtime.default_context());
+		context = t.init_context.? or_else runtime.default_context();
 
 		t.procedure(t);
 

+ 50 - 50
examples/all/all_main.odin

@@ -3,57 +3,57 @@ package all
 // Imports every package
 // This is useful for knowing what exists and producing documentation with `odin doc`
 
-import "core:bufio"
-import "core:bytes"
-import "core:c"
-import c_tokenizer "core:c/frontend/tokenizer"
+import bufio          "core:bufio"
+import bytes          "core:bytes"
+import c              "core:c"
+import c_tokenizer    "core:c/frontend/tokenizer"
 import c_preprocessor "core:c/frontend/preprocessor"
-import "core:compress"
-import "core:compress/gzip"
-import "core:compress/zlib"
-import "core:container"
-import "core:dynlib"
-import "core:encoding"
-import "core:encoding/base32"
-import "core:encoding/base64"
-import "core:encoding/csv"
-import "core:encoding/hxa"
-import "core:encoding/json"
-import "core:fmt"
-import "core:hash"
-import "core:image"
-import "core:image/png"
-import "core:io"
-import "core:log"
-import "core:math"
-import "core:math/big"
-import "core:math/bits"
-import "core:math/fixed"
-import "core:math/linalg"
-import "core:math/rand"
-import "core:mem"
-import "core:odin/ast"
-import doc_format "core:odin/doc-format"
-import "core:odin/format"
-import "core:odin/parser"
-import "core:odin/printer"
+import compress       "core:compress"
+import gzip           "core:compress/gzip"
+import zlib           "core:compress/zlib"
+import container      "core:container"
+import dynlib         "core:dynlib"
+import encoding       "core:encoding"
+import base32         "core:encoding/base32"
+import base64         "core:encoding/base64"
+import csv            "core:encoding/csv"
+import hxa            "core:encoding/hxa"
+import json           "core:encoding/json"
+import fmt            "core:fmt"
+import hash           "core:hash"
+import image          "core:image"
+import png            "core:image/png"
+import io             "core:io"
+import log            "core:log"
+import math           "core:math"
+import big            "core:math/big"
+import bits           "core:math/bits"
+import fixed          "core:math/fixed"
+import linalg         "core:math/linalg"
+import rand           "core:math/rand"
+import mem            "core:mem"
+import ast            "core:odin/ast"
+import doc_format     "core:odin/doc-format"
+import odin_format    "core:odin/format"
+import odin_parser    "core:odin/parser"
+import odin_printer   "core:odin/printer"
 import odin_tokenizer "core:odin/tokenizer"
-import "core:os"
-import "core:path"
-import "core:path/filepath"
-import "core:reflect"
-import "core:runtime"
-import "core:slice"
-import "core:sort"
-import "core:strconv"
-import "core:strings"
-import "core:sync"
-import "core:sync/sync2"
-import "core:text/scanner"
-import "core:thread"
-import "core:time"
-import "core:unicode"
-import "core:unicode/utf8"
-import "core:unicode/utf16"
+import os             "core:os"
+import path           "core:path"
+import filepath       "core:path/filepath"
+import reflect        "core:reflect"
+import runtime        "core:runtime"
+import slice          "core:slice"
+import sort           "core:sort"
+import strconv        "core:strconv"
+import strings        "core:strings"
+import sync           "core:sync"
+import sync2          "core:sync/sync2"
+import scanner        "core:text/scanner"
+import thread         "core:thread"
+import time           "core:time"
+import unicode        "core:unicode"
+import utf8           "core:unicode/utf8"
+import utf16          "core:unicode/utf16"
 
 main :: proc(){}

+ 4 - 4
examples/demo/demo.odin

@@ -2010,7 +2010,7 @@ or_else_procedure :: proc() {
 			i = 123;
 		}
 		// The above can be mapped to 'or_else'
-		i = or_else(m["hellope"], 123);
+		i = m["hellope"] or_else 123;
 
 		assert(i == 123);
 	}
@@ -2019,12 +2019,12 @@ or_else_procedure :: proc() {
 		// have optional ok semantics
 		v: union{int, f64};
 		i: int;
-		i = or_else(v.(int), 123);
-		i = or_else(v.?, 123); // Type inference magic
+		i = v.(int) or_else  123;
+		i = v.? or_else 123; // Type inference magic
 		assert(i == 123);
 
 		m: Maybe(int);
-		i = or_else(m.?, 456);
+		i = m.? or_else 456;
 		assert(i == 456);
 	}
 }

+ 0 - 119
src/check_builtin.cpp

@@ -179,11 +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:
-	case BuiltinProc_or_return:
-		// NOTE(bill): The arguments may be multi-expr
-		break;
-
 	case BuiltinProc_DIRECTIVE: {
 		ast_node(bd, BasicDirective, ce->proc);
 		String name = bd->name.string;
@@ -1835,120 +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_or_else_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_or_return: {
-		GB_ASSERT(ce->args.count == 1);
-		Ast *arg = ce->args[0];
-
-		Operand x = {};
-		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;
-		}
-
-		Type *left_type = nullptr;
-		Type *right_type = nullptr;
-		check_or_return_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 (right_type == nullptr) {
-			check_or_else_expr_no_value_error(c, builtin_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(call, "'%.*s' requires the current procedure to have at least one return value", LIT(builtin_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(call, "'%.*s' within a procedure with more than 1 return value requires that the return values are named, allowing for early return", LIT(builtin_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(call, "Cannot assign end value of type '%s' to '%s' in '%.*s'", a, b, LIT(builtin_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);
-				}
-			}
-		}
-
-		operand->type = left_type;
-		if (left_type != nullptr) {
-			operand->mode = Addressing_Value;
-		} else {
-			operand->mode = Addressing_NoValue;
-		}
-
-		if (c->curr_proc_sig == nullptr) {
-			error(call, "'%.*s' can only be used within a procedure", LIT(builtin_name));
-		}
-
-		return true;
-	}
-
 	case BuiltinProc_simd_vector: {
 		Operand x = {};
 		Operand y = {};

+ 136 - 0
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,118 @@ 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;
+			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;
+			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;
+		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;
+			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->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)) {

+ 0 - 6
src/checker_builtin_procs.hpp

@@ -33,9 +33,6 @@ enum BuiltinProcId {
 	BuiltinProc_soa_zip,
 	BuiltinProc_soa_unzip,
 
-	BuiltinProc_or_else,
-	BuiltinProc_or_return,
-
 	BuiltinProc_DIRECTIVE, // NOTE(bill): This is used for specialized hash-prefixed procedures
 
 	// "Intrinsics"
@@ -266,9 +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("or_return"),        1, false, Expr_Expr, BuiltinProcPkg_builtin},
-
 	{STR_LIT(""),                 0, true,  Expr_Expr, BuiltinProcPkg_builtin}, // DIRECTIVE
 
 

+ 8 - 0
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;

+ 0 - 5
src/llvm_backend_proc.cpp

@@ -1252,11 +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);
-	case BuiltinProc_or_return:
-		return lb_emit_or_return(p, ce->args[0], tv);
-
 	// "Intrinsics"
 
 	case BuiltinProc_alloca:

+ 34 - 4
src/parser.cpp

@@ -685,6 +685,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 +1355,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'",
@@ -2870,6 +2887,8 @@ i32 token_precedence(AstFile *f, TokenKind t) {
 	case Token_Question:
 	case Token_if:
 	case Token_when:
+	case Token_or_else:
+	case Token_or_return:
 		return 1;
 	case Token_Ellipsis:
 	case Token_RangeFull:
@@ -2924,14 +2943,18 @@ 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:
+			case Token_or_else:
+			case Token_or_return:
 				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 +2978,12 @@ 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 if (op.kind == Token_or_return) {
+				expr = ast_or_return_expr(f, expr, op);
 			} else {
 				Ast *right = parse_binary_expr(f, false, prec+1);
 				if (right == nullptr) {
@@ -2965,6 +2994,7 @@ Ast *parse_binary_expr(AstFile *f, bool lhs, i32 prec_in) {
 
 			lhs = false;
 		}
+		loop_end:;
 	}
 	return expr;
 }

+ 2 - 0
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; \

+ 4 - 0
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);

+ 4 - 2
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, "")