From 22063462a90b5c290f5f71319b259fca80da8097 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Thu, 4 Jul 2024 13:37:23 -0400 Subject: Add __length and __negative metamethods --- compile.c | 28 ++++++++++++++++++++++++++-- test/metamethods.tm | 13 +++++++++++++ typecheck.c | 13 +++++++++++-- 3 files changed, 50 insertions(+), 4 deletions(-) diff --git a/compile.c b/compile.c index bfd23925..d314e40c 100644 --- a/compile.c +++ b/compile.c @@ -1131,7 +1131,16 @@ CORD compile(env_t *env, ast_t *ast) return CORD_all("I64((", table, ").entries.length)"); } } - default: code_err(ast, "Length is only supported for strings, arrays, and tables, not: %T", t); + default: { + binding_t *b = get_namespace_binding(env, expr, "__length"); + if (b && b->type->tag == FunctionType) { + auto fn = Match(b->type, FunctionType); + if (type_eq(fn->ret, INT_TYPE) && fn->args && can_promote(t, get_arg_type(env, fn->args))) + return CORD_all(b->code, "(", compile_arguments(env, ast, fn->args, new(arg_ast_t, .value=expr)), ")"); + } + + code_err(ast, "Length is not implemented for %T values", t); + } } break; } @@ -1149,7 +1158,22 @@ CORD compile(env_t *env, ast_t *ast) else code_err(ast, "I don't know how to negate values of type %T", t); } - case Negative: return CORD_asprintf("-(%r)", compile(env, Match(ast, Negative)->value)); + case Negative: { + ast_t *value = Match(ast, Negative)->value; + type_t *t = get_type(env, value); + if (t->tag == IntType || t->tag == NumType) + return CORD_all("-(", compile(env, value), ")"); + + binding_t *b = get_namespace_binding(env, value, "__negative"); + if (b && b->type->tag == FunctionType) { + auto fn = Match(b->type, FunctionType); + if (fn->args && can_promote(t, get_arg_type(env, fn->args))) + return CORD_all(b->code, "(", compile_arguments(env, ast, fn->args, new(arg_ast_t, .value=value)), ")"); + } + + code_err(ast, "I don't know how to get the negative value of type %T", t); + + } case HeapAllocate: return CORD_asprintf("heap(%r)", compile(env, Match(ast, HeapAllocate)->value)); case StackReference: { ast_t *subject = Match(ast, StackReference)->value; diff --git a/test/metamethods.tm b/test/metamethods.tm index 97d9e5eb..26a5abd6 100644 --- a/test/metamethods.tm +++ b/test/metamethods.tm @@ -17,6 +17,12 @@ struct Vec2(x,y:Int): func __mul4(a,b:Vec2; inline)->Vec2: return Vec2(a.x*b.x, a.y*b.y) + func __negative(v:Vec2; inline)->Vec2: + return Vec2(-v.x, -v.y) + + func __length(v:Vec2; inline)->Int: + return 2 + func main(): >> x := Vec2(10, 20) >> y := Vec2(100, 200) @@ -36,3 +42,10 @@ func main(): = Vec2(x=11, y=22) >> x *= Vec2(10, -1) = Vec2(x=110, y=-22) + + >> x = Vec2(1, 2) + >> -x + = Vec2(x=-1, y=-2) + >> #x + = 2 + diff --git a/typecheck.c b/typecheck.c index 6a4dbaca..d21d28ad 100644 --- a/typecheck.c +++ b/typecheck.c @@ -741,10 +741,19 @@ type_t *get_type(env_t *env, ast_t *ast) case Pass: return Type(VoidType); case Length: return Type(IntType, .bits=64); case Negative: { - type_t *t = get_type(env, Match(ast, Negative)->value); + ast_t *value = Match(ast, Negative)->value; + type_t *t = get_type(env, value); if (t->tag == IntType || t->tag == NumType) return t; - code_err(ast, "I only know how to get negatives of numeric types, not %T", t); + + binding_t *b = get_namespace_binding(env, value, "__negative"); + if (b && b->type->tag == FunctionType) { + auto fn = Match(b->type, FunctionType); + if (fn->args && can_promote(t, get_arg_type(env, fn->args))) + return fn->ret; + } + + code_err(ast, "I don't know how to get the negative value of type %T", t); } case Not: { type_t *t = get_type(env, Match(ast, Not)->value); -- cgit v1.2.3