Last active
March 12, 2025 05:55
-
-
Save trentgill/d65eb7e5724fa9a25ef7524bc5a03463 to your computer and use it in GitHub Desktop.
sequins lovingly rewritten in fennel
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;;; sequins library | |
;; lovingly re-written in fennel | |
(var s-mt {}) ;to be metatable (avoid circular dependence) | |
(fn %1 [ix wrap] | |
"modulo for 1-based indices" | |
(-> ix (- 1) (% wrap) (+ 1))) | |
(fn sequins? [t] | |
"return true if the argument is a sequins-table" | |
(= s-mt (getmetatable t))) | |
;; modifier implementations | |
;; #[ix n self] -> string, number, or nil | |
(local flows | |
{:every (fn [ix n _] | |
(if (not= 0 (% ix n)) | |
:skip)) | |
:times (fn [ix n _] | |
(if (< n ix) :dead)) | |
:count (fn [ix n self] | |
(if (< ix n) | |
:again | |
(tset self :count :ix 0))) | |
:select (fn [_ n self] | |
(if (> n 0) | |
(do | |
(tset self :select :n (- n)) ;; available for :again undo | |
n) ;; return the index | |
(< n 0) | |
(tset self :select :n 0))) | |
:step (fn [ix n self] | |
(let [nix (%1 (+ ix n))] | |
(tset self :ix nix) | |
nix))}) ;; return the index | |
(fn flow-with [key self ?revert] | |
"apply the next step of a flow-modifier and maybe return a value" | |
(let [step (if ?revert -1 1) | |
flow (. self key)] | |
(tset flow :ix (+ step flow.ix)) | |
(let [{: ix : n} flow] | |
((. flows key) ix n self)))) | |
(fn unroll-count [self] | |
"revert the effect of flow-with on all modifiers except count" | |
(each [_ v (ipairs [:every :times :select])] ;; FIXME broken for :select | |
(flow-with v self :revert))) | |
(fn unwrap [f v] | |
"if the value is a nested sequins, unwrap it" | |
(if (sequins? v) | |
(values (v)) ;unwrap by calling the sequins | |
v)) | |
(fn realize [self] | |
"realize the next value from a sequins, or signal to its parent" | |
(case (case-try (flow-with :every self) | |
nil (flow-with :times self) | |
nil (flow-with :select self) ;; may return early with index | |
nil (let [again (flow-with :count self)] | |
(values (flow-with :step self) again))) | |
(where s (or (= s :skip) (= s :dead))) (values nil s) | |
(ix ?again) (do | |
(when (= ?again :again) | |
(unroll-count self)) | |
(case (unwrap (. self.data ix)) | |
(where (_ s) (= :string (type s)) (self) ;; recur to overcome skip etc | |
(val _) (values (self.map val) ?again)))) | |
(fn adorn [self mods] | |
"add or modify a sequins objects metaparams" | |
(each [k v (pairs mods)] | |
(tset self k :n v)) ;set modifer value (number | fn | sequins) | |
self) | |
(fn new [data mods] | |
"create a new sequins object with optional modifiers" | |
(-> {:data data | |
:len (length data) ;memoize length for speed | |
;; mods | |
:step {:ix 0 :qix 1 :n 1} ;; TODO remove :qix | |
:select {:ix 0 :n 1} ;; 0 is no select, -ve is previous | |
:count {:ix 0 :n 1} | |
:every {:ix 0 :n 1} | |
:times {:ix 0 :n 99999999} ;; a really big number of repeats | |
:map #$} | |
(adorn mods) | |
(setmetatable s-mt))) | |
; sequins metatable attached to each instance | |
(set s-mt {:__call realize | |
:__len #(. $ :len)}) | |
; expose public interface | |
; NOTE redundant is_sequins() for lua compatibility | |
(setmetatable {: new : adorn : sequins? :is_sequins sequins?} | |
{:__call (fn [_ ...] (new ...))}) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment