Rust语法分析库tree-sitter-css的使用:高效解析和操作CSS的Tree-sitter绑定

Rust语法分析库tree-sitter-css的使用:高效解析和操作CSS的Tree-sitter绑定

tree-sitter-css 是 Tree-sitter 的 CSS 语法分析器,它提供了高效的 CSS 解析能力。

安装

在你的项目目录中运行以下 Cargo 命令:

cargo add tree-sitter-css

或者在 Cargo.toml 中添加:

tree-sitter-css = "0.23.2"

使用示例

下面是一个完整的示例,展示如何使用 tree-sitter-css 解析 CSS 代码:

use tree_sitter::Parser;
use tree_sitter_css::language;

fn main() {
    // 创建解析器
    let mut parser = Parser::new();
    
    // 设置 CSS 语言
    parser.set_language(language()).expect("Error loading CSS grammar");
    
    // 要解析的 CSS 代码
    let css_code = r#"
        body {
            font-family: Arial, sans-serif;
            color: #333;
            margin: 0;
        }
        
        .container {
            max-width: 1200px;
            margin: 0 auto;
        }
    "#;
    
    // 解析 CSS
    let tree = parser.parse(css_code, None).unwrap();
    
    // 获取根节点
    let root_node = tree.root_node();
    
    // 打印语法树
    println!("{}", root_node.to_sexp());
    
    // 遍历规则集
    let mut cursor = root_node.walk();
    for child in root_node.children(&mut cursor) {
        if child.kind() == "rule_set" {
            println!("Found rule set:");
            
            // 获取选择器
            let selector = child.child_by_field_name("selectors").unwrap();
            println!("  Selector: {}", selector.utf8_text(css_code.as_bytes()).unwrap());
            
            // 获取声明块
            let block = child.child_by_field_name("block").unwrap();
            println!("  Declarations:");
            
            // 遍历声明
            let mut block_cursor = block.walk();
            for declaration in block.children(&mut block_cursor) {
                if declaration.kind() == "declaration" {
                    let property = declaration.child_by_field_name("property").unwrap();
                    let value = declaration.child_by_field_name("value").unwrap();
                    println!(
                        "    {}: {}",
                        property.utf8_text(css_code.as_bytes()).unwrap(),
                        value.utf8_text(css_code.as_bytes()).unwrap()
                    );
                }
            }
        }
    }
}

功能说明

  1. 创建解析器:使用 tree_sitter::Parser 创建解析器实例
  2. 设置语言:通过 tree_sitter_css::language() 设置 CSS 语法
  3. 解析代码:调用 parse() 方法解析 CSS 字符串
  4. 遍历语法树:可以通过节点的方法遍历和查询语法树结构
  5. 获取节点内容:使用 utf8_text() 方法获取节点对应的源代码

输出示例

运行上述代码将输出类似以下内容:

(stylesheet (rule_set (selectors (tag_name)) (block (declaration (property_name) (plain_value (string_value))) (declaration (property_name) (color_value)) (declaration (property_name) (plain_value (integer_value)))) (rule_set (selectors (class_name)) (block (declaration (property_name) (plain_value (integer_value) (unit)) (declaration (property_name) (plain_value (integer_value)) (declaration (property_name) (plain_value (integer_value)))))
Found rule set:
  Selector: body
  Declarations:
    font-family: Arial, sans-serif
    color: #333
    margin: 0
Found rule set:
  Selector: .container
  Declarations:
    max-width: 1200px
    margin: 0 auto

完整示例代码

use tree_sitter::{Parser, TreeCursor};
use tree_sitter_css::language;

fn main() {
    // 初始化解析器
    let mut parser = Parser::new();
    parser.set_language(language()).expect("加载CSS语法失败");

    // CSS示例代码
    let css = r#"
        /* 这是一个CSS注释 */
        body {
            background: #fff;
            font-size: 16px;
        }
        
        #header {
            height: 80px;
            display: flex;
        }
        
        @media (max-width: 768px) {
            body {
                font-size: 14px;
            }
        }
    "#;

    // 解析CSS代码
    let tree = parser.parse(css, None).unwrap();
    let root = tree.root_node();
    
    // 打印语法树结构
    println!("语法树结构:\n{}\n", root.to_sexp());
    
    // 遍历所有节点
    println!("开始遍历CSS节点:");
    traverse_nodes(&root, css, 0);
}

fn traverse_nodes(node: &tree_sitter::Node, source: &str, depth: usize) {
    // 打印当前节点信息
    let indent = "  ".repeat(depth);
    println!("{}{}: {}", indent, node.kind(), node.utf8_text(source.as_bytes()).unwrap().trim());
    
    // 递归遍历子节点
    let mut cursor = node.walk();
    for child in node.children(&mut cursor) {
        traverse_nodes(&child, source, depth + 1);
    }
}

示例输出

语法树结构:
(stylesheet (comment) (rule_set (selectors (tag_name)) (block (declaration (property_name) (color_value)) (declaration (property_name) (plain_value (integer_value) (unit)))) (rule_set (selectors (id_selector)) (block (declaration (property_name) (plain_value (integer_value) (unit)) (declaration (property_name) (plain_value))) (at_rule (at_keyword) (parenthesized_value (plain_value (dimension) (integer_value) (unit))) (block (rule_set (selectors (tag_name)) (block (declaration (property_name) (plain_value (integer_value) (unit)))))))

开始遍历CSS节点:
stylesheet: 
  comment: /* 这是一个CSS注释 */
  rule_set: 
    selectors: body
    block: 
      declaration: 
        property_name: background
        color_value: #fff
      declaration: 
        property_name: font-size
        plain_value: 16px
  rule_set: 
    selectors: #header
    block: 
      declaration: 
        property_name: height
        plain_value: 80px
      declaration: 
        property_name: display
        plain_value: flex
  at_rule: 
    at_keyword: @media
    parenthesized_value: (max-width: 768px)
    block: 
      rule_set: 
        selectors: body
        block: 
          declaration: 
            property_name: font-size
            plain_value: 14px

1 回复

Rust语法分析库tree-sitter-css的使用指南

概述

tree-sitter-css是一个用于解析CSS的Tree-sitter绑定库,它提供了高效解析和操作CSS代码的能力。Tree-sitter是一个增量解析系统,能够快速解析源代码并生成详细的语法树。

主要特性

  • 高性能CSS解析
  • 增量解析能力
  • 精确的语法错误定位
  • 支持查询和遍历语法树

安装方法

在Cargo.toml中添加依赖:

[dependencies]
tree-sitter = "0.20"
tree-sitter-css = "0.19"

基本使用方法

1. 解析CSS代码

use tree_sitter::Parser;
use tree_sitter_css::language;

fn main() {
    let mut parser = Parser::new();
    parser.set_language(language()).expect("Error loading CSS grammar");
    
    let css_code = r#"
        body {
            color: #333;
            margin: 0;
        }
    "#;
    
    let tree = parser.parse(css_code, None).unwrap();
    println!("{:#?}", tree.root_node());
}

2. 遍历语法树

fn walk_tree(node: tree_sitter::Node, source: &str, depth: usize) {
    let indent = "  ".repeat(depth);
    println!("{}{:?}", indent, node.kind());
    
    if node.child_count() > 0 {
        let mut cursor = node.walk();
        for child in node.children(&mut cursor) {
            walk_tree(child, source, depth + 1);
        }
    } else {
        println!("{}  Text: {:?}", indent, node.utf8_text(source.as_bytes()).unwrap());
    }
}

3. 查询特定节点

use tree_sitter::Query;

fn find_selectors(css: &str) {
    let mut parser = Parser::new();
    parser.set_language(language()).unwrap();
    let tree = parser.parse(css, None).unwrap();
    
    let query = Query::new(
        language(),
        "(rule_set (selectors) @selector)"
    ).unwrap();
    
    let mut cursor = tree_sitter::QueryCursor::new();
    for match_ in cursor.matches(&query, tree.root_node(), css.as_bytes()) {
        for capture in match_.captures {
            let node = capture.node;
            println!("Selector: {}", node.utf8_text(css.as_bytes()).unwrap());
        }
    }
}

高级用法

1. 增量解析

fn incremental_parsing() {
    let mut parser = Parser::new();
    parser.set_language(language()).unwrap();
    
    let old_source = "body { color: red; }";
    let tree = parser.parse(old_source, None).unwrap();
    
    // 修改后的代码
    let new_source = "body { color: blue; }";
    
    // 增量解析
    let new_tree = parser.parse(new_source, Some(&tree)).unwrap();
    println!("New tree: {:?}", new_tree.root_node());
}

2. 语法高亮

use tree_sitter_highlight::{HighlightConfiguration, Highlighter, HighlightEvent};

fn highlight_css(css: &str) {
    let mut highlighter = Highlighter::new();
    let config = HighlightConfiguration::new(
        language(),
        "css",
        tree_sitter_css::HIGHLIGHTS_QUERY,
        "",
        ""
    ).unwrap();
    
    let highlights = highlighter.highlight(
        &config,
        css.as_bytes(),
        None,
        |_| None
    ).unwrap();
    
    for event in highlights {
        match event.unwrap() {
            HighlightEvent::Source {start, end} => {
                println!("Text: {}", &css[start..end]);
            }
            HighlightEvent::HighlightStart(s) => {
                println!("Highlight start: {:?}", s);
            }
            HighlightEvent::HighlightEnd => {
                println!("Highlight end");
            }
        }
    }
}

错误处理

fn parse_with_errors(css: &str) {
    let mut parser = Parser::new();
    parser.set_language(language()).unwrap();
    
    let tree = parser.parse(css, None).unwrap();
    
    if tree.root_node().has_error() {
        let mut cursor = tree.walk();
        for node in tree.root_node().children(&mut cursor) {
            if node.is_error() {
                println!("Error at {}:{}", node.start_position().row, node.start_position().column);
            }
        }
    }
}

实际应用示例

提取所有CSS属性

fn extract_properties(css: &str) -> Vec<String> {
    let mut parser = Parser::new();
    parser.set_language(language()).unwrap();
    let tree = parser.parse(css, None).unwrap();
    
    let query = Query::new(
        language(),
        "(declaration (property_name) @property)"
    ).unwrap();
    
    let mut properties = Vec::new();
    let mut cursor = tree_sitter::QueryCursor::new();
    for match_ in cursor.matches(&query, tree.root_node(), css.as_bytes()) {
        for capture in match_.captures {
            let node = capture.node;
            properties.push(node.utf8_text(css.as_bytes()).unwrap().to_string());
        }
    }
    
    properties
}

性能提示

  1. 重用Parser实例以减少初始化开销
  2. 对于大型CSS文件,使用增量解析
  3. 缓存常用查询以提高性能

tree-sitter-css为Rust开发者提供了强大的CSS解析能力,适用于构建CSS处理工具、编辑器插件、静态分析工具等各种应用场景。

完整示例代码

下面是一个结合了多个功能的完整示例:

use tree_sitter::{Parser, Query, QueryCursor};
use tree_sitter_css::language;
use tree_sitter_highlight::{HighlightConfiguration, Highlighter, HighlightEvent};

fn main() {
    // 示例CSS代码
    let css_code = r#"
        body {
            color: #333;
            margin: 0;
            font-family: Arial, sans-serif;
        }
        
        .container {
            width: 100%;
            padding: 20px;
        }
        
        /* 这是一个注释 */
        #header {
            background-color: #f0f0f0;
        }
    "#;

    // 1. 初始化解析器
    let mut parser = Parser::new();
    parser.set_language(language()).expect("加载CSS语法失败");

    // 2. 解析CSS代码
    let tree = parser.parse(css_code, None).unwrap();
    println!("语法树根节点: {:#?}", tree.root_node());

    // 3. 遍历语法树
    println!("\n遍历语法树:");
    walk_tree(tree.root_node(), css_code, 0);

    // 4. 查询选择器
    println!("\n查询选择器:");
    find_selectors(css_code);

    // 5. 提取属性
    println!("\n提取所有属性:");
    let properties = extract_properties(css_code);
    for prop in properties {
        println!("- {}", prop);
    }

    // 6. 语法高亮
    println!("\n语法高亮结果:");
    highlight_css(css_code);

    // 7. 错误处理演示
    println!("\n错误处理演示:");
    let invalid_css = "body { color: ; }";
    parse_with_errors(invalid_css);
}

// 遍历语法树的函数
fn walk_tree(node: tree_sitter::Node, source: &str, depth: usize) {
    let indent = "  ".repeat(depth);
    println!("{}{:?}", indent, node.kind());
    
    if node.child_count() > 0 {
        let mut cursor = node.walk();
        for child in node.children(&mut cursor) {
            walk_tree(child, source, depth + 1);
        }
    } else {
        println!("{}  Text: {:?}", indent, node.utf8_text(source.as_bytes()).unwrap());
    }
}

// 查询选择器的函数
fn find_selectors(css: &str) {
    let mut parser = Parser::new();
    parser.set_language(language()).unwrap();
    let tree = parser.parse(css, None).unwrap();
    
    let query = Query::new(
        language(),
        "(rule_set (selectors) @selector"
    ).unwrap();
    
    let mut cursor = QueryCursor::new();
    for match_ in cursor.matches(&query, tree.root_node(), css.as_bytes()) {
        for capture in match_.captures {
            let node = capture.node;
            println!("选择器: {}", node.utf8_text(css.as_bytes()).unwrap());
        }
    }
}

// 提取属性的函数
fn extract_properties(css: &str) -> Vec<String> {
    let mut parser = Parser::new();
    parser.set_language(language()).unwrap();
    let tree = parser.parse(css, None).unwrap();
    
    let query = Query::new(
        language(),
        "(declaration (property_name) @property)"
    ).unwrap();
    
    let mut properties = Vec::new();
    let mut cursor = QueryCursor::new();
    for match_ in cursor.matches(&query, tree.root_node(), css.as_bytes()) {
        for capture in match_.captures {
            let node = capture.node;
            properties.push(node.utf8_text(css.as_bytes()).unwrap().to_string());
        }
    }
    
    properties
}

// 语法高亮的函数
fn highlight_css(css: &str) {
    let mut highlighter = Highlighter::new();
    let config = HighlightConfiguration::new(
        language(),
        "css",
        tree_sitter_css::HIGHLIGHTS_QUERY,
        "",
        ""
    ).unwrap();
    
    let highlights = highlighter.highlight(
        &config,
        css.as_bytes(),
        None,
        |_| None
    ).unwrap();
    
    for event in highlights {
        match event.unwrap() {
            HighlightEvent::Source {start, end} => {
                println!("文本: {}", &css[start..end]);
            }
            HighlightEvent::HighlightStart(s) => {
                println!("高亮开始: {:?}", s);
            }
            HighlightEvent::HighlightEnd => {
                println!("高亮结束");
            }
        }
    }
}

// 错误处理的函数
fn parse_with_errors(css: &str) {
    let mut parser = Parser::new();
    parser.set_language(language()).unwrap();
    
    let tree = parser.parse(css, None).unwrap();
    
    if tree.root_node().has_error() {
        let mut cursor = tree.walk();
        for node in tree.root_node().children(&mut cursor) {
            if node.is_error() {
                println!("错误位置: {}行{}列", 
                    node.start_position().row + 1, 
                    node.start_position().column + 1);
            }
        }
    }
}

这个完整示例演示了如何使用tree-sitter-css库进行CSS代码的解析、遍历、查询、属性提取、语法高亮和错误处理。你可以根据需要选择使用其中的部分功能或全部功能。

回到顶部