mod ast;
mod ir;
use std::fmt::Debug;
pub trait Writer {
fn write_plain(&mut self, text: &str);
fn write_colored(&mut self, text: &str, color: &str);
fn newline(&mut self);
fn with_color<F, R>(&mut self, f: F) -> R
where
F: FnOnce(&mut Self) -> R;
fn current_color(&self) -> &'static str;
fn with_depth<F, R>(&mut self, f: F) -> R
where
F: FnOnce(&mut Self) -> R;
fn source(&self) -> &str;
}
pub const NUMBER_COLOR: &str = "\x1b[0;92;1m";
pub const STRING_QUOTE_COLOR: &str = "\x1b[0;97;1m";
pub const STRING_CONTENT_COLOR: &str = "\x1b[0;94;1m";
pub trait Dump: Debug {
fn dump<W: Writer>(&self, w: &mut W);
fn is_simple(&self) -> bool {
false
}
fn has_delimiters(&self) -> bool {
false
}
fn renders_inline_parens(&self) -> bool {
false
}
fn is_empty(&self) -> bool {
false
}
fn is_atomic(&self) -> bool {
false
}
}
pub fn escape_string(s: &str) -> std::borrow::Cow<'_, str> {
fn is_non_printable(ch: char) -> bool {
let code = ch as u32;
if ch.is_control() && ch != '\n' {
return true;
}
if matches!(code, 0x2028 | 0x2029) {
return true;
}
matches!(code,
0x00AD | 0x0600..=0x0605 | 0x061C | 0x06DD | 0x070F | 0x08E2 | 0x180E | 0x200B..=0x200F | 0x202A..=0x202E | 0x2060..=0x2064 | 0x2066..=0x206F | 0xFEFF | 0xFFF9..=0xFFFB | 0x110BD | 0x110CD | 0x13430..=0x13438 | 0x1BCA0..=0x1BCA3 | 0x1D173..=0x1D17A | 0xE0001 | 0xE0020..=0xE007F )
}
if !s.chars().any(is_non_printable) {
return std::borrow::Cow::Borrowed(s);
}
let mut result = String::with_capacity(s.len() + 10);
for ch in s.chars() {
if is_non_printable(ch) {
use std::fmt::Write as _;
let _ = write!(result, "\\x{:x}", ch as u32);
} else {
result.push(ch);
}
}
std::borrow::Cow::Owned(result)
}
pub fn sub_expr<T: Dump + ?Sized, W: Writer>(w: &mut W, arg: &T) {
if arg.is_simple() {
w.write_plain(" ");
arg.dump(w);
} else if arg.has_delimiters() {
w.newline();
arg.dump(w);
} else {
let inline = arg.renders_inline_parens();
w.newline();
with_brackets(w, "(", ")", true, |w, _| {
w.write_plain(" ");
arg.dump(w);
if inline {
w.write_plain(" ");
} else {
w.newline();
}
});
}
}
pub fn with_brackets<W: Writer>(
w: &mut W,
open: &str,
close: &str,
bump_depth: bool,
body: impl FnOnce(&mut W, &'static str),
) {
w.with_color(|w| {
let delim_color = w.current_color();
let inner = |w: &mut W| {
w.write_colored(open, delim_color);
body(w, delim_color);
w.write_colored(close, delim_color);
};
if bump_depth {
w.with_depth(inner);
} else {
inner(w);
}
});
}
pub fn dump_delimited<T: Dump + ?Sized, W: Writer>(w: &mut W, value: &T) {
if value.has_delimiters() && !value.is_empty() && !value.is_simple() {
w.newline();
} else {
w.write_plain(" ");
}
value.dump(w);
}
pub fn dump_list<T: Dump, W: Writer>(w: &mut W, items: &[T], bump_depth: bool) {
if items.is_empty() {
with_brackets(w, "[", "]", false, |_, _| {});
return;
}
with_brackets(w, "[", "]", bump_depth, |w, bracket_color| {
if let [only] = items
&& only.is_simple()
{
w.write_plain(" ");
only.dump(w);
w.write_plain(" ");
} else {
for (i, item) in items.iter().enumerate() {
if i > 0 {
w.newline();
w.write_colored(",", bracket_color);
}
dump_delimited(w, item);
}
w.newline();
}
});
}
#[macro_export]
macro_rules! format_constructor {
($w:expr, $name:expr, []) => {
$w.write_plain($name);
};
($w:expr, $name:expr, [ $($arg:expr),+ $(,)? ]) => {{
$w.write_plain($name);
$(
$crate::dump::sub_expr($w, $arg);
)*
}};
}
#[macro_export]
macro_rules! format_record {
($w:expr, [ $(($name:expr, $value:expr)),+ $(,)? ]) => {{
$w.newline();
$crate::dump::with_brackets($w, "{", "}", true, |w, brace_color| {
format_record!(@fields w, brace_color; ; $( ($name, $value) ),+);
w.newline();
});
}};
(@fields $w:expr, $brace_color:expr; ; ($name:expr, $value:expr) $(, ($rest_name:expr, $rest_value:expr))* ) => {
$w.write_plain(" ");
$w.write_plain($name);
$w.write_plain(" =");
$crate::dump::dump_delimited($w, $value);
$(
format_record!(@fields $w, $brace_color; comma; ($rest_name, $rest_value));
)*
};
(@fields $w:expr, $brace_color:expr; comma; ($name:expr, $value:expr)) => {
$w.newline();
$w.write_colored(",", $brace_color);
$w.write_plain(" ");
$w.write_plain($name);
$w.write_plain(" =");
$crate::dump::dump_delimited($w, $value);
};
}
#[macro_export]
macro_rules! dump_enum {
($self:expr, $w:expr, {
$( $variant:ident $( ( $($field:ident),* $(,)? ) )? => [ $($arg:expr),* $(,)? ] ),* $(,)?
}) => {
match $self {
$(
Self::$variant $( ( $($field),* ) )? => {
format_constructor!($w, stringify!($variant), [$($arg),*]);
}
)*
}
};
($self:expr, $w:expr, {
$( $variant:ident $( ( $($field:ident),* $(,)? ) )? => [ $($arg:expr),* $(,)? ] ),* ,
_ => $wildcard_body:block $(,)?
}) => {
match $self {
$(
Self::$variant $( ( $($field),* ) )? => {
format_constructor!($w, stringify!($variant), [$($arg),*]);
}
)*
_ => $wildcard_body
}
};
}