aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compile.c28
-rw-r--r--test/metamethods.tm13
-rw-r--r--typecheck.c13
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);