blob: f8a44cba5af98f88c4d4cd83f4267c11c8f87703 [file] [log] [blame]
Marcel van Lohuizen434c3a12018-12-10 15:37:18 +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
15// Package str provides string manipulation utilities.
16package str // import "cuelang.org/go/internal/str"
17
18import (
19 "bytes"
20 "fmt"
21 "unicode"
22 "unicode/utf8"
23)
24
25// StringList flattens its arguments into a single []string.
26// Each argument in args must have type string or []string.
27func StringList(args ...interface{}) []string {
28 var x []string
29 for _, arg := range args {
30 switch arg := arg.(type) {
31 case []string:
32 x = append(x, arg...)
33 case string:
34 x = append(x, arg)
35 default:
36 panic("stringList: invalid argument of type " + fmt.Sprintf("%T", arg))
37 }
38 }
39 return x
40}
41
42// ToFold returns a string with the property that
43// strings.EqualFold(s, t) iff ToFold(s) == ToFold(t)
44// This lets us test a large set of strings for fold-equivalent
45// duplicates without making a quadratic number of calls
46// to EqualFold. Note that strings.ToUpper and strings.ToLower
47// do not have the desired property in some corner cases.
48func ToFold(s string) string {
49 // Fast path: all ASCII, no upper case.
50 // Most paths look like this already.
51 for i := 0; i < len(s); i++ {
52 c := s[i]
53 if c >= utf8.RuneSelf || 'A' <= c && c <= 'Z' {
54 goto Slow
55 }
56 }
57 return s
58
59Slow:
60 var buf bytes.Buffer
61 for _, r := range s {
62 // SimpleFold(x) cycles to the next equivalent rune > x
63 // or wraps around to smaller values. Iterate until it wraps,
64 // and we've found the minimum value.
65 for {
66 r0 := r
67 r = unicode.SimpleFold(r0)
68 if r <= r0 {
69 break
70 }
71 }
72 // Exception to allow fast path above: A-Z => a-z
73 if 'A' <= r && r <= 'Z' {
74 r += 'a' - 'A'
75 }
76 buf.WriteRune(r)
77 }
78 return buf.String()
79}
80
81// FoldDup reports a pair of strings from the list that are
82// equal according to strings.EqualFold.
83// It returns "", "" if there are no such strings.
84func FoldDup(list []string) (string, string) {
85 clash := map[string]string{}
86 for _, s := range list {
87 fold := ToFold(s)
88 if t := clash[fold]; t != "" {
89 if s > t {
90 s, t = t, s
91 }
92 return s, t
93 }
94 clash[fold] = s
95 }
96 return "", ""
97}
98
99// Contains reports whether x contains s.
100func Contains(x []string, s string) bool {
101 for _, t := range x {
102 if t == s {
103 return true
104 }
105 }
106 return false
107}
108
109func isSpaceByte(c byte) bool {
110 return c == ' ' || c == '\t' || c == '\n' || c == '\r'
111}
112
113// SplitQuotedFields splits s into a list of fields,
114// allowing single or double quotes around elements.
115// There is no unescaping or other processing within
116// quoted fields.
117func SplitQuotedFields(s string) ([]string, error) {
118 // Split fields allowing '' or "" around elements.
119 // Quotes further inside the string do not count.
120 var f []string
121 for len(s) > 0 {
122 for len(s) > 0 && isSpaceByte(s[0]) {
123 s = s[1:]
124 }
125 if len(s) == 0 {
126 break
127 }
128 // Accepted quoted string. No unescaping inside.
129 if s[0] == '"' || s[0] == '\'' {
130 quote := s[0]
131 s = s[1:]
132 i := 0
133 for i < len(s) && s[i] != quote {
134 i++
135 }
136 if i >= len(s) {
137 return nil, fmt.Errorf("unterminated %c string", quote)
138 }
139 f = append(f, s[:i])
140 s = s[i+1:]
141 continue
142 }
143 i := 0
144 for i < len(s) && !isSpaceByte(s[i]) {
145 i++
146 }
147 f = append(f, s[:i])
148 s = s[i:]
149 }
150 return f, nil
151}