Hi All,
I am experimenting with adding a new pass to the Skething language.
Instead of
read -> expand -> compile -> evaluate
I want to add an adjust
pass:
read -> adjust -> expand -> compile -> evaluate
It's relatively simple to do:
(module reader syntax/module-reader
sketching/main
#:read read-sketching
#:read-syntax read-sketching-syntax
(define (read-sketching [in (current-input-port)])
(adjust (read in)))
(define (read-sketching-syntax [source-name (object-name (current-input-port))]
[in (current-input-port)])
(adjust (read-syntax source-name in)))
Now the goal of adjust
is to detect id[expr ...]
in the source and rewrite it to (ref id expr ...)
.
Whitespace is not allowed between the identifier and the expression.
Using the source location information and the paren-shape
syntax property is was pretty simple to detect this situation.
My current problem is that I have run into the following error:
; #%top: identifier's binding is ambiguous
; in: #%top
; context...:
; #(496045 module) #(496048 module reader) #(497571 module)
; #(497574 module reader) #(520923 module)
; #(520931 module test-sketching-reader) #(521041 local)
; #(521042 intdef)
; matching binding...:
; #(sketching-top #<module-path-index:"exports-no-gui.rkt" "exports-all.rkt" sketching/main> 0)
; #(520923 module) #(520931 module test-sketching-reader)
; matching binding...:
; #(#%top #<module-path-index:'#%core> 0)
; #(496045 module) #(496048 module reader) #(497571 module)
; #(497574 module reader)
; matching binding...:
; #(#%top #<module-path-index:'#%core> 0)
; #(496045 module) #(496048 module reader)
; Context (plain; to see better errortrace context, re-run with C-u prefix):
; /Users/soegaard/.emacs.d/elpa/racket-mode-20211018.1717/racket/syntax.rkt:66:0
The definition of adjust
is as follows:
(require racket/runtime-path racket/syntax
(except-in syntax/parse char))
(define (adjust stx)
(syntax-parse stx
[(a . d) (adjust-dotted-list stx)]
[_ stx]))
(define (ref . xs) ; placeholder
(cons 'ref xs))
(define (adjust-dotted-list stx)
(syntax-parse stx
[(id:id (~and [e:expr ...] brackets) . more)
(cond
[(and (eqv? (syntax-property #'brackets 'paren-shape) #\[)
(= (+ (syntax-position #'id) (syntax-span #'id))
(syntax-position #'brackets)))
(with-syntax ([adjusted-more (adjust #'more)])
(syntax/loc #'id
(ref (id e ...) . adjusted-more)))]
[else
(with-syntax* ([(_ . rest) stx]
[adjusted-rest (adjust-dotted-list #'rest)])
(syntax/loc stx
(id . adjusted-rest)))])]
[(a . more)
(with-syntax ([adjusted-more (adjust #'more)])
(syntax/loc stx
(a . adjusted-more)))]
[_
(raise-syntax-error 'adjust-dotted-list "expected a dotted list" stx)]))
Where should I look?
It's relevant to mention that #lang sketching
uses sketching-top
as #%top
.
It is defined in [1].
The full source:
(module reader syntax/module-reader
; 1. Module path of the language.
sketching/main
; The module path `sketching/main` is used in the language position
; of read modules. That is, reading `#lang sketching` will produce a
; module with `sketching/main` as language.
; 2. Reader options (#:read, #:read-syntax, etc. ...)
; Note: When #:read and #:read-syntax are used, they both need to be supplied.
#:read read-sketching
#:read-syntax read-sketching-syntax
; 3. Forms as in the body of racket/base
; After standard reading, we will rewrite
; id[expr ...]
; to
; (#%ref id expr ...).
; We will use this to index to vectors, strings and hash tables.
(define (read-sketching [in (current-input-port)])
(adjust (read in)))
(define (read-sketching-syntax [source-name (object-name (current-input-port))]
[in (current-input-port)])
(adjust (read-syntax source-name in)))
; Since adjust is called after reading, we are essentially working with
; three passes.
; - read-syntax
; - adjust
; - expand
; Let's define our `adjust` pass.
(require racket/runtime-path racket/syntax
(except-in syntax/parse char))
; (require (only-in sketching/main #%top))
(define (read-string str #:source-name [source-name #f])
(define in (open-input-string str))
; (port-count-lines! in)
(read-syntax source-name in))
(define (adjust stx)
(syntax-parse stx
[(a . d) (adjust-dotted-list stx)]
[_ stx]))
(define (ref . xs) ; placeholder
(cons 'ref xs))
(define (adjust-dotted-list stx)
(syntax-parse stx
[(id:id (~and [e:expr ...] brackets) . more)
(cond
[(and (eqv? (syntax-property #'brackets 'paren-shape) #\[)
(= (+ (syntax-position #'id) (syntax-span #'id))
(syntax-position #'brackets)))
(with-syntax ([adjusted-more (adjust #'more)])
(syntax/loc #'id
(ref (id e ...) . adjusted-more)))]
[else
(with-syntax* ([(_ . rest) stx]
[adjusted-rest (adjust-dotted-list #'rest)])
(syntax/loc stx
(id . adjusted-rest)))])]
[(a . more)
(with-syntax ([adjusted-more (adjust #'more)])
(syntax/loc stx
(a . adjusted-more)))]
[_
(raise-syntax-error 'adjust-dotted-list "expected a dotted list" stx)]))
; > (displayln (adjust (read-string "(foo[bar])")))
; #<syntax:string::2 (ref (foo bar))>
; (displayln (adjust (read-string "(foo [bar])")))
; > #<syntax:string::1 (foo (bar))>
)
And the definition of sketching-top
is here:
[1] sketching/exports-no-gui.rkt at main · soegaard/sketching · GitHub