OILS / spec / hay.test.sh View on Github | oilshell.org

655 lines, 340 significant
1# Hay: Hay Ain't YAML
2
3## oils_failures_allowed: 2
4
5#### hay builtin usage
6
7hay define
8echo status=$?
9
10hay define -- package user
11echo status=$?
12
13hay pp | wc -l | read n
14echo read $?
15test $n -gt 0
16echo greater $?
17
18## STDOUT:
19status=2
20status=0
21read 0
22greater 0
23## END
24
25#### hay reset
26shopt --set parse_brace
27
28hay define package
29
30hay eval :a {
31 package foo
32 echo "package $?"
33}
34
35hay reset # no more names
36
37echo "reset $?"
38
39hay eval :b {
40 package foo
41 echo "package $?"
42}
43
44## status: 127
45## STDOUT:
46package 0
47reset 0
48## END
49
50
51#### hay eval can't be nested
52shopt --set parse_brace
53
54hay eval :foo {
55 echo foo
56 hay eval :bar {
57 echo bar
58 }
59}
60## status: 127
61## STDOUT:
62foo
63## END
64
65#### hay names at top level
66shopt --set parse_brace parse_at
67shopt --unset errexit
68
69hay define Package
70
71Package one
72echo status=$?
73
74setvar args = _hay()['children'][0]['args']
75write --sep ' ' $[len(_hay()['children'])] @args
76
77hay eval :result {
78 Package two
79 echo status=$?
80}
81
82setvar args = result['children'][0]['args']
83write --sep ' ' $[len(result['children'])] @args
84
85Package three
86echo status=$?
87
88setvar args = _hay()['children'][0]['args']
89write --sep ' ' $[len(_hay()['children'])] $[_hay()['children'][0]['args'][0]]
90
91## STDOUT:
92status=0
931 one
94status=0
951 two
96status=0
971 three
98## END
99
100#### Parsing Nested Attributes nodes (bug fix)
101
102shopt --set parse_brace parse_equals
103
104hay define Package/License
105
106Package glibc {
107 version = '1.0'
108
109 License {
110 path = 'LICENSE.txt'
111 }
112
113 other = 'foo'
114}
115
116json write (_hay()) | jq '.children[0].children[0].attrs' > actual.txt
117
118diff -u - actual.txt <<EOF
119{
120 "path": "LICENSE.txt"
121}
122EOF
123
124invalid = 'syntax' # parse error
125
126## status: 2
127## STDOUT:
128## END
129
130#### hay eval Attr node, and JSON
131shopt --set parse_brace parse_equals
132
133hay define Package User
134
135hay eval :result {
136 Package foo {
137 # not doing floats now
138 int = 42
139 bool = true
140 mynull = null
141 mystr = $'spam\n'
142
143 mylist = [5, 'foo', {}]
144 # TODO: Dict literals need to be in insertion order!
145 #mydict = {alice: 10, bob: 20}
146 }
147
148 User alice
149}
150
151# Note: using jq to normalize
152json write (result) | jq . > out.txt
153
154diff -u - out.txt <<EOF
155{
156 "source": null,
157 "children": [
158 {
159 "type": "Package",
160 "args": [
161 "foo"
162 ],
163 "children": [],
164 "attrs": {
165 "int": 42,
166 "bool": true,
167 "mynull": null,
168 "mystr": "spam\n",
169 "mylist": [
170 5,
171 "foo",
172 {}
173 ]
174 }
175 },
176 {
177 "type": "User",
178 "args": [
179 "alice"
180 ]
181 }
182 ]
183}
184EOF
185
186echo "diff $?"
187
188## STDOUT:
189diff 0
190## END
191
192#### hay eval shell node, and JSON
193shopt --set parse_brace parse_equals
194
195hay define TASK
196
197hay eval :result {
198 TASK { echo hi }
199
200 TASK {
201 echo one
202 echo two
203 }
204}
205
206#= result
207json write (result) | jq . > out.txt
208
209diff -u - out.txt <<'EOF'
210{
211 "source": null,
212 "children": [
213 {
214 "type": "TASK",
215 "args": [],
216 "location_str": "[ stdin ]",
217 "location_start_line": 6,
218 "code_str": " echo hi "
219 },
220 {
221 "type": "TASK",
222 "args": [],
223 "location_str": "[ stdin ]",
224 "location_start_line": 8,
225 "code_str": " \n echo one\n echo two\n "
226 }
227 ]
228}
229EOF
230
231## STDOUT:
232## END
233
234
235#### _hay() register
236shopt --set parse_paren parse_brace parse_equals parse_proc
237
238hay define user
239
240var result = {}
241
242hay eval :result {
243
244 user alice
245 # = _hay()
246 write -- $[len(_hay()['children'])]
247
248 user bob
249 setvar result = _hay()
250 write -- $[len(_hay()['children'])]
251
252}
253
254# TODO: Should be cleared here
255setvar result = _hay()
256write -- $[len(_hay()['children'])]
257
258## STDOUT:
2591
2602
2610
262## END
263
264
265#### haynode builtin can define nodes
266shopt --set parse_paren parse_brace parse_equals parse_proc
267
268# It prints JSON by default? What about the code blocks?
269# Or should there be a --json flag?
270
271hay eval :result {
272
273 # note that 'const' is required because haynode isn't capitalized
274 haynode parent alice {
275 const age = '50'
276
277 haynode child bob {
278 const age = '10'
279 }
280
281 haynode child carol {
282 const age = '20'
283 }
284
285 const other = 'str'
286 }
287}
288
289#= result
290write -- 'level 0 children' $[len(result['children'])]
291write -- 'level 1 children' $[len(result['children'][0]['children'])]
292
293hay eval :result {
294 haynode parent foo
295 haynode parent bar
296}
297write -- 'level 0 children' $[len(result['children'])]
298
299
300## STDOUT:
301level 0 children
3021
303level 1 children
3042
305level 0 children
3062
307## END
308
309
310#### haynode: usage errors (name or block required)
311shopt --set parse_brace parse_equals parse_proc
312
313# should we make it name or block required?
314# license { ... } might be useful?
315
316try {
317 hay eval :result {
318 haynode package
319 }
320}
321echo "haynode attr $_status"
322var result = _hay()
323echo "LEN $[len(result['children'])]"
324
325# requires block arg
326try {
327 hay eval :result {
328 haynode TASK build
329 }
330}
331echo "haynode code $_status"
332echo "LEN $[len(result['children'])]"
333
334echo ---
335hay define package TASK
336
337try {
338 hay eval :result {
339 package
340 }
341}
342echo "define attr $_status"
343echo "LEN $[len(result['children'])]"
344
345try {
346 hay eval :result {
347 TASK build
348 }
349}
350echo "define code $_status"
351echo "LEN $[len(result['children'])]"
352
353## STDOUT:
354haynode attr 2
355LEN 0
356haynode code 2
357LEN 0
358---
359define attr 2
360LEN 0
361define code 2
362LEN 0
363## END
364
365#### haynode: shell nodes require block args; attribute nodes don't
366
367shopt --set parse_brace parse_equals parse_proc
368
369hay define package TASK
370
371try {
372 hay eval :result {
373 package glibc > /dev/null
374 }
375}
376echo "status $_status"
377
378
379try {
380 hay eval :result {
381 TASK build
382 }
383}
384echo "status $_status"
385
386## STDOUT:
387status 0
388status 2
389## END
390
391
392#### hay eval with shopt -s oil:all
393shopt --set parse_brace parse_equals parse_proc
394
395hay define Package
396
397const x = 'foo bar'
398
399hay eval :result {
400 Package foo {
401 # set -e should be active!
402 #false
403
404 version = '1.0'
405
406 # simple_word_eval should be active!
407 write -- $x
408 }
409}
410
411## STDOUT:
412foo bar
413## END
414
415#### Attr block with duplicate names
416
417shopt --set ysh:upgrade
418
419hay define Package
420
421Package cpython {
422 version = '3.11'
423 version = '3.12'
424}
425
426= _hay()
427
428## status: 1
429## STDOUT:
430## END
431
432#### Scope of Variables Inside Hay Blocks
433
434shopt --set oil:all
435
436hay define package
437hay define deps/package
438
439hay eval :result {
440
441 const URL_PATH = 'downloads/foo.tar.gz'
442
443 package foo {
444 echo "location = https://example.com/$URL_PATH"
445 echo "backup = https://archive.example.com/$URL_PATH"
446 }
447
448 # Note: PushTemp() happens here
449 deps spam {
450 # OVERRIDE
451 const URL_PATH = 'downloads/spam.tar.gz'
452
453 const URL2 = 'downloads/spam.tar.xz'
454
455 package foo {
456 # this is a global
457 echo "deps location https://example.com/$URL_PATH"
458 echo "deps backup https://archive.example.com/$URL2"
459 }
460 }
461
462 echo "AFTER $URL_PATH"
463
464}
465
466## STDOUT:
467location = https://example.com/downloads/foo.tar.gz
468backup = https://archive.example.com/downloads/foo.tar.gz
469deps location https://example.com/downloads/spam.tar.gz
470deps backup https://archive.example.com/downloads/spam.tar.xz
471AFTER downloads/foo.tar.gz
472## END
473
474
475#### hay define and then an error
476shopt --set parse_brace parse_equals parse_proc
477
478hay define Package/License User TASK
479
480hay pp defs > /dev/null
481
482hay eval :result {
483 User bob
484 echo "user $?"
485
486 Package cppunit
487 echo "package $?"
488
489 TASK build {
490 configure
491 }
492 echo "TASK $?"
493
494 Package unzip {
495 version = '1.0'
496
497 License FOO {
498 echo 'inside'
499 }
500 echo "license $?"
501
502 License BAR
503 echo "license $?"
504
505 zz foo
506 echo 'should not get here'
507 }
508}
509
510echo 'ditto'
511
512## status: 127
513## STDOUT:
514user 0
515package 0
516TASK 0
517inside
518license 0
519license 0
520## END
521
522#### parseHay()
523shopt --set parse_proc
524
525const config_path = "$REPO_ROOT/spec/testdata/config/ci.oil"
526const block = parseHay(config_path)
527
528# Are blocks opaque?
529{
530 = block
531} | wc -l | read n
532
533# Just make sure we got more than one line?
534if test "$n" -eq 1; then
535 echo "OK"
536fi
537
538## STDOUT:
539OK
540## END
541
542
543#### Code Blocks: parseHay() then shvar _DIALECT= { evalHay() }
544shopt --set parse_brace parse_proc
545
546hay define TASK
547
548const config_path = "$REPO_ROOT/spec/testdata/config/ci.oil"
549const block = parseHay(config_path)
550
551shvar _DIALECT=sourcehut {
552 const d = evalHay(block)
553}
554
555const children = d['children']
556write 'level 0 children' $[len(children)] ---
557
558# TODO: Do we need @[] for array expression sub?
559write 'child 0' $[children[0].type] $[join(children[0].args)] ---
560write 'child 1' $[children[1].type] $[join(children[1].args)] ---
561
562## STDOUT:
563level 0 children
5642
565---
566child 0
567TASK
568cpp
569---
570child 1
571TASK
572publish-html
573---
574## END
575
576#### evalHay() usage
577shopt -s parse_brace
578
579try {
580 var d = evalHay()
581}
582echo status $_status
583
584try {
585 var d = evalHay(3)
586}
587echo status $_status
588
589try {
590 var d = evalHay(^(echo hi), 5)
591}
592echo status $_status
593
594## STDOUT:
595status 3
596status 3
597status 3
598## END
599
600#### Attribute / Data Blocks (package-manager)
601shopt --set parse_proc
602
603const path = "$REPO_ROOT/spec/testdata/config/package-manager.oil"
604
605const block = parseHay(path)
606
607hay define Package
608const d = evalHay(block)
609write 'level 0 children' $[len(d['children'])]
610write 'level 1 children' $[len(d['children'][1]['children'])]
611
612## STDOUT:
613level 0 children
6143
615level 1 children
6160
617## END
618
619
620#### Typed Args to Hay Node
621
622shopt --set oil:all
623
624hay define when
625
626# Hm I get 'too many typed args'
627# Ah this is because of 'haynode'
628# 'haynode' could silently pass through blocks and typed args?
629
630when NAME (x > 0) {
631 const version = '1.0'
632 const other = 'str'
633}
634
635= _hay()
636
637## STDOUT:
638## END
639
640
641#### OSH and hay (dynamic parsing)
642
643source $REPO_ROOT/spec/testdata/config/osh-hay.osh
644
645
646## STDOUT:
647backticks
648eval
649TYPE TASK
650CODE
651 echo `echo task backticks`
652 eval 'echo task eval'
653 ___
654## END
655