282 lines
7.9 KiB
Rust
282 lines
7.9 KiB
Rust
#![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("<keyword>"),
|
|
InvocationArgument::Ignored => buf.push_str("<ignored>"),
|
|
}
|
|
newline(buf);
|
|
do_indent(indent, buf);
|
|
}
|
|
}
|
|
Index { .. } => buf.push_str("<index>"),
|
|
IfExpression { .. } => buf.push_str("<if-expr>"),
|
|
WhileExpression { .. } => buf.push_str("<while-expr>"),
|
|
ForExpression { .. } => buf.push_str("<for-expr>"),
|
|
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("<access-expr>"),
|
|
ListLiteral(..) => buf.push_str("<list-literal>"),
|
|
}
|
|
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("<type-decl>");
|
|
}
|
|
TypeAlias { alias: _, original: _ } => {
|
|
buf.push_str("<type-alias>");
|
|
}
|
|
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 {} <body>)", name).unwrap();
|
|
}
|
|
_ => (), /*
|
|
Impl { type_name: TypeIdentifier, interface_name: Option<TypeSingletonName>, block: Vec<Declaration> },
|
|
Interface { name: Rc<String>, signatures: Vec<Signature> },
|
|
Annotation { name: Rc<String>, arguments: Vec<Expression> },
|
|
*/
|
|
}
|
|
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 <some import>)");
|
|
}
|
|
|
|
fn render_flow_control(flow: &FlowControl, _indent: usize, buf: &mut String) {
|
|
use FlowControl::*;
|
|
match flow {
|
|
Return(ref _expr) => write!(buf, "return <expr>").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);
|
|
}
|
|
}
|