diff options
| author | Bruce Hill <bruce@bruce-hill.com> | 2024-07-04 13:37:23 -0400 |
|---|---|---|
| committer | Bruce Hill <bruce@bruce-hill.com> | 2024-07-04 13:37:23 -0400 |
| commit | 22063462a90b5c290f5f71319b259fca80da8097 (patch) | |
| tree | 67d403f893415de9202477608ae9972b9171df88 | |
| parent | dc1616a3bf7d075e0b50f783313ea6a5b1076fb4 (diff) | |
Add __length and __negative metamethods
| -rw-r--r-- | compile.c | 28 | ||||
| -rw-r--r-- | test/metamethods.tm | 13 | ||||
| -rw-r--r-- | typecheck.c | 13 |
3 files changed, 50 insertions, 4 deletions
@@ -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); |
