parameters-draft.scm 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086
  1. ;;; GNU Guix --- Functional package management for GNU
  2. ;;; Copyright © 2020 Ludovic Courtès <ludo@gnu.org>
  3. ;;; Copyright © 2023 Sarthak Shah <shahsarthakw@gmail.com>
  4. ;;;
  5. ;;; This file is part of GNU Guix.
  6. ;;;
  7. ;;; GNU Guix is free software; you can redistribute it and/or modify it
  8. ;;; under the terms of the GNU General Public License as published by
  9. ;;; the Free Software Foundation; either version 3 of the License, or (at
  10. ;;; your option) any later version.
  11. ;;;
  12. ;;; GNU Guix is distributed in the hope that it will be useful, but
  13. ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
  14. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. ;;; GNU General Public License for more details.
  16. ;;;
  17. ;;; You should have received a copy of the GNU General Public License
  18. ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
  19. (define-module (guix parameters)
  20. #:use-module (guix packages)
  21. #:use-module (guix records)
  22. #:use-module (guix transformations)
  23. #:use-module (guix profiles)
  24. #:use-module (guix diagnostics)
  25. #:use-module (guix i18n)
  26. #:use-module (srfi srfi-1)
  27. #:use-module (srfi srfi-13)
  28. #:use-module (srfi srfi-26)
  29. #:use-module (srfi srfi-34)
  30. #:use-module (srfi srfi-35)
  31. #:use-module (ice-9 match)
  32. #:use-module (ice-9 hash-table)
  33. #:export (package-parameter
  34. parameter-type
  35. parameter-spec
  36. boolean
  37. parameter/morphism
  38. parameter/morphism-match
  39. parameter-spec-property
  40. package-parameter-spec
  41. parameter-spec/all-parameters
  42. parameter-spec/base-parameter-alist
  43. parameter-spec/override-alist
  44. parameter-spec/validate-parameter-alist
  45. parameter-spec/resolve-parameter-alist
  46. %global-parameters
  47. define-global-parameter
  48. package-with-parameters
  49. parameter-spec/parameter-alist
  50. parameter/if
  51. parameter/if-all
  52. parameter/match-any
  53. parameter/match-all
  54. parameter/match-case-any
  55. parameter/match
  56. parameter/match-case
  57. parameter/modify-inputs))
  58. ;;; Commentary:
  59. ;;;
  60. ;;; This module provides a way to express high-level "package parameters",
  61. ;;; which allow users to customize how packages are built. Parameters are an
  62. ;;; interface that package developers define, where each parameter has a name
  63. ;;; and type. The user interface then converts parameter values from string
  64. ;;; to Scheme values and records them in the package properties.
  65. ;;;
  66. ;;; Package parameters are discoverable; their description is
  67. ;;; internationalized. The possible values of a parameter can be enumerated,
  68. ;;; and thus the Cartesian product of all possible parameter values for a
  69. ;;; package can be enumerated as well.
  70. ;;;
  71. ;;; Code:
  72. (define (give-me-a-symbol ex)
  73. (cond ((symbol? ex) ex)
  74. ((string? ex) (string->symbol ex))
  75. (else (throw 'bad! ex))))
  76. (define-record-type* <parameter-type> parameter-type
  77. make-parameter-type
  78. parameter-type?
  79. this-parameter-type
  80. (name parameter-type-name
  81. (sanitize give-me-a-symbol))
  82. (universe parameter-type-universe)
  83. (negation parameter-type-negation
  84. (default (car (parameter-type-universe this-parameter-type)))
  85. (thunked))
  86. (description parameter-type-description
  87. (default "")))
  88. (define boolean
  89. (parameter-type
  90. (name 'boolean)
  91. (universe '(off on))
  92. (description "Boolean Parameter Type")))
  93. ;; Package parameter interface.
  94. (define-record-type* <package-parameter> package-parameter
  95. make-package-parameter
  96. package-parameter?
  97. (name package-parameter-name
  98. (sanitize sanitize-package-parameter-name))
  99. (type package-parameter-type
  100. (default boolean))
  101. (morphisms package-parameter-morphisms
  102. (default '())
  103. (sanitize sanitize-build-system-morphisms))
  104. (dependencies package-parameter-dependencies ; 7/14
  105. (default '())
  106. (thunked))
  107. ;; XXX: universal parameters don't need to be in the pspec
  108. (universal? package-parameter-universal?
  109. (default #f))
  110. (description package-parameter-description (default "")))
  111. ;; SANITIZERS
  112. (define %global-parameters
  113. (alist->hash-table '()))
  114. (define (sanitize-package-parameter-name x)
  115. (cond ((string? x)
  116. (if (string= (string-take-right x 1) "!")
  117. (throw "Negation in parameter name!" x) ; we cannot have negation in parameter name!
  118. (string->symbol x)))
  119. ((symbol? x)
  120. (if (string= (string-take-right (symbol->string x) 1) "!")
  121. (throw "Negation in parameter name!" x) ; we cannot have negation in parameter name!
  122. x))
  123. (else (throw 'bad! x))))
  124. ;; (sanitize-package-parameter-name 'x!)
  125. ;; (define (sanitize-build-system-transforms ls)
  126. (define (sanitize-build-system-morphisms ls)
  127. ;; ((a . t1 t2 ...) ((b c) t3 t4 ...))
  128. (cond ((list? ls) ls)
  129. (else (throw 'bad! ls))))
  130. (define-syntax lots-of-cons->alist
  131. (syntax-rules ()
  132. ((_ (a . b))
  133. (list (cons 'a b)))
  134. ((_ (a . b) rest ...)
  135. (cons (cons 'a b)
  136. (lots-of-cons->alist rest ...)))))
  137. ;; (define-syntax build-system/transform
  138. ;; (syntax-rules (-> _)
  139. ;; ((build-system/transform (x ...) -> y ...)
  140. ;; (map (lambda (g)
  141. ;; (cons g (lots-of-cons->alist y ...)))
  142. ;; (list x ...)))
  143. ;; ((build-system/transform _ -> y ...) ; for local parameter definitions
  144. ;; (cons 'any ; matches any build system
  145. ;; (lots-of-cons->alist y ...)))
  146. ;; ((build-system/transform x -> y ...)
  147. ;; (cons x (lots-of-cons->alist y ...)))))
  148. ;; Parameter Morphisms:
  149. ;; (parameter/morphism
  150. ;; (sym + build-system -> morphism-list))
  151. ;; alist->hash-table of the format
  152. ;; ((build-system . ((sym . ((transforms (a . b) ...) ...)) ...)) ...)
  153. (define (return-list lst)
  154. (or (and (list? lst) lst)
  155. (list lst)))
  156. (define* (merge-same-car lst #:optional (carry '()))
  157. (define (assq-append alist key cont)
  158. (if (eqv? (caar alist) key)
  159. (cons (cons key (append (cdar alist) cont))
  160. (cdr alist))
  161. (cons (car alist) (assq-append (cdr alist) key cont))))
  162. (cond ((null? lst) carry)
  163. ((null? (filter (lambda (y) (eqv? (caar lst)
  164. (car y)))
  165. carry))
  166. (merge-same-car (cdr lst) (cons (car lst) carry)))
  167. (else
  168. (merge-same-car (cdr lst)
  169. (assq-append carry (caar lst) (cdar lst))))))
  170. (define-syntax parameter/morphism
  171. (syntax-rules (-> + _)
  172. [(%) '()]
  173. [(% _ -> morphisms ...)
  174. (cons 'any (cons 'any (parameter/parse-morphisms '(morphisms ...))))]
  175. [(% _ + _ -> morphisms ...)
  176. (cons 'any (cons 'any (parameter/parse-morphisms '(morphisms ...))))]
  177. [(% sym + _ -> morphisms ...)
  178. (let ((parsed-morphisms (parameter/parse-morphisms '(morphisms ...))))
  179. (cons 'any (map (lambda (g)
  180. (cons g parsed-morphisms))
  181. (return-list 'sym))))]
  182. [(% _ + b-system -> morphisms ...)
  183. (let ((parsed-morphisms (parameter/parse-morphisms '(morphisms ...))))
  184. (map (lambda (g) (cons g (cons 'any parsed-morphisms)))
  185. (return-list 'b-system)))]
  186. [(% sym + b-system -> morphisms ...)
  187. (let ((parsed-morphisms (parameter/parse-morphisms '(morphisms ...))))
  188. (map (lambda (g) (cons g (map (lambda (h) (cons h parsed-morphisms))
  189. (return-list 'sym))))
  190. (return-list 'b-system)))]
  191. [(% sym -> morphisms ...)
  192. (let ((parsed-morphisms (parameter/parse-morphisms '(morphisms ...))))
  193. (cons 'any (map (lambda (g)
  194. (cons g parsed-morphisms))
  195. (return-list 'sym))))]))
  196. ;; (parameter/morphism (! _ 3) + (a b c) -> #:transform m1 #:rewrite m2 m3 #:modify c3)
  197. ;; look into more efficient ways to store this data
  198. ;; if we want to use break, (use-modules (srfi srfi-1) (ice-9 receive))
  199. ;;
  200. ;; (define (list-till-kw lst)
  201. ;; (receive (a b)
  202. ;; (break keyword? lst)
  203. ;; (cons a b)))
  204. ;;
  205. ;; (list-till-kw '(a b #:c d e))
  206. (define* (parameter/parse-morphisms kw-lst)
  207. (define* (list-till-kw lst #:optional (carry '()))
  208. (cond ((null? lst) (cons (reverse carry) '()))
  209. ((and (not (null? (cdr lst)))
  210. (keyword? (car lst)))
  211. (cons (reverse carry) lst))
  212. (else (list-till-kw (cdr lst) (cons (car lst) carry)))))
  213. (define* (break-keywords lst)
  214. (cond ((null? lst) '())
  215. ((null? (cdr lst)) '())
  216. ((keyword? (car lst))
  217. (let ((next-lst (list-till-kw (cdr lst))))
  218. (cons (cons (keyword->symbol (car lst))
  219. (car next-lst))
  220. (break-keywords (cdr next-lst)))))
  221. (else (throw 'bad! lst))))
  222. (merge-same-car (break-keywords kw-lst)))
  223. ;; (define-syntax build-system/transform-match
  224. ;; (syntax-rules ()
  225. ;; ((_ (x ...))
  226. ;; (list
  227. ;; (build-system/transform x ...)))
  228. ;; ((_ (x ...) rest ...)
  229. ;; (cons
  230. ;; (build-system/transform x ...)
  231. ;; (build-system/transform-match rest ...)))))
  232. ;; (parameter/parse-morphisms '(#:transform a (b c) #:rewrite d #:transform h))
  233. ;; The lock here is used to signal when merge-same-car is to be used
  234. ;; having a :lock means merge-same-car has been used further up the tree
  235. ;; note that :lock is not a keyword but a symbol
  236. (define-syntax parameter/morphism-match
  237. (syntax-rules (:lock _ -> +)
  238. ((% :lock (x ...))
  239. (list
  240. (parameter/morphism x ...)))
  241. ((% :lock (x ...) rest ...)
  242. (cons
  243. (parameter/morphism x ...)
  244. (parameter/morphism-match :lock rest ...)))
  245. ((% rest ...)
  246. (merge-same-car
  247. (parameter/morphism-match :lock rest ...)))))
  248. ;; (use-modules (ice-9 pretty-print))
  249. ;; (pretty-print
  250. ;; (parameter/morphism-match
  251. ;; ((a b c) + (d e f) -> #:transform (x _) y #:rewrite z)
  252. ;; ((a b c) + _ -> #:transform u)))
  253. (define (local-sanitizer ls)
  254. (if (list? ls)
  255. (map (lambda (val)
  256. (cond ((package-parameter? val) val)
  257. ((symbol? val) (package-parameter (name val)))
  258. ((string? val) (package-parameter (name (string->symbol val))))
  259. (else (throw 'bad! val))))
  260. ls)
  261. (throw 'bad! ls)))
  262. ;; (use-modules (ice-9 match))
  263. ;; morphism rewrite:
  264. ;; ((a b) (c d))..
  265. ;; (((a sym sym2) m) (b m2) ((c sym3)) (d))
  266. (define (morphism-sanitizer lv) ; ((a^ m) ((b sym) m2) c ((d sym1 sym2 ...) m3) ...)
  267. (define (default-morphism? psym) ; check if parameter is given as parameter^
  268. ;; TAKE SPECIAL CARE:
  269. ;; As we are treating ^ as a special character,
  270. ;; it will trim it away from the parameter symbol.
  271. ;; DO NOT USE IT AT THE END OF THE PARAMETER!
  272. (or (and (string=? (string-take-right (symbol->string psym) 1) "^")
  273. (string->symbol (string-drop-right (symbol->string psym) 1)))
  274. (and (string=? (string-take-right (symbol->string psym) 2) "^!")
  275. (string->symbol (string-append (string-drop-right (symbol->string psym) 2)
  276. "!")))))
  277. (define (default-morphism-list psym)
  278. (or (find (lambda (g) (eqv? psym
  279. (package-parameter-name g)))
  280. lv)
  281. (hash-ref %global-parameters psym)
  282. (throw 'bad! psym)))
  283. (lambda (ls)
  284. (map
  285. (match-lambda
  286. [psym
  287. ;; default morphism for psym
  288. (list
  289. (cons psym
  290. (default-morphism-list psym)))]
  291. [((psym vals ...) m)
  292. ;; assign morphism to psym at vals
  293. (let ((morphisms (if (keyword? (car m))
  294. (parameter/parse-morphisms m)
  295. m)))
  296. (map (lambda (x) (cons x morphisms))
  297. (return-list vals)))]
  298. [((? default-morphism? psym) sym)
  299. ;; get default morphism at sym
  300. (let ((csym (default-morphism? psym)))
  301. (list
  302. (cons (cons csym sym)
  303. (default-morphism-list csym))))]
  304. [(psym m)
  305. ;; morphism for psym
  306. (let ((morphisms (if (keyword? (car m))
  307. (parameter/parse-morphisms m)
  308. m)))
  309. (list
  310. (cons psym morphisms)))]
  311. [x
  312. (throw 'bad! x)])
  313. ls)))
  314. (define-syntax parameter/dependency
  315. (lambda (defn)
  316. (syntax-case defn (->)
  317. [(% p-lst -> rest ...)
  318. (syntax
  319. (let ((morphism-list (return-list '(rest ...))))
  320. (map
  321. (lambda (x)
  322. (cons x
  323. (parameter/parse-morphisms (if (keyword? (car morphism-list))
  324. morphism-list
  325. (cons #:parameters morphism-list)))))
  326. (return-list 'p-lst))))])))
  327. ;; (parameter/dependency (a b) -> #:parameters a b #:packages d)
  328. ;; (parameter/dependency (a (b yyy)) -> m n o)
  329. (define-syntax parameter/dependency-match
  330. (syntax-rules (:lock _ ->)
  331. ((% :lock (x ...))
  332. (parameter/dependency x ...))
  333. ((% :lock (x ...) rest ...)
  334. (append
  335. (parameter/dependency x ...)
  336. (parameter/dependency-match :lock rest ...)))
  337. ((% rest ...)
  338. (merge-same-car
  339. (parameter/dependency-match :lock rest ...)))))
  340. ;; (parameter/dependency-match
  341. ;; (a -> k)
  342. ;; ((a b) -> #:parameters a b #:packages d)
  343. ;; ((a (b yyy)) -> m n o))
  344. ;; thunked -> we can do stuff like (parameter-spec-optional-parameters ps) to get the optional parameters
  345. (define-record-type* <parameter-spec> parameter-spec
  346. make-parameter-spec
  347. parameter-spec?
  348. this-parameter-spec
  349. ;; local-parameters: parameters specific to the package
  350. (local parameter-spec/local
  351. ;; keeping it as an alist as it will be useful to retrieve them for the UI
  352. (default '())
  353. (sanitize local-sanitizer) ; morphism-update: all good!
  354. (thunked))
  355. ;; 6/15: Pjotr recommended using a global hash table instead.
  356. ;; See: (define-global-parameter), %global-parameters
  357. ;; Lines commented out due to this will have an 'x615' next to them
  358. ;; (global parameter-spec/global ;; global parameters used must be declared
  359. ;; (default '())
  360. ;; (sanitizer (lambda (ls)
  361. ;; (map (lambda (val) ; they must be package parameters
  362. ;; (if (package-parameter? val)
  363. ;; val
  364. ;; (throw 'bad! val)))
  365. ;; ls)))
  366. ;; (thunked))
  367. (defaults parameter-spec/defaults ; '(a b c d ...) -> '(a (b sym) (c sym2) e! ...)
  368. (default '())
  369. (thunked))
  370. (required parameter-spec/required
  371. (default '())
  372. (thunked))
  373. (optional parameter-spec/optional
  374. (default '()) ; 6/16: causing problems with parameter-spec/all-parameters
  375. ;; 6/13: removed the sanitizer as merging local and optional
  376. ;; should be handled by the parser instead.
  377. (thunked))
  378. ;; XXX: automatically create (x x!) if both are defined
  379. ;; 6/12: this will be handled by the parser
  380. (one-of parameter-spec/one-of
  381. (default '())
  382. (thunked))
  383. ;; add dependencies
  384. ;; (dependencies (parameter/dependencies
  385. ;; (a b -> d e f)
  386. ;; (c -> g h)))
  387. ;; (dependencies parameter-spec/dependencies
  388. ;; (default '())
  389. ;; (thunked))
  390. ;; 7/14 : Moved to the package-parameter record
  391. (canonical parameter-spec/canonical-combinations
  392. (default parameter-spec/defaults)
  393. (thunked))
  394. ;; (use-transforms parameter-spec/use-transforms ;; only use transforms for these
  395. ;; (default '())
  396. ;; (sanitize (transform-sanitizer (parameter-spec/local this-parameter-spec)))
  397. ;; (thunked))
  398. (use-morphisms parameter-spec/use-morphisms ;; only use morphisms for these
  399. (default '())
  400. (sanitize (morphism-sanitizer (parameter-spec/local this-parameter-spec)))
  401. (thunked))
  402. (parameter-alist parameter-spec/parameter-alist ;; this is ultimately what will be transformed by --with-parameters
  403. ;; '((a . #t) (b . #f) ...)
  404. (default (parameter-spec/base-parameter-alist this-parameter-spec)) ; if this doesn't work some tricks might be needed
  405. (thunked)))
  406. ;; g23: Most parameters should be boolean
  407. ;; Might make sense to add a recursive type
  408. ;; (define boolean
  409. ;; ;; The Boolean parameter type.
  410. ;; (parameter-type (name 'boolean)
  411. ;; (universe '(#t #f))
  412. ;; (value->string
  413. ;; (match-lambda
  414. ;; (#f "off")
  415. ;; (#t "on")))
  416. ;; (string->value
  417. ;; (lambda (str)
  418. ;; (cond ((string-ci=? str "on")
  419. ;; #t)
  420. ;; ((string-ci=? str "off")
  421. ;; #f)
  422. ;; (else
  423. ;; (raise (condition
  424. ;; (&message (message "wrong value"))))))))))
  425. (define-syntax parameter-spec-property
  426. (syntax-rules ()
  427. [(parameter-spec-property body ...)
  428. (cons 'parameter-spec
  429. (parameter-spec body ...))]))
  430. (define-syntax package-with-parameters
  431. (syntax-rules ()
  432. [(package-with-parameters body ...)
  433. (let ((the-package (package body ...)))
  434. ((options->transformation
  435. (apply append
  436. (let ((the-build-system (package-build-system the-package)))
  437. (map (lambda (x)
  438. (transform-for-build-system
  439. (assq-ref (parameter-spec/use-transforms
  440. (package-parameter-spec the-package))
  441. (car x))
  442. the-build-system))
  443. (filter (lambda (x) (eqv? #t (cdr x)))
  444. (parameter-spec/parameter-alist
  445. (package-parameter-spec the-package)))))))
  446. the-package))]))
  447. (define (package-parameter-spec package)
  448. (or (assq-ref (package-properties package) 'parameter-spec)
  449. '()))
  450. ;;; PROCESSING PIPELINE
  451. ;; % HELPER FUNCTIONS %
  452. (define (return-list lst)
  453. (or (and (list? lst) lst)
  454. (list lst)))
  455. (define (append-everything . things)
  456. (apply append
  457. (map return-list things)))
  458. (define (get-parameter-sym psym)
  459. (match psym
  460. [(a . b) a]
  461. [a a]))
  462. ;; Convention:
  463. ;; Works on Parameters? -> parameter-spec/fun
  464. ;; Works on Parameter-Spec? -> parameter-spec/fun
  465. (define (parameter-spec/get-parameter pspec psym)
  466. (or (find (lambda (x)
  467. (eqv? psym
  468. (package-parameter-name x)))
  469. (parameter-spec/local pspec))
  470. (hash-ref %global-parameters psym)
  471. (throw "Parameter not found: " psym)))
  472. (define (paramerer-spec/negation-supported? pspec x)
  473. (let ((negv
  474. (parameter-type-negation (parameter/paramerer-type (parameter-spec/get-parameter pspec x)))))
  475. (if negv
  476. negv
  477. %match-all)))
  478. ;; (define (parameter-spec/get-dependencies pspec lst)
  479. ;; (apply
  480. ;; append
  481. ;; (map
  482. ;; (lambda (x)
  483. ;; (cons x
  484. ;; (assq-ref
  485. ;; (assq-ref (parameter-spec-dependencies pspec) x)
  486. ;; 'parameters)))
  487. ;; lst)))
  488. (define (parameter-spec/get-dependencies psym)
  489. (let ([p (parameter-spec/get-parameter pspec psym)])
  490. (return-list
  491. (assq-ref (package-parameter-dependencies p)
  492. 'parameters))))
  493. ;; 1. Fetching
  494. (define (parameter-spec/base-parameter-alist pspec) ; returns base case
  495. ;; '((a . psym) (b . #f) ...)
  496. (let* ((v1 (parameter/process-list ; returns funneled list
  497. (append-everything
  498. (parameter-spec/defaults pspec)
  499. (parameter-spec/required pspec))))
  500. (v2 (parameter/process-list
  501. (append-everything
  502. (apply append
  503. (map (cut parameter-spec/get-dependencies pspec <>)
  504. (return-list v1)))
  505. v1))))
  506. ;; funnel will signal duplication err
  507. v2))
  508. ;; 2. Processing
  509. (define (parameter/process-list lst)
  510. (define (unexclaim p) ; step 1
  511. (define (negated-sym? p)
  512. (string=? (string-take-right (symbol->string (car (return-list p))) 1) "!"))
  513. (match p
  514. ;; Signal error if p! is in a cell
  515. [((? negated-sym? a) . b) (throw 'negation-in-cell! p)]
  516. [(a . b) p] ; normal cells are OK
  517. [(? negated-sym? a) (cons (string->symbol
  518. (string-drop-right (symbol->string a) 1))
  519. '%match-none)]
  520. [_ p]))
  521. (define (cellulize p) ; step 2 + 3
  522. (match p
  523. [(a b) (cons a b)]
  524. [(a . b) p]
  525. [a (cons a '%match-any)]))
  526. (define (desugarize p) ; step 4
  527. (match p
  528. [(a . '_) (cons a '%match-any)]
  529. [(a . '!) (cons a '%match-none)]
  530. [_ p]))
  531. (define (funnel plst) ; step 5
  532. ;; first we will get a list indexed by keys
  533. (define (group-val carry lst)
  534. (if (null-list? lst)
  535. carry
  536. (let ((v (assq-ref carry (caar lst))))
  537. (group-val
  538. (assq-set! carry (caar lst)
  539. (if v
  540. (cons (cdar lst) v)
  541. ;; We want a list in cdr
  542. (cons (cdar lst) '())))
  543. (cdr lst)))))
  544. (define (figure-out p)
  545. (or (and (< (length p) 3)
  546. (or (and (eq? (length p) 1) (car p))
  547. (and (member '%match-any p)
  548. (car (delq '%match-any p)))))
  549. (throw 'too-many-elements! p)))
  550. (map (lambda (x) (cons (car x)
  551. (figure-out
  552. (delete-duplicates (cdr x)))))
  553. (group-val '() plst)))
  554. (funnel (map (lambda (x)
  555. (desugarize (cellulize (unexclaim x))))
  556. lst)))
  557. ;; 3. Overriding
  558. ;; This will get us all the parameters
  559. (define (parameter-spec/all-parameters pspec) ; for the UI
  560. ;; '(sym-a sym-b ...)
  561. (delete-duplicates
  562. (map get-parameter-sym ; we do not care about the values
  563. (append-everything ; works same as before
  564. (map package-parameter-name
  565. (parameter-spec/local pspec))
  566. (parameter-spec/defaults pspec)
  567. (parameter-spec/required pspec)
  568. ;; We are NOT pulling dependencies at this phase
  569. ;; They will not be influenced by the user parameter alist
  570. (apply append (parameter-spec/one-of pspec))
  571. (parameter-spec/optional pspec)))))
  572. ;; Now we compare it against the PLIST
  573. (define (parameter-spec/override-plist pspec plist)
  574. (let* ((all-p (parameter-spec/all-parameters pspec))
  575. (filtered-plist (filter (lambda (x) (member (car x) all-p))
  576. (parameter/process-list plist)))
  577. (filtered-car (map car filtered-plist))
  578. (remaining-p (filter (lambda (x) (not (member x filtered-car)))
  579. all-p)))
  580. (append-everything filtered-plist
  581. (map (lambda (x) (if (paramerer-spec/negation-supported? pspec x)
  582. (cons x %match-none)
  583. (cons x %match-all)))
  584. remaining-p))))
  585. ;; 4. Funneling
  586. (define (parameter-spec/override-multi-match pspec plst)
  587. (map
  588. (match-lambda
  589. [(a . '%match-any)
  590. (cons a
  591. (cadr (parameter-type-universe (package-parameter (parameter-spec/get-parameter pspec a)))))]
  592. [(a . '%match-none)
  593. (cons a
  594. (parameter-type-negation (package-parameter (parameter-spec/get-parameter pspec a))))]
  595. [cell cell])
  596. plst))
  597. ;; 5. Validation
  598. (define (parameter-spec/validate pspec plst)
  599. ;; We want all tests to run
  600. (let ((works? #t))
  601. (define (m+eqv? new-val orig-val)
  602. (or (eqv? orig-val '%match-any)
  603. (eqv? orig-val new-val)))
  604. (define (throw+f sym vals)
  605. (display "Error: ")
  606. (display sym)
  607. (display " with values ")
  608. (display vals)
  609. (newline)
  610. (set! works? #f))
  611. ;; first we check duplication
  612. ;; a bit unnecessary
  613. (define (validate/duplication)
  614. (let ((symlst (map car plst)))
  615. (unless (eqv? symlst (delete-duplicates symlst))
  616. (throw+f 'duplicates plst))))
  617. ;; logic checking checks for:
  618. ;; - presence of required parameters
  619. ;; - 'one-of' conflicts
  620. ;; - dependency satisfaction
  621. (define (validate/logic)
  622. (map ; required
  623. (lambda (x)
  624. (unless
  625. (m+eqv? (assq-ref plst (car x))
  626. (cdr x))
  627. (throw+f 'unsatisfied-requirement x)))
  628. (parameter/process-list
  629. (parameter-spec-required pspec)))
  630. (map ; one-of
  631. (lambda (ls)
  632. (unless
  633. (= 1
  634. (count
  635. (lambda (x)
  636. (m+eqv? (assq-ref plst (car x))
  637. (cdr x)))
  638. (parameter/process-list ls)))
  639. (throw+f 'one-of-unsatisfied ls)))
  640. (parameter-spec-one-of pspec))
  641. ;; XXX: Needs a per-parameter rewrite
  642. ;; (map ; dependencies
  643. ;; (lambda (x)
  644. ;; (let ([deplst (parameter/process-list
  645. ;; (assq-ref
  646. ;; (assq-ref (parameter-spec-dependencies pspec) x)
  647. ;; 'parameters))])
  648. ;; (map
  649. ;; (lambda (y)
  650. ;; (unless (m+eqv? (assq-ref plst (car y))
  651. ;; (cdr y))
  652. ;; (throw+f 'dependency-unsatisfied y)))
  653. ;; deplst)))
  654. ;; plst)
  655. )
  656. (validate/duplication)
  657. (validate/logic)
  658. works?))
  659. (define (parameter-spec/resolve-list pspec plst)
  660. (let ([proper-plst (parameter-spec/override-multi-match
  661. pspec
  662. (parameter-spec/override-plist
  663. pspec
  664. (parameter/process-list plst)))])
  665. (if (parameter-spec/validate pspec proper-plst)
  666. proper-plst
  667. (parameter-spec/base-parameter-alist pspec))))
  668. ;; %global-parameters: hash table containing global parameters ref'd by syms
  669. (define-syntax define-global-parameter
  670. (syntax-rules ()
  671. [(define-global-parameter (parameter-definition ...))
  672. (let ((gp-val (parameter-definition ...)))
  673. (hash-set! %global-parameters
  674. (package-parameter-name gp-val)
  675. gp-val))]))
  676. ;; (define-global-parameter (package-parameter
  677. ;; (name "tests!")
  678. ;; (description "no tests")))
  679. ;; Works!
  680. (define-syntax parameter/inside?
  681. (syntax-rules ()
  682. [(% p plst)
  683. (not
  684. (eqv? (or (assq-ref plst p)
  685. (error "Parameter not found!"))
  686. (parameter-type-negation
  687. (package-parameter-type
  688. (parameter-spec/get-parameter
  689. (package-parameter-spec this-package)
  690. p)))))]))
  691. (define-syntax parameter/if
  692. (syntax-rules ()
  693. [(parameter/if property exp)
  694. (let ((properties
  695. (parameter-spec/parameter-alist
  696. (package-parameter-spec this-package))))
  697. (if (member
  698. #t
  699. (map (cut parameter/inside? <> properties)
  700. (parameter/process-list (return-list property))))
  701. exp
  702. '()))]
  703. [(parameter/if property exp exp-else)
  704. (let ((properties
  705. (parameter-spec/parameter-alist
  706. (package-parameter-spec this-package))))
  707. (if (member
  708. #t
  709. (map (cut parameter/inside? <> properties)
  710. (parameter/process-list (return-list property))))
  711. exp
  712. exp-else))]))
  713. (define-syntax parameter/if-all
  714. (syntax-rules ()
  715. [(parameter/if-all property exp)
  716. (let ((properties (parameter-spec/parameter-alist (package-parameter-spec this-package))))
  717. (if (not (member
  718. #f
  719. (map (cut parameter/inside? <> properties)
  720. (parameter/process-list (return-list property)))))
  721. exp
  722. '()))]
  723. [(parameter/if-all property exp exp-else)
  724. (let ((properties (parameter-spec/parameter-alist (package-parameter-spec this-package))))
  725. (if (not (member
  726. #f
  727. (map (cut parameter/inside? <> properties)
  728. (parameter/process-list (return-list property)))))
  729. exp
  730. exp-else))]))
  731. ;; Test these macros without using packages:
  732. ;; (define (parameter-spec/parameter-alist _)
  733. ;; (list (cons 'a 1)
  734. ;; (cons 'b 2)
  735. ;; (cons 'c 3)))
  736. ;; (define (package-parameter-spec _) #t)
  737. ;; (define this-package '())
  738. ;; (parameter/if '(a (b 3))
  739. ;; "YES"
  740. ;; "NO")
  741. ;; (parameter/if-all '(a (b 3))
  742. ;; "NO"
  743. ;; "YES)
  744. ;; parameter/match-any:
  745. ;; (parameter/match-any
  746. ;; ((a b) e1 e2 ..)
  747. ;; ((c) d1 d2 ..)
  748. ;; (else c1 c2 ...))
  749. (define-syntax parameter/match-any
  750. (syntax-rules (_)
  751. [(%) '()]
  752. [(% (_ clauses ...)) (begin clauses ...)]
  753. [(% ((parameters ...)) rest ...) (parameter/match-any rest ...)]
  754. [(% ((parameters ...) clauses ...) rest ...)
  755. (let ((properties (parameter-spec/parameter-alist (package-parameter-spec this-package))))
  756. (begin
  757. (and (member #t (map (cut parameter/inside? <> properties)
  758. (list parameters ...)))
  759. (begin clauses ...))
  760. (parameter/match-any rest ...)))]
  761. [(% (parameter clauses ...) rest ...)
  762. (let ((properties (parameter-spec/parameter-alist (package-parameter-spec this-package))))
  763. (begin
  764. (and (parameter/inside? parameter properties)
  765. (begin clauses ...))
  766. (parameter/match-any rest ...)))]))
  767. ;; (let ((SOME_ALIST_FOR_THIS_EXAMPLE '()))
  768. ;; (parameter/match-any
  769. ;; (('a 'd)
  770. ;; (set! SOME_ALIST_FOR_THIS_EXAMPLE (append '(1) SOME_ALIST_FOR_THIS_EXAMPLE))
  771. ;; (set! SOME_ALIST_FOR_THIS_EXAMPLE (append '(2) SOME_ALIST_FOR_THIS_EXAMPLE)))
  772. ;; (('c))
  773. ;; (('e)
  774. ;; (set! SOME_ALIST_FOR_THIS_EXAMPLE (append '(3) SOME_ALIST_FOR_THIS_EXAMPLE)))
  775. ;; (all
  776. ;; (set! SOME_ALIST_FOR_THIS_EXAMPLE (append '(4) SOME_ALIST_FOR_THIS_EXAMPLE))))
  777. ;; SOME_ALIST_FOR_THIS_EXAMPLE)
  778. ;; The answer to this should be '(4 2 1)
  779. ;; note that all is essentially useless, one can simply put the expression in all
  780. ;; outside the macro and it will work the same
  781. (define-syntax parameter/match-all
  782. (syntax-rules (_)
  783. [(%) '()]
  784. [(% (_ clauses ...)) (begin clauses ...)]
  785. [(% ((parameters ...)) rest ...) (parameter/match-all rest ...)]
  786. [(% ((parameters ...) clauses ...) rest ...)
  787. (let ((properties (parameter-spec/parameter-alist (package-parameter-spec this-package))))
  788. (begin
  789. (and (not (member #f (map (cut parameter/inside? <> properties)
  790. (list parameters ...))))
  791. (begin clauses ...))
  792. (parameter/match-all rest ...)))]
  793. [(% (parameter clauses ...) rest ...)
  794. (let ((properties (parameter-spec/parameter-alist (package-parameter-spec this-package))))
  795. (begin
  796. (and (parameter/inside? parameter properties)
  797. (begin clauses ...))
  798. (parameter/match-all rest ...)))]))
  799. ;; (parameter/match-all
  800. ;; (('a 'b) (display "YES") (display "YES"))
  801. ;; (('c 'd) (display "NO"))
  802. ;; (all (display "ALL")))
  803. (define-syntax parameter/match-case-all
  804. (syntax-rules ()
  805. [(%) '()]
  806. [(% (_ clauses ...)) (begin clauses ...)]
  807. [(% ((parameters ...)) rest ...) (parameter/match-case-any rest ...)]
  808. [(% ((parameters ...) clauses ...) rest ...)
  809. (let ((properties (parameter-spec/parameter-alist (package-parameter-spec this-package))))
  810. (and (not (member #f (map (cut parameter/inside? <> properties)
  811. (list parameters ...))))
  812. (begin clauses ...)
  813. (parameter/match-case-any rest ...)))]
  814. [(% (parameter clauses ...) rest ...)
  815. (let ((properties (parameter-spec/parameter-alist (package-parameter-spec this-package))))
  816. (and (parameter/inside? parameter properties)
  817. (begin clauses ...)
  818. (parameter/match-case-any rest ...)))]))
  819. ;; should short-circuit at YESYES
  820. ;; (parameter/match-case
  821. ;; (('a 'b 'e) (display "YES") (display "YES"))
  822. ;; (('c 'd) (display "NO"))
  823. ;; (all (display "ALL")))
  824. ;; parameter/match:
  825. ;; combine all and any into one
  826. ;; (parameter/match
  827. ;; ((any a b) ...)
  828. ;; ((all a b c) ...)
  829. ;; (all ...))
  830. (define-syntax parameter/match
  831. (syntax-rules (_ all)
  832. [(%) '()]
  833. [(% (_ clauses ...) rest ...) (begin (begin clauses ...) (parameter/match rest ...))]
  834. [(% (parameters) rest ...) (parameter/match rest ...)]
  835. [(% ((all parameters ...) clauses ...) rest ...)
  836. (let ((properties (parameter-spec/parameter-alist (package-parameter-spec this-package))))
  837. (begin
  838. (and (not (member #f (map (cut parameter/inside? <> properties)
  839. (list parameters ...))))
  840. (begin clauses ...))
  841. (parameter/match rest ...)))]
  842. [(% ((parameters ...) clauses ...) rest ...)
  843. (let ((properties (parameter-spec/parameter-alist (package-parameter-spec this-package))))
  844. (begin
  845. (and (member #t (map (cut parameter/inside? <> properties)
  846. (list parameters ...)))
  847. (begin clauses ...))
  848. (parameter/match rest ...)))]
  849. [(% (parameter clauses ...) rest ...)
  850. (let ((properties (parameter-spec/parameter-alist (package-parameter-spec this-package))))
  851. (begin
  852. (and (parameter/inside? parameter properties)
  853. (begin clauses ...))
  854. (parameter/match rest ...)))]))
  855. ;; (parameter/match
  856. ;; ((all 'a 'b) (display "YES"))
  857. ;; (_ (display "YES"))
  858. ;; (('c 'e) (display "YES"))
  859. ;; ((all 'a 'o) (display "NO"))
  860. ;; (_ (display "ALL")))
  861. (define-syntax parameter/match-case
  862. (syntax-rules (all _)
  863. [(%) '()]
  864. [(% (_ clauses ...) rest ...) (begin clauses ...)]
  865. [(% (parameters) rest ...) (parameter/match-case rest ...)]
  866. [(% ((all parameters ...) clauses ...) rest ...)
  867. (let ((properties (parameter-spec/parameter-alist (package-parameter-spec this-package))))
  868. (if (not (member #f (map (cut parameter/inside? <> properties)
  869. (list parameters ...))))
  870. (begin clauses ...)
  871. (parameter/match-case rest ...)))]
  872. [(% ((parameters ...) clauses ...) rest ...)
  873. (let ((properties (parameter-spec/parameter-alist (package-parameter-spec this-package))))
  874. (if (member #t (map (cut parameter/inside? <> properties)
  875. (list parameters ...)))
  876. (begin clauses ...)
  877. (parameter/match-case rest ...)))]
  878. [(% (parameter clauses ...) rest ...)
  879. (let ((properties (parameter-spec/parameter-alist (package-parameter-spec this-package))))
  880. (if (parameter/inside? parameter properties)
  881. (begin clauses ...)
  882. (parameter/match-case rest ...)))]))
  883. ;; (parameter/match-case
  884. ;; ((all 'a 'f) (display "NO"))
  885. ;; ;; (all (display "YES"))
  886. ;; ;; ((any 'c 'e) (display "YES"))
  887. ;; ;; ((all 'a 'b) (display "YES"))
  888. ;; (all (display "ALL")))
  889. (define-syntax parameter/modifier-if
  890. (syntax-rules (_ all delete prepend append replace)
  891. [(% _ exp exp2)
  892. exp]
  893. [(% (all parameters ...) exp exp2)
  894. (let ((properties (parameter-spec/parameter-alist (package-parameter-spec this-package))))
  895. (if (member #t
  896. (map (cut parameter/inside? <> properties)
  897. (list parameters ...)))
  898. exp
  899. exp2))]
  900. ;; [(% (all parameter) exp exp2) ; unnecessary
  901. ;; (let ((properties (parameter-spec/parameter-alist (package-parameter-spec this-package))))
  902. ;; (if (parameter/inside? parameter properties)
  903. ;; exp
  904. ;; exp-else))]
  905. [(% parameter exp exp2)
  906. (let ((properties (parameter-spec/parameter-alist (package-parameter-spec this-package))))
  907. (if (member
  908. #t
  909. (map (cut parameter/inside? <> properties)
  910. (return-list parameter)))
  911. exp
  912. exp2))]))
  913. (define-syntax parameter/modify-inputs
  914. (syntax-rules (_ all delete prepend append replace)
  915. [(% inputs (parameter) clauses ...)
  916. (parameter/modify-inputs inputs clauses ...)]
  917. [(% inputs (parameter (delete name) rest ...) clauses ...)
  918. (parameter/modify-inputs
  919. (parameter/modifier-if
  920. parameter
  921. (alist-delete name inputs)
  922. inputs)
  923. (parameter rest ...)
  924. clauses ...)]
  925. [(% inputs (parameter (delete names ...) rest ...) clauses ...)
  926. (parameter/modify-inputs
  927. (parameter/modifier-if
  928. parameter
  929. (fold alist-delete inputs (list names ...))
  930. inputs)
  931. (parameter rest ...)
  932. clauses ...)]
  933. [(% inputs (parameter (prepend lst ...) rest ...) clauses ...)
  934. (parameter/modify-inputs
  935. (parameter/modifier-if
  936. parameter
  937. (append (map add-input-label (list lst ...)) inputs)
  938. inputs)
  939. (parameter rest ...)
  940. clauses ...)]
  941. [(% inputs (parameter (append lst ...) rest ...) clauses ...)
  942. (parameter/modify-inputs
  943. (parameter/modifier-if
  944. parameter
  945. (append inputs (map add-input-label (list lst ...)))
  946. inputs)
  947. (parameter rest ...)
  948. clauses ...)]
  949. [(% inputs (parameter (replace name replacement) rest ...) clauses ...)
  950. (parameter/modify-inputs
  951. (parameter/modifier-if
  952. parameter
  953. (replace-input name replacement inputs)
  954. inputs)
  955. (parameter rest ...)
  956. clauses ...)]
  957. [(% inputs)
  958. inputs]))
  959. ;; (parameter-type-negation
  960. ;; (parameter-type
  961. ;; (name "ok")
  962. ;; (universe '(not-ok ok))))
  963. (define-syntax parameter/type
  964. (syntax-rules (_)
  965. [(% _ rest ...)
  966. (parameter/type (string-append (or (package-parameter-name this-package-parameter)
  967. "%blank")
  968. "-type")
  969. rest ...)]
  970. [(% t-name t-universe)
  971. (parameter-type
  972. (name t-name)
  973. (universe t-universe))]
  974. [(% t-name t-universe t-negation)
  975. (parameter-type
  976. (name t-name)
  977. (universe t-universe)
  978. (negation t-negation))]
  979. [(% t-name t-universe t-negation t-description)
  980. (parameter-type
  981. (name t-name)
  982. (universe t-universe)
  983. (negation t-negation)
  984. (description t-description))]))
  985. ;; (parameter-type-negation (parameter/type _ '(1 2 3)))
  986. ;;
  987. ;; (define (package-parameter-name _) #f)
  988. ;; (define this-package-parameter #f)