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

سلام به همه! من شیلندرا هستم.
در آخرین پست من-“درخت نشستن: از کد تا درخت نحو”-من در مورد چگونگی استفاده از درخت نشانگر برای تولید یک درخت نحوی برای یک زبان معین با استفاده از دستور زبان خود صحبت کردم.
در این پست ، ما یک قدم جلوتر خواهیم رفت و می بینیم که چگونه می توان از درخت نشسته برای ایجاد پرس و جو در متن منبع شما استفاده کرد تا بخش های مختلف کد را درک و تجزیه و تحلیل کند.
این یکی دیگر از ویژگی های بسیار جالب 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 راحت کنید.