From 40fc2f4d7a4813e0b2a561565a3450a9e4ac0291 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 1 Nov 2025 23:59:04 +0000 Subject: [PATCH 1/5] Initial plan From 09a6d1573e472e8148d4991be9ba3c1fcca24e36 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 2 Nov 2025 00:08:37 +0000 Subject: [PATCH 2/5] Add syntax-path->string and string->syntax-path functions Co-authored-by: jackfirth <8175575+jackfirth@users.noreply.github.com> --- private/syntax-path.rkt | 111 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) diff --git a/private/syntax-path.rkt b/private/syntax-path.rkt index 4309838b..0e0ef73c 100644 --- a/private/syntax-path.rkt +++ b/private/syntax-path.rkt @@ -20,6 +20,8 @@ [syntax-path-add (-> syntax-path? exact-nonnegative-integer? syntax-path?)] [syntax-path-remove-prefix (-> syntax-path? syntax-path? syntax-path?)] [syntax-path-neighbors? (-> syntax-path? syntax-path? boolean?)] + [syntax-path->string (-> syntax-path? string?)] + [string->syntax-path (-> string? syntax-path?)] [syntax-ref (-> syntax? syntax-path? syntax?)] [syntax-set (-> syntax? syntax-path? syntax? syntax?)] [syntax-remove-splice @@ -37,6 +39,7 @@ data/order guard racket/sequence + racket/string racket/struct racket/treelist racket/list @@ -214,6 +217,114 @@ (equal? leading (sub1 trailing)))) +(define (syntax-path->string path) + (if (empty-syntax-path? path) + "/" + (string-append + "/" + (string-join + (for/list ([elem (in-treelist (syntax-path-elements path))]) + (number->string elem)) + "/")))) + + +(module+ test + (test-case "syntax-path->string" + (test-case "empty path" + (check-equal? (syntax-path->string empty-syntax-path) "/")) + + (test-case "single element" + (check-equal? (syntax-path->string (syntax-path (list 0))) "/0")) + + (test-case "multiple elements" + (check-equal? (syntax-path->string (syntax-path (list 0 1 2))) "/0/1/2")) + + (test-case "large numbers" + (check-equal? (syntax-path->string (syntax-path (list 42 99 1000))) "/42/99/1000")))) + + +(define (string->syntax-path str) + (unless (string? str) + (raise-argument-error 'string->syntax-path "string?" str)) + (unless (string-prefix? str "/") + (raise-arguments-error + 'string->syntax-path + "syntax path string must start with /" + "given" str)) + (when (and (> (string-length str) 1) (string-suffix? str "/")) + (raise-arguments-error + 'string->syntax-path + "syntax path string must not end with / (except for root path)" + "given" str)) + (if (equal? str "/") + empty-syntax-path + (let* ([parts (string-split (substring str 1) "/")] + [numbers (for/list ([part (in-list parts)]) + (define num (string->number part)) + (unless (and num (exact-nonnegative-integer? num)) + (raise-arguments-error + 'string->syntax-path + "syntax path string contains invalid element (must be non-negative integer)" + "given" str + "invalid element" part)) + num)]) + (syntax-path numbers)))) + + +(module+ test + (test-case "string->syntax-path" + (test-case "empty path" + (check-equal? (string->syntax-path "/") empty-syntax-path)) + + (test-case "single element" + (check-equal? (string->syntax-path "/0") (syntax-path (list 0)))) + + (test-case "multiple elements" + (check-equal? (string->syntax-path "/0/1/2") (syntax-path (list 0 1 2)))) + + (test-case "large numbers" + (check-equal? (string->syntax-path "/42/99/1000") (syntax-path (list 42 99 1000)))) + + (test-case "error on missing leading slash" + (check-exn exn:fail:contract? + (λ () (string->syntax-path "0/1/2")))) + + (test-case "error on trailing slash" + (check-exn exn:fail:contract? + (λ () (string->syntax-path "/0/1/")))) + + (test-case "error on invalid element" + (check-exn exn:fail:contract? + (λ () (string->syntax-path "/0/abc/2")))) + + (test-case "error on negative number" + (check-exn exn:fail:contract? + (λ () (string->syntax-path "/0/-1/2")))) + + (test-case "error on float" + (check-exn exn:fail:contract? + (λ () (string->syntax-path "/0/1.5/2")))))) + + +(module+ test + (test-case "round-trip conversion" + (test-case "empty path" + (check-equal? (string->syntax-path (syntax-path->string empty-syntax-path)) + empty-syntax-path)) + + (test-case "single element path" + (define path (syntax-path (list 5))) + (check-equal? (string->syntax-path (syntax-path->string path)) path)) + + (test-case "multiple element path" + (define path (syntax-path (list 1 2 3 4 5))) + (check-equal? (string->syntax-path (syntax-path->string path)) path)) + + (test-case "path with large numbers" + (define path (syntax-path (list 0 100 999 1234567))) + (check-equal? (string->syntax-path (syntax-path->string path)) path)))) + + (define (syntax-ref init-stx path) (define result (for/fold ([stx init-stx]) From 2045eee606892ba4ae097e62717a39d039b77f15 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 2 Nov 2025 00:10:16 +0000 Subject: [PATCH 3/5] Address code review feedback Co-authored-by: jackfirth <8175575+jackfirth@users.noreply.github.com> --- private/syntax-path.rkt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/private/syntax-path.rkt b/private/syntax-path.rkt index 0e0ef73c..641dced7 100644 --- a/private/syntax-path.rkt +++ b/private/syntax-path.rkt @@ -244,8 +244,6 @@ (define (string->syntax-path str) - (unless (string? str) - (raise-argument-error 'string->syntax-path "string?" str)) (unless (string-prefix? str "/") (raise-arguments-error 'string->syntax-path @@ -264,7 +262,7 @@ (unless (and num (exact-nonnegative-integer? num)) (raise-arguments-error 'string->syntax-path - "syntax path string contains invalid element (must be non-negative integer)" + "syntax path string contains invalid element (must be nonnegative integer)" "given" str "invalid element" part)) num)]) From 6d4cb05e6beacf2f132681ef4b207abba12bf057 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 2 Nov 2025 00:37:05 +0000 Subject: [PATCH 4/5] Simplify syntax-path->string and enhance error test assertions Co-authored-by: jackfirth <8175575+jackfirth@users.noreply.github.com> --- private/syntax-path.rkt | 71 ++++++++++++++++++++++++++++++----------- 1 file changed, 53 insertions(+), 18 deletions(-) diff --git a/private/syntax-path.rkt b/private/syntax-path.rkt index 641dced7..7272a445 100644 --- a/private/syntax-path.rkt +++ b/private/syntax-path.rkt @@ -218,14 +218,11 @@ (define (syntax-path->string path) - (if (empty-syntax-path? path) - "/" - (string-append - "/" - (string-join - (for/list ([elem (in-treelist (syntax-path-elements path))]) - (number->string elem)) - "/")))) + (string-join + (for/list ([elem (in-treelist (syntax-path-elements path))]) + (number->string elem)) + "/" + #:before-first "/")) (module+ test @@ -284,24 +281,62 @@ (check-equal? (string->syntax-path "/42/99/1000") (syntax-path (list 42 99 1000)))) (test-case "error on missing leading slash" - (check-exn exn:fail:contract? - (λ () (string->syntax-path "0/1/2")))) + (define input "0/1/2") + (define thrown + (with-handlers ([(λ (_) #true) values]) + (string->syntax-path input) + #false)) + (check-pred exn:fail:contract? thrown) + (check-regexp-match #rx"string->syntax-path:" (exn-message thrown)) + (check-regexp-match #rx"given: \"0/1/2\"" (exn-message thrown)) + (check-regexp-match #rx"syntax path string must start with /" (exn-message thrown))) (test-case "error on trailing slash" - (check-exn exn:fail:contract? - (λ () (string->syntax-path "/0/1/")))) + (define input "/0/1/") + (define thrown + (with-handlers ([(λ (_) #true) values]) + (string->syntax-path input) + #false)) + (check-pred exn:fail:contract? thrown) + (check-regexp-match #rx"string->syntax-path:" (exn-message thrown)) + (check-regexp-match #rx"given: \"/0/1/\"" (exn-message thrown)) + (check-regexp-match #rx"syntax path string must not end with /" (exn-message thrown))) (test-case "error on invalid element" - (check-exn exn:fail:contract? - (λ () (string->syntax-path "/0/abc/2")))) + (define input "/0/abc/2") + (define thrown + (with-handlers ([(λ (_) #true) values]) + (string->syntax-path input) + #false)) + (check-pred exn:fail:contract? thrown) + (check-regexp-match #rx"string->syntax-path:" (exn-message thrown)) + (check-regexp-match #rx"given: \"/0/abc/2\"" (exn-message thrown)) + (check-regexp-match #rx"syntax path string contains invalid element" (exn-message thrown)) + (check-regexp-match #rx"invalid element: \"abc\"" (exn-message thrown))) (test-case "error on negative number" - (check-exn exn:fail:contract? - (λ () (string->syntax-path "/0/-1/2")))) + (define input "/0/-1/2") + (define thrown + (with-handlers ([(λ (_) #true) values]) + (string->syntax-path input) + #false)) + (check-pred exn:fail:contract? thrown) + (check-regexp-match #rx"string->syntax-path:" (exn-message thrown)) + (check-regexp-match #rx"given: \"/0/-1/2\"" (exn-message thrown)) + (check-regexp-match #rx"syntax path string contains invalid element" (exn-message thrown)) + (check-regexp-match #rx"invalid element: \"-1\"" (exn-message thrown))) (test-case "error on float" - (check-exn exn:fail:contract? - (λ () (string->syntax-path "/0/1.5/2")))))) + (define input "/0/1.5/2") + (define thrown + (with-handlers ([(λ (_) #true) values]) + (string->syntax-path input) + #false)) + (check-pred exn:fail:contract? thrown) + (check-regexp-match #rx"string->syntax-path:" (exn-message thrown)) + (check-regexp-match #rx"given: \"/0/1.5/2\"" (exn-message thrown)) + (check-regexp-match #rx"syntax path string contains invalid element" (exn-message thrown)) + (check-regexp-match #rx"invalid element: \"1.5\"" (exn-message thrown))))) (module+ test From 47718c48ae932feab972d0982b2254b577bf9654 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 2 Nov 2025 00:53:07 +0000 Subject: [PATCH 5/5] Change syntax-path->string to return immutable-string? Co-authored-by: jackfirth <8175575+jackfirth@users.noreply.github.com> --- private/syntax-path.rkt | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/private/syntax-path.rkt b/private/syntax-path.rkt index 7272a445..428882f5 100644 --- a/private/syntax-path.rkt +++ b/private/syntax-path.rkt @@ -20,7 +20,7 @@ [syntax-path-add (-> syntax-path? exact-nonnegative-integer? syntax-path?)] [syntax-path-remove-prefix (-> syntax-path? syntax-path? syntax-path?)] [syntax-path-neighbors? (-> syntax-path? syntax-path? boolean?)] - [syntax-path->string (-> syntax-path? string?)] + [syntax-path->string (-> syntax-path? immutable-string?)] [string->syntax-path (-> string? syntax-path?)] [syntax-ref (-> syntax? syntax-path? syntax?)] [syntax-set (-> syntax? syntax-path? syntax? syntax?)] @@ -38,6 +38,7 @@ racket/syntax) data/order guard + racket/mutability racket/sequence racket/string racket/struct @@ -218,11 +219,12 @@ (define (syntax-path->string path) - (string-join - (for/list ([elem (in-treelist (syntax-path-elements path))]) - (number->string elem)) - "/" - #:before-first "/")) + (string->immutable-string + (string-join + (for/list ([elem (in-treelist (syntax-path-elements path))]) + (number->string elem)) + "/" + #:before-first "/"))) (module+ test