blob: 47eb3e44501d6aa83188385684f5016ec7e4d351 [file] [log] [blame]
Naveen Sampath04696f72022-06-13 15:19:14 +05301// Copyright 2010 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package proto
6
7import (
8 "encoding"
9 "errors"
10 "fmt"
11 "reflect"
12 "strconv"
13 "strings"
14 "unicode/utf8"
15
16 "google.golang.org/protobuf/encoding/prototext"
17 protoV2 "google.golang.org/protobuf/proto"
18 "google.golang.org/protobuf/reflect/protoreflect"
19 "google.golang.org/protobuf/reflect/protoregistry"
20)
21
22const wrapTextUnmarshalV2 = false
23
24// ParseError is returned by UnmarshalText.
25type ParseError struct {
26 Message string
27
28 // Deprecated: Do not use.
29 Line, Offset int
30}
31
32func (e *ParseError) Error() string {
33 if wrapTextUnmarshalV2 {
34 return e.Message
35 }
36 if e.Line == 1 {
37 return fmt.Sprintf("line 1.%d: %v", e.Offset, e.Message)
38 }
39 return fmt.Sprintf("line %d: %v", e.Line, e.Message)
40}
41
42// UnmarshalText parses a proto text formatted string into m.
43func UnmarshalText(s string, m Message) error {
44 if u, ok := m.(encoding.TextUnmarshaler); ok {
45 return u.UnmarshalText([]byte(s))
46 }
47
48 m.Reset()
49 mi := MessageV2(m)
50
51 if wrapTextUnmarshalV2 {
52 err := prototext.UnmarshalOptions{
53 AllowPartial: true,
54 }.Unmarshal([]byte(s), mi)
55 if err != nil {
56 return &ParseError{Message: err.Error()}
57 }
58 return checkRequiredNotSet(mi)
59 } else {
60 if err := newTextParser(s).unmarshalMessage(mi.ProtoReflect(), ""); err != nil {
61 return err
62 }
63 return checkRequiredNotSet(mi)
64 }
65}
66
67type textParser struct {
68 s string // remaining input
69 done bool // whether the parsing is finished (success or error)
70 backed bool // whether back() was called
71 offset, line int
72 cur token
73}
74
75type token struct {
76 value string
77 err *ParseError
78 line int // line number
79 offset int // byte number from start of input, not start of line
80 unquoted string // the unquoted version of value, if it was a quoted string
81}
82
83func newTextParser(s string) *textParser {
84 p := new(textParser)
85 p.s = s
86 p.line = 1
87 p.cur.line = 1
88 return p
89}
90
91func (p *textParser) unmarshalMessage(m protoreflect.Message, terminator string) (err error) {
92 md := m.Descriptor()
93 fds := md.Fields()
94
95 // A struct is a sequence of "name: value", terminated by one of
96 // '>' or '}', or the end of the input. A name may also be
97 // "[extension]" or "[type/url]".
98 //
99 // The whole struct can also be an expanded Any message, like:
100 // [type/url] < ... struct contents ... >
101 seen := make(map[protoreflect.FieldNumber]bool)
102 for {
103 tok := p.next()
104 if tok.err != nil {
105 return tok.err
106 }
107 if tok.value == terminator {
108 break
109 }
110 if tok.value == "[" {
111 if err := p.unmarshalExtensionOrAny(m, seen); err != nil {
112 return err
113 }
114 continue
115 }
116
117 // This is a normal, non-extension field.
118 name := protoreflect.Name(tok.value)
119 fd := fds.ByName(name)
120 switch {
121 case fd == nil:
122 gd := fds.ByName(protoreflect.Name(strings.ToLower(string(name))))
123 if gd != nil && gd.Kind() == protoreflect.GroupKind && gd.Message().Name() == name {
124 fd = gd
125 }
126 case fd.Kind() == protoreflect.GroupKind && fd.Message().Name() != name:
127 fd = nil
128 case fd.IsWeak() && fd.Message().IsPlaceholder():
129 fd = nil
130 }
131 if fd == nil {
132 typeName := string(md.FullName())
133 if m, ok := m.Interface().(Message); ok {
134 t := reflect.TypeOf(m)
135 if t.Kind() == reflect.Ptr {
136 typeName = t.Elem().String()
137 }
138 }
139 return p.errorf("unknown field name %q in %v", name, typeName)
140 }
141 if od := fd.ContainingOneof(); od != nil && m.WhichOneof(od) != nil {
142 return p.errorf("field '%s' would overwrite already parsed oneof '%s'", name, od.Name())
143 }
144 if fd.Cardinality() != protoreflect.Repeated && seen[fd.Number()] {
145 return p.errorf("non-repeated field %q was repeated", fd.Name())
146 }
147 seen[fd.Number()] = true
148
149 // Consume any colon.
150 if err := p.checkForColon(fd); err != nil {
151 return err
152 }
153
154 // Parse into the field.
155 v := m.Get(fd)
156 if !m.Has(fd) && (fd.IsList() || fd.IsMap() || fd.Message() != nil) {
157 v = m.Mutable(fd)
158 }
159 if v, err = p.unmarshalValue(v, fd); err != nil {
160 return err
161 }
162 m.Set(fd, v)
163
164 if err := p.consumeOptionalSeparator(); err != nil {
165 return err
166 }
167 }
168 return nil
169}
170
171func (p *textParser) unmarshalExtensionOrAny(m protoreflect.Message, seen map[protoreflect.FieldNumber]bool) error {
172 name, err := p.consumeExtensionOrAnyName()
173 if err != nil {
174 return err
175 }
176
177 // If it contains a slash, it's an Any type URL.
178 if slashIdx := strings.LastIndex(name, "/"); slashIdx >= 0 {
179 tok := p.next()
180 if tok.err != nil {
181 return tok.err
182 }
183 // consume an optional colon
184 if tok.value == ":" {
185 tok = p.next()
186 if tok.err != nil {
187 return tok.err
188 }
189 }
190
191 var terminator string
192 switch tok.value {
193 case "<":
194 terminator = ">"
195 case "{":
196 terminator = "}"
197 default:
198 return p.errorf("expected '{' or '<', found %q", tok.value)
199 }
200
201 mt, err := protoregistry.GlobalTypes.FindMessageByURL(name)
202 if err != nil {
203 return p.errorf("unrecognized message %q in google.protobuf.Any", name[slashIdx+len("/"):])
204 }
205 m2 := mt.New()
206 if err := p.unmarshalMessage(m2, terminator); err != nil {
207 return err
208 }
209 b, err := protoV2.Marshal(m2.Interface())
210 if err != nil {
211 return p.errorf("failed to marshal message of type %q: %v", name[slashIdx+len("/"):], err)
212 }
213
214 urlFD := m.Descriptor().Fields().ByName("type_url")
215 valFD := m.Descriptor().Fields().ByName("value")
216 if seen[urlFD.Number()] {
217 return p.errorf("Any message unpacked multiple times, or %q already set", urlFD.Name())
218 }
219 if seen[valFD.Number()] {
220 return p.errorf("Any message unpacked multiple times, or %q already set", valFD.Name())
221 }
222 m.Set(urlFD, protoreflect.ValueOfString(name))
223 m.Set(valFD, protoreflect.ValueOfBytes(b))
224 seen[urlFD.Number()] = true
225 seen[valFD.Number()] = true
226 return nil
227 }
228
229 xname := protoreflect.FullName(name)
230 xt, _ := protoregistry.GlobalTypes.FindExtensionByName(xname)
231 if xt == nil && isMessageSet(m.Descriptor()) {
232 xt, _ = protoregistry.GlobalTypes.FindExtensionByName(xname.Append("message_set_extension"))
233 }
234 if xt == nil {
235 return p.errorf("unrecognized extension %q", name)
236 }
237 fd := xt.TypeDescriptor()
238 if fd.ContainingMessage().FullName() != m.Descriptor().FullName() {
239 return p.errorf("extension field %q does not extend message %q", name, m.Descriptor().FullName())
240 }
241
242 if err := p.checkForColon(fd); err != nil {
243 return err
244 }
245
246 v := m.Get(fd)
247 if !m.Has(fd) && (fd.IsList() || fd.IsMap() || fd.Message() != nil) {
248 v = m.Mutable(fd)
249 }
250 v, err = p.unmarshalValue(v, fd)
251 if err != nil {
252 return err
253 }
254 m.Set(fd, v)
255 return p.consumeOptionalSeparator()
256}
257
258func (p *textParser) unmarshalValue(v protoreflect.Value, fd protoreflect.FieldDescriptor) (protoreflect.Value, error) {
259 tok := p.next()
260 if tok.err != nil {
261 return v, tok.err
262 }
263 if tok.value == "" {
264 return v, p.errorf("unexpected EOF")
265 }
266
267 switch {
268 case fd.IsList():
269 lv := v.List()
270 var err error
271 if tok.value == "[" {
272 // Repeated field with list notation, like [1,2,3].
273 for {
274 vv := lv.NewElement()
275 vv, err = p.unmarshalSingularValue(vv, fd)
276 if err != nil {
277 return v, err
278 }
279 lv.Append(vv)
280
281 tok := p.next()
282 if tok.err != nil {
283 return v, tok.err
284 }
285 if tok.value == "]" {
286 break
287 }
288 if tok.value != "," {
289 return v, p.errorf("Expected ']' or ',' found %q", tok.value)
290 }
291 }
292 return v, nil
293 }
294
295 // One value of the repeated field.
296 p.back()
297 vv := lv.NewElement()
298 vv, err = p.unmarshalSingularValue(vv, fd)
299 if err != nil {
300 return v, err
301 }
302 lv.Append(vv)
303 return v, nil
304 case fd.IsMap():
305 // The map entry should be this sequence of tokens:
306 // < key : KEY value : VALUE >
307 // However, implementations may omit key or value, and technically
308 // we should support them in any order.
309 var terminator string
310 switch tok.value {
311 case "<":
312 terminator = ">"
313 case "{":
314 terminator = "}"
315 default:
316 return v, p.errorf("expected '{' or '<', found %q", tok.value)
317 }
318
319 keyFD := fd.MapKey()
320 valFD := fd.MapValue()
321
322 mv := v.Map()
323 kv := keyFD.Default()
324 vv := mv.NewValue()
325 for {
326 tok := p.next()
327 if tok.err != nil {
328 return v, tok.err
329 }
330 if tok.value == terminator {
331 break
332 }
333 var err error
334 switch tok.value {
335 case "key":
336 if err := p.consumeToken(":"); err != nil {
337 return v, err
338 }
339 if kv, err = p.unmarshalSingularValue(kv, keyFD); err != nil {
340 return v, err
341 }
342 if err := p.consumeOptionalSeparator(); err != nil {
343 return v, err
344 }
345 case "value":
346 if err := p.checkForColon(valFD); err != nil {
347 return v, err
348 }
349 if vv, err = p.unmarshalSingularValue(vv, valFD); err != nil {
350 return v, err
351 }
352 if err := p.consumeOptionalSeparator(); err != nil {
353 return v, err
354 }
355 default:
356 p.back()
357 return v, p.errorf(`expected "key", "value", or %q, found %q`, terminator, tok.value)
358 }
359 }
360 mv.Set(kv.MapKey(), vv)
361 return v, nil
362 default:
363 p.back()
364 return p.unmarshalSingularValue(v, fd)
365 }
366}
367
368func (p *textParser) unmarshalSingularValue(v protoreflect.Value, fd protoreflect.FieldDescriptor) (protoreflect.Value, error) {
369 tok := p.next()
370 if tok.err != nil {
371 return v, tok.err
372 }
373 if tok.value == "" {
374 return v, p.errorf("unexpected EOF")
375 }
376
377 switch fd.Kind() {
378 case protoreflect.BoolKind:
379 switch tok.value {
380 case "true", "1", "t", "True":
381 return protoreflect.ValueOfBool(true), nil
382 case "false", "0", "f", "False":
383 return protoreflect.ValueOfBool(false), nil
384 }
385 case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind:
386 if x, err := strconv.ParseInt(tok.value, 0, 32); err == nil {
387 return protoreflect.ValueOfInt32(int32(x)), nil
388 }
389
390 // The C++ parser accepts large positive hex numbers that uses
391 // two's complement arithmetic to represent negative numbers.
392 // This feature is here for backwards compatibility with C++.
393 if strings.HasPrefix(tok.value, "0x") {
394 if x, err := strconv.ParseUint(tok.value, 0, 32); err == nil {
395 return protoreflect.ValueOfInt32(int32(-(int64(^x) + 1))), nil
396 }
397 }
398 case protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind:
399 if x, err := strconv.ParseInt(tok.value, 0, 64); err == nil {
400 return protoreflect.ValueOfInt64(int64(x)), nil
401 }
402
403 // The C++ parser accepts large positive hex numbers that uses
404 // two's complement arithmetic to represent negative numbers.
405 // This feature is here for backwards compatibility with C++.
406 if strings.HasPrefix(tok.value, "0x") {
407 if x, err := strconv.ParseUint(tok.value, 0, 64); err == nil {
408 return protoreflect.ValueOfInt64(int64(-(int64(^x) + 1))), nil
409 }
410 }
411 case protoreflect.Uint32Kind, protoreflect.Fixed32Kind:
412 if x, err := strconv.ParseUint(tok.value, 0, 32); err == nil {
413 return protoreflect.ValueOfUint32(uint32(x)), nil
414 }
415 case protoreflect.Uint64Kind, protoreflect.Fixed64Kind:
416 if x, err := strconv.ParseUint(tok.value, 0, 64); err == nil {
417 return protoreflect.ValueOfUint64(uint64(x)), nil
418 }
419 case protoreflect.FloatKind:
420 // Ignore 'f' for compatibility with output generated by C++,
421 // but don't remove 'f' when the value is "-inf" or "inf".
422 v := tok.value
423 if strings.HasSuffix(v, "f") && v != "-inf" && v != "inf" {
424 v = v[:len(v)-len("f")]
425 }
426 if x, err := strconv.ParseFloat(v, 32); err == nil {
427 return protoreflect.ValueOfFloat32(float32(x)), nil
428 }
429 case protoreflect.DoubleKind:
430 // Ignore 'f' for compatibility with output generated by C++,
431 // but don't remove 'f' when the value is "-inf" or "inf".
432 v := tok.value
433 if strings.HasSuffix(v, "f") && v != "-inf" && v != "inf" {
434 v = v[:len(v)-len("f")]
435 }
436 if x, err := strconv.ParseFloat(v, 64); err == nil {
437 return protoreflect.ValueOfFloat64(float64(x)), nil
438 }
439 case protoreflect.StringKind:
440 if isQuote(tok.value[0]) {
441 return protoreflect.ValueOfString(tok.unquoted), nil
442 }
443 case protoreflect.BytesKind:
444 if isQuote(tok.value[0]) {
445 return protoreflect.ValueOfBytes([]byte(tok.unquoted)), nil
446 }
447 case protoreflect.EnumKind:
448 if x, err := strconv.ParseInt(tok.value, 0, 32); err == nil {
449 return protoreflect.ValueOfEnum(protoreflect.EnumNumber(x)), nil
450 }
451 vd := fd.Enum().Values().ByName(protoreflect.Name(tok.value))
452 if vd != nil {
453 return protoreflect.ValueOfEnum(vd.Number()), nil
454 }
455 case protoreflect.MessageKind, protoreflect.GroupKind:
456 var terminator string
457 switch tok.value {
458 case "{":
459 terminator = "}"
460 case "<":
461 terminator = ">"
462 default:
463 return v, p.errorf("expected '{' or '<', found %q", tok.value)
464 }
465 err := p.unmarshalMessage(v.Message(), terminator)
466 return v, err
467 default:
468 panic(fmt.Sprintf("invalid kind %v", fd.Kind()))
469 }
470 return v, p.errorf("invalid %v: %v", fd.Kind(), tok.value)
471}
472
473// Consume a ':' from the input stream (if the next token is a colon),
474// returning an error if a colon is needed but not present.
475func (p *textParser) checkForColon(fd protoreflect.FieldDescriptor) *ParseError {
476 tok := p.next()
477 if tok.err != nil {
478 return tok.err
479 }
480 if tok.value != ":" {
481 if fd.Message() == nil {
482 return p.errorf("expected ':', found %q", tok.value)
483 }
484 p.back()
485 }
486 return nil
487}
488
489// consumeExtensionOrAnyName consumes an extension name or an Any type URL and
490// the following ']'. It returns the name or URL consumed.
491func (p *textParser) consumeExtensionOrAnyName() (string, error) {
492 tok := p.next()
493 if tok.err != nil {
494 return "", tok.err
495 }
496
497 // If extension name or type url is quoted, it's a single token.
498 if len(tok.value) > 2 && isQuote(tok.value[0]) && tok.value[len(tok.value)-1] == tok.value[0] {
499 name, err := unquoteC(tok.value[1:len(tok.value)-1], rune(tok.value[0]))
500 if err != nil {
501 return "", err
502 }
503 return name, p.consumeToken("]")
504 }
505
506 // Consume everything up to "]"
507 var parts []string
508 for tok.value != "]" {
509 parts = append(parts, tok.value)
510 tok = p.next()
511 if tok.err != nil {
512 return "", p.errorf("unrecognized type_url or extension name: %s", tok.err)
513 }
514 if p.done && tok.value != "]" {
515 return "", p.errorf("unclosed type_url or extension name")
516 }
517 }
518 return strings.Join(parts, ""), nil
519}
520
521// consumeOptionalSeparator consumes an optional semicolon or comma.
522// It is used in unmarshalMessage to provide backward compatibility.
523func (p *textParser) consumeOptionalSeparator() error {
524 tok := p.next()
525 if tok.err != nil {
526 return tok.err
527 }
528 if tok.value != ";" && tok.value != "," {
529 p.back()
530 }
531 return nil
532}
533
534func (p *textParser) errorf(format string, a ...interface{}) *ParseError {
535 pe := &ParseError{fmt.Sprintf(format, a...), p.cur.line, p.cur.offset}
536 p.cur.err = pe
537 p.done = true
538 return pe
539}
540
541func (p *textParser) skipWhitespace() {
542 i := 0
543 for i < len(p.s) && (isWhitespace(p.s[i]) || p.s[i] == '#') {
544 if p.s[i] == '#' {
545 // comment; skip to end of line or input
546 for i < len(p.s) && p.s[i] != '\n' {
547 i++
548 }
549 if i == len(p.s) {
550 break
551 }
552 }
553 if p.s[i] == '\n' {
554 p.line++
555 }
556 i++
557 }
558 p.offset += i
559 p.s = p.s[i:len(p.s)]
560 if len(p.s) == 0 {
561 p.done = true
562 }
563}
564
565func (p *textParser) advance() {
566 // Skip whitespace
567 p.skipWhitespace()
568 if p.done {
569 return
570 }
571
572 // Start of non-whitespace
573 p.cur.err = nil
574 p.cur.offset, p.cur.line = p.offset, p.line
575 p.cur.unquoted = ""
576 switch p.s[0] {
577 case '<', '>', '{', '}', ':', '[', ']', ';', ',', '/':
578 // Single symbol
579 p.cur.value, p.s = p.s[0:1], p.s[1:len(p.s)]
580 case '"', '\'':
581 // Quoted string
582 i := 1
583 for i < len(p.s) && p.s[i] != p.s[0] && p.s[i] != '\n' {
584 if p.s[i] == '\\' && i+1 < len(p.s) {
585 // skip escaped char
586 i++
587 }
588 i++
589 }
590 if i >= len(p.s) || p.s[i] != p.s[0] {
591 p.errorf("unmatched quote")
592 return
593 }
594 unq, err := unquoteC(p.s[1:i], rune(p.s[0]))
595 if err != nil {
596 p.errorf("invalid quoted string %s: %v", p.s[0:i+1], err)
597 return
598 }
599 p.cur.value, p.s = p.s[0:i+1], p.s[i+1:len(p.s)]
600 p.cur.unquoted = unq
601 default:
602 i := 0
603 for i < len(p.s) && isIdentOrNumberChar(p.s[i]) {
604 i++
605 }
606 if i == 0 {
607 p.errorf("unexpected byte %#x", p.s[0])
608 return
609 }
610 p.cur.value, p.s = p.s[0:i], p.s[i:len(p.s)]
611 }
612 p.offset += len(p.cur.value)
613}
614
615// Back off the parser by one token. Can only be done between calls to next().
616// It makes the next advance() a no-op.
617func (p *textParser) back() { p.backed = true }
618
619// Advances the parser and returns the new current token.
620func (p *textParser) next() *token {
621 if p.backed || p.done {
622 p.backed = false
623 return &p.cur
624 }
625 p.advance()
626 if p.done {
627 p.cur.value = ""
628 } else if len(p.cur.value) > 0 && isQuote(p.cur.value[0]) {
629 // Look for multiple quoted strings separated by whitespace,
630 // and concatenate them.
631 cat := p.cur
632 for {
633 p.skipWhitespace()
634 if p.done || !isQuote(p.s[0]) {
635 break
636 }
637 p.advance()
638 if p.cur.err != nil {
639 return &p.cur
640 }
641 cat.value += " " + p.cur.value
642 cat.unquoted += p.cur.unquoted
643 }
644 p.done = false // parser may have seen EOF, but we want to return cat
645 p.cur = cat
646 }
647 return &p.cur
648}
649
650func (p *textParser) consumeToken(s string) error {
651 tok := p.next()
652 if tok.err != nil {
653 return tok.err
654 }
655 if tok.value != s {
656 p.back()
657 return p.errorf("expected %q, found %q", s, tok.value)
658 }
659 return nil
660}
661
662var errBadUTF8 = errors.New("proto: bad UTF-8")
663
664func unquoteC(s string, quote rune) (string, error) {
665 // This is based on C++'s tokenizer.cc.
666 // Despite its name, this is *not* parsing C syntax.
667 // For instance, "\0" is an invalid quoted string.
668
669 // Avoid allocation in trivial cases.
670 simple := true
671 for _, r := range s {
672 if r == '\\' || r == quote {
673 simple = false
674 break
675 }
676 }
677 if simple {
678 return s, nil
679 }
680
681 buf := make([]byte, 0, 3*len(s)/2)
682 for len(s) > 0 {
683 r, n := utf8.DecodeRuneInString(s)
684 if r == utf8.RuneError && n == 1 {
685 return "", errBadUTF8
686 }
687 s = s[n:]
688 if r != '\\' {
689 if r < utf8.RuneSelf {
690 buf = append(buf, byte(r))
691 } else {
692 buf = append(buf, string(r)...)
693 }
694 continue
695 }
696
697 ch, tail, err := unescape(s)
698 if err != nil {
699 return "", err
700 }
701 buf = append(buf, ch...)
702 s = tail
703 }
704 return string(buf), nil
705}
706
707func unescape(s string) (ch string, tail string, err error) {
708 r, n := utf8.DecodeRuneInString(s)
709 if r == utf8.RuneError && n == 1 {
710 return "", "", errBadUTF8
711 }
712 s = s[n:]
713 switch r {
714 case 'a':
715 return "\a", s, nil
716 case 'b':
717 return "\b", s, nil
718 case 'f':
719 return "\f", s, nil
720 case 'n':
721 return "\n", s, nil
722 case 'r':
723 return "\r", s, nil
724 case 't':
725 return "\t", s, nil
726 case 'v':
727 return "\v", s, nil
728 case '?':
729 return "?", s, nil // trigraph workaround
730 case '\'', '"', '\\':
731 return string(r), s, nil
732 case '0', '1', '2', '3', '4', '5', '6', '7':
733 if len(s) < 2 {
734 return "", "", fmt.Errorf(`\%c requires 2 following digits`, r)
735 }
736 ss := string(r) + s[:2]
737 s = s[2:]
738 i, err := strconv.ParseUint(ss, 8, 8)
739 if err != nil {
740 return "", "", fmt.Errorf(`\%s contains non-octal digits`, ss)
741 }
742 return string([]byte{byte(i)}), s, nil
743 case 'x', 'X', 'u', 'U':
744 var n int
745 switch r {
746 case 'x', 'X':
747 n = 2
748 case 'u':
749 n = 4
750 case 'U':
751 n = 8
752 }
753 if len(s) < n {
754 return "", "", fmt.Errorf(`\%c requires %d following digits`, r, n)
755 }
756 ss := s[:n]
757 s = s[n:]
758 i, err := strconv.ParseUint(ss, 16, 64)
759 if err != nil {
760 return "", "", fmt.Errorf(`\%c%s contains non-hexadecimal digits`, r, ss)
761 }
762 if r == 'x' || r == 'X' {
763 return string([]byte{byte(i)}), s, nil
764 }
765 if i > utf8.MaxRune {
766 return "", "", fmt.Errorf(`\%c%s is not a valid Unicode code point`, r, ss)
767 }
768 return string(rune(i)), s, nil
769 }
770 return "", "", fmt.Errorf(`unknown escape \%c`, r)
771}
772
773func isIdentOrNumberChar(c byte) bool {
774 switch {
775 case 'A' <= c && c <= 'Z', 'a' <= c && c <= 'z':
776 return true
777 case '0' <= c && c <= '9':
778 return true
779 }
780 switch c {
781 case '-', '+', '.', '_':
782 return true
783 }
784 return false
785}
786
787func isWhitespace(c byte) bool {
788 switch c {
789 case ' ', '\t', '\n', '\r':
790 return true
791 }
792 return false
793}
794
795func isQuote(c byte) bool {
796 switch c {
797 case '"', '\'':
798 return true
799 }
800 return false
801}