preserves/implementations/rust/preserves-schema/src/syntax/block.rs

309 lines
8.1 KiB
Rust

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<dyn Emittable>;
#[derive(Clone)]
pub struct Sequence {
pub items: Vec<Item>,
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<E: Emittable>(&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: Emittable>(e: E) -> String {
let mut f = Formatter::new();
f.write(e);
f.buffer
}
pub fn with_indent<R, F: FnOnce(&mut Self) -> 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<E> 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<E: 'static + Emittable>(i: E) -> Item {
std::rc::Rc::new(i)
}
pub fn name(pieces: Vec<Item>) -> Sequence {
Sequence { items: pieces, separator: "::", terminator: "" }
}
pub fn seq(items: Vec<Item>) -> Sequence {
Sequence { items: items, separator: "", terminator: "" }
}
pub fn commas(items: Vec<Item>) -> Sequence {
Sequence { items: items, separator: ", ", terminator: "" }
}
pub fn parens(items: Vec<Item>) -> Grouping {
Grouping { sequence: commas(items), open: "(", close: ")" }
}
pub fn brackets(items: Vec<Item>) -> Grouping {
Grouping { sequence: commas(items), open: "[", close: "]" }
}
pub fn anglebrackets(items: Vec<Item>) -> Grouping {
Grouping { sequence: commas(items), open: "<", close: ">" }
}
pub fn braces(items: Vec<Item>) -> Grouping {
Grouping { sequence: commas(items), open: "{", close: "}" }
}
pub fn block(items: Vec<Item>) -> Grouping {
Grouping {
sequence: Sequence { items: items, separator: " ", terminator: "" },
open: "{",
close: "}",
}
}
pub fn semiblock(items: Vec<Item>) -> 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)),*])}
}
}