github.com/google/skylark@v0.0.0-20181101142754-a5f7082aabed/testdata/function.sky (about) 1 # Tests of Skylark 'function' 2 3 # TODO(adonovan): 4 # - add some introspection functions for looking at function values 5 # and test that functions have correct position, free vars, names of locals, etc. 6 # - move the hard-coded tests of parameter passing from eval_test.go to here. 7 8 load("assert.sky", "assert", "freeze") 9 10 # Test lexical scope and closures: 11 def outer(x): 12 def inner(y): 13 return x + x + y # multiple occurrences of x should create only 1 freevar 14 return inner 15 16 z = outer(3) 17 assert.eq(z(5), 11) 18 assert.eq(z(7), 13) 19 z2 = outer(4) 20 assert.eq(z2(5), 13) 21 assert.eq(z2(7), 15) 22 assert.eq(z(5), 11) 23 assert.eq(z(7), 13) 24 25 # Function name 26 assert.eq(str(outer), '<function outer>') 27 assert.eq(str(z), '<function inner>') 28 assert.eq(str(str), '<built-in function str>') 29 assert.eq(str("".startswith), '<built-in method startswith of string value>') 30 31 # Stateful closure 32 def squares(): 33 x = [0] 34 def f(): 35 x[0] += 1 36 return x[0] * x[0] 37 return f 38 39 sq = squares() 40 assert.eq(sq(), 1) 41 assert.eq(sq(), 4) 42 assert.eq(sq(), 9) 43 assert.eq(sq(), 16) 44 45 # Freezing a closure 46 sq2 = freeze(sq) 47 assert.fails(sq2, "frozen list") 48 49 # recursion detection, simple 50 def fib(x): 51 if x < 2: 52 return x 53 return fib(x-2) + fib(x-1) 54 assert.fails(lambda: fib(10), "function fib called recursively") 55 56 # recursion detection, advanced 57 # 58 # A simplistic recursion check that looks for repeated calls to the 59 # same function value will not detect recursion using the Y 60 # combinator, which creates a new closure at each step of the 61 # recursion. To truly prohibit recursion, the dynamic check must look 62 # for repeated calls of the same syntactic function body. 63 Y = lambda f: (lambda x: x(x))(lambda y: f(lambda *args: y(y)(*args))) 64 fibgen = lambda fib: lambda x: (x if x<2 else fib(x-1)+fib(x-2)) 65 fib2 = Y(fibgen) 66 assert.fails(lambda: [fib2(x) for x in range(10)], "function lambda called recursively") 67 68 # call of function not through its name 69 # (regression test for parsing suffixes of primary expressions) 70 hf = hasfields() 71 hf.x = [len] 72 assert.eq(hf.x[0]("abc"), 3) 73 def f(): 74 return lambda: 1 75 assert.eq(f()(), 1) 76 assert.eq(["abc"][0][0].upper(), "A") 77 78 # functions may be recursively defined, 79 # so long as they don't dynamically recur. 80 calls = [] 81 def yin(x): 82 calls.append("yin") 83 if x: 84 yang(False) 85 86 def yang(x): 87 calls.append("yang") 88 if x: 89 yin(False) 90 91 yin(True) 92 assert.eq(calls, ["yin", "yang"]) 93 94 calls.clear() 95 yang(True) 96 assert.eq(calls, ["yang", "yin"]) 97 98 99 # hash(builtin_function_or_method) should be deterministic. 100 closures = set(["".count for _ in range(10)]) 101 assert.eq(len(closures), 10) 102 hashes = set([hash("".count) for _ in range(10)]) 103 assert.eq(len(hashes), 1) 104 105 --- 106 # Default values of function parameters are mutable. 107 load("assert.sky", "assert", "freeze") 108 109 def f(x=[0]): 110 return x 111 112 assert.eq(f(), [0]) 113 114 f().append(1) 115 assert.eq(f(), [0, 1]) 116 117 # Freezing a function value freezes its parameter defaults. 118 freeze(f) 119 assert.fails(lambda: f().append(2), "cannot append to frozen list") 120 121 --- 122 # This is a well known corner case of parsing in Python. 123 load("assert.sky", "assert") 124 125 f = lambda x: 1 if x else 0 126 assert.eq(f(True), 1) 127 assert.eq(f(False), 0) 128 129 x = True 130 f2 = (lambda x: 1) if x else 0 131 assert.eq(f2(123), 1) 132 133 tf = lambda: True, lambda: False 134 assert.true(tf[0]()) 135 assert.true(not tf[1]()) 136 137 --- 138 # Missing parameters are correctly reported 139 # in functions of more than 64 parameters. 140 # (This tests a corner case of the implementation: 141 # we avoid a map allocation for <64 parameters) 142 143 load("assert.sky", "assert") 144 145 def f(a, b, c, d, e, f, g, h, 146 i, j, k, l, m, n, o, p, 147 q, r, s, t, u, v, w, x, 148 y, z, A, B, C, D, E, F, 149 G, H, I, J, K, L, M, N, 150 O, P, Q, R, S, T, U, V, 151 W, X, Y, Z, aa, bb, cc, dd, 152 ee, ff, gg, hh, ii, jj, kk, ll, 153 mm): 154 pass 155 156 assert.fails(lambda: f( 157 1, 2, 3, 4, 5, 6, 7, 8, 158 9, 10, 11, 12, 13, 14, 15, 16, 159 17, 18, 19, 20, 21, 22, 23, 24, 160 25, 26, 27, 28, 29, 30, 31, 32, 161 33, 34, 35, 36, 37, 38, 39, 40, 162 41, 42, 43, 44, 45, 46, 47, 48, 163 49, 50, 51, 52, 53, 54, 55, 56, 164 57, 58, 59, 60, 61, 62, 63, 64), "takes exactly 65 arguments .64 given.") 165 166 assert.fails(lambda: f( 167 1, 2, 3, 4, 5, 6, 7, 8, 168 9, 10, 11, 12, 13, 14, 15, 16, 169 17, 18, 19, 20, 21, 22, 23, 24, 170 25, 26, 27, 28, 29, 30, 31, 32, 171 33, 34, 35, 36, 37, 38, 39, 40, 172 41, 42, 43, 44, 45, 46, 47, 48, 173 49, 50, 51, 52, 53, 54, 55, 56, 174 57, 58, 59, 60, 61, 62, 63, 64, 65, 175 mm = 100), 'multiple values for keyword argument "mm"') 176 177 178 --- 179 # Regression test for a bug in CALL_VAR_KW. 180 181 load("assert.sky", "assert") 182 183 def f(a, b, x, y): 184 return a+b+x+y 185 186 assert.eq(f(*("a", "b"), **dict(y="y", x="x")) + ".", 'abxy.') 187 --- 188 # Order of evaluation of function arguments. 189 # Regression test for github.com/google/skylark/issues/135. 190 load("assert.sky", "assert") 191 192 r = [] 193 194 def id(x): 195 r.append(x) 196 return x 197 198 def f(*args, **kwargs): 199 return (args, kwargs) 200 201 y = f(id(1), id(2), x=id(3), *[id(4)], y=id(5), **dict(z=id(6))) 202 assert.eq(y, ((1, 2, 4), dict(x=3, y=5, z=6))) 203 204 # This matches Python2, but not Skylark-in-Java: 205 # *args and *kwargs are evaluated last. 206 # See github.com/bazelbuild/starlark#13 for pending spec change. 207 assert.eq(r, [1, 2, 3, 5, 4, 6])