Added Common Lisp Bits & Pieces.
This commit is contained in:
parent
5edc89248b
commit
939b0e11bc
Binary file not shown.
|
@ -0,0 +1,4 @@
|
|||
(defun hello-world ()
|
||||
(format t "Hello, world!"))
|
||||
|
||||
(hello-world)
|
|
@ -0,0 +1,2 @@
|
|||
|
||||
((:TITLE "Jailbreak" :ARTIST "Thin Lizzy" :RATING 10 :RIPPED T) (:TITLE "Lateralus" :ARTIST "TOOL" :RATING 10 :RIPPED T) (:TITLE "Poopenfarten" :ARTIST "Zweibrüder" :RATING 11 :RIPPED T) (:TITLE "Ride The Lightning" :ARTIST "Metallica" :RATING 9 :RIPPED T) (:TITLE "The Sound Of Rancid Juices Sloshing Around Your Coffin" :ARTIST "Last Days Of Humanity" :RATING 5 :RIPPED T))
|
Binary file not shown.
|
@ -0,0 +1,94 @@
|
|||
;;;; Simple CD Database program:
|
||||
(defvar *cd-database* nil "The current state of the CD database.")
|
||||
|
||||
(defun make-cd (title artist rating ripped)
|
||||
"Creates a CD record."
|
||||
(list :title title :artist artist :rating rating :ripped ripped))
|
||||
|
||||
(defun add-cd (cd)
|
||||
"Adds a CD record to the database."
|
||||
(push cd *cd-database*) (setf *cd-database* (sort *cd-database* #'cd-sort-by-title)))
|
||||
|
||||
(defun print-cd-database (&optional database)
|
||||
"Prints the current CD database, or, optionally, a passed in database."
|
||||
(if database
|
||||
(dolist (cd database)
|
||||
(format t "~{~a: ~10t~a~%~}~%" cd))
|
||||
(dolist (cd *cd-database*)
|
||||
(format t "~{~a: ~10t~a~%~}~%" cd))))
|
||||
|
||||
(defun prompt-read (prompt)
|
||||
"Prompts for a single line of user input."
|
||||
(format *query-io* "~a: " prompt)
|
||||
(force-output *query-io*)
|
||||
(read-line *query-io*))
|
||||
|
||||
(defun prompt-for-cd ()
|
||||
"Creates a CD record using user input."
|
||||
(make-cd
|
||||
(prompt-read "Title")
|
||||
(prompt-read "Artist")
|
||||
(or (parse-integer (prompt-read "Rating") :junk-allowed t) 0)
|
||||
(y-or-n-p "Ripped [y/n]: ")))
|
||||
|
||||
(defun add-cds ()
|
||||
"Adds one or more CDs to the database with user input."
|
||||
(loop (add-cd (prompt-for-cd))
|
||||
(if (not (y-or-n-p "Another? [y/n]: ")) (return))))
|
||||
|
||||
(defun cd-sort-by-title (a b)
|
||||
"Sorts two CD records alphabetically."
|
||||
(string< (getf a :title) (getf b :title)))
|
||||
|
||||
(defun save-cds (filename)
|
||||
"Saves the CD database to a file."
|
||||
(with-open-file
|
||||
(file-output filename
|
||||
:direction :output
|
||||
:if-exists :supersede)
|
||||
(with-standard-io-syntax
|
||||
(print *cd-database* file-output))))
|
||||
|
||||
(defun read-cds (filename)
|
||||
"Loads a CD database from a file."
|
||||
(with-open-file
|
||||
(file-input filename)
|
||||
(with-standard-io-syntax
|
||||
(print-cd-database (setf *cd-database* (read file-input))))))
|
||||
|
||||
(defun delete-cds (selector-function)
|
||||
"Deletes CD records according to a selector function."
|
||||
(print-cd-database (setq *cd-database* (remove-if selector-function *cd-database*))))
|
||||
|
||||
(defun select (selector-function)
|
||||
"Select records from a database based on a selector function."
|
||||
(print-cd-database (remove-if-not selector-function *cd-database*)))
|
||||
|
||||
(defun update (selector-function &key title artist rating (ripped nil ripped-p))
|
||||
"Update selected records in a database."
|
||||
(setf *cd-database*
|
||||
(mapcar
|
||||
#'(lambda (row)
|
||||
(when (funcall selector-function row)
|
||||
(if title (setf (getf row :title) title))
|
||||
(if artist (setf (getf row :artist) artist))
|
||||
(if rating (setf (getf row :rating) rating))
|
||||
(if ripped-p (setf (getf row :ripped) ripped)))
|
||||
row) *cd-database*)))
|
||||
|
||||
(defun make-comparison-expression (field value)
|
||||
"Create a comparison function for a field and a value in a plist."
|
||||
`(equal (getf cd ,field) ,value))
|
||||
|
||||
(defun make-comparisons-list (fields)
|
||||
"Create a list of plist comparison functions."
|
||||
(loop while fields
|
||||
collecting (make-comparison-expression (pop fields) (pop fields))))
|
||||
|
||||
(defmacro where (&rest clauses)
|
||||
"Create an expression where all plist comparisons must be true."
|
||||
`#'(lambda (row) (and ,@(make-comparisons-list clauses))))
|
||||
|
||||
;; Load up the CD database on startup:
|
||||
(read-cds "~/.cds.db")
|
||||
(print-cd-database)
|
Binary file not shown.
|
@ -0,0 +1,38 @@
|
|||
(defpackage :BARRA-TESTING-FRAMEWORK
|
||||
(:use :common-lisp))
|
||||
|
||||
(in-package "BARRA-TESTING-FRAMEWORK")
|
||||
|
||||
(defvar *test-name* nil)
|
||||
|
||||
(defmacro with-gensyms ((&rest names) &body body)
|
||||
`(let ,(loop for n in names collect `(,n (gensym)))
|
||||
,@body))
|
||||
|
||||
(defmacro deftest (name parameters &body body)
|
||||
"Define a test function. Within a test function we can call
|
||||
other test functions or use 'check' to run individual test
|
||||
cases."
|
||||
`(defun ,name ,parameters
|
||||
(let ((*test-name* (append *test-name* (list ',name))))
|
||||
,@body)))
|
||||
|
||||
(defmacro check (&body forms)
|
||||
"Run each expression in 'forms' as a test case."
|
||||
`(combine-results
|
||||
,@(loop for f in forms collect `(report-result ,f ',f))))
|
||||
|
||||
(defmacro combine-results (&body forms)
|
||||
"Combine the results (as booleans) of evaluating 'forms' in order."
|
||||
(with-gensyms (result)
|
||||
`(let ((,result t))
|
||||
,@(loop for f in forms collect `(unless ,f (setf ,result nil)))
|
||||
,result)))
|
||||
|
||||
(defun report-result (result form)
|
||||
"Report the results of a single test case. Called by 'check'."
|
||||
(format t "~:[FAIL~;PASS~] - ~a: ~a~%" result *test-name* form)
|
||||
result)
|
||||
|
||||
(let ((pack (find-package :foo)))
|
||||
(do-all-symbols (sym pack) (when (eql (symbol-package sym) pack) (export sym))))
|
Binary file not shown.
|
@ -0,0 +1,99 @@
|
|||
(defpackage BARRA-FILENAME-PACKAGE
|
||||
(:use common-lisp)
|
||||
(:export
|
||||
:component-present-p
|
||||
:directory-pathname-p
|
||||
:pathname-as-directory
|
||||
:pathname-as-file
|
||||
:directory-wildcard
|
||||
:file-exists-p
|
||||
:list-directory
|
||||
:walk-directory))
|
||||
|
||||
(in-package BARRA-FILENAME-PACKAGE)
|
||||
|
||||
(defun component-present-p (value)
|
||||
(and value (not (eql value :unspecified))))
|
||||
|
||||
(defun directory-pathname-p (pathname)
|
||||
(and
|
||||
(not (component-present-p (pathname-name pathname)))
|
||||
(not (component-present-p (pathname-type pathname)))))
|
||||
|
||||
(defun pathname-as-directory (path)
|
||||
(let ((pathname (pathname path)))
|
||||
(if
|
||||
(directory-pathname-p pathname)
|
||||
pathname
|
||||
(make-pathname
|
||||
:host (pathname-host pathname)
|
||||
:device (pathname-device pathname)
|
||||
:directory (append (or (pathname-directory pathname) (file-namestring pathname))
|
||||
(list (file-namestring pathname)))
|
||||
:name nil
|
||||
:type nil
|
||||
:defaults pathname))))
|
||||
|
||||
(defun directory-wildcard (dirname)
|
||||
(make-pathname
|
||||
:name :wild
|
||||
:type #-clisp :wild #+clisp nil
|
||||
:defaults (pathname-as-directory dirname)))
|
||||
|
||||
(defun list-directory (dirname)
|
||||
(when (wild-pathname-p dirname)
|
||||
(error "Can only list concrete directory names."))
|
||||
(directory (directory-wildcard dirname)))
|
||||
|
||||
(defun file-exists-p (pathname)
|
||||
#+(or sbcl lispworks openmcl)
|
||||
(probe-file pathname)
|
||||
|
||||
#+(or allegro cmu)
|
||||
(or (probe-file (pathname-as-directory pathname))
|
||||
(probe-file pathname))
|
||||
|
||||
#+clisp
|
||||
(or (ignore-errors
|
||||
(probe-file (pathname-as-file pathname)))
|
||||
(ignore-errors
|
||||
(let ((directory-form (pathname-as-directory pathname)))
|
||||
(when (ext:probe-directory directory-form)
|
||||
directory-form))))
|
||||
|
||||
#-(or sbcl cmu lispworks openmcl allegro clisp)
|
||||
(error "list-directory not implemented"))
|
||||
|
||||
#+clisp
|
||||
(defun clisp-subdirectories-wildcard (wildcard)
|
||||
(make-pathname
|
||||
:directory (append (pathname-directory wildcard) (list :wild))
|
||||
:name nil
|
||||
:type nil
|
||||
:defaults wildcard))
|
||||
|
||||
(defun pathname-as-file (name)
|
||||
(let ((pathname (pathname name)))
|
||||
(when (wild-pathname-p pathname)
|
||||
(error "Can't reliably convert wild pathnames."))
|
||||
(if (directory-pathname-p name)
|
||||
(let* ((directory (pathname-directory pathname))
|
||||
(name-and-type (pathname (first (last directory)))))
|
||||
(make-pathname
|
||||
:directory (butlast directory)
|
||||
:name (pathname-name name-and-type)
|
||||
:type (pathname-type name-and-type)
|
||||
:defaults pathname))
|
||||
pathname)))
|
||||
|
||||
(defun walk-directory (dirname fn &key directories (test (constantly t)))
|
||||
(labels
|
||||
((walk (name)
|
||||
(cond
|
||||
((directory-pathname-p name)
|
||||
(when (and directories (funcall test name))
|
||||
(funcall fn name))
|
||||
(dolist (x (list-directory name)) (walk x)))
|
||||
((funcall test name) (funcall fn name)))))
|
||||
(walk (pathname-as-directory dirname))))
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
(ql:quickload :cl-cffi-gtk)
|
||||
|
||||
; Main window
|
||||
(defvar window (make-instance 'gtk:gtk-window :type :toplevel :title "GTK Test"))
|
||||
(defvar vbox (make-instance 'gtk:gtk-box :orientation :vertical
|
||||
:spacing 10
|
||||
:margin 10))
|
||||
|
||||
(defvar text-panel-box (make-instance 'gtk:gtk-box :orientation :horizontal :spacing 1 :margin 10))
|
||||
(defvar panel-left-window (make-instance 'gtk:gtk-scrolled-window :height-request 500 :width-request 100))
|
||||
(defvar panel-left (make-instance 'gtk:gtk-text-view :height-request 300 :width-request 100 :wrap-mode :word-char :vscroll-policy :natural))
|
||||
(defvar panel-right-window (make-instance 'gtk:gtk-scrolled-window :height-request 500 :width-request 300))
|
||||
(defvar panel-right (make-instance 'gtk:gtk-text-view :height-request 300 :width-request 300 :wrap-mode :word-char :vscroll-policy :natural))
|
||||
|
||||
(gtk:gtk-box-pack-start vbox text-panel-box)
|
||||
(gtk:gtk-container-add panel-left-window panel-left)
|
||||
(gtk:gtk-box-pack-start text-panel-box panel-left-window)
|
||||
(gtk:gtk-container-add panel-right-window panel-right)
|
||||
(gtk:gtk-box-pack-end text-panel-box panel-right-window)
|
||||
|
||||
(gtk:within-main-loop
|
||||
; Quit program when window closed
|
||||
(gobject:g-signal-connect window "destroy" (lambda (widget)
|
||||
(declare (ignore widget))
|
||||
(gtk:leave-gtk-main)))
|
||||
; Display GUI
|
||||
(gtk:gtk-container-add window vbox)
|
||||
(gtk:gtk-widget-show-all window))
|
||||
|
||||
(loop
|
||||
(sleep 3)
|
||||
(setf (gtk:gtk-text-buffer-text (gtk:gtk-text-view-buffer panel-right)) (format nil "~a~a" (gtk:gtk-text-buffer-text (gtk:gtk-text-view-buffer panel-right)) "
|
||||
Hello, world!
|
||||
")))
|
|
@ -0,0 +1,88 @@
|
|||
(ql:quickload '(clack websocket-driver alexandria))
|
||||
|
||||
(defvar *connections* (make-hash-table))
|
||||
|
||||
(defun handle-new-connection (con)
|
||||
(setf (gethash con *connections*)
|
||||
(format nil "user-~a" (random 100000))))
|
||||
|
||||
(defun broadcast-to-room (connection message)
|
||||
(let ((message (format nil "~a: ~a"
|
||||
(gethash connection *connections*)
|
||||
message)))
|
||||
(loop :for con :being :the :hash-key :of *connections* :do
|
||||
(websocket-driver:send con message))))
|
||||
|
||||
(defun handle-close-connection (connection)
|
||||
(let ((message (format nil " .... ~a has left."
|
||||
(gethash connection *connections*))))
|
||||
(remhash connection *connections*)
|
||||
(loop :for con :being :the :hash-key :of *connections* :do
|
||||
(websocket-driver:send con message))))
|
||||
|
||||
(defun chat-server (env)
|
||||
(let ((ws (websocket-driver:make-server env)))
|
||||
(websocket-driver:on :open ws
|
||||
(lambda () (handle-new-connection ws)))
|
||||
|
||||
(websocket-driver:on :message ws
|
||||
(lambda (msg)
|
||||
(broadcast-to-room ws msg)))
|
||||
|
||||
(websocket-driver:on :close ws
|
||||
(lambda (&key code reason)
|
||||
(declare (ignore code reason))
|
||||
(handle-close-connection ws)))
|
||||
(lambda (responder)
|
||||
(declare (ignore responder))
|
||||
(websocket-driver:start-connection ws))))
|
||||
|
||||
(defvar *html*
|
||||
"<!doctype html>
|
||||
|
||||
<html lang=\"en\">
|
||||
<head>
|
||||
<meta charset=\"utf-8\">
|
||||
<title>LISP-CHAT</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<ul id=\"chat-echo-area\">
|
||||
</ul>
|
||||
<div style=\"position:fixed; bottom:0;\">
|
||||
<input id=\"chat-input\" placeholder=\"say something\" >
|
||||
</div>
|
||||
<script>
|
||||
window.onload = function () {
|
||||
const inputField = document.getElementById(\"chat-input\");
|
||||
|
||||
function receivedMessage(msg) {
|
||||
let li = document.createElement(\"li\");
|
||||
li.textContent = msg.data;
|
||||
document.getElementById(\"chat-echo-area\").appendChild(li);
|
||||
}
|
||||
|
||||
const ws = new WebSocket(\"ws://localhost:12345/\");
|
||||
ws.addEventListener('message', receivedMessage);
|
||||
|
||||
inputField.addEventListener(\"keyup\", (evt) => {
|
||||
if (evt.key === \"Enter\") {
|
||||
ws.send(evt.target.value);
|
||||
evt.target.value = \"\";
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
")
|
||||
|
||||
(defun client-server (env)
|
||||
(declare (ignore env))
|
||||
`(200 (:content-type "text/html")
|
||||
(,*html*)))
|
||||
|
||||
(defvar *chat-handler* (clack:clackup #'chat-server :port 12345))
|
||||
(defvar *client-handler* (clack:clackup #'client-server :port 8080))
|
||||
|
Loading…
Reference in New Issue