use microcad_lang_base::Diagnostics;
use microcad_lang_parse::{Parse, ParseContext, ast};
mod expression;
mod extras;
mod literal;
mod node;
mod statement;
mod ty;
pub(crate) use crate::node::{BreakMode, Node};
#[derive(Debug, Clone)]
pub struct FormatConfig {
pub max_width: usize,
pub indent_width: usize,
}
impl Default for FormatConfig {
fn default() -> Self {
Self {
max_width: 60,
indent_width: 4,
}
}
}
pub(crate) trait Format {
fn format(&self, f: &FormatConfig) -> Node;
}
impl<T> Format for T
where
T: Into<Node> + Clone,
{
fn format(&self, _f: &FormatConfig) -> Node {
self.clone().into()
}
}
impl<T: Format> Format for Option<T> {
fn format(&self, f: &FormatConfig) -> Node {
match self {
Some(inner) => inner.format(f),
None => Node::Nil, }
}
}
impl Format for ast::Identifier {
fn format(&self, _: &FormatConfig) -> Node {
self.name.clone().into()
}
}
impl Format for ast::Comment {
fn format(&self, _: &FormatConfig) -> Node {
match &self.inner {
ast::CommentInner::SingleLine(line) => {
node!(Node::Softline Node::SingleLineComment(line.into()))
}
ast::CommentInner::MultiLine(line) => {
node!(Node::Softline "/* " Node::from(line.clone()) " */" Node::Softline)
}
}
}
}
impl Format for ast::DocBlock {
fn format(&self, _: &FormatConfig) -> Node {
Node::vlist(
self.lines.iter().cloned().map(|line| node!(line)),
Node::Nil,
0,
)
}
}
impl Format for ast::Program {
fn format(&self, f: &FormatConfig) -> Node {
self.statements.format(f)
}
}
#[macro_export]
macro_rules! node {
($node:expr) => {
$crate::Node::from($node)
};
($($node:expr)*) => {
$crate::Node::from(vec![
$( $crate::Node::from($node) ),*
])
};
($f:ident, $extras:expr => $($node:expr)*) => {
$crate::extras::with_extras(
&$extras,
$f,
$crate::Node::from(vec![
$( $node.format($f) ),*
])
)
};
($f:ident => $($node:expr)*) => {
$crate::Node::from(vec![
$( $node.format($f) ),*
])
};
}
pub fn format(program: &ast::Program, config: &FormatConfig) -> String {
program.format(config).to_string()
}
pub fn format_str(source: &str, config: &FormatConfig) -> Result<String, Diagnostics> {
let parse_context = ParseContext::new(source);
let source =
ast::Source::parse(&parse_context).map_err(|err| err.to_diagnostics(&parse_context))?;
Ok(format(&source.ast, config))
}
pub fn format_source(
source: &ast::Source,
config: &FormatConfig,
) -> Result<ast::Source, Diagnostics> {
let formatted = microcad_lang_base::Source::new(
source.url.clone(),
source.line_offset,
format(&source.ast, config),
);
let parse_context = ParseContext::from(&formatted);
ast::Source::parse(&parse_context).map_err(|err| err.to_diagnostics(&parse_context))
}