Skip to content

Commit 6f02fb6

Browse files
committed
Make #lang resyntax/test produce more UTS
Part of #586. This gets a lot closer to proper UTS, but it's still missing some handling of `syntax-original?` and source locations. Multi-line code block sequences are also still handled wrong.
1 parent 248c1c8 commit 6f02fb6

File tree

3 files changed

+243
-21
lines changed

3 files changed

+243
-21
lines changed

private/syntax-replacement.rkt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,12 +104,12 @@
104104
[separator-after (in-list (rest separators))])
105105
(list* (list (inserted-string separator-after)) child-list piece-lists))]
106106
[(~or v:id v:boolean v:char v:keyword v:number v:regexp v:byte-regexp v:string v:bytes)
107-
(define content (syntax-property (attribute v) 'uts-atom-content))
107+
(define content (syntax-property (attribute v) 'uts-content))
108108
(unless content
109109
(raise-arguments-error
110110
'syntax-replacement-render-using-uts
111111
(string-append "cannot render as universal tagged syntax, atom does not contain"
112-
" 'uts-atom-content syntax property")
112+
" 'uts-content syntax property")
113113
"atom" this-syntax
114114
"replacement" replacement))
115115
(list (inserted-string content))]))

private/universal-tagged-syntax.rkt

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
#lang racket/base
2+
3+
4+
(require racket/contract/base)
5+
6+
7+
(provide
8+
(contract-out
9+
[check-universal-tagged-syntax (-> syntax? syntax?)]))
10+
11+
12+
(require racket/list
13+
racket/match
14+
racket/mutability
15+
racket/port)
16+
17+
18+
(module+ test
19+
(require (submod "..")
20+
racket/string
21+
rackunit
22+
resyntax/private/source))
23+
24+
25+
;@----------------------------------------------------------------------------------------------------
26+
27+
28+
; TODO: check source locations
29+
; TODO: add a variant that checks for original reader UTS, i.e. check originality, check uts property
30+
; strings match contents in sourceloc, check source names all match, check source locations
31+
(define (check-universal-tagged-syntax stx)
32+
(let loop ([stx stx] [top? #true])
33+
(match (syntax-e stx)
34+
[(? atom?)
35+
(define content (syntax-property stx 'uts-content))
36+
(unless content
37+
(raise-arguments-error 'check-universal-tagged-syntax
38+
"atom is missing a 'uts-content syntax property"
39+
"atom" stx))]
40+
[(list children ...)
41+
(when (empty? children)
42+
(raise-arguments-error 'check-universal-tagged-syntax
43+
"empty compound forms are not allowed"
44+
"form" stx))
45+
(define shape-tag (first children))
46+
(if top?
47+
(unless (equal? (syntax-e shape-tag) 'module)
48+
(raise-arguments-error 'check-universal-tagged-syntax
49+
"the only legal top-level compound form is (module ...)"
50+
"top-level form" stx))
51+
(unless (keyword? (syntax-e shape-tag))
52+
(raise-arguments-error
53+
'check-universal-tagged-syntax
54+
"every non-top-level compound form must start with a shape tag keyword"
55+
"form" stx)))
56+
(define separators (syntax-property shape-tag 'uts-separators))
57+
(unless separators
58+
(raise-arguments-error 'check-universal-tagged-syntax
59+
"shape tag is missing a 'uts-separators syntax property"
60+
"shape tag" shape-tag
61+
"form" stx))
62+
(for ([child (in-list (rest children))])
63+
(loop child #false))]
64+
[other
65+
(raise-arguments-error 'check-universal-tagged-syntax
66+
"every form must be either an atom or a proper list"
67+
"form" stx)]))
68+
stx)
69+
70+
71+
(define (atom? v)
72+
(or (symbol? v)
73+
(keyword? v)
74+
(number? v)
75+
(boolean? v)
76+
(char? v)
77+
(immutable-string? v)
78+
(immutable-bytes? v)
79+
(regexp? v)
80+
(pregexp? v)
81+
(byte-regexp? v)
82+
(byte-pregexp? v)))
83+
84+
85+
(define (write-universal-tagged-syntax stx)
86+
(match (syntax-e stx)
87+
[(? atom?)
88+
(write-string (syntax-property stx 'uts-content))
89+
(void)]
90+
[(list tag children ...)
91+
(define seps (syntax-property tag 'uts-separators))
92+
(write-string (first seps))
93+
(for ([child (in-list children)]
94+
[suffix (in-list (rest seps))])
95+
(write-universal-tagged-syntax child)
96+
(write-string suffix))]))
97+
98+
99+
(define (universal-tagged-syntax->string stx)
100+
(with-output-to-string (λ () (write-universal-tagged-syntax stx))))
101+
102+
103+
(module+ test
104+
(test-case "universal-tagged-syntax->string"
105+
(define src
106+
(string-source
107+
(string-join
108+
(list
109+
"#lang resyntax/test"
110+
"require: resyntax/default-recommendations boolean-shortcuts"
111+
""
112+
"header:"
113+
"- #lang racket"
114+
""
115+
"test: \"foo\""
116+
"- (and a (and b c))"
117+
"- (and a b c)")
118+
"\n"
119+
#:after-last "\n")))
120+
(define stx (source-read-syntax src))
121+
122+
(check-universal-tagged-syntax stx)
123+
(define written-form (universal-tagged-syntax->string stx))
124+
125+
(check-equal? written-form (string-source-contents src))))

test.rkt

Lines changed: 116 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,7 @@
1313

1414
(require (for-syntax racket/base
1515
racket/match
16-
racket/sequence
17-
resyntax/test/private/statement
18-
syntax/parse)
19-
racket/stxparam
16+
resyntax/test/private/statement)
2017
rackunit
2118
rebellion/base/comparator
2219
rebellion/base/range
@@ -257,6 +254,7 @@
257254
(require racket/list
258255
racket/syntax-srcloc
259256
resyntax/private/syntax-traversal
257+
resyntax/private/universal-tagged-syntax
260258
resyntax/test/private/grammar
261259
resyntax/test/private/tokenizer
262260
syntax/parse)
@@ -269,17 +267,30 @@
269267
(define (read-syntax source-name in)
270268
(define parse-tree (parse source-name (make-refactoring-test-tokenizer in)))
271269
(define cleaned-parse-tree
272-
(replace-option-identifiers-with-keywords
273-
(join-multiline-code-blocks
274-
(replace-grammar-tags-with-shape-tags parse-tree))))
270+
(add-uts-properties
271+
(replace-option-identifiers-with-keywords
272+
(join-multiline-code-blocks
273+
(replace-grammar-tags-with-shape-tags parse-tree)))))
275274
(define statements
276275
(syntax-parse cleaned-parse-tree
277276
[(#:program statement ...) (attribute statement)]))
278-
(define module-datum
279-
`(module refactoring-test resyntax/test
280-
,@statements))
277+
(define raw-module-id (datum->syntax #false 'module #false parse-tree))
278+
(define module-level-separators
279+
(append (list "" "#lang ") (make-list (length statements) "\n") (list "")))
280+
(define module-id (syntax-property raw-module-id 'uts-separators module-level-separators))
281+
(define raw-modname
282+
(datum->syntax #false (derive-module-name-from-source source-name) #false parse-tree))
283+
(define modname (syntax-property raw-modname 'uts-content ""))
284+
(define raw-prelude (datum->syntax #false 'resyntax/test #false parse-tree))
285+
(define prelude (syntax-property raw-prelude 'uts-content "resyntax/test"))
286+
(define module-datum (list* module-id modname prelude statements))
281287
(define whole-program-srcloc (syntax-srcloc cleaned-parse-tree))
282-
(datum->syntax #f module-datum whole-program-srcloc))
288+
(check-universal-tagged-syntax (datum->syntax #false module-datum whole-program-srcloc)))
289+
290+
291+
(define (derive-module-name-from-source source-name)
292+
; TODO: actually pick a symbol based on the source name instead of ignoring it.
293+
'refactoring-test)
283294

284295

285296
(define (replace-grammar-tags-with-shape-tags grammar-stx)
@@ -329,6 +340,99 @@
329340
#:parent-props-modifier (λ (stx) stx)))
330341

331342

343+
(define (add-uts-properties stx)
344+
(syntax-traverse stx
345+
#:datum-literals (require header test no-change-test analysis-test)
346+
347+
[:id
348+
(define as-string (symbol->string (syntax-e this-syntax)))
349+
(syntax-property this-syntax 'uts-content as-string)]
350+
351+
[((~and tag #:statement) require-id:require mod suite)
352+
(define tag-with-prop
353+
(syntax-property (attribute tag) 'uts-separators (list "" ": " " " "\n")))
354+
(define new-datum
355+
(list tag-with-prop
356+
(add-uts-properties (attribute require-id))
357+
(add-uts-properties (attribute mod))
358+
(add-uts-properties (attribute suite))))
359+
(datum->syntax #false new-datum this-syntax this-syntax)]
360+
361+
[((~and tag #:statement) header-id:header code)
362+
(define tag-with-prop
363+
(syntax-property (attribute tag) 'uts-separators (list "" ":\n" "")))
364+
(define new-datum
365+
(list tag-with-prop
366+
(add-uts-properties (attribute header-id))
367+
(add-uts-properties (attribute code))))
368+
(datum->syntax #false new-datum this-syntax this-syntax)]
369+
370+
[((~and tag #:statement) (~and test-id (~or test no-change-test analysis-test)) arg ...)
371+
(define separators (append (list "" ": " "\n") (make-list (length (attribute arg)) "")))
372+
(define tag-with-prop (syntax-property (attribute tag) 'uts-separators separators))
373+
(define new-datum
374+
(list* tag-with-prop
375+
(add-uts-properties (attribute test-id))
376+
(for/list ([arg-stx (in-list (attribute arg))])
377+
(add-uts-properties arg-stx))))
378+
(datum->syntax #false new-datum this-syntax this-syntax)]
379+
380+
[((~and tag #:code-line) code:str)
381+
(define tag-with-prop (syntax-property (attribute tag) 'uts-separators (list "- " "")))
382+
(define code-with-prop
383+
(syntax-property (attribute code) 'uts-content (syntax-e (attribute code))))
384+
(datum->syntax #false (list tag-with-prop code-with-prop) this-syntax this-syntax)]
385+
386+
[((~and tag #:code-block) code:str)
387+
(define dash-line "--------------------\n")
388+
(define tag-with-prop
389+
(syntax-property (attribute tag) 'uts-separators (list dash-line dash-line)))
390+
(define code-with-prop
391+
(syntax-property (attribute code) 'uts-content (syntax-e (attribute code))))
392+
(datum->syntax #false (list tag-with-prop code-with-prop) this-syntax this-syntax)]
393+
394+
[((~and tag #:option) option:keyword expr)
395+
(define expr-ends-in-newline?
396+
(syntax-parse (attribute expr)
397+
[((~or #:code-line #:code-block) _ ...) #true]
398+
[_ #false]))
399+
(define separators (list "@" "" (if expr-ends-in-newline? "" "\n")))
400+
(define tag-with-prop (syntax-property (attribute tag) 'uts-separators separators))
401+
(define option-stx (attribute option))
402+
(define option-as-string (keyword->string (syntax-e option-stx)))
403+
(define option-with-prop (syntax-property option-stx 'uts-content option-as-string))
404+
(define new-datum (list tag-with-prop option-with-prop (add-uts-properties (attribute expr))))
405+
(datum->syntax #false new-datum this-syntax this-syntax)]
406+
407+
[((~and tag #:range-set) line-range ...)
408+
(define separators
409+
(append (list "")
410+
(make-list (sub1 (length (attribute line-range))) ", ")
411+
(list "")))
412+
(define tag-with-prop (syntax-property (attribute tag) 'uts-separators separators))
413+
(define new-datum
414+
(cons tag-with-prop
415+
(for/list ([line-range-stx (in-list (attribute line-range))])
416+
(add-uts-properties line-range-stx))))
417+
(datum->syntax #false new-datum this-syntax this-syntax)]
418+
419+
[((~and tag #:line-range) first last)
420+
(define tag-with-prop (syntax-property (attribute tag) 'uts-separators (list "" ".." "")))
421+
(define new-datum
422+
(list tag-with-prop
423+
(add-uts-properties (attribute first))
424+
(add-uts-properties (attribute last))))
425+
(datum->syntax #false new-datum this-syntax this-syntax)]
426+
427+
[(~or :str :number)
428+
(define as-string (format "~v" (syntax-e this-syntax)))
429+
(syntax-property this-syntax 'uts-content as-string)]
430+
431+
#:parent-context-modifier (λ (stx) stx)
432+
#:parent-srcloc-modifier (λ (stx) stx)
433+
#:parent-props-modifier (λ (stx) stx)))
434+
435+
332436
(define (read-using-syntax-reader syntax-reader in)
333437
(syntax->datum (syntax-reader #false in)))
334438

@@ -372,15 +476,8 @@
372476
#:do [(define code-strings (map syntax-e (attribute code)))]
373477
#:when (for/and ([s (in-list code-strings)])
374478
(string-with-one-newline-at-end? s))
375-
#:with statement-tag-with-seps
376-
(syntax-property (attribute statement-tag)
377-
'uts-separators
378-
(list* "" ": " "\n" (make-list (length (attribute code)) "")))
379479
#:with code-line-with-seps (syntax-property #'#:code-line 'uts-separators (list "- " ""))
380-
#:with (replacement-code ...)
381-
(for/list ([code-stx (in-list (attribute code))])
382-
(syntax-property code-stx 'uts-atom-content (syntax-e code-stx)))
383-
(statement-tag-with-seps test-id test-name (code-line-with-seps replacement-code) ...))
480+
(statement-tag test-id test-name (code-line-with-seps code) ...))
384481

385482

386483
(define (string-with-one-newline-at-end? s)

0 commit comments

Comments
 (0)