OILS / spec / hay.test.sh View on Github | oilshell.org

703 lines, 371 significant
1# Hay: Hay Ain't YAML
2
3## oils_failures_allowed: 2
4
5#### use bin
6use
7echo status=$?
8use z
9echo status=$?
10
11use bin
12echo bin status=$?
13use bin sed grep
14echo bin status=$?
15
16## STDOUT:
17status=2
18status=2
19bin status=0
20bin status=0
21## END
22
23#### use dialect
24shopt --set parse_brace
25
26source --builtin ysh/shvar.ysh
27
28use dialect
29echo status=$?
30
31use dialect ninja
32echo status=$?
33
34shvar _DIALECT=oops {
35 use dialect ninja
36 echo status=$?
37}
38
39shvar _DIALECT=ninja {
40 use dialect ninja
41 echo status=$?
42}
43
44## STDOUT:
45status=2
46status=1
47status=1
48status=0
49## END
50
51#### hay builtin usage
52
53hay define
54echo status=$?
55
56hay define -- package user
57echo status=$?
58
59hay pp | wc -l | read n
60echo read $?
61test $n -gt 0
62echo greater $?
63
64## STDOUT:
65status=2
66status=0
67read 0
68greater 0
69## END
70
71#### hay reset
72shopt --set parse_brace
73
74hay define package
75
76hay eval :a {
77 package foo
78 echo "package $?"
79}
80
81hay reset # no more names
82
83echo "reset $?"
84
85hay eval :b {
86 package foo
87 echo "package $?"
88}
89
90## status: 127
91## STDOUT:
92package 0
93reset 0
94## END
95
96
97#### hay eval can't be nested
98shopt --set parse_brace
99
100hay eval :foo {
101 echo foo
102 hay eval :bar {
103 echo bar
104 }
105}
106## status: 127
107## STDOUT:
108foo
109## END
110
111#### hay names at top level
112shopt --set parse_brace parse_at
113shopt --unset errexit
114
115hay define Package
116
117Package one
118echo status=$?
119
120setvar args = _hay()['children'][0]['args']
121write --sep ' ' $[len(_hay()['children'])] @args
122
123hay eval :result {
124 Package two
125 echo status=$?
126}
127
128setvar args = result['children'][0]['args']
129write --sep ' ' $[len(result['children'])] @args
130
131Package three
132echo status=$?
133
134setvar args = _hay()['children'][0]['args']
135write --sep ' ' $[len(_hay()['children'])] $[_hay()['children'][0]['args'][0]]
136
137## STDOUT:
138status=0
1391 one
140status=0
1411 two
142status=0
1431 three
144## END
145
146#### Parsing Nested Attributes nodes (bug fix)
147
148shopt --set parse_brace parse_equals
149
150hay define Package/License
151
152Package glibc {
153 version = '1.0'
154
155 License {
156 path = 'LICENSE.txt'
157 }
158
159 other = 'foo'
160}
161
162json write (_hay()) | jq '.children[0].children[0].attrs' > actual.txt
163
164diff -u - actual.txt <<EOF
165{
166 "path": "LICENSE.txt"
167}
168EOF
169
170invalid = 'syntax' # parse error
171
172## status: 2
173## STDOUT:
174## END
175
176#### hay eval Attr node, and JSON
177shopt --set parse_brace parse_equals
178
179hay define Package User
180
181hay 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
198json write (result) | jq . > out.txt
199
200diff -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}
230EOF
231
232echo "diff $?"
233
234## STDOUT:
235diff 0
236## END
237
238#### hay eval shell node, and JSON
239shopt --set parse_brace parse_equals
240
241hay define TASK
242
243hay eval :result {
244 TASK { echo hi }
245
246 TASK {
247 echo one
248 echo two
249 }
250}
251
252#= result
253json write (result) | jq . > out.txt
254
255diff -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}
275EOF
276
277## STDOUT:
278## END
279
280
281#### _hay() register
282shopt --set parse_paren parse_brace parse_equals parse_proc
283
284hay define user
285
286var result = {}
287
288hay 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
301setvar result = _hay()
302write -- $[len(_hay()['children'])]
303
304## STDOUT:
3051
3062
3070
308## END
309
310
311#### haynode builtin can define nodes
312shopt --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
317hay 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
336write -- 'level 0 children' $[len(result['children'])]
337write -- 'level 1 children' $[len(result['children'][0]['children'])]
338
339hay eval :result {
340 haynode parent foo
341 haynode parent bar
342}
343write -- 'level 0 children' $[len(result['children'])]
344
345
346## STDOUT:
347level 0 children
3481
349level 1 children
3502
351level 0 children
3522
353## END
354
355
356#### haynode: usage errors (name or block required)
357shopt --set parse_brace parse_equals parse_proc
358
359# should we make it name or block required?
360# license { ... } might be useful?
361
362try {
363 hay eval :result {
364 haynode package
365 }
366}
367echo "haynode attr $_status"
368var result = _hay()
369echo "LEN $[len(result['children'])]"
370
371# requires block arg
372try {
373 hay eval :result {
374 haynode TASK build
375 }
376}
377echo "haynode code $_status"
378echo "LEN $[len(result['children'])]"
379
380echo ---
381hay define package TASK
382
383try {
384 hay eval :result {
385 package
386 }
387}
388echo "define attr $_status"
389echo "LEN $[len(result['children'])]"
390
391try {
392 hay eval :result {
393 TASK build
394 }
395}
396echo "define code $_status"
397echo "LEN $[len(result['children'])]"
398
399## STDOUT:
400haynode attr 2
401LEN 0
402haynode code 2
403LEN 0
404---
405define attr 2
406LEN 0
407define code 2
408LEN 0
409## END
410
411#### haynode: shell nodes require block args; attribute nodes don't
412
413shopt --set parse_brace parse_equals parse_proc
414
415hay define package TASK
416
417try {
418 hay eval :result {
419 package glibc > /dev/null
420 }
421}
422echo "status $_status"
423
424
425try {
426 hay eval :result {
427 TASK build
428 }
429}
430echo "status $_status"
431
432## STDOUT:
433status 0
434status 2
435## END
436
437
438#### hay eval with shopt -s oil:all
439shopt --set parse_brace parse_equals parse_proc
440
441hay define Package
442
443const x = 'foo bar'
444
445hay 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:
458foo bar
459## END
460
461#### Attr block with duplicate names
462
463shopt --set ysh:upgrade
464
465hay define Package
466
467Package 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
480shopt --set oil:all
481
482hay define package
483hay define deps/package
484
485hay 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:
513location = https://example.com/downloads/foo.tar.gz
514backup = https://archive.example.com/downloads/foo.tar.gz
515deps location https://example.com/downloads/spam.tar.gz
516deps backup https://archive.example.com/downloads/spam.tar.xz
517AFTER downloads/foo.tar.gz
518## END
519
520
521#### hay define and then an error
522shopt --set parse_brace parse_equals parse_proc
523
524hay define Package/License User TASK
525
526hay pp defs > /dev/null
527
528hay 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
556echo 'ditto'
557
558## status: 127
559## STDOUT:
560user 0
561package 0
562TASK 0
563inside
564license 0
565license 0
566## END
567
568#### parseHay()
569shopt --set parse_proc
570
571const config_path = "$REPO_ROOT/spec/testdata/config/ci.oil"
572const 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?
580if test "$n" -eq 1; then
581 echo "OK"
582fi
583
584## STDOUT:
585OK
586## END
587
588
589#### Code Blocks: parseHay() then shvar _DIALECT= { evalHay() }
590shopt --set parse_brace parse_proc
591
592source --builtin ysh/shvar.ysh
593
594hay define TASK
595
596const config_path = "$REPO_ROOT/spec/testdata/config/ci.oil"
597const block = parseHay(config_path)
598
599shvar _DIALECT=sourcehut {
600 const d = evalHay(block)
601}
602
603const children = d['children']
604write 'level 0 children' $[len(children)] ---
605
606# TODO: Do we need @[] for array expression sub?
607write 'child 0' $[children[0].type] $[join(children[0].args)] ---
608write 'child 1' $[children[1].type] $[join(children[1].args)] ---
609
610## STDOUT:
611level 0 children
6122
613---
614child 0
615TASK
616cpp
617---
618child 1
619TASK
620publish-html
621---
622## END
623
624#### evalHay() usage
625shopt -s parse_brace
626
627try {
628 var d = evalHay()
629}
630echo status $_status
631
632try {
633 var d = evalHay(3)
634}
635echo status $_status
636
637try {
638 var d = evalHay(^(echo hi), 5)
639}
640echo status $_status
641
642## STDOUT:
643status 3
644status 3
645status 3
646## END
647
648#### Attribute / Data Blocks (package-manager)
649shopt --set parse_proc
650
651const path = "$REPO_ROOT/spec/testdata/config/package-manager.oil"
652
653const block = parseHay(path)
654
655hay define Package
656const d = evalHay(block)
657write 'level 0 children' $[len(d['children'])]
658write 'level 1 children' $[len(d['children'][1]['children'])]
659
660## STDOUT:
661level 0 children
6623
663level 1 children
6640
665## END
666
667
668#### Typed Args to Hay Node
669
670shopt --set oil:all
671
672hay 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
678when 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
691source $REPO_ROOT/spec/testdata/config/osh-hay.osh
692
693
694## STDOUT:
695backticks
696eval
697TYPE TASK
698CODE
699 echo `echo task backticks`
700 eval 'echo task eval'
701 ___
702## END
703