OILS / spec / hay.test.sh View on Github | oils.pub

888 lines, 463 significant
1## oils_failures_allowed: 3
2
3# Hay: Hay Ain't YAML
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 # 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
292write -- 'level 0 children' $[len(result['children'])]
293write -- 'level 1 children' $[len(result['children'][0]['children'])]
294
295hay eval :result {
296 haynode parent foo
297 haynode parent bar
298}
299write -- 'level 0 children' $[len(result['children'])]
300
301
302## STDOUT:
303level 0 children
3041
305level 1 children
3062
307level 0 children
3082
309## END
310
311
312#### haynode: usage errors (name or block required)
313shopt --set parse_brace parse_equals parse_proc
314
315# should we make it name or block required?
316# license { ... } might be useful?
317
318try {
319 hay eval :result {
320 haynode package
321 }
322}
323echo "haynode attr $_status"
324var result = _hay()
325echo "LEN $[len(result['children'])]"
326
327# requires block arg
328try {
329 hay eval :result {
330 haynode TASK build
331 }
332}
333echo "haynode code $_status"
334echo "LEN $[len(result['children'])]"
335
336echo ---
337hay define package TASK
338
339try {
340 hay eval :result {
341 package
342 }
343}
344echo "define attr $_status"
345echo "LEN $[len(result['children'])]"
346
347try {
348 hay eval :result {
349 TASK build
350 }
351}
352echo "define code $_status"
353echo "LEN $[len(result['children'])]"
354
355## STDOUT:
356haynode attr 2
357LEN 0
358haynode code 2
359LEN 0
360---
361define attr 2
362LEN 0
363define code 2
364LEN 0
365## END
366
367#### haynode: shell nodes require block args; attribute nodes don't
368
369shopt --set parse_brace parse_equals parse_proc
370
371hay define package TASK
372
373try {
374 hay eval :result {
375 package glibc > /dev/null
376 }
377}
378echo "status $_status"
379
380
381try {
382 hay eval :result {
383 TASK build
384 }
385}
386echo "status $_status"
387
388## STDOUT:
389status 0
390status 2
391## END
392
393
394#### hay eval with shopt -s ysh:all
395shopt --set parse_brace parse_equals parse_proc
396
397hay define Package
398
399const x = 'foo bar'
400
401hay 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:
414foo bar
415## END
416
417#### Attr block with duplicate names
418
419shopt --set ysh:upgrade
420
421hay define Package
422
423Package 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
436shopt --set ysh:all
437
438hay define package
439hay define deps/package
440
441hay 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:
469location = https://example.com/downloads/foo.tar.gz
470backup = https://archive.example.com/downloads/foo.tar.gz
471deps location https://example.com/downloads/spam.tar.gz
472deps backup https://archive.example.com/downloads/spam.tar.xz
473AFTER downloads/foo.tar.gz
474## END
475
476#### Nested bare assignment
477shopt --set ysh:all
478
479hay define Package/Deps
480
481Package {
482 x = 10
483 Deps {
484 # this is a const
485 x = 20
486 }
487}
488
489json write (_hay())
490
491## STDOUT:
492{
493 "source": null,
494 "children": [
495 {
496 "type": "Package",
497 "args": [],
498 "children": [
499 {
500 "type": "Deps",
501 "args": [],
502 "children": [],
503 "attrs": {
504 "x": 20
505 }
506 }
507 ],
508 "attrs": {
509 "x": 10
510 }
511 }
512 ]
513}
514## END
515
516#### Param with same name as Hay attribute
517shopt --set ysh:all
518
519# Danilo reported this on Zulip
520
521hay define Service
522
523proc gen-service(; ; variant = null) {
524 Service {
525 variant = variant
526 port = 80
527 }
528}
529
530gen-service
531gen-service (variant = 'z')
532
533var attrs = _hay().children[0].attrs
534json write (attrs)
535
536## STDOUT:
537{
538 "variant": null,
539 "port": 80
540}
541## END
542
543
544#### hay define and then an error
545shopt --set parse_brace parse_equals parse_proc
546
547hay define Package/License User TASK
548
549hay pp defs > /dev/null
550
551hay eval :result {
552 User bob
553 echo "user $?"
554
555 Package cppunit
556 echo "package $?"
557
558 TASK build {
559 configure
560 }
561 echo "TASK $?"
562
563 Package unzip {
564 version = '1.0'
565
566 License FOO {
567 echo 'inside'
568 }
569 echo "license $?"
570
571 License BAR
572 echo "license $?"
573
574 zz foo
575 echo 'should not get here'
576 }
577}
578
579echo 'ditto'
580
581## status: 127
582## STDOUT:
583user 0
584package 0
585TASK 0
586inside
587license 0
588license 0
589## END
590
591#### parseHay()
592shopt --set parse_proc
593
594const config_path = "$REPO_ROOT/spec/testdata/config/ci.oil"
595const block = parseHay(config_path)
596
597# Are blocks opaque?
598{
599 = block
600} | wc -l | read n
601
602# Just make sure we got more than one line?
603if test "$n" -eq 1; then
604 echo "OK"
605fi
606
607## STDOUT:
608OK
609## END
610
611
612#### Code Blocks: parseHay() then shvar _DIALECT= { evalHay() }
613shopt --set parse_brace parse_proc
614
615hay define TASK
616
617const config_path = "$REPO_ROOT/spec/testdata/config/ci.oil"
618const block = parseHay(config_path)
619
620shvar _DIALECT=sourcehut {
621 const d = evalHay(block)
622}
623
624const children = d['children']
625write 'level 0 children' $[len(children)] ---
626
627# TODO: Do we need @[] for array expression sub?
628write 'child 0' $[children[0].type] $[join(children[0].args)] ---
629write 'child 1' $[children[1].type] $[join(children[1].args)] ---
630
631## STDOUT:
632level 0 children
6332
634---
635child 0
636TASK
637cpp
638---
639child 1
640TASK
641publish-html
642---
643## END
644
645#### evalHay() usage
646shopt -s parse_brace
647
648try {
649 var d = evalHay()
650}
651echo status $_status
652
653try {
654 var d = evalHay(3)
655}
656echo status $_status
657
658try {
659 var d = evalHay(^(echo hi), 5)
660}
661echo status $_status
662
663## STDOUT:
664status 3
665status 3
666status 3
667## END
668
669#### Attribute / Data Blocks (package-manager)
670shopt --set parse_proc
671
672const path = "$REPO_ROOT/spec/testdata/config/package-manager.oil"
673
674const block = parseHay(path)
675
676hay define Package
677const d = evalHay(block)
678write 'level 0 children' $[len(d['children'])]
679write 'level 1 children' $[len(d['children'][1]['children'])]
680
681## STDOUT:
682level 0 children
6833
684level 1 children
6850
686## END
687
688
689#### Typed Args to Hay Node
690shopt --set ysh:all
691
692hay define when
693
694# Hm I get 'too many typed args'
695# Ah this is because of 'haynode'
696# 'haynode' could silently pass through blocks and typed args?
697
698when NAME [x > 0] {
699 const version = '1.0'
700 const other = 'str'
701}
702
703= _hay()
704
705## STDOUT:
706## END
707
708
709#### OSH and hay (dynamic parsing)
710
711source $REPO_ROOT/spec/testdata/config/osh-hay.osh
712
713## STDOUT:
714backticks
715eval
716TYPE TASK
717CODE
718 echo `echo task backticks`
719 eval 'echo task eval'
720 ___
721## END
722
723#### CODE node provides code_str, serialized code - issue #2050
724shopt --set ysh:all
725
726hay define Package
727hay define Package/INSTALL
728
729Package {
730 name = "osh"
731 INSTALL {
732 #echo hi
733
734 # The block causes a bug? Nesting?
735 cd dist {
736 ./install
737 }
738 }
739}
740
741= _hay()
742
743## STDOUT:
744## END
745
746#### Proc within Hay node
747shopt --set ysh:all
748
749hay define Package
750
751Package cpython {
752 version = '3.11'
753
754 proc build {
755 # procs have to capture
756 echo "version=$version"
757 make
758 }
759}
760
761# OK we have the proc
762= _hay()
763
764var build_proc = _hay().children[0].attrs.build
765
766= build_proc
767
768build_proc
769
770#json write (_hay())
771
772## STDOUT:
773## END
774
775
776#### Using Hay node from another module
777shopt --set ysh:all
778
779hay define Package/INSTALL
780
781use $[ENV.REPO_ROOT]/spec/testdata/config/use-hay.ysh
782
783#pp test_ (_hay())
784json write (_hay().children[0].attrs)
785
786## STDOUT:
787{
788 "version": "3.3"
789}
790## END
791
792#### Defining Hay node in another module
793shopt --set ysh:all
794
795use $[ENV.REPO_ROOT]/spec/testdata/config/define-hay.ysh
796
797Package foo {
798 version = '3.3'
799 INSTALL {
800 echo version=$version
801 }
802}
803
804json write (_hay().children[0].attrs)
805
806## STDOUT:
807{
808 "version": "3.3"
809}
810## END
811
812
813#### Using Hay with --eval flags
814shopt --set ysh:all
815
816echo 'hay define Package' >pre.ysh
817
818echo '
819Package cpython {
820 version = "3.12"
821 url = "https://python.org/release/$version/"
822 proc build {
823 echo "version = $version, url = $url"
824 }
825}
826' >def.hay
827
828# TODO:
829# null_replacer=true
830# JavaScript has a second "replacer" arg, which can be a function, or an array
831# I guess you can specify replacer=null
832#
833# Invert it: Or maybe type_errors=true
834#
835# When type_errors=false (default), any unserializable value becomes null
836
837echo 'json write (_hay().children[0], type_errors=false)' > stage-1.ysh
838
839# Stage 1
840
841... $[ENV.SH] -o ysh:all
842 # TODO: restore purity
843 #--eval-pure pre.ysh
844 #--eval-pure def.hay
845 #--eval-pure stage-1.ysh
846 --eval pre.ysh
847 --eval def.hay
848 --eval stage-1.ysh
849 -c ''
850 || true
851 ;
852
853# Stage 2
854
855echo '
856var pkg = _hay().children[0]
857var build_proc = pkg.attrs.build
858build_proc
859' > stage-2.ysh
860
861# Stage 1
862
863... $[ENV.SH] -o ysh:all
864 # TODO: restore purity
865 #--eval-pure pre.ysh
866 #--eval-pure def.hay
867 --eval pre.ysh
868 --eval def.hay
869 --eval stage-2.ysh # This one isn't pure
870 -c ''
871 ;
872
873
874## STDOUT:
875{
876 "type": "Package",
877 "args": [
878 "cpython"
879 ],
880 "children": [],
881 "attrs": {
882 "version": "3.12",
883 "url": "https://python.org/release/3.12/",
884 "build": null
885 }
886}
887version = 3.12, url = https://python.org/release/3.12/
888## END