blob: a61131a0c44fca5ec5f3cb2f969309b75aa6c658 [file] [log] [blame]
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +01001// Copyright 2018 The CUE Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package cue
16
17import (
18 "fmt"
Marcel van Lohuizen0018c742019-02-20 01:25:39 +010019 "math/rand"
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +010020 "strconv"
21 "strings"
Marcel van Lohuizen3d30a782019-02-18 23:32:10 +010022 "unicode"
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +010023 "unicode/utf8"
24
25 "cuelang.org/go/cue/ast"
26 "cuelang.org/go/cue/token"
27)
28
Marcel van Lohuizen466e3f62019-04-06 14:16:50 +020029func doEval(m options) bool {
30 return !m.raw
31}
Marcel van Lohuizen3d30a782019-02-18 23:32:10 +010032
Marcel van Lohuizen466e3f62019-04-06 14:16:50 +020033func export(ctx *context, v value, m options) ast.Expr {
Marcel van Lohuizen0018c742019-02-20 01:25:39 +010034 e := exporter{ctx, m, nil}
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +010035 return e.expr(v)
36}
37
38type exporter struct {
Marcel van Lohuizen0018c742019-02-20 01:25:39 +010039 ctx *context
Marcel van Lohuizen466e3f62019-04-06 14:16:50 +020040 mode options
Marcel van Lohuizen0018c742019-02-20 01:25:39 +010041 stack []remap
42}
43
44type remap struct {
45 key scope // structLit or params
46 from label
47 to *ast.Ident
48 syn *ast.StructLit
49}
50
51func (p *exporter) unique(s string) string {
52 s = strings.ToUpper(s)
53 lab := s
54 for {
Marcel van Lohuizen8bc02e52019-04-01 13:14:07 +020055 if _, ok := p.ctx.findLabel(lab); !ok {
Marcel van Lohuizen0018c742019-02-20 01:25:39 +010056 p.ctx.label(lab, true)
57 break
58 }
59 lab = s + fmt.Sprintf("%0.6x", rand.Intn(1<<24))
60 }
61 return lab
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +010062}
63
Marcel van Lohuizen3d30a782019-02-18 23:32:10 +010064func (p *exporter) label(f label) ast.Label {
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +010065 orig := p.ctx.labelStr(f)
66 str := strconv.Quote(orig)
Marcel van Lohuizen3d30a782019-02-18 23:32:10 +010067 if len(orig)+2 < len(str) {
68 return &ast.BasicLit{Value: str}
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +010069 }
Marcel van Lohuizen3d30a782019-02-18 23:32:10 +010070 for i, r := range orig {
71 if unicode.IsLetter(r) || r == '_' {
72 continue
73 }
74 if i > 0 && unicode.IsDigit(r) {
75 continue
76 }
77 return &ast.BasicLit{Value: str}
78 }
79 return &ast.Ident{Name: orig}
80}
81
82func (p *exporter) identifier(f label) *ast.Ident {
83 str := p.ctx.labelStr(f)
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +010084 return &ast.Ident{Name: str}
85}
86
87func (p *exporter) ident(str string) *ast.Ident {
88 return &ast.Ident{Name: str}
89}
90
91func (p *exporter) clause(v value) (n ast.Clause, next yielder) {
92 switch x := v.(type) {
93 case *feed:
94 feed := &ast.ForClause{
Marcel van Lohuizen3d30a782019-02-18 23:32:10 +010095 Value: p.identifier(x.fn.params.arcs[1].feature),
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +010096 Source: p.expr(x.source),
97 }
98 key := x.fn.params.arcs[0]
99 if p.ctx.labelStr(key.feature) != "_" {
Marcel van Lohuizen3d30a782019-02-18 23:32:10 +0100100 feed.Key = p.identifier(key.feature)
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100101 }
102 return feed, x.fn.value.(yielder)
103
104 case *guard:
105 return &ast.IfClause{Condition: p.expr(x.condition)}, x.value
106 }
107 panic(fmt.Sprintf("unsupported clause type %T", v))
108}
109
110func (p *exporter) expr(v value) ast.Expr {
Marcel van Lohuizen466e3f62019-04-06 14:16:50 +0200111 if doEval(p.mode) {
Marcel van Lohuizen0018c742019-02-20 01:25:39 +0100112 x := p.ctx.manifest(v)
113 if isIncomplete(x) {
Marcel van Lohuizen466e3f62019-04-06 14:16:50 +0200114 p = &exporter{p.ctx, options{raw: true}, p.stack}
Marcel van Lohuizen0018c742019-02-20 01:25:39 +0100115 return p.expr(v)
116 }
117 v = x
Marcel van Lohuizen3d30a782019-02-18 23:32:10 +0100118 }
119
Marcel van Lohuizen0018c742019-02-20 01:25:39 +0100120 old := p.stack
121 defer func() { p.stack = old }()
122
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100123 // TODO: also add position information.
124 switch x := v.(type) {
125 case *builtin:
126 return &ast.Ident{Name: x.Name}
Marcel van Lohuizen0018c742019-02-20 01:25:39 +0100127
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100128 case *nodeRef:
129 return nil
Marcel van Lohuizen0018c742019-02-20 01:25:39 +0100130
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100131 case *selectorExpr:
132 n := p.expr(x.x)
Marcel van Lohuizen0018c742019-02-20 01:25:39 +0100133 if n != nil {
134 return &ast.SelectorExpr{X: n, Sel: p.identifier(x.feature)}
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100135 }
Marcel van Lohuizen0018c742019-02-20 01:25:39 +0100136 ident := p.identifier(x.feature)
137 node, ok := x.x.(*nodeRef)
138 if !ok {
139 // TODO: should not happen: report error
140 return ident
141 }
142 conflict := false
143 for i := len(p.stack) - 1; i >= 0; i-- {
144 e := &p.stack[i]
145 if e.from != x.feature {
146 continue
147 }
148 if e.key != node.node {
149 conflict = true
150 continue
151 }
152 if conflict {
153 ident = e.to
154 if e.to == nil {
155 name := p.unique(p.ctx.labelStr(x.feature))
156 e.syn.Elts = append(e.syn.Elts, &ast.Alias{
157 Ident: p.ident(name),
158 Expr: p.identifier(x.feature),
159 })
160 ident = p.ident(name)
161 e.to = ident
162 }
163 }
164 return ident
165 }
166 // TODO: should not happen: report error
167 return ident
168
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100169 case *indexExpr:
170 return &ast.IndexExpr{X: p.expr(x.x), Index: p.expr(x.index)}
Marcel van Lohuizen0018c742019-02-20 01:25:39 +0100171
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100172 case *sliceExpr:
173 return &ast.SliceExpr{
174 X: p.expr(x.x),
175 Low: p.expr(x.lo),
176 High: p.expr(x.hi),
177 }
Marcel van Lohuizen0018c742019-02-20 01:25:39 +0100178
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100179 case *callExpr:
180 call := &ast.CallExpr{Fun: p.expr(x.x)}
181 for _, a := range x.args {
182 call.Args = append(call.Args, p.expr(a))
183 }
184 return call
Marcel van Lohuizen0018c742019-02-20 01:25:39 +0100185
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100186 case *unaryExpr:
187 return &ast.UnaryExpr{Op: opMap[x.op], X: p.expr(x.x)}
Marcel van Lohuizen0018c742019-02-20 01:25:39 +0100188
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100189 case *binaryExpr:
190 return &ast.BinaryExpr{
191 X: p.expr(x.left),
192 Op: opMap[x.op], Y: p.expr(x.right),
193 }
Marcel van Lohuizen0018c742019-02-20 01:25:39 +0100194
Marcel van Lohuizen7d0797b2019-02-07 18:35:28 +0100195 case *bound:
196 return &ast.UnaryExpr{Op: opMap[x.op], X: p.expr(x.value)}
Marcel van Lohuizen0018c742019-02-20 01:25:39 +0100197
Marcel van Lohuizen7d0797b2019-02-07 18:35:28 +0100198 case *unification:
199 if len(x.values) == 1 {
200 return p.expr(x.values[0])
201 }
202 bin := p.expr(x.values[0])
203 for _, v := range x.values[1:] {
Marcel van Lohuizenbedcf0c2019-02-22 18:00:00 +0100204 bin = &ast.BinaryExpr{X: bin, Op: token.AND, Y: p.expr(v)}
Marcel van Lohuizen7d0797b2019-02-07 18:35:28 +0100205 }
206 return bin
207
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100208 case *disjunction:
209 if len(x.values) == 1 {
210 return p.expr(x.values[0].val)
211 }
Marcel van Lohuizenc9b3cb22019-01-30 11:32:41 +0100212 expr := func(v dValue) ast.Expr {
213 e := p.expr(v.val)
214 if v.marked {
215 e = &ast.UnaryExpr{Op: token.MUL, X: e}
216 }
217 return e
218 }
Marcel van Lohuizen7d0797b2019-02-07 18:35:28 +0100219 bin := expr(x.values[0])
220 for _, v := range x.values[1:] {
Marcel van Lohuizenbedcf0c2019-02-22 18:00:00 +0100221 bin = &ast.BinaryExpr{X: bin, Op: token.OR, Y: expr(v)}
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100222 }
223 return bin
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100224
225 case *structLit:
226 obj := &ast.StructLit{}
Marcel van Lohuizen466e3f62019-04-06 14:16:50 +0200227 if doEval(p.mode) {
Marcel van Lohuizen0018c742019-02-20 01:25:39 +0100228 for _, a := range x.arcs {
229 p.stack = append(p.stack, remap{
230 key: x,
231 from: a.feature,
232 to: nil,
233 syn: obj,
234 })
235 }
Marcel van Lohuizen3d30a782019-02-18 23:32:10 +0100236 x = x.expandFields(p.ctx)
237 }
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100238 if x.emit != nil {
239 obj.Elts = append(obj.Elts, &ast.EmitDecl{Expr: p.expr(x.emit)})
240 }
Marcel van Lohuizen466e3f62019-04-06 14:16:50 +0200241 if !doEval(p.mode) && x.template != nil {
Marcel van Lohuizen0018c742019-02-20 01:25:39 +0100242 l, ok := x.template.evalPartial(p.ctx).(*lambdaExpr)
243 if ok {
244 obj.Elts = append(obj.Elts, &ast.Field{
245 Label: &ast.TemplateLabel{
246 Ident: p.identifier(l.params.arcs[0].feature),
247 },
248 Value: p.expr(l.value),
249 })
250 } // TODO: else record error
251 }
Marcel van Lohuizen5f5d39f2019-02-21 23:30:24 +0100252 for i, a := range x.arcs {
253 f := &ast.Field{
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100254 Label: p.label(a.feature),
Marcel van Lohuizen5f5d39f2019-02-21 23:30:24 +0100255 }
Marcel van Lohuizen466e3f62019-04-06 14:16:50 +0200256 // TODO: allow the removal of hidden fields. However, hidden fields
257 // that still used in incomplete expressions should not be removed
258 // (unless RequireConcrete is requested).
259 if a.optional {
260 // Optional fields are almost never concrete. We omit them in
261 // concrete mode to allow the user to use the -a option in eval
262 // without getting many errors.
263 if p.mode.omitOptional || p.mode.concrete {
264 continue
265 }
266 f.Optional = 1
267 }
268 if a.feature&hidden != 0 && p.mode.concrete && p.mode.omitHidden {
269 continue
270 }
271 if !doEval(p.mode) {
Marcel van Lohuizen5f5d39f2019-02-21 23:30:24 +0100272 f.Value = p.expr(a.v)
Marcel van Lohuizen466e3f62019-04-06 14:16:50 +0200273 } else if v := p.ctx.manifest(x.at(p.ctx, i)); isIncomplete(v) && !p.mode.concrete {
274 p := &exporter{p.ctx, options{raw: true}, p.stack}
Marcel van Lohuizen5f5d39f2019-02-21 23:30:24 +0100275 f.Value = p.expr(a.v)
276 } else {
277 f.Value = p.expr(v)
278 }
Marcel van Lohuizen466e3f62019-04-06 14:16:50 +0200279 if a.attrs != nil && !p.mode.omitAttrs {
Marcel van Lohuizenb9b62d32019-03-14 23:50:15 +0100280 for _, at := range a.attrs.attr {
281 f.Attrs = append(f.Attrs, &ast.Attribute{Text: at.text})
282 }
283 }
Marcel van Lohuizen5f5d39f2019-02-21 23:30:24 +0100284 obj.Elts = append(obj.Elts, f)
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100285 }
Marcel van Lohuizen0018c742019-02-20 01:25:39 +0100286
Marcel van Lohuizen66db9202018-12-17 19:02:08 +0100287 for _, c := range x.comprehensions {
288 var clauses []ast.Clause
289 next := c.clauses
290 for {
291 if yield, ok := next.(*yield); ok {
Marcel van Lohuizen3d30a782019-02-18 23:32:10 +0100292 l := p.expr(yield.key)
293 label, ok := l.(ast.Label)
294 if !ok {
295 // TODO: add an invalid field instead?
296 continue
297 }
Marcel van Lohuizen08a0ef22019-03-28 09:12:19 +0100298 opt := token.NoPos
299 if yield.opt {
300 opt = 1 // anything but token.NoPos
301 }
Marcel van Lohuizen66db9202018-12-17 19:02:08 +0100302 f := &ast.Field{
Marcel van Lohuizen08a0ef22019-03-28 09:12:19 +0100303 Label: label,
304 Optional: opt,
305 Value: p.expr(yield.value),
Marcel van Lohuizen66db9202018-12-17 19:02:08 +0100306 }
307 var decl ast.Decl = f
308 if len(clauses) > 0 {
309 decl = &ast.ComprehensionDecl{Field: f, Clauses: clauses}
310 }
311 obj.Elts = append(obj.Elts, decl)
312 break
313 }
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100314
Marcel van Lohuizen66db9202018-12-17 19:02:08 +0100315 var y ast.Clause
316 y, next = p.clause(next)
317 clauses = append(clauses, y)
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100318 }
319 }
Marcel van Lohuizen66db9202018-12-17 19:02:08 +0100320 return obj
321
322 case *fieldComprehension:
323 panic("should be handled in structLit")
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100324
325 case *listComprehension:
326 var clauses []ast.Clause
327 for y, next := p.clause(x.clauses); ; y, next = p.clause(next) {
328 clauses = append(clauses, y)
329 if yield, ok := next.(*yield); ok {
330 return &ast.ListComprehension{
331 Expr: p.expr(yield.value),
332 Clauses: clauses,
333 }
334 }
335 }
336
337 case *nullLit:
338 return p.ident("null")
339
340 case *boolLit:
341 return p.ident(fmt.Sprint(x.b))
342
343 case *stringLit:
344 return &ast.BasicLit{
345 Kind: token.STRING,
346 Value: quote(x.str, '"'),
347 }
348
349 case *bytesLit:
350 return &ast.BasicLit{
351 Kind: token.STRING,
352 Value: quote(string(x.b), '\''),
353 }
354
355 case *numLit:
356 if x.k&intKind != 0 {
357 return &ast.BasicLit{
358 Kind: token.INT,
359 Value: x.v.Text('f'),
360 }
361 }
362 return &ast.BasicLit{
363 Kind: token.FLOAT,
364 Value: x.v.Text('g'),
365 }
366
367 case *durationLit:
368 panic("unimplemented")
369
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100370 case *interpolation:
371 t := &ast.Interpolation{}
372 multiline := false
373 // TODO: mark formatting in interpolation itself.
374 for i := 0; i < len(x.parts); i += 2 {
375 str := x.parts[i].(*stringLit).str
376 if strings.IndexByte(str, '\n') >= 0 {
377 multiline = true
378 break
379 }
380 }
381 quote := `"`
382 if multiline {
383 quote = `"""`
384 }
385 prefix := quote
386 suffix := `\(`
387 for i, e := range x.parts {
388 if i%2 == 1 {
389 t.Elts = append(t.Elts, p.expr(e))
390 } else {
391 buf := []byte(prefix)
392 if i == len(x.parts)-1 {
393 suffix = quote
394 }
395 str := e.(*stringLit).str
396 if multiline {
397 buf = appendEscapeMulti(buf, str, '"')
398 } else {
399 buf = appendEscaped(buf, str, '"', true)
400 }
401 buf = append(buf, suffix...)
402 t.Elts = append(t.Elts, &ast.BasicLit{
403 Kind: token.STRING,
404 Value: string(buf),
405 })
406 }
407 prefix = ")"
408 }
409 return t
410
411 case *list:
412 list := &ast.ListLit{}
413 var expr ast.Expr = list
Marcel van Lohuizen9ee652d2019-04-25 17:16:01 +0200414 for _, e := range x.elem.arcs {
415 if doEval(p.mode) {
416 list.Elts = append(list.Elts, p.expr(e.v.evalPartial(p.ctx)))
417 } else {
418 list.Elts = append(list.Elts, p.expr(e.v))
419 }
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100420 }
Marcel van Lohuizen7d0797b2019-02-07 18:35:28 +0100421 max := maxNum(x.len)
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100422 num, ok := max.(*numLit)
423 if !ok {
Marcel van Lohuizen7d0797b2019-02-07 18:35:28 +0100424 min := minNum(x.len)
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100425 num, _ = min.(*numLit)
426 }
427 ln := 0
428 if num != nil {
429 x, _ := num.v.Int64()
430 ln = int(x)
431 }
Marcel van Lohuizen7d0797b2019-02-07 18:35:28 +0100432 open := false
433 switch max.(type) {
434 case *top, *basicType:
435 open = true
436 }
Marcel van Lohuizen9ee652d2019-04-25 17:16:01 +0200437 if !ok || ln > len(x.elem.arcs) {
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100438 list.Type = p.expr(x.typ)
Marcel van Lohuizen7d0797b2019-02-07 18:35:28 +0100439 if !open && !isTop(x.typ) {
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100440 expr = &ast.BinaryExpr{
441 X: &ast.BinaryExpr{
442 X: p.expr(x.len),
443 Op: token.MUL,
444 Y: &ast.ListLit{Elts: []ast.Expr{
445 p.expr(x.typ),
446 }},
447 },
Marcel van Lohuizenbedcf0c2019-02-22 18:00:00 +0100448 Op: token.AND,
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100449 Y: list,
450 }
451
452 }
453 }
454 return expr
455
456 case *bottom:
457 err := &ast.BottomLit{}
458 comment := &ast.Comment{Text: "// " + x.msg}
459 err.AddComment(&ast.CommentGroup{
460 Line: true,
461 Position: 1,
462 List: []*ast.Comment{comment},
463 })
464 return err
465
466 case *top:
467 return p.ident("_")
468
469 case *basicType:
470 return p.ident(x.k.String())
471
472 case *lambdaExpr:
473 return p.ident("TODO: LAMBDA")
474
475 default:
476 panic(fmt.Sprintf("unimplemented type %T", x))
477 }
478}
479
480// quote quotes the given string.
481func quote(str string, quote byte) string {
482 if strings.IndexByte(str, '\n') < 0 {
483 buf := []byte{quote}
484 buf = appendEscaped(buf, str, quote, true)
485 buf = append(buf, quote)
486 return string(buf)
487 }
488 buf := []byte{quote, quote, quote}
489 buf = append(buf, multiSep...)
490 buf = appendEscapeMulti(buf, str, quote)
491 buf = append(buf, quote, quote, quote)
492 return string(buf)
493}
494
495// TODO: consider the best indent strategy.
496const multiSep = "\n "
497
498func appendEscapeMulti(buf []byte, str string, quote byte) []byte {
499 // TODO(perf)
500 a := strings.Split(str, "\n")
501 for _, s := range a {
502 buf = appendEscaped(buf, s, quote, true)
503 buf = append(buf, multiSep...)
504 }
505 return buf
506}
507
508const lowerhex = "0123456789abcdef"
509
510func appendEscaped(buf []byte, s string, quote byte, graphicOnly bool) []byte {
511 for width := 0; len(s) > 0; s = s[width:] {
512 r := rune(s[0])
513 width = 1
514 if r >= utf8.RuneSelf {
515 r, width = utf8.DecodeRuneInString(s)
516 }
517 if width == 1 && r == utf8.RuneError {
518 buf = append(buf, `\x`...)
519 buf = append(buf, lowerhex[s[0]>>4])
520 buf = append(buf, lowerhex[s[0]&0xF])
521 continue
522 }
523 buf = appendEscapedRune(buf, r, quote, graphicOnly)
524 }
525 return buf
526}
527
528func appendEscapedRune(buf []byte, r rune, quote byte, graphicOnly bool) []byte {
529 var runeTmp [utf8.UTFMax]byte
530 if r == rune(quote) || r == '\\' { // always backslashed
531 buf = append(buf, '\\')
532 buf = append(buf, byte(r))
533 return buf
534 }
535 // TODO(perf): IsGraphic calls IsPrint.
536 if strconv.IsPrint(r) || graphicOnly && strconv.IsGraphic(r) {
537 n := utf8.EncodeRune(runeTmp[:], r)
538 buf = append(buf, runeTmp[:n]...)
539 return buf
540 }
541 switch r {
542 case '\a':
543 buf = append(buf, `\a`...)
544 case '\b':
545 buf = append(buf, `\b`...)
546 case '\f':
547 buf = append(buf, `\f`...)
548 case '\n':
549 buf = append(buf, `\n`...)
550 case '\r':
551 buf = append(buf, `\r`...)
552 case '\t':
553 buf = append(buf, `\t`...)
554 case '\v':
555 buf = append(buf, `\v`...)
556 default:
557 switch {
558 case r < ' ':
559 // Invalid for strings, only bytes.
560 buf = append(buf, `\x`...)
561 buf = append(buf, lowerhex[byte(r)>>4])
562 buf = append(buf, lowerhex[byte(r)&0xF])
563 case r > utf8.MaxRune:
564 r = 0xFFFD
565 fallthrough
566 case r < 0x10000:
567 buf = append(buf, `\u`...)
568 for s := 12; s >= 0; s -= 4 {
569 buf = append(buf, lowerhex[r>>uint(s)&0xF])
570 }
571 default:
572 buf = append(buf, `\U`...)
573 for s := 28; s >= 0; s -= 4 {
574 buf = append(buf, lowerhex[r>>uint(s)&0xF])
575 }
576 }
577 }
578 return buf
579}