• XSS.stack #1 – первый литературный журнал от юзеров форума

Rust AST analyzer

salsa20

(L2) cache
Пользователь
Регистрация
03.05.2019
Сообщения
498
Реакции
110
Гарант сделки
1
Вообщем поигрался с AST анализатором раста и выдает неплохие вещи, если вы пишите на нем, то сейчас это как-бы новый язык и инструментов под него маловато.
Мой код это анализатор дерева Rust кода, чтобы показать быстренько его структуру, то есть классы, функции, поля, ассоциации, трейты..

Думаю можно и запилить какие-то графики чтобы понятнее было!

Анализатор
Код:
extern crate tree_sitter;
extern crate tree_sitter_rust;

use std::fs;
use tree_sitter::{Node, Parser};

fn main() {
    let code = match fs::read_to_string(r"C:\Users\WORKER\Desktop\RustDev\web3\src\code.txt") {
        Ok(content) => content,
        Err(err) => {
            eprintln!("Error reading code.txt: {}", err);
            return;
        }
    };

    let mut parser = Parser::new();
    parser.set_language(tree_sitter_rust::language()).expect("Error loading Rust grammar");
    let parsed = parser.parse(&code, None).expect("Parsing error");

    let root_node = parsed.root_node();
    println!("Root node: {:?}", root_node);

    // Обработка классов
    process_classes(root_node, &code);
}

pub fn process_classes(node: Node, code: &str) {
    let mut cursor = node.walk();
    for child in node.named_children(&mut cursor) {
        // println!("Child: {:?}", child.kind());
        if child.kind() == "struct_item" || child.kind() == "enum_item" {
            // Поиск узла с именем класса
            if let Some(class_name_node) = child.child_by_field_name("name") {
                let class_name = class_name_node.utf8_text(code.as_bytes()).unwrap().to_string();
                let cll = class_name.clone();
                println!("Class: {}", cll);
            }
        }

        process_classes(child, code); // Рекурсивный вызов для вложенных классов

        // Обработка функций
        process_functions(child, code);

        // Обработка ассоциаций и зависимостей
        process_associations_and_dependencies(child, code);

        // Обработка наследования
        process_inheritance(child, code);
    }

    // Обработка интерфейсов
    process_interfaces(node, code);
}

fn process_functions(class_node: Node, code: &str) {
    fn process_parameters(node: Node, code: &str, arguments: &mut Vec<String>) {
        let mut cursor = node.walk();
        for child in node.children(&mut cursor) {
            if child.kind() == "parameter" {
                if let Some(parameter_name_node) = child.child_by_field_name("name") {
                    let parameter_name = parameter_name_node.utf8_text(code.as_bytes()).unwrap().to_string();
                    arguments.push(parameter_name);
                }
            }
        }
    }

    let mut cursor = class_node.walk();
    for child in class_node.named_children(&mut cursor) {
        // println!("Child: {:?}", child.kind());
        if child.kind() == "function_item" {
            // Получаем имя функции
            if let Some(function_name_node) = child.child_by_field_name("name") {
                let function_name = function_name_node.utf8_text(code.as_bytes()).unwrap().to_string();

                // Получаем аргументы функции
                let mut arguments = Vec::new();
                if let Some(parameter_list_node) = child.child_by_field_name("parameters") {
                    process_parameters(parameter_list_node, code, &mut arguments);
                }

                // Получаем тип возвращаемого значения
                let return_type = if let Some(return_type_node) = child.child_by_field_name("return_type") {
                    return_type_node.utf8_text(code.as_bytes()).unwrap().to_string()
                } else {
                    "void".to_string() // Здесь можно задать значение по умолчанию
                };

                // Выводим информацию о функции
                println!("  Function: {}({}) -> {}", function_name, arguments.join(", "), return_type);
            }
        }
    }
}


fn process_inheritance(class_node: Node, code: &str) {
    let mut cursor = class_node.walk();
    for child in class_node.named_children(&mut cursor) {
        if child.kind() == "trait_bound" {
            // Handle inheritance clause if it exists
            if let Some(inherited_type_node) = child.child_by_field_name("type") {
                let inherited_type = inherited_type_node.utf8_text(code.as_bytes()).unwrap().to_string();
                println!("  Inherits: {}", inherited_type);
            }
        }
    }
}

fn process_associations_and_dependencies(class_node: Node, code: &str) {
    let mut cursor = class_node.walk();
    for child in class_node.named_children(&mut cursor) {
        if child.kind() == "field_declaration" {
            // Получаем имя поля
            let field_name = child.utf8_text(code.as_bytes()).unwrap().to_string();

            // Получаем тип поля
            if let Some(field_type_node) = child.child_by_field_name("type") {
                let field_type = field_type_node.utf8_text(code.as_bytes()).unwrap().to_string();

                // Здесь вы можете анализировать тип поля и определить ассоциацию
                println!("  Field - {}: Association with class {}", field_name, field_type);
            }
        }
    }
}

fn process_interfaces(class_node: Node, code: &str) {
    let mut cursor = class_node.walk();
    for child in class_node.named_children(&mut cursor) {
        if child.kind() == "impl_item" {
            // Check if this is an implementation for a trait
            if let Some(trait_name) = get_trait_name_from_impl(child, code) {
                let type_name = get_type_name_from_impl(child, code);
                println!("Trait '{}' implemented for type '{}'", trait_name, type_name);

                // Process each function within the impl block
                process_trait_functions(child, code, &trait_name, &type_name);
            }
        }
    }
}

fn get_trait_name_from_impl(impl_node: Node, code: &str) -> Option<String> {
    // This function extracts the trait name from an impl block
    // Returns None if it's not a trait implementation
    let mut cursor = impl_node.walk();
    let option = impl_node.named_children(&mut cursor)
        .find(|n| n.kind() == "type_identifier")
        .and_then(|n| n.utf8_text(code.as_bytes()).ok())
        .map(|s| s.to_string());
    option
}

fn get_type_name_from_impl(impl_node: Node, code: &str) -> String {
    // This function extracts the type name from an impl block
    // Should be called after confirming it's a trait implementation
    let mut cursor = impl_node.walk();
    impl_node.named_children(&mut cursor)
        .filter(|n| n.kind() == "type_identifier")
        .last() // Getting the last type_identifier assuming it's the type name
        .and_then(|n| n.utf8_text(code.as_bytes()).ok())
        .unwrap_or_default()
        .to_string()
}

fn process_trait_functions(impl_node: Node, code: &str, trait_name: &str, type_name: &str) {
    let mut cursor = impl_node.walk();

    // Find the declaration_list node within the impl block
    for child in impl_node.named_children(&mut cursor) {
        if child.kind() == "declaration_list" {
            process_declaration_list(child, code, trait_name, type_name);
        }
    }
}

fn process_declaration_list(decl_list_node: Node, code: &str, trait_name: &str, type_name: &str) {
    let mut cursor = decl_list_node.walk();

    // Iterate over function items within the declaration list
    for child in decl_list_node.named_children(&mut cursor) {
        if child.kind() == "function_item" {
            // Extract function name
            if let Some(function_name_node) = child.child_by_field_name("name") {
                let function_name = function_name_node.utf8_text(code.as_bytes()).unwrap().to_string();

                // Output the trait implementation details
                println!("Trait '{}' for type '{}' implements function '{}'", trait_name, type_name, function_name);
            }
        }
    }
}

Cargo.toml
YAML:
[package]
name = "my_project"
version = "0.1.0"
edition = "2018"

[dependencies]
tree-sitter = "0.20.10"
tree-sitter-rust = "0.20.4"

Для этого кода https://pastebin.com/AbFzD17y сделает такой лог https://pastebin.com/ceJPY0tp
 
Если закинуть лог в chatgpt с запросом создать mermaid диаграмму получаем
Код:
classDiagram
  class Language
  class Tree
  class Point
  class Range
  class InputEdit
  class Node
  class Parser
  class LogType
  class TreeCursor
  class Query
  class CaptureQuantifier
  class QueryCursor
  class QueryProperty
  class QueryPredicateArg
  class QueryPredicate
  class QueryMatch
  class QueryMatches
  class QueryCaptures
  class QueryCapture
  class LanguageError
  class IncludedRangesError
  class QueryError
  class QueryErrorKind
  class TextPredicate
  class LossyUtf8

  Language --|> Tree
  Point --> usize
  Point --> usize
  Range --> usize
  Range --> usize
  Range --> Point
  Range --> Point
  InputEdit --> usize
  InputEdit --> usize
  InputEdit --> usize
  InputEdit --> Point
  InputEdit --> Point
  InputEdit --> Point
  Query --> QueryCursor
  QueryCursor --> QueryMatches
  QueryCursor --> QueryCaptures
  QueryCursor --> QueryProperty
  QueryCursor --> QueryPredicate
  QueryCursor --> QueryMatch
  QueryCursor --> QueryErrorKind
  QueryCursor --> Point
  QueryCursor --> Point
  QueryCursor --> usize
  QueryCursor --> usize
  QueryCursor --> usize
  QueryCursor --> String
  QueryCursor --> QueryErrorKind
  QueryMatch --> QueryCapture
  QueryMatches --> QueryCapture
  QueryCaptures --> QueryCapture
  QueryCapture --> Node
  QueryCapture --> u32
  LanguageError --> usize
  IncludedRangesError --> LanguageError
  QueryError --> usize
  QueryError --> usize
  QueryError --> usize
  QueryError --> String
  QueryError --> QueryErrorKind
  QueryErrorKind --> TextPredicate
  LossyUtf8 --> u8

  Language : Trait 'Language'
  Tree : Trait 'Tree'
  Point : Trait 'Point'
  Range : Trait 'Range'
  InputEdit : Trait 'InputEdit'
  Node : Trait 'Node'
  Parser : Trait 'Parser'
  LogType : Class
  TreeCursor : Class
  Query : Trait 'Query'
  CaptureQuantifier : Trait 'CaptureQuantifier'
  QueryCursor : Trait 'QueryCursor'
  QueryProperty : Trait 'QueryProperty'
  QueryPredicateArg : Class
  QueryPredicate : Trait 'QueryPredicate'
  QueryMatch : Trait 'QueryMatch'
  QueryMatches : Trait 'QueryMatches'
  QueryCaptures : Trait 'QueryCaptures'
  QueryCapture : Trait 'QueryCapture'
  LanguageError : Trait 'LanguageError'
  IncludedRangesError : Trait 'IncludedRangesError'
  QueryError : Trait 'QueryError'
  QueryErrorKind : Trait 'QueryErrorKind'
  TextPredicate : Trait 'TextPredicate'
  LossyUtf8 : Trait 'LossyUtf8'

Ну и диаграммку для глобального представления
1706932933854.png
 
Cargo.toml
rust_mermaid = "0.1.1"
Сохранение диаграммы для простого кодеса
Код:
extern crate tree_sitter;
extern crate tree_sitter_rust;

use std::fs;
use tree_sitter::{Node, Parser};

use rust_mermaid::create_diagram;
use rust_mermaid::save_diagram;


fn main() {
    let code = match fs::read_to_string(r"C:\Users\WORKER\Desktop\RustDev\web3\src\code.txt") {
        Ok(content) => content,
        Err(err) => {
            eprintln!("Error reading code.txt: {}", err);
            return;
        }
    };

    let mut parser = Parser::new();
    parser.set_language(tree_sitter_rust::language()).expect("Error loading Rust grammar");
    let parsed = parser.parse(&code, None).expect("Parsing error");

    let root_node = parsed.root_node();
    println!("Root node: {:?}", root_node);
    let mut mermaid_diagram = String::new(); // Initialize the Mermaid diagram text

    // Start building the Mermaid diagram
    mermaid_diagram.push_str("graph TD;\n");
    // Обработка классов
    process_classes(root_node, &code, &mut mermaid_diagram);

    // Print the Mermaid diagram
    println!("{}", "-".repeat(99));
    println!("Mermaid Diagram:");
    println!("{}", mermaid_diagram);
    println!("{}", "-".repeat(99));
    println!("Diagram file creation:");

    let result = save_diagram(&mermaid_diagram, "diagram.jpg");

    match result {
        Ok(_) => println!("Saved diagram to: diagram.jpg"),
        Err(error) => panic!("Ops, there is a problem! {}", error),
    }
}

pub fn process_classes(node: Node, code: &str, mermaid_diagram: &mut String) {
    let mut cursor = node.walk();
    for child in node.named_children(&mut cursor) {
        // println!("Child: {:?}", child.kind());
        if child.kind() == "struct_item" || child.kind() == "enum_item" {
            // Поиск узла с именем класса
            if let Some(class_name_node) = child.child_by_field_name("name") {
                let class_name = class_name_node.utf8_text(code.as_bytes()).unwrap().to_string();
                let cll = class_name.clone();
                println!("Class: {}", cll);
                // Create a subgraph for the class
                mermaid_diagram.push_str(&format!("    subgraph {}[{}];\n", cll, cll));
                // Close the subgraph
                mermaid_diagram.push_str("    end;\n");
            }
        }

        process_classes(child, code, mermaid_diagram); // Recursive call for nested classes

        // Обработка функций
        process_functions(child, code, mermaid_diagram);

        // Обработка ассоциаций и зависимостей
        process_associations_and_dependencies(child, code, mermaid_diagram);
    }

    // Обработка интерфейсов
    process_interfaces(node, code, mermaid_diagram);
}

fn process_functions(class_node: Node, code: &str, mermaid_diagram: &mut String) {
    fn process_parameters(node: Node, code: &str, arguments: &mut Vec<String>) {
        let mut cursor = node.walk();

        let is_enabled = false;

        if is_enabled {
            for child in node.children(&mut cursor) {
                if child.kind() == "parameter" {
                    let parameter_name = child.utf8_text(code.as_bytes()).unwrap().to_string();

                    arguments.push(parameter_name);
                }
            }
        }
    }

    let mut cursor = class_node.walk();
    for child in class_node.named_children(&mut cursor) {
        if child.kind() == "function_item" {
            // Get function name
            if let Some(function_name_node) = child.child_by_field_name("name") {
                let function_name = function_name_node.utf8_text(code.as_bytes()).unwrap().to_string();

                // Get function parameters
                let mut arguments = Vec::new();
                if let Some(parameter_list_node) = child.child_by_field_name("parameters") {
                    process_parameters(parameter_list_node, code, &mut arguments);
                }

                // Get return type
                let return_type = if let Some(return_type_node) = child.child_by_field_name("return_type") {
                    return_type_node.utf8_text(code.as_bytes()).unwrap().to_string()
                } else {
                    "void".to_string()
                };
                fn replace_brackets(input: String) -> String {
                    let mut result = String::new();

                    for c in input.chars() {
                        match c {
                            '[' => result.push(' '),
                            ']' => result.push(' '),
                            _ => result.push(c),
                        }
                    }

                    result
                }

                // Output the function information
                // println!("Args {}", arguments.len().to_string());
                if arguments.len() == 0 {
                    println!("  Function: {} -> {}", function_name, return_type);
                    // Add the function to the Mermaid diagram
                    mermaid_diagram.push_str(&format!(
                        "    {} --> {};\n",
                        function_name, function_name
                    ));
                } else {
                    let replaced_string = replace_brackets(arguments.join(", "));

                    println!("  Function: {}({}) -> {}", function_name, replaced_string, return_type);
                    // Add the function to the Mermaid diagram
                    mermaid_diagram.push_str(&format!(
                        "    {}({}) --> {};\n",
                        function_name, replaced_string, function_name
                    ));
                }
            }
        }
    }
}


fn process_associations_and_dependencies(class_node: Node, code: &str, mermaid_diagram: &mut String) {
    let mut cursor = class_node.walk();
    for child in class_node.named_children(&mut cursor) {
        if child.kind() == "field_declaration" {
            // Получаем имя поля
            let field_name = child.utf8_text(code.as_bytes()).unwrap().to_string();

            // Получаем тип поля
            if let Some(field_type_node) = child.child_by_field_name("type") {
                let field_type = field_type_node.utf8_text(code.as_bytes()).unwrap().to_string();

                // Здесь вы можете анализировать тип поля и определить ассоциацию
                println!("  Field - {}: Association with class {}", field_name, field_type);

                if let Some(index) = field_name.find(':') {
                    let colon_index = index;
                    let trimmed_field_type = &field_name[(colon_index + 1)..];
                    let result2 = &field_name[0..colon_index];

                    // Create associations within the class's subgraph
                    if trimmed_field_type == " ()" {
                        let resulted = format!("{}:{}", result2, trimmed_field_type);
                        mermaid_diagram.push_str(&format!(
                            "        {};\n",
                            resulted.replace(" ", "").replace("->", "-->").replace("()", "[Empty]")
                        ));
                    } else {
                        let resulted = format!("{}:{}", result2, trimmed_field_type);
                        mermaid_diagram.push_str(&format!(
                            "        {} --> |Association| {};\n",
                            resulted.replace(" ", "").replace("->", "-->"),
                            field_type.replace(" ", "").replace("->", "-->")
                        ));
                    }
                }
            }
        }
    }
}

fn process_interfaces(class_node: Node, code: &str, mermaid_diagram: &mut String) {
    let mut cursor = class_node.walk();
    for child in class_node.named_children(&mut cursor) {
        if child.kind() == "impl_item" {
            // Check if this is an implementation for a trait
            if let Some(trait_name) = get_trait_name_from_impl(child, code) {
                let type_name = get_type_name_from_impl(child, code);
                println!("Trait '{}' implemented for type '{}'", trait_name, type_name);
                // Add interface to the Mermaid diagram
                mermaid_diagram.push_str(&format!(
                    "    {} -->|implements| {};\n",
                    type_name, trait_name
                ));

                // Process each function within the impl block
                process_trait_functions(child, code, &trait_name, &type_name, mermaid_diagram);
            }
        }
    }
}

fn get_trait_name_from_impl(impl_node: Node, code: &str) -> Option<String> {
    // This function extracts the trait name from an impl block
    // Returns None if it's not a trait implementation
    let mut cursor = impl_node.walk();
    let option = impl_node.named_children(&mut cursor)
        .find(|n| n.kind() == "type_identifier")
        .and_then(|n| n.utf8_text(code.as_bytes()).ok())
        .map(|s| s.to_string());
    option
}

fn get_type_name_from_impl(impl_node: Node, code: &str) -> String {
    // This function extracts the type name from an impl block
    // Should be called after confirming it's a trait implementation
    let mut cursor = impl_node.walk();
    impl_node.named_children(&mut cursor)
        .filter(|n| n.kind() == "type_identifier")
        .last() // Getting the last type_identifier assuming it's the type name
        .and_then(|n| n.utf8_text(code.as_bytes()).ok())
        .unwrap_or_default()
        .to_string()
}

fn process_trait_functions(impl_node: Node, code: &str, trait_name: &str, type_name: &str, mermaid_diagram: &mut String) {
    let mut cursor = impl_node.walk();

    // Find the declaration_list node within the impl block
    for child in impl_node.named_children(&mut cursor) {
        if child.kind() == "declaration_list" {
            process_declaration_list(child, code, trait_name, type_name, mermaid_diagram);
        }
    }
}

fn process_declaration_list(
    decl_list_node: Node,
    code: &str,
    trait_name: &str,
    type_name: &str,
    mermaid_diagram: &mut String,
) {
    let mut cursor = decl_list_node.walk();

    // Iterate over function items within the declaration list
    for child in decl_list_node.named_children(&mut cursor) {
        if child.kind() == "function_item" {
            // Extract function name
            if let Some(function_name_node) = child.child_by_field_name("name") {
                let function_name = function_name_node.utf8_text(code.as_bytes()).unwrap().to_string();

                // Output the trait implementation details
                println!("Trait '{}' for type '{}' implements function '{}'", trait_name, type_name, function_name);

                // Add function implementation to the Mermaid diagram
                mermaid_diagram.push_str(&format!(
                    "    {} -->|implements| {};\n",
                    type_name, function_name
                ));
            }
        }
    }
}

для кода
Код:
// Определение интерфейса (trait)
trait Vehicle {
    fn start(&self);
    fn stop(&self);
    fn honk(&self);
}

// Реализация интерфейса для структуры Car
struct Car {
    make: String,
    model: String,
}

impl Vehicle for Car {
    fn start(&self) {
        println!("{} {} is starting.", self.make, self.model);
    }

    fn stop(&self) {
        println!("{} {} is stopping.", self.make, self.model);
    }

    fn honk(&self) {
        println!("{} {} is honking the horn.", self.make, self.model);
    }
}

// Реализация интерфейса для структуры Bicycle
struct Bicycle {
    brand: String,
    model: String,
}

impl Vehicle for Bicycle {
    fn start(&self) {
        println!("{} {} is starting to pedal.", self.brand, self.model);
    }

    fn stop(&self) {
        println!("{} {} is stopping.", self.brand, self.model);
    }

    fn honk(&self) {
        println!("{} {} doesn't have a horn.", self.brand, self.model);
    }
}

fn main() {
    let car = Car {
        make: "Toyota".to_string(),
        model: "Camry".to_string(),
    };

    let bicycle = Bicycle {
        brand: "Trek".to_string(),
        model: "Mountain Bike".to_string(),
    };

    // Вызов методов интерфейса для разных типов
    car.start();
    car.honk();
    car.stop();

    bicycle.start();
    bicycle.honk();
    bicycle.stop();
}

выдаст такую диаграмку
1706939349162.png


Без участия онлайн сервисов !
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх