///|
/// Input that is not lexable SQLite SQL.
///
/// `offset` is a UTF-8 byte offset into the source, so it points at the same
/// place a byte-oriented editor or tool would.
pub suberror LexError {
UnexpectedByte(Byte, offset~ : Int32)
PostgresCast(offset~ : Int32)
PostgresDollarQuote(offset~ : Int32)
}
///|
pub impl Show for LexError with fn output(self, logger) {
match self {
UnexpectedByte(b, offset~) =>
logger.write_string(
"unexpected byte \{escape_byte(b)} at offset \{offset}",
)
PostgresCast(offset~) =>
logger.write_string(
"'::' is not SQLite syntax (PostgreSQL cast) at offset \{offset}; dialect is SQLite",
)
PostgresDollarQuote(offset~) =>
logger.write_string(
"'$$' is not SQLite syntax (PostgreSQL dollar-quote) at offset \{offset}; dialect is SQLite",
)
}
}
///|
pub extend LexError with Show::{to_string, output}
///|
/// A byte as a quoted character literal, in the notation of OCaml's `%C` so
/// messages match the reference implementation: printable ASCII as itself,
/// `\b \t \n \r` by name, and anything else as three decimal digits
/// (`'\001'`, `'\127'`).
fn escape_byte(b : Byte) -> String {
let body = match b.to_int() {
39 => "\\'"
92 => "\\\\"
8 => "\\b"
9 => "\\t"
10 => "\\n"
13 => "\\r"
n if n >= 32 && n < 127 => n.unsafe_to_char().to_string()
n => "\\\{n.to_string().pad_start(3, '0')}"
}
"'\{body}'"
}
///|
/// The error for the character at `s[i]`. Only ASCII code units reach here:
/// every non-ASCII unit is an identifier character.
fn lex_error(s : String, i : Int32) -> LexError {
let c = s[i]
let offset = @text.utf8_width(s[:i])
match (c, if i + 1 < s.length() { s[i + 1] } else { 0 }) {
(':', ':') => PostgresCast(offset~)
('$', '$') => PostgresDollarQuote(offset~)
_ => UnexpectedByte(c.to_byte(), offset~)
}
}