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