;; A pretty-printer that outputs tables in Fennel syntax. ;; Loosely based on inspect.lua: http://github.com/kikito/inspect.lua (fn view-quote [str] (.. "\"" (: str :gsub "\"" "\\\"") "\"")) (local short-control-char-escapes {"\a" "\\a" "\b" "\\b" "\f" "\\f" "\n" "\\n" "\r" "\\r" "\t" "\\t" "\v" "\\v"}) (local long-control-char-escapes (let [long {}] (for [i 0 31] (let [ch (string.char i)] (when (not (. short-control-char-escapes ch)) (tset short-control-char-escapes ch (.. "\\" i)) (tset long ch (: "\\%03d" :format i))))) long)) (fn escape [str] (-> str (: :gsub "\\" "\\\\") (: :gsub "(%c)%f[0-9]" long-control-char-escapes) (: :gsub "%c" short-control-char-escapes))) (fn sequence-key? [k len] (and (= (type k) "number") (<= 1 k) (<= k len) (= (math.floor k) k))) (local type-order {:number 1 :boolean 2 :string 3 :table 4 :function 5 :userdata 6 :thread 7}) (fn sort-keys [a b] (let [ta (type a) tb (type b)] (if (and (= ta tb) (or (= ta "string") (= ta "number"))) (< a b) (let [dta (. type-order a) dtb (. type-order b)] (if (and dta dtb) (< dta dtb) dta true dtb false :else (< ta tb)))))) (fn get-sequence-length [t] (var len 1) (each [i (ipairs t)] (set len i)) len) (fn get-nonsequential-keys [t] (let [keys {} sequence-length (get-sequence-length t)] (each [k (pairs t)] (when (not (sequence-key? k sequence-length)) (table.insert keys k))) (table.sort keys sort-keys) (values keys sequence-length))) (fn count-table-appearances [t appearances] (when (= (type t) "table") (if (not (. appearances t)) (do (tset appearances t 1) (each [k v (pairs t)] (count-table-appearances k appearances) (count-table-appearances v appearances))) (tset appearances t (+ (or (. appearances t) 0) 1)))) appearances) (var put-value nil) ; mutual recursion going on; defined below (fn puts [self ...] (each [_ v (ipairs [...])] (table.insert self.buffer v))) (fn tabify [self] (puts self "\n" (: self.indent :rep self.level))) (fn already-visited? [self v] (not= (. self.ids v) nil)) (fn get-id [self v] (var id (. self.ids v)) (when (not id) (let [tv (type v)] (set id (+ (or (. self.max-ids tv) 0) 1)) (tset self.max-ids tv id) (tset self.ids v id))) (tostring id)) (fn put-sequential-table [self t len] (puts self "[") (set self.level (+ self.level 1)) (for [i 1 len] (when (< 1 i (+ 1 len)) (puts self " ")) (put-value self (. t i))) (set self.level (- self.level 1)) (puts self "]")) (fn put-key [self k] (if (and (= (type k) "string") (: k :find "^[-%w?\\^_!$%&*+./@:|<=>]+$")) (puts self ":" k) (put-value self k))) (fn put-kv-table [self t ordered-keys] (puts self "{") (set self.level (+ self.level 1)) ;; first, output sorted nonsequential keys (each [i k (ipairs ordered-keys)] (when (or self.table-edges (not= i 1)) (tabify self)) (put-key self k) (puts self " ") (put-value self (. t k))) ;; next, output any sequential keys (each [i v (ipairs t)] (tabify self) (put-key self i) (puts self " ") (put-value self v)) (set self.level (- self.level 1)) (when self.table-edges (tabify self)) (puts self "}")) (fn put-table [self t] (let [metamethod (and self.metamethod? (-?> t getmetatable (. :__fennelview)))] (if (and (already-visited? self t) self.detect-cycles?) (puts self "#