/ dash-x.el
dash-x.el
1 ;;; dash-x.el --- Description -*- lexical-binding: t; -*- 2 ;; 3 ;; Copyright (C) 2024 Benjamin Andresen 4 ;; 5 ;; Author: Benjamin Andresen <b@lambda.icu> 6 ;; Maintainer: Benjamin Andresen <b@lambda.icu> 7 ;; Created: August 19, 2024 8 ;; Modified: August 19, 2024 9 ;; Version: 0.0.1 10 ;; Package-Requires: ((emacs "24.3")) 11 ;; 12 ;; This file is not part of GNU Emacs. 13 ;; 14 ;;; Commentary: 15 ;; 16 ;; Description 17 ;; 18 ;;; Code: 19 20 (require 'parseedn) 21 22 (defvar dash-x-tests-enabled nil) 23 (defvar dash-x-tap-command "bb tap") 24 25 (defmacro -comment (&rest _body) 26 nil) 27 28 (defmacro -tests (&rest body) 29 "Run a series of ERT tests defined in BODY. 30 Each test is defined by a series of setup forms followed by a comparison 31 using the := symbol. The left-hand side of := is the expression to test, 32 and the right-hand side is the expected result. 33 34 Example usage: 35 (-tests 36 (setq x 10) 37 (setq y 20) 38 (+ x y) := 30 39 (- y x) := 10) 40 41 This macro is only active when `dash-x-tests-enabled' is non-nil. 42 It returns a string summarizing the test results." 43 (when (bound-and-true-p dash-x-tests-enabled) 44 (let ((setup-forms '()) 45 (test-forms '()) 46 (test-names '())) 47 (while body 48 (let ((form (pop body))) 49 (if (eq form :=) 50 (let ((lhs (pop setup-forms)) 51 (rhs (pop body)) 52 (test-name (gensym "autogenerated-test-"))) 53 (push test-name test-names) 54 (push `(ert-deftest ,test-name () 55 (should (equal ,lhs ,rhs))) 56 test-forms)) 57 (push form setup-forms)))) 58 `(progn 59 ,@(nreverse setup-forms) 60 ,@(nreverse test-forms) 61 (let ((messages '()) 62 (orig-message (symbol-function 'message))) 63 (cl-letf (((symbol-function 'message) 64 (lambda (format-string &rest args) 65 (push (apply #'format format-string args) messages)))) 66 (ert-run-tests-batch '(or ,@(nreverse test-names)))) 67 (let ((result-line (car (-filter (lambda (msg) (s-contains? "Ran " msg)) 68 (nreverse messages))))) 69 (if result-line 70 (s-trim result-line) 71 "No test results found"))))))) 72 73 (defun -tests-enable! () 74 (setq dash-x-tests-enabled t)) 75 76 (defun -slurp (filename) 77 (with-temp-buffer 78 (insert-file-contents filename) 79 (buffer-substring-no-properties 80 (point-min) 81 (point-max)))) 82 83 (defun --benchmark (duration-ms f) 84 (let* ((start (float-time)) 85 (after-first (float-time)) 86 (delta (- after-first start)) 87 (deadline (+ start (/ duration-ms 1000.0))) 88 (tight-iters (max (/ (/ duration-ms (* delta 1000)) 10) 1))) 89 (cl-loop 90 with i = 1 91 while (< (float-time) deadline) 92 do (dotimes (_ tight-iters) (funcall f)) 93 do (cl-incf i tight-iters) 94 finally return 95 (let* ((final-time (- (float-time) start)) 96 (time-per-call (* (/ final-time (float i)) 1e9))) 97 (format 98 "Time per call: %s Iterations: %d" 99 (cond 100 ((< time-per-call 1e3) (format "%.0f ns" time-per-call)) 101 ((< time-per-call 1e6) (format "%.2f µs" (/ time-per-call 1e3))) 102 ((< time-per-call 1e9) (format "%.2f ms" (/ time-per-call 1e6))) 103 (t (format "%.2f s" (/ time-per-call 1e9)))) 104 i))))) 105 106 (defmacro -time+ (&rest body) 107 "Like `--benchmark', but runs BODY for 2000ms and prints the average 108 time for a single iteration. If the first argument is a number, it's used 109 as the duration in milliseconds. Returns a formatted string with the results." 110 (let* ((duration-and-body 111 (if (numberp (car body)) 112 (cons (car body) (cdr body)) 113 (cons 2000 body))) 114 (duration (car duration-and-body)) 115 (body (cdr duration-and-body))) 116 `(--benchmark ,duration (lambda () ,@body)))) 117 118 (defun -tap> (x) 119 "Sends X to `dash-x-tap-command' which is a command that accepts edn on stdin. 120 Always retuns t." 121 (interactive) 122 (with-temp-buffer 123 (insert (parseedn-print-str x)) 124 (shell-command-on-region (point-min) (point-max) dash-x-tap-command)) 125 't) 126 127 (provide 'dash-x) 128 ;;; dash-x.el ends here