(module label mzscheme
(require (lib "contract.ss"))
(require (lib "etc.ss"))
(define-struct label (datum i j) (make-inspector))
(define (label-element? obj) #t)
(define label-element-equal? equal?)
(provide label?)
(provide/contract (rename ext:make-label make-label
(-> label-element? label?)))
(provide/contract (label-element? (-> any/c boolean?)))
(provide/contract (label-element-equal?
(-> label-element? label-element? boolean?)))
(provide/contract (string->label (-> string? label?)))
(provide/contract (string->label/with-sentinel (-> string? label?)))
(provide/contract (vector->label (-> vector? label?)))
(provide/contract (vector->label/with-sentinel (-> vector? label?)))
(provide/contract (label->string (-> label? string?)))
(provide/contract (label->vector (-> label? vector?)))
(provide/contract (label-length (-> label? natural-number/c)))
(provide/contract (label-ref (-> label? natural-number/c label-element?)))
(provide/contract
(sublabel (case->
(-> label? natural-number/c natural-number/c label?)
(-> label? natural-number/c label?))))
(provide/contract
(sublabel! (case->
(-> label? natural-number/c natural-number/c void?)
(-> label? natural-number/c void?))))
(provide/contract (label-prefix? (-> label? label? boolean?)))
(provide/contract (label-equal? (-> label? label? boolean?)))
(provide/contract (label-empty? (-> label? boolean?)))
(provide/contract (label-copy (-> label? label?)))
(provide/contract (label-ref-at-end? (-> label? natural-number/c boolean?)))
(provide/contract (label-source-id (-> label? integer?)))
(provide/contract (label-same-source? (-> label? label? boolean?)))
(define (ext:make-label label-element)
(cond ((string? label-element) (string->label label-element))
((vector? label-element) (vector->label label-element))
(else
(error 'make-label "Don't know how to make label from ~S" label-element))))
(define (vector->label vector)
(make-label (vector->immutable-vector vector) 0 (vector-length vector)))
(define (vector->label/with-sentinel vector)
(let* ((N (vector-length vector))
(V (make-vector (add1 N))))
(vector-set! V N (gensym 'sentinel))
(let loop ((i 0))
(if (< i N)
(begin (vector-set! V i (vector-ref vector i))
(loop (add1 i)))
(vector->label V)))))
(define string->label
(let ((f (compose vector->label list->vector string->list)))
(lambda (string) (f string))))
(define string->label/with-sentinel
(let ((f (compose vector->label/with-sentinel list->vector string->list)))
(lambda (string) (f string))))
(define (label-length label)
(- (label-j label) (label-i label)))
(define (label-ref label k)
(vector-ref (label-datum label) (+ k (label-i label))))
(define sublabel
(case-lambda
((label i)
(sublabel label i (label-length label)))
((label i j)
(unless (<= i j)
(error 'sublabel "illegal sublabel [~a, ~a]" i j))
(make-label (label-datum label)
(+ i (label-i label))
(+ j (label-i label))))))
(define sublabel!
(case-lambda
((label i)
(sublabel! label i (label-length label)))
((label i j)
(begin
(set-label-j! label (+ j (label-i label)))
(set-label-i! label (+ i (label-i label)))
(void)))))
(define (label-prefix? prefix other-label)
(let ((m (label-length prefix))
(n (label-length other-label)))
(if (> m n) #f
(let loop ((k 0))
(if (= k m)
#t
(and (equal? (label-ref prefix k) (label-ref other-label k))
(loop (add1 k))))))))
(define (label-equal? l1 l2)
(and (= (label-length l1) (label-length l2))
(label-prefix? l1 l2)))
(define (label-empty? label)
(>= (label-i label) (label-j label)))
(define (label->string label)
(list->string (vector->list (label->vector label))))
(define (label->vector label)
(let* ((N (label-length label))
(buffer (make-vector N)))
(let loop ((i 0))
(if (< i N)
(begin
(vector-set! buffer i (label-ref label i))
(loop (add1 i)))
(vector->immutable-vector buffer)))))
(define (label-copy label)
(make-label (label-datum label) (label-i label) (label-j label)))
(define (label-ref-at-end? label offset)
(= offset (label-length label)))
(define (label-source-id label)
(eq-hash-code (label-datum label)))
(define (label-same-source? label-1 label-2)
(eq? (label-datum label-1) (label-datum label-2)))
)