package parser import ( "strings" "asciigoat.org/core/lexer" ) // AcceptQuotedString consumes a quoted string from the source // and returns it unquoted and unescaped func (p *TextParser) AcceptQuotedString() (string, bool, error) { r, _, err := p.ReadRune() switch { case err != nil: // nothing here return "", false, err case r != RuneQuotes: // not for us p.UnreadRune() return "", false, nil default: // let's roll s, err := lexQuotedString(p) switch { case err != nil: // bad quoted string return "", false, err default: // success return s, true, nil } } } func lexQuotedString(p *TextParser) (string, *lexer.Error) { s, ok, err := lexQuotedStringNoEscape(p) switch { case err != nil: return "", err case ok: return s, nil default: // escape character detected return lexQuotedStringEscaped(p) } } func lexQuotedStringNoEscape(p *TextParser) (string, bool, *lexer.Error) { for { r, _, err := p.ReadRune() switch { case err != nil: // incomplete return "", false, NewErrIncompleteQuotedString(p) case r == RuneQuotes: // end, just remove the quotes s := p.String() l := len(s) return s[1 : l-1], true, nil case r == RuneEscape: // things just got complicated... p.UnreadRune() return "", false, nil case IsNewLine(r): // new lines within quoted values are acceptable p.UnreadRune() p.AcceptNewLine() default: // continue } } } // Unquoted removes quotes the content and unescapes the content func lexQuotedStringEscaped(p *TextParser) (string, *lexer.Error) { var result strings.Builder // append what was accepted before the escape character result.WriteString(p.String()[1:]) for { r, _, err := p.ReadRune() switch { case err != nil: // incomplete quoted return "", NewErrIncompleteQuotedString(p) case r == RuneQuotes: // end return result.String(), nil case r == RuneEscape: // escaped r2, _, err := p.ReadRune() switch { case err != nil: // incomplete escaped return "", NewErrIncompleteEscaped(p) case IsNewLine(r2): // escaped new line, skip p.UnreadRune() p.AcceptNewLine() default: // TODO: check valid escape character and // append to result p.UnreadRune() s := string([]rune{r, r2}) err := NewErrInvalidEscapeSequence(p, s) return "", err } default: // normal, append to result result.WriteRune(r) } } } // Unquoted removes quotes and unescapes the content func Unquoted(s string) (string, error) { var p TextParser if s == "" { return "", nil } p.InitString(s) unquoted, ok, err := p.AcceptQuotedString() switch { case err != nil: // bad string return "", err case ok: // success return unquoted, nil default: // not quoted return s, nil } }