feat(ast): Add graph output
This commit is contained in:
@@ -1,2 +1,3 @@
|
|||||||
/target
|
/target
|
||||||
/testcases
|
/testcases
|
||||||
|
/output
|
||||||
Generated
+56
@@ -28,12 +28,55 @@ dependencies = [
|
|||||||
"unicode-width",
|
"unicode-width",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "equivalent"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fixedbitset"
|
||||||
|
version = "0.5.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "foldhash"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.15.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
|
||||||
|
dependencies = [
|
||||||
|
"foldhash",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.17.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "heck"
|
name = "heck"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "indexmap"
|
||||||
|
version = "2.14.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9"
|
||||||
|
dependencies = [
|
||||||
|
"equivalent",
|
||||||
|
"hashbrown 0.17.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.8.0"
|
version = "2.8.0"
|
||||||
@@ -113,6 +156,18 @@ dependencies = [
|
|||||||
"autocfg",
|
"autocfg",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "petgraph"
|
||||||
|
version = "0.8.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455"
|
||||||
|
dependencies = [
|
||||||
|
"fixedbitset",
|
||||||
|
"hashbrown 0.15.5",
|
||||||
|
"indexmap",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.106"
|
version = "1.0.106"
|
||||||
@@ -166,6 +221,7 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"codespan-reporting",
|
"codespan-reporting",
|
||||||
"num",
|
"num",
|
||||||
|
"petgraph",
|
||||||
"regex",
|
"regex",
|
||||||
"strum",
|
"strum",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ edition = "2024"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
codespan-reporting = "0.13.1"
|
codespan-reporting = "0.13.1"
|
||||||
num = "0.4.3"
|
num = "0.4.3"
|
||||||
|
petgraph = "0.8.3"
|
||||||
regex = "1.12.3"
|
regex = "1.12.3"
|
||||||
strum = { version = "0.28.0", features = ["derive"] }
|
strum = { version = "0.28.0", features = ["derive"] }
|
||||||
thiserror = "2.0.18"
|
thiserror = "2.0.18"
|
||||||
|
|||||||
@@ -0,0 +1,155 @@
|
|||||||
|
use petgraph::dot::{Config, Dot};
|
||||||
|
use petgraph::graph::{Graph, NodeIndex};
|
||||||
|
|
||||||
|
use crate::ast::types::{
|
||||||
|
BlockStmt, CompileUnit, Expr, ExprValue, FuncDeclStmt, GlobalDeclStmt, Param, ReturnStmt,
|
||||||
|
Statement, VarDeclStmt, VarDeclStmtValue,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub type AstGraph = Graph<String, String>;
|
||||||
|
|
||||||
|
pub trait AstGraphExt {
|
||||||
|
fn to_graph(&self) -> AstGraph;
|
||||||
|
fn to_dot(&self) -> String {
|
||||||
|
format!("{}", Dot::with_config(&self.to_graph(), &[Config::EdgeNoLabel]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AstGraphExt for CompileUnit {
|
||||||
|
fn to_graph(&self) -> AstGraph {
|
||||||
|
let mut builder = AstGraphBuilder::new();
|
||||||
|
builder.add_compile_unit(self);
|
||||||
|
builder.graph
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct AstGraphBuilder {
|
||||||
|
graph: AstGraph,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AstGraphBuilder {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self { graph: Graph::new() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn node(&mut self, label: impl Into<String>) -> NodeIndex {
|
||||||
|
self.graph.add_node(label.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn child(&mut self, parent: NodeIndex, label: impl Into<String>) -> NodeIndex {
|
||||||
|
let child = self.node(label);
|
||||||
|
self.graph.add_edge(parent, child, String::new());
|
||||||
|
child
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_compile_unit(&mut self, compile_unit: &CompileUnit) -> NodeIndex {
|
||||||
|
let root = self.node(compile_unit.to_string());
|
||||||
|
for decl in &compile_unit.global_decls {
|
||||||
|
self.add_global_decl(root, decl);
|
||||||
|
}
|
||||||
|
root
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_global_decl(&mut self, parent: NodeIndex, decl: &GlobalDeclStmt) -> NodeIndex {
|
||||||
|
match decl {
|
||||||
|
GlobalDeclStmt::VarDecl(var_decl) => {
|
||||||
|
let node = self.child(parent, decl.to_string());
|
||||||
|
self.add_var_decl(node, var_decl);
|
||||||
|
node
|
||||||
|
}
|
||||||
|
GlobalDeclStmt::FuncDecl(func_decl) => {
|
||||||
|
let node = self.child(parent, decl.to_string());
|
||||||
|
self.add_func_decl(node, func_decl);
|
||||||
|
node
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_var_decl(&mut self, parent: NodeIndex, var_decl: &VarDeclStmt) -> NodeIndex {
|
||||||
|
let node = self.child(parent, var_decl.to_string());
|
||||||
|
for value in &var_decl.values {
|
||||||
|
self.add_var_decl_value(node, value);
|
||||||
|
}
|
||||||
|
node
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_var_decl_value(&mut self, parent: NodeIndex, value: &VarDeclStmtValue) -> NodeIndex {
|
||||||
|
self.child(parent, value.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_func_decl(&mut self, parent: NodeIndex, func_decl: &FuncDeclStmt) -> NodeIndex {
|
||||||
|
let node = self.child(parent, func_decl.to_string());
|
||||||
|
let params = self.child(node, "Params");
|
||||||
|
for param in &func_decl.params {
|
||||||
|
self.add_param(params, param);
|
||||||
|
}
|
||||||
|
self.add_block_stmt(node, &func_decl.body);
|
||||||
|
node
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_param(&mut self, parent: NodeIndex, param: &Param) -> NodeIndex {
|
||||||
|
self.child(parent, param.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_block_stmt(&mut self, parent: NodeIndex, block_stmt: &BlockStmt) -> NodeIndex {
|
||||||
|
let node = self.child(parent, block_stmt.to_string());
|
||||||
|
for stmt in &block_stmt.statements {
|
||||||
|
self.add_statement(node, stmt);
|
||||||
|
}
|
||||||
|
node
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_statement(&mut self, parent: NodeIndex, stmt: &Statement) -> NodeIndex {
|
||||||
|
match stmt {
|
||||||
|
Statement::Return(return_stmt) => {
|
||||||
|
let node = self.child(parent, stmt.to_string());
|
||||||
|
self.add_return_stmt(node, return_stmt);
|
||||||
|
node
|
||||||
|
}
|
||||||
|
Statement::Block(block_stmt) => self.add_block_stmt(parent, block_stmt),
|
||||||
|
Statement::Expr(expr) => {
|
||||||
|
let node = self.child(parent, stmt.to_string());
|
||||||
|
self.add_expr(node, expr);
|
||||||
|
node
|
||||||
|
}
|
||||||
|
Statement::VarDecl(var_decl) => {
|
||||||
|
let node = self.child(parent, stmt.to_string());
|
||||||
|
self.add_var_decl(node, var_decl);
|
||||||
|
node
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_return_stmt(&mut self, parent: NodeIndex, return_stmt: &ReturnStmt) -> NodeIndex {
|
||||||
|
match &return_stmt.value {
|
||||||
|
Some(expr) => self.add_expr(parent, expr),
|
||||||
|
None => self.child(parent, "Void"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_expr(&mut self, parent: NodeIndex, expr: &Expr) -> NodeIndex {
|
||||||
|
match &expr.value {
|
||||||
|
ExprValue::IntLit(_) | ExprValue::Var(_) => self.child(parent, expr.value.to_string()),
|
||||||
|
ExprValue::BinaryOp { lhs, op: _, rhs } => {
|
||||||
|
let node = self.child(parent, expr.value.to_string());
|
||||||
|
self.add_expr(node, lhs);
|
||||||
|
self.add_expr(node, rhs);
|
||||||
|
node
|
||||||
|
}
|
||||||
|
ExprValue::FuncCall(_, args) => {
|
||||||
|
let node = self.child(parent, expr.value.to_string());
|
||||||
|
let args_node = self.child(node, "Args");
|
||||||
|
for arg in args {
|
||||||
|
self.add_expr(args_node, arg);
|
||||||
|
}
|
||||||
|
node
|
||||||
|
}
|
||||||
|
ExprValue::Assign { lvalue, rvalue } => {
|
||||||
|
let node = self.child(parent, expr.value.to_string());
|
||||||
|
self.add_expr(node, lvalue);
|
||||||
|
self.add_expr(node, rvalue);
|
||||||
|
node
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1 +1,2 @@
|
|||||||
|
pub mod graph;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use crate::{diagnostic::span::Span, frontend::types::{TokenValue, TypeIdent}};
|
use crate::{diagnostic::span::Span, frontend::types::{TokenValue, TypeIdent}};
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
pub struct CompileUnit {
|
pub struct CompileUnit {
|
||||||
pub global_decls: Vec<GlobalDeclStmt>,
|
pub global_decls: Vec<GlobalDeclStmt>,
|
||||||
@@ -110,3 +111,111 @@ pub struct Param {
|
|||||||
pub param_type: Type,
|
pub param_type: Type,
|
||||||
pub span: Span,
|
pub span: Span,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for CompileUnit {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "CompileUnit")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for GlobalDeclStmt {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
GlobalDeclStmt::VarDecl(_) => write!(f, "GlobalVarDecl"),
|
||||||
|
GlobalDeclStmt::FuncDecl(_) => write!(f, "FuncDecl"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for VarDeclStmt {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "VarDecl")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for VarDeclStmtValue {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{} {}", self.var_type, self.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for FuncDeclStmt {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{} {}", self.return_type, self.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for BlockStmt {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "Block")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Statement {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Statement::Return(_) => write!(f, "ReturnStmt"),
|
||||||
|
Statement::Block(_) => write!(f, "BlockStmt"),
|
||||||
|
Statement::Expr(_) => write!(f, "ExprStmt"),
|
||||||
|
Statement::VarDecl(_) => write!(f, "VarDeclStmt"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for ReturnStmt {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "ReturnStmt")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Expr {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{}", self.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for ExprValue {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
ExprValue::IntLit(value) => write!(f, "IntLit({})", value),
|
||||||
|
ExprValue::Var(name) => write!(f, "Var({})", name),
|
||||||
|
ExprValue::BinaryOp { op, .. } => write!(f, "BinaryOp({})", op),
|
||||||
|
ExprValue::FuncCall(name, _) => write!(f, "FuncCall({})", name),
|
||||||
|
ExprValue::Assign { .. } => write!(f, "Assign"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Param {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{} {}", self.param_type, self.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for BinaryOp {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
let op = match self {
|
||||||
|
BinaryOp::Add => "+",
|
||||||
|
BinaryOp::Sub => "-",
|
||||||
|
BinaryOp::Mul => "*",
|
||||||
|
BinaryOp::Div => "/",
|
||||||
|
BinaryOp::Mod => "%",
|
||||||
|
BinaryOp::Equal => "==",
|
||||||
|
BinaryOp::NotEqual => "!=",
|
||||||
|
BinaryOp::Less => "<",
|
||||||
|
BinaryOp::LessEqual => "<=",
|
||||||
|
BinaryOp::Greater => ">",
|
||||||
|
BinaryOp::GreaterEqual => ">=",
|
||||||
|
};
|
||||||
|
write!(f, "{}", op)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Type {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Type::Int => write!(f, "int"),
|
||||||
|
Type::Void => write!(f, "void"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -654,6 +654,7 @@ mod tests {
|
|||||||
use std::io::BufRead;
|
use std::io::BufRead;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
|
use crate::ast::graph::AstGraphExt;
|
||||||
use crate::frontend::lexer::Lexer;
|
use crate::frontend::lexer::Lexer;
|
||||||
use crate::utils::case_list::CaseList;
|
use crate::utils::case_list::CaseList;
|
||||||
use crate::utils::num_sequence::NumberSequence;
|
use crate::utils::num_sequence::NumberSequence;
|
||||||
@@ -686,7 +687,10 @@ mod tests {
|
|||||||
is_error = true;
|
is_error = true;
|
||||||
}
|
}
|
||||||
let mut parser = Parser::new(tokens, diagnostics);
|
let mut parser = Parser::new(tokens, diagnostics);
|
||||||
let _compile_unit = parser.parse_compile_unit();
|
let compile_unit = parser.parse_compile_unit();
|
||||||
|
let dot = compile_unit.to_dot();
|
||||||
|
let case_name = case_list.get_case_name(case_no).unwrap().strip_suffix(".c").unwrap();
|
||||||
|
std::fs::write(format!("output/{}.dot", case_name), dot).unwrap();
|
||||||
if !parser.diagnostics.is_empty() {
|
if !parser.diagnostics.is_empty() {
|
||||||
parser.diagnostics.print(&format!("{}", case_path.display()), &full_text);
|
parser.diagnostics.print(&format!("{}", case_path.display()), &full_text);
|
||||||
is_error = true;
|
is_error = true;
|
||||||
|
|||||||
Reference in New Issue
Block a user