config-parser.scm 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572
  1. ;; This file is part of scheme-GNUnet.
  2. ;; Copyright (C) 2021 GNUnet e.V.
  3. ;;
  4. ;; scheme-GNUnet is free software: you can redistribute it and/or modify it
  5. ;; under the terms of the GNU Affero General Public License as published
  6. ;; by the Free Software Foundation, either version 3 of the License,
  7. ;; or (at your option) any later version.
  8. ;;
  9. ;; scheme-GNUnet is distributed in the hope that it will be useful, but
  10. ;; WITHOUT ANY WARRANTY; without even the implied warranty of
  11. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. ;; Affero General Public License for more details.
  13. ;;
  14. ;; You should have received a copy of the GNU Affero General Public License
  15. ;; along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. ;;
  17. ;; SPDX-License-Identifier: AGPL-3.0-or-later
  18. (use-modules (gnu gnunet config parser)
  19. (quickcheck)
  20. (quickcheck generator)
  21. (quickcheck arbitrary)
  22. (quickcheck property)
  23. ((rnrs conditions) #:select (&assertion))
  24. (ice-9 match)
  25. (srfi srfi-8)
  26. (srfi srfi-26))
  27. ;; Test the line parser on some valid inputs.
  28. (define-syntax-rule (cond/pos (x y) (pred? accessor ...) ...)
  29. (cond ((and (pred? x) (pred? y))
  30. (and (= (accessor x) (accessor y)) ...))
  31. ...
  32. ((and (or (pred? x) ...)
  33. (or (pred? y) ...)) #f)
  34. (#t (error "what madness is this?"))))
  35. (define (lipo=? x y)
  36. "Are two line position objects equal?"
  37. (cond/pos (x y)
  38. (#{%-position?}# position:%)
  39. (#{#-position?}# position:#)
  40. (=-position?
  41. position:variable-start
  42. position:variable-end
  43. position:=
  44. position:value-start
  45. position:value-end)
  46. (#{[]-position?}#
  47. position:section-name-start
  48. position:section-name-end)
  49. (@inline@-position?
  50. position:@inline@-start
  51. position:@inline@-end)
  52. ((cut eq? <> #f))
  53. ((cut eq? <> #t))))
  54. (define-syntax-rule (test-lipo name text expected)
  55. (test-assert name
  56. (lipo=? (parse-line text) expected)))
  57. (test-lipo "trivial empty line" "" #t)
  58. (test-lipo "empty line: lf" "\n" #t)
  59. (test-lipo "empty line: cr" "\r" #t)
  60. (test-lipo "empty line: space" " " #t)
  61. (test-lipo "empty line: space + lf" " \n" #t)
  62. (test-lipo "empty line: tab" "\t" #t)
  63. (test-lipo "section name" "[hello]"
  64. (#{make-[]-position}# 1 6))
  65. (test-lipo "section name with spaces" "[ hello ]"
  66. (#{make-[]-position}# 1 9))
  67. ;; Used for some services.
  68. (test-lipo "section name with dots" "[hell.o.gnu]"
  69. (#{make-[]-position}# 1 11))
  70. ;; Allowed in upstream.
  71. (test-lipo "section name with leading space" "\t[hello]"
  72. (#{make-[]-position}# 2 7))
  73. (test-lipo "section name with more leading space" "\t [hello]"
  74. (#{make-[]-position}# 3 8))
  75. (test-lipo "section name with trailing space" "[hello]\t"
  76. (#{make-[]-position}# 1 6))
  77. (test-lipo "section name with more trailing space" "[hello]\t\t"
  78. (#{make-[]-position}# 1 6))
  79. (test-lipo "section name with missing ]" "[hell" #f)
  80. (test-lipo "section name with missing [" "hell]" #f)
  81. (test-lipo "empty % comment" "%" (#{make-%-position}# 0))
  82. (test-lipo "empty # comment" "#" (#{make-#-position}# 0))
  83. (test-lipo "% comment with text" "%text" (#{make-%-position}# 0))
  84. (test-lipo "# comment with text" "#text" (#{make-#-position}# 0))
  85. (test-lipo "% comment with leading whitespace" " %text"
  86. (#{make-%-position}# 1))
  87. (test-lipo "# comment with leading whitespace" " #text"
  88. (#{make-#-position}# 1))
  89. (test-lipo "% comment with more leading whitespace" " \t%text"
  90. (#{make-%-position}# 2))
  91. (test-lipo "# comment with more leading whitespace" " \t#text"
  92. (#{make-#-position}# 2))
  93. (test-lipo "# comment with %" "#%stuff" (#{make-#-position}# 0))
  94. (test-lipo "% comment with #" "%#stuff" (#{make-%-position}# 0))
  95. (test-lipo "= not allowed with empty variable name" "=value" #f)
  96. (test-lipo "even with spaces" " =value" #f)
  97. (test-lipo "= with variable and value" "var=value"
  98. (make-=-position 0 3 3 4 9))
  99. (test-lipo "= with spacy variable and spacy value" "\t\tvar =\tvalue "
  100. (make-=-position 2 5 6 8 13))
  101. ;; parse-line does not impose what the end-of-line characters are.
  102. (test-lipo "= with spacier variable and spacy value" "\t\tvar \n=\tvalue "
  103. (make-=-position 2 5 7 9 14))
  104. (test-lipo "= with spaces in value" "var=val ue"
  105. (make-=-position 0 3 3 4 10))
  106. (test-lipo "line parser does not perform unquoting" "var = 'val ue'"
  107. (make-=-position 0 3 4 6 14))
  108. (test-lipo "quotes still make nice delimiters" "var = ' value '"
  109. (make-=-position 0 3 4 6 15))
  110. ;; "VAR = VALUE # comment" seems acceptable to me actually,
  111. ;; but upstream interprets it as "VAR" = "VALUE # comment"
  112. ;; IIUC.
  113. (test-lipo "= cannot be followed by a % comment" "var = value %comment "
  114. (make-=-position 0 3 4 6 20))
  115. (test-lipo "= cannot be followed by a # comment" "var = value #comment "
  116. (make-=-position 0 3 4 6 20))
  117. ;; Bug discovered with the QuickCheck tests below!
  118. (test-lipo "= with empty value" "x="
  119. (make-=-position 0 1 1 2 2))
  120. (test-lipo "= with spacy empty value" "x= "
  121. ;; (0 1 1 3 3) would also be correct.
  122. (make-=-position 0 1 1 2 2))
  123. (test-lipo "= with spacier empty value" "x= "
  124. ;; (0 1 1 3 3) and (0 1 1 4 4) would also be correct.
  125. (make-=-position 0 1 1 2 2))
  126. (define-syntax-rule (test-inline-po name line expected-fipo)
  127. (test-equal name expected-fipo
  128. (let ((l (parse-line line)))
  129. (if (@inline@-position? l)
  130. (cons (position:@inline@-filename-start l)
  131. (position:@inline@-filename-end l))
  132. 'What?))))
  133. (test-lipo "@INLINE@ with file name" "@INLINE@ /x/${stuff}.config"
  134. (make-@inline@-position 0 27))
  135. (test-inline-po "@INLINE@ file name positions" "@INLINE@ stuff" (cons 9 14))
  136. (test-lipo "@INLINE@ with file name + space" "@INLINE@ X\t"
  137. (make-@inline@-position 0 10))
  138. (test-inline-po "@INLINE@ + space file name positions" "@INLINE@ stuff "
  139. (cons 9 14))
  140. (test-lipo "@INLINE@ with file name + more space" "@INLINE@ X\t\t"
  141. (make-@inline@-position 0 10))
  142. (test-inline-po "@INLINE@ more space file name positions" "@INLINE@ X \t"
  143. (cons 9 10))
  144. (test-lipo "space + @INLINE@ with file name" " @INLINE@ X"
  145. (make-@inline@-position 1 11))
  146. (test-inline-po "space + @INLINE@ file name positions" " @INLINE@ X"
  147. (cons 10 11))
  148. ;; TODO: are empty file names acceptable?
  149. ;; If so, change the tests (see #; commented out code).
  150. (test-lipo "@INLINE@ without space" "@INLINE@" #false)
  151. (test-lipo "@INLINE@ with empty file name" "@INLINE@ "
  152. #f
  153. #;(make-@inline@-position 0 9))
  154. #;
  155. (test-inline-po "@INLINE@ with empty file name (position)" "@INLINE@ "
  156. (cons 9 9))
  157. (test-lipo "@INLINE@ with empty file name + space" "@INLINE@ \t"
  158. #f
  159. #;(make-@inline@-position 0 9))
  160. #;
  161. (test-inline-po "@INLINE@ with empty file name + space (position)" "@INLINE@ "
  162. (cons 9 9))
  163. ;; This fairly trivial procedure is copied from tests/kinds/octal.scm
  164. ;; (disarchive by Timothy Sample)
  165. ;; https://git.ngyro.com/disarchive/tree/tests/kinds/octal.scm?id=27a0fc79aacaaab0388e974b07cda885079f0f05).
  166. (define (char-set->arbitrary cs)
  167. (arbitrary
  168. (gen (choose-char cs))
  169. (xform (lambda (chr gen)
  170. (generator-variant (char->integer chr) gen)))))
  171. ;; Test the line parser on random inputs
  172. (define $interesting-char
  173. (char-set->arbitrary (string->char-set "[]=#% \tab")))
  174. (define $interesting-random-string
  175. ($string $interesting-char))
  176. (define $interesting-infix
  177. ($choose ((cute string=? "") ($const ""))
  178. ((cute string=? "@INCLUDE@") ($const "@INCLUDE@"))))
  179. (define-syntax-rule (false-if-assertion exp exp* ...)
  180. (with-exception-handler
  181. (lambda (e) #f)
  182. (lambda () exp exp* ...)
  183. #:unwind? #t
  184. #:unwind-for-type &assertion))
  185. (define (in-bounds? line pos)
  186. "Verify the position information @var{pos} is at least
  187. in-bounds for the string @var{line}."
  188. (cond ((%-position? pos)
  189. (and (<= 0 (position:% pos))
  190. (< (position:% pos) (string-length line))))
  191. ((#{#-position?}# pos)
  192. (and (<= 0 (#{position:#}# pos))
  193. (< (#{position:#}# pos) (string-length line))))
  194. ((=-position? pos)
  195. (and (<= 0 (position:= pos))
  196. (< (position:= pos) (string-length line))))
  197. ((#{[]-position?}# pos)
  198. (and (<= 0 (position:section-name-start pos)
  199. (position:section-name-end pos))
  200. (< (position:section-name-end pos)
  201. (string-length line))))
  202. ((@inline@-position? pos)
  203. (and (<= 0 (position:@inline@-start pos)
  204. (position:@inline@-end pos))
  205. (< (position:@inline@-end pos)
  206. (string-length line))))
  207. ((eq? pos #f) #t)
  208. ((eq? pos #t) #t)
  209. (#f (error "what madness is this?"))))
  210. (configure-quickcheck
  211. ;; Increase this when testing.
  212. (stop? (lambda (success-count _)
  213. (>= success-count #;16384 2048)))
  214. ;; Large inputs don't produce much additional value.
  215. (size (lambda (test-number)
  216. (if (zero? test-number)
  217. 0
  218. (1+ (inexact->exact (floor/ (log test-number) (log 8))))))))
  219. (test-assert "line position parser does not crash"
  220. (quickcheck
  221. (property ((pre $interesting-random-string)
  222. (in $interesting-infix)
  223. (post $interesting-random-string))
  224. (false-if-assertion
  225. (begin (parse-line (string-append pre in post))
  226. #t)))))
  227. (test-assert "line position parser produces in-bounds results"
  228. (quickcheck
  229. (property ((pre $interesting-random-string)
  230. (in $interesting-infix)
  231. (post $interesting-random-string))
  232. (let ((line (string-append pre in post)))
  233. (false-if-assertion
  234. (in-bounds? line (parse-line line)))))))
  235. ;; Test the position-preserving variable substitution parser.
  236. ;; First verify some properties on random data.
  237. (configure-quickcheck
  238. ;; Increase this when testing changes.
  239. (stop? (lambda (success-count _)
  240. (>= success-count 2048 #;000 success-count)))
  241. ;; Large inputs don't produce much additional value.
  242. (size (lambda (test-number)
  243. (if (zero? test-number)
  244. 0
  245. (min 6 (1+ (inexact->exact (floor/ (log test-number) (log 4)))))))))
  246. (define (expo:start expo)
  247. "Given a position object, return the starting position of
  248. the region of text it covers."
  249. (cond ((#{${:-}-position?}# expo)
  250. ;; - 2: remove the ${ in ${VAR:-DEFAULT}
  251. (- (#{expo:${:-}-name-start}# expo) 2))
  252. ((#{${}-position?}# expo)
  253. ;; - 2: remove the ${ in ${VAR}
  254. (- (#{expo:${}-name-start}# expo) 2))
  255. (($-position? expo)
  256. ;; - 1: remove the $ in $VAR
  257. (- (expo:$-name-start expo) 1))
  258. ((literal-position? expo)
  259. (expo:literal-start expo))))
  260. (define (expo:end expo)
  261. "Given a position object, return the end position (exclusive) of
  262. the region of text it covers."
  263. (cond ((#{${:-}-position?}# expo)
  264. ;; + 1: add the } in ${VAR:-DEFAULT}
  265. (+ 1 (#{expo:${:-}-value-end}# expo) 1))
  266. ((#{${}-position?}# expo)
  267. ;; + 1: add the } in ${VAR}
  268. (+ (#{expo:${}-name-end}# expo) 1))
  269. (($-position? expo)
  270. (expo:$-name-end expo))
  271. ((literal-position? expo)
  272. (expo:literal-end expo))))
  273. (define (expo:contiguous? expos)
  274. "Is the list expansion position objects @var{expos} contiguous?
  275. If so, return the last object in @var{expos}. Otherwise, return
  276. @code{#f}."
  277. (define (internally-contiguous? x)
  278. (cond ((#{${:-}-position?}# x)
  279. (let ((parts (#{expo:${:-}-value-parts}# x)))
  280. (if (null? parts)
  281. x
  282. (expo:contiguous? parts))))
  283. ((#{${}-position?}# x) #t)
  284. (($-position? x) #t)
  285. ((literal-position? x) #t)
  286. (#t (error "what is this madness?"))))
  287. (match expos
  288. (() #t)
  289. ((x) (internally-contiguous? x))
  290. ((x y . rest)
  291. (and (= (expo:end x) (expo:start y))
  292. (internally-contiguous? x)
  293. (expo:contiguous? (cdr expos))))))
  294. (define $interesting-char/expo
  295. (char-set->arbitrary (string->char-set "${:-}ab")))
  296. (define-syntax-rule ($choose-with-eq? x ...)
  297. ($choose ((cute eq? x) ($const x)) ...))
  298. (define $nested ($choose-with-eq? #f '#{${}}# '#{${:-}}#))
  299. (define-syntax-rule (true-if-parse-error exp exp* ...)
  300. (with-exception-handler
  301. (lambda (e) #t)
  302. (lambda () exp exp* ...)
  303. #:unwind? #t
  304. #:unwind-for-type &expansion-violation))
  305. (define $text-and-range
  306. (arbitrary
  307. (gen
  308. (sized-generator
  309. (lambda (size)
  310. (generator-let* ((text-length (choose-integer 0 size))
  311. (text (choose-string
  312. (arbitrary-gen $interesting-char/expo)
  313. text-length))
  314. (start (choose-integer 0 text-length))
  315. (end (choose-integer start text-length)))
  316. (generator-return (list text start end))))))
  317. (xform #f)))
  318. ;; Unfortunatly, these QuickCheck tests do not reach all lines
  319. ;; of the procedure in practice. TODO: write a fuzzer for Guile.
  320. ;;
  321. ;; (Should be feasible using the tracing framework.)
  322. (test-assert "expansion parser does not crash"
  323. (quickcheck
  324. (property ((text-and-range $text-and-range)
  325. (nested? $nested))
  326. (match text-and-range
  327. ((text start end)
  328. (false-if-assertion
  329. (true-if-parse-error
  330. (parse-expandable* text start end nested?)
  331. #t)))))))
  332. (test-assert "expansion position objects are contiguous"
  333. (quickcheck
  334. (property ((text-and-range $text-and-range)
  335. (nested? $nested))
  336. (match text-and-range
  337. ((text start end)
  338. (true-if-parse-error
  339. (receive (expos end)
  340. (parse-expandable* text start end nested?)
  341. (expo:contiguous? expos))))))))
  342. (define (maybe-parse text start end nested?)
  343. "Try to parse the range @var{start} to @var{end} of @var{text}.
  344. Return a structure that can be compares with @code{equal?} and
  345. is invariant under translations."
  346. (with-exception-handler
  347. (lambda (e)
  348. (cond ((empty-variable-violation? e)
  349. `(empty-variable-violation
  350. ,(- (expansion-violation-position e) start)
  351. ,(empty-variable-kind e)))
  352. ((missing-close-violation? e)
  353. `(missing-close-violation
  354. ,(- (expansion-violation-position e) start)
  355. ,(missing-close-kind e)))
  356. ;; See the TODO in parse-expandable*.
  357. (#t
  358. `(todo
  359. ,(- (expansion-violation-position e) start)))))
  360. (lambda ()
  361. (receive (expandibles end)
  362. (parse-expandable* text start end nested?)
  363. (cons (map (cute expansible->sexp <> start) expandibles)
  364. (- end start))))
  365. #:unwind? #t
  366. #:unwind-for-type &expansion-violation))
  367. (define (expansible->sexp pos start)
  368. (cond ((literal-position? pos)
  369. `(literal ,(- (expo:literal-start pos) start)
  370. ,(- (expo:literal-end pos) start)))
  371. (($-position? pos)
  372. `($ ,(- (expo:$-name-start pos) start)
  373. ,(- (expo:$-name-end pos) start)))
  374. ((#{${}-position?}# pos)
  375. `(#{${}}#
  376. ,(- (#{expo:${}-name-start}# pos) start)
  377. ,(- (#{expo:${}-name-end}# pos) start)))
  378. ;; HACK: work-around buggy Emacs parenthesis
  379. ;; matching detection.
  380. ((#{${:-}-position?}# pos)
  381. `(,(string->symbol "${:-}")
  382. ,(- (#{expo:${:-}-name-start}# pos) start)
  383. ,(- (#{expo:${:-}-name-end}# pos) start)
  384. ,(- (#{expo:${:-}-value-start}# pos) start)
  385. ,(- (#{expo:${:-}-value-end}# pos) start)
  386. ,(map (cute expansible->sexp <> start)
  387. (#{expo:${:-}-value-parts}# pos))))))
  388. (test-assert "start and end are respected"
  389. (quickcheck
  390. (property ((text-and-range $text-and-range)
  391. (nested? $nested))
  392. (match text-and-range
  393. ((text start end)
  394. (equal? (maybe-parse text start end nested?)
  395. (maybe-parse (substring text start end)
  396. 0 (- end start) nested?)))))))
  397. ;; Now plenty of failure cases.
  398. ;; Expand an expansion error @code{c} conforming to
  399. ;; @code{cond}.
  400. (define-syntax-rule (test-expansion-error (name nested?) (c text) cond?)
  401. (test-assert name
  402. (with-exception-handler (lambda (c) cond?)
  403. (lambda () (parse-expandable* text 0 (string-length text) nested?))
  404. #:unwind? #t
  405. #:unwind-for-type &expansion-violation)))
  406. ;; Test unbraced variable expansion, unnested.
  407. (test-expansion-error ("$ + delimiter" #f)
  408. (c "$/")
  409. (and (empty-variable-violation? c)
  410. (eq? (empty-variable-kind c) '$)
  411. (= (expansion-violation-position c) 1)))
  412. (test-expansion-error ("$ + delimiter + more" #f)
  413. (c "$/more")
  414. (and (empty-variable-violation? c)
  415. (eq? (empty-variable-kind c) '$)
  416. (= (expansion-violation-position c) 1)))
  417. (test-expansion-error ("more + $ + delimiter" #f)
  418. (c "more$/")
  419. (and (empty-variable-violation? c)
  420. (eq? (empty-variable-kind c) '$)
  421. (= (expansion-violation-position c) 5)))
  422. (test-expansion-error ("$ + end of string" #f)
  423. (c "$")
  424. (and (empty-variable-violation? c)
  425. (eq? (empty-variable-kind c) '$)
  426. (= (expansion-violation-position c) 1)))
  427. (test-expansion-error ("more + $ + end of string" #f)
  428. (c "more$")
  429. (and (empty-variable-violation? c)
  430. (eq? (empty-variable-kind c) '$)
  431. (= (expansion-violation-position c) 5)))
  432. ;; Test unbraced variable expansion, nested.
  433. (test-expansion-error ("$ + }, nested" '#{${:-}}#)
  434. (c "$}")
  435. (and (empty-variable-violation? c)
  436. (eq? (empty-variable-kind c) '$)
  437. (= (expansion-violation-position c) 1)))
  438. (test-expansion-error ("$ + } + delimiter, nested" '#{${:-}}#)
  439. ;; don't interpret this as the variable } expanded
  440. ;; folowed by a slash!
  441. (c "$}/")
  442. (and (empty-variable-violation? c)
  443. (eq? (empty-variable-kind c) '$)
  444. (= (expansion-violation-position c) 1)))
  445. ;; Test braced variables, unnested & some nesting
  446. (test-expansion-error ("empty braced variable" #f)
  447. (c "${}")
  448. (and (empty-variable-violation? c)
  449. (eq? (empty-variable-kind c) '#{${}}#)
  450. (= (expansion-violation-position c) 2)))
  451. (test-expansion-error ("empty braced variable with empty default" #f)
  452. (c "${:-}")
  453. (and (empty-variable-violation? c)
  454. (eq? (empty-variable-kind c) '#{${:-}}#)
  455. (= (expansion-violation-position c) 2)))
  456. (test-expansion-error ("empty braced variable with nonempty default" #f)
  457. (c "${:-def}")
  458. (and (empty-variable-violation? c)
  459. (eq? (empty-variable-kind c) '#{${:-}}#)
  460. (= (expansion-violation-position c) 2)))
  461. (test-expansion-error ("unclosed braced variable" #f)
  462. (c "${")
  463. (and (missing-close-violation? c)
  464. (eq? (missing-close-kind c) '#{${}}#)
  465. (= (expansion-violation-position c) 2)))
  466. (test-expansion-error ("unclosed braced variable with text" #f)
  467. (c "${text")
  468. (and (missing-close-violation? c)
  469. (eq? (missing-close-kind c) '#{${}}#)
  470. (= (expansion-violation-position c) 6)))
  471. (test-expansion-error ("unclosed braced variable with default" #f)
  472. (c "${text:-default")
  473. (and (missing-close-violation? c)
  474. (eq? (missing-close-kind c) '#{${:-}}#)
  475. (= (expansion-violation-position c) 15)))
  476. (test-expansion-error ("unclosed braced variable and weird character after -" #f)
  477. (c "${text:@") ; <-- allowed in upstream
  478. (and (expansion-violation? c)
  479. (= (expansion-violation-position c) 7)))
  480. ;; Now some success cases.
  481. (define-syntax-rule (test-expansion text expected ...)
  482. (test-equal text
  483. (map (cute expansible->sexp <> 0)
  484. (list expected ...))
  485. (match (maybe-parse text 0 (string-length text) #f)
  486. ((x . y) x)
  487. (z (cons 'what-is-this-madness z)))))
  488. (test-expansion "$TMP" (make-$-position 1 4))
  489. (test-expansion "$TMP/gnunet_arm.sock"
  490. (make-$-position 1 4)
  491. (make-literal-position 4 20))
  492. (test-expansion "${TMP}" (#{make-${}-position}# 2 5))
  493. (test-expansion "${TMP}/gnunet_arm.sock"
  494. (#{make-${}-position}# 2 5)
  495. (make-literal-position 6 22))
  496. (test-expansion "${TMP:-/tmp}"
  497. (#{make-${:-}-position}# 2 5 7 11
  498. (list (make-literal-position 7 11))))
  499. (test-expansion "${TMP:-/tmp}/gnunet_arm.sock"
  500. (#{make-${:-}-position}# 2 5 7 11
  501. (list (make-literal-position 7 11)))
  502. (make-literal-position 12 28))
  503. (test-expansion "some ${STUFF:-${TMP:-/tmp}/etc$etera}/other"
  504. (make-literal-position 0 5)
  505. (#{make-${:-}-position}# 7 12 14 36
  506. (list (#{make-${:-}-position}# 16 19 21 25
  507. (list (make-literal-position 21 25)))
  508. (make-literal-position 26 30)
  509. (make-$-position 31 36)))
  510. (make-literal-position 37 43))
  511. ;; TODO: what should ${{} be parsed as?
  512. ;; As ${} } or as the braced variable expansion with name
  513. ;; {?
  514. ;;; Local Variables:
  515. ;;; eval: (put 'property 'scheme-indent-function 1)
  516. ;;; eval: (put 'test-expansion-error 'scheme-indent-function 1)
  517. ;;; End: