diff options
Diffstat (limited to 'module/hnh')
-rw-r--r-- | module/hnh/module-introspection.scm | 22 | ||||
-rw-r--r-- | module/hnh/module-introspection/all-modules.scm | 55 | ||||
-rw-r--r-- | module/hnh/module-introspection/module-uses.scm | 116 | ||||
-rw-r--r-- | module/hnh/module-introspection/static-util.scm | 9 | ||||
-rw-r--r-- | module/hnh/test/testrunner.scm | 126 | ||||
-rw-r--r-- | module/hnh/test/util.scm | 57 | ||||
-rw-r--r-- | module/hnh/test/xmllint.scm | 27 | ||||
-rw-r--r-- | module/hnh/util.scm | 55 | ||||
-rw-r--r-- | module/hnh/util/env.scm | 13 | ||||
-rw-r--r-- | module/hnh/util/io.scm | 20 | ||||
-rw-r--r-- | module/hnh/util/path.scm | 35 | ||||
-rw-r--r-- | module/hnh/util/state-monad.scm | 120 | ||||
-rw-r--r-- | module/hnh/util/uuid.scm | 14 |
13 files changed, 651 insertions, 18 deletions
diff --git a/module/hnh/module-introspection.scm b/module/hnh/module-introspection.scm new file mode 100644 index 00000000..83e561f1 --- /dev/null +++ b/module/hnh/module-introspection.scm @@ -0,0 +1,22 @@ +(define-module (hnh module-introspection) + :use-module (srfi srfi-1) + :use-module (hnh util) + :export (unique-symbols + find-module-declaration + module-declaration? + )) + + +(define (unique-symbols tree) + (uniq + (sort* (filter symbol? (flatten tree)) + string<? symbol->string))) + +(define (module-declaration? form) + (cond ((null? form) #f) + ((not (pair? form)) #f) + (else (eq? 'define-module (car form))))) + +(define (find-module-declaration forms) + (and=> (find module-declaration? forms) + cadr)) diff --git a/module/hnh/module-introspection/all-modules.scm b/module/hnh/module-introspection/all-modules.scm new file mode 100644 index 00000000..1bf39e1e --- /dev/null +++ b/module/hnh/module-introspection/all-modules.scm @@ -0,0 +1,55 @@ +(define-module (hnh module-introspection all-modules) + :use-module (ice-9 regex) + :use-module (srfi srfi-1) + :use-module (ice-9 ftw) + :use-module (ice-9 match) + :use-module (hnh util path) + :use-module (hnh module-introspection) + :use-module ((hnh module-introspection static-util) :select (get-forms)) + :export (all-files-and-modules-under-directory + all-modules-under-directory + fs-find-base fs-find + module-file-mapping + )) + +(define (fs-find dir) + (define files '()) + (ftw dir (lambda args (set! files (cons args files)) #t)) + files) + +;; (define (fs-find proc dir) +;; (filter proc (fs-find-base dir))) + +(define (all-files-and-modules-under-directory dir) + (define re (make-regexp "\\.scm$")) + + (define files + (map car + (filter (match-lambda ((filename _ 'regular) + (and (regexp-exec re filename) + (not (file-hidden? filename)))) + (_ #f)) + (fs-find dir)))) + + (map (lambda (file) + (list file + (call-with-input-file file + (compose find-module-declaration get-forms)))) + files)) + +(define (all-modules-under-directory dir) + "Returns two values, all scm files in dir, and all top +level modules in those files" + + (define pairs (all-files-and-modules-under-directory dir)) + (values + (map car pairs) + (filter identity (map cadr pairs)))) + +;; Returns an association list from module names the modules +;; containing filename +(define (module-file-mapping dir) + (filter + car + (map (lambda (pair) (cons (cadr pair) (car pair))) + (all-files-and-modules-under-directory dir)))) diff --git a/module/hnh/module-introspection/module-uses.scm b/module/hnh/module-introspection/module-uses.scm new file mode 100644 index 00000000..3bed2a5e --- /dev/null +++ b/module/hnh/module-introspection/module-uses.scm @@ -0,0 +1,116 @@ +(define-module (hnh module-introspection module-uses) + :use-module (ice-9 match) + :use-module (hnh util) + :use-module ((srfi srfi-1) :select (concatenate)) + :use-module ((srfi srfi-88) :select (string->keyword)) + :use-module (rnrs records syntactic) + :export (module-uses*)) + +;;; Commentary: +;;; Static analyze version of guile's built in module-uses. +;;; Will give a less accurate result, but in turn doesn't +;;; require that the target module compiles. +;;; Code: + +(define-record-type (module make-module% module?) + (fields name select hide prefix renamer version)) + +(define* (make-module name key: + (select #f) + (hide '()) + (prefix #f) + (renamer #f) + (version #f)) + (make-module% name select hide prefix renamer version)) + +(define (module->list module) + (append + (list (module-name module)) + (awhen (module-select module) `(#:select ,it)) + (awhen (module-hide module) `(#:hide ,it)) + (awhen (module-prefix module) `(#:prefix ,it)) + (awhen (module-renamer module) `(#:renamer ,it)) + (awhen (module-version module) `(#:version ,it)))) + +;; Normalizes keywords (#:key) and pseudo keywords (:key) used by define-module syntax. +(define (normalize-keyword kw-or-symb) + (cond ((symbol? kw-or-symb) + (-> (symbol->string kw-or-symb) + (string-drop 1) + string->keyword)) + ((keyword? kw-or-symb) + kw-or-symb) + (else (error "Bad keyword like" kw-or-symb)))) + +;; Takes one argument as taken by @code{use-modules}, or following #:use-module +;; in @code{define-module}. +;; returns a list on the form +;; (module-name (key value) ...) +;; where module name is something like (srfi srfi-1) +(define (parse-interface-specification interface-specification) + (match interface-specification + ;; matches `((srfi srfi-1) :select (something)) + (((parts ...) args ...) + (apply make-module + `(,parts ,@(concatenate + (map (lambda (pair) + (cons (normalize-keyword (car pair)) + (cdr pair))) + (group args 2)))))) + ;; matches `(srfi srfi-1) + ((parts ...) + (make-module parts)) + (_ (error "Bad module declaration")))) + +;; Finds all define-module forms, and returns what they +;; pull in (including autoloads) +(define (module-declaration-uses forms) + (match forms + (('define-module module-name directives ...) + (let loop ((directives directives)) + (cond ((null? directives) '()) + ((memv (car directives) '(#:use-module #{:use-module}#)) + (cons (parse-interface-specification (cadr directives)) + (loop (cddr directives)))) + ((memv (car directives) '(#:autoload #{:autoload}#)) + (cons (cadr directives) + (loop (cdddr directives)))) + (else (loop (cdr directives)))))) + ((form forms ...) + (append (module-declaration-uses form) + (module-declaration-uses forms))) + (_ '()))) + +;; find all use-modules forms, and return what they pull in +;; NOTE this will pull in all forms looking like a (use-modules ...) +;; form, even if they are quoted, or in a cond-expand +(define (module-use-module-uses forms) + (match forms + (('use-modules modules ...) + (map parse-interface-specification modules)) + ((form forms ...) + (append (module-use-module-uses form) + (module-use-module-uses forms))) + (_ '()))) + +;; find all explicit module references (e.g. +;; (@ (module) var) and (@@ (module) private-var)), +;; and return those modules +(define (module-refer-uses forms) + (match forms + (((or '@ '@@) module symb) + (list (make-module module select: (list symb)))) + ((form forms ...) + (append (module-refer-uses form) + (module-refer-uses forms))) + (_ '()))) + +;; List of all modules pulled in in any of forms +;; Returns a list where each element suitable to have +;; resolve-interface applied to it. +(define (module-uses* forms) + (map module->list + (append + (module-declaration-uses forms) + (module-use-module-uses forms) + (module-refer-uses forms)))) diff --git a/module/hnh/module-introspection/static-util.scm b/module/hnh/module-introspection/static-util.scm new file mode 100644 index 00000000..7593ce3c --- /dev/null +++ b/module/hnh/module-introspection/static-util.scm @@ -0,0 +1,9 @@ +(define-module (hnh module-introspection static-util) + :export (get-forms)) + +(define (get-forms port) + (let loop ((done '())) + (let ((form (read port))) + (if (eof-object? form) + done + (loop (cons form done)))))) diff --git a/module/hnh/test/testrunner.scm b/module/hnh/test/testrunner.scm new file mode 100644 index 00000000..384afd4b --- /dev/null +++ b/module/hnh/test/testrunner.scm @@ -0,0 +1,126 @@ +(define-module (hnh test testrunner) + :use-module (srfi srfi-64) + :use-module (hnh test util) + :use-module (ice-9 pretty-print) + :use-module (ice-9 format) + :export (verbose? construct-test-runner) + ) + +(define verbose? (make-parameter #f)) + +(define (pp form indent prefix-1) + (let ((prefix (make-string (+ (string-length indent) + (string-length prefix-1)) + #\space))) + (string-replace-head + (with-output-to-string + (lambda () (pretty-print + form + display?: #t + per-line-prefix: prefix + width: (- 79 (string-length indent))))) + (string-append indent prefix-1)))) + + +(define (construct-test-runner) + (define runner (test-runner-null)) + (define depth 0) + ;; end of individual test case + (test-runner-on-test-begin! runner + (lambda (runner) + (test-runner-aux-value! runner (transform-time-of-day (gettimeofday))))) + (test-runner-on-test-end! runner + (lambda (runner) + (when (verbose?) (display (make-indent depth))) + (case (test-result-kind runner) + ((pass) (display (green "X"))) + ((fail) (display (red "E"))) + ((xpass) (display (yellow "X"))) + ((xfail) (display (yellow "E"))) + ((skip) (display (yellow "-")))) + (when (or (verbose?) (eq? 'fail (test-result-kind))) + (format #t " ~a~%" + (cond ((test-runner-test-name runner) + (negate string-null?) => identity) + ((test-result-ref runner 'expected-value) + => (lambda (p) (with-output-to-string + (lambda () + (display (bold "[SOURCE]: ")) + (truncated-print p width: 60)))))))) + (when (eq? 'fail (test-result-kind)) + (cond ((test-result-ref runner 'actual-error) + => (lambda (err) + (if (and (list? err) + (= 5 (length err))) + (let ((err (list-ref err 0)) + (proc (list-ref err 1)) + (fmt (list-ref err 2)) + (args (list-ref err 3))) + (format #t "~a~a in ~a: ~?~%" + (make-indent (1+ depth)) + err proc fmt args)) + (format #t "~aError: ~s~%" (make-indent (1+ depth)) err)))) + (else + (let ((unknown-expected (gensym)) + (unknown-actual (gensym))) + (let ((expected (test-result-ref runner 'expected-value unknown-expected)) + (actual (test-result-ref runner 'actual-value unknown-actual))) + (let ((indent (make-indent (1+ depth)))) + (if (eq? expected unknown-expected) + (format #t "~aAssertion failed~%" indent) + (begin + (display (pp expected indent "Expected: ")) + (display (pp actual indent "Received: ")) + (let ((d (diff (pp expected "" "") + (pp actual "" "")))) + (display + (string-join + (map (lambda (line) (string-append indent "|" line)) + (string-split d #\newline)) + "\n" 'suffix)))))))))) + (format #t "~aNear ~a:~a~%" + (make-indent (1+ depth)) + (test-result-ref runner 'source-file) + (test-result-ref runner 'source-line)) + (pretty-print (test-result-ref runner 'source-form) + (current-output-port) + per-line-prefix: (string-append (make-indent (1+ depth)) "> ") + )) + + (let ((start (test-runner-aux-value runner)) + (end (transform-time-of-day (gettimeofday)))) + (when (< (µs 1) (- end start)) + (format #t "~%Slow test: ~s, took ~a~%" + (test-runner-test-name runner) + (exact->inexact (/ (- end start) (µs 1))) + ))))) + + ;; on start of group + (test-runner-on-group-begin! runner + ;; count is number of #f + (lambda (runner name count) + (if (<= depth 1) + (format #t "~a ~a ~a~%" + (make-string 10 #\=) + name + (make-string 10 #\=)) + (when (verbose?) + (format #t "~a~a~%" (make-string (* depth 2) #\space) name))) + (set! depth (1+ depth)))) + (test-runner-on-group-end! runner + (lambda (runner) + (set! depth (1- depth)) + (when (<= depth 1) + (newline)))) + ;; after everything else is done + (test-runner-on-final! runner + (lambda (runner) + (format #t "Guile version ~a~%~%" (version)) + (format #t "pass: ~a~%" (test-runner-pass-count runner)) + (format #t "fail: ~a~%" (test-runner-fail-count runner)) + (format #t "xpass: ~a~%" (test-runner-xpass-count runner)) + (format #t "xfail: ~a~%" (test-runner-xfail-count runner)) + )) + + runner) + diff --git a/module/hnh/test/util.scm b/module/hnh/test/util.scm new file mode 100644 index 00000000..3d51e162 --- /dev/null +++ b/module/hnh/test/util.scm @@ -0,0 +1,57 @@ +(define-module (hnh test util) + :use-module ((hnh util) :select (begin1)) + :use-module ((hnh util io) :select (call-with-tmpfile)) + :use-module (ice-9 pretty-print) + :use-module ((ice-9 rdelim) :select (read-string)) + :use-module ((ice-9 popen) + :select (open-pipe* + close-pipe)) + :export (µs + transform-time-of-day + green + red + yellow + bold + make-indent + string-replace-head + diff + )) + +(define (µs x) + (* x #e1e6)) + +(define (transform-time-of-day tod) + (+ (* (µs 1) (car tod)) + (cdr tod))) + +(define (escaped sequence string) + (format #f "\x1b[~am~a\x1b[m" sequence string)) + +;; Terminal output formatting. Doesn NOT compose +(define (green s) (escaped 32 s)) +(define (red s) (escaped 31 s)) +(define (yellow s) (escaped 33 s)) +(define (bold s) (escaped 1 s)) + +(define (make-indent depth) + (make-string (* 2 depth) #\space)) + +(define (string-replace-head s1 s2) + (string-replace s1 s2 + 0 (string-length s2))) + + +(define diff-cmd + ;; '("diff") + '("git" "diff" "--no-index" "--color-moved=default" "--color=always"; "--word-diff=color" + ) + ) + +(define (diff s1 s2) + (let ((filename1 (call-with-tmpfile (lambda (p f) (pretty-print s1 p display?: #t) f))) + (filename2 (call-with-tmpfile (lambda (p f) (pretty-print s2 p display?: #t) f)))) + (let ((pipe (apply open-pipe* + OPEN_READ + (append diff-cmd (list filename1 filename2))))) + (begin1 (read-string pipe) + (close-pipe pipe))))) diff --git a/module/hnh/test/xmllint.scm b/module/hnh/test/xmllint.scm new file mode 100644 index 00000000..95362607 --- /dev/null +++ b/module/hnh/test/xmllint.scm @@ -0,0 +1,27 @@ +(define-module (hnh test xmllint) + :use-module (srfi srfi-1) + :use-module (srfi srfi-71) + :use-module (srfi srfi-88) + :use-module ((rnrs io ports) :select (get-string-all)) + :use-module ((hnh util) :select (begin1)) + :export (xmllint) + ) + + +(define (xmllint str) + (let ((in* out (car+cdr (pipe))) + (in out* (car+cdr (pipe))) + (cmdline (string-split "xmllint --format -" #\space))) + (define pid + (spawn (car cmdline) cmdline + input: in* + output: out*)) + (close-port in*) + (close-port out*) + + (display str out) + (force-output out) + (close-port out) + + (begin1 (get-string-all in) + (close-port in)))) diff --git a/module/hnh/util.scm b/module/hnh/util.scm index d2c0dd5f..c88a029e 100644 --- a/module/hnh/util.scm +++ b/module/hnh/util.scm @@ -17,6 +17,7 @@ find-extreme find-min find-max filter-sorted != + init+last take-to string-take-to string-first @@ -55,6 +56,12 @@ assq-ref-all assv-ref-all + uniqx + uniq + univ + uniqv + unique + vector-last ->string @@ -64,6 +71,10 @@ :replace (set! define-syntax when unless)) +(cond-expand + (guile-3 (use-modules ((ice-9 copy-tree) :select (copy-tree)))) + (else)) + ((@ (guile) define-syntax) define-syntax (syntax-rules () ((_ (name args ...) body ...) @@ -112,6 +123,9 @@ ((for (<var> <vars> ...) in <collection> b1 body ...) (map ((@ (ice-9 match) match-lambda) [(<var> <vars> ...) b1 body ...]) <collection>)) + ((for (<var> <vars> ... . <tail>) in <collection> b1 body ...) + (map ((@ (ice-9 match) match-lambda) [(<var> <vars> ... . <tail>) b1 body ...]) + <collection>)) ((for <var> in <collection> b1 body ...) (map (lambda (<var>) b1 body ...) <collection>)))) @@ -137,9 +151,12 @@ +;; TODO this is called flip in Haskell land (define (swap f) (lambda args (apply f (reverse args)))) - +;; Swap would be +;; (define (swap p) +;; (xcons (car p) (cdr p))) ;; Allow set to work on multiple values at once, ;; similar to Common Lisp's @var{setf} @@ -240,6 +257,12 @@ ;; (define (!= a b) (not (= a b))) (define != (negate =)) + +(define (init+last l) + (let ((last rest (car+cdr (reverse l)))) + (values (reverse rest) last))) + + (define (take-to lst i) "Like @var{take}, but might lists shorter than length." (if (> i (length lst)) @@ -307,7 +330,8 @@ (define (kvlist->assq kvlist) (map (lambda (pair) - (cons (keyword->symbol (car pair)) (cdr pair))) + (cons (keyword->symbol (car pair)) + (cadr pair))) (group kvlist 2))) (define* (assq-limit alist optional: (number 1)) @@ -320,8 +344,7 @@ (for value in lst (let ((key (proc value))) (hash-set! h key (cons value (hash-ref h key '()))))) - ;; NOTE changing this list to cons allows the output to work with assq-merge. - (hash-map->list list h))) + (hash-map->list cons h))) ;; (split-by '(0 1 2 3 4 2 5 6) 2) ;; ⇒ ((0 1) (3 4) (5 6)) @@ -383,7 +406,7 @@ (reverse (cons (map list last) rest )))))) ;; Given an arbitary tree, do a pre-order traversal, appending all strings. -;; non-strings allso allowed, converted to strings and also appended. +;; non-strings also allowed, converted to strings and also appended. (define (string-flatten tree) (cond [(string? tree) tree] [(list? tree) (string-concatenate (map string-flatten tree))] @@ -506,6 +529,19 @@ (define (assv-ref-all alist key) (ass%-ref-all alist key eqv?)) +(define (uniqx = lst) + (cond ((null? lst) lst) + ((null? (cdr lst)) lst) + ((and (pair? lst) + (= (car lst) (cadr lst))) + (uniqx = (cons (car lst) (cddr lst)))) + (else (cons (car lst) + (uniqx = (cdr lst)))))) + +(define (uniq lst) (uniqx eq? lst)) +(define (univ lst) (uniqx eqv? lst)) +(define (unique lst) (uniqx equal? lst)) + (define (vector-last v) @@ -517,9 +553,12 @@ (define-syntax catch* - (syntax-rules () + (syntax-rules (pre-unwind) + ((_ thunk ((pre-unwind key) handler)) + (with-throw-handler (quote key) thunk handler)) ((_ thunk (key handler)) (catch (quote key) thunk handler)) - ((_ thunk (key handler) rest ...) - (catch* (lambda () (catch (quote key) thunk handler)) + + ((_ thunk pair rest ...) + (catch* (lambda () (catch* thunk pair)) rest ...)))) diff --git a/module/hnh/util/env.scm b/module/hnh/util/env.scm index 18ec0543..32ea1cc1 100644 --- a/module/hnh/util/env.scm +++ b/module/hnh/util/env.scm @@ -1,5 +1,7 @@ (define-module (hnh util env) - :export (let-env with-working-directory)) + :export (let-env + with-working-directory + with-locale1)) (define-syntax let-env (syntax-rules () @@ -33,3 +35,12 @@ thunk (lambda () (chdir old-cwd))))) + +(define-syntax-rule (with-locale1 category locale thunk) + (let ((old #f)) + (dynamic-wind + (lambda () + (set! old (setlocale category)) + (setlocale category locale)) + thunk + (lambda () (setlocale category old))))) diff --git a/module/hnh/util/io.scm b/module/hnh/util/io.scm index d638ebb4..09900f8d 100644 --- a/module/hnh/util/io.scm +++ b/module/hnh/util/io.scm @@ -4,7 +4,9 @@ :export (open-input-port open-output-port read-lines - with-atomic-output-to-file)) + with-atomic-output-to-file + call-with-tmpfile + ->port)) (define (open-input-port str) (if (string=? "-" str) @@ -62,3 +64,19 @@ ;; counted on, since anything with an unspecified return ;; value might as well return #f) #f)))) + +(define* (call-with-tmpfile proc key: (tmpl "/tmp/file-XXXXXXX")) + (let* ((filename (string-copy tmpl)) + (port (mkstemp! filename))) + (with-continuation-barrier + (lambda () + (begin1 + (proc port filename) + (close-port port)))))) + +(define (->port port-or-string) + (cond ((port? port-or-string) port-or-string) + ((string? port-or-string) (open-input-string port-or-string)) + (else (scm-error 'misc-error "->port" + "Not a port or string" + (list port-or-string) #f)))) diff --git a/module/hnh/util/path.scm b/module/hnh/util/path.scm index ea081e85..b0991073 100644 --- a/module/hnh/util/path.scm +++ b/module/hnh/util/path.scm @@ -3,15 +3,20 @@ :use-module (srfi srfi-71) :use-module (hnh util) :export (path-append + path-absolute? path-join path-split file-hidden? filename-extension - realpath)) + realpath + relative-to)) (define // file-name-separator-string) (define /? file-name-separator?) +(define path-absolute? absolute-file-name?) + +;; TODO remove intermidiate period components (define (path-append . strings) (fold (lambda (s done) (string-append @@ -87,3 +92,31 @@ (if (absolute-file-name? filename) filename (path-append (getcwd) filename))) + + +(define (relative-to base path) + ;; (typecheck base string?) + ;; (typecheck path string?) + + (when (string-null? base) + (error "Base can't be empty" )) + + (let ((base (if (absolute-file-name? base) + base + (path-append (getcwd) base)))) + + (cond ((equal? '("") base) path) + ((not (absolute-file-name? path)) + (path-append base path)) + (else + (let loop ((a (path-split base)) + (b (path-split path))) + (cond + ((null? a) (path-join b)) + ((null? b) path) + ((string=? (car a) (car b)) (loop (cdr a) (cdr b))) + (else + (path-join + (append + (make-list (length a) "..") + (drop b (length a))))))))))) diff --git a/module/hnh/util/state-monad.scm b/module/hnh/util/state-monad.scm new file mode 100644 index 00000000..91201583 --- /dev/null +++ b/module/hnh/util/state-monad.scm @@ -0,0 +1,120 @@ +;;; Commentary: +;;; A state monad similar to (and directly influenced by) the one found in in +;;; Haskell +;;; Each procedure can either explicitly take the state as a curried last +;;; argument, or use the `do' notation, which handles that implicitly. +;;; Each procedure MUST return two values, where the second value is the state +;;; value which will be chained. +;;; +;;; Code borrowed from guile-dns +;;; Code: + +(define-module (hnh util state-monad) + :use-module (ice-9 curried-definitions) + :replace (do mod) + :export (with-temp-state + <$> return get get* put put* sequence lift + eval-state exec-state)) + +(define-syntax do + (syntax-rules (<- let =) + ((_ (a ...) <- b rest ...) + (lambda state-args + (call-with-values (lambda () (apply b state-args)) + (lambda (a* . next-state) + (apply (lambda (a ...) + (apply (do rest ...) + next-state)) + a*))))) + ((_ a <- b rest ...) + (lambda state-args + (call-with-values (lambda () (apply b state-args)) + (lambda (a . next-state) + (apply (do rest ...) + next-state))))) + + ((_ a = b rest ...) + (let ((a b)) + (do rest ...))) + + ((_ a) + (lambda state (apply a state))) + ((_ a rest ...) + (lambda state + (call-with-values (lambda () (apply a state)) + (lambda (_ . next-state) + (apply (do rest ...) + next-state))))))) + + +(define (with-temp-state state* op) + (do old <- (get*) + (apply put* state*) + ret-value <- op + (apply put* old) + (return ret-value))) + + +(define (<$> f y) + (do tmp <- y + (return (f tmp)))) + +(define ((return x) . y) + (apply values x y)) + +(define ((get*) . state) + "Like @code{get}, but always returns a list" + (values state state)) + +(define ((get) fst . state) + "If state contains a single variable return that, otherwise, return a list of all variables in state" + (if (null? state) + (values fst fst) + (apply values (cons fst state) fst state))) + +(define ((put . new-state) fst . old-state) + (if (null? old-state) + (apply values fst new-state) + (apply values (cons fst old-state) new-state))) + +;; Like put, but doesn't return anything (useful) +(define ((put* . new-state) . _) + (apply values #f new-state)) + +(define (mod proc) + (do + a <- (get) + (put (proc a)))) + +;; ms must be a list of continuations +(define (sequence ms) + (if (null? ms) + (return '()) + (do + fst <- (car ms) + rest <- (sequence (cdr ms)) + (return (cons fst rest))))) + + +(define (lift proc . arguments) + (do xs <- (sequence arguments) + (return (apply proc xs)))) + + +;; Run state, returning value +(define (eval-state st init) + (call-with-values + (lambda () + (if (procedure? init) + (call-with-values init st) + (st init))) + (lambda (r . _) r))) + +;; Run state, returning state +(define (exec-state st init) + (call-with-values + (lambda () + (if (procedure? init) + (call-with-values init st) + (st init))) + (lambda (_ . v) (apply values v)))) diff --git a/module/hnh/util/uuid.scm b/module/hnh/util/uuid.scm index 68455243..8e0434e3 100644 --- a/module/hnh/util/uuid.scm +++ b/module/hnh/util/uuid.scm @@ -1,19 +1,19 @@ (define-module (hnh util uuid) :use-module (ice-9 format) - :export (uuid uuid-v4)) + :export (seed uuid uuid-v4)) -(define %seed (random-state-from-platform)) +(define seed (make-parameter (random-state-from-platform))) (define (uuid-v4) (define version 4) (define variant #b10) (format #f "~8'0x-~4'0x-~4'0x-~4'0x-~12'0x" - (random (ash 1 (* 4 8)) %seed) - (random (ash 1 (* 4 4)) %seed) + (random (ash 1 (* 4 8)) (seed)) + (random (ash 1 (* 4 4)) (seed)) (logior (ash version (* 4 3)) - (random (1- (ash 1 (* 4 3))) %seed)) + (random (1- (ash 1 (* 4 3))) (seed))) (logior (ash variant (+ 2 (* 4 3))) - (random (ash 1 (+ 2 (* 4 3))) %seed)) - (random (ash 1 (* 4 12)) %seed))) + (random (ash 1 (+ 2 (* 4 3))) (seed))) + (random (ash 1 (* 4 12)) (seed)))) (define uuid uuid-v4) |