///|
/// Decode one token. Unknown escapes are diagnosed, never silently discarded.
fn decode_value(
source : String,
line : Line,
start : Int,
end : Int,
dialect : Dialect,
diagnostics : Array[Diagnostic],
) -> (String, Span) {
if start == end {
return ("", line_span(line, start, end))
}
if source[start] == '"' || source[start] == '\'' {
let quote = source[start]
let out = StringBuilder()
let mut i = start + 1
let mut chunk = i
let mut closed = false
while i < end {
if source[i] == quote {
out.write_string(text(source, chunk, i))
i += 1
closed = true
break
} else if source[i] == '\\' && quote == '"' {
out.write_string(text(source, chunk, i))
if i + 1 >= end {
break
}
match source[i + 1] {
'n' => out.write_string("\n")
'r' => out.write_string("\r")
't' => out.write_string("\t")
'\\' => out.write_string("\\")
'"' => out.write_string("\"")
_ => {
diagnostics.push(
diagnostic(
"INI008",
"unsupported quoted escape",
line_span(line, i, i + 2),
),
)
out.write_string(text(source, i, i + 2))
}
}
i += 2
chunk = i
} else {
i += 1
}
}
if !closed {
diagnostics.push(
diagnostic(
"INI006",
"unclosed quoted value",
line_span(line, start, end),
),
)
return (text(source, start, end), line_span(line, start, end))
}
let (tail, _) = trim_bounds(source, i, end)
if tail < end &&
!(dialect.inline_comments && tail > i && is_comment(source[tail], dialect)) {
diagnostics.push(
diagnostic(
"INI007",
"unexpected text after quoted value",
line_span(line, tail, end),
),
)
}
(out.to_string(), line_span(line, start, i))
} else {
let mut stop = end
if dialect.inline_comments {
let mut i = start
while i < end {
if is_comment(source[i], dialect) &&
(i == start || horizontal(source[i - 1])) {
stop = i
break
}
i += 1
}
}
let (_, b) = trim_bounds(source, start, stop)
(text(source, start, b), line_span(line, start, b))
}
}