mirror of https://github.com/adamdruppe/arsd.git
Add support for single-quoted strings to `arsd.ini`
This commit is contained in:
parent
c0aed7220a
commit
f821ebdc08
152
ini.d
152
ini.d
|
@ -17,15 +17,19 @@ enum isCompatibleString(T) = (is(T == string) || is(T == const(char)[]) || is(T
|
||||||
///
|
///
|
||||||
enum IniDialect : ulong {
|
enum IniDialect : ulong {
|
||||||
lite = 0,
|
lite = 0,
|
||||||
|
|
||||||
lineComments = 0b_0000_0000_0000_0001,
|
lineComments = 0b_0000_0000_0000_0001,
|
||||||
inlineComments = 0b_0000_0000_0000_0011,
|
inlineComments = 0b_0000_0000_0000_0011,
|
||||||
hashLineComments = 0b_0000_0000_0000_0100,
|
hashLineComments = 0b_0000_0000_0000_0100,
|
||||||
hashInlineComments = 0b_0000_0000_0000_1100,
|
hashInlineComments = 0b_0000_0000_0000_1100,
|
||||||
|
|
||||||
escapeSequences = 0b_0000_0000_0001_0000,
|
escapeSequences = 0b_0000_0000_0001_0000,
|
||||||
lineFolding = 0b_0000_0000_0010_0000,
|
lineFolding = 0b_0000_0000_0010_0000,
|
||||||
quotedStrings = 0b_0000_0000_0100_0000,
|
quotedStrings = 0b_0000_0000_0100_0000,
|
||||||
arrays = 0b_0000_0000_1000_0000,
|
singleQuoteQuotedStrings = 0b_0000_0000_1000_0000,
|
||||||
colonKeys = 0b_0000_0001_0000_0000,
|
|
||||||
|
arrays = 0b_0000_0001_0000_0000,
|
||||||
|
colonKeys = 0b_0000_0010_0000_0000,
|
||||||
defaults = (lineComments | quotedStrings),
|
defaults = (lineComments | quotedStrings),
|
||||||
}
|
}
|
||||||
//dfmt on
|
//dfmt on
|
||||||
|
@ -265,17 +269,40 @@ struct IniParser(
|
||||||
whitespace,
|
whitespace,
|
||||||
}
|
}
|
||||||
|
|
||||||
static if (dialect.hasFeature(Dialect.quotedStrings)) {
|
enum QuotedString : ubyte {
|
||||||
bool inQuotedString = false;
|
none = 0,
|
||||||
|
regular,
|
||||||
|
single,
|
||||||
|
}
|
||||||
|
|
||||||
|
// dfmt off
|
||||||
|
enum hasAnyQuotedString = (
|
||||||
|
dialect.hasFeature(Dialect.quotedStrings) ||
|
||||||
|
dialect.hasFeature(Dialect.singleQuoteQuotedStrings)
|
||||||
|
);
|
||||||
|
// dfmt on
|
||||||
|
|
||||||
|
static if (hasAnyQuotedString) {
|
||||||
|
auto inQuotedString = QuotedString.none;
|
||||||
|
}
|
||||||
|
static if (dialect.hasFeature(Dialect.quotedStrings)) {
|
||||||
if (_source[0] == '"') {
|
if (_source[0] == '"') {
|
||||||
inQuotedString = true;
|
inQuotedString = QuotedString.regular;
|
||||||
|
|
||||||
// chomp quote initiator
|
// chomp quote initiator
|
||||||
_source = _source[1 .. $];
|
_source = _source[1 .. $];
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
enum inQuotedString = false;
|
static if (dialect.hasFeature(Dialect.singleQuoteQuotedStrings)) {
|
||||||
|
if (_source[0] == '\'') {
|
||||||
|
inQuotedString = QuotedString.single;
|
||||||
|
|
||||||
|
// chomp quote initiator
|
||||||
|
_source = _source[1 .. $];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static if (!hasAnyQuotedString) {
|
||||||
|
enum inQuotedString = QuotedString.none;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result nextChar(const char c) @safe pure nothrow @nogc {
|
Result nextChar(const char c) @safe pure nothrow @nogc {
|
||||||
|
@ -289,20 +316,32 @@ struct IniParser(
|
||||||
case '\x0B':
|
case '\x0B':
|
||||||
case '\x0C':
|
case '\x0C':
|
||||||
case ' ':
|
case ' ':
|
||||||
return (inQuotedString) ? Result.regular : Result.whitespace;
|
return (inQuotedString != QuotedString.none) ? Result.regular : Result.whitespace;
|
||||||
|
|
||||||
case '\x0A':
|
case '\x0A':
|
||||||
case '\x0D':
|
case '\x0D':
|
||||||
return (inQuotedString)
|
return (inQuotedString != QuotedString.none)
|
||||||
? Result.regular : Result.end;
|
? Result.regular : Result.end;
|
||||||
|
|
||||||
case '"':
|
case '"':
|
||||||
return (inQuotedString)
|
static if (dialect.hasFeature(Dialect.quotedStrings)) {
|
||||||
? Result.end : Result.regular;
|
return (inQuotedString == QuotedString.regular)
|
||||||
|
? Result.end : Result.regular;
|
||||||
|
} else {
|
||||||
|
return Result.regular;
|
||||||
|
}
|
||||||
|
|
||||||
|
case '\'':
|
||||||
|
static if (dialect.hasFeature(Dialect.singleQuoteQuotedStrings)) {
|
||||||
|
return (inQuotedString == QuotedString.single)
|
||||||
|
? Result.end : Result.regular;
|
||||||
|
} else {
|
||||||
|
return Result.regular;
|
||||||
|
}
|
||||||
|
|
||||||
case '#':
|
case '#':
|
||||||
if (dialect.hasFeature(Dialect.hashInlineComments)) {
|
if (dialect.hasFeature(Dialect.hashInlineComments)) {
|
||||||
return (inQuotedString)
|
return (inQuotedString != QuotedString.none)
|
||||||
? Result.regular : Result.end;
|
? Result.regular : Result.end;
|
||||||
} else {
|
} else {
|
||||||
return Result.regular;
|
return Result.regular;
|
||||||
|
@ -310,7 +349,7 @@ struct IniParser(
|
||||||
|
|
||||||
case ';':
|
case ';':
|
||||||
if (dialect.hasFeature(Dialect.inlineComments)) {
|
if (dialect.hasFeature(Dialect.inlineComments)) {
|
||||||
return (inQuotedString)
|
return (inQuotedString != QuotedString.none)
|
||||||
? Result.regular : Result.end;
|
? Result.regular : Result.end;
|
||||||
} else {
|
} else {
|
||||||
return Result.regular;
|
return Result.regular;
|
||||||
|
@ -325,7 +364,7 @@ struct IniParser(
|
||||||
|
|
||||||
case '=':
|
case '=':
|
||||||
static if (tokenType == TokenType.key) {
|
static if (tokenType == TokenType.key) {
|
||||||
return (inQuotedString)
|
return (inQuotedString != QuotedString.none)
|
||||||
? Result.regular : Result.end;
|
? Result.regular : Result.end;
|
||||||
} else {
|
} else {
|
||||||
return Result.regular;
|
return Result.regular;
|
||||||
|
@ -333,7 +372,7 @@ struct IniParser(
|
||||||
|
|
||||||
case ']':
|
case ']':
|
||||||
static if (tokenType == TokenType.sectionHeader) {
|
static if (tokenType == TokenType.sectionHeader) {
|
||||||
return (inQuotedString)
|
return (inQuotedString != QuotedString.none)
|
||||||
? Result.regular : Result.end;
|
? Result.regular : Result.end;
|
||||||
} else {
|
} else {
|
||||||
return Result.regular;
|
return Result.regular;
|
||||||
|
@ -360,9 +399,11 @@ struct IniParser(
|
||||||
auto token = Token(tokenType, _source[0 .. idxEOT]);
|
auto token = Token(tokenType, _source[0 .. idxEOT]);
|
||||||
_source = _source[idxEOT .. $];
|
_source = _source[idxEOT .. $];
|
||||||
|
|
||||||
if (inQuotedString) {
|
if (inQuotedString != QuotedString.none) {
|
||||||
// chomp quote terminator
|
if (_source.length > 0) {
|
||||||
_source = _source[1 .. $];
|
// chomp quote terminator
|
||||||
|
_source = _source[1 .. $];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return token;
|
return token;
|
||||||
|
@ -856,6 +897,81 @@ s2key2 = value no.4
|
||||||
assert(parser.skipIrrelevant());
|
assert(parser.skipIrrelevant());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@safe unittest {
|
||||||
|
static immutable rawIni =
|
||||||
|
"\"foo=bar\"=foobar\n"
|
||||||
|
~ "'foo = bar' = foo_bar\n"
|
||||||
|
~ "foo = \"bar\"\n"
|
||||||
|
~ "foo = 'bar'\n"
|
||||||
|
~ "multi_line = 'line1\nline2'\n"
|
||||||
|
~ "syntax = \"error";
|
||||||
|
enum dialect = (Dialect.quotedStrings | Dialect.singleQuoteQuotedStrings);
|
||||||
|
auto parser = makeIniFilteredParser!dialect(rawIni);
|
||||||
|
|
||||||
|
{
|
||||||
|
assert(!parser.empty);
|
||||||
|
assert(parser.front == parser.Token(TokenType.key, "foo=bar"));
|
||||||
|
|
||||||
|
parser.popFront();
|
||||||
|
assert(!parser.empty);
|
||||||
|
assert(parser.front == parser.Token(TokenType.value, "foobar"));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
parser.popFront();
|
||||||
|
assert(!parser.empty);
|
||||||
|
assert(parser.front == parser.Token(TokenType.key, "foo = bar"));
|
||||||
|
|
||||||
|
parser.popFront();
|
||||||
|
assert(!parser.empty);
|
||||||
|
assert(parser.front == parser.Token(TokenType.value, "foo_bar"));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
parser.popFront();
|
||||||
|
assert(!parser.empty);
|
||||||
|
assert(parser.front == parser.Token(TokenType.key, "foo"));
|
||||||
|
|
||||||
|
parser.popFront();
|
||||||
|
assert(!parser.empty);
|
||||||
|
assert(parser.front == parser.Token(TokenType.value, "bar"));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
parser.popFront();
|
||||||
|
assert(!parser.empty);
|
||||||
|
assert(parser.front == parser.Token(TokenType.key, "foo"));
|
||||||
|
|
||||||
|
parser.popFront();
|
||||||
|
assert(!parser.empty);
|
||||||
|
assert(parser.front == parser.Token(TokenType.value, "bar"));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
parser.popFront();
|
||||||
|
assert(!parser.empty);
|
||||||
|
assert(parser.front == parser.Token(TokenType.key, "multi_line"));
|
||||||
|
|
||||||
|
parser.popFront();
|
||||||
|
assert(!parser.empty);
|
||||||
|
assert(parser.front == parser.Token(TokenType.value, "line1\nline2"));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
parser.popFront();
|
||||||
|
assert(!parser.empty);
|
||||||
|
assert(parser.front == parser.Token(TokenType.key, "syntax"));
|
||||||
|
|
||||||
|
parser.popFront();
|
||||||
|
assert(!parser.empty);
|
||||||
|
assert(parser.front == parser.Token(TokenType.value, "error"));
|
||||||
|
}
|
||||||
|
|
||||||
|
parser.popFront();
|
||||||
|
assert(parser.empty);
|
||||||
|
}
|
||||||
|
|
||||||
/++
|
/++
|
||||||
Convenience function to create a low-level parser
|
Convenience function to create a low-level parser
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue