OILS / spec / ysh-func.test.sh View on Github | oilshell.org

496 lines, 293 significant
1# spec/ysh-func
2
3## our_shell: ysh
4
5#### Identity function
6func id(x) {
7 return (x)
8}
9
10json write (id("ysh"))
11
12## STDOUT:
13"ysh"
14## END
15
16#### Too many args
17func f(x) { return (x + 1) }
18
19= f(0, 1)
20## status: 3
21## STDOUT:
22## END
23
24#### Too few args
25func f(x) { return (x + 1) }
26
27= f()
28## status: 3
29## STDOUT:
30## END
31
32#### Positional args
33
34func f(x, y, ...rest) {
35 echo "pos $x $y"
36 echo rest @rest
37}
38
39call f(1, 2, 3, 4)
40
41# This is an error
42#call f(1, 2, m=2, n=3)
43
44## STDOUT:
45pos 1 2
46rest 3 4
47## END
48
49
50#### named args
51func f(; x=3) {
52 echo x=$x
53}
54
55call f()
56
57call f(x=4)
58
59## STDOUT:
60x=3
61x=4
62## END
63
64#### Named args with ...rest
65func f(; x=3, ...named) {
66 echo x=$x
67 pp test_ (named)
68}
69
70call f()
71
72call f(x=4)
73
74call f(x=4, y=5)
75
76## STDOUT:
77x=3
78(Dict) {}
79x=4
80(Dict) {}
81x=4
82(Dict) {"y":5}
83## END
84
85#### Spread/splat of named args: f(...more)
86
87func f(; x, y) {
88 echo "$x $y"
89}
90
91call f(; x=9, y=10)
92
93var args = {x: 3, y: 4}
94
95call f(; ...args)
96
97
98## STDOUT:
999 10
1003 4
101## END
102
103
104#### Multiple spreads
105
106func f(...pos; ...named) {
107 pp test_ (pos)
108 pp test_ (named)
109}
110
111var a = [1,2,3]
112var d = {m: 'spam', n: 'no'}
113var e = {p: 5, q: 6}
114
115call f(...a, ...a; ...d, ...e)
116
117## STDOUT:
118(List) [1,2,3,1,2,3]
119(Dict) {"m":"spam","n":"no","p":5,"q":6}
120## END
121
122
123#### Proc-style return in a func is error
124func t() { return 0 }
125
126= t()
127## status: 2
128## STDOUT:
129## END
130
131#### Typed return in a proc is error
132proc t() { return (0) }
133
134= t()
135## status: 2
136## STDOUT:
137## END
138
139#### Redefining functions is allowed
140func f() { return (0) }
141func f() { return (1) }
142## status: 0
143## STDOUT:
144## END
145
146#### Functions can redefine vars
147var f = 0
148func f() { return (1) }
149pp test_ (f)
150## STDOUT:
151<Func>
152## END
153
154#### Multiple func calls
155
156func inc(x) {
157 # increment
158
159 return (x + 1)
160}
161
162func dec(x) {
163 # decrement
164
165 return (x - 1)
166}
167
168echo $[inc(1)]
169echo $[inc(inc(1))]
170echo $[dec(inc(inc(1)))]
171
172var y = dec(dec(1))
173echo $[dec(y)]
174
175## STDOUT:
1762
1773
1782
179-2
180## END
181
182#### Undefined var in function
183
184func g(x) {
185 var z = y # make sure dynamic scope is off
186 return (x + z)
187}
188
189func f() {
190 var y = 42 # if dynamic scope were on, g() would see this
191 return (g(0))
192}
193
194echo $[f()]
195
196## status: 1
197## STDOUT:
198## END
199
200#### Param binding semantics
201# value
202var x = 'foo'
203
204func f(x) {
205 setvar x = 'bar'
206}
207
208pp test_ (x)
209pp test_ (f(x))
210pp test_ (x)
211
212# reference
213var y = ['a', 'b', 'c']
214
215func g(y) {
216 setvar y[0] = 'z'
217}
218
219pp test_ (y)
220pp test_ (g(y))
221pp test_ (y)
222## STDOUT:
223(Str) "foo"
224(Null) null
225(Str) "foo"
226(List) ["a","b","c"]
227(Null) null
228(List) ["z","b","c"]
229## END
230
231#### Recursive functions
232func fib(n) {
233 # TODO: add assert n > 0
234 if (n < 2) {
235 return (n)
236 }
237
238 return (fib(n - 1) + fib(n - 2))
239}
240
241json write (fib(10))
242## STDOUT:
24355
244## END
245
246#### Recursive functions with LRU Cache
247source $LIB_YSH/list.ysh
248
249var cache = []
250var maxSize = 4
251
252func remove(l, i) {
253 for i in (i .. len(l) - 1) {
254 setvar l[i] = l[i + 1]
255 }
256
257 call l->pop() # remove duplicate last element
258}
259
260func fib(n) {
261 var i = len(cache) - 1
262 var j = 0;
263 while (i >= 0) {
264 var item = cache[i]
265
266 if (item[0] === n) {
267 call remove(cache, i)
268 call cache->append(item)
269
270 echo hit: $n
271 return (item[1])
272 }
273
274 setvar i = i - 1
275 setvar j += 1
276 }
277
278 var result = 0
279 if (n < 2) {
280 setvar result = n
281 } else {
282 setvar result = fib(n - 1) + fib(n - 2)
283 }
284
285 if (len(cache) >= maxSize) {
286 call remove(cache, 0)
287 }
288 call cache->append([n, result])
289
290 return (result)
291}
292
293json write (fib(10))
294json write (cache)
295
296## STDOUT:
297hit: 1
298hit: 2
299hit: 3
300hit: 4
301hit: 5
302hit: 6
303hit: 7
304hit: 8
30555
306[
307 [
308 7,
309 13
310 ],
311 [
312 9,
313 34
314 ],
315 [
316 8,
317 21
318 ],
319 [
320 10,
321 55
322 ]
323]
324## END
325
326#### Varadic arguments, no other args
327func f(...args) {
328pp test_ (args)
329}
330
331call f()
332call f(1)
333call f(1, 2)
334call f(1, 2, 3)
335## STDOUT:
336(List) []
337(List) [1]
338(List) [1,2]
339(List) [1,2,3]
340## END
341
342#### Varadic arguments, other args
343func f(a, b, ...args) {
344pp test_ ([a, b, args])
345}
346
347call f(1, 2)
348call f(1, 2, 3)
349call f(1, 2, 3, 4)
350## STDOUT:
351(List) [1,2,[]]
352(List) [1,2,[3]]
353(List) [1,2,[3,4]]
354## END
355
356#### Varadic arguments, too few args
357func f(a, b, ...args) {
358 = [a, b, args]
359}
360
361call f(1)
362## status: 3
363## STDOUT:
364## END
365
366#### Userland max
367func mymax (...args) {
368 if (len(args) === 0) {
369 error 'Requires 1 arg'
370 } elif (len(args) === 1) {
371 # TODO: assert List
372 var mylist = args[0]
373 var max = mylist[0]
374
375 for item in (mylist) {
376 if (item > max) {
377 setvar max = item
378 }
379 }
380
381 return (max)
382 } elif (len(args) === 2) {
383 if (args[0] >= args[1]) {
384 return (args[0])
385 } else {
386 return (args[1])
387 }
388 } else {
389 # max(1, 2, 3) doesn't work in YSH, but does in Python
390 error 'too many'
391 }
392}
393
394= mymax(5,6) # => 6
395= mymax([5,6,7]) # => 7
396= mymax(5,6,7,8) # error
397
398## status: 10
399## STDOUT:
400(Int) 6
401(Int) 7
402## END
403
404#### Functions share a namespace with variables
405func f(x) {
406 return (x * x)
407}
408
409var g = f
410echo "g(2) -> $[g(2)]"
411## STDOUT:
412g(2) -> 4
413## END
414
415#### We can store funcs in dictionaries
416func dog_speak() {
417 echo "Woof"
418}
419
420func dog_type() {
421 return ("DOG")
422}
423
424const Dog = {
425 speak: dog_speak,
426 type: dog_type,
427}
428
429func cat_speak() {
430 echo "Meow"
431}
432
433func cat_type() {
434 return ("CAT")
435}
436
437const Cat = {
438 speak: cat_speak,
439 type: cat_type,
440}
441
442# First class "modules"!
443const animals = [Dog, Cat]
444for animal in (animals) {
445 var type = animal.type()
446 echo This is a $type
447 call animal.speak()
448}
449## STDOUT:
450This is a DOG
451Woof
452This is a CAT
453Meow
454## END
455
456#### Functions can be nested
457proc build {
458 func f(x) {
459 return (x)
460 }
461
462 echo $[f(0)]
463}
464build
465echo $[f(0)] # This will fail as f is locally scoped in `proc build`
466## status: 1
467## STDOUT:
4680
469## END
470
471#### Functions can be shadowed
472func mysum(items) {
473 var mysum = 0
474 for x in (items) {
475 setvar mysum += x
476 }
477 return (mysum)
478}
479
480echo 1 + 2 + 3 = $[mysum([1, 2, 3])]
481
482func inAnotherScope() {
483 # variable mysum has not yet shadowed func mysum in evaluation
484 var mysum = mysum([1, 2, 3])
485 echo mysum=$mysum
486}
487call inAnotherScope()
488
489var mysum = mysum([0, 1])
490echo mysum=$mysum
491
492## STDOUT:
4931 + 2 + 3 = 6
494mysum=6
495mysum=1
496## END