sxml.xpath.test 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699
  1. ;;;; sxml.xpath.test -*- scheme -*-
  2. ;;;;
  3. ;;;; Copyright (C) 2010 Free Software Foundation, Inc.
  4. ;;;; Copyright (C) 2001,2002,2003,2004 Oleg Kiselyov <oleg at pobox dot com>
  5. ;;;;
  6. ;;;; This library is free software; you can redistribute it and/or
  7. ;;;; modify it under the terms of the GNU Lesser General Public
  8. ;;;; License as published by the Free Software Foundation; either
  9. ;;;; version 3 of the License, or (at your option) any later version.
  10. ;;;;
  11. ;;;; This library is distributed in the hope that it will be useful,
  12. ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. ;;;; Lesser General Public License for more details.
  15. ;;;;
  16. ;;;; You should have received a copy of the GNU Lesser General Public
  17. ;;;; License along with this library; if not, write to the Free Software
  18. ;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  19. ;;; Commentary:
  20. ;;
  21. ;; Unit tests for (sxml xpath).
  22. ;;
  23. ;;; Code:
  24. (define-module (test-suite sxml-xpath)
  25. #:use-module (test-suite lib)
  26. #:use-module (sxml xpath))
  27. (define tree1
  28. '(html
  29. (head (title "Slides"))
  30. (body
  31. (p (@ (align "center"))
  32. (table (@ (style "font-size: x-large"))
  33. (tr
  34. (td (@ (align "right")) "Talks ")
  35. (td (@ (align "center")) " = ")
  36. (td " slides + transition"))
  37. (tr (td)
  38. (td (@ (align "center")) " = ")
  39. (td " data + control"))
  40. (tr (td)
  41. (td (@ (align "center")) " = ")
  42. (td " programs"))))
  43. (ul
  44. (li (a (@ (href "slides/slide0001.gif")) "Introduction"))
  45. (li (a (@ (href "slides/slide0010.gif")) "Summary")))
  46. )))
  47. ;; Example from a posting "Re: DrScheme and XML",
  48. ;; Shriram Krishnamurthi, comp.lang.scheme, Nov. 26. 1999.
  49. ;; http://www.deja.com/getdoc.xp?AN=553507805
  50. (define tree3
  51. '(poem (@ (title "The Lovesong of J. Alfred Prufrock")
  52. (poet "T. S. Eliot"))
  53. (stanza
  54. (line "Let us go then, you and I,")
  55. (line "When the evening is spread out against the sky")
  56. (line "Like a patient etherized upon a table:"))
  57. (stanza
  58. (line "In the room the women come and go")
  59. (line "Talking of Michaelangelo."))))
  60. (define (run-test selector node expected)
  61. (pass-if expected
  62. (equal? expected (selector node))))
  63. (with-test-prefix "test-all"
  64. ;; Location path, full form: child::para
  65. ;; Location path, abbreviated form: para
  66. ;; selects the para element children of the context node
  67. (let ((tree
  68. '(elem (@) (para (@) "para") (br (@)) "cdata" (para (@) "second par"))
  69. )
  70. (expected '((para (@) "para") (para (@) "second par")))
  71. )
  72. (run-test (select-kids (node-typeof? 'para)) tree expected)
  73. (run-test (sxpath '(para)) tree expected))
  74. ;; Location path, full form: child::*
  75. ;; Location path, abbreviated form: *
  76. ;; selects all element children of the context node
  77. (let ((tree
  78. '(elem (@) (para (@) "para") (br (@)) "cdata" (para "second par"))
  79. )
  80. (expected
  81. '((para (@) "para") (br (@)) (para "second par")))
  82. )
  83. (run-test (select-kids (node-typeof? '*)) tree expected)
  84. (run-test (sxpath '(*)) tree expected))
  85. ;; Location path, full form: child::text()
  86. ;; Location path, abbreviated form: text()
  87. ;; selects all text node children of the context node
  88. (let ((tree
  89. '(elem (@) (para (@) "para") (br (@)) "cdata" (para "second par"))
  90. )
  91. (expected
  92. '("cdata"))
  93. )
  94. (run-test (select-kids (node-typeof? '*text*)) tree expected)
  95. (run-test (sxpath '(*text*)) tree expected))
  96. ;; Location path, full form: child::node()
  97. ;; Location path, abbreviated form: node()
  98. ;; selects all the children of the context node, whatever their node type
  99. (let* ((tree
  100. '(elem (@) (para (@) "para") (br (@)) "cdata" (para "second par"))
  101. )
  102. (expected (cdr tree))
  103. )
  104. (run-test (select-kids (node-typeof? '*any*)) tree expected)
  105. (run-test (sxpath '(*any*)) tree expected)
  106. )
  107. ;; Location path, full form: child::*/child::para
  108. ;; Location path, abbreviated form: */para
  109. ;; selects all para grandchildren of the context node
  110. (let ((tree
  111. '(elem (@) (para (@) "para") (br (@)) "cdata" (para "second par")
  112. (div (@ (name "aa")) (para "third para")))
  113. )
  114. (expected
  115. '((para "third para")))
  116. )
  117. (run-test
  118. (node-join (select-kids (node-typeof? '*))
  119. (select-kids (node-typeof? 'para)))
  120. tree expected)
  121. (run-test (sxpath '(* para)) tree expected)
  122. )
  123. ;; Location path, full form: attribute::name
  124. ;; Location path, abbreviated form: @name
  125. ;; selects the 'name' attribute of the context node
  126. (let ((tree
  127. '(elem (@ (name "elem") (id "idz"))
  128. (para (@) "para") (br (@)) "cdata" (para (@) "second par")
  129. (div (@ (name "aa")) (para (@) "third para")))
  130. )
  131. (expected
  132. '((name "elem")))
  133. )
  134. (run-test
  135. (node-join (select-kids (node-typeof? '@))
  136. (select-kids (node-typeof? 'name)))
  137. tree expected)
  138. (run-test (sxpath '(@ name)) tree expected)
  139. )
  140. ;; Location path, full form: attribute::*
  141. ;; Location path, abbreviated form: @*
  142. ;; selects all the attributes of the context node
  143. (let ((tree
  144. '(elem (@ (name "elem") (id "idz"))
  145. (para (@) "para") (br (@)) "cdata" (para "second par")
  146. (div (@ (name "aa")) (para (@) "third para")))
  147. )
  148. (expected
  149. '((name "elem") (id "idz")))
  150. )
  151. (run-test
  152. (node-join (select-kids (node-typeof? '@))
  153. (select-kids (node-typeof? '*)))
  154. tree expected)
  155. (run-test (sxpath '(@ *)) tree expected)
  156. )
  157. ;; Location path, full form: descendant::para
  158. ;; Location path, abbreviated form: .//para
  159. ;; selects the para element descendants of the context node
  160. (let ((tree
  161. '(elem (@ (name "elem") (id "idz"))
  162. (para (@) "para") (br (@)) "cdata" (para "second par")
  163. (div (@ (name "aa")) (para (@) "third para")))
  164. )
  165. (expected
  166. '((para (@) "para") (para "second par") (para (@) "third para")))
  167. )
  168. (run-test
  169. (node-closure (node-typeof? 'para))
  170. tree expected)
  171. (run-test (sxpath '(// para)) tree expected)
  172. )
  173. ;; Location path, full form: self::para
  174. ;; Location path, abbreviated form: _none_
  175. ;; selects the context node if it is a para element; otherwise selects nothing
  176. (let ((tree
  177. '(elem (@ (name "elem") (id "idz"))
  178. (para (@) "para") (br (@)) "cdata" (para "second par")
  179. (div (@ (name "aa")) (para (@) "third para")))
  180. )
  181. )
  182. (run-test (node-self (node-typeof? 'para)) tree '())
  183. (run-test (node-self (node-typeof? 'elem)) tree (list tree))
  184. )
  185. ;; Location path, full form: descendant-or-self::node()
  186. ;; Location path, abbreviated form: //
  187. ;; selects the context node, all the children (including attribute nodes)
  188. ;; of the context node, and all the children of all the (element)
  189. ;; descendants of the context node.
  190. ;; This is _almost_ a powerset of the context node.
  191. (let* ((tree
  192. '(para (@ (name "elem") (id "idz"))
  193. (para (@) "para") (br (@)) "cdata" (para "second par")
  194. (div (@ (name "aa")) (para (@) "third para")))
  195. )
  196. (expected
  197. (cons tree
  198. (append (cdr tree)
  199. '((@) "para" (@) "second par"
  200. (@ (name "aa")) (para (@) "third para")
  201. (@) "third para"))))
  202. )
  203. (run-test
  204. (node-or
  205. (node-self (node-typeof? '*any*))
  206. (node-closure (node-typeof? '*any*)))
  207. tree expected)
  208. (run-test (sxpath '(//)) tree expected)
  209. )
  210. ;; Location path, full form: ancestor::div
  211. ;; Location path, abbreviated form: _none_
  212. ;; selects all div ancestors of the context node
  213. ;; This Location expression is equivalent to the following:
  214. ; /descendant-or-self::div[descendant::node() = curr_node]
  215. ;; This shows that the ancestor:: axis is actually redundant. Still,
  216. ;; it can be emulated as the following SXPath expression demonstrates.
  217. ;; The insight behind "ancestor::div" -- selecting all "div" ancestors
  218. ;; of the current node -- is
  219. ;; S[ancestor::div] context_node =
  220. ;; { y | y=subnode*(root), context_node=subnode(subnode*(y)),
  221. ;; isElement(y), name(y) = "div" }
  222. ;; We observe that
  223. ;; { y | y=subnode*(root), pred(y) }
  224. ;; can be expressed in SXPath as
  225. ;; ((node-or (node-self pred) (node-closure pred)) root-node)
  226. ;; The composite predicate 'isElement(y) & name(y) = "div"' corresponds to
  227. ;; (node-self (node-typeof? 'div)) in SXPath. Finally, filter
  228. ;; context_node=subnode(subnode*(y)) is tantamount to
  229. ;; (node-closure (node-eq? context-node)), whereas node-reduce denotes the
  230. ;; the composition of converters-predicates in the filtering context.
  231. (let*
  232. ((root
  233. '(div (@ (name "elem") (id "idz"))
  234. (para (@) "para") (br (@)) "cdata" (para (@) "second par")
  235. (div (@ (name "aa")) (para (@) "third para"))))
  236. (context-node ; /descendant::any()[child::text() == "third para"]
  237. (car
  238. ((node-closure
  239. (select-kids
  240. (node-equal? "third para")))
  241. root)))
  242. (pred
  243. (node-reduce (node-self (node-typeof? 'div))
  244. (node-closure (node-eq? context-node))
  245. ))
  246. )
  247. (run-test
  248. (node-or
  249. (node-self pred)
  250. (node-closure pred))
  251. root
  252. (cons root
  253. '((div (@ (name "aa")) (para (@) "third para")))))
  254. )
  255. ;; Location path, full form: child::div/descendant::para
  256. ;; Location path, abbreviated form: div//para
  257. ;; selects the para element descendants of the div element
  258. ;; children of the context node
  259. (let ((tree
  260. '(elem (@ (name "elem") (id "idz"))
  261. (para (@) "para") (br (@)) "cdata" (para "second par")
  262. (div (@ (name "aa")) (para (@) "third para")
  263. (div (para "fourth para"))))
  264. )
  265. (expected
  266. '((para (@) "third para") (para "fourth para")))
  267. )
  268. (run-test
  269. (node-join
  270. (select-kids (node-typeof? 'div))
  271. (node-closure (node-typeof? 'para)))
  272. tree expected)
  273. (run-test (sxpath '(div // para)) tree expected)
  274. )
  275. ;; Location path, full form: /descendant::olist/child::item
  276. ;; Location path, abbreviated form: //olist/item
  277. ;; selects all the item elements that have an olist parent (which is not root)
  278. ;; and that are in the same document as the context node
  279. ;; See the following test.
  280. ;; Location path, full form: /descendant::td/attribute::align
  281. ;; Location path, abbreviated form: //td/@align
  282. ;; Selects 'align' attributes of all 'td' elements in tree1
  283. (let ((tree tree1)
  284. (expected
  285. '((align "right") (align "center") (align "center") (align "center"))
  286. ))
  287. (run-test
  288. (node-join
  289. (node-closure (node-typeof? 'td))
  290. (select-kids (node-typeof? '@))
  291. (select-kids (node-typeof? 'align)))
  292. tree expected)
  293. (run-test (sxpath '(// td @ align)) tree expected)
  294. )
  295. ;; Location path, full form: /descendant::td[attribute::align]
  296. ;; Location path, abbreviated form: //td[@align]
  297. ;; Selects all td elements that have an attribute 'align' in tree1
  298. (let ((tree tree1)
  299. (expected
  300. '((td (@ (align "right")) "Talks ") (td (@ (align "center")) " = ")
  301. (td (@ (align "center")) " = ") (td (@ (align "center")) " = "))
  302. ))
  303. (run-test
  304. (node-reduce
  305. (node-closure (node-typeof? 'td))
  306. (filter
  307. (node-join
  308. (select-kids (node-typeof? '@))
  309. (select-kids (node-typeof? 'align)))))
  310. tree expected)
  311. (run-test (sxpath `(// td ,(node-self (sxpath '(@ align))))) tree expected)
  312. (run-test (sxpath '(// (td (@ align)))) tree expected)
  313. (run-test (sxpath '(// ((td) (@ align)))) tree expected)
  314. ;; note! (sxpath ...) is a converter. Therefore, it can be used
  315. ;; as any other converter, for example, in the full-form SXPath.
  316. ;; Thus we can mix the full and abbreviated form SXPath's freely.
  317. (run-test
  318. (node-reduce
  319. (node-closure (node-typeof? 'td))
  320. (filter
  321. (sxpath '(@ align))))
  322. tree expected)
  323. )
  324. ;; Location path, full form: /descendant::td[attribute::align = "right"]
  325. ;; Location path, abbreviated form: //td[@align = "right"]
  326. ;; Selects all td elements that have an attribute align = "right" in tree1
  327. (let ((tree tree1)
  328. (expected
  329. '((td (@ (align "right")) "Talks "))
  330. ))
  331. (run-test
  332. (node-reduce
  333. (node-closure (node-typeof? 'td))
  334. (filter
  335. (node-join
  336. (select-kids (node-typeof? '@))
  337. (select-kids (node-equal? '(align "right"))))))
  338. tree expected)
  339. (run-test (sxpath '(// (td (@ (equal? (align "right")))))) tree expected)
  340. )
  341. ;; Location path, full form: child::para[position()=1]
  342. ;; Location path, abbreviated form: para[1]
  343. ;; selects the first para child of the context node
  344. (let ((tree
  345. '(elem (@ (name "elem") (id "idz"))
  346. (para (@) "para") (br (@)) "cdata" (para "second par")
  347. (div (@ (name "aa")) (para (@) "third para")))
  348. )
  349. (expected
  350. '((para (@) "para"))
  351. ))
  352. (run-test
  353. (node-reduce
  354. (select-kids (node-typeof? 'para))
  355. (node-pos 1))
  356. tree expected)
  357. (run-test (sxpath '((para 1))) tree expected)
  358. )
  359. ;; Location path, full form: child::para[position()=last()]
  360. ;; Location path, abbreviated form: para[last()]
  361. ;; selects the last para child of the context node
  362. (let ((tree
  363. '(elem (@ (name "elem") (id "idz"))
  364. (para (@) "para") (br (@)) "cdata" (para "second par")
  365. (div (@ (name "aa")) (para (@) "third para")))
  366. )
  367. (expected
  368. '((para "second par"))
  369. ))
  370. (run-test
  371. (node-reduce
  372. (select-kids (node-typeof? 'para))
  373. (node-pos -1))
  374. tree expected)
  375. (run-test (sxpath '((para -1))) tree expected)
  376. )
  377. ;; Illustrating the following Note of Sec 2.5 of XPath:
  378. ;; "NOTE: The location path //para[1] does not mean the same as the
  379. ;; location path /descendant::para[1]. The latter selects the first
  380. ;; descendant para element; the former selects all descendant para
  381. ;; elements that are the first para children of their parents."
  382. (let ((tree
  383. '(elem (@ (name "elem") (id "idz"))
  384. (para (@) "para") (br (@)) "cdata" (para "second par")
  385. (div (@ (name "aa")) (para (@) "third para")))
  386. )
  387. )
  388. (run-test
  389. (node-reduce ; /descendant::para[1] in SXPath
  390. (node-closure (node-typeof? 'para))
  391. (node-pos 1))
  392. tree '((para (@) "para")))
  393. (run-test (sxpath '(// (para 1))) tree
  394. '((para (@) "para") (para (@) "third para")))
  395. )
  396. ;; Location path, full form: parent::node()
  397. ;; Location path, abbreviated form: ..
  398. ;; selects the parent of the context node. The context node may be
  399. ;; an attribute node!
  400. ;; For the last test:
  401. ;; Location path, full form: parent::*/attribute::name
  402. ;; Location path, abbreviated form: ../@name
  403. ;; Selects the name attribute of the parent of the context node
  404. (let* ((tree
  405. '(elem (@ (name "elem") (id "idz"))
  406. (para (@) "para") (br (@)) "cdata" (para "second par")
  407. (div (@ (name "aa")) (para (@) "third para")))
  408. )
  409. (para1 ; the first para node
  410. (car ((sxpath '(para)) tree)))
  411. (para3 ; the third para node
  412. (car ((sxpath '(div para)) tree)))
  413. (div ; div node
  414. (car ((sxpath '(// div)) tree)))
  415. )
  416. (run-test
  417. (node-parent tree)
  418. para1 (list tree))
  419. (run-test
  420. (node-parent tree)
  421. para3 (list div))
  422. (run-test ; checking the parent of an attribute node
  423. (node-parent tree)
  424. ((sxpath '(@ name)) div) (list div))
  425. (run-test
  426. (node-join
  427. (node-parent tree)
  428. (select-kids (node-typeof? '@))
  429. (select-kids (node-typeof? 'name)))
  430. para3 '((name "aa")))
  431. (run-test
  432. (sxpath `(,(node-parent tree) @ name))
  433. para3 '((name "aa")))
  434. )
  435. ;; Location path, full form: following-sibling::chapter[position()=1]
  436. ;; Location path, abbreviated form: none
  437. ;; selects the next chapter sibling of the context node
  438. ;; The path is equivalent to
  439. ;; let cnode = context-node
  440. ;; in
  441. ;; parent::* / child::chapter [take-after node_eq(self::*,cnode)]
  442. ;; [position()=1]
  443. (let* ((tree
  444. '(document
  445. (preface "preface")
  446. (chapter (@ (id "one")) "Chap 1 text")
  447. (chapter (@ (id "two")) "Chap 2 text")
  448. (chapter (@ (id "three")) "Chap 3 text")
  449. (chapter (@ (id "four")) "Chap 4 text")
  450. (epilogue "Epilogue text")
  451. (appendix (@ (id "A")) "App A text")
  452. (References "References"))
  453. )
  454. (a-node ; to be used as a context node
  455. (car ((sxpath '(// (chapter (@ (equal? (id "two")))))) tree)))
  456. (expected
  457. '((chapter (@ (id "three")) "Chap 3 text")))
  458. )
  459. (run-test
  460. (node-reduce
  461. (node-join
  462. (node-parent tree)
  463. (select-kids (node-typeof? 'chapter)))
  464. (take-after (node-eq? a-node))
  465. (node-pos 1)
  466. )
  467. a-node expected)
  468. )
  469. ;; preceding-sibling::chapter[position()=1]
  470. ;; selects the previous chapter sibling of the context node
  471. ;; The path is equivalent to
  472. ;; let cnode = context-node
  473. ;; in
  474. ;; parent::* / child::chapter [take-until node_eq(self::*,cnode)]
  475. ;; [position()=-1]
  476. (let* ((tree
  477. '(document
  478. (preface "preface")
  479. (chapter (@ (id "one")) "Chap 1 text")
  480. (chapter (@ (id "two")) "Chap 2 text")
  481. (chapter (@ (id "three")) "Chap 3 text")
  482. (chapter (@ (id "four")) "Chap 4 text")
  483. (epilogue "Epilogue text")
  484. (appendix (@ (id "A")) "App A text")
  485. (References "References"))
  486. )
  487. (a-node ; to be used as a context node
  488. (car ((sxpath '(// (chapter (@ (equal? (id "three")))))) tree)))
  489. (expected
  490. '((chapter (@ (id "two")) "Chap 2 text")))
  491. )
  492. (run-test
  493. (node-reduce
  494. (node-join
  495. (node-parent tree)
  496. (select-kids (node-typeof? 'chapter)))
  497. (take-until (node-eq? a-node))
  498. (node-pos -1)
  499. )
  500. a-node expected)
  501. )
  502. ;; /descendant::figure[position()=42]
  503. ;; selects the forty-second figure element in the document
  504. ;; See the next example, which is more general.
  505. ;; Location path, full form:
  506. ;; child::table/child::tr[position()=2]/child::td[position()=3]
  507. ;; Location path, abbreviated form: table/tr[2]/td[3]
  508. ;; selects the third td of the second tr of the table
  509. (let ((tree ((node-closure (node-typeof? 'p)) tree1))
  510. (expected
  511. '((td " data + control"))
  512. ))
  513. (run-test
  514. (node-join
  515. (select-kids (node-typeof? 'table))
  516. (node-reduce (select-kids (node-typeof? 'tr))
  517. (node-pos 2))
  518. (node-reduce (select-kids (node-typeof? 'td))
  519. (node-pos 3)))
  520. tree expected)
  521. (run-test (sxpath '(table (tr 2) (td 3))) tree expected)
  522. )
  523. ;; Location path, full form:
  524. ;; child::para[attribute::type='warning'][position()=5]
  525. ;; Location path, abbreviated form: para[@type='warning'][5]
  526. ;; selects the fifth para child of the context node that has a type
  527. ;; attribute with value warning
  528. (let ((tree
  529. '(chapter
  530. (para "para1")
  531. (para (@ (type "warning")) "para 2")
  532. (para (@ (type "warning")) "para 3")
  533. (para (@ (type "warning")) "para 4")
  534. (para (@ (type "warning")) "para 5")
  535. (para (@ (type "warning")) "para 6"))
  536. )
  537. (expected
  538. '((para (@ (type "warning")) "para 6"))
  539. ))
  540. (run-test
  541. (node-reduce
  542. (select-kids (node-typeof? 'para))
  543. (filter
  544. (node-join
  545. (select-kids (node-typeof? '@))
  546. (select-kids (node-equal? '(type "warning")))))
  547. (node-pos 5))
  548. tree expected)
  549. (run-test (sxpath '( (((para (@ (equal? (type "warning"))))) 5 ) ))
  550. tree expected)
  551. (run-test (sxpath '( (para (@ (equal? (type "warning"))) 5 ) ))
  552. tree expected)
  553. )
  554. ;; Location path, full form:
  555. ;; child::para[position()=5][attribute::type='warning']
  556. ;; Location path, abbreviated form: para[5][@type='warning']
  557. ;; selects the fifth para child of the context node if that child has a 'type'
  558. ;; attribute with value warning
  559. (let ((tree
  560. '(chapter
  561. (para "para1")
  562. (para (@ (type "warning")) "para 2")
  563. (para (@ (type "warning")) "para 3")
  564. (para (@ (type "warning")) "para 4")
  565. (para (@ (type "warning")) "para 5")
  566. (para (@ (type "warning")) "para 6"))
  567. )
  568. (expected
  569. '((para (@ (type "warning")) "para 5"))
  570. ))
  571. (run-test
  572. (node-reduce
  573. (select-kids (node-typeof? 'para))
  574. (node-pos 5)
  575. (filter
  576. (node-join
  577. (select-kids (node-typeof? '@))
  578. (select-kids (node-equal? '(type "warning"))))))
  579. tree expected)
  580. (run-test (sxpath '( (( (para 5)) (@ (equal? (type "warning"))))))
  581. tree expected)
  582. (run-test (sxpath '( (para 5 (@ (equal? (type "warning")))) ))
  583. tree expected)
  584. )
  585. ;; Location path, full form:
  586. ;; child::*[self::chapter or self::appendix]
  587. ;; Location path, semi-abbreviated form: *[self::chapter or self::appendix]
  588. ;; selects the chapter and appendix children of the context node
  589. (let ((tree
  590. '(document
  591. (preface "preface")
  592. (chapter (@ (id "one")) "Chap 1 text")
  593. (chapter (@ (id "two")) "Chap 2 text")
  594. (chapter (@ (id "three")) "Chap 3 text")
  595. (epilogue "Epilogue text")
  596. (appendix (@ (id "A")) "App A text")
  597. (References "References"))
  598. )
  599. (expected
  600. '((chapter (@ (id "one")) "Chap 1 text")
  601. (chapter (@ (id "two")) "Chap 2 text")
  602. (chapter (@ (id "three")) "Chap 3 text")
  603. (appendix (@ (id "A")) "App A text"))
  604. ))
  605. (run-test
  606. (node-join
  607. (select-kids (node-typeof? '*))
  608. (filter
  609. (node-or
  610. (node-self (node-typeof? 'chapter))
  611. (node-self (node-typeof? 'appendix)))))
  612. tree expected)
  613. (run-test (sxpath `(* ,(node-or (node-self (node-typeof? 'chapter))
  614. (node-self (node-typeof? 'appendix)))))
  615. tree expected)
  616. )
  617. ;; Location path, full form: child::chapter[child::title='Introduction']
  618. ;; Location path, abbreviated form: chapter[title = 'Introduction']
  619. ;; selects the chapter children of the context node that have one or more
  620. ;; title children with string-value equal to Introduction
  621. ;; See a similar example: //td[@align = "right"] above.
  622. ;; Location path, full form: child::chapter[child::title]
  623. ;; Location path, abbreviated form: chapter[title]
  624. ;; selects the chapter children of the context node that have one or
  625. ;; more title children
  626. ;; See a similar example //td[@align] above.
  627. (let ((tree tree3)
  628. (expected
  629. '("Let us go then, you and I," "In the room the women come and go")
  630. ))
  631. (run-test
  632. (node-join
  633. (node-closure (node-typeof? 'stanza))
  634. (node-reduce
  635. (select-kids (node-typeof? 'line)) (node-pos 1))
  636. (select-kids (node-typeof? '*text*)))
  637. tree expected)
  638. (run-test (sxpath '(// stanza (line 1) *text*)) tree expected)
  639. )
  640. )