mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-06 07:38:48 +00:00
Selector Call Expressions: x->y(123) == x.y(x, 123)
This commit is contained in:
+27
-1984
File diff suppressed because it is too large
Load Diff
+168
-37
@@ -6525,7 +6525,7 @@ bool evaluate_where_clauses(CheckerContext *ctx, Ast *call_expr, Scope *scope, A
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type *proc_type, Ast *call) {
|
CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type *proc_type, Ast *call, Array<Ast *> const &args) {
|
||||||
ast_node(ce, CallExpr, call);
|
ast_node(ce, CallExpr, call);
|
||||||
|
|
||||||
CallArgumentCheckerType *call_checker = check_call_arguments_internal;
|
CallArgumentCheckerType *call_checker = check_call_arguments_internal;
|
||||||
@@ -6537,13 +6537,13 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
|
|||||||
if (is_call_expr_field_value(ce)) {
|
if (is_call_expr_field_value(ce)) {
|
||||||
call_checker = check_named_call_arguments;
|
call_checker = check_named_call_arguments;
|
||||||
|
|
||||||
operands = array_make<Operand>(heap_allocator(), ce->args.count);
|
operands = array_make<Operand>(heap_allocator(), args.count);
|
||||||
|
|
||||||
// NOTE(bill): This is give type hints for the named parameters
|
// NOTE(bill): This is give type hints for the named parameters
|
||||||
// in order to improve the type inference system
|
// in order to improve the type inference system
|
||||||
|
|
||||||
StringMap<Type *> type_hint_map = {}; // Key: String
|
StringMap<Type *> type_hint_map = {}; // Key: String
|
||||||
string_map_init(&type_hint_map, heap_allocator(), 2*ce->args.count);
|
string_map_init(&type_hint_map, heap_allocator(), 2*args.count);
|
||||||
defer (string_map_destroy(&type_hint_map));
|
defer (string_map_destroy(&type_hint_map));
|
||||||
|
|
||||||
Type *ptype = nullptr;
|
Type *ptype = nullptr;
|
||||||
@@ -6620,8 +6620,8 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
for_array(i, ce->args) {
|
for_array(i, args) {
|
||||||
Ast *arg = ce->args[i];
|
Ast *arg = args[i];
|
||||||
ast_node(fv, FieldValue, arg);
|
ast_node(fv, FieldValue, arg);
|
||||||
Ast *field = fv->field;
|
Ast *field = fv->field;
|
||||||
|
|
||||||
@@ -6637,7 +6637,7 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
|
|||||||
check_expr_or_type(c, &operands[i], fv->value, type_hint);
|
check_expr_or_type(c, &operands[i], fv->value, type_hint);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
operands = array_make<Operand>(heap_allocator(), 0, 2*ce->args.count);
|
operands = array_make<Operand>(heap_allocator(), 0, 2*args.count);
|
||||||
Entity **lhs = nullptr;
|
Entity **lhs = nullptr;
|
||||||
isize lhs_count = -1;
|
isize lhs_count = -1;
|
||||||
bool is_variadic = false;
|
bool is_variadic = false;
|
||||||
@@ -6645,7 +6645,7 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
|
|||||||
lhs = populate_proc_parameter_list(c, proc_type, &lhs_count, &is_variadic);
|
lhs = populate_proc_parameter_list(c, proc_type, &lhs_count, &is_variadic);
|
||||||
}
|
}
|
||||||
if (operand->mode != Addressing_ProcGroup) {
|
if (operand->mode != Addressing_ProcGroup) {
|
||||||
check_unpack_arguments(c, lhs, lhs_count, &operands, ce->args, false, is_variadic);
|
check_unpack_arguments(c, lhs, lhs_count, &operands, args, false, is_variadic);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6667,7 +6667,7 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
|
|||||||
isize lhs_count = -1;
|
isize lhs_count = -1;
|
||||||
bool is_variadic = false;
|
bool is_variadic = false;
|
||||||
lhs = populate_proc_parameter_list(c, e->type, &lhs_count, &is_variadic);
|
lhs = populate_proc_parameter_list(c, e->type, &lhs_count, &is_variadic);
|
||||||
check_unpack_arguments(c, lhs, lhs_count, &operands, ce->args, false, is_variadic);
|
check_unpack_arguments(c, lhs, lhs_count, &operands, args, false, is_variadic);
|
||||||
|
|
||||||
|
|
||||||
CallArgumentData data = {};
|
CallArgumentData data = {};
|
||||||
@@ -6740,7 +6740,7 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
check_unpack_arguments(c, lhs, lhs_count, &operands, ce->args, false, false);
|
check_unpack_arguments(c, lhs, lhs_count, &operands, args, false, false);
|
||||||
|
|
||||||
if (lhs != nullptr) {
|
if (lhs != nullptr) {
|
||||||
gb_free(heap_allocator(), lhs);
|
gb_free(heap_allocator(), lhs);
|
||||||
@@ -7269,33 +7269,36 @@ CallArgumentError check_polymorphic_record_type(CheckerContext *c, Operand *oper
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Type *type_hint) {
|
ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *proc, Array<Ast *> const &args, ProcInlining inlining, Type *type_hint) {
|
||||||
ast_node(ce, CallExpr, call);
|
if (proc != nullptr &&
|
||||||
if (ce->proc != nullptr &&
|
proc->kind == Ast_BasicDirective) {
|
||||||
ce->proc->kind == Ast_BasicDirective) {
|
ast_node(bd, BasicDirective, proc);
|
||||||
ast_node(bd, BasicDirective, ce->proc);
|
|
||||||
String name = bd->name;
|
String name = bd->name;
|
||||||
if (name == "location" || name == "assert" || name == "panic" || name == "defined" || name == "config" || name == "load") {
|
if (name == "location" || name == "assert" || name == "panic" || name == "defined" || name == "config" || name == "load") {
|
||||||
operand->mode = Addressing_Builtin;
|
operand->mode = Addressing_Builtin;
|
||||||
operand->builtin_id = BuiltinProc_DIRECTIVE;
|
operand->builtin_id = BuiltinProc_DIRECTIVE;
|
||||||
operand->expr = ce->proc;
|
operand->expr = proc;
|
||||||
operand->type = t_invalid;
|
operand->type = t_invalid;
|
||||||
add_type_and_value(&c->checker->info, ce->proc, operand->mode, operand->type, operand->value);
|
add_type_and_value(&c->checker->info, proc, operand->mode, operand->type, operand->value);
|
||||||
} else {
|
} else {
|
||||||
GB_PANIC("Unhandled #%.*s", LIT(name));
|
GB_PANIC("Unhandled #%.*s", LIT(name));
|
||||||
}
|
}
|
||||||
if (ce->inlining != ProcInlining_none) {
|
if (inlining != ProcInlining_none) {
|
||||||
error(call, "Inlining operators are not allowed on built-in procedures");
|
error(call, "Inlining operators are not allowed on built-in procedures");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
check_expr_or_type(c, operand, ce->proc);
|
if (proc != nullptr) {
|
||||||
|
check_expr_or_type(c, operand, proc);
|
||||||
|
} else {
|
||||||
|
GB_ASSERT(operand->expr != nullptr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ce->args.count > 0) {
|
if (args.count > 0) {
|
||||||
bool fail = false;
|
bool fail = false;
|
||||||
bool first_is_field_value = (ce->args[0]->kind == Ast_FieldValue);
|
bool first_is_field_value = (args[0]->kind == Ast_FieldValue);
|
||||||
for_array(i, ce->args) {
|
for_array(i, args) {
|
||||||
Ast *arg = ce->args[i];
|
Ast *arg = args[i];
|
||||||
bool mix = false;
|
bool mix = false;
|
||||||
if (first_is_field_value) {
|
if (first_is_field_value) {
|
||||||
mix = arg->kind != Ast_FieldValue;
|
mix = arg->kind != Ast_FieldValue;
|
||||||
@@ -7316,8 +7319,8 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Type *t
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (operand->mode == Addressing_Invalid) {
|
if (operand->mode == Addressing_Invalid) {
|
||||||
for_array(i, ce->args) {
|
for_array(i, args) {
|
||||||
Ast *arg = ce->args[i];
|
Ast *arg = args[i];
|
||||||
if (arg->kind == Ast_FieldValue) {
|
if (arg->kind == Ast_FieldValue) {
|
||||||
arg = arg->FieldValue.value;
|
arg = arg->FieldValue.value;
|
||||||
}
|
}
|
||||||
@@ -7352,12 +7355,12 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Type *t
|
|||||||
defer (gb_string_free(str));
|
defer (gb_string_free(str));
|
||||||
|
|
||||||
operand->mode = Addressing_Invalid;
|
operand->mode = Addressing_Invalid;
|
||||||
isize arg_count = ce->args.count;
|
isize arg_count = args.count;
|
||||||
switch (arg_count) {
|
switch (arg_count) {
|
||||||
case 0: error(call, "Missing argument in conversion to '%s'", str); break;
|
case 0: error(call, "Missing argument in conversion to '%s'", str); break;
|
||||||
default: error(call, "Too many arguments in conversion to '%s'", str); break;
|
default: error(call, "Too many arguments in conversion to '%s'", str); break;
|
||||||
case 1: {
|
case 1: {
|
||||||
Ast *arg = ce->args[0];
|
Ast *arg = args[0];
|
||||||
if (arg->kind == Ast_FieldValue) {
|
if (arg->kind == Ast_FieldValue) {
|
||||||
error(call, "'field = value' cannot be used in a type conversion");
|
error(call, "'field = value' cannot be used in a type conversion");
|
||||||
arg = arg->FieldValue.value;
|
arg = arg->FieldValue.value;
|
||||||
@@ -7412,7 +7415,7 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Type *t
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CallArgumentData data = check_call_arguments(c, operand, proc_type, call);
|
CallArgumentData data = check_call_arguments(c, operand, proc_type, call, args);
|
||||||
Type *result_type = data.result_type;
|
Type *result_type = data.result_type;
|
||||||
gb_zero_item(operand);
|
gb_zero_item(operand);
|
||||||
operand->expr = call;
|
operand->expr = call;
|
||||||
@@ -7460,15 +7463,17 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Type *t
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (ce->inlining) {
|
switch (inlining) {
|
||||||
case ProcInlining_inline: {
|
case ProcInlining_inline: {
|
||||||
Entity *e = entity_from_expr(ce->proc);
|
if (proc != nullptr) {
|
||||||
if (e != nullptr && e->kind == Entity_Procedure) {
|
Entity *e = entity_from_expr(proc);
|
||||||
DeclInfo *decl = e->decl_info;
|
if (e != nullptr && e->kind == Entity_Procedure) {
|
||||||
if (decl->proc_lit) {
|
DeclInfo *decl = e->decl_info;
|
||||||
ast_node(pl, ProcLit, decl->proc_lit);
|
if (decl->proc_lit) {
|
||||||
if (pl->inlining == ProcInlining_no_inline) {
|
ast_node(pl, ProcLit, decl->proc_lit);
|
||||||
error(call, "'inline' cannot be applied to a procedure that has be marked as 'no_inline'");
|
if (pl->inlining == ProcInlining_no_inline) {
|
||||||
|
error(call, "'inline' cannot be applied to a procedure that has be marked as 'no_inline'");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -9097,6 +9102,117 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
|
|||||||
node->viral_state_flags |= se->expr->viral_state_flags;
|
node->viral_state_flags |= se->expr->viral_state_flags;
|
||||||
case_end;
|
case_end;
|
||||||
|
|
||||||
|
case_ast_node(se, SelectorCallExpr, node);
|
||||||
|
// IMPORTANT NOTE(bill, 2020-05-22): This is a complete hack to get a shorthand which is extremely useful for vtables
|
||||||
|
// COM APIs is a great example of where this kind of thing is extremely useful
|
||||||
|
// General idea:
|
||||||
|
//
|
||||||
|
// x->y(123) == x.y(x, 123)
|
||||||
|
//
|
||||||
|
// How this has been implemented at the moment is quite hacky but it's done so to reduce need for huge backend changes
|
||||||
|
// Just regenerating a new AST aids things
|
||||||
|
//
|
||||||
|
// TODO(bill): Is this a good hack or not?
|
||||||
|
//
|
||||||
|
// NOTE(bill, 2020-05-22): I'm going to regret this decision, ain't I?
|
||||||
|
|
||||||
|
Operand x = {};
|
||||||
|
ExprKind kind = check_expr_base(c, &x, se->expr, nullptr);
|
||||||
|
if (x.mode == Addressing_Invalid || x.type == t_invalid) {
|
||||||
|
o->mode = Addressing_Invalid;
|
||||||
|
o->type = t_invalid;
|
||||||
|
o->expr = node;
|
||||||
|
return kind;
|
||||||
|
}
|
||||||
|
if (!is_type_proc(x.type)) {
|
||||||
|
gbString type_str = type_to_string(x.type);
|
||||||
|
error(se->call, "Selector call expressions expect a procedure type for the call, got '%s'", type_str);
|
||||||
|
gb_string_free(type_str);
|
||||||
|
|
||||||
|
o->mode = Addressing_Invalid;
|
||||||
|
o->type = t_invalid;
|
||||||
|
o->expr = node;
|
||||||
|
return Expr_Stmt;
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_node(ce, CallExpr, se->call);
|
||||||
|
|
||||||
|
GB_ASSERT(x.expr->kind == Ast_SelectorExpr);
|
||||||
|
|
||||||
|
Ast *first_arg = x.expr->SelectorExpr.expr;
|
||||||
|
GB_ASSERT(first_arg != nullptr);
|
||||||
|
|
||||||
|
Type *pt = base_type(x.type);
|
||||||
|
GB_ASSERT(pt->kind == Type_Proc);
|
||||||
|
Type *first_type = nullptr;
|
||||||
|
String first_arg_name = {};
|
||||||
|
if (pt->Proc.param_count > 0) {
|
||||||
|
Entity *f = pt->Proc.params->Tuple.variables[0];
|
||||||
|
first_type = f->type;
|
||||||
|
first_arg_name = f->token.string;
|
||||||
|
}
|
||||||
|
if (first_arg_name.len == 0) {
|
||||||
|
first_arg_name = str_lit("_");
|
||||||
|
}
|
||||||
|
|
||||||
|
Operand y = {};
|
||||||
|
y.mode = first_arg->tav.mode;
|
||||||
|
y.type = first_arg->tav.type;
|
||||||
|
y.value = first_arg->tav.value;
|
||||||
|
if (check_is_assignable_to(c, &y, first_type)) {
|
||||||
|
// Do nothing, it's valid
|
||||||
|
} else {
|
||||||
|
Operand z = y;
|
||||||
|
z.type = type_deref(y.type);
|
||||||
|
if (check_is_assignable_to(c, &z, first_type)) {
|
||||||
|
// NOTE(bill): AST GENERATION HACK!
|
||||||
|
Token op = {Token_Pointer};
|
||||||
|
first_arg = ast_deref_expr(first_arg->file, first_arg, op);
|
||||||
|
} else if (y.mode == Addressing_Variable) {
|
||||||
|
Operand w = y;
|
||||||
|
w.type = alloc_type_pointer(y.type);
|
||||||
|
if (check_is_assignable_to(c, &w, first_type)) {
|
||||||
|
// NOTE(bill): AST GENERATION HACK!
|
||||||
|
Token op = {Token_And};
|
||||||
|
first_arg = ast_unary_expr(first_arg->file, op, first_arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ce->args.count > 0) {
|
||||||
|
bool fail = false;
|
||||||
|
bool first_is_field_value = (ce->args[0]->kind == Ast_FieldValue);
|
||||||
|
for_array(i, ce->args) {
|
||||||
|
Ast *arg = ce->args[i];
|
||||||
|
bool mix = false;
|
||||||
|
if (first_is_field_value) {
|
||||||
|
mix = arg->kind != Ast_FieldValue;
|
||||||
|
} else {
|
||||||
|
mix = arg->kind == Ast_FieldValue;
|
||||||
|
}
|
||||||
|
if (mix) {
|
||||||
|
fail = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!fail && first_is_field_value) {
|
||||||
|
Token op = {Token_Eq};
|
||||||
|
AstFile *f = first_arg->file;
|
||||||
|
first_arg = ast_field_value(f, ast_ident(f, make_token_ident(first_arg_name)), first_arg, op);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
auto modified_args = array_make<Ast *>(heap_allocator(), ce->args.count+1);
|
||||||
|
modified_args[0] = first_arg;
|
||||||
|
array_copy(&modified_args, ce->args, 1);
|
||||||
|
ce->args = modified_args;
|
||||||
|
se->modified_call = true;
|
||||||
|
|
||||||
|
check_expr_with_type_hint(c, o, se->call, type_hint);
|
||||||
|
case_end;
|
||||||
|
|
||||||
|
|
||||||
case_ast_node(ise, ImplicitSelectorExpr, node);
|
case_ast_node(ise, ImplicitSelectorExpr, node);
|
||||||
o->type = t_invalid;
|
o->type = t_invalid;
|
||||||
@@ -9487,7 +9603,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
|
|||||||
|
|
||||||
|
|
||||||
case_ast_node(ce, CallExpr, node);
|
case_ast_node(ce, CallExpr, node);
|
||||||
return check_call_expr(c, o, node, type_hint);
|
return check_call_expr(c, o, node, ce->proc, ce->args, ce->inlining, type_hint);
|
||||||
case_end;
|
case_end;
|
||||||
|
|
||||||
case_ast_node(de, DerefExpr, node);
|
case_ast_node(de, DerefExpr, node);
|
||||||
@@ -9758,7 +9874,7 @@ gbString write_expr_to_string(gbString str, Ast *node) {
|
|||||||
|
|
||||||
case_ast_node(se, SelectorExpr, node);
|
case_ast_node(se, SelectorExpr, node);
|
||||||
str = write_expr_to_string(str, se->expr);
|
str = write_expr_to_string(str, se->expr);
|
||||||
str = gb_string_append_rune(str, '.');
|
str = string_append_token(str, se->token);
|
||||||
str = write_expr_to_string(str, se->selector);
|
str = write_expr_to_string(str, se->selector);
|
||||||
case_end;
|
case_end;
|
||||||
|
|
||||||
@@ -9767,6 +9883,21 @@ gbString write_expr_to_string(gbString str, Ast *node) {
|
|||||||
str = write_expr_to_string(str, se->selector);
|
str = write_expr_to_string(str, se->selector);
|
||||||
case_end;
|
case_end;
|
||||||
|
|
||||||
|
case_ast_node(se, SelectorCallExpr, node);
|
||||||
|
str = write_expr_to_string(str, se->expr);
|
||||||
|
str = gb_string_appendc(str, "(");
|
||||||
|
ast_node(ce, CallExpr, se->call);
|
||||||
|
isize start = se->modified_call ? 1 : 0;
|
||||||
|
for (isize i = start; i < ce->args.count; i++) {
|
||||||
|
Ast *arg = ce->args[i];
|
||||||
|
if (i > start) {
|
||||||
|
str = gb_string_appendc(str, ", ");
|
||||||
|
}
|
||||||
|
str = write_expr_to_string(str, arg);
|
||||||
|
}
|
||||||
|
str = gb_string_appendc(str, ")");
|
||||||
|
case_end;
|
||||||
|
|
||||||
case_ast_node(ta, TypeAssertion, node);
|
case_ast_node(ta, TypeAssertion, node);
|
||||||
str = write_expr_to_string(str, ta->expr);
|
str = write_expr_to_string(str, ta->expr);
|
||||||
str = gb_string_appendc(str, ".(");
|
str = gb_string_appendc(str, ".(");
|
||||||
|
|||||||
@@ -1272,6 +1272,18 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
} else if (operand.expr->kind == Ast_SelectorCallExpr) {
|
||||||
|
AstSelectorCallExpr *se = &operand.expr->SelectorCallExpr;
|
||||||
|
ast_node(ce, CallExpr, se->call);
|
||||||
|
Type *t = type_of_expr(ce->proc);
|
||||||
|
if (is_type_proc(t)) {
|
||||||
|
if (t->Proc.require_results) {
|
||||||
|
gbString expr_str = expr_to_string(ce->proc);
|
||||||
|
error(node, "'%s' requires that its results must be handled", expr_str);
|
||||||
|
gb_string_free(expr_str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
gbString expr_str = expr_to_string(operand.expr);
|
gbString expr_str = expr_to_string(operand.expr);
|
||||||
error(node, "Expression is not used: '%s'", expr_str);
|
error(node, "Expression is not used: '%s'", expr_str);
|
||||||
|
|||||||
@@ -7678,6 +7678,13 @@ irValue *ir_build_expr_internal(irProcedure *proc, Ast *expr) {
|
|||||||
return ir_add_module_constant(proc->module, tv.type, tv.value);
|
return ir_add_module_constant(proc->module, tv.type, tv.value);
|
||||||
case_end;
|
case_end;
|
||||||
|
|
||||||
|
case_ast_node(se, SelectorCallExpr, expr);
|
||||||
|
GB_ASSERT(se->modified_call);
|
||||||
|
TypeAndValue tav = type_and_value_of_expr(expr);
|
||||||
|
GB_ASSERT(tav.mode != Addressing_Invalid);
|
||||||
|
return ir_addr_load(proc, ir_build_addr(proc, se->call));
|
||||||
|
case_end;
|
||||||
|
|
||||||
case_ast_node(te, TernaryExpr, expr);
|
case_ast_node(te, TernaryExpr, expr);
|
||||||
ir_emit_comment(proc, str_lit("TernaryExpr"));
|
ir_emit_comment(proc, str_lit("TernaryExpr"));
|
||||||
|
|
||||||
|
|||||||
@@ -9025,6 +9025,13 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) {
|
|||||||
return lb_const_value(p->module, tv.type, tv.value);
|
return lb_const_value(p->module, tv.type, tv.value);
|
||||||
case_end;
|
case_end;
|
||||||
|
|
||||||
|
case_ast_node(se, SelectorCallExpr, expr);
|
||||||
|
GB_ASSERT(se->modified_call);
|
||||||
|
TypeAndValue tav = type_and_value_of_expr(expr);
|
||||||
|
GB_ASSERT(tav.mode != Addressing_Invalid);
|
||||||
|
return lb_addr_load(p, lb_build_addr(p, se->call));
|
||||||
|
case_end;
|
||||||
|
|
||||||
case_ast_node(te, TernaryExpr, expr);
|
case_ast_node(te, TernaryExpr, expr);
|
||||||
LLVMValueRef incoming_values[2] = {};
|
LLVMValueRef incoming_values[2] = {};
|
||||||
LLVMBasicBlockRef incoming_blocks[2] = {};
|
LLVMBasicBlockRef incoming_blocks[2] = {};
|
||||||
|
|||||||
+22
-2
@@ -24,6 +24,11 @@ Token ast_token(Ast *node) {
|
|||||||
return ast_token(node->SelectorExpr.selector);
|
return ast_token(node->SelectorExpr.selector);
|
||||||
}
|
}
|
||||||
return node->SelectorExpr.token;
|
return node->SelectorExpr.token;
|
||||||
|
case Ast_SelectorCallExpr:
|
||||||
|
if (node->SelectorCallExpr.expr != nullptr) {
|
||||||
|
return ast_token(node->SelectorCallExpr.expr);
|
||||||
|
}
|
||||||
|
return node->SelectorCallExpr.token;
|
||||||
case Ast_ImplicitSelectorExpr:
|
case Ast_ImplicitSelectorExpr:
|
||||||
if (node->ImplicitSelectorExpr.selector != nullptr) {
|
if (node->ImplicitSelectorExpr.selector != nullptr) {
|
||||||
return ast_token(node->ImplicitSelectorExpr.selector);
|
return ast_token(node->ImplicitSelectorExpr.selector);
|
||||||
@@ -174,6 +179,10 @@ Ast *clone_ast(Ast *node) {
|
|||||||
case Ast_ImplicitSelectorExpr:
|
case Ast_ImplicitSelectorExpr:
|
||||||
n->ImplicitSelectorExpr.selector = clone_ast(n->ImplicitSelectorExpr.selector);
|
n->ImplicitSelectorExpr.selector = clone_ast(n->ImplicitSelectorExpr.selector);
|
||||||
break;
|
break;
|
||||||
|
case Ast_SelectorCallExpr:
|
||||||
|
n->SelectorCallExpr.expr = clone_ast(n->SelectorCallExpr.expr);
|
||||||
|
n->SelectorCallExpr.call = clone_ast(n->SelectorCallExpr.call);
|
||||||
|
break;
|
||||||
case Ast_IndexExpr:
|
case Ast_IndexExpr:
|
||||||
n->IndexExpr.expr = clone_ast(n->IndexExpr.expr);
|
n->IndexExpr.expr = clone_ast(n->IndexExpr.expr);
|
||||||
n->IndexExpr.index = clone_ast(n->IndexExpr.index);
|
n->IndexExpr.index = clone_ast(n->IndexExpr.index);
|
||||||
@@ -546,6 +555,14 @@ Ast *ast_implicit_selector_expr(AstFile *f, Token token, Ast *selector) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ast *ast_selector_call_expr(AstFile *f, Token token, Ast *expr, Ast *call) {
|
||||||
|
Ast *result = alloc_ast_node(f, Ast_SelectorCallExpr);
|
||||||
|
result->SelectorCallExpr.token = token;
|
||||||
|
result->SelectorCallExpr.expr = expr;
|
||||||
|
result->SelectorCallExpr.call = call;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Ast *ast_index_expr(AstFile *f, Ast *expr, Ast *index, Token open, Token close) {
|
Ast *ast_index_expr(AstFile *f, Ast *expr, Ast *index, Token open, Token close) {
|
||||||
Ast *result = alloc_ast_node(f, Ast_IndexExpr);
|
Ast *result = alloc_ast_node(f, Ast_IndexExpr);
|
||||||
@@ -2417,8 +2434,11 @@ Ast *parse_atom_expr(AstFile *f, Ast *operand, bool lhs) {
|
|||||||
|
|
||||||
case Token_ArrowRight: {
|
case Token_ArrowRight: {
|
||||||
Token token = advance_token(f);
|
Token token = advance_token(f);
|
||||||
syntax_error(token, "Selector expressions use '.' rather than '->'");
|
// syntax_error(token, "Selector expressions use '.' rather than '->'");
|
||||||
operand = ast_selector_expr(f, token, operand, parse_ident(f));
|
|
||||||
|
Ast *sel = ast_selector_expr(f, token, operand, parse_ident(f));
|
||||||
|
Ast *call = parse_call_expr(f, sel);
|
||||||
|
operand = ast_selector_call_expr(f, token, sel, call);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -271,6 +271,7 @@ AST_KIND(_ExprBegin, "", bool) \
|
|||||||
AST_KIND(ParenExpr, "parentheses expression", struct { Ast *expr; Token open, close; }) \
|
AST_KIND(ParenExpr, "parentheses expression", struct { Ast *expr; Token open, close; }) \
|
||||||
AST_KIND(SelectorExpr, "selector expression", struct { Token token; Ast *expr, *selector; }) \
|
AST_KIND(SelectorExpr, "selector expression", struct { Token token; Ast *expr, *selector; }) \
|
||||||
AST_KIND(ImplicitSelectorExpr, "implicit selector expression", struct { Token token; Ast *selector; }) \
|
AST_KIND(ImplicitSelectorExpr, "implicit selector expression", struct { Token token; Ast *selector; }) \
|
||||||
|
AST_KIND(SelectorCallExpr, "selector call expression", struct { Token token; Ast *expr, *call; bool modified_call; }) \
|
||||||
AST_KIND(IndexExpr, "index expression", struct { Ast *expr, *index; Token open, close; }) \
|
AST_KIND(IndexExpr, "index expression", struct { Ast *expr, *index; Token open, close; }) \
|
||||||
AST_KIND(DerefExpr, "dereference expression", struct { Token op; Ast *expr; }) \
|
AST_KIND(DerefExpr, "dereference expression", struct { Token op; Ast *expr; }) \
|
||||||
AST_KIND(SliceExpr, "slice expression", struct { \
|
AST_KIND(SliceExpr, "slice expression", struct { \
|
||||||
|
|||||||
Reference in New Issue
Block a user