config-expand.scm 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. ;; This file is part of scheme-GNUnet.
  2. ;; Copyright (C) 2021 Maxime Devos
  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: AGPL3.0-or-later
  18. (use-modules (gnu gnunet config parser)
  19. (gnu gnunet config expand)
  20. (srfi srfi-64))
  21. (define (region=? line1 start1 end1
  22. line2 start2 end2)
  23. (string=? (substring/shared line1 start1 end1)
  24. (substring/shared line2 start2 end2)))
  25. (define (query/not line start end)
  26. (error "this test should not call the query procedure"))
  27. (define (region=?/not line1 start1 end1
  28. line2 start2 end2)
  29. (error "this test should not call the region=? procedure"))
  30. ;; § Literals
  31. (test-equal "literal"
  32. "text"
  33. (expand->string query/not region=?/not "text"
  34. (list (make-literal-position 0 4))))
  35. (test-equal "part of literal (1)"
  36. "text"
  37. (expand->string query/not region=?/not "some text"
  38. (list (make-literal-position 5 9))))
  39. (test-equal "part of literal (2)"
  40. "some"
  41. (expand->string query/not region=?/not "some text"
  42. (list (make-literal-position 0 4))))
  43. (test-equal "quotes are not removed"
  44. "'text'"
  45. (expand->string query/not region=?/not "'text'"
  46. (list (make-literal-position 0 6))))
  47. (test-equal "zero literals"
  48. ""
  49. (expand->string query/not region=?/not 'anything '()))
  50. (test-equal "two overlapping literals"
  51. "spoon"
  52. (expand->string query/not region=?/not "spon"
  53. (list (make-literal-position 0 3)
  54. (make-literal-position 2 4))))
  55. ;; § Variable references
  56. (define (alist->query alist)
  57. (lambda (line start end)
  58. (let ((entry (assoc (substring line start end) alist)))
  59. (if entry
  60. (apply values (cdr entry))
  61. (error "this variable was not meant to be encountered"
  62. line start end)))))
  63. (test-equal "variable reference ($)"
  64. "iable"
  65. (expand->string (alist->query `(("var" "variable"
  66. (,(make-literal-position 3 8)))))
  67. region=?/not "$var"
  68. (list (make-$-position 1 4))))
  69. (test-equal "variable reference (${})"
  70. "iable"
  71. (expand->string (alist->query `(("var" "variable"
  72. (,(make-literal-position 3 8)))))
  73. region=?/not "${var}"
  74. (list (#{make-${}-position}# 2 5))))
  75. (test-equal "variable reference (${:-})"
  76. "iable"
  77. (expand->string (alist->query `(("var" "variable"
  78. (,(make-literal-position 3 8)))))
  79. region=?/not "${var:-default}"
  80. (list (#{make-${:-}-position}# 2 5 7 14 '()))))
  81. ;; This is the expander, not the parser.
  82. (test-equal "expander does not care about delimiters ($)"
  83. "iable"
  84. (expand->string (alist->query `(("#@}!/" "variable"
  85. (,(make-literal-position 3 8)))))
  86. region=?/not "${pre}#@}!/${post}"
  87. (list (make-$-position 6 11))))
  88. (test-equal "expander does not care about delimiters (${})"
  89. "iable"
  90. (expand->string (alist->query `(("#@}!/" "variable"
  91. (,(make-literal-position 3 8)))))
  92. region=?/not "${pre}#@}!/${post}"
  93. (list (#{make-${}-position}# 6 11))))
  94. (test-equal "expander does not care about delimiters (${:-})"
  95. "iable"
  96. (expand->string (alist->query `(("#@}!/" "variable"
  97. (,(make-literal-position 3 8)))))
  98. region=?/not "${pre}#@}!/${post}"
  99. (list (#{make-${:-}-position}# 6 11 13 15 '()))))
  100. (test-equal "undefined variable -> default (${:-})"
  101. "default"
  102. (expand->string (alist->query '(("var")))
  103. region=?/not "var default"
  104. (list (#{make-${:-}-position}# 0 3 5 12
  105. (list (make-literal-position 5 12))))))
  106. (test-equal "undefined variable -> default (${:-}, recursive)"
  107. "default"
  108. (expand->string (alist->query `(("var")
  109. ("var2" "default"
  110. (,(make-literal-position 0 7)))))
  111. region=?/not "var var2"
  112. (list (#{make-${:-}-position}# 0 3 5 9
  113. (list (make-$-position 5 9))))))
  114. ;; § Exceptions (undefined variable)
  115. ;;
  116. ;; Convert the exception into a S-expression
  117. ;; to be able to compare results with @code{equal?}.
  118. (define (expand->string/catch query region=? line expo-list)
  119. (with-exception-handler
  120. (lambda (e)
  121. (cond ((undefined-variable-error? e)
  122. `(undefined-variable-error
  123. (line ,(undefined-variable-line e))
  124. (start ,(undefined-variable-start e))
  125. (end ,(undefined-variable-end e))))
  126. ((expansion-loop-error? e)
  127. `(expansion-loop-error
  128. (visited . ,(expansion-loop-error-visited e))))
  129. (#t (error "what is this madness"))))
  130. (lambda ()
  131. (expand->string query region=? line expo-list))
  132. #:unwind? #t
  133. #:unwind-for-type &expansion-error))
  134. (test-equal "undefined variable -> exception ($)"
  135. `(undefined-variable-error
  136. (line "var")
  137. (start 0)
  138. (end 3))
  139. (expand->string/catch (alist->query '(("var")))
  140. region=?/not "var"
  141. (list (make-$-position 0 3))))
  142. (test-equal "undefined variable -> exception (${})"
  143. `(undefined-variable-error
  144. (line "var")
  145. (start 0)
  146. (end 3))
  147. (expand->string/catch (alist->query '(("var")))
  148. region=?/not "var"
  149. (list (#{make-${}-position}# 0 3))))
  150. ;; Like @code{region=?}, but #(line start end) must be in @var{acceptable}.
  151. (define (region=?/restricted . acceptable)
  152. (lambda (line1 start1 end1 line2 start2 end2)
  153. (unless (and (member (vector line1 start1 end1) acceptable)
  154. (member (vector line2 start2 end2) acceptable))
  155. (error "where did this variable reference come from?"
  156. (vector line1 start1 end1)
  157. (vector line2 start2 end2)))
  158. (region=? line1 start1 end1 line2 start2 end2)))
  159. (test-equal "undefined variable (nested) -> exception ($, correct line)"
  160. `(undefined-variable-error
  161. (line "var1 = $var2")
  162. (start 8)
  163. (end 12))
  164. (expand->string/catch (alist->query `(("var1" "var1 = $var2"
  165. (,(make-$-position 8 12)))
  166. ("var2")))
  167. (region=?/restricted
  168. #("$var1" 1 5)
  169. #("var1 = $var2" 8 12))
  170. "$var1"
  171. (list (make-$-position 1 5))))
  172. (test-equal "undefined variable (nested) -> exception (${}, correct line)"
  173. `(undefined-variable-error
  174. (line "var1 = ${var2}")
  175. (start 9)
  176. (end 13))
  177. (expand->string/catch (alist->query `(("var1" "var1 = ${var2}"
  178. (,(#{make-${}-position}# 9 13)))
  179. ("var2")))
  180. (region=?/restricted
  181. #("$var1" 1 5)
  182. #("var1 = ${var2}" 9 13))
  183. "$var1"
  184. (list (make-$-position 1 5))))
  185. ;; § Exceptions (loops)
  186. ;; Verify the line number information and verify the loopiness is
  187. ;; visible in the ‘visited’ list.
  188. (test-equal "loop ($, $)"
  189. `(expansion-loop-error
  190. (visited #("var = the $variable" 11 19)
  191. #("variable = $var" 12 15)
  192. #("$variable" 1 9)))
  193. (expand->string/catch (alist->query `(("variable"
  194. "variable = $var"
  195. (,(make-$-position 12 15)))
  196. ("var"
  197. "var = the $variable"
  198. (,(make-$-position 11 19)))))
  199. (region=?/restricted
  200. #("variable = $var" 12 15)
  201. #("var = the $variable" 11 19)
  202. #("$variable" 1 9))
  203. "$variable"
  204. (list (make-$-position 1 9))))
  205. (test-equal "loop (${}, ${})"
  206. `(expansion-loop-error
  207. (visited #("variable = ${var}" 13 16)
  208. #("var = the ${variable}" 12 20)
  209. #("$var" 1 4)))
  210. (expand->string/catch (alist->query `(("variable"
  211. "variable = ${var}"
  212. (,(#{make-${}-position}# 13 16)))
  213. ("var"
  214. "var = the ${variable}"
  215. (,(#{make-${}-position}# 12 20)))))
  216. (region=?/restricted
  217. #("variable = ${var}" 13 16)
  218. #("var = the ${variable}" 12 20)
  219. #("$var" 1 4))
  220. "$var"
  221. (list (#{make-$-position}# 1 4))))
  222. (test-equal "loop (${:-}, ${:-})"
  223. `(expansion-loop-error
  224. (visited #("variable = ${var:-}" 13 16)
  225. #("var = the ${variable:-}" 12 20)
  226. #("$var" 1 4)))
  227. (expand->string/catch
  228. (alist->query `(("variable"
  229. "variable = ${var:-}"
  230. (,(#{make-${:-}-position}# 13 16 18 18 '())))
  231. ("var"
  232. "var = the ${variable:-}"
  233. (,(#{make-${:-}-position}# 12 20 22 22 '())))))
  234. (region=?/restricted
  235. #("variable = ${var:-}" 13 16)
  236. #("var = the ${variable:-}" 12 20)
  237. #("$var" 1 4))
  238. "$var"
  239. (list (make-$-position 1 4))))
  240. (test-equal "${:-} with default --> no visited entry"
  241. `(expansion-loop-error
  242. (visited #("var = $var" 7 10)
  243. #("${does-not-exist:-$var}" 19 22)))
  244. (expand->string/catch
  245. (alist->query `(("var" "var = $var"
  246. (,(make-$-position 7 10)))
  247. ("does-not-exist")))
  248. (region=?/restricted
  249. #("var = $var" 7 10)
  250. #("${does-not-exist:-$var}" 19 22))
  251. "${does-not-exist:-$var}"
  252. (list (#{make-${:-}-position}# 2 16 18 22
  253. (list (make-$-position 19 22))))))
  254. ;; This should _not_ lead to an &expansion-loop-error.
  255. (test-equal "variable expanded multiple times"
  256. "example example"
  257. (expand->string/catch
  258. (alist->query `(("var" "example"
  259. (,(make-literal-position 0 7)))))
  260. region=?/not
  261. "var "
  262. (list (make-$-position 0 3)
  263. (make-literal-position 3 4)
  264. (make-$-position 0 3))))