1 # Hay: Hay Ain't YAML
2
3 ## oils_failures_allowed: 2
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 const age = '10'
279 }
280
281 haynode child carol {
282 const age = '20'
283 }
284
285 const other = 'str'
286 }
287 }
288
289 #= result
290 write -- 'level 0 children' $[len(result['children'])]
291 write -- 'level 1 children' $[len(result['children'][0]['children'])]
292
293 hay eval :result {
294 haynode parent foo
295 haynode parent bar
296 }
297 write -- 'level 0 children' $[len(result['children'])]
298
299
300 ## STDOUT:
301 level 0 children
302 1
303 level 1 children
304 2
305 level 0 children
306 2
307 ## END
308
309
310 #### haynode: usage errors (name or block required)
311 shopt --set parse_brace parse_equals parse_proc
312
313 # should we make it name or block required?
314 # license { ... } might be useful?
315
316 try {
317 hay eval :result {
318 haynode package
319 }
320 }
321 echo "haynode attr $_status"
322 var result = _hay()
323 echo "LEN $[len(result['children'])]"
324
325 # requires block arg
326 try {
327 hay eval :result {
328 haynode TASK build
329 }
330 }
331 echo "haynode code $_status"
332 echo "LEN $[len(result['children'])]"
333
334 echo ---
335 hay define package TASK
336
337 try {
338 hay eval :result {
339 package
340 }
341 }
342 echo "define attr $_status"
343 echo "LEN $[len(result['children'])]"
344
345 try {
346 hay eval :result {
347 TASK build
348 }
349 }
350 echo "define code $_status"
351 echo "LEN $[len(result['children'])]"
352
353 ## STDOUT:
354 haynode attr 2
355 LEN 0
356 haynode code 2
357 LEN 0
358 ---
359 define attr 2
360 LEN 0
361 define code 2
362 LEN 0
363 ## END
364
365 #### haynode: shell nodes require block args; attribute nodes don't
366
367 shopt --set parse_brace parse_equals parse_proc
368
369 hay define package TASK
370
371 try {
372 hay eval :result {
373 package glibc > /dev/null
374 }
375 }
376 echo "status $_status"
377
378
379 try {
380 hay eval :result {
381 TASK build
382 }
383 }
384 echo "status $_status"
385
386 ## STDOUT:
387 status 0
388 status 2
389 ## END
390
391
392 #### hay eval with shopt -s oil:all
393 shopt --set parse_brace parse_equals parse_proc
394
395 hay define Package
396
397 const x = 'foo bar'
398
399 hay 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:
412 foo bar
413 ## END
414
415 #### Attr block with duplicate names
416
417 shopt --set ysh:upgrade
418
419 hay define Package
420
421 Package 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
434 shopt --set oil:all
435
436 hay define package
437 hay define deps/package
438
439 hay 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:
467 location = https://example.com/downloads/foo.tar.gz
468 backup = https://archive.example.com/downloads/foo.tar.gz
469 deps location https://example.com/downloads/spam.tar.gz
470 deps backup https://archive.example.com/downloads/spam.tar.xz
471 AFTER downloads/foo.tar.gz
472 ## END
473
474
475 #### hay define and then an error
476 shopt --set parse_brace parse_equals parse_proc
477
478 hay define Package/License User TASK
479
480 hay pp defs > /dev/null
481
482 hay 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
510 echo 'ditto'
511
512 ## status: 127
513 ## STDOUT:
514 user 0
515 package 0
516 TASK 0
517 inside
518 license 0
519 license 0
520 ## END
521
522 #### parseHay()
523 shopt --set parse_proc
524
525 const config_path = "$REPO_ROOT/spec/testdata/config/ci.oil"
526 const 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?
534 if test "$n" -eq 1; then
535 echo "OK"
536 fi
537
538 ## STDOUT:
539 OK
540 ## END
541
542
543 #### Code Blocks: parseHay() then shvar _DIALECT= { evalHay() }
544 shopt --set parse_brace parse_proc
545
546 hay define TASK
547
548 const config_path = "$REPO_ROOT/spec/testdata/config/ci.oil"
549 const block = parseHay(config_path)
550
551 shvar _DIALECT=sourcehut {
552 const d = evalHay(block)
553 }
554
555 const children = d['children']
556 write 'level 0 children' $[len(children)] ---
557
558 # TODO: Do we need @[] for array expression sub?
559 write 'child 0' $[children[0].type] $[join(children[0].args)] ---
560 write 'child 1' $[children[1].type] $[join(children[1].args)] ---
561
562 ## STDOUT:
563 level 0 children
564 2
565 ---
566 child 0
567 TASK
568 cpp
569 ---
570 child 1
571 TASK
572 publish-html
573 ---
574 ## END
575
576 #### evalHay() usage
577 shopt -s parse_brace
578
579 try {
580 var d = evalHay()
581 }
582 echo status $_status
583
584 try {
585 var d = evalHay(3)
586 }
587 echo status $_status
588
589 try {
590 var d = evalHay(^(echo hi), 5)
591 }
592 echo status $_status
593
594 ## STDOUT:
595 status 3
596 status 3
597 status 3
598 ## END
599
600 #### Attribute / Data Blocks (package-manager)
601 shopt --set parse_proc
602
603 const path = "$REPO_ROOT/spec/testdata/config/package-manager.oil"
604
605 const block = parseHay(path)
606
607 hay define Package
608 const d = evalHay(block)
609 write 'level 0 children' $[len(d['children'])]
610 write 'level 1 children' $[len(d['children'][1]['children'])]
611
612 ## STDOUT:
613 level 0 children
614 3
615 level 1 children
616 0
617 ## END
618
619
620 #### Typed Args to Hay Node
621
622 shopt --set oil:all
623
624 hay 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
630 when 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
643 source $REPO_ROOT/spec/testdata/config/osh-hay.osh
644
645
646 ## STDOUT:
647 backticks
648 eval
649 TYPE TASK
650 CODE
651 echo `echo task backticks`
652 eval 'echo task eval'
653 ___
654 ## END
655