From 21cbac755ec2e18e5471c8f8e1406a87156d8043 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 15 Aug 2021 17:14:35 +0100 Subject: [PATCH] Make `or_else` and `or_return` operators (binary and suffix respectively) --- core/os/os2/errors.odin | 2 +- core/os/os2/file_stream.odin | 2 +- core/testing/runner_windows.odin | 2 +- core/thread/thread_unix.odin | 2 +- core/thread/thread_windows.odin | 2 +- examples/all/all_main.odin | 100 +++++++++++------------ examples/demo/demo.odin | 8 +- src/check_builtin.cpp | 119 --------------------------- src/check_expr.cpp | 136 +++++++++++++++++++++++++++++++ src/checker_builtin_procs.hpp | 6 -- src/llvm_backend_expr.cpp | 8 ++ src/llvm_backend_proc.cpp | 5 -- src/parser.cpp | 38 ++++++++- src/parser.hpp | 2 + src/parser_pos.cpp | 4 + src/tokenizer.cpp | 6 +- 16 files changed, 247 insertions(+), 195 deletions(-) diff --git a/core/os/os2/errors.odin b/core/os/os2/errors.odin index b7e89b493..8dcfe8dd0 100644 --- a/core/os/os2/errors.odin +++ b/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; } diff --git a/core/os/os2/file_stream.odin b/core/os/os2/file_stream.odin index 936d9da57..849f0866e 100644 --- a/core/os/os2/file_stream.odin +++ b/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; } diff --git a/core/testing/runner_windows.odin b/core/testing/runner_windows.odin index 03ae9ede9..255d44f1b 100644 --- a/core/testing/runner_windows.odin +++ b/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); diff --git a/core/thread/thread_unix.odin b/core/thread/thread_unix.odin index ef17596b8..fa82832ec 100644 --- a/core/thread/thread_unix.odin +++ b/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); diff --git a/core/thread/thread_windows.odin b/core/thread/thread_windows.odin index ad834ddc7..1dfa0c0b6 100644 --- a/core/thread/thread_windows.odin +++ b/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); diff --git a/examples/all/all_main.odin b/examples/all/all_main.odin index 9baff266c..e04cf7d49 100644 --- a/examples/all/all_main.odin +++ b/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(){} diff --git a/examples/demo/demo.odin b/examples/demo/demo.odin index 2590fdc01..eed633cd6 100644 --- a/examples/demo/demo.odin +++ b/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); } } diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 77c4ee453..e1615ce87 100644 --- a/src/check_builtin.cpp +++ b/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 = {}; diff --git a/src/check_expr.cpp b/src/check_expr.cpp index f01e9a328..c4715f67e 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,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)) { diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index bd105f0d7..57b5d7eb9 100644 --- a/src/checker_builtin_procs.hpp +++ b/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 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 b3178e008..03b052c71 100644 --- a/src/llvm_backend_proc.cpp +++ b/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: diff --git a/src/parser.cpp b/src/parser.cpp index 893ce9891..9e684028d 100644 --- a/src/parser.cpp +++ b/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; } 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..ad70905f0 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_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_context, "context"), \ - TOKEN_KIND(Token_asm, "asm"), \ TOKEN_KIND(Token__KeywordEnd, ""), \ TOKEN_KIND(Token_Count, "")