better racket example support
This commit is contained in:
@@ -612,4 +612,117 @@ async fn test_racket_search() {
|
|||||||
.filter_map(|m| m.captures.get("name").map(|s| s.as_str()))
|
.filter_map(|m| m.captures.get("name").map(|s| s.as_str()))
|
||||||
.collect();
|
.collect();
|
||||||
assert!(names.contains(&"greet"));
|
assert!(names.contains(&"greet"));
|
||||||
|
assert!(names.contains(&"add"));
|
||||||
|
assert!(names.contains(&"factorial"));
|
||||||
|
assert!(names.contains(&"person-greet"));
|
||||||
|
assert!(names.contains(&"describe-list"));
|
||||||
|
assert!(names.contains(&"sum-squares"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_racket_structs() {
|
||||||
|
// Get the workspace root (where Cargo.toml is)
|
||||||
|
let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap();
|
||||||
|
let workspace_root = std::path::Path::new(&manifest_dir)
|
||||||
|
.parent()
|
||||||
|
.and_then(|p| p.parent())
|
||||||
|
.unwrap();
|
||||||
|
let test_code_path = workspace_root.join("examples/test_code");
|
||||||
|
|
||||||
|
let request = CodeSearchRequest {
|
||||||
|
searches: vec![SearchSpec {
|
||||||
|
name: "racket_structs".to_string(),
|
||||||
|
query: r#"(list . (symbol) @kw (#eq? @kw "struct") . (symbol) @name)"#.to_string(),
|
||||||
|
language: "racket".to_string(),
|
||||||
|
paths: vec![test_code_path.to_string_lossy().to_string()],
|
||||||
|
context_lines: 0,
|
||||||
|
}],
|
||||||
|
max_concurrency: 4,
|
||||||
|
max_matches_per_search: 500,
|
||||||
|
};
|
||||||
|
|
||||||
|
let response = execute_code_search(request).await.unwrap();
|
||||||
|
assert_eq!(response.searches.len(), 1);
|
||||||
|
assert!(response.searches[0].matches.len() > 0);
|
||||||
|
|
||||||
|
// Should find person and point structs
|
||||||
|
let names: Vec<&str> = response.searches[0]
|
||||||
|
.matches
|
||||||
|
.iter()
|
||||||
|
.filter_map(|m| m.captures.get("name").map(|s| s.as_str()))
|
||||||
|
.collect();
|
||||||
|
assert!(names.contains(&"person"), "Should find 'person' struct, found: {:?}", names);
|
||||||
|
assert!(names.contains(&"point"), "Should find 'point' struct, found: {:?}", names);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_racket_macros() {
|
||||||
|
// Get the workspace root (where Cargo.toml is)
|
||||||
|
let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap();
|
||||||
|
let workspace_root = std::path::Path::new(&manifest_dir)
|
||||||
|
.parent()
|
||||||
|
.and_then(|p| p.parent())
|
||||||
|
.unwrap();
|
||||||
|
let test_code_path = workspace_root.join("examples/test_code");
|
||||||
|
|
||||||
|
let request = CodeSearchRequest {
|
||||||
|
searches: vec![SearchSpec {
|
||||||
|
name: "racket_macros".to_string(),
|
||||||
|
query: r#"(list . (symbol) @kw (#eq? @kw "define-syntax-rule") . (list . (symbol) @name))"#.to_string(),
|
||||||
|
language: "racket".to_string(),
|
||||||
|
paths: vec![test_code_path.to_string_lossy().to_string()],
|
||||||
|
context_lines: 0,
|
||||||
|
}],
|
||||||
|
max_concurrency: 4,
|
||||||
|
max_matches_per_search: 500,
|
||||||
|
};
|
||||||
|
|
||||||
|
let response = execute_code_search(request).await.unwrap();
|
||||||
|
assert_eq!(response.searches.len(), 1);
|
||||||
|
assert!(response.searches[0].matches.len() > 0, "Should find macros, error: {:?}", response.searches[0].error);
|
||||||
|
|
||||||
|
// Should find swap! and unless macros
|
||||||
|
let names: Vec<&str> = response.searches[0]
|
||||||
|
.matches
|
||||||
|
.iter()
|
||||||
|
.filter_map(|m| m.captures.get("name").map(|s| s.as_str()))
|
||||||
|
.collect();
|
||||||
|
assert!(names.contains(&"swap!"), "Should find 'swap!' macro, found: {:?}", names);
|
||||||
|
assert!(names.contains(&"unless"), "Should find 'unless' macro, found: {:?}", names);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_racket_contracts() {
|
||||||
|
// Get the workspace root (where Cargo.toml is)
|
||||||
|
let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap();
|
||||||
|
let workspace_root = std::path::Path::new(&manifest_dir)
|
||||||
|
.parent()
|
||||||
|
.and_then(|p| p.parent())
|
||||||
|
.unwrap();
|
||||||
|
let test_code_path = workspace_root.join("examples/test_code");
|
||||||
|
|
||||||
|
let request = CodeSearchRequest {
|
||||||
|
searches: vec![SearchSpec {
|
||||||
|
name: "racket_contracts".to_string(),
|
||||||
|
query: r#"(list . (symbol) @kw (#eq? @kw "define/contract") . (list . (symbol) @name))"#.to_string(),
|
||||||
|
language: "racket".to_string(),
|
||||||
|
paths: vec![test_code_path.to_string_lossy().to_string()],
|
||||||
|
context_lines: 0,
|
||||||
|
}],
|
||||||
|
max_concurrency: 4,
|
||||||
|
max_matches_per_search: 500,
|
||||||
|
};
|
||||||
|
|
||||||
|
let response = execute_code_search(request).await.unwrap();
|
||||||
|
assert_eq!(response.searches.len(), 1);
|
||||||
|
assert!(response.searches[0].matches.len() > 0, "Should find contract functions, error: {:?}", response.searches[0].error);
|
||||||
|
|
||||||
|
// Should find safe-divide and non-negative-add
|
||||||
|
let names: Vec<&str> = response.searches[0]
|
||||||
|
.matches
|
||||||
|
.iter()
|
||||||
|
.filter_map(|m| m.captures.get("name").map(|s| s.as_str()))
|
||||||
|
.collect();
|
||||||
|
assert!(names.contains(&"safe-divide"), "Should find 'safe-divide', found: {:?}", names);
|
||||||
|
assert!(names.contains(&"non-negative-add"), "Should find 'non-negative-add', found: {:?}", names);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,8 @@ g3 includes a syntax-aware code search tool powered by tree-sitter. Unlike text-
|
|||||||
- Java
|
- Java
|
||||||
- C
|
- C
|
||||||
- C++
|
- C++
|
||||||
- Kotlin
|
- Racket
|
||||||
|
- Scheme
|
||||||
|
|
||||||
## Basic Usage
|
## Basic Usage
|
||||||
|
|
||||||
@@ -231,6 +232,65 @@ Tree-sitter queries use S-expression syntax. The basic pattern is:
|
|||||||
(namespace_definition name: (identifier) @name)
|
(namespace_definition name: (identifier) @name)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Racket
|
||||||
|
|
||||||
|
Racket uses an S-expression grammar where code is represented as nested lists. The tree-sitter-racket parser represents most forms as `(list (symbol) ...)` nodes.
|
||||||
|
|
||||||
|
```lisp
|
||||||
|
;; Function definitions: (define (name args...) body)
|
||||||
|
(list . (symbol) @kw (#eq? @kw "define") . (list . (symbol) @name))
|
||||||
|
|
||||||
|
;; Variable definitions: (define name value)
|
||||||
|
(list . (symbol) @kw (#eq? @kw "define") . (symbol) @name)
|
||||||
|
|
||||||
|
;; Struct definitions: (struct name (fields...))
|
||||||
|
(list . (symbol) @kw (#eq? @kw "struct") . (symbol) @name)
|
||||||
|
|
||||||
|
;; Lambda expressions
|
||||||
|
(list . (symbol) @kw (#match? @kw "^(lambda|λ)$"))
|
||||||
|
|
||||||
|
;; Let bindings
|
||||||
|
(list . (symbol) @kw (#match? @kw "^(let|let\\*|letrec)$"))
|
||||||
|
|
||||||
|
;; Require statements
|
||||||
|
(list . (symbol) @kw (#eq? @kw "require"))
|
||||||
|
|
||||||
|
;; Provide statements
|
||||||
|
(list . (symbol) @kw (#eq? @kw "provide"))
|
||||||
|
|
||||||
|
;; Module definitions
|
||||||
|
(list . (symbol) @kw (#match? @kw "^module"))
|
||||||
|
|
||||||
|
;; Contracts
|
||||||
|
(list . (symbol) @kw (#eq? @kw "define/contract") . (list . (symbol) @name))
|
||||||
|
|
||||||
|
;; Macros
|
||||||
|
(list . (symbol) @kw (#match? @kw "^(define-syntax|define-syntax-rule)$") . (symbol) @name)
|
||||||
|
|
||||||
|
;; For loops
|
||||||
|
(list . (symbol) @kw (#match? @kw "^for"))
|
||||||
|
|
||||||
|
;; Match expressions
|
||||||
|
(list . (symbol) @kw (#eq? @kw "match"))
|
||||||
|
|
||||||
|
;; Class definitions
|
||||||
|
(list . (symbol) @kw (#match? @kw "^class"))
|
||||||
|
```
|
||||||
|
|
||||||
|
**Note**: The `.` (dot) in queries like `(list . (symbol))` means "first child" - it matches the symbol that appears immediately after the opening parenthesis.
|
||||||
|
|
||||||
|
### Scheme
|
||||||
|
|
||||||
|
Scheme uses similar patterns to Racket:
|
||||||
|
|
||||||
|
```lisp
|
||||||
|
;; Function definitions
|
||||||
|
(list . (symbol) @kw (#eq? @kw "define") . (list . (symbol) @name))
|
||||||
|
|
||||||
|
;; Lambda expressions
|
||||||
|
(list . (symbol) @kw (#eq? @kw "lambda"))
|
||||||
|
```
|
||||||
|
|
||||||
## Advanced Queries
|
## Advanced Queries
|
||||||
|
|
||||||
### Wildcards
|
### Wildcards
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
#lang racket
|
#lang racket
|
||||||
|
|
||||||
|
;; ============================================
|
||||||
|
;; Example Racket file for g3 code search tests
|
||||||
|
;; ============================================
|
||||||
|
|
||||||
|
;; --- Basic Functions ---
|
||||||
|
|
||||||
(define (greet name)
|
(define (greet name)
|
||||||
(printf "Hello, ~a!\n" name))
|
(printf "Hello, ~a!\n" name))
|
||||||
|
|
||||||
@@ -11,14 +17,116 @@
|
|||||||
1
|
1
|
||||||
(* n (factorial (- n 1)))))
|
(* n (factorial (- n 1)))))
|
||||||
|
|
||||||
|
;; --- Variable Definitions ---
|
||||||
|
|
||||||
|
(define pi 3.14159)
|
||||||
|
(define greeting "Hello, World!")
|
||||||
|
|
||||||
|
;; --- Structs ---
|
||||||
|
|
||||||
(struct person (name age) #:transparent)
|
(struct person (name age) #:transparent)
|
||||||
|
(struct point (x y) #:transparent)
|
||||||
|
|
||||||
(define (person-greet p)
|
(define (person-greet p)
|
||||||
(printf "Hello, I'm ~a\n" (person-name p)))
|
(printf "Hello, I'm ~a\n" (person-name p)))
|
||||||
|
|
||||||
|
;; --- Pattern Matching ---
|
||||||
|
|
||||||
|
(define (describe-list lst)
|
||||||
|
(match lst
|
||||||
|
['() "empty"]
|
||||||
|
[(list x) (format "singleton: ~a" x)]
|
||||||
|
[(list x y) (format "pair: ~a, ~a" x y)]
|
||||||
|
[_ "many elements"]))
|
||||||
|
|
||||||
|
(define (point-quadrant p)
|
||||||
|
(match p
|
||||||
|
[(point (? positive?) (? positive?)) 'first]
|
||||||
|
[(point (? negative?) (? positive?)) 'second]
|
||||||
|
[(point (? negative?) (? negative?)) 'third]
|
||||||
|
[(point (? positive?) (? negative?)) 'fourth]
|
||||||
|
[_ 'origin-or-axis]))
|
||||||
|
|
||||||
|
;; --- Lambda and Higher-Order Functions ---
|
||||||
|
|
||||||
|
(define double (lambda (x) (* x 2)))
|
||||||
|
(define triple (λ (x) (* x 3)))
|
||||||
|
|
||||||
|
(define (apply-twice f x)
|
||||||
|
(f (f x)))
|
||||||
|
|
||||||
|
;; --- Let Bindings ---
|
||||||
|
|
||||||
|
(define (circle-area radius)
|
||||||
|
(let ([pi 3.14159])
|
||||||
|
(* pi radius radius)))
|
||||||
|
|
||||||
|
(define (swap-and-sum a b)
|
||||||
|
(let* ([temp a]
|
||||||
|
[a b]
|
||||||
|
[b temp])
|
||||||
|
(+ a b)))
|
||||||
|
|
||||||
|
;; --- For Loops ---
|
||||||
|
|
||||||
|
(define (sum-squares n)
|
||||||
|
(for/sum ([i (in-range 1 (add1 n))])
|
||||||
|
(* i i)))
|
||||||
|
|
||||||
|
(define (collect-evens n)
|
||||||
|
(for/list ([i (in-range n)]
|
||||||
|
#:when (even? i))
|
||||||
|
i))
|
||||||
|
|
||||||
|
(define (matrix-coords rows cols)
|
||||||
|
(for*/list ([r (in-range rows)]
|
||||||
|
[c (in-range cols)])
|
||||||
|
(cons r c)))
|
||||||
|
|
||||||
|
;; --- Macros ---
|
||||||
|
|
||||||
|
(define-syntax-rule (swap! x y)
|
||||||
|
(let ([tmp x])
|
||||||
|
(set! x y)
|
||||||
|
(set! y tmp)))
|
||||||
|
|
||||||
|
(define-syntax-rule (unless condition body ...)
|
||||||
|
(when (not condition)
|
||||||
|
body ...))
|
||||||
|
|
||||||
|
;; --- Contracts ---
|
||||||
|
|
||||||
|
(define/contract (safe-divide x y)
|
||||||
|
(-> number? (and/c number? (not/c zero?)) number?)
|
||||||
|
(/ x y))
|
||||||
|
|
||||||
|
(define/contract (non-negative-add a b)
|
||||||
|
(-> (>=/c 0) (>=/c 0) (>=/c 0))
|
||||||
|
(+ a b))
|
||||||
|
|
||||||
|
;; --- Require and Provide ---
|
||||||
|
|
||||||
|
(require racket/string)
|
||||||
|
(require racket/list)
|
||||||
|
|
||||||
|
;; --- Module ---
|
||||||
|
|
||||||
|
(module+ test
|
||||||
|
(require rackunit)
|
||||||
|
|
||||||
|
(check-equal? (add 2 3) 5)
|
||||||
|
(check-equal? (factorial 5) 120)
|
||||||
|
(check-equal? (sum-squares 3) 14)
|
||||||
|
(check-equal? (describe-list '()) "empty")
|
||||||
|
(check-equal? (describe-list '(1)) "singleton: 1"))
|
||||||
|
|
||||||
|
(module+ main
|
||||||
(greet "World")
|
(greet "World")
|
||||||
(displayln (add 5 3))
|
(displayln (add 5 3))
|
||||||
(displayln (factorial 5))
|
(displayln (factorial 5))
|
||||||
|
|
||||||
(define alice (person "Alice" 30))
|
(define alice (person "Alice" 30))
|
||||||
(person-greet alice)
|
(person-greet alice)
|
||||||
|
|
||||||
|
(displayln (sum-squares 10))
|
||||||
|
(displayln (collect-evens 10)))
|
||||||
|
|||||||
Reference in New Issue
Block a user