forked from AbleOS/holey-bytes
added formatting
This commit is contained in:
parent
93deeee6b9
commit
f9e46b4641
3
hblang/command-help.txt
Normal file
3
hblang/command-help.txt
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
--fmt - format all source files
|
||||||
|
--fmt-current - format mentioned file
|
||||||
|
--fmt-stdout - dont write the formatted file but print it
|
|
@ -1580,7 +1580,9 @@ impl Codegen {
|
||||||
});
|
});
|
||||||
Some(Value::void())
|
Some(Value::void())
|
||||||
}
|
}
|
||||||
E::Call { func: fast, args } => {
|
E::Call {
|
||||||
|
func: fast, args, ..
|
||||||
|
} => {
|
||||||
let func_ty = self.ty(fast);
|
let func_ty = self.ty(fast);
|
||||||
let ty::Kind::Func(mut func_id) = func_ty.expand() else {
|
let ty::Kind::Func(mut func_id) = func_ty.expand() else {
|
||||||
self.report(fast.pos(), "can't call this, maybe in the future");
|
self.report(fast.pos(), "can't call this, maybe in the future");
|
||||||
|
|
|
@ -1,12 +1,56 @@
|
||||||
fn main() -> std::io::Result<()> {
|
fn main() -> std::io::Result<()> {
|
||||||
let root = std::env::args()
|
let args = std::env::args().collect::<Vec<_>>();
|
||||||
.nth(1)
|
let args = args.iter().map(String::as_str).collect::<Vec<_>>();
|
||||||
.unwrap_or_else(|| "main.hb".to_string());
|
let root = args.get(1).copied().unwrap_or("main.hb");
|
||||||
|
|
||||||
let parsed = hblang::parse_from_fs(1, &root)?;
|
if args.contains(&"--help") || args.contains(&"-h") {
|
||||||
let mut codegen = hblang::codegen::Codegen::default();
|
println!("Usage: hbc [OPTIONS...] <FILE>");
|
||||||
codegen.files = parsed;
|
println!(include_str!("../command-help.txt"));
|
||||||
|
return Err(std::io::ErrorKind::Other.into());
|
||||||
|
}
|
||||||
|
|
||||||
codegen.generate();
|
let parsed = hblang::parse_from_fs(1, root)?;
|
||||||
codegen.dump(&mut std::io::stdout())
|
|
||||||
|
fn format_to_stdout(ast: hblang::parser::Ast) -> std::io::Result<()> {
|
||||||
|
let source = std::fs::read_to_string(&*ast.path)?;
|
||||||
|
hblang::parser::with_fmt_source(&source, || {
|
||||||
|
for expr in ast.exprs() {
|
||||||
|
use std::io::Write;
|
||||||
|
writeln!(std::io::stdout(), "{expr}")?;
|
||||||
|
}
|
||||||
|
std::io::Result::Ok(())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format_ast(ast: hblang::parser::Ast) -> std::io::Result<()> {
|
||||||
|
let source = std::fs::read_to_string(&*ast.path)?;
|
||||||
|
let mut output = Vec::new();
|
||||||
|
hblang::parser::with_fmt_source(&source, || {
|
||||||
|
for expr in ast.exprs() {
|
||||||
|
use std::io::Write;
|
||||||
|
writeln!(output, "{expr}")?;
|
||||||
|
}
|
||||||
|
std::io::Result::Ok(())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
std::fs::write(&*ast.path, output)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
if args.contains(&"--fmt") {
|
||||||
|
for parsed in parsed {
|
||||||
|
format_ast(parsed)?;
|
||||||
|
}
|
||||||
|
} else if args.contains(&"--fmt-current") {
|
||||||
|
format_to_stdout(parsed.into_iter().next().unwrap())?;
|
||||||
|
} else {
|
||||||
|
let mut codegen = hblang::codegen::Codegen::default();
|
||||||
|
codegen.files = parsed;
|
||||||
|
|
||||||
|
codegen.generate();
|
||||||
|
codegen.dump(&mut std::io::stdout())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,15 +58,16 @@ struct ScopeIdent {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Parser<'a, 'b> {
|
pub struct Parser<'a, 'b> {
|
||||||
path: &'b str,
|
path: &'b str,
|
||||||
loader: Loader<'b>,
|
loader: Loader<'b>,
|
||||||
lexer: Lexer<'b>,
|
lexer: Lexer<'b>,
|
||||||
arena: &'b Arena<'a>,
|
arena: &'b Arena<'a>,
|
||||||
token: Token,
|
token: Token,
|
||||||
symbols: &'b mut Symbols,
|
symbols: &'b mut Symbols,
|
||||||
ns_bound: usize,
|
ns_bound: usize,
|
||||||
idents: Vec<ScopeIdent>,
|
trailing_sep: bool,
|
||||||
captured: Vec<Ident>,
|
idents: Vec<ScopeIdent>,
|
||||||
|
captured: Vec<Ident>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'b> Parser<'a, 'b> {
|
impl<'a, 'b> Parser<'a, 'b> {
|
||||||
|
@ -80,6 +81,7 @@ impl<'a, 'b> Parser<'a, 'b> {
|
||||||
arena,
|
arena,
|
||||||
symbols,
|
symbols,
|
||||||
ns_bound: 0,
|
ns_bound: 0,
|
||||||
|
trailing_sep: false,
|
||||||
idents: Vec::new(),
|
idents: Vec::new(),
|
||||||
captured: Vec::new(),
|
captured: Vec::new(),
|
||||||
}
|
}
|
||||||
|
@ -384,6 +386,7 @@ impl<'a, 'b> Parser<'a, 'b> {
|
||||||
T::LParen => Expr::Call {
|
T::LParen => Expr::Call {
|
||||||
func: self.arena.alloc(expr),
|
func: self.arena.alloc(expr),
|
||||||
args: self.collect_list(T::Comma, T::RParen, Self::expr),
|
args: self.collect_list(T::Comma, T::RParen, Self::expr),
|
||||||
|
trailing_comma: std::mem::take(&mut self.trailing_sep),
|
||||||
},
|
},
|
||||||
T::Ctor => E::Ctor {
|
T::Ctor => E::Ctor {
|
||||||
pos: token.start,
|
pos: token.start,
|
||||||
|
@ -464,7 +467,7 @@ impl<'a, 'b> Parser<'a, 'b> {
|
||||||
self.collect(|s| {
|
self.collect(|s| {
|
||||||
s.advance_if(end).not().then(|| {
|
s.advance_if(end).not().then(|| {
|
||||||
let val = f(s);
|
let val = f(s);
|
||||||
s.advance_if(delim);
|
s.trailing_sep = s.advance_if(delim);
|
||||||
val
|
val
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -600,6 +603,7 @@ generate_expr! {
|
||||||
Call {
|
Call {
|
||||||
func: &'a Self,
|
func: &'a Self,
|
||||||
args: &'a [Self],
|
args: &'a [Self],
|
||||||
|
trailing_comma: bool,
|
||||||
},
|
},
|
||||||
Return {
|
Return {
|
||||||
pos: Pos,
|
pos: Pos,
|
||||||
|
@ -686,6 +690,17 @@ impl<'a> Poser for &Expr<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
thread_local! {
|
||||||
|
static FMT_SOURCE: Cell<*const str> = const { Cell::new("") };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_fmt_source<T>(source: &str, f: impl FnOnce() -> T) -> T {
|
||||||
|
FMT_SOURCE.with(|s| s.set(source));
|
||||||
|
let r = f();
|
||||||
|
FMT_SOURCE.with(|s| s.set(""));
|
||||||
|
r
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> std::fmt::Display for Expr<'a> {
|
impl<'a> std::fmt::Display for Expr<'a> {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
thread_local! {
|
thread_local! {
|
||||||
|
@ -708,6 +723,33 @@ impl<'a> std::fmt::Display for Expr<'a> {
|
||||||
write!(f, "{end}")
|
write!(f, "{end}")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn fmt_trailing_list<T>(
|
||||||
|
f: &mut std::fmt::Formatter,
|
||||||
|
end: &str,
|
||||||
|
list: &[T],
|
||||||
|
fmt: impl Fn(&T, &mut std::fmt::Formatter) -> std::fmt::Result,
|
||||||
|
) -> std::fmt::Result {
|
||||||
|
writeln!(f)?;
|
||||||
|
INDENT.with(|i| i.set(i.get() + 1));
|
||||||
|
let res = (|| {
|
||||||
|
for stmt in list {
|
||||||
|
for _ in 0..INDENT.with(|i| i.get()) {
|
||||||
|
write!(f, "\t")?;
|
||||||
|
}
|
||||||
|
fmt(stmt, f)?;
|
||||||
|
writeln!(f, ",")?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
})();
|
||||||
|
INDENT.with(|i| i.set(i.get() - 1));
|
||||||
|
|
||||||
|
for _ in 0..INDENT.with(|i| i.get()) {
|
||||||
|
write!(f, "\t")?;
|
||||||
|
}
|
||||||
|
write!(f, "{end}")?;
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
macro_rules! impl_parenter {
|
macro_rules! impl_parenter {
|
||||||
($($name:ident => $pat:pat,)*) => {
|
($($name:ident => $pat:pat,)*) => {
|
||||||
$(
|
$(
|
||||||
|
@ -732,8 +774,21 @@ impl<'a> std::fmt::Display for Expr<'a> {
|
||||||
Consecutive => Expr::UnOp { .. },
|
Consecutive => Expr::UnOp { .. },
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let source = unsafe { &*FMT_SOURCE.with(|s| s.get()) };
|
||||||
|
let pos = self.pos();
|
||||||
|
|
||||||
|
if let Some(before) = source.get(..pos as usize) {
|
||||||
|
let trailing_whitespace = &before[before.trim_end().len()..];
|
||||||
|
let ncount = trailing_whitespace.chars().filter(|&c| c == '\n').count();
|
||||||
|
if ncount > 1 {
|
||||||
|
writeln!(f)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
match *self {
|
match *self {
|
||||||
Self::Comment { literal, .. } => write!(f, "{literal}"),
|
Self::Comment { literal, .. } => write!(f, "{}", literal.trim_end()),
|
||||||
Self::Mod { path, .. } => write!(f, "@mod(\"{path}\")"),
|
Self::Mod { path, .. } => write!(f, "@mod(\"{path}\")"),
|
||||||
Self::Field { target, field } => write!(f, "{}.{field}", Postfix(target)),
|
Self::Field { target, field } => write!(f, "{}.{field}", Postfix(target)),
|
||||||
Self::Directive { name, args, .. } => {
|
Self::Directive { name, args, .. } => {
|
||||||
|
@ -787,28 +842,24 @@ impl<'a> std::fmt::Display for Expr<'a> {
|
||||||
fmt_list(f, "", args, |arg, f| write!(f, "{}: {}", arg.name, arg.ty))?;
|
fmt_list(f, "", args, |arg, f| write!(f, "{}: {}", arg.name, arg.ty))?;
|
||||||
write!(f, "): {ret} {body}")
|
write!(f, "): {ret} {body}")
|
||||||
}
|
}
|
||||||
Self::Call { func, args } => {
|
Self::Call {
|
||||||
|
func,
|
||||||
|
args,
|
||||||
|
trailing_comma,
|
||||||
|
} => {
|
||||||
write!(f, "{}(", Postfix(func))?;
|
write!(f, "{}(", Postfix(func))?;
|
||||||
fmt_list(f, ")", args, std::fmt::Display::fmt)
|
if trailing_comma {
|
||||||
|
fmt_trailing_list(f, ")", args, std::fmt::Display::fmt)
|
||||||
|
} else {
|
||||||
|
fmt_list(f, ")", args, std::fmt::Display::fmt)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Self::Return { val: Some(val), .. } => write!(f, "return {val};"),
|
Self::Return { val: Some(val), .. } => write!(f, "return {val};"),
|
||||||
Self::Return { val: None, .. } => write!(f, "return;"),
|
Self::Return { val: None, .. } => write!(f, "return;"),
|
||||||
Self::Ident { name, .. } => write!(f, "{name}"),
|
Self::Ident { name, .. } => write!(f, "{name}"),
|
||||||
Self::Block { stmts, .. } => {
|
Self::Block { stmts, .. } => {
|
||||||
writeln!(f, "{{")?;
|
write!(f, "{{")?;
|
||||||
INDENT.with(|i| i.set(i.get() + 1));
|
fmt_trailing_list(f, "}", stmts, std::fmt::Display::fmt)
|
||||||
let res = (|| {
|
|
||||||
for stmt in stmts {
|
|
||||||
for _ in 0..INDENT.with(|i| i.get()) {
|
|
||||||
write!(f, " ")?;
|
|
||||||
}
|
|
||||||
writeln!(f, "{stmt}")?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
})();
|
|
||||||
INDENT.with(|i| i.set(i.get() - 1));
|
|
||||||
write!(f, "}}")?;
|
|
||||||
res
|
|
||||||
}
|
}
|
||||||
Self::Number { value, .. } => write!(f, "{value}"),
|
Self::Number { value, .. } => write!(f, "{value}"),
|
||||||
Self::Bool { value, .. } => write!(f, "{value}"),
|
Self::Bool { value, .. } => write!(f, "{value}"),
|
||||||
|
@ -1109,3 +1160,51 @@ impl Drop for ArenaChunk {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
fn format(ident: &str, input: &str) {
|
||||||
|
let ast = super::Ast::new(ident, input, &super::no_loader);
|
||||||
|
let mut output = String::new();
|
||||||
|
super::with_fmt_source(input, || {
|
||||||
|
for expr in ast.exprs() {
|
||||||
|
use std::fmt::Write;
|
||||||
|
writeln!(output, "{expr}").unwrap();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let input_path = format!("formatter_{ident}.expected");
|
||||||
|
let output_path = format!("formatter_{ident}.actual");
|
||||||
|
std::fs::write(&input_path, input).unwrap();
|
||||||
|
std::fs::write(&output_path, output).unwrap();
|
||||||
|
|
||||||
|
let success = std::process::Command::new("diff")
|
||||||
|
.arg("-u")
|
||||||
|
.arg("--color")
|
||||||
|
.arg(&input_path)
|
||||||
|
.arg(&output_path)
|
||||||
|
.status()
|
||||||
|
.unwrap()
|
||||||
|
.success();
|
||||||
|
std::fs::remove_file(&input_path).unwrap();
|
||||||
|
std::fs::remove_file(&output_path).unwrap();
|
||||||
|
assert!(success, "test failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! test {
|
||||||
|
($($name:ident => $input:expr;)*) => {$(
|
||||||
|
#[test]
|
||||||
|
fn $name() {
|
||||||
|
format(stringify!($name), $input);
|
||||||
|
}
|
||||||
|
)*};
|
||||||
|
}
|
||||||
|
|
||||||
|
test! {
|
||||||
|
comments => "// comment\n// comment\n\n// comment\n\n\
|
||||||
|
/* comment */\n/* comment */\n\n/* comment */\n";
|
||||||
|
some_ordinary_code => "loft := fn(): int return loft(1, 2, 3);\n";
|
||||||
|
some_arg_per_line_code => "loft := fn(): int return loft(\
|
||||||
|
\n\t1,\n\t2,\n\t3,\n);\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue