1*4947cdc7SCole Faust# Tests of Starlark 'function' 2*4947cdc7SCole Faust# option:set 3*4947cdc7SCole Faust 4*4947cdc7SCole Faust# TODO(adonovan): 5*4947cdc7SCole Faust# - add some introspection functions for looking at function values 6*4947cdc7SCole Faust# and test that functions have correct position, free vars, names of locals, etc. 7*4947cdc7SCole Faust# - move the hard-coded tests of parameter passing from eval_test.go to here. 8*4947cdc7SCole Faust 9*4947cdc7SCole Faustload("assert.star", "assert", "freeze") 10*4947cdc7SCole Faust 11*4947cdc7SCole Faust# Test lexical scope and closures: 12*4947cdc7SCole Faustdef outer(x): 13*4947cdc7SCole Faust def inner(y): 14*4947cdc7SCole Faust return x + x + y # multiple occurrences of x should create only 1 freevar 15*4947cdc7SCole Faust return inner 16*4947cdc7SCole Faust 17*4947cdc7SCole Faustz = outer(3) 18*4947cdc7SCole Faustassert.eq(z(5), 11) 19*4947cdc7SCole Faustassert.eq(z(7), 13) 20*4947cdc7SCole Faustz2 = outer(4) 21*4947cdc7SCole Faustassert.eq(z2(5), 13) 22*4947cdc7SCole Faustassert.eq(z2(7), 15) 23*4947cdc7SCole Faustassert.eq(z(5), 11) 24*4947cdc7SCole Faustassert.eq(z(7), 13) 25*4947cdc7SCole Faust 26*4947cdc7SCole Faust# Function name 27*4947cdc7SCole Faustassert.eq(str(outer), '<function outer>') 28*4947cdc7SCole Faustassert.eq(str(z), '<function inner>') 29*4947cdc7SCole Faustassert.eq(str(str), '<built-in function str>') 30*4947cdc7SCole Faustassert.eq(str("".startswith), '<built-in method startswith of string value>') 31*4947cdc7SCole Faust 32*4947cdc7SCole Faust# Stateful closure 33*4947cdc7SCole Faustdef squares(): 34*4947cdc7SCole Faust x = [0] 35*4947cdc7SCole Faust def f(): 36*4947cdc7SCole Faust x[0] += 1 37*4947cdc7SCole Faust return x[0] * x[0] 38*4947cdc7SCole Faust return f 39*4947cdc7SCole Faust 40*4947cdc7SCole Faustsq = squares() 41*4947cdc7SCole Faustassert.eq(sq(), 1) 42*4947cdc7SCole Faustassert.eq(sq(), 4) 43*4947cdc7SCole Faustassert.eq(sq(), 9) 44*4947cdc7SCole Faustassert.eq(sq(), 16) 45*4947cdc7SCole Faust 46*4947cdc7SCole Faust# Freezing a closure 47*4947cdc7SCole Faustsq2 = freeze(sq) 48*4947cdc7SCole Faustassert.fails(sq2, "frozen list") 49*4947cdc7SCole Faust 50*4947cdc7SCole Faust# recursion detection, simple 51*4947cdc7SCole Faustdef fib(x): 52*4947cdc7SCole Faust if x < 2: 53*4947cdc7SCole Faust return x 54*4947cdc7SCole Faust return fib(x-2) + fib(x-1) 55*4947cdc7SCole Faustassert.fails(lambda: fib(10), "function fib called recursively") 56*4947cdc7SCole Faust 57*4947cdc7SCole Faust# recursion detection, advanced 58*4947cdc7SCole Faust# 59*4947cdc7SCole Faust# A simplistic recursion check that looks for repeated calls to the 60*4947cdc7SCole Faust# same function value will not detect recursion using the Y 61*4947cdc7SCole Faust# combinator, which creates a new closure at each step of the 62*4947cdc7SCole Faust# recursion. To truly prohibit recursion, the dynamic check must look 63*4947cdc7SCole Faust# for repeated calls of the same syntactic function body. 64*4947cdc7SCole FaustY = lambda f: (lambda x: x(x))(lambda y: f(lambda *args: y(y)(*args))) 65*4947cdc7SCole Faustfibgen = lambda fib: lambda x: (x if x<2 else fib(x-1)+fib(x-2)) 66*4947cdc7SCole Faustfib2 = Y(fibgen) 67*4947cdc7SCole Faustassert.fails(lambda: [fib2(x) for x in range(10)], "function lambda called recursively") 68*4947cdc7SCole Faust 69*4947cdc7SCole Faust# However, this stricter check outlaws many useful programs 70*4947cdc7SCole Faust# that are still bounded, and creates a hazard because 71*4947cdc7SCole Faust# helper functions such as map below cannot be used to 72*4947cdc7SCole Faust# call functions that themselves use map: 73*4947cdc7SCole Faustdef map(f, seq): return [f(x) for x in seq] 74*4947cdc7SCole Faustdef double(x): return x+x 75*4947cdc7SCole Faustassert.eq(map(double, [1, 2, 3]), [2, 4, 6]) 76*4947cdc7SCole Faustassert.eq(map(double, ["a", "b", "c"]), ["aa", "bb", "cc"]) 77*4947cdc7SCole Faustdef mapdouble(x): return map(double, x) 78*4947cdc7SCole Faustassert.fails(lambda: map(mapdouble, ([1, 2, 3], ["a", "b", "c"])), 79*4947cdc7SCole Faust 'function map called recursively') 80*4947cdc7SCole Faust# With the -recursion option it would yield [[2, 4, 6], ["aa", "bb", "cc"]]. 81*4947cdc7SCole Faust 82*4947cdc7SCole Faust# call of function not through its name 83*4947cdc7SCole Faust# (regression test for parsing suffixes of primary expressions) 84*4947cdc7SCole Fausthf = hasfields() 85*4947cdc7SCole Fausthf.x = [len] 86*4947cdc7SCole Faustassert.eq(hf.x[0]("abc"), 3) 87*4947cdc7SCole Faustdef f(): 88*4947cdc7SCole Faust return lambda: 1 89*4947cdc7SCole Faustassert.eq(f()(), 1) 90*4947cdc7SCole Faustassert.eq(["abc"][0][0].upper(), "A") 91*4947cdc7SCole Faust 92*4947cdc7SCole Faust# functions may be recursively defined, 93*4947cdc7SCole Faust# so long as they don't dynamically recur. 94*4947cdc7SCole Faustcalls = [] 95*4947cdc7SCole Faustdef yin(x): 96*4947cdc7SCole Faust calls.append("yin") 97*4947cdc7SCole Faust if x: 98*4947cdc7SCole Faust yang(False) 99*4947cdc7SCole Faust 100*4947cdc7SCole Faustdef yang(x): 101*4947cdc7SCole Faust calls.append("yang") 102*4947cdc7SCole Faust if x: 103*4947cdc7SCole Faust yin(False) 104*4947cdc7SCole Faust 105*4947cdc7SCole Faustyin(True) 106*4947cdc7SCole Faustassert.eq(calls, ["yin", "yang"]) 107*4947cdc7SCole Faust 108*4947cdc7SCole Faustcalls.clear() 109*4947cdc7SCole Faustyang(True) 110*4947cdc7SCole Faustassert.eq(calls, ["yang", "yin"]) 111*4947cdc7SCole Faust 112*4947cdc7SCole Faust 113*4947cdc7SCole Faust# builtin_function_or_method use identity equivalence. 114*4947cdc7SCole Faustclosures = set(["".count for _ in range(10)]) 115*4947cdc7SCole Faustassert.eq(len(closures), 10) 116*4947cdc7SCole Faust 117*4947cdc7SCole Faust--- 118*4947cdc7SCole Faust# Default values of function parameters are mutable. 119*4947cdc7SCole Faustload("assert.star", "assert", "freeze") 120*4947cdc7SCole Faust 121*4947cdc7SCole Faustdef f(x=[0]): 122*4947cdc7SCole Faust return x 123*4947cdc7SCole Faust 124*4947cdc7SCole Faustassert.eq(f(), [0]) 125*4947cdc7SCole Faust 126*4947cdc7SCole Faustf().append(1) 127*4947cdc7SCole Faustassert.eq(f(), [0, 1]) 128*4947cdc7SCole Faust 129*4947cdc7SCole Faust# Freezing a function value freezes its parameter defaults. 130*4947cdc7SCole Faustfreeze(f) 131*4947cdc7SCole Faustassert.fails(lambda: f().append(2), "cannot append to frozen list") 132*4947cdc7SCole Faust 133*4947cdc7SCole Faust--- 134*4947cdc7SCole Faust# This is a well known corner case of parsing in Python. 135*4947cdc7SCole Faustload("assert.star", "assert") 136*4947cdc7SCole Faust 137*4947cdc7SCole Faustf = lambda x: 1 if x else 0 138*4947cdc7SCole Faustassert.eq(f(True), 1) 139*4947cdc7SCole Faustassert.eq(f(False), 0) 140*4947cdc7SCole Faust 141*4947cdc7SCole Faustx = True 142*4947cdc7SCole Faustf2 = (lambda x: 1) if x else 0 143*4947cdc7SCole Faustassert.eq(f2(123), 1) 144*4947cdc7SCole Faust 145*4947cdc7SCole Fausttf = lambda: True, lambda: False 146*4947cdc7SCole Faustassert.true(tf[0]()) 147*4947cdc7SCole Faustassert.true(not tf[1]()) 148*4947cdc7SCole Faust 149*4947cdc7SCole Faust--- 150*4947cdc7SCole Faust# Missing parameters are correctly reported 151*4947cdc7SCole Faust# in functions of more than 64 parameters. 152*4947cdc7SCole Faust# (This tests a corner case of the implementation: 153*4947cdc7SCole Faust# we avoid a map allocation for <64 parameters) 154*4947cdc7SCole Faust 155*4947cdc7SCole Faustload("assert.star", "assert") 156*4947cdc7SCole Faust 157*4947cdc7SCole Faustdef f(a, b, c, d, e, f, g, h, 158*4947cdc7SCole Faust i, j, k, l, m, n, o, p, 159*4947cdc7SCole Faust q, r, s, t, u, v, w, x, 160*4947cdc7SCole Faust y, z, A, B, C, D, E, F, 161*4947cdc7SCole Faust G, H, I, J, K, L, M, N, 162*4947cdc7SCole Faust O, P, Q, R, S, T, U, V, 163*4947cdc7SCole Faust W, X, Y, Z, aa, bb, cc, dd, 164*4947cdc7SCole Faust ee, ff, gg, hh, ii, jj, kk, ll, 165*4947cdc7SCole Faust mm): 166*4947cdc7SCole Faust pass 167*4947cdc7SCole Faust 168*4947cdc7SCole Faustassert.fails(lambda: f( 169*4947cdc7SCole Faust 1, 2, 3, 4, 5, 6, 7, 8, 170*4947cdc7SCole Faust 9, 10, 11, 12, 13, 14, 15, 16, 171*4947cdc7SCole Faust 17, 18, 19, 20, 21, 22, 23, 24, 172*4947cdc7SCole Faust 25, 26, 27, 28, 29, 30, 31, 32, 173*4947cdc7SCole Faust 33, 34, 35, 36, 37, 38, 39, 40, 174*4947cdc7SCole Faust 41, 42, 43, 44, 45, 46, 47, 48, 175*4947cdc7SCole Faust 49, 50, 51, 52, 53, 54, 55, 56, 176*4947cdc7SCole Faust 57, 58, 59, 60, 61, 62, 63, 64), "missing 1 argument \\(mm\\)") 177*4947cdc7SCole Faust 178*4947cdc7SCole Faustassert.fails(lambda: f( 179*4947cdc7SCole Faust 1, 2, 3, 4, 5, 6, 7, 8, 180*4947cdc7SCole Faust 9, 10, 11, 12, 13, 14, 15, 16, 181*4947cdc7SCole Faust 17, 18, 19, 20, 21, 22, 23, 24, 182*4947cdc7SCole Faust 25, 26, 27, 28, 29, 30, 31, 32, 183*4947cdc7SCole Faust 33, 34, 35, 36, 37, 38, 39, 40, 184*4947cdc7SCole Faust 41, 42, 43, 44, 45, 46, 47, 48, 185*4947cdc7SCole Faust 49, 50, 51, 52, 53, 54, 55, 56, 186*4947cdc7SCole Faust 57, 58, 59, 60, 61, 62, 63, 64, 65, 187*4947cdc7SCole Faust mm = 100), 'multiple values for parameter "mm"') 188*4947cdc7SCole Faust 189*4947cdc7SCole Faust--- 190*4947cdc7SCole Faust# Regression test for github.com/google/starlark-go/issues/21, 191*4947cdc7SCole Faust# which concerns dynamic checks. 192*4947cdc7SCole Faust# Related: https://github.com/bazelbuild/starlark/issues/21, 193*4947cdc7SCole Faust# which concerns static checks. 194*4947cdc7SCole Faust 195*4947cdc7SCole Faustload("assert.star", "assert") 196*4947cdc7SCole Faust 197*4947cdc7SCole Faustdef f(*args, **kwargs): 198*4947cdc7SCole Faust return args, kwargs 199*4947cdc7SCole Faust 200*4947cdc7SCole Faustassert.eq(f(x=1, y=2), ((), {"x": 1, "y": 2})) 201*4947cdc7SCole Faustassert.fails(lambda: f(x=1, **dict(x=2)), 'multiple values for parameter "x"') 202*4947cdc7SCole Faust 203*4947cdc7SCole Faustdef g(x, y): 204*4947cdc7SCole Faust return x, y 205*4947cdc7SCole Faust 206*4947cdc7SCole Faustassert.eq(g(1, y=2), (1, 2)) 207*4947cdc7SCole Faustassert.fails(lambda: g(1, y=2, **{'y': 3}), 'multiple values for parameter "y"') 208*4947cdc7SCole Faust 209*4947cdc7SCole Faust--- 210*4947cdc7SCole Faust# Regression test for a bug in CALL_VAR_KW. 211*4947cdc7SCole Faust 212*4947cdc7SCole Faustload("assert.star", "assert") 213*4947cdc7SCole Faust 214*4947cdc7SCole Faustdef f(a, b, x, y): 215*4947cdc7SCole Faust return a+b+x+y 216*4947cdc7SCole Faust 217*4947cdc7SCole Faustassert.eq(f(*("a", "b"), **dict(y="y", x="x")) + ".", 'abxy.') 218*4947cdc7SCole Faust--- 219*4947cdc7SCole Faust# Order of evaluation of function arguments. 220*4947cdc7SCole Faust# Regression test for github.com/google/skylark/issues/135. 221*4947cdc7SCole Faustload("assert.star", "assert") 222*4947cdc7SCole Faust 223*4947cdc7SCole Faustr = [] 224*4947cdc7SCole Faust 225*4947cdc7SCole Faustdef id(x): 226*4947cdc7SCole Faust r.append(x) 227*4947cdc7SCole Faust return x 228*4947cdc7SCole Faust 229*4947cdc7SCole Faustdef f(*args, **kwargs): 230*4947cdc7SCole Faust return (args, kwargs) 231*4947cdc7SCole Faust 232*4947cdc7SCole Fausty = f(id(1), id(2), x=id(3), *[id(4)], **dict(z=id(5))) 233*4947cdc7SCole Faustassert.eq(y, ((1, 2, 4), dict(x=3, z=5))) 234*4947cdc7SCole Faust 235*4947cdc7SCole Faust# This matches Python2 and Starlark-in-Java, but not Python3 [1 2 4 3 6]. 236*4947cdc7SCole Faust# *args and *kwargs are evaluated last. 237*4947cdc7SCole Faust# (Python[23] also allows keyword arguments after *args.) 238*4947cdc7SCole Faust# See github.com/bazelbuild/starlark#13 for spec change. 239*4947cdc7SCole Faustassert.eq(r, [1, 2, 3, 4, 5]) 240*4947cdc7SCole Faust 241*4947cdc7SCole Faust--- 242*4947cdc7SCole Faust# option:recursion 243*4947cdc7SCole Faust# See github.com/bazelbuild/starlark#170 244*4947cdc7SCole Faustload("assert.star", "assert") 245*4947cdc7SCole Faust 246*4947cdc7SCole Faustdef a(): 247*4947cdc7SCole Faust list = [] 248*4947cdc7SCole Faust def b(n): 249*4947cdc7SCole Faust list.append(n) 250*4947cdc7SCole Faust if n > 0: 251*4947cdc7SCole Faust b(n - 1) # recursive reference to b 252*4947cdc7SCole Faust 253*4947cdc7SCole Faust b(3) 254*4947cdc7SCole Faust return list 255*4947cdc7SCole Faust 256*4947cdc7SCole Faustassert.eq(a(), [3, 2, 1, 0]) 257*4947cdc7SCole Faust 258*4947cdc7SCole Faustdef c(): 259*4947cdc7SCole Faust list = [] 260*4947cdc7SCole Faust x = 1 261*4947cdc7SCole Faust def d(): 262*4947cdc7SCole Faust list.append(x) # this use of x observes both assignments 263*4947cdc7SCole Faust d() 264*4947cdc7SCole Faust x = 2 265*4947cdc7SCole Faust d() 266*4947cdc7SCole Faust return list 267*4947cdc7SCole Faust 268*4947cdc7SCole Faustassert.eq(c(), [1, 2]) 269*4947cdc7SCole Faust 270*4947cdc7SCole Faustdef e(): 271*4947cdc7SCole Faust def f(): 272*4947cdc7SCole Faust return x # forward reference ok: x is a closure cell 273*4947cdc7SCole Faust x = 1 274*4947cdc7SCole Faust return f() 275*4947cdc7SCole Faust 276*4947cdc7SCole Faustassert.eq(e(), 1) 277*4947cdc7SCole Faust 278*4947cdc7SCole Faust--- 279*4947cdc7SCole Faustload("assert.star", "assert") 280*4947cdc7SCole Faust 281*4947cdc7SCole Faustdef e(): 282*4947cdc7SCole Faust x = 1 283*4947cdc7SCole Faust def f(): 284*4947cdc7SCole Faust print(x) # this reference to x fails 285*4947cdc7SCole Faust x = 3 # because this assignment makes x local to f 286*4947cdc7SCole Faust f() 287*4947cdc7SCole Faust 288*4947cdc7SCole Faustassert.fails(e, "local variable x referenced before assignment") 289*4947cdc7SCole Faust 290*4947cdc7SCole Faustdef f(): 291*4947cdc7SCole Faust def inner(): 292*4947cdc7SCole Faust return x 293*4947cdc7SCole Faust if False: 294*4947cdc7SCole Faust x = 0 295*4947cdc7SCole Faust return x # fails (x is an uninitialized cell of this function) 296*4947cdc7SCole Faust 297*4947cdc7SCole Faustassert.fails(f, "local variable x referenced before assignment") 298*4947cdc7SCole Faust 299*4947cdc7SCole Faustdef g(): 300*4947cdc7SCole Faust def inner(): 301*4947cdc7SCole Faust return x # fails (x is an uninitialized cell of the enclosing function) 302*4947cdc7SCole Faust if False: 303*4947cdc7SCole Faust x = 0 304*4947cdc7SCole Faust return inner() 305*4947cdc7SCole Faust 306*4947cdc7SCole Faustassert.fails(g, "local variable x referenced before assignment") 307*4947cdc7SCole Faust 308*4947cdc7SCole Faust--- 309*4947cdc7SCole Faust# A trailing comma is allowed in any function definition or call. 310*4947cdc7SCole Faust# This reduces the need to edit neighboring lines when editing defs 311*4947cdc7SCole Faust# or calls splayed across multiple lines. 312*4947cdc7SCole Faust 313*4947cdc7SCole Faustdef a(x,): pass 314*4947cdc7SCole Faustdef b(x, y=None, ): pass 315*4947cdc7SCole Faustdef c(x, y=None, *args, ): pass 316*4947cdc7SCole Faustdef d(x, y=None, *args, z=None, ): pass 317*4947cdc7SCole Faustdef e(x, y=None, *args, z=None, **kwargs, ): pass 318*4947cdc7SCole Faust 319*4947cdc7SCole Fausta(1,) 320*4947cdc7SCole Faustb(1, y=2, ) 321*4947cdc7SCole Faust#c(1, *[], ) 322*4947cdc7SCole Faust#d(1, *[], z=None, ) 323*4947cdc7SCole Faust#e(1, *[], z=None, *{}, ) 324