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

536 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 json write --pretty=F (named)
68}
69
70call f()
71
72call f(x=4)
73
74call f(x=4, y=5)
75
76## STDOUT:
77x=3
78{}
79x=4
80{}
81x=4
82{"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 json write --pretty=F (pos)
108 json write --pretty=F (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[1,2,3,1,2,3]
119{"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))
318#json write --pretty=F (cache)
319json write (cache)
320
321## STDOUT:
322hit: 1
323hit: 2
324hit: 3
325hit: 4
326hit: 5
327hit: 6
328hit: 7
329hit: 8
33055
331[
332 [
333 7,
334 13
335 ],
336 [
337 9,
338 34
339 ],
340 [
341 8,
342 21
343 ],
344 [
345 10,
346 55
347 ]
348]
349## END
350
351#### Varadic arguments, no other args
352func f(...args) {
353pp line (args)
354}
355
356call f()
357call f(1)
358call f(1, 2)
359call f(1, 2, 3)
360## STDOUT:
361(List) []
362(List) [1]
363(List) [1,2]
364(List) [1,2,3]
365## END
366
367#### Varadic arguments, other args
368func f(a, b, ...args) {
369pp line ([a, b, args])
370}
371
372call f(1, 2)
373call f(1, 2, 3)
374call f(1, 2, 3, 4)
375## STDOUT:
376(List) [1,2,[]]
377(List) [1,2,[3]]
378(List) [1,2,[3,4]]
379## END
380
381#### Varadic arguments, too few args
382func f(a, b, ...args) {
383 = [a, b, args]
384}
385
386call f(1)
387## status: 3
388## STDOUT:
389## END
390
391#### Userland max
392func mymax (...args) {
393 if (len(args) === 0) {
394 error 'Requires 1 arg'
395 } elif (len(args) === 1) {
396 # TODO: assert List
397 var mylist = args[0]
398 var max = mylist[0]
399
400 for item in (mylist) {
401 if (item > max) {
402 setvar max = item
403 }
404 }
405
406 return (max)
407 } elif (len(args) === 2) {
408 if (args[0] >= args[1]) {
409 return (args[0])
410 } else {
411 return (args[1])
412 }
413 } else {
414 # max(1, 2, 3) doesn't work in YSH, but does in Python
415 error 'too many'
416 }
417}
418
419= mymax(5,6) # => 6
420= mymax([5,6,7]) # => 7
421= mymax(5,6,7,8) # error
422
423## status: 10
424## STDOUT:
425(Int) 6
426(Int) 7
427## END
428
429#### Functions share a namespace with variables
430func f(x) {
431 return (x * x)
432}
433
434var g = f
435echo "g(2) -> $[g(2)]"
436## STDOUT:
437g(2) -> 4
438## END
439
440#### We can store funcs in dictionaries
441func dog_speak() {
442 echo "Woof"
443}
444
445func dog_type() {
446 return ("DOG")
447}
448
449const Dog = {
450 speak: dog_speak,
451 type: dog_type,
452}
453
454func cat_speak() {
455 echo "Meow"
456}
457
458func cat_type() {
459 return ("CAT")
460}
461
462const Cat = {
463 speak: cat_speak,
464 type: cat_type,
465}
466
467# First class "modules"!
468const animals = [Dog, Cat]
469for animal in (animals) {
470 var type = animal.type()
471 echo This is a $type
472 call animal.speak()
473}
474## STDOUT:
475This is a DOG
476Woof
477This is a CAT
478Meow
479## END
480
481#### Functions cannot be nested
482proc build {
483 func f(x) {
484 return (x)
485 }
486}
487## status: 2
488## STDOUT:
489## END
490
491#### Functions can be shadowed
492func mysum(items) {
493 var mysum = 0
494 for x in (items) {
495 setvar mysum += x
496 }
497 return (mysum)
498}
499
500echo 1 + 2 + 3 = $[mysum([1, 2, 3])]
501
502func inAnotherScope() {
503 # variable mysum has not yet shadowed func mysum in evaluation
504 var mysum = mysum([1, 2, 3])
505 echo mysum=$mysum
506}
507call inAnotherScope()
508
509# We need a scope otherwise we'd overwrite `mysum` in the global scope
510var mysum = mysum([1, 2, 3]) # will raise status=1
511## status: 1
512## STDOUT:
5131 + 2 + 3 = 6
514mysum=6
515## END
516
517#### Function names cannot be redeclared
518# Behaves like: const f = ...
519func f(x) {
520 return (x)
521}
522
523var f = "some val"
524## status: 1
525## STDOUT:
526## END
527
528#### Functions cannot be mutated
529func f(x) {
530 return (x)
531}
532
533setvar f = "some val"
534## status: 1
535## STDOUT:
536## END