readQuotedString handles the actual scanning of a single/double-quoted string
(quote rune)
| 979 | |
| 980 | // readQuotedString handles the actual scanning of a single/double-quoted string |
| 981 | func (t *Tokenizer) readQuotedString(quote rune) (models.Token, error) { |
| 982 | // Store start position for error reporting |
| 983 | startPos := t.pos |
| 984 | |
| 985 | // Check for triple quotes |
| 986 | if t.pos.Index+2 < len(t.input) { |
| 987 | next1, _ := utf8.DecodeRune(t.input[t.pos.Index+1:]) |
| 988 | next2, _ := utf8.DecodeRune(t.input[t.pos.Index+2:]) |
| 989 | if next1 == quote && next2 == quote { |
| 990 | return t.readTripleQuotedString(quote) |
| 991 | } |
| 992 | } |
| 993 | |
| 994 | // Get opening quote and remember the original quote character |
| 995 | r, size := utf8.DecodeRune(t.input[t.pos.Index:]) |
| 996 | originalQuote := r |
| 997 | quote = normalizeQuote(r) |
| 998 | |
| 999 | // Skip opening quote |
| 1000 | t.pos.AdvanceRune(r, size) |
| 1001 | |
| 1002 | var buf bytes.Buffer |
| 1003 | for t.pos.Index < len(t.input) { |
| 1004 | r, size := utf8.DecodeRune(t.input[t.pos.Index:]) |
| 1005 | r = normalizeQuote(r) |
| 1006 | |
| 1007 | if r == quote { |
| 1008 | // Check for escaped quote |
| 1009 | if t.pos.Index+size < len(t.input) { |
| 1010 | nextR, nextSize := utf8.DecodeRune(t.input[t.pos.Index+size:]) |
| 1011 | nextR = normalizeQuote(nextR) |
| 1012 | if nextR == quote { |
| 1013 | // Include one quote and skip the other |
| 1014 | buf.WriteRune(r) |
| 1015 | t.pos.Index += size + nextSize |
| 1016 | t.pos.Column += 2 |
| 1017 | continue |
| 1018 | } |
| 1019 | } |
| 1020 | |
| 1021 | // End of string |
| 1022 | t.pos.Index += size |
| 1023 | t.pos.Column++ |
| 1024 | |
| 1025 | value := buf.String() |
| 1026 | // For test compatibility, use appropriate token types based on quote style |
| 1027 | var tokenType models.TokenType |
| 1028 | if originalQuote == '\'' || originalQuote == '\u2018' || originalQuote == '\u2019' || |
| 1029 | originalQuote == '«' || originalQuote == '»' { |
| 1030 | tokenType = models.TokenTypeSingleQuotedString // 124 |
| 1031 | } else if originalQuote == '"' || originalQuote == '\u201C' || originalQuote == '\u201D' { |
| 1032 | tokenType = models.TokenTypeDoubleQuotedString // 124 |
| 1033 | } else { |
| 1034 | tokenType = models.TokenTypeString // 20 |
| 1035 | } |
| 1036 | return models.Token{ |
| 1037 | Type: tokenType, |
| 1038 | Value: value, |
no test coverage detected