use std::io;
use crate::number::{self, Number};
pub use crate::syntax::{CharSyntax, KeywordSyntax, StringSyntax};
use crate::Value;
#[derive(Copy, Clone, Debug)]
pub struct Options {
keyword_syntax: KeywordSyntax,
nil_syntax: NilSyntax,
bool_syntax: BoolSyntax,
vector_syntax: VectorSyntax,
bytes_syntax: BytesSyntax,
string_syntax: StringSyntax,
char_syntax: CharSyntax,
}
impl Options {
pub fn elisp() -> Self {
Options {
keyword_syntax: KeywordSyntax::ColonPrefix,
nil_syntax: NilSyntax::Symbol,
bool_syntax: BoolSyntax::Symbol,
vector_syntax: VectorSyntax::Brackets,
bytes_syntax: BytesSyntax::Elisp,
string_syntax: StringSyntax::Elisp,
char_syntax: CharSyntax::Elisp,
}
}
pub fn with_keyword_syntax(mut self, syntax: KeywordSyntax) -> Self {
self.keyword_syntax = syntax;
self
}
pub fn with_nil_syntax(mut self, syntax: NilSyntax) -> Self {
self.nil_syntax = syntax;
self
}
pub fn with_bool_syntax(mut self, syntax: BoolSyntax) -> Self {
self.bool_syntax = syntax;
self
}
pub fn with_vector_syntax(mut self, syntax: VectorSyntax) -> Self {
self.vector_syntax = syntax;
self
}
pub fn with_bytes_syntax(mut self, syntax: BytesSyntax) -> Self {
self.bytes_syntax = syntax;
self
}
pub fn with_string_syntax(mut self, syntax: StringSyntax) -> Self {
self.string_syntax = syntax;
self
}
pub fn with_char_syntax(mut self, syntax: CharSyntax) -> Self {
self.char_syntax = syntax;
self
}
}
impl Default for Options {
fn default() -> Self {
Options {
keyword_syntax: KeywordSyntax::Octothorpe,
nil_syntax: NilSyntax::Token,
bool_syntax: BoolSyntax::Token,
vector_syntax: VectorSyntax::Octothorpe,
bytes_syntax: BytesSyntax::R7RS,
string_syntax: StringSyntax::R6RS,
char_syntax: CharSyntax::R6RS,
}
}
}
#[derive(Debug, Clone, Copy)]
pub enum NilSyntax {
Symbol,
Token,
EmptyList,
False,
}
#[derive(Debug, Clone, Copy)]
pub enum BoolSyntax {
Token,
Symbol,
}
#[derive(Debug, Clone, Copy)]
pub enum VectorSyntax {
Octothorpe,
Brackets,
}
#[derive(Debug, Clone, Copy)]
pub enum BytesSyntax {
R6RS,
R7RS,
Elisp,
}
pub enum CharEscape {
Quote,
ReverseSolidus,
Alert,
Backspace,
LineFeed,
CarriageReturn,
Tab,
AsciiControl(u8),
}
impl CharEscape {
#[inline]
fn from_escape_table(escape: u8, byte: u8) -> CharEscape {
match escape {
self::AA => CharEscape::Alert,
self::BB => CharEscape::Backspace,
self::TT => CharEscape::Tab,
self::NN => CharEscape::LineFeed,
self::RR => CharEscape::CarriageReturn,
self::QU => CharEscape::Quote,
self::BS => CharEscape::ReverseSolidus,
self::UU => CharEscape::AsciiControl(byte),
_ => unreachable!(),
}
}
}
pub enum VectorType {
Generic,
Byte,
}
pub trait Formatter {
#[inline]
fn write_nil<W: ?Sized>(&mut self, writer: &mut W) -> io::Result<()>
where
W: io::Write,
{
writer.write_all(b"#nil")
}
#[inline]
fn write_null<W: ?Sized>(&mut self, writer: &mut W) -> io::Result<()>
where
W: io::Write,
{
writer.write_all(b"()")
}
#[inline]
fn write_bool<W: ?Sized>(&mut self, writer: &mut W, value: bool) -> io::Result<()>
where
W: io::Write,
{
writer.write_all(if value { b"#t" } else { b"#f" })
}
#[inline]
fn write_number<W: ?Sized>(&mut self, writer: &mut W, value: &Number) -> io::Result<()>
where
W: io::Write,
{
struct Write<'a, W: io::Write + ?Sized> {
writer: &'a mut W,
}
impl<'a, W: io::Write + ?Sized> number::Visitor for Write<'a, W> {
type Value = ();
type Error = io::Error;
fn error<T: Into<String>>(msg: T) -> io::Error {
io::Error::new(io::ErrorKind::Other, msg.into())
}
fn visit_u64(self, n: u64) -> io::Result<()> {
itoa::write(self.writer, n).map(drop)
}
fn visit_i64(self, n: i64) -> io::Result<()> {
itoa::write(self.writer, n).map(drop)
}
fn visit_f64(self, n: f64) -> io::Result<()> {
let mut buffer = ryu::Buffer::new();
let s = buffer.format(n);
self.writer.write_all(s.as_bytes())
}
}
value.visit(Write { writer })
}
fn write_char<W: ?Sized>(&mut self, writer: &mut W, c: char) -> io::Result<()>
where
W: io::Write,
{
write_scheme_char(writer, c)
}
#[inline]
fn begin_string<W: ?Sized>(&mut self, writer: &mut W) -> io::Result<()>
where
W: io::Write,
{
writer.write_all(b"\"")
}
#[inline]
fn end_string<W: ?Sized>(&mut self, writer: &mut W) -> io::Result<()>
where
W: io::Write,
{
writer.write_all(b"\"")
}
#[inline]
fn write_string_fragment<W: ?Sized>(&mut self, writer: &mut W, fragment: &str) -> io::Result<()>
where
W: io::Write,
{
writer.write_all(fragment.as_bytes())
}
#[inline]
fn write_char_escape<W: ?Sized>(
&mut self,
writer: &mut W,
char_escape: CharEscape,
) -> io::Result<()>
where
W: io::Write,
{
write_r6rs_char_escape(writer, char_escape)
}
#[inline]
fn write_symbol<W: ?Sized>(&mut self, writer: &mut W, name: &str) -> io::Result<()>
where
W: io::Write,
{
writer.write_all(name.as_bytes())
}
#[inline]
fn write_keyword<W: ?Sized>(&mut self, writer: &mut W, name: &str) -> io::Result<()>
where
W: io::Write,
{
writer.write_all(b"#:")?;
writer.write_all(name.as_bytes())
}
#[inline]
fn write_bytes<W: ?Sized>(&mut self, writer: &mut W, bytes: &[u8]) -> io::Result<()>
where
W: io::Write,
{
write_scheme_vector(self, writer, VectorType::Byte, bytes, |writer, &octet| {
itoa::write(writer, octet).map(|_| ())
})
}
#[inline]
fn begin_list<W: ?Sized>(&mut self, writer: &mut W) -> io::Result<()>
where
W: io::Write,
{
writer.write_all(b"(")
}
#[inline]
fn end_list<W: ?Sized>(&mut self, writer: &mut W) -> io::Result<()>
where
W: io::Write,
{
writer.write_all(b")")
}
#[inline]
fn begin_seq_element<W: ?Sized>(&mut self, writer: &mut W, first: bool) -> io::Result<()>
where
W: io::Write,
{
if first {
Ok(())
} else {
writer.write_all(b" ")
}
}
#[inline]
fn end_seq_element<W: ?Sized>(&mut self, _writer: &mut W) -> io::Result<()>
where
W: io::Write,
{
Ok(())
}
#[inline]
fn begin_vector<W: ?Sized>(&mut self, kind: VectorType, writer: &mut W) -> io::Result<()>
where
W: io::Write,
{
match kind {
VectorType::Generic => writer.write_all(b"#("),
VectorType::Byte => writer.write_all(b"#u8("),
}
}
#[inline]
fn end_vector<W: ?Sized>(&mut self, writer: &mut W) -> io::Result<()>
where
W: io::Write,
{
writer.write_all(b")")
}
#[inline]
fn write_dot<W: ?Sized>(&mut self, writer: &mut W) -> io::Result<()>
where
W: io::Write,
{
writer.write_all(b".")
}
}
#[derive(Clone, Debug)]
pub struct DefaultFormatter;
impl Formatter for DefaultFormatter {}
#[derive(Clone, Debug)]
pub struct CustomizedFormatter {
options: Options,
}
impl Formatter for CustomizedFormatter {
fn write_nil<W: ?Sized>(&mut self, writer: &mut W) -> io::Result<()>
where
W: io::Write,
{
match self.options.nil_syntax {
NilSyntax::EmptyList => writer.write_all(b"()"),
NilSyntax::Symbol => writer.write_all(b"nil"),
NilSyntax::Token => writer.write_all(b"#nil"),
NilSyntax::False => self.write_bool(writer, false),
}
}
fn write_bool<W: ?Sized>(&mut self, writer: &mut W, value: bool) -> io::Result<()>
where
W: io::Write,
{
match self.options.bool_syntax {
BoolSyntax::Symbol => writer.write_all(if value { b"t" } else { b"nil" }),
BoolSyntax::Token => writer.write_all(if value { b"#t" } else { b"#f" }),
}
}
fn write_keyword<W: ?Sized>(&mut self, writer: &mut W, name: &str) -> io::Result<()>
where
W: io::Write,
{
match self.options.keyword_syntax {
KeywordSyntax::ColonPostfix => {
writer.write_all(name.as_bytes())?;
writer.write_all(b":")
}
KeywordSyntax::ColonPrefix => {
writer.write_all(b":")?;
writer.write_all(name.as_bytes())
}
KeywordSyntax::Octothorpe => {
writer.write_all(b"#:")?;
writer.write_all(name.as_bytes())
}
}
}
fn begin_vector<W: ?Sized>(&mut self, kind: VectorType, writer: &mut W) -> io::Result<()>
where
W: io::Write,
{
match self.options.vector_syntax {
VectorSyntax::Brackets => writer.write_all(b"["),
VectorSyntax::Octothorpe => match kind {
VectorType::Generic => writer.write_all(b"#("),
VectorType::Byte => match self.options.bytes_syntax {
BytesSyntax::R6RS => writer.write_all(b"#vu8("),
BytesSyntax::R7RS => writer.write_all(b"#u8("),
_ => panic!("invalid combination of VectorSyntax and BytesSyntax"),
},
},
}
}
fn end_vector<W: ?Sized>(&mut self, writer: &mut W) -> io::Result<()>
where
W: io::Write,
{
match self.options.vector_syntax {
VectorSyntax::Brackets => writer.write_all(b"]"),
VectorSyntax::Octothorpe => writer.write_all(b")"),
}
}
fn write_char<W: ?Sized>(&mut self, writer: &mut W, c: char) -> io::Result<()>
where
W: io::Write,
{
match self.options.char_syntax {
CharSyntax::R6RS => write_scheme_char(writer, c),
CharSyntax::Elisp => write_elisp_char(writer, c),
}
}
#[inline]
fn write_char_escape<W: ?Sized>(
&mut self,
writer: &mut W,
char_escape: CharEscape,
) -> io::Result<()>
where
W: io::Write,
{
match self.options.string_syntax {
StringSyntax::R6RS => write_r6rs_char_escape(writer, char_escape),
StringSyntax::Elisp => write_elisp_char_escape(writer, char_escape),
}
}
fn write_bytes<W: ?Sized>(&mut self, writer: &mut W, bytes: &[u8]) -> io::Result<()>
where
W: io::Write,
{
match self.options.bytes_syntax {
BytesSyntax::R6RS | BytesSyntax::R7RS => {
write_scheme_vector(self, writer, VectorType::Byte, bytes, |writer, &octet| {
itoa::write(writer, octet).map(|_| ())
})
}
BytesSyntax::Elisp => {
static OCTAL_CHARS: &[u8] = b"012345678";
writer.write_all(b"\"")?;
for octet in bytes {
writer.write_all(b"\\")?;
for &triplet in &[
(octet >> 6) & 0b111u8,
(octet >> 3) & 0b111u8,
octet & 0b111u8,
] {
let index = triplet as usize;
writer.write_all(&OCTAL_CHARS[index..=index])?;
}
}
writer.write_all(b"\"")
}
}
}
}
#[derive(Debug)]
pub struct Printer<W, F = DefaultFormatter> {
writer: W,
formatter: F,
}
impl<W> Printer<W, CustomizedFormatter>
where
W: io::Write,
{
pub fn with_options(writer: W, options: Options) -> Self {
Printer {
writer,
formatter: CustomizedFormatter { options },
}
}
}
impl<W, F> Printer<W, F>
where
W: io::Write,
F: Formatter,
{
#[inline]
pub fn with_formatter(writer: W, formatter: F) -> Self {
Printer { writer, formatter }
}
#[inline]
pub fn into_inner(self) -> W {
self.writer
}
pub fn print(&mut self, value: &Value) -> io::Result<()> {
match value {
Value::Nil => self.formatter.write_nil(&mut self.writer),
Value::Null => self.formatter.write_null(&mut self.writer),
Value::Bool(b) => self.formatter.write_bool(&mut self.writer, *b),
Value::Number(n) => self.formatter.write_number(&mut self.writer, &n),
Value::Char(c) => self.formatter.write_char(&mut self.writer, *c),
Value::Symbol(name) => self.formatter.write_symbol(&mut self.writer, &name),
Value::Keyword(name) => self.formatter.write_keyword(&mut self.writer, &name),
Value::String(s) => format_escaped_str(&mut self.writer, &mut self.formatter, &s),
Value::Bytes(bytes) => self.formatter.write_bytes(&mut self.writer, bytes),
Value::Cons(elements) => {
self.formatter.begin_list(&mut self.writer)?;
for (i, pair) in elements.iter().enumerate() {
self.formatter.begin_seq_element(&mut self.writer, i == 0)?;
self.print(pair.car())?;
self.formatter.end_seq_element(&mut self.writer)?;
match pair.cdr() {
Value::Null | Value::Cons(_) => {}
_ => {
self.formatter.begin_seq_element(&mut self.writer, false)?;
self.formatter.write_dot(&mut self.writer)?;
self.formatter.end_seq_element(&mut self.writer)?;
self.formatter.begin_seq_element(&mut self.writer, false)?;
self.print(pair.cdr())?;
self.formatter.end_seq_element(&mut self.writer)?;
}
}
}
self.formatter.end_list(&mut self.writer)
}
Value::Vector(elements) => {
self.write_vector(VectorType::Generic, elements.iter(), |printer, element| {
printer.print(element)
})
}
}
}
fn write_vector<I, O>(&mut self, kind: VectorType, elements: I, mut output: O) -> io::Result<()>
where
I: IntoIterator,
O: FnMut(&mut Self, I::Item) -> io::Result<()>,
{
self.formatter.begin_vector(kind, &mut self.writer)?;
for (i, element) in elements.into_iter().enumerate() {
self.formatter.begin_seq_element(&mut self.writer, i == 0)?;
output(self, element)?;
self.formatter.end_seq_element(&mut self.writer)?;
}
self.formatter.end_vector(&mut self.writer)
}
}
fn write_scheme_vector<F: ?Sized, W: ?Sized, I, O>(
fmt: &mut F,
writer: &mut W,
kind: VectorType,
elements: I,
mut output: O,
) -> io::Result<()>
where
F: Formatter,
W: io::Write,
I: IntoIterator,
O: FnMut(&mut W, I::Item) -> io::Result<()>,
{
fmt.begin_vector(kind, writer)?;
for (i, element) in elements.into_iter().enumerate() {
fmt.begin_seq_element(writer, i == 0)?;
output(writer, element)?;
fmt.end_seq_element(writer)?;
}
fmt.end_vector(writer)
}
impl<W> Printer<W>
where
W: io::Write,
{
#[inline]
pub fn new(writer: W) -> Self {
Printer::with_formatter(writer, DefaultFormatter)
}
}
impl<W, F> io::Write for Printer<W, F>
where
W: io::Write,
{
#[inline]
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.writer.write(buf)
}
#[inline]
fn flush(&mut self) -> io::Result<()> {
self.writer.flush()
}
}
fn format_escaped_str<W: ?Sized, F: ?Sized>(
writer: &mut W,
formatter: &mut F,
value: &str,
) -> io::Result<()>
where
W: io::Write,
F: Formatter,
{
formatter.begin_string(writer)?;
format_escaped_str_contents(writer, formatter, value)?;
formatter.end_string(writer)?;
Ok(())
}
fn format_escaped_str_contents<W: ?Sized, F: ?Sized>(
writer: &mut W,
formatter: &mut F,
value: &str,
) -> io::Result<()>
where
W: io::Write,
F: Formatter,
{
let bytes = value.as_bytes();
let mut start = 0;
for (i, &byte) in bytes.iter().enumerate() {
let escape = ESCAPE[byte as usize];
if escape == 0 {
continue;
}
if start < i {
formatter.write_string_fragment(writer, &value[start..i])?;
}
let char_escape = CharEscape::from_escape_table(escape, byte);
formatter.write_char_escape(writer, char_escape)?;
start = i + 1;
}
if start != bytes.len() {
formatter.write_string_fragment(writer, &value[start..])?;
}
Ok(())
}
fn write_r6rs_char_escape<W: ?Sized>(writer: &mut W, char_escape: CharEscape) -> io::Result<()>
where
W: io::Write,
{
use self::CharEscape::*;
let s = match char_escape {
Quote => b"\\\"",
ReverseSolidus => b"\\\\",
Alert => b"\\a",
Backspace => b"\\b",
LineFeed => b"\\n",
CarriageReturn => b"\\r",
Tab => b"\\t",
AsciiControl(byte) => {
static HEX_DIGITS: [u8; 16] = *b"0123456789ABCDEF";
let bytes = &[
b'\\',
b'x',
HEX_DIGITS[(byte >> 4) as usize],
HEX_DIGITS[(byte & 0xF) as usize],
b';',
];
return writer.write_all(bytes);
}
};
writer.write_all(s)
}
fn write_elisp_char_escape<W: ?Sized>(writer: &mut W, char_escape: CharEscape) -> io::Result<()>
where
W: io::Write,
{
use self::CharEscape::*;
let s = match char_escape {
Quote => b"\\\"",
ReverseSolidus => b"\\\\",
Alert => b"\\a",
Backspace => b"\\b",
LineFeed => b"\\n",
CarriageReturn => b"\\r",
Tab => b"\\t",
AsciiControl(byte) => {
static HEX_DIGITS: [u8; 16] = *b"0123456789ABCDEF";
let bytes = &[
b'\\',
b'u',
b'0',
b'0',
HEX_DIGITS[(byte >> 4) as usize],
HEX_DIGITS[(byte & 0xF) as usize],
];
return writer.write_all(bytes);
}
};
writer.write_all(s)
}
fn write_scheme_char<W: ?Sized>(writer: &mut W, c: char) -> io::Result<()>
where
W: io::Write,
{
let n = u32::from(c);
if n >= 32 && n < 127 {
let buf = [b'#', b'\\', n as u8];
writer.write_all(&buf)
} else {
write!(writer, "#\\x{:x}", n)
}
}
fn write_elisp_char<W: ?Sized>(writer: &mut W, c: char) -> io::Result<()>
where
W: io::Write,
{
let n = u32::from(c);
if n >= 32 && n < 127 {
let c = n as u8;
if ELISP_ESCAPE_CHARS.contains(&c) {
writer.write_all(&[b'?', b'\\', c])
} else {
writer.write_all(&[b'?', c])
}
} else {
write!(writer, "?\\x{:x}", n)
}
}
const AA: u8 = b'a';
const BB: u8 = b'b';
const TT: u8 = b't';
const NN: u8 = b'n';
const RR: u8 = b'r';
const QU: u8 = b'"';
const BS: u8 = b'\\';
const UU: u8 = b'u';
const __: u8 = 0;
static ESCAPE: [u8; 256] = [
UU, UU, UU, UU, UU, UU, UU, AA, BB, TT, NN, UU, UU, RR, UU, UU,
UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU,
__, __, QU, __, __, __, __, __, __, __, __, __, __, __, __, __,
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __,
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __,
__, __, __, __, __, __, __, __, __, __, __, __, BS, __, __, __,
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __,
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, UU,
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __,
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __,
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __,
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __,
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __,
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __,
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __,
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __,
];
static ELISP_ESCAPE_CHARS: &[u8] = b"()[]\\;|'`#.,";
#[inline]
pub fn to_writer<W: io::Write>(writer: W, value: &Value) -> io::Result<()> {
let mut printer = Printer::new(writer);
printer.print(value)?;
Ok(())
}
#[inline]
pub fn to_writer_custom<W: io::Write>(
writer: W,
value: &Value,
options: Options,
) -> io::Result<()> {
let mut printer = Printer::with_options(writer, options);
printer.print(value)?;
Ok(())
}
#[inline]
pub fn to_vec(value: &Value) -> io::Result<Vec<u8>> {
let mut writer = Vec::with_capacity(128);
to_writer(&mut writer, value)?;
Ok(writer)
}
#[inline]
pub fn to_vec_custom(value: &Value, options: Options) -> io::Result<Vec<u8>> {
let mut writer = Vec::with_capacity(128);
to_writer_custom(&mut writer, value, options)?;
Ok(writer)
}
#[inline]
pub fn to_string(value: &Value) -> io::Result<String> {
let vec = to_vec(value)?;
let string = unsafe {
String::from_utf8_unchecked(vec)
};
Ok(string)
}
#[inline]
pub fn to_string_custom(value: &Value, options: Options) -> io::Result<String> {
let vec = to_vec_custom(value, options)?;
let string = unsafe {
String::from_utf8_unchecked(vec)
};
Ok(string)
}
#[cfg(test)]
mod tests;