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()))
|
||||
.collect();
|
||||
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
|
||||
- C
|
||||
- C++
|
||||
- Kotlin
|
||||
- Racket
|
||||
- Scheme
|
||||
|
||||
## Basic Usage
|
||||
|
||||
@@ -231,6 +232,65 @@ Tree-sitter queries use S-expression syntax. The basic pattern is:
|
||||
(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
|
||||
|
||||
### Wildcards
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
#lang racket
|
||||
|
||||
;; ============================================
|
||||
;; Example Racket file for g3 code search tests
|
||||
;; ============================================
|
||||
|
||||
;; --- Basic Functions ---
|
||||
|
||||
(define (greet name)
|
||||
(printf "Hello, ~a!\n" name))
|
||||
|
||||
@@ -11,14 +17,116 @@
|
||||
1
|
||||
(* n (factorial (- n 1)))))
|
||||
|
||||
;; --- Variable Definitions ---
|
||||
|
||||
(define pi 3.14159)
|
||||
(define greeting "Hello, World!")
|
||||
|
||||
;; --- Structs ---
|
||||
|
||||
(struct person (name age) #:transparent)
|
||||
(struct point (x y) #:transparent)
|
||||
|
||||
(define (person-greet p)
|
||||
(printf "Hello, I'm ~a\n" (person-name p)))
|
||||
|
||||
(greet "World")
|
||||
(displayln (add 5 3))
|
||||
(displayln (factorial 5))
|
||||
;; --- Pattern Matching ---
|
||||
|
||||
(define alice (person "Alice" 30))
|
||||
(person-greet alice)
|
||||
(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")
|
||||
(displayln (add 5 3))
|
||||
(displayln (factorial 5))
|
||||
|
||||
(define alice (person "Alice" 30))
|
||||
(person-greet alice)
|
||||
|
||||
(displayln (sum-squares 10))
|
||||
(displayln (collect-evens 10)))
|
||||
|
||||
Reference in New Issue
Block a user