Basic visualizer for AST
Pretty-prints a representation of the AST
This commit is contained in:
parent
5d04a020dc
commit
b2d9622feb
@ -3,11 +3,13 @@
|
||||
|
||||
use std::{
|
||||
convert::{AsRef, From},
|
||||
fmt,
|
||||
rc::Rc,
|
||||
};
|
||||
|
||||
mod operators;
|
||||
mod visitor;
|
||||
mod visualize;
|
||||
|
||||
pub use operators::{BinOp, PrefixOp};
|
||||
pub use visitor::*;
|
||||
@ -20,14 +22,6 @@ use crate::{
|
||||
|
||||
define_id_kind!(ASTItem);
|
||||
|
||||
/*
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Default)]
|
||||
pub struct ASTItem;
|
||||
impl IdKind for ASTItem {
|
||||
fn tag() -> &'static str { "ASTItem" }
|
||||
}
|
||||
*/
|
||||
|
||||
pub type ItemId = Id<ASTItem>;
|
||||
|
||||
#[derive(Derivative, Debug)]
|
||||
@ -38,6 +32,12 @@ pub struct AST {
|
||||
pub statements: Block,
|
||||
}
|
||||
|
||||
impl fmt::Display for AST {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", visualize::render_ast(self))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Derivative, Debug, Clone)]
|
||||
#[derivative(PartialEq)]
|
||||
pub struct Statement {
|
||||
@ -89,6 +89,22 @@ pub struct QualifiedName {
|
||||
pub components: Vec<Rc<String>>,
|
||||
}
|
||||
|
||||
impl fmt::Display for QualifiedName {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match &self.components[..] {
|
||||
[] => write!(f, "[<empty>]"),
|
||||
[name] => write!(f, "{}", name),
|
||||
[name, rest @ ..] => {
|
||||
write!(f, "{}", name)?;
|
||||
for c in rest {
|
||||
write!(f, "::{}", c)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct FormalParam {
|
||||
pub name: ParamName,
|
||||
|
268
schala-lang/language/src/ast/visualize.rs
Normal file
268
schala-lang/language/src/ast/visualize.rs
Normal file
@ -0,0 +1,268 @@
|
||||
use super::{
|
||||
Block, Declaration, Expression, ExpressionKind, ImportSpecifier, InvocationArgument, ModuleSpecifier,
|
||||
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 = format!("(AST\n");
|
||||
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),
|
||||
Module(ref spec) => render_module(spec, 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) => buf.push_str(&format!("(StringLiteral {})", 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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
_ => (), /*
|
||||
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_str(")");
|
||||
}
|
||||
|
||||
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_module(_expr: &ModuleSpecifier, _indent: usize, buf: &mut String) {
|
||||
buf.push_str("(Module <some mod>)");
|
||||
}
|
||||
|
||||
#[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);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user