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])