588 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			V
		
	
	
			
		
		
	
	
			588 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			V
		
	
	
| import simplemodule
 | |
| 
 | |
| fn simple<T>(p T) T {
 | |
| 	return p
 | |
| }
 | |
| 
 | |
| fn test_identity() {
 | |
| 	assert simple<int>(1) == 1
 | |
| 	assert simple<int>(1 + 0) == 1
 | |
| 	assert simple<string>('g') == 'g'
 | |
| 	assert simple<string>('g') + 'h' == 'gh'
 | |
| 
 | |
| 	assert simple<[]int>([1])[0] == 1
 | |
| 	assert simple<map[string]string>({
 | |
| 		'a': 'b'
 | |
| 	})['a'] == 'b'
 | |
| 
 | |
| 	assert simple<simplemodule.Data>(simplemodule.Data{ value: 8 }).value == 8
 | |
| 	x := &simplemodule.Data{
 | |
| 		value: 123
 | |
| 	}
 | |
| 	assert simple<&simplemodule.Data>(x).value == 123
 | |
| }
 | |
| 
 | |
| fn plus<T>(xxx T, b T) T {
 | |
| 	// x := a
 | |
| 	// y := b
 | |
| 	// ww := ww
 | |
| 	// q := xx + 1
 | |
| 	return xxx + b
 | |
| }
 | |
| 
 | |
| fn test_infix_expr() {
 | |
| 	a := plus<int>(2, 3)
 | |
| 	assert a == 5
 | |
| 	assert plus<int>(10, 1) == 11
 | |
| 	assert plus<string>('a', 'b') == 'ab'
 | |
| }
 | |
| 
 | |
| fn plus_one<T>(a T) T {
 | |
| 	mut b := a
 | |
| 	b++
 | |
| 	return b
 | |
| }
 | |
| 
 | |
| fn minus_one<T>(a T) T {
 | |
| 	mut b := a
 | |
| 	b--
 | |
| 	return b
 | |
| }
 | |
| 
 | |
| fn test_postfix_expr() {
 | |
| 	assert plus_one(-1) == 0
 | |
| 	assert plus_one(u8(0)) == 1
 | |
| 	assert plus_one(u16(1)) == 2
 | |
| 	assert plus_one(u32(2)) == 3
 | |
| 	assert plus_one(u64(3)) == 4
 | |
| 	assert plus_one(i8(-10)) == -9
 | |
| 	assert plus_one(i16(-9)) == -8
 | |
| 	assert plus_one(int(-8)) == -7
 | |
| 	assert plus_one(i64(-7)) == -6
 | |
| 	assert minus_one(0) == -1
 | |
| 	assert minus_one(u8(1)) == 0
 | |
| 	assert minus_one(u16(2)) == 1
 | |
| 	assert minus_one(u32(3)) == 2
 | |
| 	assert minus_one(u64(4)) == 3
 | |
| 	assert minus_one(i8(-8)) == -9
 | |
| 	assert minus_one(i16(-7)) == -8
 | |
| 	assert minus_one(int(-6)) == -7
 | |
| 	assert minus_one(i64(-5)) == -6
 | |
| 
 | |
| 	// the point is to see if it compiles, more than if the result
 | |
| 	// is correct, so 1e-6 isn't necessarily the right value to do this
 | |
| 	// but it's not important
 | |
| 	delta := 1e-6
 | |
| 	assert plus_one(1.1) - 2.1 < delta
 | |
| 	assert plus_one(f32(2.2)) - 3.2 < delta
 | |
| 	assert plus_one(f64(3.3)) - 4.3 < delta
 | |
| 	assert minus_one(1.1) - 0.1 < delta
 | |
| 	assert minus_one(f32(2.2)) - 1.2 < delta
 | |
| 	assert minus_one(f64(3.3)) - 2.3 < delta
 | |
| }
 | |
| 
 | |
| fn sum<T>(l []T) T {
 | |
| 	mut r := T(0)
 | |
| 	for e in l {
 | |
| 		r += e
 | |
| 	}
 | |
| 	return r
 | |
| }
 | |
| 
 | |
| fn test_array() {
 | |
| 	b := [1, 2, 3]
 | |
| 	assert sum(b) == 6
 | |
| }
 | |
| 
 | |
| fn max<T>(brug string, a ...T) T {
 | |
| 	mut max := a[0]
 | |
| 	for item in a[1..] {
 | |
| 		if max < item {
 | |
| 			max = item
 | |
| 		}
 | |
| 	}
 | |
| 	return max
 | |
| }
 | |
| 
 | |
| fn test_generic_variadic() {
 | |
| 	assert max('krkr', 1, 2, 3, 4) == 4
 | |
| 	a := [f64(1.2), 3.2, 0.1, 2.2]
 | |
| 	assert max('krkr', ...a) == 3.2
 | |
| 	assert max('krkr', ...[u8(4), 3, 2, 1]) == 4
 | |
| }
 | |
| 
 | |
| fn create<T>() {
 | |
| 	_ := T{}
 | |
| 	mut xx := T{}
 | |
| 	xx.name = 'foo'
 | |
| 	assert xx.name == 'foo'
 | |
| 	xx.init()
 | |
| }
 | |
| 
 | |
| struct User {
 | |
| mut:
 | |
| 	name string
 | |
| }
 | |
| 
 | |
| struct City {
 | |
| mut:
 | |
| 	name string
 | |
| }
 | |
| 
 | |
| fn (u User) init() {
 | |
| }
 | |
| 
 | |
| fn (c City) init() {
 | |
| }
 | |
| 
 | |
| fn mut_arg<T>(mut x T) {
 | |
| 	// println(x.name) // = 'foo'
 | |
| }
 | |
| 
 | |
| fn mut_arg2<T>(mut x T) T {
 | |
| 	// println(x.name) // = 'foo'
 | |
| 	return *x
 | |
| }
 | |
| 
 | |
| fn test_create() {
 | |
| 	create<User>()
 | |
| 	create<City>()
 | |
| 	mut u := User{}
 | |
| 	mut_arg<User>(mut u)
 | |
| 	mut_arg2<User>(mut u)
 | |
| }
 | |
| 
 | |
| fn return_array<T>(arr []T) []T {
 | |
| 	return arr
 | |
| }
 | |
| 
 | |
| fn test_return_array() {
 | |
| 	a1 := return_array<int>([1, 2, 3])
 | |
| 	assert a1 == [1, 2, 3]
 | |
| 	a2 := return_array<f64>([1.1, 2.2, 3.3])
 | |
| 	assert a2 == [1.1, 2.2, 3.3]
 | |
| 	a3 := return_array<string>(['a', 'b', 'c'])
 | |
| 	assert a3 == ['a', 'b', 'c']
 | |
| 	a4 := return_array<bool>([true, false, true])
 | |
| 	assert a4 == [true, false, true]
 | |
| }
 | |
| 
 | |
| fn opt<T>(v T) ?T {
 | |
| 	if sizeof(T) > 1 {
 | |
| 		return v
 | |
| 	}
 | |
| 	return none
 | |
| }
 | |
| 
 | |
| fn test_optional() {
 | |
| 	s := opt('hi') or { '' }
 | |
| 	assert s == 'hi'
 | |
| 	i := opt(5) or { 0 }
 | |
| 	assert i == 5
 | |
| 	b := opt(s[0]) or { 99 }
 | |
| 	assert b == 99
 | |
| }
 | |
| 
 | |
| fn ptr<T>(v T) &T {
 | |
| 	a := [v]
 | |
| 	return a.data
 | |
| }
 | |
| 
 | |
| fn test_ptr() {
 | |
| 	assert *ptr(4) == 4
 | |
| 	assert *ptr('aa') == 'aa'
 | |
| }
 | |
| 
 | |
| fn map_f<T, U>(l []T, f fn (T) U) []U {
 | |
| 	mut r := []U{}
 | |
| 	for e in l {
 | |
| 		r << f(e)
 | |
| 	}
 | |
| 	return r
 | |
| }
 | |
| 
 | |
| /*
 | |
| fn foldl<T>(l []T, nil T, f fn(T,T)T) T {
 | |
| 	mut r := nil
 | |
| 	for e in l {
 | |
| 		r = f(r, e)
 | |
| 	}
 | |
| 	return r
 | |
| }
 | |
| */
 | |
| fn square(x int) int {
 | |
| 	return x * x
 | |
| }
 | |
| 
 | |
| fn mul_int(x int, y int) int {
 | |
| 	return x * y
 | |
| }
 | |
| 
 | |
| fn assert_eq<T>(a T, b T) {
 | |
| 	r := a == b
 | |
| 	assert r
 | |
| }
 | |
| 
 | |
| fn print_nice<T>(x T, indent int) string {
 | |
| 	mut space := ''
 | |
| 	for _ in 0 .. indent {
 | |
| 		space = space + ' '
 | |
| 	}
 | |
| 	return '$space$x'
 | |
| }
 | |
| 
 | |
| fn test_generic_fn() {
 | |
| 	assert_eq(simple(0 + 1), 1)
 | |
| 	assert_eq(simple('g') + 'h', 'gh')
 | |
| 	assert_eq(sum([5.1, 6.2, 7.0]), 18.3)
 | |
| 	assert_eq(plus(i64(4), i64(6)), i64(10))
 | |
| 	a := [1, 2, 3, 4]
 | |
| 	b := map_f(a, square)
 | |
| 	assert_eq(sum(b), 30) // 1+4+9+16 = 30
 | |
| 	// assert_eq(foldl(b, 1, mul_int), 576)   // 1*4*9*16 = 576
 | |
| 	assert print_nice('str', 8) == '        str'
 | |
| }
 | |
| 
 | |
| struct Point {
 | |
| mut:
 | |
| 	x f64
 | |
| 	y f64
 | |
| }
 | |
| 
 | |
| fn (mut p Point) translate<T>(x T, y T) {
 | |
| 	p.x += x
 | |
| 	p.y += y
 | |
| }
 | |
| 
 | |
| fn test_generic_method() {
 | |
| 	mut p := Point{}
 | |
| 	p.translate(2, 1.0)
 | |
| 	assert p.x == 2.0 && p.y == 1.0
 | |
| }
 | |
| 
 | |
| fn get_values<T>(i T) []T {
 | |
| 	return [i]
 | |
| }
 | |
| 
 | |
| fn test_generic_fn_in_for_in_expression() {
 | |
| 	for value in get_values(1) {
 | |
| 		assert value == 1
 | |
| 	}
 | |
| 
 | |
| 	for i, val in get_values(0) {
 | |
| 		assert i == val
 | |
| 	}
 | |
| 
 | |
| 	for value in get_values('a') {
 | |
| 		assert value == 'a'
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // test generic struct
 | |
| struct DB {
 | |
| 	driver string
 | |
| }
 | |
| 
 | |
| struct Group {
 | |
| pub mut:
 | |
| 	name       string
 | |
| 	group_name string
 | |
| }
 | |
| 
 | |
| struct Permission {
 | |
| pub mut:
 | |
| 	name string
 | |
| }
 | |
| 
 | |
| struct Repo<T, U> {
 | |
| 	db DB
 | |
| pub mut:
 | |
| 	model      T
 | |
| 	permission U
 | |
| }
 | |
| 
 | |
| // TODO: multiple type generic struct  needs fixing in return for fn
 | |
| // fn new_repo<T>(db DB) Repo<T,U> {
 | |
| // return Repo<T,Permission>{db: db}
 | |
| // }
 | |
| fn test_generic_struct() {
 | |
| 	mut a := Repo<User, Permission>{
 | |
| 		model: User{
 | |
| 			name: 'joe'
 | |
| 		}
 | |
| 	}
 | |
| 	assert a.model.name == 'joe'
 | |
| 	mut b := Repo<Group, Permission>{
 | |
| 		permission: Permission{
 | |
| 			name: 'superuser'
 | |
| 		}
 | |
| 	}
 | |
| 	b.model.name = 'admins'
 | |
| 	assert b.model.name == 'admins'
 | |
| 	assert b.permission.name == 'superuser'
 | |
| 	assert typeof(a.model).name == 'User'
 | |
| 	assert typeof(b.model).name == 'Group'
 | |
| }
 | |
| 
 | |
| struct Foo<T> {
 | |
| pub:
 | |
| 	data T
 | |
| }
 | |
| 
 | |
| fn (f Foo<int>) value() string {
 | |
| 	return f.data.str()
 | |
| }
 | |
| 
 | |
| fn test_generic_struct_method() {
 | |
| 	foo_int := Foo<int>{2}
 | |
| 	assert foo_int.value() == '2'
 | |
| }
 | |
| 
 | |
| fn test_struct_from_other_module() {
 | |
| 	g := simplemodule.ThisIsGeneric<Permission>{}
 | |
| 	assert g.msg.name == ''
 | |
| }
 | |
| 
 | |
| fn test_generic_struct_print_array_as_field() {
 | |
| 	foo := Foo<[]string>{
 | |
| 		data: []string{}
 | |
| 	}
 | |
| 	assert foo.str() == 'Foo<[]string>{\n    data: []\n}'
 | |
| }
 | |
| 
 | |
| struct Abc {
 | |
| 	x int
 | |
| 	y int
 | |
| 	z int
 | |
| }
 | |
| 
 | |
| fn p<T>(args ...T) {
 | |
| 	size := sizeof(T)
 | |
| 	print('p called with size: ${size:3} | ')
 | |
| 	for _, x in args {
 | |
| 		print(x)
 | |
| 		print(' ')
 | |
| 	}
 | |
| 	println('')
 | |
| 	assert true
 | |
| }
 | |
| 
 | |
| fn test_generic_fn_with_variadics() {
 | |
| 	s := 'abc'
 | |
| 	i := 1
 | |
| 	abc := Abc{1, 2, 3}
 | |
| 
 | |
| 	// these calls should all compile, and print the arguments,
 | |
| 	// even though the arguments are all a different type and arity:
 | |
| 	p(s)
 | |
| 	p(i)
 | |
| 	p(abc)
 | |
| 	p('Good', 'morning', 'world')
 | |
| }
 | |
| 
 | |
| struct Context {}
 | |
| 
 | |
| struct App {
 | |
| mut:
 | |
| 	context Context
 | |
| }
 | |
| 
 | |
| fn test<T>(mut app T) {
 | |
| 	nested_test<T>(mut app)
 | |
| }
 | |
| 
 | |
| fn nested_test<T>(mut app T) {
 | |
| 	app.context = Context{}
 | |
| }
 | |
| 
 | |
| fn test_pass_generic_to_nested_function() {
 | |
| 	mut app := App{}
 | |
| 	test(mut app)
 | |
| }
 | |
| 
 | |
| fn generic_return_map<M>() map[string]M {
 | |
| 	return {
 | |
| 		'': M{}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| fn test_generic_return_map() {
 | |
| 	assert typeof(generic_return_map<string>()).name == 'map[string]string'
 | |
| }
 | |
| 
 | |
| fn generic_return_nested_map<M>() map[string]map[string]M {
 | |
| 	return {
 | |
| 		'': {
 | |
| 			'': M{}
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| fn test_generic_return_nested_map() {
 | |
| 	assert typeof(generic_return_nested_map<string>()).name == 'map[string]map[string]string'
 | |
| }
 | |
| 
 | |
| fn multi_return<A, B>() (A, B) {
 | |
| 	return A{}, B{}
 | |
| }
 | |
| 
 | |
| struct Foo1 {}
 | |
| 
 | |
| struct Foo2 {}
 | |
| 
 | |
| struct Foo3 {}
 | |
| 
 | |
| struct Foo4 {}
 | |
| 
 | |
| fn test_multi_return() {
 | |
| 	// compiles
 | |
| 	multi_return<Foo1, Foo2>()
 | |
| 	multi_return<Foo3, Foo4>()
 | |
| }
 | |
| 
 | |
| fn multi_generic_args<T, V>(t T, v V) bool {
 | |
| 	return true
 | |
| }
 | |
| 
 | |
| fn test_multi_generic_args() {
 | |
| 	assert multi_generic_args('Super', 2021)
 | |
| }
 | |
| 
 | |
| fn new<T>() T {
 | |
| 	return T{}
 | |
| }
 | |
| 
 | |
| fn test_generic_init() {
 | |
| 	// array init
 | |
| 	mut a := new<[]string>()
 | |
| 	assert a.len == 0
 | |
| 	a << 'a'
 | |
| 	assert a.len == 1
 | |
| 	assert a[0] == 'a'
 | |
| 
 | |
| 	// map init
 | |
| 	mut b := new<map[string]string>()
 | |
| 	assert b.len == 0
 | |
| 	b['b'] = 'b'
 | |
| 	assert b.len == 1
 | |
| 	assert b['b'] == 'b'
 | |
| 
 | |
| 	// struct init
 | |
| 	mut c := new<User>()
 | |
| 	c.name = 'c'
 | |
| 	assert c.name == 'c'
 | |
| }
 | |
| 
 | |
| fn return_one<T>(rec int, useless T) T {
 | |
| 	// foo < bar<T>() should work
 | |
| 	if rec == 0 || 0 < return_one<T>(rec - 1, useless) {
 | |
| 		return T(1)
 | |
| 	}
 | |
| 	return T(0)
 | |
| }
 | |
| 
 | |
| struct MultiLevel<T> {
 | |
| 	foo T
 | |
| }
 | |
| 
 | |
| fn get_multilevel_foo<T>(bar MultiLevel<T>) int {
 | |
| 	return bar.foo.foo
 | |
| }
 | |
| 
 | |
| fn get_multilevel_foo_2<T, U>(bar T, baz U) int {
 | |
| 	return bar.foo.foo + baz.foo.foo
 | |
| }
 | |
| 
 | |
| fn test_multi_level_generics() {
 | |
| 	one := MultiLevel<int>{
 | |
| 		foo: 10
 | |
| 	}
 | |
| 	two := MultiLevel<MultiLevel<int>>{
 | |
| 		foo: one
 | |
| 	}
 | |
| 	assert two.foo.foo == 10
 | |
| 	three := MultiLevel<MultiLevel<MultiLevel<int>>>{
 | |
| 		foo: two
 | |
| 	}
 | |
| 	assert three.foo.foo.foo == 10
 | |
| 	assert get_multilevel_foo<MultiLevel<int>>(two) == 10
 | |
| 	assert get_multilevel_foo_2<MultiLevel<MultiLevel<int>>, MultiLevel<MultiLevel<int>>>(two,
 | |
| 		two) == 20
 | |
| }
 | |
| 
 | |
| struct Empty_ {}
 | |
| 
 | |
| fn (e1 Empty_) < (e2 Empty_) bool {
 | |
| 	return true
 | |
| }
 | |
| 
 | |
| struct TandU<T, U> {
 | |
| 	t T
 | |
| 	u U
 | |
| }
 | |
| 
 | |
| fn boring_function<T>(t T) bool {
 | |
| 	return true
 | |
| }
 | |
| 
 | |
| fn test_generic_detection() {
 | |
| 	v1, v2 := -1, 1
 | |
| 
 | |
| 	// not generic
 | |
| 	a1, a2 := v1 < v2, v2 > v1
 | |
| 	assert a1 && a2
 | |
| 	b1, b2 := v1 < simplemodule.zero, v2 > v1
 | |
| 	assert b1 && b2
 | |
| 
 | |
| 	// generic
 | |
| 	assert multi_generic_args<int, string>(0, 's')
 | |
| 	assert multi_generic_args<Foo1, Foo2>(Foo1{}, Foo2{})
 | |
| 	assert multi_generic_args<Foo<int>, Foo<int>>(Foo<int>{}, Foo<int>{})
 | |
| 
 | |
| 	// TODO: assert multi_generic_args<Foo<int>, Foo<int>>(Foo1{}, Foo2{})
 | |
| 	assert multi_generic_args<simplemodule.Data, int>(simplemodule.Data{}, 0)
 | |
| 	assert multi_generic_args<int, simplemodule.Data>(0, simplemodule.Data{})
 | |
| 	assert multi_generic_args<[]int, int>([]int{}, 0)
 | |
| 	assert multi_generic_args<map[int]int, int>(map[int]int{}, 0)
 | |
| 	assert 0 < return_one<int>(10, 0)
 | |
| 
 | |
| 	// "the hardest cases"
 | |
| 	foo, bar, baz := 1, 2, 16
 | |
| 	res1, res2 := foo < bar, baz >> (foo + 1 - 1)
 | |
| 	assert res1
 | |
| 	assert res2 == 8
 | |
| 	res3, res4 := Empty_{} < Empty_{}, baz >> (foo + 1 - 1)
 | |
| 	assert res3
 | |
| 	assert res4 == 8
 | |
| 	assert boring_function<TandU<Empty_, int>>(TandU<Empty_, int>{
 | |
| 		t: Empty_{}
 | |
| 		u: 10
 | |
| 	})
 | |
| 
 | |
| 	assert boring_function<MultiLevel<MultiLevel<int>>>(MultiLevel<MultiLevel<int>>{
 | |
| 		foo: MultiLevel<int>{
 | |
| 			foo: 10
 | |
| 		}
 | |
| 	})
 | |
| 
 | |
| 	assert boring_function<TandU<MultiLevel<int>, []int>>(TandU<MultiLevel<int>, []int>{
 | |
| 		t: MultiLevel<int>{
 | |
| 			foo: 10
 | |
| 		}
 | |
| 		u: [10]
 | |
| 	})
 | |
| 
 | |
| 	// this final case challenges your scanner :-)
 | |
| 	assert boring_function<TandU<TandU<int, MultiLevel<Empty_>>, map[string][]int>>(TandU<TandU<int, MultiLevel<Empty_>>, map[string][]int>{
 | |
| 		t: TandU<int, MultiLevel<Empty_>>{
 | |
| 			t: 20
 | |
| 			u: MultiLevel<Empty_>{
 | |
| 				foo: Empty_{}
 | |
| 			}
 | |
| 		}
 | |
| 		u: {
 | |
| 			'bar': [40]
 | |
| 		}
 | |
| 	})
 | |
| }
 |