github.com/gopherd/gonum@v0.0.4/cmplxs/cscalar/parse.go (about) 1 // Copyright ©2020 The Gonum Authors. All rights reserved. 2 // Use of this code is governed by a BSD-style 3 // license that can be found in the LICENSE file 4 5 package cscalar 6 7 import ( 8 "fmt" 9 "math/cmplx" 10 "strconv" 11 "strings" 12 ) 13 14 // parse converts the string s to a complex128. The string may be parenthesized and 15 // has the format [±]N±Ni. The order of the components is not strict. 16 func parse(s string) (complex128, error) { 17 if len(s) == 0 { 18 return 0, parseError{state: -1} 19 } 20 orig := s 21 22 wantClose := s[0] == '(' 23 if wantClose { 24 if s[len(s)-1] != ')' { 25 return 0, parseError{string: orig, state: -1} 26 } 27 s = s[1 : len(s)-1] 28 } 29 if len(s) == 0 { 30 return 0, parseError{string: orig, state: -1} 31 } 32 switch s[0] { 33 case 'n', 'N': 34 if strings.ToLower(s) == "nan" { 35 return cmplx.NaN(), nil 36 } 37 case 'i', 'I': 38 if strings.ToLower(s) == "inf" { 39 return cmplx.Inf(), nil 40 } 41 } 42 43 var q complex128 44 var parts byte 45 for i := 0; i < 4; i++ { 46 beg, end, p, err := floatPart(s) 47 if err != nil { 48 return q, parseError{string: orig, state: -1} 49 } 50 if parts&(1<<p) != 0 { 51 return q, parseError{string: orig, state: -1} 52 } 53 parts |= 1 << p 54 var v float64 55 switch s[:end] { 56 case "-": 57 if len(s[end:]) == 0 { 58 return q, parseError{string: orig, state: -1} 59 } 60 v = -1 61 case "+": 62 if len(s[end:]) == 0 { 63 return q, parseError{string: orig, state: -1} 64 } 65 v = 1 66 default: 67 v, err = strconv.ParseFloat(s[beg:end], 64) 68 if err != nil { 69 return q, err 70 } 71 } 72 s = s[end:] 73 switch p { 74 case 0: 75 q += complex(v, 0) 76 case 1: 77 q += complex(0, v) 78 s = s[1:] 79 } 80 if len(s) == 0 { 81 return q, nil 82 } 83 if !isSign(rune(s[0])) { 84 return q, parseError{string: orig, state: -1} 85 } 86 } 87 88 return q, parseError{string: orig, state: -1} 89 } 90 91 func floatPart(s string) (beg, end int, part uint, err error) { 92 const ( 93 wantMantSign = iota 94 wantMantIntInit 95 wantMantInt 96 wantMantFrac 97 wantExpSign 98 wantExpInt 99 100 wantInfN 101 wantInfF 102 wantCloseInf 103 104 wantNaNA 105 wantNaNN 106 wantCloseNaN 107 ) 108 var i, state int 109 var r rune 110 for i, r = range s { 111 switch state { 112 case wantMantSign: 113 switch { 114 default: 115 return 0, i, 0, parseError{string: s, state: state, rune: r} 116 case isSign(r): 117 state = wantMantIntInit 118 case isDigit(r): 119 state = wantMantInt 120 case isDot(r): 121 state = wantMantFrac 122 case r == 'i', r == 'I': 123 state = wantInfN 124 case r == 'n', r == 'N': 125 state = wantNaNA 126 } 127 128 case wantMantIntInit: 129 switch { 130 default: 131 return 0, i, 0, parseError{string: s, state: state, rune: r} 132 case isDigit(r): 133 state = wantMantInt 134 case isDot(r): 135 state = wantMantFrac 136 case r == 'i': 137 // We need to sneak a look-ahead here. 138 if i == len(s)-1 || s[i+1] == '-' || s[i+1] == '+' { 139 return 0, i, 1, nil 140 } 141 fallthrough 142 case r == 'I': 143 state = wantInfN 144 case r == 'n', r == 'N': 145 state = wantNaNA 146 } 147 148 case wantMantInt: 149 switch { 150 default: 151 return 0, i, 0, parseError{string: s, state: state, rune: r} 152 case isDigit(r): 153 // Do nothing 154 case isDot(r): 155 state = wantMantFrac 156 case isExponent(r): 157 state = wantExpSign 158 case isSign(r): 159 return 0, i, 0, nil 160 case r == 'i': 161 return 0, i, 1, nil 162 } 163 164 case wantMantFrac: 165 switch { 166 default: 167 return 0, i, 0, parseError{string: s, state: state, rune: r} 168 case isDigit(r): 169 // Do nothing 170 case isExponent(r): 171 state = wantExpSign 172 case isSign(r): 173 return 0, i, 0, nil 174 case r == 'i': 175 return 0, i, 1, nil 176 } 177 178 case wantExpSign: 179 switch { 180 default: 181 return 0, i, 0, parseError{string: s, state: state, rune: r} 182 case isSign(r) || isDigit(r): 183 state = wantExpInt 184 } 185 186 case wantExpInt: 187 switch { 188 default: 189 return 0, i, 0, parseError{string: s, state: state, rune: r} 190 case isDigit(r): 191 // Do nothing 192 case isSign(r): 193 return 0, i, 0, nil 194 case r == 'i': 195 return 0, i, 1, nil 196 } 197 198 case wantInfN: 199 if r != 'n' && r != 'N' { 200 return 0, i, 0, parseError{string: s, state: state, rune: r} 201 } 202 state = wantInfF 203 case wantInfF: 204 if r != 'f' && r != 'F' { 205 return 0, i, 0, parseError{string: s, state: state, rune: r} 206 } 207 state = wantCloseInf 208 case wantCloseInf: 209 switch { 210 default: 211 return 0, i, 0, parseError{string: s, state: state, rune: r} 212 case isSign(r): 213 return 0, i, 0, nil 214 case r == 'i': 215 return 0, i, 1, nil 216 } 217 218 case wantNaNA: 219 if r != 'a' && r != 'A' { 220 return 0, i, 0, parseError{string: s, state: state, rune: r} 221 } 222 state = wantNaNN 223 case wantNaNN: 224 if r != 'n' && r != 'N' { 225 return 0, i, 0, parseError{string: s, state: state, rune: r} 226 } 227 state = wantCloseNaN 228 case wantCloseNaN: 229 if isSign(rune(s[0])) { 230 beg = 1 231 } 232 switch { 233 default: 234 return beg, i, 0, parseError{string: s, state: state, rune: r} 235 case isSign(r): 236 return beg, i, 0, nil 237 case r == 'i': 238 return beg, i, 1, nil 239 } 240 } 241 } 242 switch state { 243 case wantMantSign, wantExpSign, wantExpInt: 244 if state == wantExpInt && isDigit(r) { 245 break 246 } 247 return 0, i, 0, parseError{string: s, state: state, rune: r} 248 } 249 return 0, len(s), 0, nil 250 } 251 252 func isSign(r rune) bool { 253 return r == '+' || r == '-' 254 } 255 256 func isDigit(r rune) bool { 257 return '0' <= r && r <= '9' 258 } 259 260 func isExponent(r rune) bool { 261 return r == 'e' || r == 'E' 262 } 263 264 func isDot(r rune) bool { 265 return r == '.' 266 } 267 268 type parseError struct { 269 string string 270 state int 271 rune rune 272 } 273 274 func (e parseError) Error() string { 275 if e.state < 0 { 276 return fmt.Sprintf("quat: failed to parse: %q", e.string) 277 } 278 return fmt.Sprintf("quat: failed to parse in state %d with %q: %q", e.state, e.rune, e.string) 279 }