go.starlark.net@v0.0.0-20231101134539-556fd59b42f6/starlark/testdata/function.star (about)

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