#![allow(clippy::single_char_add_str)] use std::fmt::Write; use super::{ Block, Declaration, Expression, ExpressionKind, FlowControl, ImportSpecifier, InvocationArgument, Signature, Statement, StatementKind, AST, }; const LEVEL: usize = 2; fn do_indent(n: usize, buf: &mut String) { for _ in 0..n { buf.push(' '); } } fn newline(buf: &mut String) { buf.push('\n'); } pub(super) fn render_ast(ast: &AST) -> String { let AST { statements, .. } = ast; let mut buf = "(AST\n".to_string(); render_block(statements, LEVEL, &mut buf); buf.push(')'); buf } fn render_statement(stmt: &Statement, indent: usize, buf: &mut String) { use StatementKind::*; do_indent(indent, buf); match stmt.kind { Expression(ref expr) => render_expression(expr, indent, buf), Declaration(ref decl) => render_declaration(decl, indent, buf), Import(ref spec) => render_import(spec, indent, buf), Flow(ref flow_control) => render_flow_control(flow_control, indent, buf), } } fn render_expression(expr: &Expression, indent: usize, buf: &mut String) { use ExpressionKind::*; buf.push_str("(Expr "); match &expr.kind { NatLiteral(n) => buf.push_str(&format!("(NatLiteral {})", n)), FloatLiteral(f) => buf.push_str(&format!("(FloatLiteral {})", f)), StringLiteral { s, prefix } => buf.push_str(&format!("(StringLiteral prefix: {:?} {})", prefix, s)), BoolLiteral(b) => buf.push_str(&format!("(BoolLiteral {})", b)), BinExp(binop, lhs, rhs) => { let new_indent = indent + LEVEL; buf.push_str(&format!("Binop {}\n", binop.sigil())); do_indent(new_indent, buf); render_expression(lhs, new_indent, buf); newline(buf); do_indent(new_indent, buf); render_expression(rhs, new_indent, buf); newline(buf); do_indent(indent, buf); } PrefixExp(prefix, expr) => { let new_indent = indent + LEVEL; buf.push_str(&format!("PrefixOp {}\n", prefix.sigil())); do_indent(new_indent, buf); render_expression(expr, new_indent, buf); newline(buf); do_indent(indent, buf); } TupleLiteral(..) => (), Value(name) => { buf.push_str(&format!("Value {})", name)); } NamedStruct { name: _, fields: _ } => (), Call { f, arguments } => { let new_indent = indent + LEVEL; buf.push_str("Call "); render_expression(f, new_indent, buf); newline(buf); for arg in arguments { do_indent(new_indent, buf); match arg { InvocationArgument::Positional(expr) => render_expression(expr, new_indent, buf), InvocationArgument::Keyword { .. } => buf.push_str(""), InvocationArgument::Ignored => buf.push_str(""), } newline(buf); do_indent(indent, buf); } } Index { .. } => buf.push_str(""), IfExpression { .. } => buf.push_str(""), WhileExpression { .. } => buf.push_str(""), ForExpression { .. } => buf.push_str(""), Lambda { params, type_anno: _, body } => { let new_indent = indent + LEVEL; buf.push_str("Lambda "); newline(buf); do_indent(new_indent, buf); buf.push_str("(Args "); for p in params { buf.push_str(&format!("{} ", p.name)); } buf.push(')'); newline(buf); do_indent(new_indent, buf); buf.push_str("(Body "); newline(buf); render_block(body, new_indent + LEVEL, buf); do_indent(new_indent, buf); buf.push(')'); newline(buf); do_indent(indent, buf); } Access { .. } => buf.push_str(""), ListLiteral(..) => buf.push_str(""), } buf.push(')'); } fn render_declaration(decl: &Declaration, indent: usize, buf: &mut String) { use Declaration::*; buf.push_str("(Decl "); match decl { FuncSig(ref sig) => render_signature(sig, indent, buf), FuncDecl(ref sig, ref block) => { let indent = indent + LEVEL; buf.push_str("Function"); newline(buf); do_indent(indent, buf); render_signature(sig, indent, buf); newline(buf); do_indent(indent, buf); buf.push_str("(Body"); newline(buf); render_block(block, indent + LEVEL, buf); do_indent(indent, buf); buf.push_str(")"); newline(buf); } TypeDecl { name: _, body: _, .. } => { buf.push_str(""); } TypeAlias { alias: _, original: _ } => { buf.push_str(""); } Binding { name, constant: _, type_anno: _, expr } => { let new_indent = indent + LEVEL; buf.push_str(&format!("Binding {}", name)); newline(buf); do_indent(new_indent, buf); render_expression(expr, new_indent, buf); newline(buf); } Module { name, items: _ } => { write!(buf, "(Module {} )", name).unwrap(); } _ => (), /* Impl { type_name: TypeIdentifier, interface_name: Option, block: Vec }, Interface { name: Rc, signatures: Vec }, Annotation { name: Rc, arguments: Vec }, */ } do_indent(indent, buf); buf.push(')'); } fn render_block(block: &Block, indent: usize, buf: &mut String) { for stmt in block.statements.iter() { render_statement(stmt, indent, buf); newline(buf); } } fn render_signature(sig: &Signature, _indent: usize, buf: &mut String) { buf.push_str(&format!("(Signature {} )", sig.name)); } fn render_import(_import: &ImportSpecifier, _indent: usize, buf: &mut String) { buf.push_str("(Import )"); } fn render_flow_control(flow: &FlowControl, _indent: usize, buf: &mut String) { use FlowControl::*; match flow { Return(ref _expr) => write!(buf, "return ").unwrap(), Break => write!(buf, "break").unwrap(), Continue => write!(buf, "continue").unwrap(), } } #[cfg(test)] mod test { use super::render_ast; use crate::util::quick_ast; #[test] fn test_visualization() { let ast = quick_ast( r#" fn test(x) { let m = 9 1 * 4 <> m |> somemod::output(x) } let quincy = \(no, yes, maybe) { let a = 10 yes * no + a } let b = 54 test(b) == 3 "#, ); let expected_output = r#"(AST (Decl Function (Signature test ) (Body (Decl Binding m (Expr (NatLiteral 9)) ) (Expr Binop * (Expr (NatLiteral 1)) (Expr Binop |> (Expr Binop <> (Expr (NatLiteral 4)) (Expr Value m)) ) (Expr Call (Expr Value somemod::output)) (Expr Value x)) ) ) ) ) ) (Decl Binding quincy (Expr Lambda (Args no yes maybe ) (Body (Decl Binding a (Expr (NatLiteral 10)) ) (Expr Binop + (Expr Binop * (Expr Value yes)) (Expr Value no)) ) (Expr Value a)) ) ) ) ) (Decl Binding b (Expr (NatLiteral 54)) ) (Expr Binop == (Expr Call (Expr Value test)) (Expr Value b)) ) (Expr (NatLiteral 3)) ) )"#; let rendered = render_ast(&ast); assert_eq!(rendered, expected_output); } }