برنامه نویسی

حفر عمیق تر به کد با درخت نشانگر: چگونه می توان از درخت نحو خود پرس و جو کرد

سلام به همه! من شیلندرا هستم.
در آخرین پست من-“درخت نشستن: از کد تا درخت نحو”-من در مورد چگونگی استفاده از درخت نشانگر برای تولید یک درخت نحوی برای یک زبان معین با استفاده از دستور زبان خود صحبت کردم.

در این پست ، ما یک قدم جلوتر خواهیم رفت و می بینیم که چگونه می توان از درخت نشسته برای ایجاد پرس و جو در متن منبع شما استفاده کرد تا بخش های مختلف کد را درک و تجزیه و تحلیل کند.

این یکی دیگر از ویژگی های بسیار جالب Tree-Sitter است که می تواند به شما در دریافت بینش عمیق تر از کد منبع خود کمک کند. به عنوان مثال ، می توانید یک پرس و جو بنویسید تا تمام کارکردهای موجود در کد منبع repo را لیست کنید. و این دقیقاً همان چیزی است که ما در این پست کشف خواهیم کرد.

درست مانند آخرین پست ، من از Golang برای نشان دادن موارد استفاده استفاده می کنم. اگر با نحوه استفاده از Tree-Sitter در Golang آشنا نیستید ، لطفاً پست قبلی را برای مراحل نصب و تنظیم کد اصلی بررسی کنید.


مورد استفاده ما

حال ، بیایید به پرونده استفاده خود بپردازیم. همانطور که گفته شد ، من نحوه لیست همه توابع را از یک فایل منبع معین نشان می دهم.

در اینجا کد Golang که مورد تجزیه و تحلیل قرار می گیریم آورده شده است:

package main

import "fmt"

func add(num1 int32, num2 int32) int32 {
    return num1 + num2
}

func subtract(num1, num2 int32) int32 {
    return num1 - num2
}

func calculator(operator string, num1, num2 int32) int32 {
    switch operator {
    case "+":
        return add(num1, num2)
    case "-":
        return subtract(num1, num2)
    default:
        fmt.Errorf("Unknown operator")
    }

    return -1
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید


خروجی درخت تجزیه

بیایید به سرعت ببینیم که چگونه درخت پارس به دنبال این کد است:

(source_file
  (package_clause
    (package_identifier))

  (import_declaration
    (import_spec
      path: (interpreted_string_literal)))

  (function_declaration
    name: (identifier)
    parameters: (parameter_list
      (parameter_declaration
        name: (identifier)
        type: (type_identifier))
      (parameter_declaration
        name: (identifier)
        type: (type_identifier)))
    result: (type_identifier)
    body: (block
      (return_statement
        (expression_list
          (binary_expression
            left: (identifier)
            right: (identifier))))))

  (function_declaration
    name: (identifier)
    parameters: (parameter_list
      (parameter_declaration
        name: (identifier)
        name: (identifier)
        type: (type_identifier)))
    result: (type_identifier)
    body: (block
      (return_statement
        (expression_list
          (binary_expression
            left: (identifier)
            right: (identifier))))))

  (function_declaration
    name: (identifier)
    parameters: (parameter_list
      (parameter_declaration
        name: (identifier)
        type: (type_identifier))
      (parameter_declaration
        name: (identifier)
        name: (identifier)
        type: (type_identifier)))
    result: (type_identifier)
    body: (block
      (expression_switch_statement
        value: (identifier)

        (expression_case
          value: (expression_list
            (interpreted_string_literal))
          (return_statement
            (expression_list
              (call_expression
                function: (identifier)
                arguments: (argument_list
                  (identifier)
                  (identifier))))))

        (expression_case
          value: (expression_list
            (interpreted_string_literal))
          (return_statement
            (expression_list
              (call_expression
                function: (identifier)
                arguments: (argument_list
                  (identifier)
                  (identifier))))))

        (default_case
          (expression_statement
            (call_expression
              function: (selector_expression
                operand: (identifier)
                field: (field_identifier))
              arguments: (argument_list
                (interpreted_string_literal))))))

      (return_statement
        (expression_list
          (unary_expression
            operand: (int_literal))))))
)
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

توجه کنید که چگونه پارامترهای موجود در add وت subtract توابع متفاوت ارائه می شوند. به این دلیل است که GO به شما امکان می دهد پارامترهای گروهی را با همان نوع گروه بندی کنید ، و درخت نشانگر آن را به خوبی در درخت نحو منعکس می کند.


چاپ درخت نحو

در اینجا کد اصلی است که خروجی درخت نشسته فوق را چاپ می کند:

package main

import (
    "context"
    "fmt"
    "log"
    "os"

    tree_sitter "github.com/smacker/go-tree-sitter"
    "github.com/smacker/go-tree-sitter/golang"
)

func main() {
    parser := tree_sitter.NewParser()
    parser.SetLanguage(golang.GetLanguage())

    data, err := os.ReadFile("example.go")
    if err != nil {
        log.Fatal(err)
    }
    tree, err := parser.ParseCtx(context.Background(), nil, data)
    if err != nil {
        log.Fatal(err)
    }

    root := tree.RootNode()
    fmt.Println("Root type:", root.Type())
    fmt.Println("Tree:
", root.String())
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

توجه: کد منبع بالا بخشی از example.go پرونده


نام عملکرد پرس و جو

حال ، بیایید سعی کنیم تمام نام عملکردهای موجود در منبع را ضبط کنیم. برای انجام این کار ، ما یک پرس و جو ارائه می دهیم که مطابق با تمام نام عملکرد در درخت نحو باشد.

یک گره تابع به این شکل است:

(function_declaration
    name: (identifier)
    parameters: (parameter_list)
    result: (type_identifier)
    body: (block)
)
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

برای استخراج فقط نام عملکرد:

(function_declaration
    name: (identifier) @function-name
)
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

کد برای این پرس و جو:

package main

import (
    "context"
    "fmt"
    "log"
    "os"

    tree_sitter "github.com/smacker/go-tree-sitter"
    "github.com/smacker/go-tree-sitter/golang"
)

var (
    function_declarations_query = `
        (function_declaration
            name: (identifier) @function-name
        )
    `
)

func main() {
    parser := tree_sitter.NewParser()
    parser.SetLanguage(golang.GetLanguage())

    data, err := os.ReadFile("example.go")
    if err != nil {
        log.Fatal(err)
    }
    tree, err := parser.ParseCtx(context.Background(), nil, data)
    if err != nil {
        log.Fatal(err)
    }

    query, err := tree_sitter.NewQuery([]byte(function_declarations_query), golang.GetLanguage())
    if err != nil {
        log.Fatal(err)
    }

    cursor := tree_sitter.NewQueryCursor()
    cursor.Exec(query, tree.RootNode())

    for {
        match, more := cursor.NextMatch()
        if !more {
            break
        }

        for _, capture := range match.Captures {
            node := capture.Node
            fmt.Println("Found function:", node.Content(data))
        }
    }
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

خروجی:

$ go run main.go 
Found function: add
Found function: subtract
Found function: calculator
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید


پارامترهای پرس و جو نیز

برای چاپ پارامترها نیز ، پرس و جو را مانند این به روز کنید:

(function_declaration
    name: (identifier) @function-name
    parameters: (parameter_list) @parameter-list
)
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

ما فقط این را اضافه کردیم:
parameters: (parameter_list) @parameter-list

خروجی:

$ go run main.go 
Found function: add
Parameters list: (num1 int32, num2 int32)
Found function: subtract
Parameters list: (num1, num2 int32)
Found function: calculator
Parameters list: (operator string, num1, num2 int32)
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید


افکار نهایی

امیدوارم این به روشن شدن چگونگی استفاده از نمایش داده ها در درختان درخت برای تجزیه و تحلیل قسمت های مختلف کد کمک کند. من شما را تشویق می کنم تا با آزمایش با سؤالات مختلف و مشاهده خروجی ، بیشتر کاوش کنید. این بهترین راه برای درک چگونگی نشان دادن درخت نشانگر قسمتهای مختلف کد منبع با استفاده از گره های درخت نحوی است.


🔔 یادداشت مهم

قبل از اینکه ما بپیچیم – اگر شما کسی هستید که از هدر رفتن وقت در جستجوی و تلاش برای درک API های داخلی خسته شده اید ، ابزار AI را بررسی کنید زندگیبشر این یک ابزار بسیار مفید برای کار با API های داخلی در کد های بزرگ است.

LiveApi به شما کمک می کند کشف کردنبا درک کردنوت استفاده کردن API ها در زیرساخت های Big Tech.

زندگی API خود را با LiveApi راحت کنید.


نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا