Module: lazy-xpath

 This module implements lazy SXPath evaluation over lazy SXML documents

 This software is in Public Domain.
 IT IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND.

 Please send bug reports and comments to:
   lizorkin@hotbox.ru    Dmitry Lizorkin

 In a lazy SXML document, each node may be a promise. If forced, the promise
 results into an SXML node or a nodeset. For a nodeset, its members are SXML
 nodes and promises in turn.
 With every promise forced, a lazy SXML document must conform to SXML
 Specification. In particular, an attribute node must occur before any child
 nodes, attribute value must be atomic, etc.

 SXPath evaluation is lazy in that it results to a nodeset whose last member
 may be a promise. Such a nodeset with a promise as its last member denotes
 the first portion of the result. If the promise is forced, it is evaluated
 into another nodeset, which corresponds to the next portion of the result.
 SXPath evaluator returns the result in portions when some branch in the
 document is to be forced in order to obtain the next part of the result.
 However, a portion that is not the last one, must contain at least one result
 node. To fulfill this requirement, branches of the document may be forced
 until at least a result node for a portion is obtained.


f: lazy:or

Misc helpers for working with a lazy nodeset
a: cond-expand
f: lazy:promise?
Lazy analogues for common list operations
f: lazy:null?
f: lazy:map
f: lazy:filter
f: lazy:car
f: lazy:cdr
f: lazy:length
f: lazy:result->list
f: lazy:node->sxml
f: lazy:reach-root
f: lazy:contextset->nodeset
f: lazy:recover-contextset
f: lazy:find-proper-context

Axes
f: lazy:output-siblings
f: lazy:find-foll-siblings
f: lazy:find-prec-siblings
Axis functions
f: lazy:ancestor
f: lazy:ancestor-or-self
f: lazy:attribute
f: lazy:child
f: lazy:descendant
f: lazy:descendant-or-self
f: lazy:following
f: lazy:following-sibling
f: lazy:namespace
f: lazy:parent
f: lazy:preceding
f: lazy:preceding-sibling
f: lazy:self
Making every axis consume a nodeset
f: lazy:axis-consume-nodeset

Lazy analogues for "sxpath-ext.scm"
SXML counterparts to W3C XPath Core Functions Library
f: lazy:string
f: lazy:boolean
f: lazy:number
f: lazy:string-value
Comparators for XPath objects
f: lazy:equality-cmp
f: lazy:equal?
f: lazy:not-equal?
f: lazy:relational-cmp

XPath Core Function Library
4.1 Node Set Functions
f: lazy:core-last
f: lazy:core-position
f: lazy:core-count
f: lazy:core-id
f: lazy:core-local-name
f: lazy:core-namespace-uri
f: lazy:core-name
4.2 String Functions
f: lazy:core-string
f: lazy:core-concat
f: lazy:core-starts-with
f: lazy:core-contains
f: lazy:core-substring-before
f: lazy:core-substring-after
f: lazy:core-substring
f: lazy:core-string-length
f: lazy:core-normalize-space
f: lazy:core-translate
4.3 Boolean Functions
f: lazy:core-boolean
f: lazy:core-not
f: lazy:core-true
f: lazy:core-false
f: lazy:core-lang
4.4 Number Functions
f: lazy:core-number
f: lazy:core-sum
f: lazy:core-floor
f: lazy:core-ceiling
f: lazy:core-round

XPath AST processing
f: lazy:ast-axis-specifier
In this section, each function accepts 2 arguments
f: lazy:ast-location-path
f: lazy:ast-absolute-location-path
f: lazy:ast-relative-location-path
f: lazy:ast-step
f: lazy:ast-step-list
f: lazy:ast-predicate
f: lazy:ast-predicate-list
f: lazy:ast-expr
f: lazy:ast-or-expr
f: lazy:ast-and-expr
f: lazy:ast-equality-expr
f: lazy:ast-relational-expr
f: lazy:ast-additive-expr
f: lazy:ast-multiplicative-expr
f: lazy:ast-union-expr
f: lazy:ast-path-expr
f: lazy:ast-filter-expr
f: lazy:ast-variable-reference
f: lazy:ast-literal
f: lazy:ast-number
f: lazy:ast-function-call
f: lazy:ast-function-arguments

Highest level API functions
f: lazy:api-helper
f: lazy:txpath
f: lazy:xpath-expr
f: lazy:sxpath

lazy:or

(define (lazy:or . args)
... Full Code ... )
 Implement 'or' as a function, so that we could 'apply' it



Misc helpers for working with a lazy nodeset


cond-expand

... Source Code ...
 Escaping the ## for some Scheme implementations


lazy:promise?

(define lazy:promise?
... Full Code ... )
 Predicate for detecting a promise
 There is no such a predicate in R5RS, so different Scheme implementations
 use different names for this functionality



Lazy analogues for common list operations


lazy:null?

(define (lazy:null? nodeset)
... Full Code ... )
 Checks whether the nodeset is empty
 Note that a promise can evaluate to an empty list, and thus a nodeset
 consisting of promises only may potentially be empty


lazy:map

(define (lazy:map func nodeset)
... Full Code ... )
 Like conventional map, but applicable to a lazy nodeset


lazy:filter

(define (lazy:filter func nodeset)
... Full Code ... )
 Lazy analogue for filter


lazy:car

(define (lazy:car nodeset)
... Full Code ... )
 Like conventional car, but for a lazy nodeset


lazy:cdr

(define (lazy:cdr nodeset)
... Full Code ... )
 Like conventional cdr


lazy:length

(define (lazy:length nodeset)
... Full Code ... )
 Like conventional length, but for a lazy nodeset
 ATTENTION: it has to force all the nodeset members in order to determine
 the length properly



 Converts the lazy result into a list, by forcing all the promises one by one

lazy:result->list

(define (lazy:result->list nodeset)
... Full Code ... )


lazy:node->sxml

(define (lazy:node->sxml node)
... Full Code ... )
 Converts the lazy node to SXML, by forcing all of its descendants
 The node itself is not a promise


lazy:reach-root

(define (lazy:reach-root contextset)
... Full Code ... )
 Reaches the root of the root of the contextset
 Result: singleton nodeset


lazy:contextset->nodeset

(define (lazy:contextset->nodeset obj)
... Full Code ... )
 Analogue for draft:contextset->nodeset for the lazy case


lazy:recover-contextset

(define (lazy:recover-contextset nodeset root-node num-anc)
... Full Code ... )
 Lazy analogue for draft:recover-contextset


lazy:find-proper-context

(define (lazy:find-proper-context nodeset context-set num-anc)
... Full Code ... )
 Makes a context-set from a nodeset supplied, with the num-anc required
 Members of the nodeset are known to be descendants-or-selves of
 (map sxml:context->node context-set)



Axes


lazy:output-siblings

(define (lazy:output-siblings test-pred? siblings ancestors)
... Full Code ... )
 A helper that tests sibling nodes with respect to the test-pred? and returns
 them in the lazy manner. Each of the siblings may be a promise.
 This function is applied by axes: child, attribute, namespace,
 following-sibling, preceding-sibling


lazy:find-foll-siblings

(define (lazy:find-foll-siblings node nodeset)
... Full Code ... )
 Returns nodeset membert that are following siblings of the given node
 Nodeset members may be promises


lazy:find-prec-siblings

(define (lazy:find-prec-siblings node nodeset)
... Full Code ... )
 Returns nodeset members that are preceding siblings of the given node
 Nodeset members may be promises



Axis functions

 They have the signature of:
  test-pred? [num-ancestors] -> Node -> Nodeset
 Note that each axis function produces the function to be applied to a single
 _node_, not to a nodeset

lazy:ancestor

(define (lazy:ancestor test-pred? . num-ancestors)
... Full Code ... )
 Ancestor axis
 It should be noted that ancestors of the context node are already forced


lazy:ancestor-or-self

(define (lazy:ancestor-or-self test-pred? . num-ancestors)
... Full Code ... )
 Ancestor-or-self axis
 It should be noted that ancestors of the context node are already forced


lazy:attribute

(define (lazy:attribute test-pred? . num-ancestors)
... Full Code ... )
 Attribute axis


lazy:child

(define (lazy:child test-pred? . num-ancestors)
... Full Code ... )
 Child axis


lazy:descendant

(define (lazy:descendant test-pred? . num-ancestors)
... Full Code ... )
 Descendant axis


lazy:descendant-or-self

(define (lazy:descendant-or-self test-pred? . num-ancestors)
... Full Code ... )
 Descendant-or-self axis


lazy:following

(define (lazy:following test-pred? . num-ancestors)
... Full Code ... )
 Following axis


lazy:following-sibling

(define (lazy:following-sibling test-pred? . num-ancestors)
... Full Code ... )
 Following-sibling axis


lazy:namespace

(define (lazy:namespace test-pred? . num-ancestors)
... Full Code ... )
 Namespace axis
 Since a namespace axis somewhat redundant for SXML, we'll provide a
 not-very-effective implementation for it


lazy:parent

(define (lazy:parent test-pred? . num-ancestors)
... Full Code ... )
 Parent axis
 It should be noted that the parent of the context node is already forced


lazy:preceding

(define (lazy:preceding test-pred? . num-ancestors)
... Full Code ... )
 Preceding axis


lazy:preceding-sibling

(define (lazy:preceding-sibling test-pred? . num-ancestors)
... Full Code ... )
 Preceding-sibling axis


lazy:self

(define (lazy:self test-pred? . num-ancestors)
... Full Code ... )
 Self axis
 Shortens the context if it contains more nodes than specified by num-ancestor
 In most cases, this work can be considered redundant; however, it eliminates
 some classes of error that are hard to detect



Making every axis consume a nodeset


lazy:axis-consume-nodeset

(define (lazy:axis-consume-nodeset axis)
... Full Code ... )
 Given the axis of the form
  Node -> Nodeset
 produces the axis of the form
  Node|Nodeset -> Nodeset



Lazy analogues for "sxpath-ext.scm"



SXML counterparts to W3C XPath Core Functions Library


lazy:string

(define (lazy:string object)
... Full Code ... )
 The counterpart to XPath 'string' function (section 4.2 XPath Rec.)
 Converts a given object to a string


lazy:boolean

(define (lazy:boolean object)
... Full Code ... )
 The counterpart to XPath 'boolean' function (section 4.3 XPath Rec.)
 Converts its argument to a boolean


lazy:number

(define (lazy:number obj)
... Full Code ... )
 The counterpart to XPath 'number' function (section 4.4 XPath Rec.)
 Converts its argument to a number
 NOTE: 
  1. The argument is not optional (yet?)
  2. string->number conversion is not IEEE 754 round-to-nearest
  3. NaN is represented as 0


lazy:string-value

(define (lazy:string-value node)
... Full Code ... )
 Returns a string value for a given node in accordance to
 XPath Rec. 5.1 - 5.7
 Undocumented functionality - can be applied for a node that is a promise



Comparators for XPath objects


lazy:equality-cmp

(define (lazy:equality-cmp bool-op number-op string-op)
... Full Code ... )
 A helper for XPath equality operations: = , !=
  'bool-op', 'number-op' and 'string-op' are comparison operations for 
 a pair of booleans,  numbers and strings respectively


lazy:equal?

(define lazy:equal?
... Full Code ... )


lazy:not-equal?

(define lazy:not-equal?
... Full Code ... )


lazy:relational-cmp

(define (lazy:relational-cmp op)
... Full Code ... )
 Relational operation ( < , > , <= , >= ) for two XPath objects
  op is comparison procedure: < , > , <= or >=



XPath Core Function Library



4.1 Node Set Functions


lazy:core-last

(define (lazy:core-last num-anc)
... Full Code ... )
 last()


lazy:core-position

(define (lazy:core-position num-anc)
... Full Code ... )
 position()


lazy:core-count

(define (lazy:core-count num-anc arg-func)
... Full Code ... )
 count(node-set)


lazy:core-id

(define (lazy:core-id num-anc arg-func)
... Full Code ... )
 id(object)


lazy:core-local-name

(define (lazy:core-local-name num-anc . arg-func)
... Full Code ... )
 local-name(node-set?)


lazy:core-namespace-uri

(define (lazy:core-namespace-uri num-anc . arg-func)
... Full Code ... )
 namespace-uri(node-set?)


lazy:core-name

(define (lazy:core-name num-anc . arg-func)
... Full Code ... )
 name(node-set?)



4.2 String Functions


lazy:core-string

(define (lazy:core-string num-anc . arg-func)
... Full Code ... )
 string(object?)


lazy:core-concat

(define (lazy:core-concat num-anc . arg-func-lst)
... Full Code ... )
 concat(string, string, string*)


lazy:core-starts-with

(define (lazy:core-starts-with num-anc arg-func1 arg-func2)
... Full Code ... )
 starts-with(string, string)


lazy:core-contains

(define (lazy:core-contains num-anc arg-func1 arg-func2)
... Full Code ... )
 contains(string, string)


lazy:core-substring-before

(define (lazy:core-substring-before num-anc arg-func1 arg-func2)
... Full Code ... )
 substring-before(string, string)


lazy:core-substring-after

(define (lazy:core-substring-after num-anc arg-func1 arg-func2)
... Full Code ... )
 substring-after(string, string)


lazy:core-substring

(define (lazy:core-substring num-anc arg-func1 arg-func2 . arg-func3)
... Full Code ... )
 substring(string, number, number?)


lazy:core-string-length

(define (lazy:core-string-length num-anc . arg-func)
... Full Code ... )
 string-length(string?)


lazy:core-normalize-space

(define (lazy:core-normalize-space num-anc . arg-func)
... Full Code ... )
 normalize-space(string?)


lazy:core-translate

(define (lazy:core-translate num-anc arg-func1 arg-func2 arg-func3)
... Full Code ... )
 translate(string, string, string)



4.3 Boolean Functions


lazy:core-boolean

(define (lazy:core-boolean num-anc arg-func)
... Full Code ... )
 boolean(object)


lazy:core-not

(define (lazy:core-not num-anc arg-func)
... Full Code ... )
 not(boolean)


lazy:core-true

(define (lazy:core-true num-anc)
... Full Code ... )
 true()


lazy:core-false

(define (lazy:core-false num-anc)
... Full Code ... )
 false()


lazy:core-lang

(define (lazy:core-lang num-anc arg-func)
... Full Code ... )
 lang(string)



4.4 Number Functions


lazy:core-number

(define (lazy:core-number num-anc . arg-func)
... Full Code ... )
 number(object?)


lazy:core-sum

(define (lazy:core-sum num-anc arg-func)
... Full Code ... )
 sum(node-set)


lazy:core-floor

(define (lazy:core-floor num-anc arg-func)
... Full Code ... )
 floor(number)


lazy:core-ceiling

(define (lazy:core-ceiling num-anc arg-func)
... Full Code ... )
 ceiling(number)


lazy:core-round

(define (lazy:core-round num-anc arg-func)
... Full Code ... )
 round(number)



XPath AST processing

 AST is considered to be properly formed

lazy:ast-axis-specifier

(define (lazy:ast-axis-specifier op num-anc)
... Full Code ... )
 {5} <AxisSpecifier> ::= (axis-specifier  <AxisName> )
 {6} <AxisName> ::= (ancestor)
                    | (ancestor-or-self)
                    | (attribute)
                    | (child)
                    | (descendant)
                    | (descendant-or-self)
                    | (following)
                    | (following-sibling)
                    | (namespace)
                    | (parent)
                    | (preceding)
                    | (preceding-sibling)
                    | (self)
                    | (arc)  ; the following 3 are added by SXLink
                    | (traverse)
                    | (traverse-arc)
 Returns:  (cons lambda num-ancestors)



In this section, each function accepts 2 arguments

  op - S-expression which represents the operation
  num-anc - how many ancestors are required in the context after that
            operation
 and returns either #f, which signals of a semantic error, or
  (list (lambda (nodeset position+size var-binding) ...)
        num-anc-it-requires
        requires-size? )
  position+size - the same to what was called 'context' in TXPath-1
  requires-size? - context size in required for evaluating the operation

lazy:ast-location-path

(define (lazy:ast-location-path op num-anc)
... Full Code ... )
 {1} <LocationPath> ::= <RelativeLocationPath>
                        | <AbsoluteLocationPath>


lazy:ast-absolute-location-path

(define (lazy:ast-absolute-location-path op num-anc)
... Full Code ... )
 {2} <AbsoluteLocationPath> ::= (absolute-location-path  <Step>* )


lazy:ast-relative-location-path

(define (lazy:ast-relative-location-path op num-anc)
... Full Code ... )
 {3} <RelativeLocationPath> ::= (relative-location-path  <Step>+ )


lazy:ast-step

(define (lazy:ast-step op num-anc)
... Full Code ... )
 {4} <Step> ::= (step  <AxisSpecifier> <NodeTest> <Predicate>* )
                | (range-to  (expr <Expr>)  <Predicate>* )


lazy:ast-step-list

(define (lazy:ast-step-list step-lst num-anc)
... Full Code ... )
 {4a} ( <Step>+ )
 Returns (list (listof step-impl) num-anc) or #f
 NOTE: requires-size? is not needed here, since it is always #f


lazy:ast-predicate

(define (lazy:ast-predicate op num-anc)
... Full Code ... )
 {8} <Predicate> ::= (predicate  <Expr> )
 NOTE: num-anc is dummy here, since it is always 0 for Predicates


lazy:ast-predicate-list

(define (lazy:ast-predicate-list op-lst num-anc)
... Full Code ... )
 {8a} ( <Predicate>+ )
 Returns (list (listof pred-impl) num-anc) or #f
 NOTE: num-anc is dummy here, since it is always 0 for Predicates


lazy:ast-expr

(define (lazy:ast-expr op num-anc)
... Full Code ... )
 {9} <Expr> ::= <OrExpr>
                | <AndExpr>
                | <EqualityExpr>
                | <RelationalExpr>
                | <AdditiveExpr>
                | <MultiplicativeExpr>
                | <UnionExpr>
                | <PathExpr>
                | <FilterExpr>
                | <VariableReference>
                | <Literal>
                | <Number>
                | <FunctionCall>
                | <LocationPath>


lazy:ast-or-expr

(define (lazy:ast-or-expr op num-anc)
... Full Code ... )
 {10} <OrExpr> ::= (or <Expr> <Expr>+ )
 NOTE: num-anc is dummy here, since it is always 0 for OrExpr


lazy:ast-and-expr

(define (lazy:ast-and-expr op num-anc)
... Full Code ... )
 {11} <AndExpr> ::= (and <Expr> <Expr>+ )
 NOTE: num-anc is dummy here, since it is always 0 for AndExpr


lazy:ast-equality-expr

(define (lazy:ast-equality-expr op num-anc)
... Full Code ... )
 {12} <EqualityExpr> ::= (=  <Expr> <Expr> )
                         | (!=  <Expr> <Expr> )
 NOTE: num-anc is dummy here, since it is always 0 for EqualityExpr


lazy:ast-relational-expr

(define (lazy:ast-relational-expr op num-anc)
... Full Code ... )
 {13} <RelationalExpr> ::= (<  <Expr> <Expr> )
                           | (>  <Expr> <Expr> )
                           | (<=  <Expr> <Expr> )
                           | (>=  <Expr> <Expr> )
 NOTE: num-anc is dummy here, since it is always 0 for RelationalExpr


lazy:ast-additive-expr

(define (lazy:ast-additive-expr op num-anc)
... Full Code ... )
 {14} <AdditiveExpr> ::= (+  <Expr> <Expr> )
                         | (-  <Expr> <Expr>? )
 NOTE: num-anc is dummy here, since it is always 0 for AdditiveExpr


lazy:ast-multiplicative-expr

(define (lazy:ast-multiplicative-expr op num-anc)
... Full Code ... )
 {15} <MultiplicativeExpr> ::= (*  <Expr> <Expr> )
                               | (div  <Expr> <Expr> )
                               | (mod  <Expr> <Expr> )
 NOTE: num-anc is dummy here, since it is always 0 for MultiplicativeExpr


lazy:ast-union-expr

(define (lazy:ast-union-expr op num-anc)
... Full Code ... )
 {16} <UnionExpr> ::= (union-expr  <Expr> <Expr>+ )


lazy:ast-path-expr

(define (lazy:ast-path-expr op num-anc)
... Full Code ... )
 {17} <PathExpr> ::= (path-expr  <FilterExpr> <Step>+ )


lazy:ast-filter-expr

(define (lazy:ast-filter-expr op num-anc)
... Full Code ... )
 {18} <FilterExpr> ::= (filter-expr (primary-expr  <Expr> )
                                    <Predicate>* )


lazy:ast-variable-reference

(define (lazy:ast-variable-reference op num-anc)
... Full Code ... )
 {19} <VariableReference> ::= (variable-reference  <String> )


lazy:ast-literal

(define (lazy:ast-literal op num-anc)
... Full Code ... )
 {20} <Literal> ::= (literal  <String> )


lazy:ast-number

(define (lazy:ast-number op num-anc)
... Full Code ... )
 {21} <Number> :: (number  <Number> )


lazy:ast-function-call

(define (lazy:ast-function-call op num-anc)
... Full Code ... )
 {22} <FunctionCall> ::= (function-call (function-name  <String> )
                                        (argument  <Expr> )* )


lazy:ast-function-arguments

(define (lazy:ast-function-arguments op-lst)
... Full Code ... )
 {22a} ( (argument  <Expr> )* )
 Returns: (listof expr-impl) or #f



Highest level API functions

 The API is identical to the API of a context-based SXPath (here we even use
 API helpers from "xpath-context.scm"). For convenience, below we repeat
 comments for the API (borrowed from "xpath-context.scm")

 xpath-string - an XPath location path (a string)
 ns+na - can contain 'ns-binding' and/or 'num-ancestors' and/or none of them
 ns-binding - declared namespace prefixes (an optional argument)
  ns-binding ::= (listof (prefix . uri))
  prefix - a symbol
  uri - a string
 num-ancestors - number of ancestors required for resulting nodeset. Can
  generally be omitted and is than defaulted to 0, which denotes a _usual_
  nodeset. If a negative number, this signals that all ancestors should be
  remembered in the context

 Returns: (lambda (nodeset position+size var-binding) ...)
 position+size - the same to what was called 'context' in TXPath-1
 var-binding - XPath variable bindings (an optional argument)
  var-binding = (listof (var-name . value))
  var-name - (a symbol) a name of a variable
  value - its value. The value can have the following type: boolean, number,
  string, nodeset. NOTE: a node must be represented as a singleton nodeset

lazy:api-helper

(define (lazy:api-helper grammar-parser ast-parser)
... Full Code ... )
 Helper for constructing several highest-level API functions


lazy:txpath

(define lazy:txpath
... Full Code ... )


lazy:xpath-expr

(define lazy:xpath-expr
... Full Code ... )


lazy:sxpath

(define lazy:sxpath
... Full Code ... )
 Support for native sxpath syntax


Code

lazy:or

Index
 Implement 'or' as a function, so that we could 'apply' it
(define (lazy:or . args)
  (if (null? args) #f (or (car args) (apply lazy:or (cdr args)))))
cond-expand
Index
 Escaping the ## for some Scheme implementations
(cond-expand
 (gambit
  ; The following macro constructs Gambit-specific ids on the fly
  ; Borrowed from "http.scm"
  (define-macro (_gid id)
    (string->symbol (string-append "##" (symbol->string id))))
  )
 (chicken
  ; The following macro encapsulates the function ##sys#structure?
  ; Thanks to Thomas Chust and Felix Winkelmann for the explanation of
  ; qualified symbols in Chicken
  (define-macro (chk:sys-structure?)
    (string->symbol
     (string-append (string (integer->char 3)) "sys" "structure?")))
  )
 (else
  #t))

lazy:promise?

Index
 Predicate for detecting a promise
 There is no such a predicate in R5RS, so different Scheme implementations
 use different names for this functionality
(define lazy:promise?
  (cond-expand
   (plt promise?)
   (bigloo
    procedure?   ; ATTENTION: returns #t in more general situations
    )
   (chicken
    ; Thanks to Zbigniew Szadkowski <zbigniewsz@gmail.com>
    ; for the insight of this function
    (lambda (p) ((chk:sys-structure?) p 'promise))
    )
   (gambit
    (_gid promise?)
    )
   (else
    (lambda (obj) #f)   ; ATTENTION: just makes the approach applicable for
                        ; conventional SXML documents
   )))

lazy:output-siblings

Index
 A helper that tests sibling nodes with respect to the test-pred? and returns
 them in the lazy manner. Each of the siblings may be a promise.
 This function is applied by axes: child, attribute, namespace,
 following-sibling, preceding-sibling
(define (lazy:output-siblings test-pred? siblings ancestors)
  (letrec
      ((iterate-siblings
        (lambda (src)
          (let loop ((src src) (res '()))
            (cond
              ((null? src)  ; iteration is over
               (reverse res))
              ((lazy:promise? (car src))               
                (reverse  ; otherwise - return the result with a promise
                 (cons
                  (delay
                    (iterate-siblings
                     (append (as-nodeset (force (car src))) (cdr src))))
                  res)))
              (else  ; the first src is a node
               (loop (cdr src)
                     (if (test-pred? (car src))
                         (cons
                          (if (null? ancestors)  ; don't construct context
                              (car src)
                              (draft:make-context (car src) ancestors))
                          res)
                         res))))))))
    (iterate-siblings siblings)))

lazy:find-foll-siblings

Index
 Returns nodeset membert that are following siblings of the given node
 Nodeset members may be promises
(define (lazy:find-foll-siblings node nodeset)
  (cond
    ((null? nodeset)  ; not found
     '())
    ((lazy:promise? (car nodeset))
     (lazy:find-foll-siblings
      node
      (append (as-nodeset (force (car nodeset)))
              (cdr nodeset))))
    ; (car nodeset) an ordinary node
    ((eq? node (car nodeset))
     (cdr nodeset))
    (else
     (lazy:find-foll-siblings node (cdr nodeset)))))

lazy:find-prec-siblings

Index
 Returns nodeset members that are preceding siblings of the given node
 Nodeset members may be promises
(define (lazy:find-prec-siblings node nodeset)
  (let loop ((nodeset nodeset)
             (res '()))
    (cond
      ((null? nodeset)  ; not found
       '())
      ((lazy:promise? (car nodeset))
       (loop
        (append (as-nodeset (force (car nodeset)))
                (cdr nodeset))
        res))
      ; the first member in a nodeset an ordinary node
      ((eq? node (car nodeset))
       res)
      (else
       (loop (cdr nodeset)
             (cons (car nodeset) res))))))

lazy:ast-axis-specifier

Index
 {5} <AxisSpecifier> ::= (axis-specifier  <AxisName> )
 {6} <AxisName> ::= (ancestor)
                    | (ancestor-or-self)
                    | (attribute)
                    | (child)
                    | (descendant)
                    | (descendant-or-self)
                    | (following)
                    | (following-sibling)
                    | (namespace)
                    | (parent)
                    | (preceding)
                    | (preceding-sibling)
                    | (self)
                    | (arc)  ; the following 3 are added by SXLink
                    | (traverse)
                    | (traverse-arc)
 Returns:  (cons lambda num-ancestors)
(define (lazy:ast-axis-specifier op num-anc)
  (if
   (not (eq? (car op) 'axis-specifier))
   (draft:signal-semantic-error "not an AxisSpecifier - " op)
   (case (caadr op)  ; AxisName
     ((ancestor)
      (cons lazy:ancestor #f))
     ((ancestor-or-self)
      (cons lazy:ancestor-or-self #f))
     ((attribute)
      (cons lazy:attribute (draft:na-minus-nneg num-anc 1)))
     ((child)
      (cons lazy:child (draft:na-minus-nneg num-anc 1)))
     ((descendant)
      (cons lazy:descendant (draft:na-minus-nneg num-anc 1)))
     ((descendant-or-self)
      (cons lazy:descendant-or-self num-anc))
     ((following)
      (cons lazy:following #f))
     ((following-sibling)
      (cons lazy:following-sibling (draft:na-max num-anc 1)))
     ((namespace)
      (cons lazy:namespace (draft:na-minus-nneg num-anc 1)))
     ((parent)
      (cons lazy:parent (draft:na+ num-anc 1)))
     ((preceding)
      (cons lazy:preceding #f))
     ((preceding-sibling)
      (cons lazy:preceding-sibling (draft:na-max num-anc 1)))
     ((self)
      (cons lazy:self num-anc))     
     (else
      (draft:signal-semantic-error "unknown AxisName - " op)))))

lazy:api-helper

Index
 Helper for constructing several highest-level API functions
(define (lazy:api-helper grammar-parser ast-parser)
  (lambda (xpath-string . ns+na)
    (call-with-values
     (lambda () (draft:arglist->ns+na ns+na))
     (lambda (ns-binding num-anc)
       (and-let*
        ((ast (grammar-parser xpath-string ns-binding))
         (impl-lst (ast-parser ast num-anc)))
        (let ((query-impl (car impl-lst)))
          (lambda (node . var-binding)
            (let ((query-res
                   (query-impl
                    (as-nodeset node) (cons 1 1)
                    (if (null? var-binding) var-binding (car var-binding)))))
              (if
               (and num-anc (zero? num-anc) (nodeset? query-res))
               (lazy:map sxml:context->node query-res)
               query-res)))))))))

lazy:txpath

Index
(define lazy:txpath (lazy:api-helper txp:xpath->ast lazy:ast-location-path))

lazy:xpath-expr

Index
(define lazy:xpath-expr (lazy:api-helper txp:expr->ast lazy:ast-expr))

lazy:sxpath

Index
 Support for native sxpath syntax
(define lazy:sxpath (lazy:api-helper txp:sxpath->ast lazy:ast-expr))

lazy:null?

Index
 Checks whether the nodeset is empty
 Note that a promise can evaluate to an empty list, and thus a nodeset
 consisting of promises only may potentially be empty
(define (lazy:null? nodeset)
  (cond
    ((null? nodeset) #t)
    ((not (null? (filter   ; contains at least one non-promise
                  (lambda (node) (not (lazy:promise? node)))
                  nodeset)))
     #f)
    (else  ; all nodeset members are promises
     (let iter-promises ((nset nodeset))
       (cond
         ((null? nset) #t)
         ((lazy:null? (as-nodeset (force (car nset))))
          (iter-promises (cdr nset)))
         (else #f))))))

lazy:map

Index
 Like conventional map, but applicable to a lazy nodeset
(define (lazy:map func nodeset)
  (cond
    ((null? nodeset)  ; iteration is over
     nodeset)
    ((lazy:promise? (car nodeset))
     (list
      (delay
        (lazy:map func
                  (append (as-nodeset (force (car nodeset)))
                          (cdr nodeset))))))
    (else  ; the first member is a node
     (cons (func (car nodeset))
           (lazy:map func (cdr nodeset))))))

lazy:filter

Index
 Lazy analogue for filter
(define (lazy:filter func nodeset)
  (cond
    ((null? nodeset)  ; iteration is over
     nodeset)
    ((lazy:promise? (car nodeset))
     (list
      (delay
        (lazy:filter func
                  (append (as-nodeset (force (car nodeset)))
                          (cdr nodeset))))))
    ; the first member is a node
    ((func (car nodeset))
     (cons (car nodeset)
           (lazy:filter func (cdr nodeset))))
    (else  ; the first member doesn't satisfy the predicate
     (lazy:filter func (cdr nodeset)))))

lazy:car

Index
 Like conventional car, but for a lazy nodeset
(define (lazy:car nodeset)
  (cond
    ; Checking for a safe variant
    ;((null? nodeset)  ; failed
    ; #f)
    ((lazy:promise? (car nodeset))
     (let ((nset-car (force (car nodeset))))
       (lazy:car
        ((if (nodeset? nset-car) append cons)
         nset-car (cdr nodeset)))))
    (else
     (car nodeset))))

lazy:cdr

Index
 Like conventional cdr
(define (lazy:cdr nodeset)
  (if
   (lazy:promise? (car nodeset))
   (let ((nset-car (force (car nodeset))))
     (lazy:cdr
      ((if (nodeset? nset-car) append cons)
       nset-car (cdr nodeset)))))
  (cdr nodeset))

lazy:length

Index
 Like conventional length, but for a lazy nodeset
 ATTENTION: it has to force all the nodeset members in order to determine
 the length properly
(define (lazy:length nodeset)
  (cond
    ((null? nodeset) 0)
    ((lazy:promise? (car nodeset))
     (let ((nset-car (force (car nodeset))))
       (lazy:length
        ((if (nodeset? nset-car) append cons)
         nset-car (cdr nodeset)))))
    (else
     (+ 1 (lazy:length (cdr nodeset))))))

lazy:result->list

Index
(define (lazy:result->list nodeset)
  (let iter-nset ((nset nodeset)
                  (res '()))
    (cond
      ((null? nset)  ; finished scanning
       (reverse res))
      ((lazy:promise? (car nset))
       (iter-nset (append (as-nodeset (force (car nset))) (cdr nset))
                  res))
      (else  ; the first member is a node
       (iter-nset (cdr nset)
                  (cons (car nset) res))))))

lazy:node->sxml

Index
 Converts the lazy node to SXML, by forcing all of its descendants
 The node itself is not a promise
(define (lazy:node->sxml node)
  (letrec
      ((force-nodeset
        (lambda (nodeset)
          (cond
            ((null? nodeset) nodeset)
            ((lazy:promise? (car nodeset))
             (let ((nset-car (force (car nodeset))))               
               (force-nodeset
                ((if (nodeset? nset-car) append cons)
                 nset-car (cdr nodeset)))))
            (else
             (cons (lazy:node->sxml (car nodeset))
                   (force-nodeset (cdr nodeset))))))))
    (if
     (or (not (pair? node))
         (null? ((sxml:descendant lazy:promise?) node)))
     node     ; will not make a copy of the node
     (cons (car node) (force-nodeset (cdr node))))))

lazy:reach-root

Index
 Reaches the root of the root of the contextset
 Result: singleton nodeset
(define (lazy:reach-root contextset)
  (letrec
      ((find-root
        (lambda (src prev-result)
          (let loop ((src src)
                     (res '())
                     (prev-result prev-result))
            (cond
              ((null? src)  ; nothing more to do
               (reverse res))
              ((lazy:promise? (car src))
               (if
                (null? res)  ; need to force this
                (loop (append (as-nodeset (force (car src)))
                              (cdr src))
                      res prev-result)
                (reverse
                 (cons (delay (find-root src prev-result))
                       res))))
              (else  ; (car src) is the ordinary node
               (let ((rt (if (sxml:context? (car src))
                             (draft:list-last
                              (sxml:context->ancestors-u (car src)))
                             (car src))))
                 (loop (cdr src)
                       (if
                        (memq rt prev-result)  ; already returned
                        res (cons rt res))
                       (cons rt prev-result)))))))))
    (find-root contextset '())))

lazy:contextset->nodeset

Index
 Analogue for draft:contextset->nodeset for the lazy case
(define (lazy:contextset->nodeset obj)
  (letrec
      ((iter-nset
        (lambda (nset)
          (cond
            ((null? nset) nset)
            ((lazy:promise? (car nset))
             (list
              (delay (iter-nset (append (as-nodeset (force (car nset)))
                                        (cdr nset))))))
            (else  ; (car nset) is a node
             (cons
              (sxml:context->node (car nset))
              (iter-nset (cdr nset))))))))
  (if
   (nodeset? obj)
   (iter-nset obj)
   obj)))

lazy:recover-contextset

Index
 Lazy analogue for draft:recover-contextset
(define (lazy:recover-contextset nodeset root-node num-anc)
  (cond
    ((null? nodeset)  ; nothing more to do
     '())
    ((lazy:promise? (car nodeset))
     (delay (lazy:recover-contextset
             (append (as-nodeset (force (car nodeset)))
                     (cdr nodeset))
             root-node num-anc)))
    (else  ; (car nodeset) is a common node
     (cons
      (draft:smart-make-context
       (car nodeset)
       (((sxml:ancestor (lambda (x) #t)) root-node) (car nodeset))
       num-anc)
      (lazy:recover-contextset (cdr nodeset) root-node num-anc)))))

lazy:find-proper-context

Index
 Makes a context-set from a nodeset supplied, with the num-anc required
 Members of the nodeset are known to be descendants-or-selves of
 (map sxml:context->node context-set)
(define (lazy:find-proper-context nodeset context-set num-anc)
  (let* ((descend (lazy:descendant-or-self sxml:node? num-anc))
         (possible-ancestors
          (map
           cdr  ; ignore starting '*CONTEXT* for a faster search
           (map-union
            (lambda (node)
              ; Has to be evaluated in the active manner, since all of the
              ; candidates generally have to be scanned
              (lazy:result->list (descend node)))
            ;(lazy:result->list ancestors-set)
            (map-union
             sxml:context->ancestors
             (lazy:result->list context-set))))))
    (let iter-nset ((nodeset nodeset)
                    (res '()))   ; DL: was: res
      (cond
        ((null? nodeset)  ; scanning is over
         (reverse res))
        ((lazy:promise? (car nodeset))
         (if (null? res)  ; result is still null => have to force
             (iter-nset (append (as-nodeset (force (car nodeset)))
                                (cdr nodeset))
                        res)
             (reverse
              (cons
               (delay (iter-nset (append (as-nodeset (force (car nodeset)))
                                         (cdr nodeset))
                                 '()))
               res))))
        ((sxml:context? (car nodeset))  ; already a context
         (iter-nset (cdr nodeset)
                    (cons (car nodeset) res)))
        ((assq (car nodeset) possible-ancestors)
         => (lambda (ancestors)
              (iter-nset (cdr nodeset)
                         (cons
                          (draft:make-context
                           (car ancestors)  ; = (car nodeset)
                           (cdr ancestors))
                          res))))
        (else  ; this is a newly constructed node
         ; Keep it as is
         (iter-nset (cdr nodeset)
                    (cons (car nodeset) res)))))))

lazy:ancestor

Index
 Ancestor axis
 It should be noted that ancestors of the context node are already forced
(define (lazy:ancestor test-pred? . num-ancestors)
  (let* ((num-anc (if (null? num-ancestors) 0 (car num-ancestors))))
    (lambda (node)  ; not a nodeset
      (if
       (sxml:context? node)
       (let loop ((ancs-to-view (sxml:context->ancestors-u node))
                  (res '()))
         (cond
           ((null? ancs-to-view)  ; processed everyone
            (reverse res)  ; reverse document order required
            )
           ((test-pred? (car ancs-to-view))  ; can add it to result
            (loop
             (cdr ancs-to-view)
             (cons
              (draft:smart-make-context
               (car ancs-to-view) (cdr ancs-to-view) num-anc)
              res)))
           (else  ; current node doesn't satisfy the predicate
            (loop (cdr ancs-to-view) res))))
       '()  ; no ancestors
       ))))

lazy:ancestor-or-self

Index
 Ancestor-or-self axis
 It should be noted that ancestors of the context node are already forced
(define (lazy:ancestor-or-self test-pred? . num-ancestors)
  (let ((num-anc (if (null? num-ancestors) 0 (car num-ancestors))))
    (lambda (node)  ; not a nodeset
      (cond
        ((sxml:context? node)
         (let loop ((ancs-to-view (sxml:context->content-u node))
                    (res '()))
           (cond
             ((null? ancs-to-view)  ; processed everyone
              (reverse res)  ; reverse document order required
              )
             ((test-pred? (car ancs-to-view))  ; can add it to result
              (loop
               (cdr ancs-to-view)
               (cons
                (draft:smart-make-context
                 (car ancs-to-view) (cdr ancs-to-view) num-anc)
                res)))
             (else  ; current node doesn't satisfy the predicate
              (loop (cdr ancs-to-view) res)))))
        ; ordinary SXML node
        ((test-pred? node)  ; satisfies the predicate
         (list node))
        (else
         '())))))

lazy:attribute

Index
 Attribute axis
(define (lazy:attribute test-pred? . num-ancestors)
  (let ((num-anc (if (null? num-ancestors) 0 (car num-ancestors))))
    (letrec
        ((find-attr-node
          ; Either returns an attribute node, or #f
          ; Nodeset members may be promises
          (lambda (nodeset)
            (cond
              ((null? nodeset)  ; failed
               #f)
              ((lazy:promise? (car nodeset))
               (find-attr-node
                (append (as-nodeset (force (car nodeset)))
                        (cdr nodeset))))
              ; (car nodeset) an ordinary node
              (((ntype?? '@) (car nodeset))
               (car nodeset))
              (else #f)))))
      (lambda (node)  ; not a nodeset
        (cond
          ((not (pair? node)) '())   ; no attributes
          ; (car node) is always a symbol
          ((sxml:context-u? node)  ; a context node
           (let ((attr-node (find-attr-node (sxml:context->node-u node))))
             (if (not attr-node)  ; not found
                 '()
                 ((lazy:child test-pred? num-anc)
                  (if (and num-anc (zero? num-anc))
                      attr-node
                      (draft:make-context
                       attr-node (sxml:context->content-u node)))))))
          (else  ; an ordinary node, and is a pair
           (let ((attr-node (find-attr-node node)))
             (if (not attr-node)  ; not found
                 '()
                 ((lazy:child test-pred? num-anc)
                  (if (and num-anc (zero? num-anc))
                      attr-node                      
                      (draft:make-context attr-node (list node))))))))))))           

lazy:child

Index
 Child axis
(define (lazy:child test-pred? . num-ancestors)
  (let ((num-anc (if (null? num-ancestors) 0 (car num-ancestors))))    
    (lambda (node)  ; not a nodeset
      (cond
        ((not (pair? node))  ; no children
         '())
        ; (car node) is always a symbol
        ((sxml:context-u? node)  ; a context node
         (let ((this (sxml:context->node-u node)))
           (if
            (or (not (pair? this))
                (memq (car this) '(*PI* *COMMENT* *ENTITY*)))
            '()  ; no children
            (lazy:output-siblings
             test-pred?
             (cdr this)  ; gives its children
             (draft:list-head (sxml:context->content-u node) num-anc)))))
        ; an ordinary node, and is a pair
        ((memq (car node) '(*PI* *COMMENT* *ENTITY*))
         '())
        (else
         (lazy:output-siblings
          test-pred?
          (cdr node)  ; gives its children
          (if (and num-anc (zero? num-anc))
              '() (list node))))))))

lazy:descendant

Index
 Descendant axis
(define (lazy:descendant test-pred? . num-ancestors)
  (let* ((num-anc (if (null? num-ancestors) 0 (car num-ancestors)))
         (child (lazy:child sxml:node? num-anc)))
    (lambda (node)  ; not a nodeset
      (let rpt ((res '())
                (more (child node)))        
        (cond
          ((null? more)  ; no more candidates         
           (reverse res))
          ((lazy:promise? (car more))  ; need to force it
           (reverse
            (cons
             (delay (rpt '()
                         (append (as-nodeset (force (car more)))
                                 (cdr more))))
             res)))
          (else  ; first in more is a node
           (rpt (if (test-pred? (sxml:context->node (car more)))
                    (cons (car more) res)
                    res)
                (append (child (car more)) (cdr more)))))))))

lazy:descendant-or-self

Index
 Descendant-or-self axis
(define (lazy:descendant-or-self test-pred? . num-ancestors)
  (let* ((num-anc (if (null? num-ancestors) 0 (car num-ancestors)))
         (child (lazy:child sxml:node? num-anc)))
    (lambda (node)  ; not a nodeset
      (let rpt ((res '())
                (more (list node)))
        (cond
          ((null? more)  ; no more candidates         
           (reverse res))
          ((lazy:promise? (car more))  ; need to force it
           (reverse
            (cons
             (delay (rpt '()
                         (append (as-nodeset (force (car more)))
                                 (cdr more))))
             res)))
          (else  ; first in more is a node
           (rpt (if (test-pred? (sxml:context->node (car more)))
                    (cons (car more) res)
                    res)
                (append (child (car more)) (cdr more)))))))))

lazy:following

Index
 Following axis
(define (lazy:following test-pred? . num-ancestors)
  (let* ((num-anc (if (null? num-ancestors) 0 (car num-ancestors)))
         (descend (lazy:descendant-or-self test-pred? num-anc)))
    (lambda (node)  ; not a nodeset
      (if
       (sxml:context? node)
       (let loop ((curr-node (sxml:context->node-u node))
                  (ancs-to-view (sxml:context->ancestors-u node))
                  (foll-siblings '())
                  (descendants '())
                  (res '()))
         (cond
           ((null? descendants)  ; candidates for result
            (cond
              ((null? foll-siblings)  ; no more siblings of the curr-node
               (if
                (null? ancs-to-view)  ; processed everyone                 
                (reverse res)
                (loop (car ancs-to-view)
                      (cdr ancs-to-view)
                      (lazy:find-foll-siblings
                       curr-node
                       (cdr  ; parent is an element => cdr gives its children
                        (car ancs-to-view)))
                      '()
                      res)))
              ((lazy:promise? (car foll-siblings))
               (reverse
                (cons
                 (delay
                   (loop curr-node ancs-to-view
                         (append (as-nodeset (force (car foll-siblings)))
                                 (cdr foll-siblings))
                         '() '()))
                 res)))
              (else  ; (car foll-siblings) is a node
               (loop curr-node ancs-to-view
                     (cdr foll-siblings)
                     (descend  ; descendants are currently null
                      (draft:smart-make-context
                       (car foll-siblings)
                       ancs-to-view num-anc))
                     res))))
           ((lazy:promise? (car descendants))  ; need to force descendant axis
            (reverse
             (cons
              (delay
                (loop curr-node ancs-to-view foll-siblings
                      (append (as-nodeset (force (car descendants)))
                              (cdr descendants))
                      '()))
              res)))
           (else  ; the first in descendants is a node
            (loop curr-node ancs-to-view foll-siblings
                  (cdr descendants) (cons (car descendants) res)))))
       '()  ; no following members       
       ))))

lazy:following-sibling

Index
 Following-sibling axis
(define (lazy:following-sibling test-pred? . num-ancestors)
  (let ((num-anc (if (null? num-ancestors) 0 (car num-ancestors))))
    (lambda (node)  ; not a nodeset
      (if
       (and (sxml:context? node)
            (not (null? (sxml:context->ancestors-u node))))
       (lazy:output-siblings
        test-pred?
        (lazy:find-foll-siblings
         (sxml:context->node-u node)
         (cdr  ; parent is an element => cdr gives its children
          (car (sxml:context->ancestors-u node))))
        (draft:list-head
         (sxml:context->ancestors-u node) num-anc))
       '()  ; no parent => no siblings
       ))))

lazy:namespace

Index
 Namespace axis
 Since a namespace axis somewhat redundant for SXML, we'll provide a
 not-very-effective implementation for it
(define (lazy:namespace test-pred? . num-ancestors)
  (let ((num-anc (if (null? num-ancestors) 0 (car num-ancestors))))    
    (lambda (node)  ; not a nodeset
      (lazy:filter
       (lambda (context)
         (test-pred? (sxml:context->node context)))
       ((lazy:sxpath '(@@ *NAMESPACES* *) num-anc) node)))))

lazy:parent

Index
 Parent axis
 It should be noted that the parent of the context node is already forced
(define (lazy:parent test-pred? . num-ancestors)
  (let ((num-anc (if (null? num-ancestors) 0 (car num-ancestors))))
    (lambda (node)  ; not a nodeset
      (if
       (and (sxml:context? node)
            (not (null? (sxml:context->ancestors-u node)))
            (test-pred? (car (sxml:context->ancestors-u node))))
       (draft:smart-make-context
        (car (sxml:context->ancestors-u node))
        (cdr (sxml:context->ancestors-u node))
        num-anc)
       '()  ; no parent
       ))))

lazy:preceding

Index
 Preceding axis
(define (lazy:preceding test-pred? . num-ancestors)
  (let* ((num-anc (if (null? num-ancestors) 0 (car num-ancestors)))
         (descend (lazy:descendant-or-self test-pred? num-anc)))
    (lambda (node)  ; not a nodeset
      (if
       (sxml:context? node)
       (let loop ((curr-node (sxml:context->node-u node))
                  (ancs-to-view (sxml:context->ancestors-u node))
                  (prec-siblings '())
                  (descendants '())
                  (res '()))
         (cond
           ((null? descendants)  ; candidates for result
            (cond
              ((null? prec-siblings)  ; no more siblings of the curr-node
               (if
                (null? ancs-to-view)  ; processed everyone                 
                (reverse res)
                (loop (car ancs-to-view)
                      (cdr ancs-to-view)
                      (lazy:find-prec-siblings
                       curr-node
                       (cdr  ; parent is an element => cdr gives its children
                        (car ancs-to-view)))
                      descendants  ; is null
                      res)))
              ((lazy:promise? (car prec-siblings))
               (reverse
                (cons
                 (delay
                   (loop curr-node ancs-to-view
                         (append (as-nodeset (force (car prec-siblings)))
                                 (cdr prec-siblings))
                         descendants  ; is null
                         '()))
                 res)))
              (else  ; (car prec-siblings) is a node
               (loop curr-node ancs-to-view
                     (cdr prec-siblings)
                     (reverse
                      (descend  ; descendants are currently null
                       (draft:smart-make-context
                        (car prec-siblings)
                        ancs-to-view num-anc)))
                     res))))
           ((lazy:promise? (car descendants))  ; need to force descendant axis
            (reverse
             (cons
              (delay
                (loop curr-node ancs-to-view prec-siblings
                      (append (reverse (as-nodeset (force (car descendants))))
                              (cdr descendants))
                      '()))
              res)))
           (else  ; the first in descendants is a node
            (loop curr-node ancs-to-view prec-siblings
                  (cdr descendants) (cons (car descendants) res)))))
       '()  ; no preceding members       
       ))))    

lazy:preceding-sibling

Index
 Preceding-sibling axis
(define (lazy:preceding-sibling test-pred? . num-ancestors)
  (let ((num-anc (if (null? num-ancestors) 0 (car num-ancestors))))
    (lambda (node)  ; not a nodeset
      (if
       (and (sxml:context? node)                  
            (not (null? (sxml:context->ancestors-u node))))
       (draft:siblings->context-set
        ((sxml:filter test-pred?)
         (lazy:find-prec-siblings
          (sxml:context->node-u node)
          (cdr  ; parent is an element => cdr gives its children
           (car (sxml:context->ancestors-u node)))))
        (draft:list-head
         (sxml:context->ancestors-u node) num-anc))
       '()  ; no parent => no siblings
       ))))

lazy:self

Index
 Self axis
 Shortens the context if it contains more nodes than specified by num-ancestor
 In most cases, this work can be considered redundant; however, it eliminates
 some classes of error that are hard to detect
(define (lazy:self test-pred? . num-ancestors)
  (let ((num-anc (if (null? num-ancestors) 0 (car num-ancestors))))
    (lambda (node)  ; not a nodeset
      (if (sxml:context? node)
          (if (test-pred? (sxml:context->node-u node))
              (list (draft:smart-make-context
                     (sxml:context->node-u node)
                     (sxml:context->ancestors-u node)
                     num-anc))
              '())
          (if (test-pred? node) (list node) '())))))

lazy:axis-consume-nodeset

Index
 Given the axis of the form
  Node -> Nodeset
 produces the axis of the form
  Node|Nodeset -> Nodeset
(define (lazy:axis-consume-nodeset axis)
  (letrec
      ((iterate-nodeset
        ; candidates - candidate nodes for a result
        (lambda (src candidates)
          (let loop ((src src)
                     (candidates candidates)
                     (res '()))
            (cond
              ((null? candidates)  ; consume the following node from src
               (cond
                 ((null? src)  ; iteration is over
                  (reverse res))
                 ((lazy:promise? (car src))
                  (if
                   (null? res)  ; result is still empty, need to force src
                   (let ((src-car (as-nodeset (force (car src)))))
                     (cond
                       ((null? src-car)  ; a rare practical situation
                        (loop (cdr src) candidates res))
                       ((lazy:promise? (car src-car))  ; this shouldn't happen
                        (loop (append src-car (cdr src))
                              candidates
                              res))
                       (else  ; we can finally apply the axis
                        (loop (append (cdr src-car) (cdr src))
                              (axis (car src-car))  ; candidates are null
                              res))))
                   (reverse  ; otherwise - return the result with a promise
                    (cons
                     (delay (iterate-nodeset src candidates))
                     res))))
                 (else  ; (car src) is a node
                  (loop (cdr src)
                        (axis (car src))  ; candidates are null
                        res))))
              ((lazy:promise? (car candidates))               
               ; First candidate is a promise
               (if
                (null? res)  ; result is still empty, need to force candidate
                (let ((cand-car (as-nodeset (force (car candidates)))))
                  (cond
                    ((null? cand-car)  ; generally, (cdr candidates)=null
                     (loop src (cdr candidates) res))
                    ((lazy:promise? (car cand-car))  ; this shouldn't happen
                     (loop src
                           (append cand-car (cdr candidates))
                           res))
                    (else  ; add candidate to result
                     (loop src
                           (append (cdr cand-car) (cdr candidates))
                           (list (car cand-car))   ; res is null
                           ))))
                (reverse  ; otherwise - return the result with a promise
                 (cons
                  (delay (iterate-nodeset src candidates))
                  res))))
              (else   ; the first candidate is a node
               (loop src (cdr candidates)
                     (cons (car candidates) res))))))))
    (lambda (nodeset)  ; node or nodeset
      (cond
        ((null? nodeset)  ; nothing to do
         '())
        ((and (pair? nodeset) (symbol? (car nodeset)))  ; node
         (axis nodeset))
        (else  ; the general case
         (iterate-nodeset nodeset '()))))))

lazy:string

Index
 The counterpart to XPath 'string' function (section 4.2 XPath Rec.)
 Converts a given object to a string
(define (lazy:string object)
  (cond
    ((string? object) object)
    ((nodeset? object) (if (lazy:null? object)
                           ""
                           (lazy:string-value (lazy:car object))))
    ((number? object)
     (if (and (rational? object) (not (integer? object)))  ; like 1/2
         (number->string (exact->inexact object))
         (number->string object)))
    ((boolean? object) (if object "true" "false"))
    (else   ; Unknown type -> empty string.   
     "")))

lazy:boolean

Index
 The counterpart to XPath 'boolean' function (section 4.3 XPath Rec.)
 Converts its argument to a boolean
(define (lazy:boolean object)
  (cond
    ((boolean? object) object)
    ((number? object) (not (= object 0)))
    ((string? object) (> (string-length object) 0))
    ((nodeset? object) (not (lazy:null? object)))
    (else  ; Not specified in XPath Rec.
     #f)))

lazy:number

Index
 The counterpart to XPath 'number' function (section 4.4 XPath Rec.)
 Converts its argument to a number
 NOTE: 
  1. The argument is not optional (yet?)
  2. string->number conversion is not IEEE 754 round-to-nearest
  3. NaN is represented as 0
(define (lazy:number obj)
  (cond
    ((number? obj) obj)
    ((string? obj)
     (let ((nmb (call-with-input-string obj read)))
       (if (number? nmb)
	 nmb
	 0))) ; NaN
    ((boolean? obj) (if obj 1 0))
    ((nodeset? obj) (lazy:number (lazy:string obj)))
    (else 0))) ; unknown datatype

lazy:string-value

Index
 Returns a string value for a given node in accordance to
 XPath Rec. 5.1 - 5.7
 Undocumented functionality - can be applied for a node that is a promise
(define (lazy:string-value node)
  (cond
    ((lazy:promise? node)
     (let ((value (force node)))
       (if (nodeset? value)
           (apply string-append
                  (map lazy:string-value value))
           (lazy:string-value value))))
    ((not (pair? node))  ; a text node?
     (if (string? node)
         node ""))
    ((lazy:null? (cdr node))  ; null content
     "")
    (else
     (apply
      string-append
      (cons ""
            (map
             lazy:string-value
             (let ((frst (lazy:car (cdr node))))
               (if
                (and (pair? frst) (eq? '@ (car frst)))  ; attribute node
                (lazy:cdr (cdr node))
                (cdr node)))))))))

lazy:equality-cmp

Index
 A helper for XPath equality operations: = , !=
  'bool-op', 'number-op' and 'string-op' are comparison operations for 
 a pair of booleans,  numbers and strings respectively
(define (lazy:equality-cmp bool-op number-op string-op)
  (lambda (obj1 obj2)
    (cond
      ((and (not (nodeset? obj1)) (not (nodeset? obj2)))  
       ; neither object is a nodeset
       (cond
         ((boolean? obj1) (bool-op obj1 (sxml:boolean obj2)))
         ((boolean? obj2) (bool-op (sxml:boolean obj1) obj2))
         ((number? obj1) (number-op obj1 (sxml:number obj2)))
         ((number? obj2) (number-op (sxml:number obj1) obj2))
         (else  ; both objects are strings
          (string-op obj1 obj2))))
      ((and (nodeset? obj1) (nodeset? obj2))  ; both objects are nodesets
       (let first ((str-set1 (lazy:map lazy:string-value obj1))
                   (str-set2 (lazy:map lazy:string-value obj2)))
         (cond
           ((null? str-set1) #f)
           ((lazy:promise? (car str-set1))   ; time to get the next portion
            (first (append (as-nodeset (force (car str-set1)))
                           (cdr str-set1))
                   str-set2))
           ((let second ((elem (car str-set1))
                         (set2 str-set2))
              (cond
                ((null? set2) #f)
                ((lazy:promise? (car set2))   ; time to get the next portion
                 (second elem
                         (append (as-nodeset (force (car set2)))
                                 (cdr set2))))
                ((string-op elem (car set2)) #t)
                (else (second elem (cdr set2))))) #t)
           (else
            (first (cdr str-set1) str-set2)))))
      (else  ; one of the objects is a nodeset, the other is not
       (call-with-values
        (lambda ()
          (if (nodeset? obj1) (values obj1 obj2) (values obj2 obj1)))
        (lambda (nset elem)
          (cond
            ((boolean? elem) (bool-op elem (lazy:boolean nset)))
            ((number? elem)
             (let loop ((nset
                         (lazy:map
                          (lambda (node) (lazy:number (lazy:string-value node)))
                          nset)))
               (cond
                 ((null? nset) #f)
                 ((lazy:promise? (car nset))  ; time to get the next portion
                  (loop (append (as-nodeset (force (car nset)))
                                (cdr nset))))
                 ((number-op elem (car nset)) #t)
                 (else (loop (cdr nset))))))
            ((string? elem)
             (let loop ((nset (lazy:map lazy:string-value nset)))
               (cond
                 ((null? nset) #f)
                 ((lazy:promise? (car nset))  ; time to get the next portion
                  (loop (append (as-nodeset (force (car nset)))
                                (cdr nset))))
                 ((string-op elem (car nset)) #t)
                 (else (loop (cdr nset))))))
            (else  ; unknown datatype
             (cerr "Unknown datatype: " elem nl)
             #f))))))))

lazy:equal?

Index
(define lazy:equal? (lazy:equality-cmp eq? = string=?))

lazy:not-equal?

Index
(define lazy:not-equal?
  (lazy:equality-cmp
   (lambda (bool1 bool2) (not (eq? bool1 bool2)))
   (lambda (num1 num2) (not (= num1 num2)))
   (lambda (str1 str2) (not (string=? str1 str2)))))

lazy:relational-cmp

Index
 Relational operation ( < , > , <= , >= ) for two XPath objects
  op is comparison procedure: < , > , <= or >=
(define (lazy:relational-cmp op)
  (lambda (obj1 obj2)
    (cond
      ((not (or (nodeset? obj1) (nodeset? obj2)))  ; neither obj is a nodeset
       (op (lazy:number obj1) (lazy:number obj2)))
      ((boolean? obj1)  ; 'obj1' is a boolean, 'obj2' is a nodeset
       (op (lazy:number obj1) (lazy:number (lazy:boolean obj2))))
      ((boolean? obj2)  ; 'obj1' is a nodeset, 'obj2' is a boolean
       (op (lazy:number (lazy:boolean obj1)) (lazy:number obj2)))
      ((or (null? obj1) (null? obj2)) ; one of the objects is an empty nodeset
       #f)
      (else  ; at least one object is a nodeset
       (op
        (cond
          ((nodeset? obj1)  ; 'obj1' is a (non-empty) nodeset
           (let ((nset1 (lazy:map
                         (lambda (node) (lazy:number (lazy:string-value node)))
                         obj1)))
             (let first ((num1 (car nset1))
                         (nset1 (cdr nset1)))
               (cond
                 ((null? nset1) num1)
                 ((lazy:promise? (car nset1))  ; time to obtain the next portion
                  (first num1
                         (apply (as-nodeset (force (car nset1)))
                                (cdr nset1))))
                 ((op num1 (car nset1)) (first num1 (cdr nset1)))
                 (else (first (car nset1) (cdr nset1)))))))
          ((string? obj1) (sxml:number obj1))
          (else  ; 'obj1' is a number
           obj1))
        (cond
          ((nodeset? obj2)  ; 'obj2' is a (non-empty) nodeset
           (let ((nset2 (lazy:map
                         (lambda (node) (lazy:number (lazy:string-value node)))
                         obj2)))
             (let second ((num2 (car nset2))
                          (nset2 (cdr nset2)))
               (cond
                 ((null? nset2) num2)
                 ((lazy:promise? (car nset2))  ; time to obtain the next portion
                  (second num2
                          (apply (as-nodeset (force (car nset2)))
                                 (cdr nset2))))
                 ((op num2 (car nset2)) (second (car nset2) (cdr nset2)))
                 (else (second num2 (cdr nset2)))))))
          ((string? obj2) (sxml:number obj2))
          (else  ; 'obj2' is a number
           obj2)))))))

lazy:core-last

Index
 last()
(define (lazy:core-last num-anc)
  (lambda (nodeset position+size var-binding)
    (cdr position+size)))

lazy:core-position

Index
 position()
(define (lazy:core-position num-anc)
  (lambda (nodeset position+size var-binding)
    (car position+size)))

lazy:core-count

Index
 count(node-set)
(define (lazy:core-count num-anc arg-func)
  (lambda (nodeset position+size var-binding)
    (let ((res (arg-func nodeset position+size var-binding)))
      (cond
        ((nodeset? res) (lazy:length res))
        (else
         (sxml:xpointer-runtime-error
          "count() function - an argument is not a nodeset")
         0)))))

lazy:core-id

Index
 id(object)
(define (lazy:core-id num-anc arg-func) 
  (lambda (nodeset position+size var-binding)    
    (let* ((root-node (list (lazy:car
                             (lazy:reach-root nodeset))))
           (id-nset ((sxml:child (ntype?? 'id-index))
                     ((sxml:child (ntype?? '@@)) root-node))))
      (if
       (null? id-nset)  ; no id-index
       '()  ; ID function returns an empty nodeset
       (let ((res ((sxml:id (cdar id-nset))  ; implemented in "sxpath-ext.scm"
                   (lazy:result->list
                    (lazy:contextset->nodeset
                     (arg-func nodeset position+size var-binding))))))
         (if (and num-anc (zero? num-anc))  ; no ancestors required
             res
             (lazy:recover-contextset res root-node num-anc)))))))

lazy:core-local-name

Index
 local-name(node-set?)
(define (lazy:core-local-name num-anc . arg-func)  ; optional argument 
  (if (null? arg-func)  ; no argument supplied
      (lambda (nodeset position+size var-binding)
        (let ((nodeset (lazy:contextset->nodeset nodeset)))
          (cond
            ((lazy:null? nodeset) "")
            ((not (pair? (lazy:car nodeset))) "")  ; no name
            (else
             (let ((name (symbol->string (car (lazy:car nodeset)))))
               (cond
                 ((string-rindex name #\:)
                  => (lambda (pos)
                       (substring name (+ pos 1) (string-length name))))
                 (else  ; a NCName
                  name)))))))
      (let ((func (car arg-func)))
        (lambda (nodeset position+size var-binding)          
          (let ((obj
                 (lazy:contextset->nodeset
                  (func nodeset position+size var-binding))))
            (cond
              ((null? obj) "")  ; an empty nodeset
              ((not (nodeset? obj))
               (sxml:xpointer-runtime-error
                "NAME function - an argument is not a nodeset")              
               "")
              ((not (pair? (lazy:car obj))) "")  ; no name
              (else
               (let ((name (symbol->string (car (lazy:car obj)))))
                 (cond
                   ((string-rindex name #\:)
                    => (lambda (pos)
                         (substring
                          name (+ pos 1) (string-length name))))
                   (else  ; a NCName
                    name))))))))))

lazy:core-namespace-uri

Index
 namespace-uri(node-set?)
(define (lazy:core-namespace-uri num-anc . arg-func)  ; optional argument
  (if (null? arg-func)  ; no argument supplied
      (lambda (nodeset position+size var-binding)
        (let ((nodeset (lazy:contextset->nodeset nodeset)))
          (cond
            ((lazy:null? nodeset) "")
            ((not (pair? (lazy:car nodeset))) "")  ; no name
            (else
             (let ((name (symbol->string (car (lazy:car nodeset)))))
               (cond
                 ((string-rindex name #\:)
                  => (lambda (pos)
                       (substring name 0 pos)))
                 (else  ; a NCName
                  "")))))))
      (let ((func (car arg-func)))
        (lambda (nodeset position+size var-binding)          
          (let ((obj
                 (lazy:contextset->nodeset
                  (func nodeset position+size var-binding))))           
            (cond
              ((lazy:null? obj) "")  ; an empty nodeset
              ((not (nodeset? obj))
               (sxml:xpointer-runtime-error
                "NAME function - an argument is not a nodeset")
               "")
              ((not (pair? (lazy:car obj))) "")  ; no name
              (else
               (let ((name (symbol->string (car (lazy:car obj)))))
                 (cond
                   ((string-rindex name #\:)
                    => (lambda (pos)
                         (substring name 0 pos)))
                   (else ""))))))))))

lazy:core-name

Index
 name(node-set?)
(define (lazy:core-name num-anc . arg-func)  ; optional argument
  (if (null? arg-func)  ; no argument supplied
      (lambda (nodeset position+size var-binding)
        (let ((nodeset (lazy:contextset->nodeset nodeset)))
          (cond
            ((lazy:null? nodeset) "")
            ((not (pair? (lazy:car nodeset))) "")  ; no name
            (else
             (symbol->string (car (lazy:car nodeset)))))))
      (let ((func (car arg-func)))
        (lambda (nodeset position+size var-binding)
          (let ((obj
                 (lazy:contextset->nodeset
                  (func nodeset position+size var-binding))))        
            (cond
              ((lazy:null? obj) "")  ; an empty nodeset
              ((not (nodeset? obj))
               (sxml:xpointer-runtime-error
                "NAME function - an argument is not a nodeset")
               "")
              ((not (pair? (lazy:car obj))) "")  ; no name
              (else
               (symbol->string (car (lazy:car obj))))))))))

lazy:core-string

Index
 string(object?)
(define (lazy:core-string num-anc . arg-func)  ; optional argument
  (if (null? arg-func)  ; no argument supplied
      (lambda (nodeset position+size var-binding)
        (lazy:string
         (lazy:contextset->nodeset nodeset)))
      (let ((func (car arg-func)))
        (lambda (nodeset position+size var-binding)
          (lazy:string
           (lazy:contextset->nodeset
            (func nodeset position+size var-binding)))))))

lazy:core-concat

Index
 concat(string, string, string*)
(define (lazy:core-concat num-anc . arg-func-lst)
  (lambda (nodeset position+size var-binding)
    (apply
     string-append
     (map
      (lambda (f)
        (lazy:string
         (lazy:contextset->nodeset
          (f nodeset position+size var-binding))))
      arg-func-lst))))

lazy:core-starts-with

Index
 starts-with(string, string)
(define (lazy:core-starts-with num-anc arg-func1 arg-func2)
  (lambda (nodeset position+size var-binding)
    (let ((str1 (lazy:string
                 (lazy:contextset->nodeset
                  (arg-func1 nodeset position+size var-binding))))
          (str2 (lazy:string
                 (lazy:contextset->nodeset
                  (arg-func2 nodeset position+size var-binding)))))
      (string-prefix? str2 str1))))

lazy:core-contains

Index
 contains(string, string)
(define (lazy:core-contains num-anc arg-func1 arg-func2)
  (lambda (nodeset position+size var-binding)
    (let ((str1 (lazy:string
                 (lazy:contextset->nodeset
                  (arg-func1 nodeset position+size var-binding))))
          (str2 (lazy:string
                 (lazy:contextset->nodeset
                  (arg-func2 nodeset position+size var-binding)))))
      (if (substring? str2 str1) #t #f)  ; must return a boolean
      )))

lazy:core-substring-before

Index
 substring-before(string, string)
(define (lazy:core-substring-before num-anc arg-func1 arg-func2)
  (lambda (nodeset position+size var-binding)
    (let* ((str1 (lazy:string
                  (lazy:contextset->nodeset
                   (arg-func1 nodeset position+size var-binding))))
           (str2 (lazy:string
                  (lazy:contextset->nodeset
                   (arg-func2 nodeset position+size var-binding))))
           (pos (substring? str2 str1)))
      (if (not pos)  ; STR1 doesn't contain STR2
          ""
          (substring str1 0 pos)))))

lazy:core-substring-after

Index
 substring-after(string, string)
(define (lazy:core-substring-after num-anc arg-func1 arg-func2)
  (lambda (nodeset position+size var-binding)
    (let* ((str1 (lazy:string
                  (lazy:contextset->nodeset
                   (arg-func1 nodeset position+size var-binding))))
           (str2 (lazy:string
                  (lazy:contextset->nodeset
                   (arg-func2 nodeset position+size var-binding))))
           (pos (substring? str2 str1)))
      (if
       (not pos)  ; STR1 doesn't contain STR2
       ""
       (substring
        str1 (+ pos (string-length str2)) (string-length str1))))))

lazy:core-substring

Index
 substring(string, number, number?)
(define (lazy:core-substring num-anc arg-func1 arg-func2 . arg-func3)
  (if (null? arg-func3)  ; no third argument supplied
      (lambda (nodeset position+size var-binding)
        (let ((str (lazy:string
                    (lazy:contextset->nodeset
                     (arg-func1 nodeset position+size var-binding))))
              (num1 (lazy:number
                     (lazy:contextset->nodeset
                      (arg-func2 nodeset position+size var-binding)))))
          (let ((len (string-length str))
                (start (- (inexact->exact (round num1)) 1)))
            (if (> start len)
                ""
                (substring str (if (< start 0) 0 start) len)))))
      (let ((arg-func3 (car arg-func3)))
        (lambda (nodeset position+size var-binding)
          (let ((str (lazy:string
                      (lazy:contextset->nodeset
                       (arg-func1 nodeset position+size var-binding))))
                (num1 (lazy:number
                       (lazy:contextset->nodeset
                        (arg-func2 nodeset position+size var-binding))))
                (num2 (lazy:number
                       (lazy:contextset->nodeset
                        (arg-func3 nodeset position+size var-binding)))))
            (let* ((len (string-length str))
                   (start (- (inexact->exact (round num1)) 1))
                   (fin (+ start (inexact->exact (round num2)))))
              (if (or (> start len) (< fin 0) (< fin start))
                  ""
                  (substring str
                             (if (< start 0) 0 start)
                             (if (> fin len) len fin)))))))))

lazy:core-string-length

Index
 string-length(string?)
(define (lazy:core-string-length num-anc . arg-func)  ; optional argument
  (if (null? arg-func)  ; no argument supplied
      (lambda (nodeset position+size var-binding)
        (string-length
         (lazy:string (lazy:contextset->nodeset nodeset))))
      (let ((func (car arg-func)))
        (lambda (nodeset position+size var-binding)
          (string-length
           (lazy:string
            (lazy:contextset->nodeset
             (func nodeset position+size var-binding))))))))

lazy:core-normalize-space

Index
 normalize-space(string?)
(define (lazy:core-normalize-space num-anc . arg-func)  ; optional argument
  (if (null? arg-func)  ; no argument supplied
      (lambda (nodeset position+size var-binding)
        (let rpt ((src (string-split
                        (lazy:string (lazy:contextset->nodeset nodeset))
                        sxml:whitespace))
                  (res '()))
          (cond
            ((null? src)
             (apply string-append (reverse res)))
            ((= (string-length (car src)) 0)  ; empty string
             (rpt (cdr src) res))
            ((null? res)
             (rpt (cdr src) (cons (car src) res)))
            (else
             (rpt (cdr src) (cons (car src) (cons " " res)))))))
      (let ((func (car arg-func)))
        (lambda (nodeset position+size var-binding)
          (let rpt ((src (string-split
                          (lazy:string
                           (lazy:contextset->nodeset
                            (func nodeset position+size var-binding)))
                          sxml:whitespace))
                    (res '()))
            (cond
              ((null? src)
               (apply string-append (reverse res)))
              ((= (string-length (car src)) 0)  ; empty string
               (rpt (cdr src) res))
              ((null? res)
               (rpt (cdr src) (cons (car src) res)))
              (else
               (rpt (cdr src) (cons (car src) (cons " " res))))))))))

lazy:core-translate

Index
 translate(string, string, string)
(define (lazy:core-translate num-anc arg-func1 arg-func2 arg-func3)
  (lambda (nodeset position+size var-binding)    
    (let ((str1 (lazy:string
                 (lazy:contextset->nodeset
                  (arg-func1 nodeset position+size var-binding))))
          (str2 (lazy:string
                 (lazy:contextset->nodeset
                  (arg-func2 nodeset position+size var-binding))))
          (str3 (lazy:string
                 (lazy:contextset->nodeset
                  (arg-func3 nodeset position+size var-binding)))))
      (let ((alist
             (let while ((lst2 (string->list str2))
                         (lst3 (string->list str3))
                         (alist '()))
               (cond
                 ((null? lst2) (reverse alist))
                 ((null? lst3)
                  (append
                   (reverse alist)
                   (map
                    (lambda (ch) (cons ch #f))
                    lst2)))
                 (else
                  (while
                   (cdr lst2)
                   (cdr lst3)
                   (cons (cons (car lst2) (car lst3)) alist)))))))
        (let rpt ((lst1 (string->list str1))
                  (res '()))
          (cond
            ((null? lst1) (list->string (reverse res)))
            ((assoc (car lst1) alist)
             => (lambda (pair)
                  (if (cdr pair)
                      (rpt (cdr lst1) (cons (cdr pair) res))
                      (rpt (cdr lst1) res))))
            (else
             (rpt (cdr lst1) (cons (car lst1) res)))))))))

lazy:core-boolean

Index
 boolean(object)
(define (lazy:core-boolean num-anc arg-func)
  (lambda (nodeset position+size var-binding)
    (lazy:boolean
     (arg-func nodeset position+size var-binding))))

lazy:core-not

Index
 not(boolean)
(define (lazy:core-not num-anc arg-func)
  (lambda (nodeset position+size var-binding)
    (not (lazy:boolean 
          (arg-func nodeset position+size var-binding)))))

lazy:core-true

Index
 true()
(define (lazy:core-true num-anc)
  (lambda (nodeset position+size var-binding) #t))

lazy:core-false

Index
 false()
(define (lazy:core-false num-anc)
  (lambda (nodeset position+size var-binding) #f))

lazy:core-lang

Index
 lang(string)
(define (lazy:core-lang num-anc arg-func)
  (lambda (nodeset position+size var-binding)    
    (let ((arg (lazy:string
                (lazy:contextset->nodeset
                 (arg-func nodeset position+size var-binding))))
          (lng
           ((lazy:child (ntype?? '*text*))
            ((lazy:attribute (ntype?? 'xml:lang))
             ((lazy:ancestor-or-self (lambda (x) #t))
              (lazy:car nodeset)  ; context-node = (car nodeset)
              )))))
      (and (not (null? lng))
           (or (string-ci=? arg (lazy:car lng))
               (string-prefix-ci? (string-append arg "-") (lazy:car lng)))))))

lazy:core-number

Index
 number(object?)
(define (lazy:core-number num-anc . arg-func)  ; optional argument
  (if (null? arg-func)  ; no argument supplied
      (lambda (nodeset position+size var-binding)
        (lazy:number (lazy:contextset->nodeset nodeset)))
      (let ((func (car arg-func)))
        (lambda (nodeset position+size var-binding)
          (lazy:number
           (lazy:contextset->nodeset
            (func nodeset position+size var-binding)))))))

lazy:core-sum

Index
 sum(node-set)
(define (lazy:core-sum num-anc arg-func)
  (lambda (nodeset position+size var-binding)
    (let ((res (arg-func nodeset position+size var-binding)))
      (cond
        ((nodeset? res)
         (apply +
                (map
                 (lambda (node)
                   (lazy:number
                    (lazy:string-value (sxml:context->node node))))
                 (lazy:result->list res))))
        (else
         (sxml:xpointer-runtime-error
          "SUM function - an argument is not a nodeset")
         0)))))

lazy:core-floor

Index
 floor(number)
(define (lazy:core-floor num-anc arg-func)
  (lambda (nodeset position+size var-binding)
    (inexact->exact
     (floor (lazy:number
             (lazy:contextset->nodeset
              (arg-func nodeset position+size var-binding)))))))

lazy:core-ceiling

Index
 ceiling(number)
(define (lazy:core-ceiling num-anc arg-func)
  (lambda (nodeset position+size var-binding)
    (inexact->exact
     (ceiling (lazy:number
               (lazy:contextset->nodeset
                (arg-func nodeset position+size var-binding)))))))

lazy:core-round

Index
 round(number)
(define (lazy:core-round num-anc arg-func)
  (lambda (nodeset position+size var-binding)
    (inexact->exact
     (round (lazy:number
             (lazy:contextset->nodeset
              (arg-func nodeset position+size var-binding)))))))

lazy:ast-location-path

Index
 {1} <LocationPath> ::= <RelativeLocationPath>
                        | <AbsoluteLocationPath>
(define (lazy:ast-location-path op num-anc)
  (case (car op)
    ((absolute-location-path)
     (lazy:ast-absolute-location-path op num-anc))
    ((relative-location-path)
     (lazy:ast-relative-location-path op num-anc))
    (else
     (draft:signal-semantic-error "improper LocationPath - " op))))

lazy:ast-absolute-location-path

Index
 {2} <AbsoluteLocationPath> ::= (absolute-location-path  <Step>* )
(define (lazy:ast-absolute-location-path op num-anc)
  (cond
    ((not (eq? (car op) 'absolute-location-path))
     (draft:signal-semantic-error "not an AbsoluteLocationPath - " op))
    ((null? (cdr op))  ; no Steps
     (list
      (lambda (nodeset position+size var-binding)
        (lazy:reach-root nodeset))
      #f  ; num-ancestors
      #f  ; requires-size?
      ))
    (else
     (and-let*
      ((steps-res (lazy:ast-step-list (cdr op) num-anc)))
      (list
       (if
        (null? (cdar steps-res))  ; only a single step
        (let ((step-impl (caar steps-res)))
          (lambda (nodeset position+size var-binding)
            (step-impl
             (lazy:reach-root nodeset) position+size var-binding)))
        (let ((converters (car steps-res)))
          (lambda (nodeset position+size var-binding)
            (let rpt ((nset (lazy:reach-root nodeset))
                      (fs converters))
              (if (null? fs)
                  nset
                  (rpt ((car fs) nset position+size var-binding)
                       (cdr fs)))))))
       #f  ; num-ancestors
       #f  ; requires-size?
       )))))

lazy:ast-relative-location-path

Index
 {3} <RelativeLocationPath> ::= (relative-location-path  <Step>+ )
(define (lazy:ast-relative-location-path op num-anc)
  (if
   (not (eq? (car op) 'relative-location-path))
   (draft:signal-semantic-error "not a RelativeLocationPath - " op)
   (and-let*
    ((steps-res (lazy:ast-step-list (cdr op) num-anc)))
    (list
     (if
      (null? (cdar steps-res))  ; only a single step
      (caar steps-res)
      (let ((converters (car steps-res)))
        (lambda (nodeset position+size var-binding)
          (let rpt ((nset nodeset)
                    (fs converters))
            (if (null? fs)
                nset
                (rpt ((car fs) nset position+size var-binding)
                     (cdr fs)))))))
     (cadr steps-res)  ; num-ancestors
     #f  ; requires-size?
     ))))

lazy:ast-step

Index
 {4} <Step> ::= (step  <AxisSpecifier> <NodeTest> <Predicate>* )
                | (range-to  (expr <Expr>)  <Predicate>* )
(define (lazy:ast-step op num-anc)
  (cond
    ((eq? (car op) 'range-to)
     (draft:signal-semantic-error "range-to function not implemented"))
    ((eq? (car op) 'filter-expr)
     (lazy:ast-filter-expr op num-anc))
    ((eq? (car op) 'lambda-step)  ; created by sxpath
     (let ((proc (cadr op)))
       (list
        (if
         (and num-anc (zero? num-anc))  ; no ancestors required
         (lambda (nodeset position+size var-binding)
           (proc (lazy:contextset->nodeset (as-nodeset nodeset))
                 var-binding))
         (lambda (nodeset position+size var-binding)
           (lazy:find-proper-context
            (proc (lazy:contextset->nodeset (as-nodeset nodeset))
                  var-binding)
            (as-nodeset nodeset)
            num-anc)))
        num-anc  ; num-ancestors
        #f  ; requires-last?
        )))
    ((eq? (car op) 'step)
     (if
      (null? (cdddr op))  ; no Predicates
      (and-let*
       ((axis-lst (lazy:ast-axis-specifier (cadr op) num-anc))
        (ntest (draft:ast-node-test (caddr op))))
       (let ((axis
              (lazy:axis-consume-nodeset
               ((car axis-lst) ntest num-anc))))
         (list
          (lambda (nodeset position+size var-binding)
            (axis nodeset))
          (cdr axis-lst)  ; num-ancestors
          #f  ; requires-size?
          )))
      (and-let*
       ((preds-res (lazy:ast-predicate-list (cdddr op) 0))
        (axis-lst (lazy:ast-axis-specifier
                   (cadr op) (draft:na-max num-anc (cadr preds-res))))
        (ntest (draft:ast-node-test (caddr op))))
       (let ((axis ((car axis-lst)
                    ntest (draft:na-max num-anc (cadr preds-res))))
             (pred-impl-lst (car preds-res)))
         (list
          (lambda (nodeset position+size var-binding)
            (let iter-src ((src nodeset)
                           (candidates '())
                           (res '()))
              (cond
                ((null? candidates)  ; consume the following node from src
                 (cond
                   ((null? src)  ; iteration is over
                    (reverse res))
                   ((lazy:promise? (car src))
                    (if
                     (null? res)  ; result is still empty, need to force src
                     (iter-src (append (as-nodeset (force (car src)))
                                       (cdr src))
                               candidates
                               res)
                     (reverse  ; otherwise - return the result with a promise
                      (cons
                       (delay (iter-src src candidates '()))
                       res))))
                   (else  ; (car src) is a node
                    (iter-src
                     (cdr src)
                     (let iter-preds ((nset (axis (car src)))
                                      (preds pred-impl-lst))
                       (if
                        (null? preds)
                        nset
                        (iter-preds
                         ((car preds) nset position+size var-binding)
                         (cdr preds))))
                     res))))
                ((lazy:promise? (car candidates))
                 ; First candidate is a promise
                 (if
                  (null? res)  ; result is still empty, need to force candidate
                  (iter-src src
                            (append (as-nodeset (force (car candidates)))
                                    (cdr candidates))
                            res)
                  (reverse  ; otherwise - return the result with a promise
                   (cons
                    (delay (iter-src src candidates '()))
                    res))))
                (else   ; the first candidate is a node
                 (iter-src src (cdr candidates)
                           (cons (car candidates) res))))))
          (cdr axis-lst)  ; num-ancestors
          #f  ; requires-last?
         )))))
    (else
     (draft:signal-semantic-error "not a Step - " op))))

lazy:ast-step-list

Index
 {4a} ( <Step>+ )
 Returns (list (listof step-impl) num-anc) or #f
 NOTE: requires-size? is not needed here, since it is always #f
(define (lazy:ast-step-list step-lst num-anc)
  (let loop ((steps-to-view (reverse step-lst))
             (res-lst '())
             (num-anc num-anc))
    (if
     (null? steps-to-view)  ; everyone processed
     (list res-lst num-anc)
     (and-let*
      ((step-res (lazy:ast-step (car steps-to-view) num-anc)))
      (loop
       (cdr steps-to-view)
       (cons (car step-res) res-lst)
       (cadr step-res))))))

lazy:ast-predicate

Index
 {8} <Predicate> ::= (predicate  <Expr> )
 NOTE: num-anc is dummy here, since it is always 0 for Predicates
(define (lazy:ast-predicate op num-anc)
  (if
   (not (eq? (car op) 'predicate))
   (draft:signal-semantic-error "not an Predicate - " op)
   (and-let*
    ((expr-res (lazy:ast-expr (cadr op) 0)))
    (let ((pred (car expr-res)))
      (list
       (if
        (caddr expr-res)  ; requires-last?
        (lambda (nodeset position+size var-binding)
          (if
           (null? nodeset)  ; already empty
           nodeset  ; nothing to filter
           (let ((size (lazy:length nodeset)))
             (let loop ((nset nodeset)
                        (res '())
                        (pos 1))
               (cond
                 ((null? nset)
                  (reverse res))
                 ((lazy:promise? (car nset))
                  ; This promise was already forced when evaluating lazy:length
                  (loop (append (as-nodeset (force (car nset)))
                                (cdr nset))
                        res pos))
                 (else  ; (car nset) is a node
                  (let ((value (pred (list (car nset))
                                     (cons pos size)
                                     var-binding)))
                    (loop (cdr nset)
                          (if (if (number? value)
                                  (= value pos)
                                  (lazy:boolean value))
                              (cons (car nset) res)
                              res)
                          (+ pos 1)))))))))
        (lambda (nodeset position+size var-binding)
          (if
           (null? nodeset)  ; already empty
           nodeset  ; nothing to filter
           (let loop ((nset nodeset)
                      (res '())
                      (pos 1))
             (cond
               ((null? nset)
                (reverse res))
               ((lazy:promise? (car nset))
                (reverse
                 (cons
                  (delay (loop
                          (append (as-nodeset (force (car nset)))
                                  (cdr nset))
                          '()  ; turns res to empty
                          pos))
                  res)))
               (else  ; (car nset) is a node
                (let ((value (pred (list (car nset))
                                   (cons pos 1)  ; context size is dummy
                                   var-binding)))
                  (loop (cdr nset)
                        (if (if (number? value)
                                (= value pos)
                                (lazy:boolean value))
                            (cons (car nset) res)
                            res)
                        (+ pos 1)))))))))
       (cadr expr-res)  ; num-ancestors
       (caddr expr-res)  ; requires-last?
       )))))

lazy:ast-predicate-list

Index
 {8a} ( <Predicate>+ )
 Returns (list (listof pred-impl) num-anc) or #f
 NOTE: num-anc is dummy here, since it is always 0 for Predicates
(define (lazy:ast-predicate-list op-lst num-anc)
  (let ((pred-res-lst
         (map
          (lambda (op) (lazy:ast-predicate op 0))
          op-lst)))
    (if
     (member #f pred-res-lst)  ; error detected
     #f
     (list
      (map car pred-res-lst)
      (apply draft:na-max (map cadr pred-res-lst))))))

lazy:ast-expr

Index
 {9} <Expr> ::= <OrExpr>
                | <AndExpr>
                | <EqualityExpr>
                | <RelationalExpr>
                | <AdditiveExpr>
                | <MultiplicativeExpr>
                | <UnionExpr>
                | <PathExpr>
                | <FilterExpr>
                | <VariableReference>
                | <Literal>
                | <Number>
                | <FunctionCall>
                | <LocationPath>
(define (lazy:ast-expr op num-anc)
  (case (car op)
    ((or)
     (lazy:ast-or-expr op num-anc))
    ((and)
     (lazy:ast-and-expr op num-anc))
    ((= !=)
     (lazy:ast-equality-expr op num-anc))
    ((< > <= >=)
     (lazy:ast-relational-expr op num-anc))
    ((+ -)
     (lazy:ast-additive-expr op num-anc))
    ((* div mod)
     (lazy:ast-multiplicative-expr op num-anc))
    ((union-expr)
     (lazy:ast-union-expr op num-anc))
    ((path-expr)
     (lazy:ast-path-expr op num-anc))
    ((filter-expr)
     (lazy:ast-filter-expr op num-anc))
    ((variable-reference)
     (lazy:ast-variable-reference op num-anc))
    ((literal)
     (lazy:ast-literal op num-anc))
    ((number)
     (lazy:ast-number op num-anc))
    ((function-call)
     (lazy:ast-function-call op num-anc))
    ((absolute-location-path)
     (lazy:ast-absolute-location-path op num-anc))
    ((relative-location-path)
     (lazy:ast-relative-location-path op num-anc))
    (else
     (draft:signal-semantic-error "unknown Expr - " op))))

lazy:ast-or-expr

Index
 {10} <OrExpr> ::= (or <Expr> <Expr>+ )
 NOTE: num-anc is dummy here, since it is always 0 for OrExpr
(define (lazy:ast-or-expr op num-anc)
  (let ((expr-res-lst
         (map
          (lambda (expr) (lazy:ast-expr expr 0))
          (cdr op))))
    (if
     (member #f expr-res-lst)  ; error detected
     #f
     (let ((expr-impls (map car expr-res-lst)))
     (list
      (lambda (nodeset position+size var-binding)
        (let rpt ((fs expr-impls))
          (cond
            ((null? fs) #f)
            ((lazy:boolean ((car fs) nodeset position+size var-binding)) #t)
            (else (rpt (cdr fs))))))
      (apply draft:na-max (map cadr expr-res-lst))  ; num-ancestors
      (apply lazy:or (map caddr expr-res-lst))  ; requires-last?
      )))))

lazy:ast-and-expr

Index
 {11} <AndExpr> ::= (and <Expr> <Expr>+ )
 NOTE: num-anc is dummy here, since it is always 0 for AndExpr
(define (lazy:ast-and-expr op num-anc)
  (let ((expr-res-lst
         (map
          (lambda (expr) (lazy:ast-expr expr 0))
          (cdr op))))
    (if
     (member #f expr-res-lst)  ; error detected
     #f
     (let ((expr-impls (map car expr-res-lst)))
     (list
      (lambda (nodeset position+size var-binding)
        (let rpt ((fs expr-impls))
          (cond
            ((null? fs) #t)
            ((not
              (lazy:boolean ((car fs) nodeset position+size var-binding)))
             #f)
            (else (rpt (cdr fs))))))
      (apply draft:na-max (map cadr expr-res-lst))  ; num-ancestors
      (apply lazy:or (map caddr expr-res-lst))  ; requires-last?
      )))))

lazy:ast-equality-expr

Index
 {12} <EqualityExpr> ::= (=  <Expr> <Expr> )
                         | (!=  <Expr> <Expr> )
 NOTE: num-anc is dummy here, since it is always 0 for EqualityExpr
(define (lazy:ast-equality-expr op num-anc)
  (and-let*
   ((left-lst (lazy:ast-expr (cadr op) 0))
    (right-lst (lazy:ast-expr (caddr op) 0)))
   (let ((cmp-op (cadr (assq (car op) `((= ,lazy:equal?)
                                        (!= ,lazy:not-equal?)))))
         (left (car left-lst))
         (right (car right-lst)))
     (list
      (lambda (nodeset position+size var-binding)
        (cmp-op
         (lazy:contextset->nodeset
          (left nodeset position+size var-binding))
         (lazy:contextset->nodeset
          (right nodeset position+size var-binding))))
      (draft:na-max (cadr left-lst) (cadr right-lst))  ; num-ancestors
      (or (caddr left-lst) (caddr right-lst))  ; requires-last?
      ))))

lazy:ast-relational-expr

Index
 {13} <RelationalExpr> ::= (<  <Expr> <Expr> )
                           | (>  <Expr> <Expr> )
                           | (<=  <Expr> <Expr> )
                           | (>=  <Expr> <Expr> )
 NOTE: num-anc is dummy here, since it is always 0 for RelationalExpr
(define (lazy:ast-relational-expr op num-anc)
  (and-let*
   ((left-lst (lazy:ast-expr (cadr op) 0))
    (right-lst (lazy:ast-expr (caddr op) 0)))
   (let ((cmp-op
          (lazy:relational-cmp
           (cadr (assq (car op) `((< ,<) (> ,>) (<= ,<=) (>= ,>=))))))
         (left (car left-lst))
         (right (car right-lst)))
     (list
      (lambda (nodeset position+size var-binding)
        (cmp-op
         (lazy:contextset->nodeset
          (left nodeset position+size var-binding))
         (lazy:contextset->nodeset
          (right nodeset position+size var-binding))))
      (draft:na-max (cadr left-lst) (cadr right-lst))  ; num-ancestors
      (or (caddr left-lst) (caddr right-lst))  ; requires-last?
      ))))

lazy:ast-additive-expr

Index
 {14} <AdditiveExpr> ::= (+  <Expr> <Expr> )
                         | (-  <Expr> <Expr>? )
 NOTE: num-anc is dummy here, since it is always 0 for AdditiveExpr
(define (lazy:ast-additive-expr op num-anc)
  (let ((expr-res-lst
         (map
          (lambda (expr) (lazy:ast-expr expr 0))
          (cdr op))))
    (if
     (member #f expr-res-lst)  ; error detected
     #f
     (let ((add-op (cadr (assq (car op) `((+ ,+) (- ,-)))))
           (expr-impls (map car expr-res-lst)))
     (list
      (lambda (nodeset position+size var-binding)
        (apply
         add-op
         (map
          (lambda (expr)
            (lazy:number
             (lazy:contextset->nodeset
              (expr nodeset position+size var-binding))))
          expr-impls)))
      (apply draft:na-max (map cadr expr-res-lst))  ; num-ancestors
      (apply lazy:or (map caddr expr-res-lst))  ; requires-last?
      )))))

lazy:ast-multiplicative-expr

Index
 {15} <MultiplicativeExpr> ::= (*  <Expr> <Expr> )
                               | (div  <Expr> <Expr> )
                               | (mod  <Expr> <Expr> )
 NOTE: num-anc is dummy here, since it is always 0 for MultiplicativeExpr
(define (lazy:ast-multiplicative-expr op num-anc)
  (and-let*
   ((left-lst (lazy:ast-expr (cadr op) 0))
    (right-lst (lazy:ast-expr (caddr op) 0)))
   (let ((mul-op
          (cadr (assq (car op) `((* ,*) (div ,/) (mod ,remainder)))))
         (left (car left-lst))
         (right (car right-lst)))
     (list
      (lambda (nodeset position+size var-binding)
        (mul-op
         (lazy:number
          (lazy:contextset->nodeset
           (left nodeset position+size var-binding)))
         (lazy:number
          (lazy:contextset->nodeset
           (right nodeset position+size var-binding)))))
      (draft:na-max (cadr left-lst) (cadr right-lst))  ; num-ancestors
      (or (caddr left-lst) (caddr right-lst))  ; requires-last?
      ))))

lazy:ast-union-expr

Index
 {16} <UnionExpr> ::= (union-expr  <Expr> <Expr>+ )
(define (lazy:ast-union-expr op num-anc)
  (let ((expr-res-lst
         (map
          (lambda (expr) (lazy:ast-expr expr 0))
          (cdr op))))
    (if
     (member #f expr-res-lst)  ; error detected
     #f
     (let ((expr-impls (map car expr-res-lst)))
       (list
        (lambda (nodeset position+size var-binding)
          (let iter-operands ((fs expr-impls)
                              (candidates '())
                              (res '()))
            (cond
              ((null? candidates)
               (if
                (null? fs)  ; no more operands to be unioned
                (reverse res)                
                (iter-operands
                 (cdr fs)
                 (let ((nset ((car fs) nodeset position+size var-binding)))
                   (cond
                     ((not (nodeset? nset))
                      (sxml:xpointer-runtime-error 
                       "expected - nodeset instead of " nset)
                      '())
                   (else nset)))
                 res)))
              ((lazy:promise? (car candidates))
               (if
                (null? res)  ; res is still null, need to force candidate
                (iter-operands
                 fs
                 (append (as-nodeset (force (car candidates)))
                         (cdr candidates))
                 res)
                (reverse
                 (cons
                  (delay (iter-operands
                          fs
                          (append (as-nodeset (force (car candidates)))
                                  (cdr candidates))
                          '()))
                  res))))
              (else  ; first candidate is a node
               (iter-operands
                fs (cdr candidates) (cons (car candidates) res))))))
        (apply draft:na-max (map cadr expr-res-lst))
        (apply lazy:or (map caddr expr-res-lst))  ; requires-last?
        )))))

lazy:ast-path-expr

Index
 {17} <PathExpr> ::= (path-expr  <FilterExpr> <Step>+ )
(define (lazy:ast-path-expr op num-anc)
  (and-let*
    ((steps-res (lazy:ast-step-list (cddr op) num-anc))
     (filter-lst (lazy:ast-filter-expr (cadr op) (cadr steps-res))))
    (let ((init-impl (car filter-lst))
          (converters (car steps-res)))
      (list
        (lambda (nodeset position+size var-binding)
          (let ((nset
                 (init-impl nodeset position+size var-binding)))
            (let rpt ((nset 
                       (cond
                         ((nodeset? nset) nset)
                         (else
                          (sxml:xpointer-runtime-error 
                           "expected - nodeset instead of " nset)
                          '())))
                      (fs converters))
              (if (null? fs)
                  nset
                  (rpt ((car fs) nset position+size var-binding)
                       (cdr fs))))))
        (cadr filter-lst)  ; num-ancestors
        (caddr filter-lst)  ; requires-last?
        ))))

lazy:ast-filter-expr

Index
 {18} <FilterExpr> ::= (filter-expr (primary-expr  <Expr> )
                                    <Predicate>* )
(define (lazy:ast-filter-expr op num-anc)
  (cond
    ((not (eq? (car op) 'filter-expr))
     (draft:signal-semantic-error "not an FilterExpr - " op))
    ((not (eq? (caadr op) 'primary-expr))
     (draft:signal-semantic-error "not an PrimaryExpr - " (cadr op)))
    ((null? (cddr op))  ; no Predicates
     (lazy:ast-expr (cadadr op) num-anc))
    (else  ; there are predicates
     (and-let*
       ((preds-res (lazy:ast-predicate-list (cddr op) 0))
        (expr-lst (lazy:ast-expr
                   (cadadr op) (draft:na-max num-anc (cadr preds-res)))))
       (let ((expr-impl (car expr-lst))
             (pred-impl-lst (car preds-res)))
         (list
          (lambda (nodeset position+size var-binding)
            (let ((prim-res (expr-impl nodeset position+size var-binding)))
              (let iter-preds ((nset
                                (if
                                 (nodeset? prim-res)
                                 prim-res
                                 (begin 
                                   (sxml:xpointer-runtime-error 
                                    "expected - nodeset instead of " prim-res)
                                   '())))
                               (preds pred-impl-lst))
                (if
                 (null? preds)
                 nset
                 (iter-preds
                  ((car preds) nset position+size var-binding)
                  (cdr preds))))))
          (cadr expr-lst)  ; num-ancestors
          #f  ; requires-last?
         ))))))

lazy:ast-variable-reference

Index
 {19} <VariableReference> ::= (variable-reference  <String> )
(define (lazy:ast-variable-reference op num-anc)
  (let ((name (string->symbol (cadr op))))
    (list
     (lambda (nodeset position+size var-binding)
       (cond
         ((assoc name var-binding)
          => cdr)
         (else
          (sxml:xpointer-runtime-error "unbound variable - " name)
          '())))
     0  ; num-ancestors
     #f  ; requires-last?
     )))

lazy:ast-literal

Index
 {20} <Literal> ::= (literal  <String> )
(define (lazy:ast-literal op num-anc)
  (let ((literal (cadr op)))
    (list
     (lambda (nodeset position+size var-binding) literal)
     0 #f)))

lazy:ast-number

Index
 {21} <Number> :: (number  <Number> )
(define (lazy:ast-number op num-anc)
  (let ((number (cadr op)))
    (list
     (lambda (nodeset position+size var-binding) number)
     0 #f)))

lazy:ast-function-call

Index
 {22} <FunctionCall> ::= (function-call (function-name  <String> )
                                        (argument  <Expr> )* )
(define (lazy:ast-function-call op num-anc)
  (let ((core-alist
         ; (list fun-name min-num-args max-num-args na4res impl requires-last?)
         `((last 0 0 0 ,lazy:core-last #t)
           (position 0 0 0 ,lazy:core-position #f)
           (count 1 1 0 ,lazy:core-count #f)
           (id 1 1 #f ,lazy:core-id #f)
           (local-name 0 1 0 ,lazy:core-local-name #f)
           (namespace-uri 0 1 0 ,lazy:core-namespace-uri #f)
           (name 0 1 0 ,lazy:core-name #f)
           (string 0 1 0 ,lazy:core-string #f)
           (concat 2 -1 0 ,lazy:core-concat #f)
           (starts-with 2 2 0 ,lazy:core-starts-with #f)
           (contains 2 2 0 ,lazy:core-contains #f)
           (substring-before 2 2 0 ,lazy:core-substring-before #f)
           (substring-after 2 2 0 ,lazy:core-substring-after #f)
           (substring 2 3 0 ,lazy:core-substring #f)
           (string-length 0 1 0 ,lazy:core-string-length #f)
           (normalize-space 0 1 0 ,lazy:core-normalize-space #f)
           (translate 3 3 0 ,lazy:core-translate #f)
           (boolean 1 1 0 ,lazy:core-boolean #f)
           (not 1 1 0 ,lazy:core-not #f)
           (true 0 0 0 ,lazy:core-true #f)
           (false 0 0 0 ,lazy:core-false #f)
           (lang 1 1 #f ,lazy:core-lang #f)
           (number 0 1 0 ,lazy:core-number #f)
           (sum 1 1 0 ,lazy:core-sum #f)
           (floor 1 1 0 ,lazy:core-floor #f)
           (ceiling 1 1 0 ,lazy:core-ceiling #f)
           (round 1 1 0 ,lazy:core-round #f))))
    (cond
      ((not (eq? (caadr op) 'function-name))
       (draft:signal-semantic-error "not an FunctionName - " (cadr op)))
      ((assq (string->symbol (cadadr op)) core-alist)       
       => (lambda (description)  ; Core function found
            (cond
              ((< (length (cddr op)) (cadr description))
               (draft:signal-semantic-error
                "too few arguments for the Core Function call - "
                (cadadr op)))
              ((and (>= (caddr description) 0)
                    (> (length (cddr op)) (caddr description)))
               (draft:signal-semantic-error
                "too many arguments for the Core Function call - "
                (cadadr op)))
              (else  ; correct number of arguments
               (and-let*
                ((args-impl (lazy:ast-function-arguments (cddr op))))
                (list
                 ; Producing a function implementation
                 (apply (list-ref description 4) num-anc args-impl)
                 (list-ref description 3)
                 (list-ref description 5)  ; requires-last?
                 ))))))
           (else  ; function definition not found
            (draft:signal-semantic-error
             "function call to an unknown function - " (cadadr op))))))

lazy:ast-function-arguments

Index
 {22a} ( (argument  <Expr> )* )
 Returns: (listof expr-impl) or #f
(define (lazy:ast-function-arguments op-lst)
  (let ((arg-res-lst
         (map
          (lambda (op)
            (if
             (not (eq? (car op) 'argument))
             (draft:signal-semantic-error "not an Argument - " op)
             (lazy:ast-expr (cadr op) 0)))
          op-lst)))
    (if
     (member #f arg-res-lst)  ; semantic error detected
     #f
     (map car arg-res-lst))))