#lang racket/base
(define-struct label (datum i j) #:mutable)
(define (label-element? obj) #t)
(define label-element-equal? equal?)
(provide label?
(rename-out [ext:make-label make-label])
label-element?
label-element-equal?
string->label
string->label/with-sentinel
vector->label
vector->label/with-sentinel
label->string
label->string/removing-sentinel
label->vector
label-length
label-ref
sublabel
sublabel!
label-prefix?
label-equal?
label-empty?
label-copy
label-ref-at-end?
label-source-id
label-same-source?)
(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 (make-sentinel)
(gensym 'sentinel))
(define (sentinel? datum)
(symbol? datum))
(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 (make-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 (str) (f str))))
(define string->label/with-sentinel
(let ((f (compose vector->label/with-sentinel list->vector string->list)))
(lambda (str) (f str))))
(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->string/removing-sentinel label)
(let* ([ln (label-length label)]
[N (if (and (> ln 0) (sentinel? (label-ref label (sub1 ln))))
(sub1 ln)
ln)])
(build-string N (lambda (i) (label-ref label i)))))
(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)))