use std::fmt::Write; use std::str; pub const DEFAULT_WIDTH: usize = 80; pub trait Emittable: std::fmt::Debug { fn write_on(&self, f: &mut Formatter); } pub type Item = std::rc::Rc; #[derive(Clone)] pub struct Sequence { pub items: Vec, pub separator: &'static str, pub terminator: &'static str, } #[derive(Clone)] pub struct Grouping { pub sequence: Sequence, pub open: &'static str, pub close: &'static str, } pub struct Formatter { pub width: usize, indent_delta: String, current_indent: String, pub buffer: String, } impl Formatter { pub fn new() -> Self { Formatter { width: DEFAULT_WIDTH, indent_delta: " ".to_owned(), current_indent: "\n".to_owned(), buffer: String::new(), } } pub fn copy_empty(&self) -> Formatter { Formatter { width: self.width, indent_delta: self.indent_delta.clone(), current_indent: self.current_indent.clone(), buffer: String::new(), } } pub fn indent_size(self) -> usize { self.indent_delta.len() } pub fn set_indent_size(&mut self, n: usize) { self.indent_delta = str::repeat(" ", n) } pub fn write(&mut self, e: E) { e.write_on(self) } pub fn newline(&mut self) { self.buffer.push_str(&self.current_indent) } pub fn to_string(e: E) -> String { let mut f = Formatter::new(); f.write(e); f.buffer } pub fn with_indent R>(&mut self, f: F) -> R { let old_indent = self.current_indent.clone(); self.current_indent += &self.indent_delta; let r = f(self); self.current_indent = old_indent; r } } //--------------------------------------------------------------------------- impl Emittable for &str { fn write_on(&self, f: &mut Formatter) { f.buffer.push_str(self) } } impl Emittable for String { fn write_on(&self, f: &mut Formatter) { f.write(self.as_str()) } } impl<'a, E: Emittable> Emittable for &'a Vec where &'a E: Emittable { fn write_on(&self, f: &mut Formatter) { for e in self.iter() { f.write(e) } } } impl Emittable for Sequence { fn write_on(&self, f: &mut Formatter) { let mut need_sep = false; for e in self.items.iter() { if need_sep { self.separator.write_on(f) } else { need_sep = true } e.write_on(f) } self.terminator.write_on(f) } } impl Emittable for Grouping { fn write_on(&self, f: &mut Formatter) { let mut g = f.copy_empty(); self.open.write_on(&mut g); g.write(&self.sequence); self.close.write_on(&mut g); let s = g.buffer; if s.len() <= f.width { f.write(&s) } else { self.open.write_on(f); if !self.sequence.items.is_empty() { f.with_indent(|f| { let mut i = self.sequence.items.len(); for e in self.sequence.items.iter() { f.newline(); e.write_on(f); let delim = if i == 1 { self.sequence.terminator } else { self.sequence.separator }; delim.trim_end().write_on(f); i = i - 1; } }); f.newline() } self.close.write_on(f); } } } impl<'a, E: Emittable> Emittable for &'a E { fn write_on(&self, f: &mut Formatter) { (*self).write_on(f) } } impl Emittable for Item { fn write_on(&self, f: &mut Formatter) { (**self).write_on(f) } } impl std::fmt::Debug for Sequence { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { f.write_str(&Formatter::to_string(self)) } } impl std::fmt::Debug for Grouping { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { f.write_str(&Formatter::to_string(self)) } } //--------------------------------------------------------------------------- pub fn escape_string(s: &str) -> String { let mut buf = String::new(); buf.push('"'); for c in s.chars() { match c { '\\' => buf.push_str("\\\\"), '"' => buf.push_str("\\\""), _ if c >= ' ' && c <= '~' => buf.push(c), _ => write!(&mut buf, "\\u{{{:x}}}", c as i32).expect("no IO errors building a string"), } } buf.push('"'); buf } pub fn escape_bytes(bs: &[u8]) -> String { let mut buf = String::new(); buf.push_str("b\""); for b in bs { let c = *b as char; match c { '\\' => buf.push_str("\\\\"), '"' => buf.push_str("\\\""), _ if c >= ' ' && c <= '~' => buf.push(c), _ => write!(&mut buf, "\\x{{{:02x}}}", b).expect("no IO errors building a string"), } } buf.push('"'); buf } //--------------------------------------------------------------------------- pub mod constructors { use super::Sequence; use super::Grouping; use super::Item; use super::Emittable; pub fn item(i: E) -> Item { std::rc::Rc::new(i) } pub fn name(pieces: Vec) -> Sequence { Sequence { items: pieces, separator: "::", terminator: "" } } pub fn seq(items: Vec) -> Sequence { Sequence { items: items, separator: "", terminator: "" } } pub fn commas(items: Vec) -> Sequence { Sequence { items: items, separator: ", ", terminator: "" } } pub fn parens(items: Vec) -> Grouping { Grouping { sequence: commas(items), open: "(", close: ")" } } pub fn brackets(items: Vec) -> Grouping { Grouping { sequence: commas(items), open: "[", close: "]" } } pub fn anglebrackets(items: Vec) -> Grouping { Grouping { sequence: commas(items), open: "<", close: ">" } } pub fn braces(items: Vec) -> Grouping { Grouping { sequence: commas(items), open: "{", close: "}" } } pub fn block(items: Vec) -> Grouping { Grouping { sequence: Sequence { items: items, separator: " ", terminator: "" }, open: "{", close: "}", } } pub fn semiblock(items: Vec) -> Grouping { Grouping { sequence: Sequence { items: items, separator: "; ", terminator: "" }, open: "{", close: "}", } } } pub mod macros { #[macro_export] macro_rules! name { ($($item:expr),*) => {crate::syntax::block::constructors::name(vec![$(std::rc::Rc::new($item)),*])} } #[macro_export] macro_rules! seq { ($($item:expr),*) => {crate::syntax::block::constructors::seq(vec![$(std::rc::Rc::new($item)),*])} } #[macro_export] macro_rules! commas { ($($item:expr),*) => {crate::syntax::block::constructors::commas(vec![$(std::rc::Rc::new($item)),*])} } #[macro_export] macro_rules! parens { ($($item:expr),*) => {crate::syntax::block::constructors::parens(vec![$(std::rc::Rc::new($item)),*])} } #[macro_export] macro_rules! brackets { ($($item:expr),*) => {crate::syntax::block::constructors::brackets(vec![$(std::rc::Rc::new($item)),*])} } #[macro_export] macro_rules! anglebrackets { ($($item:expr),*) => {crate::syntax::block::constructors::anglebrackets(vec![$(std::rc::Rc::new($item)),*])} } #[macro_export] macro_rules! braces { ($($item:expr),*) => {crate::syntax::block::constructors::braces(vec![$(std::rc::Rc::new($item)),*])} } #[macro_export] macro_rules! block { ($($item:expr),*) => {crate::syntax::block::constructors::block(vec![$(std::rc::Rc::new($item)),*])} } #[macro_export] macro_rules! semiblock { ($($item:expr),*) => {crate::syntax::block::constructors::semiblock(vec![$(std::rc::Rc::new($item)),*])} } }