From aa14fd1b0586735a5cd442de55db004fd5dfc92b Mon Sep 17 00:00:00 2001 From: playX Date: Fri, 13 Aug 2021 10:06:59 +0300 Subject: [PATCH] v.gen.js: support operator overloading (#11171) --- vlib/v/gen/js/js.v | 107 ++++++++++++++----- vlib/v/gen/js/tests/testdata/overloading.out | 3 + vlib/v/gen/js/tests/testdata/overloading.v | 24 +++++ 3 files changed, 107 insertions(+), 27 deletions(-) create mode 100644 vlib/v/gen/js/tests/testdata/overloading.out create mode 100644 vlib/v/gen/js/tests/testdata/overloading.v diff --git a/vlib/v/gen/js/js.v b/vlib/v/gen/js/js.v index 458d9db882..d86e34e382 100644 --- a/vlib/v/gen/js/js.v +++ b/vlib/v/gen/js/js.v @@ -428,6 +428,9 @@ fn (mut g JsGen) js_name(name_ string) string { '*' { '\$mul' } + '%' { + '\$mod' + } '==' { 'eq' } @@ -1769,8 +1772,14 @@ fn (mut g JsGen) gen_infix_expr(it ast.InfixExpr) { } if it.op == .eq || it.op == .ne { - // Shallow equatables - if l_sym.kind in js.shallow_equatables && r_sym.kind in js.shallow_equatables { + has_operator_overloading := g.table.type_has_method(l_sym, '==') + if has_operator_overloading { + g.expr(it.left) + g.write('.eq(') + g.expr(it.right) + g.write(')') + // Shallow equatables + } else if l_sym.kind in js.shallow_equatables && r_sym.kind in js.shallow_equatables { // wrap left expr in parens so binary operations will work correctly. g.write('(') g.expr(it.left) @@ -1816,35 +1825,79 @@ fn (mut g JsGen) gen_infix_expr(it ast.InfixExpr) { g.expr(it.left) g.write(' instanceof ') g.write(g.typ(it.right_type)) + } else if it.op in [.lt, .gt, .ge, .le] && g.table.type_has_method(l_sym, '<') + && l_sym.kind == r_sym.kind { + if it.op in [.le, .ge] { + g.write('!') + } + if it.op in [.lt, .ge] { + g.expr(it.left) + g.write('.\$lt (') + g.expr(it.right) + g.write(')') + } else { + g.expr(it.right) + g.write('.\$lt (') + g.expr(it.left) + g.write(')') + } } else { is_arithmetic := it.op in [token.Kind.plus, .minus, .mul, .div, .mod] - - mut greater_typ := 0 - // todo(playX): looks like this cast is always required to perform .eq operation on types. - if is_arithmetic { - greater_typ = g.greater_typ(it.left_type, it.right_type) - if g.cast_stack.len > 0 { - // needs_cast = g.cast_stack.last() != greater_typ + has_operator_overloading := g.table.type_has_method(l_sym, it.op.str()) + if has_operator_overloading { + g.expr(it.left) + name := match it.op.str() { + '+' { + '\$add' + } + '-' { + '\$sub' + } + '/' { + '\$div' + } + '*' { + '\$mul' + } + '%' { + '\$mod' + } + else { + panic('unreachable') + '' + } } - } - - if is_arithmetic { - if g.ns.name == 'builtin' { - g.write('new ') - } - g.write('${g.typ(greater_typ)}(') - g.cast_stack << greater_typ - } - - g.expr(it.left) - - g.write(' $it.op ') - - g.expr(it.right) - - if is_arithmetic { - g.cast_stack.delete_last() + g.write('.$name (') + g.expr(it.right) g.write(')') + } else { + mut greater_typ := 0 + // todo(playX): looks like this cast is always required to perform .eq operation on types. + if is_arithmetic { + greater_typ = g.greater_typ(it.left_type, it.right_type) + if g.cast_stack.len > 0 { + // needs_cast = g.cast_stack.last() != greater_typ + } + } + + if is_arithmetic { + if g.ns.name == 'builtin' { + g.write('new ') + } + g.write('${g.typ(greater_typ)}(') + g.cast_stack << greater_typ + } + + g.expr(it.left) + + g.write(' $it.op ') + + g.expr(it.right) + + if is_arithmetic { + g.cast_stack.delete_last() + g.write(')') + } } } diff --git a/vlib/v/gen/js/tests/testdata/overloading.out b/vlib/v/gen/js/tests/testdata/overloading.out new file mode 100644 index 0000000000..11dc3d88ce --- /dev/null +++ b/vlib/v/gen/js/tests/testdata/overloading.out @@ -0,0 +1,3 @@ +Foo { x: 5 , y: 5 } +true +true \ No newline at end of file diff --git a/vlib/v/gen/js/tests/testdata/overloading.v b/vlib/v/gen/js/tests/testdata/overloading.v new file mode 100644 index 0000000000..c061bc5306 --- /dev/null +++ b/vlib/v/gen/js/tests/testdata/overloading.v @@ -0,0 +1,24 @@ +struct Foo { + x f32 + y f32 +} + +pub fn (x Foo) == (y Foo) bool { + return x.x == y.y +} + +pub fn (x Foo) < (y Foo) bool { + return x.x < y.x && x.y < y.y +} + +pub fn (a Foo) + (b Foo) Foo { + return Foo{a.x + b.x, a.y + b.y} +} + +fn main() { + x := Foo{4.0, 3.0} + y := Foo{1.0, 2.0} + println(x + y) + println(Foo{42.42, 0.0} == Foo{0.0, 42.42}) + println(x > y) +}