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

535 lines, 303 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 line (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 line (pos)
108 pp line (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 not allowed (with shopt -u redefine_proc_func)
140shopt -u redefine_proc_func
141func f() { return (0) }
142func f() { return (1) }
143## status: 1
144## STDOUT:
145## END
146
147#### Redefining functions is allowed (with shopt -s redefine_proc_func)
148shopt -s redefine_proc_func
149func f() { return (0) }
150func f() { return (1) }
151## status: 0
152## STDOUT:
153## END
154
155#### Functions cannot redefine readonly vars (even with shopt -s redefine_proc_func)
156shopt -s redefine_proc_func
157const f = 0
158func f() { return (1) }
159## status: 1
160## STDOUT:
161## END
162
163#### Functions can redefine non-readonly vars
164var f = 0
165func f() { return (1) }
166## status: 0
167## STDOUT:
168## END
169
170#### Vars cannot redefine functions (even with shopt -s redefine_proc_func)
171shopt -s redefine_proc_func
172func f() { return (1) }
173const f = 0
174## status: 1
175## STDOUT:
176## END
177
178#### Multiple func calls
179
180func inc(x) {
181 # increment
182
183 return (x + 1)
184}
185
186func dec(x) {
187 # decrement
188
189 return (x - 1)
190}
191
192echo $[inc(1)]
193echo $[inc(inc(1))]
194echo $[dec(inc(inc(1)))]
195
196var y = dec(dec(1))
197echo $[dec(y)]
198
199## STDOUT:
2002
2013
2022
203-2
204## END
205
206#### Undefined var in function
207
208func g(x) {
209 var z = y # make sure dynamic scope is off
210 return (x + z)
211}
212
213func f() {
214 var y = 42 # if dynamic scope were on, g() would see this
215 return (g(0))
216}
217
218echo $[f()]
219
220## status: 1
221## STDOUT:
222## END
223
224#### Param binding semantics
225# value
226var x = 'foo'
227
228func f(x) {
229 setvar x = 'bar'
230}
231
232pp line (x)
233pp line (f(x))
234pp line (x)
235
236# reference
237var y = ['a', 'b', 'c']
238
239func g(y) {
240 setvar y[0] = 'z'
241}
242
243pp line (y)
244pp line (g(y))
245pp line (y)
246## STDOUT:
247(Str) "foo"
248(Null) null
249(Str) "foo"
250(List) ["a","b","c"]
251(Null) null
252(List) ["z","b","c"]
253## END
254
255#### Recursive functions
256func fib(n) {
257 # TODO: add assert n > 0
258 if (n < 2) {
259 return (n)
260 }
261
262 return (fib(n - 1) + fib(n - 2))
263}
264
265json write (fib(10))
266## STDOUT:
26755
268## END
269
270#### Recursive functions with LRU Cache
271source --builtin list.ysh
272
273var cache = []
274var maxSize = 4
275
276func remove(l, i) {
277 for i in (i .. len(l) - 1) {
278 setvar l[i] = l[i + 1]
279 }
280
281 call l->pop() # remove duplicate last element
282}
283
284func fib(n) {
285 var i = len(cache) - 1
286 var j = 0;
287 while (i >= 0) {
288 var item = cache[i]
289
290 if (item[0] === n) {
291 call remove(cache, i)
292 call cache->append(item)
293
294 echo hit: $n
295 return (item[1])
296 }
297
298 setvar i = i - 1
299 setvar j += 1
300 }
301
302 var result = 0
303 if (n < 2) {
304 setvar result = n
305 } else {
306 setvar result = fib(n - 1) + fib(n - 2)
307 }
308
309 if (len(cache) >= maxSize) {
310 call remove(cache, 0)
311 }
312 call cache->append([n, result])
313
314 return (result)
315}
316
317json write (fib(10))
318json write (cache)
319
320## STDOUT:
321hit: 1
322hit: 2
323hit: 3
324hit: 4
325hit: 5
326hit: 6
327hit: 7
328hit: 8
32955
330[
331 [
332 7,
333 13
334 ],
335 [
336 9,
337 34
338 ],
339 [
340 8,
341 21
342 ],
343 [
344 10,
345 55
346 ]
347]
348## END
349
350#### Varadic arguments, no other args
351func f(...args) {
352pp line (args)
353}
354
355call f()
356call f(1)
357call f(1, 2)
358call f(1, 2, 3)
359## STDOUT:
360(List) []
361(List) [1]
362(List) [1,2]
363(List) [1,2,3]
364## END
365
366#### Varadic arguments, other args
367func f(a, b, ...args) {
368pp line ([a, b, args])
369}
370
371call f(1, 2)
372call f(1, 2, 3)
373call f(1, 2, 3, 4)
374## STDOUT:
375(List) [1,2,[]]
376(List) [1,2,[3]]
377(List) [1,2,[3,4]]
378## END
379
380#### Varadic arguments, too few args
381func f(a, b, ...args) {
382 = [a, b, args]
383}
384
385call f(1)
386## status: 3
387## STDOUT:
388## END
389
390#### Userland max
391func mymax (...args) {
392 if (len(args) === 0) {
393 error 'Requires 1 arg'
394 } elif (len(args) === 1) {
395 # TODO: assert List
396 var mylist = args[0]
397 var max = mylist[0]
398
399 for item in (mylist) {
400 if (item > max) {
401 setvar max = item
402 }
403 }
404
405 return (max)
406 } elif (len(args) === 2) {
407 if (args[0] >= args[1]) {
408 return (args[0])
409 } else {
410 return (args[1])
411 }
412 } else {
413 # max(1, 2, 3) doesn't work in YSH, but does in Python
414 error 'too many'
415 }
416}
417
418= mymax(5,6) # => 6
419= mymax([5,6,7]) # => 7
420= mymax(5,6,7,8) # error
421
422## status: 10
423## STDOUT:
424(Int) 6
425(Int) 7
426## END
427
428#### Functions share a namespace with variables
429func f(x) {
430 return (x * x)
431}
432
433var g = f
434echo "g(2) -> $[g(2)]"
435## STDOUT:
436g(2) -> 4
437## END
438
439#### We can store funcs in dictionaries
440func dog_speak() {
441 echo "Woof"
442}
443
444func dog_type() {
445 return ("DOG")
446}
447
448const Dog = {
449 speak: dog_speak,
450 type: dog_type,
451}
452
453func cat_speak() {
454 echo "Meow"
455}
456
457func cat_type() {
458 return ("CAT")
459}
460
461const Cat = {
462 speak: cat_speak,
463 type: cat_type,
464}
465
466# First class "modules"!
467const animals = [Dog, Cat]
468for animal in (animals) {
469 var type = animal.type()
470 echo This is a $type
471 call animal.speak()
472}
473## STDOUT:
474This is a DOG
475Woof
476This is a CAT
477Meow
478## END
479
480#### Functions cannot be nested
481proc build {
482 func f(x) {
483 return (x)
484 }
485}
486## status: 2
487## STDOUT:
488## END
489
490#### Functions can be shadowed
491func mysum(items) {
492 var mysum = 0
493 for x in (items) {
494 setvar mysum += x
495 }
496 return (mysum)
497}
498
499echo 1 + 2 + 3 = $[mysum([1, 2, 3])]
500
501func inAnotherScope() {
502 # variable mysum has not yet shadowed func mysum in evaluation
503 var mysum = mysum([1, 2, 3])
504 echo mysum=$mysum
505}
506call inAnotherScope()
507
508# We need a scope otherwise we'd overwrite `mysum` in the global scope
509var mysum = mysum([1, 2, 3]) # will raise status=1
510## status: 1
511## STDOUT:
5121 + 2 + 3 = 6
513mysum=6
514## END
515
516#### Function names cannot be redeclared
517# Behaves like: const f = ...
518func f(x) {
519 return (x)
520}
521
522var f = "some val"
523## status: 1
524## STDOUT:
525## END
526
527#### Functions cannot be mutated
528func f(x) {
529 return (x)
530}
531
532setvar f = "some val"
533## status: 1
534## STDOUT:
535## END