#!/usr/bin/env ascript
print_func(func:function) = printf('%s = %s\n', func, func.expr())
tester(test:expr):map:dynamic_scope = printf("%s .. %s\n", test, eval(test)):nomap

lambda {
	accum = 0
	f(x:number):map:reduce = { accum += x }
	tester(`f(1..10))
}()
lambda {
	m = module {
		A = class {
			__init__(name) = { self.name = name }
			func() = { println('message from ', self.name) }
		}
		obj1 = A('object-1')
		obj2 = A('object-2')
		obj3 = A('object-3')
		f1 = obj1.func
		f2 = obj2.func
		f3 = obj3.func
	}
	m.obj1.func()
	m.obj2.func()
	m.obj3.func()
	f1 = m.obj1.func
	f2 = m.obj2.func
	f3 = m.obj3.func
	f1()
	f2()
	f3()
	m.f1()
	m.f2()
	m.f3()
}()
lambda {
	f() {`block} = {
		println(typename(block))
	}
	g() {`block?} = {
		f() {|block|}
	}
	g()
	g() {}
}()
lambda {
	g(opts%) = {
		println(opts):nomap
	}
	f(opts%) = {
		println(opts):nomap
		g(opts%)
	}
	f(a => `println('hello'))
}()
lambda {
	f() {block} = {
		block()
	}
	lambda {
		x = 3
		f {
			println(x)
		}
	}()
}()
lambda {
	f(a, b, c, d, dict%) = {
		format("a=%d b=%d c=%d d=%d %s", a, b, c, d, dict)
	}
	tester(`f((%{`a => 1, `b => 2, `c => 3, `d => 4})%))
	tester(`f((%{`a => 1, `b => 2})%, (%{`c => 3, `d => 4})%))
	tester(`f((%{`a => 1, `b => 2})%, (%{`c => 3})%, d => 4))
	tester(`f(1, 2, 3, 4, (%{`a => 1, `b => 2, `c => 3, `d => 4})%))
	tester(`f(1, 2, 3, 4, (%{'a' => 1, 'b' => 2, 'c' => 3, 'd' => 4})%))
	f(a, dict%) = {
		format("a=%d %s", a, dict)
	}
	tester(`f(1, (%{`a => 1, `b => 2, `c => 3, `d => 4})%))
	f(a):map = {a}
	try {
		tester(`f(1, 2, 3, a => 1))
	} except (ValueError) {|e|
		println("expected error occurs: ", e.text)
	}
	try {
		tester(`f(1, 2, 3, (%{a => 1})%))
	} except (ValueError) {|e|
		println("expected error occurs: ", e.text)
	}
}()
lambda {
	funcorg() = {}
	print_func(funcorg)
	funcorg.setsymbol(`funcnew)
	print_func(funcorg)
	funcorg.setsymbol('funcnew1')
	print_func(funcorg)
}()
lambda {
	x = [[1, 1], [1, 2], [1, 3], [2, 1], [2, 2], [2, 3], [3, 1], [3, 2], [3, 3]]
	for ([a, b] in x) {
		switch {
			case (a == 1) {
				switch {
					case (b == 1) {
						println("[a, b] = [1, 1]")
					}
					case (b == 2) {
						println("[a, b] = [1, 2]")
					}
					default {
						println("[a, b] = [1, default]")
					}
				}
			}
			case (a == 2) {
				switch {
					case (b == 1) {
						println("[a, b] = [2, 1]")
					}
					case (b == 2) {
						println("[a, b] = [2, 2]")
					}
					default {
						println("[a, b] = [2, default]")
					}
				}
			}
			default {
				switch {
					case (b == 1) {
						println("[a, b] = [default, 1]")
					}
					case (b == 2) {
						println("[a, b] = [default, 2]")
					}
					default {
						println("[a, b] = [default, default]")
					}
				}
			}
			case (a == 3) {
				println("a == 3")
			}
		}
	}
}()
lambda {
	x = `{|a, b, c, d, e:number| a + b + c + d + e}
	f(g:function) = g(1, 2, 3, 4, 5)
	tester(`f(x))
}()
lambda {
	x = `{|a, b, c, d, e:number| a + b + c + d + e}
	f = x.tofunction()
	print_func(f)
	tester(`f(1, 2, 3, 4, 5))
}()
lambda {
	f(x, y):map = [x, y]
	g(x, y:nomap):map = [x, y]
	tester(`f([1, 2, 3], [4, 5, 6]))
	tester(`g([1, 2, 3], [4, 5, 6]))
}()
lambda {
	f(x[]) = x
	tester(`f(1..10))
	f(x[]:string) = x
	tester(`f(1..10))
	f(x:list) = x
	tester(`f(1..10))
}()
lambda {
	import(re)
	f(m:re.match) = printf("%s\n", m.group(1))
	m = re.match(r"([0-9]+)", "hello 100 world")
	f(m)
	try {
		f(10)
	} except(TypeError) {|e|
		println("expected error occurs: ", e.text)
	}
	try {
		f("foo")
	} except(TypeError) {|e|
		println("expected error occurs: ", e.text)
	}
}()
lambda {
	f(a, b, c, d, e, f, g, h, i, j, dict%) = {
		format("a=%d b=%d c=%d d=%d e=%d f=%d g=%d h=%d i=%d j=%d %s",
									 a, b, c, d, e, f, g, h, i, j, dict)
	}
	tester(`f(1, 2, 3, 4, 5, 6, 7, 8, 9, 10))
	tester(`f(1, 2, 3, [4..10]*))
	tester(`f(1, 2, 3, [4..6]*, 7, 8, 9, 10))
	tester(`f(1, 2, 3, [4..6]*, 7, [8..10]*))
	tester(`f([1..3]*, [4..6]*, 7, [8..10]*))
}()
lambda {
	f = "hello world".left
	tester(`f)
	tester(`f(3))
	tester(`f(5))
	tester(`f(9))
	tester(`(["first", "second", "third", "fourth", "fifth", "sixth", "seventh"]::left * 2))
	tester(`[["first", "second", "third", "fourth", "fifth", "sixth", "seventh"]:*left * 2])
}()
lambda {
	f(a, b, c, d, rest*, dict%) = {
		format("a=%d b=%d c=%d d=%d rest=%s dict=%s", a, b, c, d, rest, dict):nomap
	}
	print_func(f)
	tester(`f(1, 2, 3, 4, 5, 6))
	tester(`f(1, 2, 3, 4, 5, 6, e => 2, f => 3))
}()
lambda {
	f(a, b, c, d, dict%) = format("a=%d b=%d c=%d d=%d dict=%s", a, b, c, d, dict)
	print_func(f)
	tester(`f(1, 2, 3, 4))
	tester(`f(1, 2, d => 4, c => 4))
	tester(`f(a => 1, b => 2, c => 3, d => 4))
	tester(`f(c => 3, a => 1, b => 2, d => 4))
	tester(`f(b => 2, c => 3, d => 4, a => 1, e => 1))
	tester(`f(d => 4, 1, 2, 3))
	tester(`f(d => 4, 1, 2, 3, e => 5, f => 6, g => 7))
}()
lambda {
	f1(a, b?, c?) = printf("a=%s, b=%s:%d, c=%s:%d\n", a, b, isdefined(b), c, isdefined(c))
	f1(2)
	f1(2, 3)
	f1(2, 3, "def")
	f2(a, b => 10, c => "abc") = printf("a=%s, b=%s, c=%s\n", a, b, c)
	f2(2)
	f2(2, 3)
	f2(2, 3, "def")
	f3(a, b, c*) = printf("a=%s, b=%s, c=%s\n", a, b, c.tostring())
	f3(2, 3, 4, 5, 6, 7)
	f3(2, 3)
	f4(a, b, c+) = printf("a=%s, b=%s, c=%s\n", a, b, c.tostring())
	f4(2, 3, 4, 5, 6, 7)
	try {
		f4(2, 3)
	} except(TypeError) {|e|
		println("expected error occurs: ", e.text)
	}
}()
lambda {
	println("test of closure")
	newCounter(n:number):map = {
		lambda() { n += 1}
	}
	cnt = newCounter(2)
	repeat(6) {
		printf("%2d", cnt())
	}
	println()
	cnts = newCounter(4, 2, 1, 3)
	repeat(10) {
		printf("%2d %2d %2d %2d\n", cnts[0](), cnts[1](), cnts[2](), cnts[3]())
	}
}()
lambda {
	lambda(n):map { printf("%3d", n) }([0..10])
	println()
}()
lambda {
	println((&{math.sin($n) * math.cos($n)})(3))
}()
lambda {
	f(x) = x
	g(x):void = x
	print_func(f)
	print_func(g)
	tester(`f(2))
	tester(`g(2))
}()
lambda {
	f = &{$x < $y}
	print_func(f)
	tester(`f(1, 3))
	tester(`f(4, 3))
}()
lambda {
	f = &{$a + $b + $c + $d + $e + $f + $g + $h + $i + $j}
	print_func(f)
	tester(`f(1, 2, 3, 4, 5, 6, 7, 8, 9, 10))
}()
lambda {
	t = [interval(0, 1, 10)]	# 0 <= x <= 1
	for (func in [math.acos, math.asin, math.atan]) {
		print(func, " .. ", func(t).tostring(), "\n")
	}
	t = [interval(-2, 2, 11)]	# -2 <= x <= 2
	for (func in [math.ceil, math.cos, math.cosh, math.exp, math.abs, math.floor,
									math.sin, math.sinh, math.sqrt, math.tan, math.tanh]) {
		print(func, " .. ", func(t).tostring(), "\n")
	}
	t = [range(1, 11)]
	for (func in [math.log, math.log10]) {
		print(func, " .. ", func(t).tostring(), "\n")
	}
}()
lambda {
	f(x) = {
		g(y) = x + y
	}
	g = f(2)
	x = 4
	printf("%d .. should be 5\n", g(3))
}()
