You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

541 lines
15 KiB

5 years ago
;;;; utils.lisp -- some random utils I collect. might be useful, probably won't be.
(defpackage :flan-utils (:use :cl))
(in-package :flan-utils)
(defmacro export* (&rest syms)
"Export all symbols"
(cons 'progn (mapcan #'(lambda (x) `((export ',x))) syms)))
5 years ago
(defmacro eval-all (syms)
"Eval all statements, return in list."
`(mapcar #'eval '(,@syms)))
(export 'eval-all)
5 years ago
(defmacro export*! (&rest syms)
"Eval all statements export return values"
`(mapc #'export (eval-all ,syms)))
5 years ago
(export 'export*)
(export 'export*!)
5 years ago
5 years ago
(defmacro defexport (&rest val)
"Same as export*! for some reason"
5 years ago
`(export*! ,@val))
(export 'defexport)
5 years ago
5 years ago
(defun file-get-contents (filename)
"Read file into string"
5 years ago
(with-open-file (stream filename)
(let ((contents (make-string (file-length stream))))
(read-sequence contents stream)
contents)))
(export 'file-get-contents)
(defparameter *old-readtables* nil)
(defexport
5 years ago
;;; --- actual (exported) code goes here --
5 years ago
(defmacro val-if-or (val test or)
"(if (test val) val or)"
`(let ((vv ,val))
(if (funcall ,test vv) vv ,or)))
5 years ago
(defun where (expr items)
(mapcan #'(lambda (x)
5 years ago
(when (funcall expr x) (list x)))
5 years ago
items))
5 years ago
(defun true (f)
(not (null f)))
(defun nop ()
"Do nothing"
5 years ago
nil)
(defun yep ()
"Do nothing"
5 years ago
t)
(defun mapline (input fi &key (read-line #'read-line))
"Map lines from stream"
(loop for line = (funcall read-line input nil)
while line do (funcall fi line)))
(defun strcat (&rest str)
"Concat all strings, if item is not string it is written to one."
5 years ago
(apply #'concatenate (cons 'string (mapcar #'(lambda (x)
(typecase x
(string x)
(character (string x))
(t (write-to-string x))))
str))))
5 years ago
(defmacro until (stmt)
"Repeat stmt until its return is not NIL, then return that value."
5 years ago
`(let ((ret nil))
(loop while (null (setf ret ,stmt)))
ret))
(defmacro popor (li or)
"If li is list, pop it, else return or"
5 years ago
`(if (atom ,li) ,or
(pop ,li)))
(defun rand-in (l &key (random #'random) )
"Random member of, slide right if nil"
"Pretty sure this doesn't work"
5 years ago
(let ((rng (funcall random (list-length l))))
5 years ago
(let ((nl (nthcdr rng l)))
5 years ago
(until (pop nl)))))
(defun regex-replace-many (str matches replwith)
"Replace list of regexes with list of new string"
(let ((ret str))
(loop for match in matches
for repl in replwith
do (setf ret (cl-ppcre:regex-replace-all match ret repl)))))
(defun in-range(num r s)
(and (>= num r)
(<= num s)))
(defun format-string (fmt &rest r)
"Like sprintf I guess"
5 years ago
(with-output-to-string (stream)
(apply #'format `(,stream ,fmt . ,r))))
(defun get-all-symbols ()
(let ((lst '()))
(do-all-symbols (s lst)
(push s lst))
lst))
(defun symbol-match (scan &optional (symbols nil))
"Return all symbols whose names match regex `scan'. If symbols are not provided, get them all."
(let ((symbols (val-if-or symbols #'true
(get-all-symbols))))
(where #'(lambda (x) (cl-ppcre:scan scan (write-to-string x)))
symbols)))
(defun index (i max)
"Looping index"
(if (< i 0)
(index (+ max i) max)
(mod i max)))
(defun slice (seq start end)
"Like JS slice()?"
(let ((start (index start (length seq)))
(end (index end (length seq))))
(rplacd (nthcdr end seq) nil)
(nthcdr start seq)))
(defun flatten-top-level (li)
"'( (1 2) 3 4 (5 6 7) (8) ((9 10))) -> ( 1 2 3 4 5 6 7 8 (9 10))"
(mapcan #'(lambda (x)
(if (atom x) (list x) x))
li))
(defun flatten (li)
"'( (1 2) 3 4 (5 6 7) (8) ((9 10))) -> ( 1 2 3 4 5 6 7 8 9 10)"
(mapcan #'(lambda (x)
(if (atom x)
(list x)
(flatten x)))
li))
(defun strjoin (delim &rest strs)
"Join strings with deliminator"
(let ((strs (flatten-top-level strs)))
(apply #'strcat
(slice
(mapcan #'(lambda (x)
(list x delim))
strs)
0 -2))))
(defun export-struct (struct &optional (symbols nil)) ;;XXX: Exporting make-* does not check for $
"Export all symbols relating to `struct'"
(mapc #'export (symbol-match (strcat "^(MAKE-)?" (write-to-string struct) "-?") symbols)))
(defun -export*-struct (structs &optional (symbols nil)) ;;XXX
(mapc #'export (symbol-match
(strcat
"^(MAKE-)?("
(strjoin "|" (mapcar #'write-to-string structs))
")-?")
symbols)))
(defmacro export*-struct (&rest structs)
"Export all symbols relating to multiple structs"
`(-export*-struct '(,@structs)))
(defun mapn (lam &rest lists)
"Map and return last"
(let ((ret '()))
(mapc #'(lambda (x)
(setf ret (funcall lam x)))
(flatten-top-level lists))
ret))
(defun cyclic (list)
"Simple circular list"
(cdr (rplacd (last list) list)))
(defun map1 (lam &rest lists)
"Map and return first"
(let ((ret '())
(change t))
(mapc #'(lambda (x)
(if change
(progn
(setf ret (funcall lam x))
(setf change nil))
(funcall lam x)))
(flatten-top-level lists))
ret))
(defun mapnth (lam n &rest args)
"Map and return nth or nil (second value t if match found)"
(let ((index 0)
(ret '())
(match nil))
(mapc #'(lambda (x)
(if (= n index)
(progn (setf ret (funcall lam x)) (setf match t))
(funcall lam x))
(incf index))
(flatten-top-level args))
(values ret match)))
(defun mapv (lam &rest args)
"Map and return values()"
(apply #'values
(mapcar lam (flatten-top-level args))))
(defmacro lexical-boundp (lex)
"XXX: Use (declare (special ...)) instead"
`(when (ignore-errors ,lex) t))
;; --- progressive evals
(defmacro progen (&rest things)
`(mapn #'eval '(,@things)))
(defmacro proge1 (&rest things)
`(map1 #'eval '(,@things)))
(defmacro progenth (n &rest things)
`(mapnth #'eval ,n '(,@things)))
(defmacro progev (&rest things)
`(mapv #'eval '(,@things)))
(defmacro progel (&rest things)
`(mapcar #'eval '(,@things)))
(defmacro yield-return (&rest things)
5 years ago
"Create yield block, returns last of push()ed outputs of yield() (so, last is first etc)"
`(let ((--yield-vars '())
(--yield-var nil))
(declare (special --yield-vars
--yield-var))
,@things
5 years ago
(apply #'values --yield-var)))
(defmacro yield (value)
"Yield return value `value' if in yield-return(...) block."
5 years ago
`(when (boundp '--yield-vars)
(setf --yield-var (multiple-value-list ,value))
(push (car --yield-var) --yield-vars)))
(defparameter *yield-global-vars* '())
(defmacro yield-return-global (name &rest things)
"Like yield-return but stores values by key in alist and is accessable outside of current lexenv"
`(car (yield-return
(push (cons ,name '()) *yield-global-vars*)
,@things
(yield (cdr (assoc ,name *yield-global-vars*)))
(setf *yield-global-vars* (remove ,name *yield-global-vars* :key #'car :count 1)))))
(defmacro yield-global (name value)
`(when (assoc ,name *yield-global-vars*)
(push ,value (cdr (assoc ,name *yield-global-vars*)))))
;; ---
(defmacro push-unique (thing things &key (key nil) (test #'eql))
"Push if not member"
`(let ((lex-thing ,thing))
(if (member lex-thing ,things :key ,key :test ,test)
(values ,things nil)
(values (push lex-thing ,things) t))))
(defmacro import* (&rest args)
"Import all symbols"
(cons 'progn (mapcan #'(lambda (x) `((import ',x))) args)))
(defmacro shadowing-import* (&rest args)
"Import all symbols (shadowing)"
(cons 'progn (mapcan #'(lambda (x) `((shadowing-import ',x))) args)))
(defmacro import*! (&rest args)
"Map eval then import all returns"
`(mapc #'import (progel ,args)))
(defmacro shadowing-import*! (&rest args)
"Map eval then import all returns (shadowing)"
`(mapc #'shadowing-import (progel ,args)))
(defmacro import*-from (package &rest args)
"Import all symbols from package. NOTE: You shouldn't prefix the symbols with the package name"
(let ((ret (mapcar #'(lambda (x) (read-from-string (strcat (subseq (write-to-string package) 1) ":" (write-to-string x)))) args)))
`(shadowing-import* @,ret)))
;(defun restrain-index (i max)
;
; (if nil
; (if (>= i max) (1- max)
; (if (< i 0) 0
; i))
; i))
(defun many-equals (items &key (test #'eql))
"For some reason equality comparers don't like more than 2 args"
(let ((comp (car items)))
(labels ((compare (x to)
(when (null x) t)
(when (funcall test (car x) to)
(compare (cdr x) to))))
(compare (cdr items) comp))))
(defun many-eql (&rest items)
(many-equals items :test #'eql))
; --- async stuff
5 years ago
(defstruct async-info
thread
handlers
lock)
(defmacro push-handler (name lam)
"Push new handler to current async-info with name"
"NOTE: Should only be used inside an async() body."
5 years ago
`(bt:with-lock-held
((async-info-lock current-async-info))
(push (cons ,name ,lam)
(async-info-handlers current-async-info))))
(defmacro async (&rest form)
"Run form(s) async"
5 years ago
`(let ((current-async-info (make-async-info)))
(setf (async-info-handlers current-async-info) nil)
(setf (async-info-lock current-async-info) (bt:make-lock))
(setf (async-info-thread current-async-info)
(bt:make-thread
#'(lambda ()
,@form)))
current-async-info))
(defun async-info-handler (async name &key (test 'eql))
"Get handler from async-info of name"
5 years ago
(bt:with-lock-held ((async-info-lock async))
(let ((as (assoc name (async-info-handlers async) :test test )))
(and as
(cdr as)))))
(defun wait (handle)
"Wait on async()"
5 years ago
(if (async-info-p handle)
(wait (async-info-thread handle))
(bt:join-thread handle)))
5 years ago
(defun async-kill (handle)
"Kill async()"
5 years ago
(if (async-info-p handle)
(async-kill (async-info-thread handle))
(bt:destroy-thread handle)))
(defun async-alive (handle)
"Is async() alive"
5 years ago
(if (async-info-p handle)
(async-alive (async-info-thread handle))
(bt:thread-alive-p handle)))
(defun val (v) v)
5 years ago
(defun groupn (n list)
"Group list into sublists every `n' items."
5 years ago
(let ((last nil)
(ret nil))
(loop for x in list
for y from 0 below (length list)
do (if (= 0 (mod y n))
(push x last)
(progn
(push x last)
(setf ret (cons (reverse last) ret))
(setf last nil))))
(reverse ret)))
; --- reader macros
(defun sexpr-reader (stream char &key (func 'val))
"Read next token only if S expression, else return as is"
(if (char= (peek-char t stream t nil t) #\()
(values (funcall func (read stream t nil t)) t)
(let ((*readtable* (copy-readtable)))
(set-macro-character char nil)
(values (read-from-string (strcat (string char) (write-to-string (read stream t nil t)))) nil))))
(defun bang-reader (stream char)
(declare (ignore char))
(list (quote not) (read stream t nil t)))
(defun read-delimiter (stream char)
(declare (ignore stream char)))
(defun read-next-until (stream char)
(if (char= (peek-char t stream t nil t) char)
(progn
(read-char stream t nil t) nil)
(read stream t nil t)))
(defun export-reader (stream char)
(declare (ignore char))
(loop for next = (read-next-until stream #\])
while next
collect next into objects
finally (return `(defexport ,@objects))))
(defun top-level-reader (stream char)
(multiple-value-bind (thing okay) (sexpr-reader stream char)
(if okay
(append (list 'eval-when '(:compile-toplevel :load-toplevel :execute)) (list thing))
thing)))
(defun async-reader (stream char)
(multiple-value-bind (thing okay) (sexpr-reader stream char)
(if okay
(cons 'async (list thing))
thing)))
5 years ago
(defun lambda-reader (stream char)
(declare (ignore char))
`(lambda () ,(read stream t nil t)))
(defmacro enable-all-readers ()
5 years ago
"Turn on reader macros"
'(eval-when (:compile-toplevel :load-toplevel :execute)
(push *readtable* *old-readtables*)
(setq *readtable* (copy-readtable))
(set-macro-character #\[ 'export-reader) ;"Exports all in brackets []"
5 years ago
(set-macro-character #\] 'read-delimiter)
(set-macro-character #\$ 'async-reader) ;"Run statement in seperate thread"
5 years ago
(set-macro-character #\¬ 'bang-reader) ;"Negates next statement"
(set-macro-character #\£ 'lambda-reader) ;"Wrap statement in lambda"
(set-macro-character #\€ 'top-level-reader))) ;"Run at compile,load and execute"
(defmacro disable-all-readers()
5 years ago
"Turn off reader macros"
'(eval-when (:compile-toplevel :load-toplevel :execute)
(setq *readtable* (pop *old-readtables*))))
; ((defun map-parallel (func seq &key (map #'mapcar) (split 0.25) (debug nil)) ;;; TODO: make this work lol
; (flet ((dprint (x) (when debug (format t "~S~%" x)) x ))
; "Map over list in parallel"
; (let* ((step (floor (* split (list-length seq))))
; (threads (val-if-or (ceiling (/ 1 split))
; #'(lambda (val)
; (< val step))
; 0)))
; (if (< threads 2)
; (funcall map func seq) ;; no splits
; (let ((threads '())
; (no-threads threads)
; (outputs (make-list threads))
; (left '()))
; (loop for i from 0 below no-threads
; for start from 0 below (list-length seq) by step
; do (progn
; (push (bt:make-thread
; #'(lambda ()
; ;(dprint (list i start (restrain-index (+ step start) (list-length seq) )))
; (rplaca (nthcdr i outputs)
; (funcall map func (slice seq start (+ (1- step) start))))))
; threads)))
; (setf left
; (when (> (mod (list-length seq) step) 0)
; (funcall map func (slice seq (* no-threads step) (+ (* no-threads step) (mod (list-length seq) step))))))
;
; (loop while (apply #'= (cons 0 (mapcar #'(lambda (x) (if (bt:thread-alive-p x) 1 0)) threads))) do
; (dprint
; (mapcar #'bt:thread-alive-p
; threads)))
; (apply #'nconc (append outputs left)))))))
;
;(defun map-parallel-test (&key (length 10001))
; (map-parallel
; #'(lambda (x)
; x)
; (make-list length :initial-element 1)
; :debug t))
; --- others
5 years ago
(defmacro popto (li val &key (test #'eql))
"pop() list <li> until (car li) is equal to <val>, return elements pop()ed in new list"
`(loop while (not (funcall ,test (car ,li) ,val))
collect (pop ,li)))
(defmacro popn (li n)
"pop() list <li> <n> times, return elements pop()ed in a new list."
(if (numberp n)
(list 'let '((tmp 'nil))
(apply #'list 'progn
(loop for x from 1 to n
collect `(setf tmp (cons (pop ,li) tmp))))
'(reverse tmp))
`(loop for x from 1 to ,n collect (pop ,li))))
(defun make-paged-vector (blocksize &key (element-type 'integer))
"Vector that increases size in blocks"
(list (make-array blocksize :element-type element-type :fill-pointer 0 :adjustable t) blocksize 0 1))
(defun paged-vector<-size (vec) (caddr vec))
(defun paged-vector<-blocksize (vec) (cadr vec))
(defun paged-vector<-blocks (vec) (cadddr vec))
(defmacro paged-vector->push (vec elem)
"add <elem> to end, extending if needed"
`(if (>= (1+ (mod (paged-vector<-size ,vec) (paged-vector<-blocksize ,vec))) (paged-vector<-blocksize ,vec))
(progn
(adjust-array (car ,vec)
(* (1+ (paged-vector<-blocks ,vec))
(paged-vector<-blocksize ,vec)))
(incf (cadddr ,vec))
(incf (caddr ,vec))
(vector-push ,elem (car ,vec))
,vec)
(progn (incf (caddr ,vec)) (vector-push ,elem (car ,vec)) ,vec)))
(defun make-paged-vector-s (elements blocksize)
"make-paged-vector with default elements"
(let ((out (make-paged-vector blocksize)))
(mapc #'(lambda (x) (paged-vector->push out x)) elements) out))
(defmacro paged-vector<- (vec) `(car ,vec))
) ;; -- end export