diff --git a/src/graphviz_converter/expression.rs b/src/graphviz_converter/expression.rs index 184c71c..1dedb7b 100644 --- a/src/graphviz_converter/expression.rs +++ b/src/graphviz_converter/expression.rs @@ -1,7 +1,11 @@ use graphviz_rust::{dot_generator::*, dot_structures::*}; use crate::{ - ast::expr::{Expr, ExprType}, + ast::{ + expr::{Expr, ExprType}, + name::Name, + token::Token, + }, graphviz_converter::random_id, }; @@ -19,8 +23,8 @@ impl GraphvizConverter for ExprType { ExprType::Assign(name, expr) => { single_child(format!("Assignment to \"{}\"", name.name).as_str(), expr) } - ExprType::Binary(_, _, _) => todo!(), - ExprType::Logical(_, _, _) => todo!(), + ExprType::Binary(left, token, right) => binary(token, left, right), + ExprType::Logical(left, token, right) => binary(token, left, right), ExprType::Grouping(expr) => single_child("Gouping", expr), ExprType::Literal(literal) => { GraphvizRepr::single(expr(format!("Literal: {}", literal).as_str())) @@ -31,11 +35,11 @@ impl GraphvizConverter for ExprType { ExprType::Variable(name_expr) => { GraphvizRepr::single(expr(format!("Variable: {}", name_expr.name).as_str())) } - ExprType::Call(_, _) => todo!(), + ExprType::Call(callee, arguments) => call(callee, arguments), ExprType::Get(expr, name) => { single_child(format!("Get expression \"{}\"", name.name).as_str(), expr) } - ExprType::Set(_, _, _) => todo!(), + ExprType::Set(object, name, value) => set(object, &name.name, value), ExprType::This => GraphvizRepr::single(expr("this")), ExprType::Super(name) => { GraphvizRepr::single(expr(format!("super.{}", name.name).as_str())) @@ -50,8 +54,50 @@ fn expr(label: &str) -> Node { fn single_child(label: &str, expression: &Expr) -> GraphvizRepr { let mut node = GraphvizRepr::single(expr(label)); - let mut expr_repr = expression.to_graphviz(); - node.stmts.append(&mut expr_repr.stmts); + let expr_repr = expression.to_graphviz(); + node.stmts.extend(expr_repr.stmts); node.push(edge!(node.id.clone() => expr_repr.id.clone())); node } + +fn binary(token: &Token, left: &Expr, right: &Expr) -> GraphvizRepr { + let mut node = GraphvizRepr::single(expr(token.token_type.to_string().as_str())); + let left = left.to_graphviz(); + node.stmts.extend(left.stmts); + node.push(edge!(node.id.clone() => left.id.clone(); attr!("label", "left"))); + + let right = right.to_graphviz(); + node.stmts.extend(right.stmts); + node.push(edge!(node.id.clone() => right.id.clone(); attr!("label", "right"))); + node +} + +fn call(callee: &Expr, arguments: &[Expr]) -> GraphvizRepr { + let mut node = GraphvizRepr::single(expr("call")); + let callee = callee.to_graphviz(); + node.stmts.extend(callee.stmts); + node.push(edge!(node.id.clone() => callee.id.clone(); attr!("label", "callee"))); + + let mut args = GraphvizRepr::single(expr("arguments")); + arguments.iter().for_each(|a| { + let a = a.to_graphviz(); + args.append(a.stmts); + args.push(edge!(args.id.clone() => a.id)) + }); + + node.stmts.extend(args.stmts); + node.push(edge!(node.id.clone() => args.id.clone(); attr!("label", "arguments"))); + node +} + +fn set(object: &Expr, name: &Name, value: &Expr) -> GraphvizRepr { + let mut node = GraphvizRepr::single(expr(format!("set {}", name).as_str())); + let object = object.to_graphviz(); + node.stmts.extend(object.stmts); + node.push(edge!(node.id.clone() => object.id.clone(); attr!("label", "object"))); + + let value = value.to_graphviz(); + node.stmts.extend(value.stmts); + node.push(edge!(node.id.clone() => value.id.clone(); attr!("label", "value"))); + node +} diff --git a/src/graphviz_converter/statement.rs b/src/graphviz_converter/statement.rs index 0d5dd56..d89951c 100644 --- a/src/graphviz_converter/statement.rs +++ b/src/graphviz_converter/statement.rs @@ -1,13 +1,9 @@ use crate::ast::{ - self, expr::Expr, + name::{Name, NameExpr}, stmt::{self, Function, StmtType}, }; -use graphviz_rust::{ - attributes::{rank, SubgraphAttributes}, - dot_generator::*, - dot_structures::*, -}; +use graphviz_rust::{dot_generator::*, dot_structures::*}; use std::fmt::Write; use super::{random_cluster_id, random_id, GraphvizConverter, GraphvizRepr}; @@ -23,21 +19,23 @@ impl GraphvizConverter for StmtType { match self { StmtType::Expression(expr) => single_expr("Expr", expr), StmtType::Print(expr) => single_expr("print", expr), - StmtType::Var { name, initializer } => todo!(), + StmtType::Var { name, initializer } => { + single_with_option_expr(format!("var {}", name).as_str(), initializer) + } StmtType::Function(f) => function(f, "fun"), - StmtType::Return(_) => todo!(), - StmtType::Block(_) => todo!(), + StmtType::Return(expr) => single_with_option_expr("return", expr), + StmtType::Block(stmts) => block(stmts, "block"), StmtType::If { condition, then_stmt, else_stmt, } => convert_if(condition, then_stmt, else_stmt.as_deref()), - StmtType::While { condition, body } => todo!(), + StmtType::While { condition, body } => convert_while(condition, body), StmtType::Class { name, methods, superclass, - } => todo!(), + } => class(name, methods, superclass), } } } @@ -50,6 +48,42 @@ fn single_expr(label: &str, expr: &Expr) -> GraphvizRepr { print } +fn rank_subgraph(ids: &[NodeId]) -> (Subgraph, Vec) { + let mut rank_subgraph = subgraph!(esc random_id()); + ids.iter().for_each(|id| { + rank_subgraph + .stmts + .push(Node::new(id.clone(), vec![]).into()) + }); + rank_subgraph.stmts.push(attr!("rank", "same").into()); + let invisible_edges = ids + .windows(2) + .map(|window| edge!(window[0].clone() => window[1].clone(); attr!("style", "invis")).into()) + .collect(); + (rank_subgraph, invisible_edges) +} + +fn block(block: &[stmt::Stmt], label: &str) -> GraphvizRepr { + let mut node = GraphvizRepr::single(stmt(label)); + let node_id = node.id.clone(); + let (ids, stmts): (Vec<_>, Vec<_>) = block + .iter() + .map(|s| s.to_graphviz()) + .map(|g| (g.id, g.stmts)) + .unzip(); + let mut stmts: Vec = stmts.into_iter().flatten().collect(); + let mut subgraph = subgraph!(esc random_cluster_id()); + subgraph.stmts.append(&mut stmts); + subgraph.stmts.push(attr!("style", "dotted").into()); + let (rank_subgraph, mut rank_edges) = rank_subgraph(&ids); + subgraph.stmts.push(rank_subgraph.into()); + subgraph.stmts.append(&mut rank_edges); + node.push(subgraph); + ids.into_iter() + .for_each(|id| node.push(edge!(node_id.clone() => id; attr!("style", "dashed")))); + node +} + fn function(function: &Function, function_type: &str) -> GraphvizRepr { let name = &function.name; let mut parameters = String::new(); @@ -59,37 +93,7 @@ fn function(function: &Function, function_type: &str) -> GraphvizRepr { .for_each(|arg| write!(&mut parameters, "{arg}, ").unwrap()); let parameters = parameters.trim_end_matches(", "); let label = format!("{function_type} {name}({parameters})"); - let mut node = GraphvizRepr::single(stmt(label.as_str())); - let node_id = node.id.clone(); - let (body_ids, body_stmts): (Vec<_>, Vec<_>) = function - .body - .iter() - .map(|s| s.to_graphviz()) - .map(|g| (g.id, g.stmts)) - .unzip(); - let mut body_stmts: Vec = body_stmts.into_iter().flatten().collect(); - let mut body_subgraph = subgraph!(esc random_cluster_id()); - body_subgraph.stmts.append(&mut body_stmts); - body_ids.windows(2).for_each(|window| { - body_subgraph - .stmts - .push(edge!(window[0].clone() => window[1].clone(); attr!("style", "invis")).into()) - }); - body_subgraph.stmts.push(attr!("style", "dotted").into()); - body_subgraph.stmts.push(attr!("label", "body").into()); - let mut rank_subgraph = subgraph!(esc random_id()); - body_ids.iter().for_each(|id| { - rank_subgraph - .stmts - .push(Node::new(id.clone(), vec![]).into()) - }); - rank_subgraph.stmts.push(attr!("rank", "min").into()); - body_subgraph.stmts.push(rank_subgraph.into()); - node.push(body_subgraph); - body_ids.into_iter().for_each(|id| { - node.push(edge!(node_id.clone() => id; attr!("style", "dashed"), attr!("weight","0.9"))) - }); - node + block(&function.body, label.as_str()) } fn convert_if( @@ -98,9 +102,75 @@ fn convert_if( else_stmt: Option<&stmt::Stmt>, ) -> GraphvizRepr { let mut node = GraphvizRepr::single(stmt("if")); + let mut ids = vec![]; let condition = condition.to_graphviz(); + ids.push(condition.id.clone()); node.append(condition.stmts); node.push(edge!(node.id.clone() => condition.id; attr!("label", "condition"))); + let then = then_stmt.to_graphviz(); + ids.push(then.id.clone()); + node.append(then.stmts); + node.push(edge!(node.id.clone() => then.id; attr!("label", "then"))); + else_stmt.into_iter().for_each(|e| { + let else_node = e.to_graphviz(); + ids.push(else_node.id.clone()); + node.append(else_node.stmts); + node.push(edge!(node.id.clone() => else_node.id; attr!("label", "else"))); + }); + node +} + +fn convert_while(condition: &Expr, body: &stmt::Stmt) -> GraphvizRepr { + let mut node = GraphvizRepr::single(stmt("while")); + let mut ids = vec![]; + let condition = condition.to_graphviz(); + let mut subgraph = subgraph!(esc random_id()); + ids.push(condition.id.clone()); + subgraph.stmts.extend(condition.stmts); + let body = body.to_graphviz(); + ids.push(body.id.clone()); + subgraph.stmts.extend(body.stmts); + let (rank_subgraph, mut rank_edges) = rank_subgraph(&ids); + subgraph.stmts.push(rank_subgraph.into()); + node.push(edge!(node.id.clone() => condition.id; attr!("label", "condition"))); + node.push(edge!(node.id.clone() => body.id; attr!("label", "body"))); + node.stmts.push(subgraph.into()); + node.stmts.append(&mut rank_edges); + node +} + +fn single_with_option_expr(label: &str, expr: &Option) -> GraphvizRepr { + let mut node = GraphvizRepr::single(stmt(label)); + expr.iter().for_each(|e| { + let e = e.to_graphviz(); + node.append(e.stmts); + node.stmts.push(edge!(node.id.clone() => e.id).into()); + }); + node +} + +fn class(name: &Name, methods: &[Function], superclass: &Option) -> GraphvizRepr { + let label = match superclass { + Some(superclass) => format!("class {} < {}", name, superclass.name), + None => format!("class {}", name), + }; + let mut node = GraphvizRepr::single(stmt(label.as_str())); + let node_id = node.id.clone(); + let (ids, stmts): (Vec<_>, Vec<_>) = methods + .iter() + .map(|f| function(f, "method")) + .map(|g| (g.id, g.stmts)) + .unzip(); + let mut stmts: Vec = stmts.into_iter().flatten().collect(); + let mut subgraph = subgraph!(esc random_cluster_id()); + subgraph.stmts.append(&mut stmts); + subgraph.stmts.push(attr!("style", "dotted").into()); + let (rank_subgraph, mut rank_edges) = rank_subgraph(&ids); + subgraph.stmts.push(rank_subgraph.into()); + subgraph.stmts.append(&mut rank_edges); + node.push(subgraph); + ids.into_iter() + .for_each(|id| node.push(edge!(node_id.clone() => id; attr!("style", "dashed")))); node }