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::{
|
use std::{
|
||||||
convert::{AsRef, From},
|
convert::{AsRef, From},
|
||||||
|
fmt,
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
};
|
};
|
||||||
|
|
||||||
mod operators;
|
mod operators;
|
||||||
mod visitor;
|
mod visitor;
|
||||||
|
mod visualize;
|
||||||
|
|
||||||
pub use operators::{BinOp, PrefixOp};
|
pub use operators::{BinOp, PrefixOp};
|
||||||
pub use visitor::*;
|
pub use visitor::*;
|
||||||
@ -20,14 +22,6 @@ use crate::{
|
|||||||
|
|
||||||
define_id_kind!(ASTItem);
|
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>;
|
pub type ItemId = Id<ASTItem>;
|
||||||
|
|
||||||
#[derive(Derivative, Debug)]
|
#[derive(Derivative, Debug)]
|
||||||
@ -38,6 +32,12 @@ pub struct AST {
|
|||||||
pub statements: Block,
|
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)]
|
#[derive(Derivative, Debug, Clone)]
|
||||||
#[derivative(PartialEq)]
|
#[derivative(PartialEq)]
|
||||||
pub struct Statement {
|
pub struct Statement {
|
||||||
@ -89,6 +89,22 @@ pub struct QualifiedName {
|
|||||||
pub components: Vec<Rc<String>>,
|
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)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub struct FormalParam {
|
pub struct FormalParam {
|
||||||
pub name: ParamName,
|
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