#cs(module xpath-parser mzscheme
(require (lib "string.ss" "srfi/13"))
(require (planet "ssax.ss" ("lizorkin" "ssax.plt" 2 0)))
(require "sxml-tools.ss")
(define (txp:param-value param-name txp-params)
(cond
((assq param-name txp-params)
=> cadr)
(else
(display "Parameter unspecified: ")
(display param-name)
0 )))
(define (txp:error? obj)
(or (eq? obj 'txp:parser-error)
(eq? obj 'txp:semantic-error)))
(define (sxml:xpointer-parse-error . text)
(apply cerr
(append (list "XPath/XPointer parser error: ") text (list nl)))
#f)
(define (sxml:xpointer-parse-warning . text)
(apply cerr
(append (list "XPath/XPointer parser warning: ") text (list nl))))
(define (txp:semantic-errs-detected? . res-list)
(not (null?
(filter
(lambda (res) (eq? res 'txp:semantic-error))
res-list))))
(define (txp:signal-semantic-error . text)
(apply cerr
(append (list "XPath/XPointer semantic error: ") text (list nl)))
'txp:semantic-error)
(define sxml:whitespace '(#\space #\return #\newline #\tab))
(define sxml:delimiter (append sxml:whitespace
'(#\( #\) #\< #\> #\[ #\] #\: #\/ #\+
#\* #\, #\= #\| #\! #\" #\' #\@ #\$)))
(define (sxml:non-first? ch)
(or (char-numeric? ch)
(memv ch sxml:delimiter)
(memv ch '(#\. #\-))))
(define (sxml:skip-ws path)
(if (or (null? path)
(not (memv (car path) sxml:whitespace)))
path
(sxml:skip-ws (cdr path))))
(define (sxml:assert-end-of-path path)
(let ((path (sxml:skip-ws path)))
(or
(null? path)
(begin
(sxml:xpointer-parse-error "unexpected - \"" (list->string path) "\"")
#f))))
(define (sxml:parse-check str path . char-list)
(let loop ((lst (string->list str))
(p (sxml:skip-ws path)))
(cond
((null? lst)
(if
(or (null? p) (null? char-list) (memv (car p) (car char-list)))
p
#f))
((null? p) #f)
((char=? (car lst) (car p))
(loop (cdr lst) (cdr p)))
(else #f))))
(define (sxml:parse-check-sequence str-seq path . char-list)
(let ((char-list (if (null? char-list) #f (car char-list))))
(let loop ((str-seq str-seq)
(path path))
(cond
((null? str-seq) path) ((if char-list
(sxml:parse-check (car str-seq) path char-list)
(sxml:parse-check (car str-seq) path))
=> (lambda (new-path)
(loop (cdr str-seq) new-path)))
(else #f)))))
(define (sxml:parse-assert str path)
(let loop ((lst (string->list str))
(p (sxml:skip-ws path)))
(cond
((null? lst) p)
((null? p)
(sxml:xpointer-parse-error
"unexpected end of XPath expression. "
"Expected - \"" str "\", given - \"" (list->string path) "\""))
((char=? (car lst) (car p)) (loop (cdr lst) (cdr p)))
(else
(sxml:xpointer-parse-error
"expected - \"" str "\", given - \"" (list->string path) "\"")))))
(define (sxml:parse-ncname path)
(let((path (sxml:skip-ws path)))
(cond
((null? path)
(sxml:xpointer-parse-error
"unexpected end of XPath expression. Expected - NCName"))
((sxml:non-first? (car path))
(sxml:xpointer-parse-error
"expected - NCName instead of " (car path)))
(else
(let loop ((ncname (list (car path)))
(path (cdr path)))
(cond
((null? path) (list (list->string (reverse ncname)) path))
((memv (car path) sxml:delimiter)
(list (list->string (reverse ncname)) path))
(else (loop (cons (car path) ncname) (cdr path)))))))))
(define (sxml:parse-name path)
(let ((path (sxml:skip-ws path)))
(cond
((null? path)
(sxml:xpointer-parse-error
"unexpected end of XPath expression. Expected - Name"))
((and (sxml:non-first? (car path))
(not (char=? (car path) #\:)))
(sxml:xpointer-parse-error "expected - Name instead of " (car path)))
(else (let loop ((ncname (list (car path)))
(path (cdr path)))
(cond
((null? path)
(list (list->string (reverse ncname)) path))
((and (memv (car path) sxml:delimiter)
(not (char=? (car path) #\:)))
(list (list->string (reverse ncname)) path))
(else (loop (cons (car path) ncname) (cdr path)))))))))
(define (sxml:parse-qname path)
(and-let* ((r1 (sxml:parse-ncname path)))
(let ((first (car r1))
(path2 (cadr r1)))
(cond
((null? path2) (list first path2))
((not (char=? (car path2) #\:)) (list first path2))
((null? (cdr path2))
(sxml:xpointer-parse-error "no local part of a qualified name"))
((char=? (cadr path2) #\:) (list first path2))
(else (and-let* ((r2 (sxml:parse-ncname (cdr path2))))
(list (cons first (car r2)) (cadr r2)))
)))))
(define (sxml:parse-natural path)
(let ((path (sxml:skip-ws path)))
(cond
((null? path)
(sxml:xpointer-parse-error
"unexpected end of XPath expression. Expected - number"))
((or (char<? (car path) #\1) (char>? (car path) #\9))
(sxml:xpointer-parse-error "expected - number instead of " (car path)))
(else (let loop ((res (- (char->integer (car path))
48)) (path (cdr path)))
(cond
((null? path) (list res path))
((char-numeric? (car path))
(loop (+ (* res 10) (- (char->integer (car path))
48)) (cdr path)))
(else (list res path))))))))
(define (sxml:parse-literal path)
(let ((ch (if (sxml:parse-check "\"" path) #\" #\')))
(let loop ((res '())
(path (sxml:parse-assert (if (char=? ch #\") "\"" "'")
path)))
(cond
((not path) #f)
((null? path)
(sxml:parse-assert (if (char=? ch #\") "\"" "'")
path)
#f)
((char=? (car path) ch)
(list (list->string (reverse res))
(cdr path)))
(else (loop (cons (car path) res) (cdr path)))))))
(define (sxml:parse-number path)
(define (digits path)
(let loop ((n-lst '())
(path path))
(cond
((and (null? path) (null? n-lst))
(sxml:xpointer-parse-error
"unexpected end of XPath expression. Expected - number"))
((null? path) (list n-lst path))
((and (or (char<? (car path) #\0) (char>? (car path) #\9))
(null? n-lst))
(sxml:xpointer-parse-error "expected - number instead of " (car path)))
((or (char<? (car path) #\0) (char>? (car path) #\9))
(list n-lst path))
(else
(loop (cons (- (char->integer (car path)) (char->integer #\0)) n-lst)
(cdr path))))))
(let ((path (sxml:skip-ws path)))
(cond
((null? path)
(sxml:xpointer-parse-error
"unexpected end of XPath expression. Expected - number"))
((char=? (car path) #\.)
(and-let*
((lst (digits (cdr path))))
(let rpt ((res 0)
(n-lst (car lst))
(path (cadr lst)))
(if (null? n-lst)
(list (/ res 10) path)
(rpt (+ (/ res 10) (car n-lst))
(cdr n-lst)
path)))))
(else
(and-let*
((lst (digits path)))
(let loop ((num1 0)
(n-lst (reverse (car lst)))
(path (cadr lst)))
(if (null? n-lst)
(cond
((or (null? path) (not (char=? (car path) #\.)))
(list num1 path))
((or (null? (cdr path))
(char<? (cadr path) #\0) (char>? (cadr path) #\9))
(list (exact->inexact num1) (cdr path)))
(else
(and-let* ((lst2 (digits (cdr path))))
(let rpt ((num2 0)
(n-lst (car lst2))
(path (cadr lst2)))
(if (null? n-lst)
(list (+ num1 (/ num2 10)) path)
(rpt (+ (/ num2 10) (car n-lst))
(cdr n-lst)
path))))))
(loop (+ (* num1 10) (car n-lst))
(cdr n-lst)
path))))))))
(define (txp:resolve-ns-prefix prefix ns-binding)
(cond
((assq prefix ns-binding)
=> cdr)
(else
(and (eq? prefix 'xml) "xml"))))
(define (txp:parameterize-parser txp-params)
(letrec
(
(txp:parse-axis-specifier
(let* ((axis-param-value (txp:param-value 'axis txp-params))
(child-impl (txp:param-value 'child axis-param-value))
(parser-pairs
(cons
`(("@") ,(txp:param-value 'attribute axis-param-value))
(map
(lambda (single-pair)
(list
(list (symbol->string (car single-pair)) "::")
(cadr single-pair)))
axis-param-value))))
(lambda (path ns-binding add-on) (let loop ((pairs parser-pairs))
(cond
((null? pairs) (list (child-impl add-on) path))
((sxml:parse-check-sequence (caar pairs) path)
=> (lambda (path)
(list ((cadar pairs) add-on) path)))
(else (loop (cdr pairs))))))))
(txp:parse-node-test
(let* ((ntest-param-value (txp:param-value 'node-test txp-params))
(star-impl (txp:param-value 'star ntest-param-value))
(uri+star-impl (txp:param-value 'uri+star ntest-param-value))
(qname-impl (txp:param-value 'qname ntest-param-value))
(comment-impl (txp:param-value 'comment ntest-param-value))
(text-impl (txp:param-value 'text ntest-param-value))
(pi-impl
(txp:param-value 'processing-instruction ntest-param-value))
(node-impl (txp:param-value 'node ntest-param-value))
(point-impl (txp:param-value 'point ntest-param-value))
(range-impl (txp:param-value 'range ntest-param-value))
(brackets
(lambda (path)
(and-let* ((path (sxml:parse-assert "(" path)))
(sxml:parse-assert ")" path)))))
(lambda (path ns-binding add-on)
(cond
((sxml:parse-check-sequence '("comment" "(") path)
=> (lambda (path)
(and-let* ((path (sxml:parse-assert ")" path)))
(list (comment-impl add-on) path))))
((sxml:parse-check-sequence '("text" "(") path)
=> (lambda (path)
(and-let* ((path (sxml:parse-assert ")" path)))
(list (text-impl add-on) path))))
((sxml:parse-check-sequence '("node" "(") path)
=> (lambda (path)
(and-let* ((path (sxml:parse-assert ")" path)))
(list (node-impl add-on) path))))
((sxml:parse-check-sequence '("processing-instruction" "(") path)
=> (lambda (path)
(cond
((sxml:parse-check ")" path)
=> (lambda (path)
(list (pi-impl #f add-on) path)))
(else
(and-let*
((lst (sxml:parse-literal path))
(name (car lst))
(path (sxml:parse-assert ")" (cadr lst))))
(list (pi-impl name add-on) path))))))
((sxml:parse-check-sequence '("point" "(") path)
=> (lambda (path)
(and-let* ((path (sxml:parse-assert ")" path)))
(list (point-impl add-on) path))))
((sxml:parse-check-sequence '("range" "(") path)
=> (lambda (path)
(and-let* ((path (sxml:parse-assert ")" path)))
(list (range-impl add-on) path))))
((sxml:parse-check "*" path)
=> (lambda (path)
(list (star-impl add-on) path)))
(else (and-let*
((lst (sxml:parse-ncname path)))
(let ((path (cadr lst)))
(if
(or (null? path) (not (char=? (car path) #\:))) (list (qname-impl #f (car lst) add-on) path)
(let* ((name (string->symbol (car lst)))
(path (sxml:parse-assert ":" path))
(uri (txp:resolve-ns-prefix name ns-binding)))
(cond
((not uri)
(sxml:xpointer-parse-error
"unknown namespace prefix - " name))
((and (not (null? path)) (char=? (car path) #\*))
(list
(uri+star-impl uri add-on)
(sxml:parse-assert "*" path)))
(else
(and-let*
((lst (sxml:parse-ncname path)))
(list
(qname-impl uri (car lst) add-on)
(cadr lst))))))))))))))
(txp:parse-step
(let* ((step-param-value (txp:param-value 'step txp-params))
(common-value (txp:param-value 'common step-param-value))
(range-to-value (txp:param-value 'range-to step-param-value))
(axis-param-value (txp:param-value 'axis txp-params))
(self-value (txp:param-value 'self axis-param-value))
(parent-value (txp:param-value 'parent axis-param-value))
(ntest-param-value (txp:param-value 'node-test txp-params))
(node-value (txp:param-value 'node ntest-param-value)))
(lambda (path ns-binding add-on)
(cond
((sxml:parse-check ".." path)
(list
(common-value (parent-value add-on)
(node-value add-on) '() add-on)
(sxml:parse-assert ".." path)))
((sxml:parse-check "." path)
(list
(common-value (self-value add-on)
(node-value add-on) '() add-on)
(sxml:parse-assert "." path)))
((sxml:parse-check "range-to" path)
(and-let*
((path0
(sxml:parse-assert "(" (sxml:parse-assert "range-to" path)))
(lst (txp:parse-expr path0 ns-binding add-on))
(path (sxml:parse-assert ")" (cadr lst))))
(let ((expr-res (car lst)))
(let loop ((path path)
(pred-lst '()))
(if
(sxml:parse-check "[" path)
(and-let*
((lst (txp:parse-predicate path ns-binding add-on)))
(loop (cadr lst)
(cons (car lst) pred-lst)))
(list
(if
(apply txp:semantic-errs-detected?
(cons expr-res pred-lst))
'txp:semantic-error
(range-to-value expr-res (reverse pred-lst) add-on))
path))))))
(else (and-let*
((lst (txp:parse-axis-specifier path ns-binding add-on)))
(let ((axis (car lst)))
(and-let*
((lst (txp:parse-node-test (cadr lst) ns-binding add-on)))
(let ((test (car lst)))
(let loop ((preds '())
(path (cadr lst)))
(if
(sxml:parse-check "[" path)
(and-let*
((lst (txp:parse-predicate path ns-binding add-on)))
(loop (cons (car lst) preds)
(cadr lst)))
(list
(if (or (txp:semantic-errs-detected? axis test)
(apply txp:semantic-errs-detected? preds))
'txp:semantic-error
(common-value axis test (reverse preds) add-on))
path))))))))))))
(txp:parse-relative-location-path
(let* ((relative-lpath-value
(txp:param-value 'relative-lpath txp-params))
(step-param-value (txp:param-value 'step txp-params))
(common-value (txp:param-value 'common step-param-value))
(axis-param-value (txp:param-value 'axis txp-params))
(descendant-or-self-value
(txp:param-value 'descendant-or-self axis-param-value))
(ntest-param-value (txp:param-value 'node-test txp-params))
(node-value (txp:param-value 'node ntest-param-value)))
(lambda (path ns-binding add-on)
(let loop ((step-res-lst '())
(path path))
(and-let*
((lst (txp:parse-step path ns-binding add-on)))
(let ((step-res (car lst))
(path (cadr lst)))
(cond
((sxml:parse-check "//" path)
(loop
(cons
(common-value
(descendant-or-self-value add-on)
(node-value add-on) '() add-on)
(cons step-res step-res-lst))
(sxml:parse-assert "//" path)))
((sxml:parse-check "/" path)
(loop (cons step-res step-res-lst)
(sxml:parse-assert "/" path)))
(else (list
(if
(apply txp:semantic-errs-detected? step-res-lst)
'txp:semantic-error
(relative-lpath-value
(reverse (cons step-res step-res-lst)) add-on))
path)))))))))
(txp:parse-location-path
(let* ((location-path-value
(txp:param-value 'location-path txp-params))
(bare-slash-value
(txp:param-value 'bare-slash location-path-value))
(slash-value
(txp:param-value 'slash location-path-value))
(double-slash-value
(txp:param-value 'double-slash location-path-value))
(nothing? (lambda (path)
(let ((path (sxml:skip-ws path)))
(cond
((null? path) #t)
((memv (car path)
'(#\| #\+ #\- #\< #\> #\= #\) #\] #\,)) #t)
((or (sxml:parse-check "mod" path sxml:delimiter)
(sxml:parse-check "div" path sxml:delimiter)
(sxml:parse-check "!=" path)
(sxml:parse-check "and" path sxml:delimiter)
(sxml:parse-check "or" path sxml:delimiter)) #t)
(else #f))))))
(lambda (path ns-binding add-on)
(cond
((sxml:parse-check "//" path)
(and-let*
((lst (txp:parse-relative-location-path
(sxml:parse-assert "//" path) ns-binding add-on)))
(let ((relative-res (car lst))
(path (cadr lst)))
(list
(if (txp:semantic-errs-detected? relative-res)
'txp:semantic-error
(double-slash-value relative-res add-on))
path))))
((sxml:parse-check "/" path)
=> (lambda (path)
(if (nothing? path)
(list (bare-slash-value add-on) path)
(and-let*
((lst (txp:parse-relative-location-path
path ns-binding add-on)))
(let ((relative-res (car lst))
(path (cadr lst)))
(list
(if (txp:semantic-errs-detected? relative-res)
'txp:semantic-error
(slash-value relative-res add-on))
path))))))
(else (txp:parse-relative-location-path path ns-binding add-on))))))
(txp:parse-predicate
(let ((predicate-value (txp:param-value 'predicate txp-params)))
(lambda (path ns-binding add-on)
(and-let*
((path0 (sxml:parse-assert "[" path))
(lst (txp:parse-expr path0 ns-binding add-on))
(path (sxml:parse-assert "]" (cadr lst))))
(list
(if (txp:semantic-errs-detected? (car lst))
'txp:semantic-error
(predicate-value (car lst) add-on))
path)))))
(txp:parse-variable-reference
(let ((var-ref-value (txp:param-value 'variable-ref txp-params)))
(lambda (path ns-binding add-on)
(and-let*
((path (sxml:parse-assert "$" path))
(lst (sxml:parse-qname path)))
(let ((name
(if (pair? (car lst)) (string-append (caar lst) ":" (cdar lst))
(car lst))))
(list (var-ref-value name add-on) (cadr lst)))))))
(txp:parse-function-call
(let ((fun-call-value (txp:param-value 'function-call txp-params))
(parse-arguments
(lambda (path ns-binding add-on)
(and-let*
((path (sxml:parse-assert "(" path)))
(cond
((sxml:parse-check ")" path)
=> (lambda (path) (list '() path)))
(else
(let single-arg ((arg-res-lst '())
(path path))
(and-let*
((lst (txp:parse-expr path ns-binding add-on)))
(let ((arg-res (car lst))
(path (cadr lst)))
(cond
((sxml:parse-check ")" path)
=> (lambda (path)
(list (reverse (cons arg-res arg-res-lst))
path)))
(else
(and-let*
((path (sxml:parse-assert "," path)))
(single-arg
(cons arg-res arg-res-lst) path)))))))))))))
(lambda (path ns-binding add-on)
(and-let*
((lst (sxml:parse-qname path)))
(let ((fun-name (car lst))) (and-let*
((lst (parse-arguments (cadr lst) ns-binding add-on)))
(let ((arg-res-lst (car lst))
(path (cadr lst)))
(list
(if (apply txp:semantic-errs-detected? arg-res-lst)
'txp:semantic-error
(fun-call-value
(if (pair? fun-name) (string-append (car fun-name) ":" (cdr fun-name))
fun-name)
arg-res-lst add-on))
path))))))))
(txp:parse-primary-expr
(let* ((primary-expr-value (txp:param-value 'primary-expr txp-params))
(literal-value (txp:param-value 'literal primary-expr-value))
(number-value (txp:param-value 'number primary-expr-value)))
(lambda (path ns-binding add-on)
(cond
((sxml:parse-check "$" path) (txp:parse-variable-reference path ns-binding add-on))
((sxml:parse-check "(" path) (and-let*
((lst (txp:parse-expr
(sxml:parse-assert "(" path) ns-binding add-on))
(path (sxml:parse-assert ")" (cadr lst))))
(let ((expr-res (car lst)))
(list expr-res path))))
((or (sxml:parse-check "\"" path)
(sxml:parse-check "'" path)) (and-let*
((lst (sxml:parse-literal path)))
(list
(literal-value (car lst) add-on)
(cadr lst))))
((let ((p (sxml:skip-ws path))) (cond ((null? p) #f)
((char=? (car p) #\.) #t)
((and (char>=? (car p) #\0) (char<=? (car p) #\9)) #t)
(else #f)))
(and-let*
((lst (sxml:parse-number path)))
(list
(number-value (car lst) add-on)
(cadr lst))))
(else (txp:parse-function-call path ns-binding add-on))))))
(txp:parse-filter-expr
(let ((filter-expr-value (txp:param-value 'filter-expr txp-params)))
(lambda (path ns-binding add-on)
(and-let*
((lst (txp:parse-primary-expr path ns-binding add-on)))
(let ((prim-res (car lst)))
(let loop ((pred-res-lst '())
(path (cadr lst)))
(cond
((sxml:parse-check "[" path)
(and-let*
((lst (txp:parse-predicate path ns-binding add-on)))
(loop (cons (car lst) pred-res-lst)
(cadr lst))))
((null? pred-res-lst) (list prim-res path))
(else
(list
(if
(apply txp:semantic-errs-detected?
(cons prim-res pred-res-lst))
'txp:semantic-error
(filter-expr-value prim-res (reverse pred-res-lst) add-on))
path)))))))))
(txp:parse-path-expr
(let ((filter-expr?
(lambda (path)
(let ((path (sxml:skip-ws path)))
(cond
((null? path) #f)
((member
(car path)
'(#\$ #\( #\" #\' #\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9))
#t)
((char=? (car path) #\.)
(cond
((null? (cdr path)) #f)
((member
(cadr path)
'(#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9))
#t)
(else #f)))
((member
(car path)
'(#\) #\< #\> #\[ #\] #\/ #\+ #\* #\, #\= #\| #\! #\@ #\-))
#f)
(else
(let ((lst (sxml:parse-ncname path)))
(cond
((not lst) #f)
((sxml:parse-check "::" (cadr lst)) #f)
(else
(and-let*
((lst (sxml:parse-name path)))
(let ((name (car lst))
(new-path (sxml:skip-ws (cadr lst))))
(cond
((string=? name "range-to") #f)
((string=? name "comment") #f)
((string=? name "text") #f)
((string=? name "processing-instruction") #f)
((string=? name "node") #f)
((string=? name "point") #f)
((string=? name "range") #f)
((null? new-path) #f)
((char=? (car new-path) #\() #t)
(else #f)))))))))))))
(let* ((path-expr-value (txp:param-value 'path-expr txp-params))
(slash-value (txp:param-value 'slash path-expr-value))
(double-slash-value
(txp:param-value 'double-slash path-expr-value)))
(lambda (path ns-binding add-on)
(if
(not (filter-expr? path))
(txp:parse-location-path path ns-binding add-on)
(and-let*
((lst (txp:parse-filter-expr path ns-binding add-on)))
(let ((filter-ex-res (car lst))
(path (cadr lst)))
(cond
((sxml:parse-check "//" path)
(and-let*
((lst2
(txp:parse-relative-location-path
(sxml:parse-assert "//" path) ns-binding add-on)))
(let ((rel-lpath-res (car lst2))
(path (cadr lst2)))
(list
(if
(txp:semantic-errs-detected?
filter-ex-res rel-lpath-res)
'txp:semantic-error
(double-slash-value
filter-ex-res rel-lpath-res add-on))
path))))
((sxml:parse-check "/" path)
(and-let*
((lst2
(txp:parse-relative-location-path
(sxml:parse-assert "/" path) ns-binding add-on)))
(let ((rel-lpath-res (car lst2))
(path (cadr lst2)))
(list
(if
(txp:semantic-errs-detected?
filter-ex-res rel-lpath-res)
'txp:semantic-error
(slash-value filter-ex-res rel-lpath-res add-on))
path))))
(else lst)))))))))
(txp:parse-union-expr
(let ((union-expr-value (txp:param-value 'union-expr txp-params)))
(lambda (path ns-binding add-on)
(let loop ((p-e-res-lst '())
(path path))
(and-let*
((lst (txp:parse-path-expr path ns-binding add-on)))
(let ((p-e-res (car lst))
(path (cadr lst)))
(let ((new-path (sxml:parse-check "|" path)))
(cond
(new-path (loop (cons p-e-res p-e-res-lst) new-path))
((null? p-e-res-lst) (list p-e-res path))
(else (list
(if
(apply txp:semantic-errs-detected?
(cons p-e-res p-e-res-lst))
'txp:semantic-error
(union-expr-value
(reverse (cons p-e-res p-e-res-lst)) add-on))
path))))))))))
(txp:parse-unary-expr
(let ((unary-expr-value (txp:param-value 'unary-expr txp-params)))
(lambda (path ns-binding add-on)
(if (not (sxml:parse-check "-" path))
(txp:parse-union-expr path ns-binding add-on)
(let loop ((num-minuses 0) (path path))
(let ((new-path (sxml:parse-check "-" path)))
(if new-path (loop (+ num-minuses 1) new-path)
(and-let*
((lst (txp:parse-union-expr path ns-binding add-on)))
(let ((union-expr-res (car lst))
(path (cadr lst)))
(list
(if
(txp:semantic-errs-detected? union-expr-res)
'txp:semantic-error
(unary-expr-value
union-expr-res num-minuses add-on))
path))))))))))
(txp:parse-multiplicative-expr
(let* ((mul-expr-value (txp:param-value 'mul-expr txp-params))
(operations-value (txp:param-value 'operations txp-params))
(multiply-value (txp:param-value '* operations-value))
(div-value (txp:param-value 'div operations-value))
(mod-value (txp:param-value 'mod operations-value)))
(lambda (path ns-binding add-on)
(let loop ((unary-expr-res-lst '())
(op-lst '())
(path path))
(and-let*
((lst (txp:parse-unary-expr path ns-binding add-on)))
(let ((unary-expr-res (car lst))
(path (cadr lst)))
(cond
((sxml:parse-check "*" path)
(loop (cons unary-expr-res unary-expr-res-lst)
(cons (multiply-value add-on) op-lst)
(sxml:parse-assert "*" path)))
((sxml:parse-check "div" path sxml:delimiter)
(loop (cons unary-expr-res unary-expr-res-lst)
(cons (div-value add-on) op-lst)
(sxml:parse-assert "div" path)))
((sxml:parse-check "mod" path sxml:delimiter)
(loop (cons unary-expr-res unary-expr-res-lst)
(cons (mod-value add-on) op-lst)
(sxml:parse-assert "mod" path)))
((null? unary-expr-res-lst) lst)
(else (list
(if
(apply txp:semantic-errs-detected?
(cons unary-expr-res unary-expr-res-lst))
'txp:semantic-error
(mul-expr-value
(reverse (cons unary-expr-res unary-expr-res-lst))
(reverse op-lst) add-on))
path)))))))))
(txp:parse-additive-expr
(let* ((add-expr-value (txp:param-value 'add-expr txp-params))
(operations-value (txp:param-value 'operations txp-params))
(plus-value (txp:param-value '+ operations-value))
(minus-value (txp:param-value '- operations-value)))
(lambda (path ns-binding add-on)
(let loop ((mul-expr-res-lst '())
(op-lst '())
(path path))
(and-let*
((lst (txp:parse-multiplicative-expr path ns-binding add-on)))
(let ((mul-expr-res (car lst))
(path (cadr lst)))
(cond
((sxml:parse-check "+" path)
(loop (cons mul-expr-res mul-expr-res-lst)
(cons (plus-value add-on) op-lst)
(sxml:parse-assert "+" path)))
((sxml:parse-check "-" path)
(loop (cons mul-expr-res mul-expr-res-lst)
(cons (minus-value add-on) op-lst)
(sxml:parse-assert "-" path)))
((null? mul-expr-res-lst) lst)
(else (list
(if
(apply txp:semantic-errs-detected?
(cons mul-expr-res mul-expr-res-lst))
'txp:semantic-error
(add-expr-value
(reverse (cons mul-expr-res mul-expr-res-lst))
(reverse op-lst) add-on))
path)))))))))
(txp:parse-relational-expr
(let* ((rel-expr-value (txp:param-value 'relational-expr txp-params))
(operations-value (txp:param-value 'operations txp-params))
(ls-value (txp:param-value '< operations-value))
(gt-value (txp:param-value '> operations-value))
(le-value (txp:param-value '<= operations-value))
(ge-value (txp:param-value '>= operations-value)))
(lambda (path ns-binding add-on)
(let loop ((add-res-lst '())
(cmp-op-lst '())
(path path))
(and-let*
((lst (txp:parse-additive-expr path ns-binding add-on)))
(let ((add-res (car lst))
(path (cadr lst)))
(cond
((sxml:parse-check "<=" path)
(loop (cons add-res add-res-lst)
(cons (le-value add-on) cmp-op-lst)
(sxml:parse-assert "<=" path)))
((sxml:parse-check ">=" path)
(loop (cons add-res add-res-lst)
(cons (ge-value add-on) cmp-op-lst)
(sxml:parse-assert ">=" path)))
((sxml:parse-check "<" path)
(loop (cons add-res add-res-lst)
(cons (ls-value add-on) cmp-op-lst)
(sxml:parse-assert "<" path)))
((sxml:parse-check ">" path)
(loop (cons add-res add-res-lst)
(cons (gt-value add-on) cmp-op-lst)
(sxml:parse-assert ">" path)))
((null? add-res-lst) lst)
(else (list
(if
(apply txp:semantic-errs-detected?
(cons add-res add-res-lst))
'txp:semantic-error
(rel-expr-value
(reverse (cons add-res add-res-lst))
(reverse cmp-op-lst) add-on))
path)))))))))
(txp:parse-equality-expr
(let* ((equality-expr-value
(txp:param-value 'equality-expr txp-params))
(operations-value
(txp:param-value 'operations txp-params))
(equal-value (txp:param-value '= operations-value))
(not-equal-value (txp:param-value '!= operations-value)))
(lambda (path ns-binding add-on)
(let loop ((rel-res-lst '())
(cmp-op-lst '())
(path path))
(and-let*
((lst (txp:parse-relational-expr path ns-binding add-on)))
(let ((rel-res (car lst))
(path (cadr lst)))
(cond
((sxml:parse-check "=" path)
(loop (cons rel-res rel-res-lst)
(cons (equal-value add-on) cmp-op-lst)
(sxml:parse-assert "=" path)))
((sxml:parse-check "!=" path)
(loop (cons rel-res rel-res-lst)
(cons (not-equal-value add-on) cmp-op-lst)
(sxml:parse-assert "!=" path)))
((null? rel-res-lst) lst)
(else (list
(if
(apply txp:semantic-errs-detected?
(cons rel-res rel-res-lst))
'txp:semantic-error
(equality-expr-value
(reverse (cons rel-res rel-res-lst))
(reverse cmp-op-lst) add-on))
path)))))))))
(txp:parse-and-expr
(let ((and-expr-value (txp:param-value 'and-expr txp-params)))
(lambda (path ns-binding add-on)
(let loop ((equality-res-lst '())
(path path))
(and-let*
((lst (txp:parse-equality-expr path ns-binding add-on)))
(let ((equality-res (car lst))
(path (cadr lst)))
(let ((new-path (sxml:parse-check "and" path sxml:delimiter)))
(cond
(new-path
(loop (cons equality-res equality-res-lst) new-path))
((null? equality-res-lst) lst)
(else (list
(if
(apply txp:semantic-errs-detected?
(cons equality-res equality-res-lst))
'txp:semantic-error
(and-expr-value
(reverse (cons equality-res equality-res-lst))
add-on))
path))))))))))
(txp:parse-expr
(let ((or-expr-value (txp:param-value 'or-expr txp-params)))
(lambda (path ns-binding add-on)
(let loop ((and-res-lst '())
(path path))
(and-let*
((lst (txp:parse-and-expr path ns-binding add-on)))
(let ((and-res (car lst))
(path (cadr lst)))
(let ((new-path (sxml:parse-check "or" path sxml:delimiter)))
(cond
(new-path
(loop (cons and-res and-res-lst) new-path))
((null? and-res-lst) lst)
(else (list
(if
(apply txp:semantic-errs-detected?
(cons and-res and-res-lst))
'txp:semantic-error
(or-expr-value
(reverse (cons and-res and-res-lst)) add-on))
path))))))))))
(txp:parse-full-xptr
(let ((full-xptr-value (txp:param-value 'full-xptr txp-params)))
(lambda (path ns-binding add-on)
(let loop ((expr-res-lst '())
(ns-binding ns-binding)
(path path))
(if
(null? (sxml:skip-ws path)) (cond
((= (length expr-res-lst) 1) (car expr-res-lst))
((apply txp:semantic-errs-detected? expr-res-lst)
'txp:semantic-error)
(else
(full-xptr-value (reverse expr-res-lst) add-on)))
(and-let*
((lst (sxml:parse-name path))
(name (car lst))
(path (cadr lst)))
(cond
((string=? name "xpointer") (and-let*
((path (sxml:parse-assert "(" path))
(lst2 (txp:parse-expr path ns-binding add-on)))
(let ((expr-res (car lst2))
(path (cadr lst2)))
(and-let*
((path (sxml:parse-assert ")" path)))
(loop (cons expr-res expr-res-lst) ns-binding path)))))
((string=? name "xmlns") (and-let*
((path0 (sxml:parse-assert "(" path))
(lst2 (sxml:parse-ncname path0))
(prefix (string->symbol (car lst2)))
(path (sxml:parse-assert "=" (cadr lst2))))
(let rpt2 ((path (sxml:skip-ws path)) (uri '()))
(cond
((null? path)
(sxml:parse-assert ")" path)
#f)
((and (char=? (car path) #\)) (null? uri))
(sxml:xpointer-parse-error
"namespace URI cannot be empty"))
((char=? (car path) #\))
(loop expr-res-lst
(cons
(cons prefix (list->string (reverse uri)))
ns-binding)
(cdr path)))
(else
(rpt2 (cdr path) (cons (car path) uri)))))))
(else (and-let*
((path (sxml:parse-assert "(" path)))
(let rpt3 ((n 1) (path path))
(cond
((= n 0)
(sxml:xpointer-parse-warning
"unknown xpointer schema - " name ". Ignoring")
(loop expr-res-lst ns-binding path))
((null? path)
(sxml:parse-assert ")" path)
#f)
((char=? (car path) #\() (rpt3 (+ n 1) (cdr path)))
((char=? (car path) #\)) (rpt3 (- n 1) (cdr path)))
(else (rpt3 n (cdr path))))))))))))))
(txp:parse-child-seq
(let ((helper
(lambda (path)
(let loop ((num-lst '())
(path path))
(let ((path2 (sxml:parse-check "/" path)))
(cond
(path2 (and-let* ((lst (sxml:parse-natural path2)))
(loop (cons (car lst) num-lst)
(cadr lst))))
((null? (sxml:skip-ws path)) (reverse num-lst))
(else (sxml:parse-assert "/" path))))))))
(let* ((child-seq-value (txp:param-value 'child-seq txp-params))
(with-name-value (txp:param-value 'with-name child-seq-value))
(without-name-value
(txp:param-value 'without-name child-seq-value)))
(lambda (path ns-binding add-on)
(let ((path2 (sxml:parse-check "/" path)))
(if
path2 (and-let*
((number-lst (helper path)))
(without-name-value number-lst add-on))
(and-let*
((lst (sxml:parse-name path))
(name (car lst))
(number-lst (helper (cadr lst))))
(with-name-value name number-lst add-on))))))))
(txp:parse-xpath
(lambda (path-string ns-binding add-on)
(let ((res (txp:parse-location-path
(string->list path-string) ns-binding add-on)))
(if (and res (sxml:assert-end-of-path (cadr res)))
(car res)
'txp:parser-error))))
(txp:parse-xpointer
(lambda (path-string ns-binding add-on)
(let ((path (string->list path-string)))
(if (sxml:parse-check "/" path) (txp:parse-child-seq path ns-binding add-on)
(and-let*
((lst (sxml:parse-name path))
(new-path (cadr lst)))
(if (sxml:parse-check "(" new-path) (txp:parse-full-xptr path ns-binding add-on)
(txp:parse-child-seq path ns-binding add-on)))))))
(txp:parse-xpath-expression
(lambda (path-string ns-binding add-on)
(let ((res (txp:parse-expr
(string->list path-string) ns-binding add-on)))
(if (and res (sxml:assert-end-of-path (cadr res)))
(car res)
'txp:parser-error))))
)
`((xpath ,txp:parse-xpath)
(xpointer ,txp:parse-xpointer)
(expr ,txp:parse-xpath-expression))
))
(provide (all-defined)))