85 lines
3.2 KiB
Go
85 lines
3.2 KiB
Go
package lexer
|
|
|
|
import "github.com/mmakaay/toml/parser"
|
|
|
|
// There are four ways to express strings: basic, multi-line basic, literal,
|
|
// and multi-line literal. All strings must contain only valid UTF-8 characters.
|
|
// * Multi-line basic strings are surrounded by three quotation marks on each side.
|
|
// * Basic strings are surrounded by quotation marks.
|
|
func stateStringValue(l *parser.Parser) parser.StateFn {
|
|
switch {
|
|
case l.SkipMatching(doubleQuote3...):
|
|
return stateMultiLineBasicString
|
|
case l.SkipMatching(doubleQuote):
|
|
return l.QueueStates(stateParseString, stateBasicStringSpecific)
|
|
}
|
|
return l.UnexpectedInputError("a string value")
|
|
}
|
|
|
|
// Specific handling of input for basic strings.
|
|
// * A double quote ends the string
|
|
// * No additional \escape sequences are allowed. What the spec say about this:
|
|
// "All other escape sequences [..] are reserved and, if used, TOML should
|
|
// produce an error.""
|
|
|
|
func stateBasicStringSpecific(p *parser.Parser) parser.StateFn {
|
|
switch {
|
|
case p.SkipMatching(doubleQuote):
|
|
if err := p.EmitInterpreted(ItemString); err != nil {
|
|
return p.EmitError("Invalid data in string: %s", err)
|
|
}
|
|
return stateKeyValuePair
|
|
case p.Upcoming(backslash):
|
|
return p.EmitError("Invalid escape sequence")
|
|
default:
|
|
return p.QueueStates(stateParseString, stateBasicStringSpecific)
|
|
}
|
|
}
|
|
|
|
func stateMultiLineBasicString(l *parser.Parser) parser.StateFn {
|
|
l.EmitError("Not yet implemented")
|
|
return nil
|
|
}
|
|
|
|
// Any Unicode character may be used except those that must be escaped:
|
|
// quotation mark, backslash, and the control characters (U+0000 to U+001F, U+007F).
|
|
const invalidBasicStringCharacters string = "\"\\" +
|
|
"\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007" +
|
|
"\u0008\u0009\u000A\u000B\u000C\u000D\u000E\u000F" +
|
|
"\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017" +
|
|
"\u0018\u0019\u001A\u001B\u001C\u001D\u001E\u001F" +
|
|
"\u007F"
|
|
|
|
func stateParseString(l *parser.Parser) parser.StateFn {
|
|
switch {
|
|
case l.AtEndOfFile():
|
|
return l.UnexpectedEndOfFile("basic string token")
|
|
case l.AcceptMatching(backslash, escapeChars):
|
|
// For convenience, some popular characters have a compact escape sequence.
|
|
// \b - backspace (U+0008)
|
|
// \t - tab (U+0009)
|
|
// \n - linefeed (U+000A)
|
|
// \f - form feed (U+000C)
|
|
// \r - carriage return (U+000D)
|
|
// \" - quote (U+0022)
|
|
// \\ - backslash (U+005C)
|
|
case l.AcceptMatching(shortUtf8Match...):
|
|
// \uXXXX - unicode (U+XXXX)
|
|
case l.AcceptMatching(longUtf8Match...):
|
|
// \UXXXXXXXX - unicode (U+XXXXXXXX)
|
|
case l.Upcoming(backslash) || l.Upcoming(doubleQuote):
|
|
// Returning to the parent state to have special cases handled,
|
|
// because there are differences between single and multi line strings.
|
|
return l.ToParentState()
|
|
case l.Upcoming(invalidBasicStringCharacters):
|
|
// Any Unicode character may be used except those that must be escaped:
|
|
// quotation mark, backslash, and the control characters (U+0000 to U+001F, U+007F).
|
|
r, _, _ := l.Match(invalidBasicStringCharacters)
|
|
l.EmitError("Invalid character in basic string: %q (must be escaped)", r[0])
|
|
return nil
|
|
default:
|
|
l.AcceptAny()
|
|
}
|
|
return stateParseString
|
|
}
|