scanString scans a quoted string presuming the first double quote has already been matched and consumed from the input. scanString does not consume the terminating double quote.
()
| 252 | // already been matched and consumed from the input. scanString does not |
| 253 | // consume the terminating double quote. |
| 254 | func (l *Lexer) scanString() (string, error) { |
| 255 | var s strings.Builder |
| 256 | // We optimistically try to scan the string as a basic ascii string |
| 257 | // with standard escapes, \t, \n etc. If we hit \u or any non-ascii UTF |
| 258 | // we read the rest of the string into a bytes buffer and call scanStringBytes() |
| 259 | // to finish the job. |
| 260 | for { |
| 261 | c, err := l.peek() |
| 262 | if err != nil { |
| 263 | return "", err |
| 264 | } |
| 265 | if c == '"' { |
| 266 | return s.String(), nil |
| 267 | } |
| 268 | if c >= utf8.RuneSelf { |
| 269 | bytes, err := l.scanToCloseQuote(nil) |
| 270 | if err != nil { |
| 271 | return "", err |
| 272 | } |
| 273 | return parseStringBytes(&s, bytes) |
| 274 | } |
| 275 | l.skip(1) |
| 276 | if c == '\n' { |
| 277 | return "", errors.New("unescaped line break") |
| 278 | } |
| 279 | if c == '\\' { |
| 280 | c, err = l.readByte() |
| 281 | if err != nil { |
| 282 | if err == io.EOF { |
| 283 | err = errors.New("no end quote") |
| 284 | } |
| 285 | return "", err |
| 286 | } |
| 287 | switch c { |
| 288 | case 'u': |
| 289 | bytes, err := l.scanToCloseQuote([]byte{'\\', 'u'}) |
| 290 | if err != nil { |
| 291 | return "", err |
| 292 | } |
| 293 | return parseStringBytes(&s, bytes) |
| 294 | case '"', '\\', '/': |
| 295 | // Write this byte below as is. |
| 296 | case 'b': |
| 297 | c = '\b' |
| 298 | case 'f': |
| 299 | c = '\f' |
| 300 | case 'n': |
| 301 | c = '\n' |
| 302 | case 'r': |
| 303 | c = '\r' |
| 304 | case 't': |
| 305 | c = '\t' |
| 306 | default: |
| 307 | return "", fmt.Errorf("illegal escape (\\%c)", c) |
| 308 | } |
| 309 | } |
| 310 | s.WriteByte(c) |
| 311 | } |
no test coverage detected