1 ## oils_failures_allowed: 7
2
3 # Hay: Hay Ain't YAML
4
5 #### hay builtin usage
6
7 hay define
8 echo status=$?
9
10 hay define -- package user
11 echo status=$?
12
13 hay pp | wc -l | read n
14 echo read $?
15 test $n -gt 0
16 echo greater $?
17
18 ## STDOUT:
19 status=2
20 status=0
21 read 0
22 greater 0
23 ## END
24
25 #### hay reset
26 shopt --set parse_brace
27
28 hay define package
29
30 hay eval :a {
31 package foo
32 echo "package $?"
33 }
34
35 hay reset # no more names
36
37 echo "reset $?"
38
39 hay eval :b {
40 package foo
41 echo "package $?"
42 }
43
44 ## status: 127
45 ## STDOUT:
46 package 0
47 reset 0
48 ## END
49
50
51 #### hay eval can't be nested
52 shopt --set parse_brace
53
54 hay eval :foo {
55 echo foo
56 hay eval :bar {
57 echo bar
58 }
59 }
60 ## status: 127
61 ## STDOUT:
62 foo
63 ## END
64
65 #### hay names at top level
66 shopt --set parse_brace parse_at
67 shopt --unset errexit
68
69 hay define Package
70
71 Package one
72 echo status=$?
73
74 setvar args = _hay()['children'][0]['args']
75 write --sep ' ' $[len(_hay()['children'])] @args
76
77 hay eval :result {
78 Package two
79 echo status=$?
80 }
81
82 setvar args = result['children'][0]['args']
83 write --sep ' ' $[len(result['children'])] @args
84
85 Package three
86 echo status=$?
87
88 setvar args = _hay()['children'][0]['args']
89 write --sep ' ' $[len(_hay()['children'])] $[_hay()['children'][0]['args'][0]]
90
91 ## STDOUT:
92 status=0
93 1 one
94 status=0
95 1 two
96 status=0
97 1 three
98 ## END
99
100 #### Parsing Nested Attributes nodes (bug fix)
101
102 shopt --set parse_brace parse_equals
103
104 hay define Package/License
105
106 Package glibc {
107 version = '1.0'
108
109 License {
110 path = 'LICENSE.txt'
111 }
112
113 other = 'foo'
114 }
115
116 json write (_hay()) | jq '.children[0].children[0].attrs' > actual.txt
117
118 diff -u - actual.txt <<EOF
119 {
120 "path": "LICENSE.txt"
121 }
122 EOF
123
124 invalid = 'syntax' # parse error
125
126 ## status: 2
127 ## STDOUT:
128 ## END
129
130 #### hay eval Attr node, and JSON
131 shopt --set parse_brace parse_equals
132
133 hay define Package User
134
135 hay 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
152 json write (result) | jq . > out.txt
153
154 diff -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 }
184 EOF
185
186 echo "diff $?"
187
188 ## STDOUT:
189 diff 0
190 ## END
191
192 #### hay eval shell node, and JSON
193 shopt --set parse_brace parse_equals
194
195 hay define TASK
196
197 hay eval :result {
198 TASK { echo hi }
199
200 TASK {
201 echo one
202 echo two
203 }
204 }
205
206 #= result
207 json write (result) | jq . > out.txt
208
209 diff -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 }
229 EOF
230
231 ## STDOUT:
232 ## END
233
234
235 #### _hay() register
236 shopt --set parse_paren parse_brace parse_equals parse_proc
237
238 hay define user
239
240 var result = {}
241
242 hay 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
255 setvar result = _hay()
256 write -- $[len(_hay()['children'])]
257
258 ## STDOUT:
259 1
260 2
261 0
262 ## END
263
264
265 #### haynode builtin can define nodes
266 shopt --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
271 hay 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 # TODO: Is 'const' being created in the old ENCLOSING frame? Not the new
279 # ENCLOSED one?
280 const age = '10'
281 }
282
283 haynode child carol {
284 const age = '20'
285 }
286
287 const other = 'str'
288 }
289 }
290
291 #= result
292 write -- 'level 0 children' $[len(result['children'])]
293 write -- 'level 1 children' $[len(result['children'][0]['children'])]
294
295 hay eval :result {
296 haynode parent foo
297 haynode parent bar
298 }
299 write -- 'level 0 children' $[len(result['children'])]
300
301
302 ## STDOUT:
303 level 0 children
304 1
305 level 1 children
306 2
307 level 0 children
308 2
309 ## END
310
311
312 #### haynode: usage errors (name or block required)
313 shopt --set parse_brace parse_equals parse_proc
314
315 # should we make it name or block required?
316 # license { ... } might be useful?
317
318 try {
319 hay eval :result {
320 haynode package
321 }
322 }
323 echo "haynode attr $_status"
324 var result = _hay()
325 echo "LEN $[len(result['children'])]"
326
327 # requires block arg
328 try {
329 hay eval :result {
330 haynode TASK build
331 }
332 }
333 echo "haynode code $_status"
334 echo "LEN $[len(result['children'])]"
335
336 echo ---
337 hay define package TASK
338
339 try {
340 hay eval :result {
341 package
342 }
343 }
344 echo "define attr $_status"
345 echo "LEN $[len(result['children'])]"
346
347 try {
348 hay eval :result {
349 TASK build
350 }
351 }
352 echo "define code $_status"
353 echo "LEN $[len(result['children'])]"
354
355 ## STDOUT:
356 haynode attr 2
357 LEN 0
358 haynode code 2
359 LEN 0
360 ---
361 define attr 2
362 LEN 0
363 define code 2
364 LEN 0
365 ## END
366
367 #### haynode: shell nodes require block args; attribute nodes don't
368
369 shopt --set parse_brace parse_equals parse_proc
370
371 hay define package TASK
372
373 try {
374 hay eval :result {
375 package glibc > /dev/null
376 }
377 }
378 echo "status $_status"
379
380
381 try {
382 hay eval :result {
383 TASK build
384 }
385 }
386 echo "status $_status"
387
388 ## STDOUT:
389 status 0
390 status 2
391 ## END
392
393
394 #### hay eval with shopt -s ysh:all
395 shopt --set parse_brace parse_equals parse_proc
396
397 hay define Package
398
399 const x = 'foo bar'
400
401 hay eval :result {
402 Package foo {
403 # set -e should be active!
404 #false
405
406 version = '1.0'
407
408 # simple_word_eval should be active!
409 write -- $x
410 }
411 }
412
413 ## STDOUT:
414 foo bar
415 ## END
416
417 #### Attr block with duplicate names
418
419 shopt --set ysh:upgrade
420
421 hay define Package
422
423 Package cpython {
424 version = '3.11'
425 version = '3.12'
426 }
427
428 = _hay()
429
430 ## status: 1
431 ## STDOUT:
432 ## END
433
434 #### Scope of Variables Inside Hay Blocks
435
436 shopt --set ysh:all
437
438 hay define package
439 hay define deps/package
440
441 hay eval :result {
442
443 const URL_PATH = 'downloads/foo.tar.gz'
444
445 package foo {
446 echo "location = https://example.com/$URL_PATH"
447 echo "backup = https://archive.example.com/$URL_PATH"
448 }
449
450 # Note: PushTemp() happens here
451 deps spam {
452 # OVERRIDE
453 const URL_PATH = 'downloads/spam.tar.gz'
454
455 const URL2 = 'downloads/spam.tar.xz'
456
457 package foo {
458 # this is a global
459 echo "deps location https://example.com/$URL_PATH"
460 echo "deps backup https://archive.example.com/$URL2"
461 }
462 }
463
464 echo "AFTER $URL_PATH"
465
466 }
467
468 ## STDOUT:
469 location = https://example.com/downloads/foo.tar.gz
470 backup = https://archive.example.com/downloads/foo.tar.gz
471 deps location https://example.com/downloads/spam.tar.gz
472 deps backup https://archive.example.com/downloads/spam.tar.xz
473 AFTER downloads/foo.tar.gz
474 ## END
475
476 #### Nested bare assignment
477 shopt --set ysh:all
478
479 hay define Package/Deps
480
481 Package {
482 x = 10
483 Deps {
484 # this is a const
485 x = 20
486 }
487 }
488
489 pp test_ (_hay())
490
491 ## STDOUT:
492 ## END
493
494
495 #### hay define and then an error
496 shopt --set parse_brace parse_equals parse_proc
497
498 hay define Package/License User TASK
499
500 hay pp defs > /dev/null
501
502 hay eval :result {
503 User bob
504 echo "user $?"
505
506 Package cppunit
507 echo "package $?"
508
509 TASK build {
510 configure
511 }
512 echo "TASK $?"
513
514 Package unzip {
515 version = '1.0'
516
517 License FOO {
518 echo 'inside'
519 }
520 echo "license $?"
521
522 License BAR
523 echo "license $?"
524
525 zz foo
526 echo 'should not get here'
527 }
528 }
529
530 echo 'ditto'
531
532 ## status: 127
533 ## STDOUT:
534 user 0
535 package 0
536 TASK 0
537 inside
538 license 0
539 license 0
540 ## END
541
542 #### parseHay()
543 shopt --set parse_proc
544
545 const config_path = "$REPO_ROOT/spec/testdata/config/ci.oil"
546 const block = parseHay(config_path)
547
548 # Are blocks opaque?
549 {
550 = block
551 } | wc -l | read n
552
553 # Just make sure we got more than one line?
554 if test "$n" -eq 1; then
555 echo "OK"
556 fi
557
558 ## STDOUT:
559 OK
560 ## END
561
562
563 #### Code Blocks: parseHay() then shvar _DIALECT= { evalHay() }
564 shopt --set parse_brace parse_proc
565
566 hay define TASK
567
568 const config_path = "$REPO_ROOT/spec/testdata/config/ci.oil"
569 const block = parseHay(config_path)
570
571 shvar _DIALECT=sourcehut {
572 const d = evalHay(block)
573 }
574
575 const children = d['children']
576 write 'level 0 children' $[len(children)] ---
577
578 # TODO: Do we need @[] for array expression sub?
579 write 'child 0' $[children[0].type] $[join(children[0].args)] ---
580 write 'child 1' $[children[1].type] $[join(children[1].args)] ---
581
582 ## STDOUT:
583 level 0 children
584 2
585 ---
586 child 0
587 TASK
588 cpp
589 ---
590 child 1
591 TASK
592 publish-html
593 ---
594 ## END
595
596 #### evalHay() usage
597 shopt -s parse_brace
598
599 try {
600 var d = evalHay()
601 }
602 echo status $_status
603
604 try {
605 var d = evalHay(3)
606 }
607 echo status $_status
608
609 try {
610 var d = evalHay(^(echo hi), 5)
611 }
612 echo status $_status
613
614 ## STDOUT:
615 status 3
616 status 3
617 status 3
618 ## END
619
620 #### Attribute / Data Blocks (package-manager)
621 shopt --set parse_proc
622
623 const path = "$REPO_ROOT/spec/testdata/config/package-manager.oil"
624
625 const block = parseHay(path)
626
627 hay define Package
628 const d = evalHay(block)
629 write 'level 0 children' $[len(d['children'])]
630 write 'level 1 children' $[len(d['children'][1]['children'])]
631
632 ## STDOUT:
633 level 0 children
634 3
635 level 1 children
636 0
637 ## END
638
639
640 #### Typed Args to Hay Node
641 shopt --set ysh:all
642
643 hay define when
644
645 # Hm I get 'too many typed args'
646 # Ah this is because of 'haynode'
647 # 'haynode' could silently pass through blocks and typed args?
648
649 when NAME [x > 0] {
650 const version = '1.0'
651 const other = 'str'
652 }
653
654 = _hay()
655
656 ## STDOUT:
657 ## END
658
659
660 #### OSH and hay (dynamic parsing)
661
662 source $REPO_ROOT/spec/testdata/config/osh-hay.osh
663
664 # TODO: code not serialized correctly - Samuel brought this up
665
666 ## STDOUT:
667 backticks
668 eval
669 TYPE TASK
670 CODE
671 echo `echo task backticks`
672 eval 'echo task eval'
673 ___
674 ## END
675
676 #### CODE node provides code_str, serialized code - issue #2050
677 shopt --set ysh:all
678
679 hay define Package
680 hay define Package/INSTALL
681
682 Package {
683 name = "osh"
684 INSTALL {
685 #echo hi
686
687 # The block causes a bug? Nesting?
688 cd dist {
689 ./install
690 }
691 }
692 }
693
694 = _hay()
695
696 ## STDOUT:
697 ## END
698
699 #### Proc within Hay node
700 shopt --set ysh:all
701
702 hay define Package
703
704 Package cpython {
705 version = '3.11'
706
707 proc build {
708 # procs have to capture
709 echo "version=$version"
710 make
711 }
712 }
713
714 # OK we have the proc
715 = _hay()
716
717 var build_proc = _hay().children[0].attrs.build
718
719 = build_proc
720
721 build_proc
722
723 #json write (_hay())
724
725 ## STDOUT:
726 ## END
727
728
729 #### Using Hay node from another module
730 shopt --set ysh:all
731
732 hay define Package/INSTALL
733
734 use $[ENV.REPO_ROOT]/spec/testdata/config/use-hay.ysh
735
736 #pp test_ (_hay())
737 json write (_hay().children[0].attrs)
738
739 ## STDOUT:
740 {
741 "version": "3.3"
742 }
743 ## END
744
745 #### Defining Hay node in another module
746 shopt --set ysh:all
747
748 use $[ENV.REPO_ROOT]/spec/testdata/config/define-hay.ysh
749
750 Package foo {
751 version = '3.3'
752 INSTALL {
753 echo version=$version
754 }
755 }
756
757 json write (_hay().children[0].attrs)
758
759 ## STDOUT:
760 {
761 "version": "3.3"
762 }
763 ## END
764