///|
#callsite(autofill(loc))
pub fn[A] error(msg : String, loc~ : SourceLoc) -> A raise ControlFlow {
raise ControlFlow::Error(msg + " " + loc.to_string())
}
///|
fn fromCharCode(hex : StringView, base : Int) -> Char {
(try! @string.parse_int(hex, base~)).to_char().unwrap_or('?')
}
///|
pub fn manualUnescape(input : StringView, buf : StringBuilder) -> Unit {
for state = input {
match state {
// EOF
"" => break
rest => {
let (c, next_rest) = lexmatch rest with longest {
("\\n", rest) => ('\n', rest)
("\\t", rest) => ('\t', rest)
("\\r", rest) => ('\r', rest)
// Backspace
("\\b", rest) => ('\b', rest)
// Unicode escape
("\\u[0-9a-fA-F]{4}" as raw, rest) =>
(fromCharCode(raw[2:], 16), rest)
("\\x[0-9a-fA-F]{2}" as raw, rest) =>
(fromCharCode(raw[2:], 16), rest)
// Unicode escape with braces
("\\u[{][0-9a-fA-F]+[}]" as raw, rest) =>
(fromCharCode(raw[3:-1], 16), rest)
// Octal escape
("\\o[0-3][0-7]{2}" as raw, rest) => (fromCharCode(raw[2:], 8), rest)
("\\\\", rest) => ('\\', rest)
("." as c, rest) => (c, rest)
_ => break
}
buf.write_char(c)
continue next_rest
}
}
}
}
///|
test "manualUnescape" {
let builder = StringBuilder::new()
manualUnescape("\\nhello", builder)
inspect(
builder.to_string(),
content=(
#|
#|hello
),
)
let builder = StringBuilder::new()
manualUnescape("hello\\nworld", builder)
inspect(
builder.to_string(),
content=(
#|hello
#|world
),
)
let builder = StringBuilder::new()
manualUnescape("\\u0041", builder)
inspect(builder.to_string(), content="A")
let builder = StringBuilder::new()
manualUnescape("\\x41", builder)
inspect(builder.to_string(), content="A")
let builder = StringBuilder::new()
manualUnescape("\\o061", builder)
inspect(builder.to_string(), content="1")
}