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