<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="https://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:gd='http://schemas.google.com/g/2005' xmlns:georss='http://www.georss.org/georss' xmlns:thr='http://purl.org/syndication/thread/1.0' xmlns:blogger='http://schemas.google.com/blogger/2008'><id>tag:blogger.com,1999:blog-4168503339888369834</id><updated>2015-09-16T15:32:20.536-04:00</updated><category term='tr1'/><category term='build'/><category term='spam?'/><category term='government'/><category term='source control'/><category term='personal'/><category term='meta'/><category term='rxvt'/><category term='slime'/><category term='bitcoin'/><category term='lisp'/><category term='lambda'/><category term='boost'/><category term='dependencyinjection'/><category term='gtkmm'/><category term='rant'/><category term='nvidia'/><category term='vancpp'/><category term='computers'/><category term='clang'/><category term='asdf'/><category term='opengl'/><category term='parenscript'/><category term='code'/><category term='quit'/><category term='c++1x'/><category term='cl-opengl'/><category term='deepinthought'/><category term='ninja'/><category term='ubuntu'/><category term='research'/><category term='afk'/><category term='bzr'/><category term='java'/><category term='clsql'/><category term='business'/><category term='broken'/><category term='qt'/><category term='python'/><category term='google'/><category term='clbuild'/><category term='hunchentoot'/><category term='soapbox'/><category term='git'/><category term='gtk'/><category term='wla'/><category term='bjam'/><category term='cl-who'/><category term='docbook'/><category term='wtf'/><category term='linux'/><category term='mercurial'/><category term='sbcl'/><category term='nothingtoseehere'/><category term='cl-selenium'/><category term='dontreadme'/><category term='c++0x'/><category term='c++'/><category term='boostcon'/><category term='i&apos;m on the list now'/><category term='30day'/><category term='scons'/><category term='di'/><category term='factor'/><category term='psa'/><category term='weblocks'/><category term='svn'/><category term='ajax'/><category term='strictlybusiness'/><category term='subversion'/><category term='futurereference'/><category term='serialization'/><category term='gui'/><category term='development'/><category term='buildingsoftware'/><category term='mfc465-cn'/><category term='money'/><category term='svk'/><category term='notaloser'/><category term='software'/><category term='testing'/><category term='emacs'/><title type='text'>sizeof(uint32t)</title><subtitle type='html'>Dodheim reads only four blogs. This is one of them.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://uint32t.blogspot.com/feeds/posts/full/-/lisp'/><link rel='self' type='application/atom+xml' href='http://uint32t.blogspot.com/feeds/posts/full/-/lisp'/><link rel='alternate' type='text/html' href='http://uint32t.blogspot.com/search/label/lisp'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://uint32t.blogspot.com/feeds/posts/full/-/lisp/-/lisp?start-index=26&amp;max-results=25'/><author><name>Sohail Somani</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-t-OCQSnHolk/AAAAAAAAAAI/AAAAAAAABRU/cl0ejUW86Yk/s32-c/photo.jpg'/></author><generator version='7.00' uri='https://www.blogger.com'>Blogger</generator><openSearch:totalResults>41</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-4168503339888369834.post-7231893924325400141</id><published>2009-03-19T18:51:00.000-04:00</published><updated>2009-03-19T18:56:34.894-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='qt'/><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><title type='text'>A seemingly complete Qt API for CL</title><content type='html'>Just came across this library called &lt;a href="http://common-lisp.net/project/commonqt/"&gt;CommonQT&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;It is more complete than the API I developed earlier. It is also using the KDE Smoke libraries which make it damn easy to create the API on the fly.&lt;br /&gt;&lt;br /&gt;Very exciting.</content><link rel='replies' type='application/atom+xml' href='http://uint32t.blogspot.com/feeds/7231893924325400141/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=4168503339888369834&amp;postID=7231893924325400141' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/4168503339888369834/posts/default/7231893924325400141'/><link rel='self' type='application/atom+xml' href='http://uint32t.blogspot.com/feeds/posts/default/7231893924325400141'/><link rel='alternate' type='text/html' href='http://uint32t.blogspot.com/2009/03/seemingly-complete-qt-api-for-cl.html' title='A seemingly complete Qt API for CL'/><author><name>Sohail Somani</name><uri>https://plus.google.com/106964604256511867632</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-t-OCQSnHolk/AAAAAAAAAAI/AAAAAAAABRU/cl0ejUW86Yk/s32-c/photo.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4168503339888369834.post-4344157724641342161</id><published>2008-07-31T11:06:00.000-04:00</published><updated>2008-08-02T10:41:42.927-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='qt'/><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><title type='text'>Qt/Lisp: more progress</title><content type='html'>I've replaced the unsightly &lt;tt&gt;(make-instance 'qstring :ch "Foo")&lt;/tt&gt; with a reader macro: &lt;tt&gt;#q"Foo"&lt;/tt&gt;. Ideally we'd use &lt;tt&gt;cl:string&lt;/tt&gt; and &lt;tt&gt;qstring&lt;/tt&gt; interchangeably but right now its too much of a PITA to make that work.&lt;br /&gt;&lt;br /&gt;I've also made it possible to use closures for slots (see example below.) Frickin sweet.&lt;br /&gt;&lt;br /&gt;Anyway, I started getting into the &lt;a href="http://doc.trolltech.com/4.3/mainwindows-dockwidgets.html"&gt;Dock Widgets example&lt;/a&gt; but lost interest after seeing all the code. &lt;a href="http://paste.lisp.org/display/64482"&gt;Here&lt;/a&gt; is the code so far. Not very concise yet but will get there. Once I get into make-it-less-ugly mode, I will probably copy what Paul did with &lt;a href="http://lisp-cffi-qt4.sourceforge.net/"&gt;EQL&lt;/a&gt; as he calls it now.&lt;br /&gt;&lt;br /&gt;&lt;s&gt;Also the :foreign-pointer thingy will go away when I get around to adding cffi type translations.&lt;/s&gt;</content><link rel='replies' type='application/atom+xml' href='http://uint32t.blogspot.com/feeds/4344157724641342161/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=4168503339888369834&amp;postID=4344157724641342161' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/4168503339888369834/posts/default/4344157724641342161'/><link rel='self' type='application/atom+xml' href='http://uint32t.blogspot.com/feeds/posts/default/4344157724641342161'/><link rel='alternate' type='text/html' href='http://uint32t.blogspot.com/2008/07/qtlisp-more-progress.html' title='Qt/Lisp: more progress'/><author><name>Sohail Somani</name><uri>https://plus.google.com/106964604256511867632</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-t-OCQSnHolk/AAAAAAAAAAI/AAAAAAAABRU/cl0ejUW86Yk/s32-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4168503339888369834.post-1790320458741790045</id><published>2008-07-22T13:04:00.000-04:00</published><updated>2008-07-27T19:59:05.580-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='qt'/><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><title type='text'>Simple CFFI - Qt4 integration attempt</title><content type='html'>Update: Paul has sent me his code. He calls it "EQL" for ECL and Qt. Or something like that anyway! I have put it up &lt;a href="http://taggedtype.net/~sohail/lisp/eql-2008-07-24.tgz"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;The author of the &lt;a href="http://lisp-cffi-qt4.sourceforge.net/"&gt;Simple CFFI - Qt4 integration attempt&lt;/a&gt; has contacted me to let me know that a modification of the above linked software is in commercial use. I have prodded him to release his modified version. He says he'll do it Real Soon Now (TM).&lt;br /&gt;&lt;br /&gt;I don't know if he is reading this blog but maybe a little pressure will help. Just kidding!&lt;br /&gt;&lt;br /&gt;What I like most about his method is the use of the introspection capabilities of Qt. Compared to the duct tape and glue I used, his is a lot more elegant. Currently, you cannot extend Qt in Lisp (via inheritance) but all the other goodies are still there. Indeed he tells me that for his application, he extended Qt from within C++ and used these extensions from his Lisp.&lt;br /&gt;&lt;br /&gt;I look forward to the update and to using the library.</content><link rel='replies' type='application/atom+xml' href='http://uint32t.blogspot.com/feeds/1790320458741790045/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=4168503339888369834&amp;postID=1790320458741790045' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/4168503339888369834/posts/default/1790320458741790045'/><link rel='self' type='application/atom+xml' href='http://uint32t.blogspot.com/feeds/posts/default/1790320458741790045'/><link rel='alternate' type='text/html' href='http://uint32t.blogspot.com/2008/07/simple-cffi-qt4-integration-attempt.html' title='Simple CFFI - Qt4 integration attempt'/><author><name>Sohail Somani</name><uri>https://plus.google.com/106964604256511867632</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-t-OCQSnHolk/AAAAAAAAAAI/AAAAAAAABRU/cl0ejUW86Yk/s32-c/photo.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4168503339888369834.post-3159387250960714266</id><published>2008-07-18T13:22:00.000-04:00</published><updated>2008-12-08T21:20:03.909-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='qt'/><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><title type='text'>Qt/Lisp: looking for feedback</title><content type='html'>Update: You can download the generated code from &lt;a href="http://taggedtype.net/~sohail/package.tgz"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;The tubes were down this morning, so I managed to get up to tutorial 6 in my Qt/Lisp API. It covers about 95% of the API by design so most of the non-extension tutorials/examples should work.&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt; &lt;li&gt;&lt;a href="http://paste.lisp.org/display/63901"&gt;Tutorial 1:&lt;/a&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_NJChfbPZ2fA/SIDeB2YpWsI/AAAAAAAAAC4/JjX-aSairMI/s1600-h/tutorial1.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://3.bp.blogspot.com/_NJChfbPZ2fA/SIDeB2YpWsI/AAAAAAAAAC4/JjX-aSairMI/s200/tutorial1.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5224419691109898946" /&gt;&lt;/a&gt;&lt;/li&gt;&lt;br /&gt; &lt;li&gt;&lt;a href="http://paste.lisp.org/display/63902"&gt;Tutorial 2&lt;/a&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_NJChfbPZ2fA/SIDeT6lTQ5I/AAAAAAAAADA/n_bsHvr8TnQ/s1600-h/tutorial2.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://1.bp.blogspot.com/_NJChfbPZ2fA/SIDeT6lTQ5I/AAAAAAAAADA/n_bsHvr8TnQ/s200/tutorial2.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5224420001474364306" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt; &lt;li&gt;&lt;a href="http://paste.lisp.org/display/63903"&gt;Tutorial 3&lt;/a&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_NJChfbPZ2fA/SIDeUFoum0I/AAAAAAAAADI/k5WFqsLpLD8/s1600-h/tutorial3.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://1.bp.blogspot.com/_NJChfbPZ2fA/SIDeUFoum0I/AAAAAAAAADI/k5WFqsLpLD8/s200/tutorial3.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5224420004441529154" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt; &lt;li&gt;&lt;a href="http://paste.lisp.org/display/63904"&gt;Tutorial 4&lt;/a&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_NJChfbPZ2fA/SIDeUCT_JTI/AAAAAAAAADQ/swwXN5ZydAM/s1600-h/tutorial4.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://2.bp.blogspot.com/_NJChfbPZ2fA/SIDeUCT_JTI/AAAAAAAAADQ/swwXN5ZydAM/s200/tutorial4.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5224420003549226290" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt; &lt;li&gt;&lt;a href="http://paste.lisp.org/display/63905"&gt;Tutorial 5&lt;/a&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_NJChfbPZ2fA/SIDeUUJJlDI/AAAAAAAAADY/A8bVadsAK6g/s1600-h/tutorial5.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://1.bp.blogspot.com/_NJChfbPZ2fA/SIDeUUJJlDI/AAAAAAAAADY/A8bVadsAK6g/s200/tutorial5.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5224420008335610930" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt; &lt;li&gt;&lt;a href="http://paste.lisp.org/display/63906"&gt;Tutorial 6&lt;/a&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_NJChfbPZ2fA/SIDeUQ3qmwI/AAAAAAAAADg/EeaYKCV29kI/s1600-h/tutorial6.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://2.bp.blogspot.com/_NJChfbPZ2fA/SIDeUQ3qmwI/AAAAAAAAADg/EeaYKCV29kI/s200/tutorial6.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5224420007456971522" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;I would like to hear your complaints, suggestions, feedback if you have any.&lt;br /&gt;&lt;br /&gt;Note that this API is a first draft and I thought it better to have something out there than wait until everything is perfect.&lt;br /&gt;&lt;br /&gt;To start with, my current complaints:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt; &lt;li&gt;(in-package :qt) -&gt; Nothing is actually exported from the package yet :-)&lt;/li&gt;&lt;br /&gt; &lt;li&gt;&lt;s&gt;(make-instance 'qstring ...) -&gt; A really verbose way to pass a string! Should convert lisp strings to qstrings automagically.&lt;/s&gt;&lt;/li&gt;&lt;br /&gt; &lt;li&gt;Naming: instead of qpush-button, q:push-button&lt;/li&gt;&lt;br /&gt; &lt;li&gt;No way to extend Qt from within Lisp yet (&lt;a href="http://doc.trolltech.com/4.3/tutorial-t7.html"&gt;tutorial 7&lt;/a&gt; won't work)&lt;/li&gt;&lt;br /&gt; &lt;li&gt;&lt;s&gt;No way to attach arbitrary functions as slots&lt;/s&gt;&lt;/li&gt;&lt;br /&gt; &lt;li&gt;Not Lispy enough.&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;I expect to address most of these at some point though the "Not Lispy enough" complaint is a bit subjective.</content><link rel='replies' type='application/atom+xml' href='http://uint32t.blogspot.com/feeds/3159387250960714266/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=4168503339888369834&amp;postID=3159387250960714266' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/4168503339888369834/posts/default/3159387250960714266'/><link rel='self' type='application/atom+xml' href='http://uint32t.blogspot.com/feeds/posts/default/3159387250960714266'/><link rel='alternate' type='text/html' href='http://uint32t.blogspot.com/2008/07/qtlisp-looking-for-feedback.html' title='Qt/Lisp: looking for feedback'/><author><name>Sohail Somani</name><uri>https://plus.google.com/106964604256511867632</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-t-OCQSnHolk/AAAAAAAAAAI/AAAAAAAABRU/cl0ejUW86Yk/s32-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_NJChfbPZ2fA/SIDeB2YpWsI/AAAAAAAAAC4/JjX-aSairMI/s72-c/tutorial1.png' height='72' width='72'/><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4168503339888369834.post-8492558566909423275</id><published>2008-05-29T01:18:00.000-04:00</published><updated>2008-05-29T01:46:07.562-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='parenscript'/><title type='text'>YUI and Parenscript</title><content type='html'>&lt;a href="http://common-lisp.net/project/parenscript/"&gt;Parenscript&lt;/a&gt; is a nice little mini-language that lets you compile Lisp to JavaScript. Sort of like Google does with &lt;a href="http://code.google.com/webtoolkit/"&gt;GWT&lt;/a&gt; and the language to end all languages: Java.&lt;br /&gt;&lt;br /&gt;Parenscript code looks like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;(ps:ps&lt;br /&gt;  (defun say-hi ()&lt;br /&gt;    (alert "Hi!"))&lt;br /&gt;  (say-hi))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The macro &lt;tt&gt;ps:ps&lt;/tt&gt; compiles the Lisp code to a JS string which looks like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;function sayHi() {&lt;br /&gt;    alert('Hi!');&lt;br /&gt;};&lt;br /&gt;sayHi();&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Notice the conversion from &lt;tt&gt;say-hi&lt;/tt&gt; to &lt;tt&gt;sayHi&lt;/tt&gt;. I love the Common Lisp naming conventions. They make so much sense and I miss them everywhere else. Obviously the writers of Parenscript agreed with me so they convert each symbol they see into a JS symbol which keeps things looking mostly neat.&lt;br /&gt;&lt;br /&gt;The major conventions are (I don't know that there are more):&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;  &lt;li&gt;&lt;tt&gt;*foo*&lt;/tt&gt; becomes &lt;tt&gt;FOO&lt;/tt&gt;&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;&lt;tt&gt;foo-bar&lt;/tt&gt; becomes &lt;tt&gt;fooBar&lt;/tt&gt;&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;&lt;tt&gt;foo.*bar&lt;/tt&gt; becomes &lt;tt&gt;foo.Bar&lt;/tt&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;Anyhoo, &lt;a href="http://developer.yahoo.com/yui/"&gt;YUI&lt;/a&gt; is a largish but nice JavaScript library that one can use for creating quite interactive web applications.&lt;br /&gt;&lt;br /&gt;The problem is that YUI has names like &lt;tt&gt;YAHOO.util.Event.onDOMReady&lt;/tt&gt; which is a handful to type even in JavaScript. If you try converting this symbol via ParenScript you will be in for a surprise (try it!) Anyway, there are a couple of ways to write this in ParenScript and keep the casing intact without losing your hair:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt; &lt;li&gt;&lt;tt&gt;*yahoo*.util.*event.on-d-o-m-ready&lt;/tt&gt;&lt;/li&gt;&lt;br /&gt; &lt;li&gt;&lt;tt&gt;|:YAHOO.util.:Event.:onDOMReady|&lt;/tt&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;This works because the bars prevent the reader from downcasing or upcasing the symbol and ParenScript promises that it will leave symbols that begin with a colon alone.&lt;br /&gt;&lt;br /&gt;Even so, this is still a PITA to type. What I do is I have a wrapper script that gets compiled once with short names for all of these. So for the above example, using Hunchentoot:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;(defin-easy-handler (my-yui :uri "/ps/my-yui.ps") ()&lt;br /&gt;  (ps:ps&lt;br /&gt;    (defun yui-on-dom-ready (fn) &lt;br /&gt;      (|:YAHOO.util.:Event.:onDOMReady| fn))&lt;br /&gt;    (defvar yui-some-other-funky-name |:YAHOO.util.:FunkyChickens|)))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Yay.</content><link rel='replies' type='application/atom+xml' href='http://uint32t.blogspot.com/feeds/8492558566909423275/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=4168503339888369834&amp;postID=8492558566909423275' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/4168503339888369834/posts/default/8492558566909423275'/><link rel='self' type='application/atom+xml' href='http://uint32t.blogspot.com/feeds/posts/default/8492558566909423275'/><link rel='alternate' type='text/html' href='http://uint32t.blogspot.com/2008/05/yui-and-parenscript.html' title='YUI and Parenscript'/><author><name>Sohail Somani</name><uri>https://plus.google.com/106964604256511867632</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-t-OCQSnHolk/AAAAAAAAAAI/AAAAAAAABRU/cl0ejUW86Yk/s32-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4168503339888369834.post-843042658649741889</id><published>2008-05-14T03:36:00.000-04:00</published><updated>2008-05-14T03:42:53.059-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><title type='text'>Lisp compiler for .NET</title><content type='html'>I've always wanted to write a compiler for Lisp and .NET. And tonight I did both! Ok I lie. It only handles strings right now but my plan is to add some type inference soon and see how things go. This would not be any sort of faithful lisp implementation as it would be a bearable skin on .NET. To me, that would be worth the work.&lt;br /&gt;&lt;br /&gt;Input:&lt;br /&gt;&lt;pre class="lisp"&gt;&lt;br /&gt;(import &lt;span class="string"&gt;&amp;quot;mscorlib&amp;quot;&lt;/span&gt;)&lt;br /&gt;(assembly &lt;span class="string"&gt;&amp;quot;hello&amp;quot;&lt;/span&gt;)&lt;br /&gt;&lt;br /&gt;(&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;print-it&lt;/span&gt; ()&lt;br /&gt;  (print &lt;span class="string"&gt;&amp;quot;Hello! How are you?&amp;quot;&lt;/span&gt;)&lt;br /&gt;  (print &lt;span class="string"&gt;&amp;quot;I am fine!&amp;quot;&lt;/span&gt;))&lt;br /&gt;&lt;br /&gt;(&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;main&lt;/span&gt; ()&lt;br /&gt;  (.d entrypoint)&lt;br /&gt;  (print-it))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;MSIL output:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;.assembly extern mscorlib {}&lt;br /&gt;.assembly hello {}&lt;br /&gt;.method static public void PRINT_IT () cil managed&lt;br /&gt;{&lt;br /&gt;.maxstack 1&lt;br /&gt;ldstr "Hello! How are you?"&lt;br /&gt;call void [mscorlib]System.Console::WriteLine(class System.String)&lt;br /&gt;ldstr "I am fine!"&lt;br /&gt;call void [mscorlib]System.Console::WriteLine(class System.String)&lt;br /&gt;ret&lt;br /&gt;}&lt;br /&gt;.method static public void MAIN () cil managed&lt;br /&gt;{&lt;br /&gt;.maxstack 1&lt;br /&gt;.entrypoint&lt;br /&gt;call void PRINT_IT&lt;br /&gt;ret&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Program output:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;sohail@dev-ubuntu-wks:/tmp$ mono foo.exe &lt;br /&gt;Hello! How are you?&lt;br /&gt;I am fine!&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now I can go to sleep!</content><link rel='replies' type='application/atom+xml' href='http://uint32t.blogspot.com/feeds/843042658649741889/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=4168503339888369834&amp;postID=843042658649741889' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/4168503339888369834/posts/default/843042658649741889'/><link rel='self' type='application/atom+xml' href='http://uint32t.blogspot.com/feeds/posts/default/843042658649741889'/><link rel='alternate' type='text/html' href='http://uint32t.blogspot.com/2008/05/lisp-compiler-for-net.html' title='Lisp compiler for .NET'/><author><name>Sohail Somani</name><uri>https://plus.google.com/106964604256511867632</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-t-OCQSnHolk/AAAAAAAAAAI/AAAAAAAABRU/cl0ejUW86Yk/s32-c/photo.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4168503339888369834.post-1641994081291858995</id><published>2008-04-24T00:56:00.000-04:00</published><updated>2008-04-24T00:58:34.400-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><title type='text'>Mandelbrot generator in Common Lisp</title><content type='html'>A couple of months back I had a bad flu and couldn't really do much so of course I wrote a Mandelbrot generator. This took me between .5 hr to 1 hr so go easy on me. Just dug it up today b/c of a discussion I was having so thought I'd post it here. It uses cl-opengl.&lt;br /&gt;&lt;br /&gt;&lt;pre class="lisp"&gt;&lt;br /&gt;(&lt;span class="keyword"&gt;in-package&lt;/span&gt; #&lt;span class="builtin"&gt;:cl-glut-examples&lt;/span&gt;)&lt;br /&gt;&lt;br /&gt;(&lt;span class="keyword"&gt;progn&lt;/span&gt; (setq glut::*argcp* (cffi:null-pointer) glut::*argv* (cffi:null-pointer)))(glut:init)&lt;br /&gt;&lt;br /&gt;(&lt;span class="keyword"&gt;defclass&lt;/span&gt; &lt;span class="type"&gt;mandelbrot-window&lt;/span&gt; (glut:window)&lt;br /&gt;  ((image &lt;span class="builtin"&gt;:initform&lt;/span&gt; nil&lt;br /&gt;          &lt;span class="builtin"&gt;:accessor&lt;/span&gt; image)&lt;br /&gt;   (width &lt;span class="builtin"&gt;:initarg&lt;/span&gt; &lt;span class="builtin"&gt;:width&lt;/span&gt;&lt;br /&gt;          &lt;span class="builtin"&gt;:accessor&lt;/span&gt; width)&lt;br /&gt;   (height &lt;span class="builtin"&gt;:initarg&lt;/span&gt; &lt;span class="builtin"&gt;:height&lt;/span&gt;&lt;br /&gt;           &lt;span class="builtin"&gt;:accessor&lt;/span&gt; height)&lt;br /&gt;   (need-recalc &lt;span class="builtin"&gt;:initform&lt;/span&gt; t&lt;br /&gt;                &lt;span class="builtin"&gt;:accessor&lt;/span&gt; need-recalc)&lt;br /&gt;   (real-extents &lt;span class="builtin"&gt;:initarg&lt;/span&gt; &lt;span class="builtin"&gt;:real-extents&lt;/span&gt;&lt;br /&gt;                 &lt;span class="builtin"&gt;:accessor&lt;/span&gt; real-extents)&lt;br /&gt;   (imag-extents &lt;span class="builtin"&gt;:initarg&lt;/span&gt; &lt;span class="builtin"&gt;:imag-extents&lt;/span&gt;&lt;br /&gt;                 &lt;span class="builtin"&gt;:accessor&lt;/span&gt; imag-extents))&lt;br /&gt;  (&lt;span class="builtin"&gt;:default-initargs&lt;/span&gt; &lt;span class="builtin"&gt;:pos-x&lt;/span&gt; 100 &lt;span class="builtin"&gt;:pos-y&lt;/span&gt; 100&lt;br /&gt;                     &lt;span class="builtin"&gt;:mode&lt;/span&gt; '(&lt;span class="builtin"&gt;:single&lt;/span&gt; &lt;span class="builtin"&gt;:rgb&lt;/span&gt;) &lt;span class="builtin"&gt;:title&lt;/span&gt; &lt;span class="string"&gt;&amp;quot;mandelbrot.lisp&amp;quot;&lt;/span&gt;))&lt;br /&gt;&lt;br /&gt;(&lt;span class="keyword"&gt;defmethod&lt;/span&gt; &lt;span class="function-name"&gt;glut:display-window&lt;/span&gt; &lt;span class="builtin"&gt;:before&lt;/span&gt; ((w mandelbrot-window))&lt;br /&gt;  &lt;span class="comment-delimiter"&gt;;; &lt;/span&gt;&lt;span class="comment"&gt;Select clearing color.&lt;br /&gt;&lt;/span&gt;  (gl:clear-color 0 0 0 0)&lt;br /&gt;  &lt;span class="comment-delimiter"&gt;;; &lt;/span&gt;&lt;span class="comment"&gt;Initialize viewing values.&lt;br /&gt;&lt;/span&gt;  (gl:matrix-mode &lt;span class="builtin"&gt;:projection&lt;/span&gt;)&lt;br /&gt;  (gl:load-identity)&lt;br /&gt;  (gl:ortho 0 1 0 1 -1 1))&lt;br /&gt;&lt;br /&gt;(&lt;span class="keyword"&gt;defmethod&lt;/span&gt; &lt;span class="function-name"&gt;glut:display&lt;/span&gt; ((w mandelbrot-window))&lt;br /&gt;  (gl:clear &lt;span class="builtin"&gt;:color-buffer&lt;/span&gt;)&lt;br /&gt;  (gl:raster-pos 0 0)&lt;br /&gt;  (&lt;span class="keyword"&gt;when&lt;/span&gt; (need-recalc w)&lt;br /&gt;    (format t &lt;span class="string"&gt;&amp;quot;Need to recalc...~%&amp;quot;&lt;/span&gt;)&lt;br /&gt;    (format t &lt;span class="string"&gt;&amp;quot;Real: ~A Imag: ~A~%&amp;quot;&lt;/span&gt; (real-extents w) (imag-extents w))&lt;br /&gt;    (setf (image w) (calculate-mandelbrot-image (width w) (height w) &lt;span class="builtin"&gt;:real&lt;/span&gt; (real-extents w) &lt;span class="builtin"&gt;:imag&lt;/span&gt; (imag-extents w) &lt;span class="builtin"&gt;:max-iterations&lt;/span&gt; 256))&lt;br /&gt;    (setf (need-recalc w) nil)&lt;br /&gt;    (format t &lt;span class="string"&gt;&amp;quot;Done...~%&amp;quot;&lt;/span&gt;))&lt;br /&gt;  (gl:draw-pixels (image-width (image w)) (image-height (image w)) &lt;span class="builtin"&gt;:rgb&lt;/span&gt; &lt;span class="builtin"&gt;:unsigned-byte&lt;/span&gt; (image-1darray (image w)))&lt;br /&gt;  (gl:flush))&lt;br /&gt;&lt;br /&gt;(&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;rb-mandelbrot&lt;/span&gt; (width height &lt;span class="type"&gt;&amp;amp;key&lt;/span&gt; (max-iterations 1000) (real '(-1.5 1.5)) (imag '(-1.5 1.5)))&lt;br /&gt;  (glut:display-window &lt;br /&gt;   (make-instance 'mandelbrot-window&lt;br /&gt;                  &lt;span class="builtin"&gt;:width&lt;/span&gt; width &lt;span class="builtin"&gt;:height&lt;/span&gt; height&lt;br /&gt;                  &lt;span class="builtin"&gt;:real-extents&lt;/span&gt; real &lt;span class="builtin"&gt;:imag-extents&lt;/span&gt; imag)))&lt;br /&gt;&lt;span class="comment-delimiter"&gt;;                  &lt;/span&gt;&lt;span class="comment"&gt;:image (calculate-mandelbrot-image width height :real real :imag imag :max-iterations max-iterations))))&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;(&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;midpoint&lt;/span&gt; (lst)&lt;br /&gt;  (/ (+ (first lst) (second lst))&lt;br /&gt;     2))&lt;br /&gt;&lt;br /&gt;(&lt;span class="keyword"&gt;defmethod&lt;/span&gt; &lt;span class="function-name"&gt;glut:keyboard&lt;/span&gt; ((w mandelbrot-window) key x y)&lt;br /&gt;  (&lt;span class="keyword"&gt;with-slots&lt;/span&gt; (real-extents imag-extents) w&lt;br /&gt;    (&lt;span class="keyword"&gt;let&lt;/span&gt; ((imag-delta (* 4 (/ (- (second imag-extents) (first imag-extents)) (height w))))&lt;br /&gt;          (real-delta (* 4 (/ (- (second real-extents) (first real-extents)) (width w))))&lt;br /&gt;          (imag-midpoint (midpoint imag-extents))&lt;br /&gt;          (real-midpoint (midpoint real-extents)))&lt;br /&gt;      (&lt;span class="keyword"&gt;case&lt;/span&gt; key&lt;br /&gt;        (#\a &lt;br /&gt;         (incf (first imag-extents) imag-delta)&lt;br /&gt;         (incf (second imag-extents) imag-delta))&lt;br /&gt;        (#\A&lt;br /&gt;         (incf (first imag-extents) (* 10 imag-delta))&lt;br /&gt;         (incf (second imag-extents) (* 10 imag-delta)))&lt;br /&gt;        (#\d &lt;br /&gt;         (incf (first imag-extents) (- imag-delta))&lt;br /&gt;         (incf (second imag-extents) (- imag-delta)))&lt;br /&gt;        (#\D&lt;br /&gt;         (incf (first imag-extents) (* 10 (- imag-delta)))&lt;br /&gt;         (incf (second imag-extents) (* 10 (- imag-delta))))&lt;br /&gt;        (#\w&lt;br /&gt;         (incf (first real-extents) (- real-delta))&lt;br /&gt;         (incf (second real-extents) (- real-delta)))&lt;br /&gt;        (#\s &lt;br /&gt;         (incf (first real-extents) real-delta)&lt;br /&gt;         (incf (second real-extents) real-delta))&lt;br /&gt;        (#\S&lt;br /&gt;         (&lt;span class="keyword"&gt;let&lt;/span&gt; ((imag-midpoint-distance (- imag-midpoint (first imag-extents)))&lt;br /&gt;               (real-midpoint-distance (- real-midpoint (first real-extents))))&lt;br /&gt;           (setf (first imag-extents) (- (first imag-extents) imag-midpoint-distance))&lt;br /&gt;           (setf (second imag-extents) (+ (second imag-extents) imag-midpoint-distance))&lt;br /&gt;           (setf (first real-extents) (- (first real-extents) real-midpoint-distance))&lt;br /&gt;           (setf (second real-extents) (+ (second real-extents) real-midpoint-distance))))&lt;br /&gt;        (#\W&lt;br /&gt;         (setf (first imag-extents) (midpoint (list (first imag-extents)&lt;br /&gt;                                                    imag-midpoint)))&lt;br /&gt;         (setf (second imag-extents) (midpoint (list imag-midpoint&lt;br /&gt;                                                     (second imag-extents))))&lt;br /&gt;         (setf (first real-extents) (midpoint (list (first real-extents)&lt;br /&gt;                                                    real-midpoint)))&lt;br /&gt;         (setf (second real-extents) (midpoint (list real-midpoint&lt;br /&gt;                                                     (second real-extents))))))&lt;br /&gt;      (&lt;span class="keyword"&gt;case&lt;/span&gt; key&lt;br /&gt;        ((#\w #\s #\a #\d #\W #\D #\A #\S)&lt;br /&gt;         (setf (need-recalc w) t)&lt;br /&gt;         (setf (real-extents w) real-extents)&lt;br /&gt;         (setf (imag-extents w) imag-extents)))))&lt;br /&gt;  (glut:post-redisplay))&lt;br /&gt;&lt;br /&gt;(&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;make-image&lt;/span&gt; (w h)&lt;br /&gt;  (&lt;span class="keyword"&gt;let*&lt;/span&gt; ((underlying-array (make-array (* w h 3) &lt;span class="builtin"&gt;:element-type&lt;/span&gt; '(unsigned-byte 8)&lt;br /&gt;                                       &lt;span class="builtin"&gt;:initial-element&lt;/span&gt; 0))&lt;br /&gt;         (displaced-array (make-array (list w h 3) &lt;span class="builtin"&gt;:element-type&lt;/span&gt; '(unsigned-byte 8)&lt;br /&gt;                                      &lt;span class="builtin"&gt;:displaced-to&lt;/span&gt; underlying-array)))&lt;br /&gt;        &lt;br /&gt;  (list w h underlying-array displaced-array)))&lt;br /&gt;&lt;br /&gt;(&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;image-width&lt;/span&gt; (image)&lt;br /&gt;  (first image))&lt;br /&gt;&lt;br /&gt;(&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;image-height&lt;/span&gt; (image)&lt;br /&gt;  (second image))&lt;br /&gt;&lt;br /&gt;(&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;image-1darray&lt;/span&gt; (image)&lt;br /&gt;  (third image))&lt;br /&gt;&lt;br /&gt;(&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;image-3darray&lt;/span&gt; (image)&lt;br /&gt;  (fourth image))&lt;br /&gt;&lt;br /&gt;(&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;set-colour&lt;/span&gt; (image x y iterations-taken max-iterations)&lt;br /&gt;  (&lt;span class="keyword"&gt;let&lt;/span&gt; (depth)&lt;br /&gt;    (&lt;span class="keyword"&gt;if&lt;/span&gt; (= iterations-taken max-iterations)&lt;br /&gt;        (setf depth 0)&lt;br /&gt;        (&lt;span class="keyword"&gt;progn&lt;/span&gt;&lt;br /&gt;          (setf depth (truncate (* 255 (/ iterations-taken 1000))))))&lt;br /&gt;    (&lt;span class="keyword"&gt;let&lt;/span&gt; ((r (min 180 (* 20 depth)))&lt;br /&gt;          (g (min 180 (* 5 depth)))&lt;br /&gt;          (b (min 180 (* 1 depth)))&lt;br /&gt;          (data (image-3darray image)))&lt;br /&gt;      (setf (aref data x y 0) r)&lt;br /&gt;      (setf (aref data x y 1) g)&lt;br /&gt;      (setf (aref data x y 2) b))))&lt;br /&gt;&lt;br /&gt;(&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;within-radius&lt;/span&gt; (z)&lt;br /&gt;  (&lt;span class="keyword"&gt;let&lt;/span&gt; ((r (realpart z))&lt;br /&gt;        (i (imagpart z)))&lt;br /&gt;    (&amp;lt;= (+ (* r r) (* i i))&lt;br /&gt;        4)))&lt;br /&gt;&lt;br /&gt;(&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;calculate-mandelbrot-image&lt;/span&gt; (width height &lt;span class="type"&gt;&amp;amp;key&lt;/span&gt; (max-iterations 1000)&lt;br /&gt;                                   (real '(-1.5 1.5))&lt;br /&gt;                                   (imag '(-1.5 1.5)))&lt;br /&gt;  (&lt;span class="keyword"&gt;let*&lt;/span&gt; ((image (make-image width height))&lt;br /&gt;         (low-real (first real))&lt;br /&gt;         (high-real (second real))&lt;br /&gt;         (low-imag (first imag))&lt;br /&gt;         (high-imag (second imag))&lt;br /&gt;         (real-length (- high-real low-real))&lt;br /&gt;         (imag-length (- high-imag low-imag))&lt;br /&gt;         (real-by (/ real-length width))&lt;br /&gt;         (imag-by (/ imag-length height)))&lt;br /&gt;    (&lt;span class="keyword"&gt;flet&lt;/span&gt;  ((do-it (start-x end-x start-cr)&lt;br /&gt;              (&lt;span class="keyword"&gt;loop&lt;/span&gt;&lt;br /&gt;                 for x from start-x to end-x&lt;br /&gt;                 for cr from start-cr by real-by&lt;br /&gt;                 do&lt;br /&gt;                   (&lt;span class="keyword"&gt;loop&lt;/span&gt; &lt;br /&gt;                      for y below height&lt;br /&gt;                      for ci from low-imag by imag-by&lt;br /&gt;                      do&lt;br /&gt;                        (&lt;span class="keyword"&gt;let*&lt;/span&gt; ((c (complex cr ci))&lt;br /&gt;                               (iterations-taken&lt;br /&gt;                                (&lt;span class="keyword"&gt;loop&lt;/span&gt;&lt;br /&gt;                                   for z = c then (+ (* z z) c)&lt;br /&gt;                                   for iteration from 0 below max-iterations&lt;br /&gt;                                   while (within-radius z)&lt;br /&gt;                                   count iteration)))&lt;br /&gt;                          (set-colour image (truncate x) (truncate y) iterations-taken max-iterations))))))&lt;br /&gt;      (&lt;span class="keyword"&gt;let*&lt;/span&gt; ((end-x (truncate (/ width 2)))&lt;br /&gt;             (threads (list (sb-thread:make-thread (&lt;span class="keyword"&gt;lambda&lt;/span&gt; ()&lt;br /&gt;                                                     (do-it 0 end-x low-real)))&lt;br /&gt;                            (sb-thread:make-thread (&lt;span class="keyword"&gt;lambda&lt;/span&gt; ()&lt;br /&gt;                                                     (do-it (1+ end-x) (1- width) (+ (* end-x real-by) low-real)))))))&lt;br /&gt;        (&lt;span class="keyword"&gt;loop&lt;/span&gt; for thread in threads&lt;br /&gt;           do (sb-thread:join-thread thread))))&lt;br /&gt;    image))&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;</content><link rel='replies' type='application/atom+xml' href='http://uint32t.blogspot.com/feeds/1641994081291858995/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=4168503339888369834&amp;postID=1641994081291858995' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/4168503339888369834/posts/default/1641994081291858995'/><link rel='self' type='application/atom+xml' href='http://uint32t.blogspot.com/feeds/posts/default/1641994081291858995'/><link rel='alternate' type='text/html' href='http://uint32t.blogspot.com/2008/04/mandelbrot-generator-in-common-lisp.html' title='Mandelbrot generator in Common Lisp'/><author><name>Sohail Somani</name><uri>https://plus.google.com/106964604256511867632</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-t-OCQSnHolk/AAAAAAAAAAI/AAAAAAAABRU/cl0ejUW86Yk/s32-c/photo.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4168503339888369834.post-6547953075406616700</id><published>2008-04-13T14:45:00.000-04:00</published><updated>2008-04-13T15:45:10.178-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='weblocks'/><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><title type='text'>Weblocks: Presentations</title><content type='html'>When Slava set out to write &lt;a href="http://common-lisp.net/project/cl-weblocks"&gt;Weblocks&lt;/a&gt;, he claims to have a goal to never write HTML again. The way he proposed to accomplish this was through the use of presentations. You can see an example of his &lt;a href="http://www.defmacro.org/ramblings/ui-dsl.html"&gt;UI-DSL&lt;/a&gt;. I don't claim to understand the UI-DSL but I do understand how presentations work and I like them!&lt;br /&gt;&lt;br /&gt;I set out to add support for money for something I am working on. So first, I wanted to be able to validate/display a number. Simple enough. First thing I did was define a new type so I can change it later into something that handles currencies properly:&lt;br /&gt;&lt;pre class="lisp"&gt;(&lt;span class="keyword"&gt;deftype&lt;/span&gt; &lt;span class="type"&gt;money&lt;/span&gt; () &lt;br /&gt;  'number)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;There are basically two things you need to be able to do (depending on context): parse and display. Obviously, presentations are responsible for the display while parsers are responsible for ... parsing!&lt;br /&gt;&lt;pre class="lisp"&gt;(&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;parse-money&lt;/span&gt; (string)&lt;br /&gt;  (read-from-string string))&lt;br /&gt;&lt;br /&gt;(&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;format-money-display&lt;/span&gt; (amount)&lt;br /&gt;  &lt;span class="comment-delimiter"&gt;;; &lt;/span&gt;&lt;span class="comment"&gt;Note the extra &amp;quot;$&lt;/span&gt;&lt;span class="comment"&gt;&amp;quot;&lt;/span&gt;&lt;span class="comment"&gt;&lt;br /&gt;&lt;/span&gt;  (format nil &lt;span class="string"&gt;&amp;quot;$~4$&amp;quot;&lt;/span&gt; amount))&lt;br /&gt;&lt;br /&gt;(&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;format-money-input&lt;/span&gt; (amount)&lt;br /&gt;  (format nil &lt;span class="string"&gt;&amp;quot;~d&amp;quot;&lt;/span&gt; amount))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The reason there are two format functions is that you need to format the value for display or for editing. The latter is named &lt;tt&gt;format-money-input&lt;/tt&gt;.&lt;br /&gt;&lt;br /&gt;The protocol calls the generic functions &lt;tt&gt;weblocks:typespec-&gt;view-field-presentation&lt;/tt&gt; and &lt;tt&gt;weblocks:typespec-&gt;form-view-field-parser&lt;/tt&gt; depending on what the scaffold is rendering. The purpose of these functions is to take a typespec and create the corresponding presentation or parser.&lt;br /&gt;&lt;br /&gt;You can have form scaffolds in addition to the regular scaffold. You would use the form scaffold when the method of display would be different. For example, for a boolean input you would use a checkbox for form input, whereas when simply rendering for display, you could use "true" or "false" or "yes" or "no".&lt;br /&gt;&lt;br /&gt;In this case, I don't need anything this drastic, so I defined the functions as follows:&lt;br /&gt;&lt;pre class="lisp"&gt;(&lt;span class="keyword"&gt;defmethod&lt;/span&gt; &lt;span class="function-name"&gt;weblocks:typespec-&amp;gt;view-field-presentation&lt;/span&gt; (scaffold&lt;br /&gt;                                                       (typespec (eql 'money)) args)&lt;br /&gt;  (values t (make-instance 'money-presentation)))&lt;br /&gt;&lt;br /&gt;(&lt;span class="keyword"&gt;defmethod&lt;/span&gt; &lt;span class="function-name"&gt;weblocks:typespec-&amp;gt;form-view-field-parser&lt;/span&gt; ((scaffold form-scaffold)&lt;br /&gt;                                                      (typespec (eql 'money)) args)&lt;br /&gt;  (values t (make-instance 'money-parser)))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;In this case, the &lt;tt&gt;money-presentation&lt;/tt&gt; and &lt;tt&gt;money-parser&lt;/tt&gt; types are just tags to make the generic function machinery work. The types are defined as follows:&lt;br /&gt;&lt;pre class="lisp"&gt;&lt;br /&gt;(&lt;span class="keyword"&gt;defclass&lt;/span&gt; &lt;span class="type"&gt;money-presentation&lt;/span&gt; (weblocks:text-presentation weblocks:input-presentation)&lt;br /&gt;  ())&lt;br /&gt;&lt;br /&gt;(&lt;span class="keyword"&gt;defclass&lt;/span&gt; &lt;span class="type"&gt;money-parser&lt;/span&gt; (parser)&lt;br /&gt;  ()&lt;br /&gt;  (&lt;span class="builtin"&gt;:default-initargs&lt;/span&gt; &lt;span class="builtin"&gt;:error-message&lt;/span&gt; &lt;span class="string"&gt;&amp;quot;a money value (for example, 100.51 or 4.52)&amp;quot;&lt;/span&gt;)&lt;br /&gt;  (&lt;span class="builtin"&gt;:documentation&lt;/span&gt; &lt;span class="string"&gt;&amp;quot;A parser designed to parse strings into&lt;br /&gt;  money.&amp;quot;&lt;/span&gt;))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Using &lt;tt&gt;:default-initargs&lt;/tt&gt; you can define an error message that will be seen when parsing fails (for whatever reason).&lt;br /&gt;&lt;br /&gt;When Weblocks has your parser and presentation types, it then calls the following generic functions depending on the rendering context:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt; &lt;li&gt;&lt;tt&gt;weblocks:print-view-field-value&lt;/tt&gt;&lt;/li&gt;&lt;br /&gt; &lt;li&gt;&lt;tt&gt;weblocks:parse-view-field-value&lt;/tt&gt;&lt;/li&gt;&lt;br /&gt; &lt;li&gt;&lt;tt&gt;weblocks:render-view-field-value&lt;/tt&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;You won't believe it, but there is barely any HTML involved. I love it!&lt;br /&gt;&lt;pre class="lisp"&gt;&lt;br /&gt;(&lt;span class="keyword"&gt;defmethod&lt;/span&gt; &lt;span class="function-name"&gt;weblocks:print-view-field-value&lt;/span&gt; (value &lt;br /&gt;                                            (presentation money-presentation)&lt;br /&gt;                                            field view widget obj &lt;span class="type"&gt;&amp;amp;rest&lt;/span&gt; args)&lt;br /&gt;  (&lt;span class="keyword"&gt;declare&lt;/span&gt; (ignore args))&lt;br /&gt;  (format-money-display value))&lt;br /&gt;&lt;br /&gt;(&lt;span class="keyword"&gt;defmethod&lt;/span&gt; &lt;span class="function-name"&gt;weblocks:parse-view-field-value&lt;/span&gt; ((parser money-parser)&lt;br /&gt;                                            value obj&lt;br /&gt;                                            (view form-view) (field form-view-field)&lt;br /&gt;                                            &lt;span class="type"&gt;&amp;amp;rest&lt;/span&gt; args)&lt;br /&gt;  (&lt;span class="keyword"&gt;declare&lt;/span&gt; (ignore args))&lt;br /&gt;  (&lt;span class="keyword"&gt;declare&lt;/span&gt; (optimize safety))&lt;br /&gt;  (&lt;span class="keyword"&gt;ignore-errors&lt;/span&gt;&lt;br /&gt;    (&lt;span class="keyword"&gt;let*&lt;/span&gt; ((presentp (text-input-present-p value))&lt;br /&gt;    (money-value (&lt;span class="keyword"&gt;when&lt;/span&gt; presentp&lt;br /&gt;                          (parse-money value))))&lt;br /&gt;      (values t presentp money-value))))&lt;br /&gt;&lt;br /&gt;(&lt;span class="keyword"&gt;defmethod&lt;/span&gt; &lt;span class="function-name"&gt;weblocks:render-view-field-value&lt;/span&gt; (value (presentation money-presentation)&lt;br /&gt;                                             (field form-view-field)&lt;br /&gt;                                             (view form-view)&lt;br /&gt;                                             widget obj &lt;span class="type"&gt;&amp;amp;rest&lt;/span&gt; args)&lt;br /&gt;  (&lt;span class="keyword"&gt;declare&lt;/span&gt; (ignore args))&lt;br /&gt;  (render-text-input (view-field-slot-name field)&lt;br /&gt;                     (format-money-input value)&lt;br /&gt;                     &lt;span class="builtin"&gt;:maxlength&lt;/span&gt; 50))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;An example of usage would be:&lt;br /&gt;&lt;pre class="lisp"&gt;(&lt;span class="keyword"&gt;defclass&lt;/span&gt; &lt;span class="type"&gt;transaction&lt;/span&gt; ()&lt;br /&gt;  ((id &lt;span class="builtin"&gt;:accessor&lt;/span&gt; transaction-id)&lt;br /&gt;   (date &lt;span class="builtin"&gt;:initform&lt;/span&gt; (get-universal-time)&lt;br /&gt;         &lt;span class="builtin"&gt;:accessor&lt;/span&gt; transaction-date&lt;br /&gt;         &lt;span class="builtin"&gt;:initarg&lt;/span&gt; &lt;span class="builtin"&gt;:date&lt;/span&gt;)&lt;br /&gt;   (amount &lt;span class="builtin"&gt;:initform&lt;/span&gt; *default-money-value*&lt;br /&gt;           &lt;span class="builtin"&gt;:accessor&lt;/span&gt; transaction-amount&lt;br /&gt;           &lt;span class="builtin"&gt;:initarg&lt;/span&gt; &lt;span class="builtin"&gt;:amount&lt;/span&gt;&lt;br /&gt;           &lt;span class="builtin"&gt;:type&lt;/span&gt; money)))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;And that is all you need to add your own reusable type to Weblocks. Not bad. Haven't added a date type yet, but will soon.</content><link rel='replies' type='application/atom+xml' href='http://uint32t.blogspot.com/feeds/6547953075406616700/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=4168503339888369834&amp;postID=6547953075406616700' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/4168503339888369834/posts/default/6547953075406616700'/><link rel='self' type='application/atom+xml' href='http://uint32t.blogspot.com/feeds/posts/default/6547953075406616700'/><link rel='alternate' type='text/html' href='http://uint32t.blogspot.com/2008/04/weblocks-presentations.html' title='Weblocks: Presentations'/><author><name>Sohail Somani</name><uri>https://plus.google.com/106964604256511867632</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-t-OCQSnHolk/AAAAAAAAAAI/AAAAAAAABRU/cl0ejUW86Yk/s32-c/photo.jpg'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4168503339888369834.post-8988330808213491789</id><published>2008-02-25T16:09:00.000-05:00</published><updated>2008-02-25T16:31:22.524-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='docbook'/><category scheme='http://www.blogger.com/atom/ns#' term='cl-who'/><title type='text'>Update: Generating DocBook output using Common Lisp and SBCL</title><content type='html'>Referencing &lt;a href="http://uint32t.blogspot.com/2008/02/generating-docbook-output-using-common.html"&gt;yesterday's post&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Since &lt;a href="http://www.blogger.com/comment.g?blogID=4168503339888369834&amp;postID=7474351063090543491"&gt;Jason asked for the source code&lt;/a&gt;, I figured I would post it in a blog instead of a comment as it is more annoying to write a medium-sized comment.&lt;br /&gt;&lt;br /&gt;Be warned, it will only work in SBCL and on Linux :-) You need to install docbook-utils (which is apparently the old way of doing things.) On Ubuntu, &lt;tt&gt;apt-get install docbook-utils&lt;/tt&gt; should do you. Here are the files in my little demo:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://taggedtype.net/~sohail/lisp/ebook.lisp"&gt;ebook.lisp&lt;/a&gt;: The "EBook". Contains all the source code for translation.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://taggedtype.net/~sohail/lisp/introduction.lisp"&gt;introduction.lisp&lt;/a&gt;: A file containing a single chapter.&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;This code is a hackjob so please keep the pointing and laughing to a minimum. Especially &lt;tt&gt;docbook-tree-to-xml-string&lt;/tt&gt;. There are so many things wrong with that function!&lt;br /&gt;&lt;br /&gt;The way I use it is I start up Slime, edit ebook.lisp and use C-c C-l which reloads the file into the Lisp image.&lt;br /&gt;&lt;br /&gt;Enjoy and leave comments if you have any questions.</content><link rel='replies' type='application/atom+xml' href='http://uint32t.blogspot.com/feeds/8988330808213491789/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=4168503339888369834&amp;postID=8988330808213491789' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/4168503339888369834/posts/default/8988330808213491789'/><link rel='self' type='application/atom+xml' href='http://uint32t.blogspot.com/feeds/posts/default/8988330808213491789'/><link rel='alternate' type='text/html' href='http://uint32t.blogspot.com/2008/02/update-generating-docbook-output-using.html' title='Update: Generating DocBook output using Common Lisp and SBCL'/><author><name>Sohail Somani</name><uri>https://plus.google.com/106964604256511867632</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-t-OCQSnHolk/AAAAAAAAAAI/AAAAAAAABRU/cl0ejUW86Yk/s32-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4168503339888369834.post-7474351063090543491</id><published>2008-02-25T00:32:00.000-05:00</published><updated>2008-02-25T21:58:20.674-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='cl-who'/><title type='text'>Generating DocBook output using Common Lisp and SBCL</title><content type='html'>I've been a bit busy lately with Sohail 3.0 and an unexpected trip to Toronto (still here for a week) so I haven't had much time for much blogging. &lt;br /&gt;&lt;br /&gt;Anyway, one of the things I've always wanted to learn was DocBook. Today I had a bit of down time so I downloaded the &lt;a href="http://opensource.bureau-cornavin.com/crash-course/en/crash-course.pdf"&gt;DocBook crash course&lt;/a&gt;. Oh boy. XML... If Java could be a markup language, it would be XML. If only I could get rid of the redundancy. Long story short, I thought of &lt;a href="http://weitz.de/cl-who/"&gt;CL-WHO&lt;/a&gt; to generate the XML. Normally, CL-WHO is used to generate HTML, but it actually knows very little about HTML. To get CL-WHO to generate XML, all you need to do is set the prologue and mode to XML. To do this, execute the following at the REPL:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;(setf (cl-who:html-mode) :xml)&lt;br /&gt;;;; This is printed before anything else&lt;br /&gt;(setf cl-who:*prologue* "&amp;lt;?xml version=\"1.0\" encoding=\"UTF-8\" ?&amp;gt;")&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;For sample output, evaluate the following at the REPL:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;(cl-who:with-html-output-to-string (s nil :indent t :prologue t)&lt;br /&gt;&amp;nbsp;&amp;nbsp;(:people&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(:person :id "1" (:firstname "Sohail") (:surname "Somani"))&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(:person :id "2" (:firstname "Heinz") (:surname "Ketchup"))))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Which generates:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;?xml&amp;nbsp;version=\&amp;quot;1.0\&amp;quot;&amp;nbsp;encoding=\&amp;quot;UTF-8\&amp;quot;&amp;nbsp;?&amp;gt;&lt;br /&gt;&amp;lt;people&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;lt;person&amp;nbsp;id=\'1\'&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;firstname&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Sohail&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/firstname&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;surname&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Somani&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/surname&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;lt;/person&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;lt;person&amp;nbsp;id=\'2\'&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;firstname&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Heinz&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/firstname&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;surname&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Ketchup&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/surname&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;lt;/person&amp;gt;&lt;br /&gt;&amp;lt;/people&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The workflow is pretty straighforward:&lt;br /&gt;&lt;ol&gt;&lt;br /&gt; &lt;li&gt;Write CL-WHO document&lt;/li&gt;&lt;br /&gt; &lt;li&gt;Convert document to XML&lt;/li&gt;&lt;br /&gt; &lt;li&gt;Write to file&lt;/li&gt;&lt;br /&gt; &lt;li&gt;Run through docbook2&amp;lt;target&amp;gt;&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;Here is a "real example" where by "real" I mean copied from the above mentioned ebook:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;(defparameter&amp;nbsp;*book*&lt;br /&gt;&amp;nbsp;&amp;nbsp;`(:book&amp;nbsp;:lang&amp;nbsp;"en"&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(:bookinfo&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(:title&amp;nbsp;"Hello,&amp;nbsp;world!")&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(:authorgroup&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(:author&amp;nbsp;(:firstname&amp;nbsp;"Sohail")&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(:surname&amp;nbsp;"Somani")))&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(:abstract&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(:para&amp;nbsp;(:application&amp;nbsp;"twiddle")&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"is&amp;nbsp;an&amp;nbsp;application&amp;nbsp;specially&amp;nbsp;designed&amp;nbsp;to&amp;nbsp;do&amp;nbsp;nothing&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;you&amp;nbsp;would&amp;nbsp;ever&amp;nbsp;want"))&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(:keywordset&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(:keyword&amp;nbsp;"twiddle")&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(:keyword&amp;nbsp;"sample&amp;nbsp;application")))&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;,(docbook-part&amp;nbsp;"introduction.lisp")&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(:chapter&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(:title&amp;nbsp;"Zomg")&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(:sect1&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(:title&amp;nbsp;"Testing")&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(:para&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(:segmentedlist&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(:segtitle&amp;nbsp;"Name")&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(:segtitle&amp;nbsp;"Occupation")&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(:segtitle&amp;nbsp;"Favourite&amp;nbsp;food")&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(:seglistitem&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(:seg&amp;nbsp;"Tux")&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(:seg&amp;nbsp;"Linux&amp;nbsp;Mascot")&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(:seg&amp;nbsp;"Herring")))&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(:ulink&amp;nbsp;:url&amp;nbsp;"http://www.kde.org"&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"My&amp;nbsp;favourite&amp;nbsp;website"))))))&lt;br /&gt;&lt;br /&gt;(docbook2pdf&amp;nbsp;*book*) ;; actually also launches pdf reader :-D&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;So now, with Emacs and Slime (C-c C-l specifically!) I have a fully programmable DocBook IDE. Not to mention that I can use Lisp. Leave a comment if you are actually interested in the code :-)</content><link rel='replies' type='application/atom+xml' href='http://uint32t.blogspot.com/feeds/7474351063090543491/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=4168503339888369834&amp;postID=7474351063090543491' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/4168503339888369834/posts/default/7474351063090543491'/><link rel='self' type='application/atom+xml' href='http://uint32t.blogspot.com/feeds/posts/default/7474351063090543491'/><link rel='alternate' type='text/html' href='http://uint32t.blogspot.com/2008/02/generating-docbook-output-using-common.html' title='Generating DocBook output using Common Lisp and SBCL'/><author><name>Sohail Somani</name><uri>https://plus.google.com/106964604256511867632</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-t-OCQSnHolk/AAAAAAAAAAI/AAAAAAAABRU/cl0ejUW86Yk/s32-c/photo.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4168503339888369834.post-8318030403941231353</id><published>2008-01-30T14:15:00.000-05:00</published><updated>2008-01-30T15:04:54.136-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='emacs'/><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='slime'/><category scheme='http://www.blogger.com/atom/ns#' term='cl-opengl'/><title type='text'>Experience report: Common Lisp and OpenGL</title><content type='html'>I am working through the (in)famous &lt;a href="http://fly.cc.fer.hr/~unreal/theredbook/"&gt;OpenGL Red Book&lt;/a&gt; using &lt;a href="http://common-lisp.net/~loliveira/darcs/cl-opengl-thomas/"&gt;cl-opengl(-thomas)&lt;/a&gt;. Who is this Thomas guy anyway?&lt;br /&gt;&lt;br /&gt;I just thought I would post my experience about it so far.&lt;br /&gt;&lt;br /&gt;I considered just using C or C++ to work through the examples but the thought of the turnaround time before something was running was bugging me. So I decided to use Common Lisp.&lt;br /&gt;&lt;br /&gt;First of all, using &lt;a href="http://common-lisp.net/project/clbuild/"&gt;clbuild&lt;/a&gt; was a smart idea. I additionally set up Emacs+Slime to be able to use the clbuild core or using the default core. I currently have a couple of projects and don't use clbuild for the others. The .emacs is quite ugly as a result but does what I need it to ;-)&lt;br /&gt;&lt;br /&gt;Secondly, cl-opengl uses generic functions instead of direct function callbacks. At first, I did not like this deviation from the way OpenGL normally works (via callbacks) but now I have come to appreciate it. One benefit is that there are (apparently!) no global variables. Secondly, there is a lot of nice stuff you can do with generic functions that you cannot do with function callbacks and cl-opengl takes full advantage of this fact.&lt;br /&gt;&lt;br /&gt;Thirdly, I was afraid that it might not perform well enough but it turns out that I haven't reached any part of the book yet that requires absolute performance (or it is fast enough!) I'm told that choice of language should not generally be an issue if you have done things right so that is encouraging.&lt;br /&gt;&lt;br /&gt;Last, but not least, Emacs + Slime for development of the examples is awesome. I can modify the definition of the display code at runtime and the changes show up. That is very, very useful for understanding (for example) how functions like gluLookAt work.&lt;br /&gt;&lt;br /&gt;In general, I believe the experience has been better than it would have been had I used C or C++ for going through the book's examples.</content><link rel='replies' type='application/atom+xml' href='http://uint32t.blogspot.com/feeds/8318030403941231353/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=4168503339888369834&amp;postID=8318030403941231353' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/4168503339888369834/posts/default/8318030403941231353'/><link rel='self' type='application/atom+xml' href='http://uint32t.blogspot.com/feeds/posts/default/8318030403941231353'/><link rel='alternate' type='text/html' href='http://uint32t.blogspot.com/2008/01/experience-report-common-lisp-and.html' title='Experience report: Common Lisp and OpenGL'/><author><name>Sohail Somani</name><uri>https://plus.google.com/106964604256511867632</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-t-OCQSnHolk/AAAAAAAAAAI/AAAAAAAABRU/cl0ejUW86Yk/s32-c/photo.jpg'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4168503339888369834.post-2780925206239714111</id><published>2008-01-29T15:42:00.000-05:00</published><updated>2008-01-30T00:58:48.235-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='clbuild'/><category scheme='http://www.blogger.com/atom/ns#' term='opengl'/><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><title type='text'>Getting started with clbuild and opengl</title><content type='html'>&lt;quote&gt;&lt;br /&gt;A script to update/install the latest versions of all the most important Common Lisp packages.&lt;br /&gt;&lt;/quote&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;-- &lt;a href="http://common-lisp.net/project/clbuild/"&gt;clbuild project page&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;clbuild is a nice way to keep up to date on the latest libraries. An advantage over ASDF-INSTALL is that it can retrieve from more than just http. Look at &lt;a href="http://bc.tech.coop/blog/080116.html"&gt;Bill Clementson's blog&lt;/a&gt; for a good overview on how to get started. This post is about how to get started with cl-opengl(-thomas) with clbuild.&lt;br /&gt;&lt;br /&gt;Once you have retrieved clbuild (lines prefixed by $ are what you would type in, everything else is output):&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;$ cd /path/to/clbuild&lt;br /&gt;$ ./clbuild build cl-opengl&lt;br /&gt;The following extra dependencies were found: alexandria babel cffi trivial-features&lt;br /&gt;include dependencies in update? (Y/n)y&lt;br /&gt;UPDATE darcs pull alexandria&lt;br /&gt;Pulling from "http://common-lisp.net/project/alexandria/darcs/alexandria"...&lt;br /&gt;No remote changes to pull in!&lt;br /&gt;UPDATE darcs pull babel&lt;br /&gt;Pulling from "http://common-lisp.net/~loliveira/darcs/babel"...&lt;br /&gt;No remote changes to pull in!&lt;br /&gt;UPDATE darcs pull cffi&lt;br /&gt;Pulling from "http://common-lisp.net/~loliveira/darcs/cffi+lotsastuff"...&lt;br /&gt;No remote changes to pull in!&lt;br /&gt;UPDATE darcs pull cl-opengl&lt;br /&gt;Pulling from "http://common-lisp.net/~loliveira/darcs/cl-opengl-thomas"...&lt;br /&gt;No remote changes to pull in!&lt;br /&gt;UPDATE darcs pull trivial-features&lt;br /&gt;Pulling from "http://common-lisp.net/~loliveira/darcs/trivial-features"...&lt;br /&gt;No remote changes to pull in!&lt;br /&gt;update complete&lt;br /&gt;20 system definition files registered&lt;br /&gt;; loading system definition from&lt;br /&gt;; /home/sohail/src/thirdparty/clbuild/systems/cl-ppcre.asd into&lt;br /&gt;; #&lt;PACKAGE "ASDF0"&gt;&lt;br /&gt;; registering #&lt;SYSTEM :CL-PPCRE {B009769}&gt; as CL-PPCRE&lt;br /&gt;Loading cl-glu...&lt;br /&gt;Loading cl-glut-examples...&lt;br /&gt;Loading cl-glut...&lt;br /&gt;Loading cl-opengl...&lt;br /&gt;Dumping monster.core...&lt;br /&gt;[undoing binding stack and other enclosing state... done]&lt;br /&gt;[saving current Lisp image into /home/sohail/src/thirdparty/clbuild/monster.core:&lt;br /&gt;writing 2976 bytes from the read-only space at 0x01000000&lt;br /&gt;writing 5424 bytes from the static space at 0x01100000&lt;br /&gt;writing 33984512 bytes from the dynamic space at 0x09000000&lt;br /&gt;done]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;You can start the core as follows:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;sbcl --core monster.core&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now, you might think you can just call (glut:init) and be off to the races, but you would be wrong:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;* (glut:init)&lt;br /&gt;*** glibc detected *** sbcl: free(): invalid pointer: 0x080876a8 ***&lt;br /&gt;*** lockup ensues ***&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The problem here is that when clbuild dumped core, the pointers for command line arguments are saved along with it. Unfortunately, these are FFI pointers and so the values are invalid. When you call glut:init, it tries to free these pointers which results in the lockup above. Luis Oliveira (luis on #lisp!) suggested that this is something CFFI should handle and said he would make a note of it.&lt;br /&gt;&lt;br /&gt;In the meantime, you can execute the following to get around it:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;* (progn (setq glut::*argcp* (cffi:null-pointer) glut::*argv* (cffi:null-pointer)))&lt;br /&gt;#.(SB-SYS:INT-SAP #X00000000)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And now, you are good to go:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;* (glut:init)&lt;br /&gt;; No value&lt;br /&gt;*&lt;br /&gt;&lt;/pre&gt;</content><link rel='replies' type='application/atom+xml' href='http://uint32t.blogspot.com/feeds/2780925206239714111/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=4168503339888369834&amp;postID=2780925206239714111' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/4168503339888369834/posts/default/2780925206239714111'/><link rel='self' type='application/atom+xml' href='http://uint32t.blogspot.com/feeds/posts/default/2780925206239714111'/><link rel='alternate' type='text/html' href='http://uint32t.blogspot.com/2008/01/getting-started-with-clbuild-and-opengl.html' title='Getting started with clbuild and opengl'/><author><name>Sohail Somani</name><uri>https://plus.google.com/106964604256511867632</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-t-OCQSnHolk/AAAAAAAAAAI/AAAAAAAABRU/cl0ejUW86Yk/s32-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4168503339888369834.post-8095178911262744189</id><published>2008-01-24T19:50:00.000-05:00</published><updated>2008-01-24T22:50:45.038-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='clsql'/><title type='text'>Not very clever.</title><content type='html'>I've just been formally adding the notion of fixtures to my CLSQL-backed application using Postgres SQL. Fixtures are fixed scenarios for your application that you can run tests against. Atleast that is what I call them!&lt;br /&gt;&lt;br /&gt;One of the tasks when dealing with testing database applications is that you have to populate the database for part of your tests. The problem is that you have to also empty the tables before running the same set of tests again otherwise your results are either not repeatable or you will accidentally violate some uniqueness constraints. For example, you might have some users table and one of the uniqueness constraints applies to email addresses.&lt;br /&gt;&lt;br /&gt;I came up with a really clever way of keeping the database clean during testing by abusing transactions (hint: whenever someone says they did something clever, it usually isn't!) The idea was to begin and always rollback a transaction for each fixture. This way, the database never actually got populated. Score one for me. The code looks something like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;(defmacro with-no-db-side-effects (&amp;body body)&lt;br /&gt;  `(progn&lt;br /&gt;     (clsql:start-transaction)&lt;br /&gt;     (unwind-protect&lt;br /&gt;          (progn ,@body)&lt;br /&gt;       (clsql:rollback))))&lt;br /&gt;&lt;br /&gt;(defmacro def-fixture (name args &amp;body body)&lt;br /&gt;  `(defun ,name ,args&lt;br /&gt;     ,@body))&lt;br /&gt;&lt;br /&gt;(defmacro with-fixtures ((&amp;rest fixtures) &amp;body body)&lt;br /&gt;  `(with-no-db-side-effects&lt;br /&gt;     (let (&lt;br /&gt;           ,@(loop for fixture in fixtures&lt;br /&gt;                collect `(,fixture (,fixture))))&lt;br /&gt;       (declare (ignorable ,@fixtures))&lt;br /&gt;       ,@body)))&lt;br /&gt;...&lt;br /&gt;(def-fixture fixture-a ()&lt;br /&gt;  (populate-database))&lt;br /&gt;...&lt;br /&gt;(test test-something-specific-about-scenario-a&lt;br /&gt;  (with-fixtures (fixture-a)&lt;br /&gt;    (validate-foo)&lt;br /&gt;    (validate-bar)))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Pretty clever (remember the above hint!) It worked as I expected.&lt;br /&gt;&lt;br /&gt;At some point during the testing, you must also test the integrity of the relational model. I think you must do this because the integrity of your database should not solely be protected by the application. The IT department will always figure out a way to futz with your data!&lt;br /&gt;&lt;br /&gt;One way in which you might try to test the integrity of your database is by entering some data that should be unique. You might try and add the same user twice, for example. So I did:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;(test test-something-specific-about-scenario-a&lt;br /&gt;  (with-fixtures (fixture-a)&lt;br /&gt;    (validate-foo)&lt;br /&gt;    (validate-bar)&lt;br /&gt;    (signals duplicate-key-error (insert-duplicate-user))))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Which immediately results in:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;Error POSTGRESQL-ERROR / ERROR:  current transaction is aborted, commands ignored until end of transaction block&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Duh! If a query fails within a transaction, you are supposed to roll it back. It especially doesn't make any sense for PGSQL to execute any more of my commands until I rollback the transaction.&lt;br /&gt;&lt;br /&gt;Oh well, it was almost clever. I wonder how people solve this problem with testing? I guess another way around it might be to force your fixtures to tell you which tables they populate:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;(def-fixture fixture-a (:view-classes '(user foo bar))&lt;br /&gt;  ...&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Then the &lt;tt&gt;with-fixtures&lt;/tt&gt; macro collects all the view classes and does a clause-less delete:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;(defmacro with-fixtures ((&amp;rest fixtures) &amp;body body)&lt;br /&gt;  (let ((tables (collect-all-tables-from-view-classes-of-fixtures fixtures)))&lt;br /&gt;    `(unwind-protect (progn ,@body)&lt;br /&gt;       (progn ,@(loop for table in tables collect `(clsql:delete-records :from ,table))))))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Clever!&lt;br /&gt;&lt;br /&gt;Update: Still not very clever as the success of the above depends on the order of deletion. Dang it.</content><link rel='replies' type='application/atom+xml' href='http://uint32t.blogspot.com/feeds/8095178911262744189/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=4168503339888369834&amp;postID=8095178911262744189' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/4168503339888369834/posts/default/8095178911262744189'/><link rel='self' type='application/atom+xml' href='http://uint32t.blogspot.com/feeds/posts/default/8095178911262744189'/><link rel='alternate' type='text/html' href='http://uint32t.blogspot.com/2008/01/not-very-clever.html' title='Not very clever.'/><author><name>Sohail Somani</name><uri>https://plus.google.com/106964604256511867632</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-t-OCQSnHolk/AAAAAAAAAAI/AAAAAAAABRU/cl0ejUW86Yk/s32-c/photo.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4168503339888369834.post-514170754579890821</id><published>2008-01-20T22:35:00.000-05:00</published><updated>2008-01-20T22:51:09.754-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='cl-selenium'/><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><title type='text'>cl-selenium moved to common-lisp.net</title><content type='html'>CL-Selenium, a project that I use/contribute to is now located at &lt;a href="http://common-lisp.net/project/cl-selenium/"&gt;http://common-lisp.net/project/cl-selenium&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;A little bit about the project:&lt;br /&gt;&lt;br /&gt;Selenium is a test tool for web applications. You might use Selenium as a foundation for acceptance or functional testing. CL-Selenium is a Common Lisp interface to Selenium.&lt;br /&gt;&lt;br /&gt;Right now, it is at version 0.1 as it is very new but it is quite usable. See the &lt;a href="http://common-lisp.net/project/cl-selenium/tutorial.html"&gt;getting started tutorial&lt;/a&gt; if you don't believe me. As a bonus, it is now adsf-installable!&lt;br /&gt;&lt;br /&gt;Thanks to the common-lisp.net guys, whoever you are. Thanks also to Matt Kennedy for starting up the project!</content><link rel='replies' type='application/atom+xml' href='http://uint32t.blogspot.com/feeds/514170754579890821/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=4168503339888369834&amp;postID=514170754579890821' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/4168503339888369834/posts/default/514170754579890821'/><link rel='self' type='application/atom+xml' href='http://uint32t.blogspot.com/feeds/posts/default/514170754579890821'/><link rel='alternate' type='text/html' href='http://uint32t.blogspot.com/2008/01/cl-selenium-moved-to-common-lispnet.html' title='cl-selenium moved to common-lisp.net'/><author><name>Sohail Somani</name><uri>https://plus.google.com/106964604256511867632</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-t-OCQSnHolk/AAAAAAAAAAI/AAAAAAAABRU/cl0ejUW86Yk/s32-c/photo.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4168503339888369834.post-3155292905901330027</id><published>2008-01-19T16:49:00.000-05:00</published><updated>2008-01-19T17:20:14.933-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='clsql'/><title type='text'>Some useful CLSQL helper functions</title><content type='html'>Quite often, you need to query the database for when all values equal something or the other. For example, you might look for an authenticated user as follows (in SQL):&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SELECT USERS.* FROM USERS WHERE USER_LOGIN='sohail' AND USER_PASSWORD='myhashedpassword'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Of course, in the year 2008, no one writes their own SQL anymore (or so Rails propaganda would have you believe!) So you use something like &lt;a href="http://clsql.b9.com/"&gt;CLSQL&lt;/a&gt; and end up writing something like:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;(clsql:select 'user :where [ and [ = [ user-login ] "sohail" ] [ user-password ] 'myhashedpassword' ])&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Certainly more Lispy but still quite annoying. The Rails and Django guys have you write something that looks like:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;ModelObject.find(id=5)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I've written a couple of helper functions for CLSQL (reproduced below) that let you write:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;CL-USER&gt; (find-one 'user :user-login "sohail" :user-password "myhashedpassword")&lt;br /&gt;#&amp;lt;USER {B47D0C9}&amp;gt;&lt;br /&gt;CL-USER&gt; (find-all 'user :user-site-id 3)&lt;br /&gt;(#&amp;lt;USER {B729209}&amp;gt; #&amp;lt;USER {B72AD81}&amp;gt;)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Makes it a lot easier to write bespoke queries without resorting to CLSQL's bracket syntax or SQL itself.&lt;br /&gt;&lt;br /&gt;The code is here (would appreciate any comments as to how to clean it up a bit!):&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;(defun find-all (type &amp;rest args &amp;key (clause-op 'and) &amp;allow-other-keys)&lt;br /&gt;  (if (&gt; (length args) 0) ;; if there are any filters to apply&lt;br /&gt;      (let ((expressions (loop for (k v) on args by #'cddr&lt;br /&gt;                              collect&lt;br /&gt;                              (let ((op '=)&lt;br /&gt;                                    (value v))&lt;br /&gt;                                (make-instance 'clsql-sys:sql-relational-exp&lt;br /&gt;                                               :operator op&lt;br /&gt;                                               :sub-expressions&lt;br /&gt;                                               (list (make-instance 'clsql-sys:sql-ident-attribute :name k :qualifier nil :type nil)&lt;br /&gt;                                                     value))))))&lt;br /&gt;        (clsql:select type :flatp t&lt;br /&gt;                      :where (make-instance 'clsql-sys:sql-relational-exp&lt;br /&gt;                                            :operator clause-op&lt;br /&gt;                                            :sub-expressions expressions)))&lt;br /&gt;      (clsql:select type :flatp t)))&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;(defun find-one (&amp;rest args)&lt;br /&gt;  (let ((result (apply #'find-all args)))&lt;br /&gt;    (if (= 1 (length result))&lt;br /&gt;        (first result)&lt;br /&gt;        (error "More than one result returned when only one expected!"))))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Enjoy!</content><link rel='replies' type='application/atom+xml' href='http://uint32t.blogspot.com/feeds/3155292905901330027/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=4168503339888369834&amp;postID=3155292905901330027' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/4168503339888369834/posts/default/3155292905901330027'/><link rel='self' type='application/atom+xml' href='http://uint32t.blogspot.com/feeds/posts/default/3155292905901330027'/><link rel='alternate' type='text/html' href='http://uint32t.blogspot.com/2008/01/some-useful-clsql-helper-functions.html' title='Some useful CLSQL helper functions'/><author><name>Sohail Somani</name><uri>https://plus.google.com/106964604256511867632</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-t-OCQSnHolk/AAAAAAAAAAI/AAAAAAAABRU/cl0ejUW86Yk/s32-c/photo.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4168503339888369834.post-3055098885480197714</id><published>2008-01-14T02:54:00.000-05:00</published><updated>2008-01-21T12:14:52.229-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='cl-selenium'/><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><title type='text'>Getting started with cl-selenium</title><content type='html'>I've created some documentation for the cl-selenium package. You can find it &lt;a href="http://common-lisp.net/project/cl-selenium/tutorial.html"&gt;here&lt;/a&gt;. Let me know if you have any issues.&lt;br /&gt;&lt;br /&gt;Update: CL-Selenium has been moved to &lt;a href="http://common-lisp.net/project/cl-selenium/tutorial.html"&gt;http://common-lisp.net/project/cl-selenium/tutorial.html&lt;/a&gt;.</content><link rel='replies' type='application/atom+xml' href='http://uint32t.blogspot.com/feeds/3055098885480197714/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=4168503339888369834&amp;postID=3055098885480197714' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/4168503339888369834/posts/default/3055098885480197714'/><link rel='self' type='application/atom+xml' href='http://uint32t.blogspot.com/feeds/posts/default/3055098885480197714'/><link rel='alternate' type='text/html' href='http://uint32t.blogspot.com/2008/01/getting-started-with-cl-selenium.html' title='Getting started with cl-selenium'/><author><name>Sohail Somani</name><uri>https://plus.google.com/106964604256511867632</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-t-OCQSnHolk/AAAAAAAAAAI/AAAAAAAABRU/cl0ejUW86Yk/s32-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4168503339888369834.post-2253127633163245465</id><published>2008-01-09T00:26:00.000-05:00</published><updated>2008-12-08T21:20:05.577-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='weblocks'/><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><title type='text'>Weblocks: starting with a blank slate</title><content type='html'>If you do the customary Hello World application in Weblocks, the UI looks something like this:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_NJChfbPZ2fA/R4Rb4IYZ7FI/AAAAAAAAABE/Do4-pVKLEi0/s1600-h/Screenshot-1.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://4.bp.blogspot.com/_NJChfbPZ2fA/R4Rb4IYZ7FI/AAAAAAAAABE/Do4-pVKLEi0/s320/Screenshot-1.png" alt="" id="BLOGGER_PHOTO_ID_5153344893499337810" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The code to create this page is:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;(weblocks:defwebapp 'our-application)&lt;br /&gt;(defun init-user-session (comp)&lt;br /&gt;   (setf (weblocks:composite-widgets comp)&lt;br /&gt;         (list "Hello!")))&lt;br /&gt;(weblocks:reset-sessions)&lt;br /&gt;;; Starts the server on localhost:8080&lt;br /&gt;(weblocks:start-weblocks)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This minimal application brings in the following CSS files:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;layout.css&lt;/li&gt;&lt;br /&gt;&lt;li&gt;dialog.css&lt;/li&gt;&lt;br /&gt;&lt;li&gt;main.css&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;And the following Javscript files:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt; &lt;li&gt;scriptaculous.js&lt;/li&gt;&lt;br /&gt; &lt;li&gt;builder.js&lt;/li&gt;&lt;br /&gt; &lt;li&gt;shortcut.js&lt;/li&gt;&lt;br /&gt; &lt;li&gt;weblocks.js&lt;/li&gt;&lt;br /&gt; &lt;li&gt;dialog.js&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;Additionally, scriptalicious brings in a bunch of more dependencies that double the number of JS files.&lt;br /&gt;&lt;br /&gt;The JS files are obviously what give you the nice Ajaxian effects and desktop application-like feel when you work with Weblocks widgets. However, I typically like to start from scratch so I can control what the framework brings in.&lt;br /&gt;&lt;br /&gt;When you instantiate your Weblocks application (via &lt;tt&gt;weblocks:defwebapp&lt;/tt&gt;), the variable &lt;tt&gt;weblocks:*application-public-dependencies*&lt;/tt&gt; is set to a default list:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;CL-USER&gt; weblocks:*application-public-dependencies*&lt;br /&gt;(#P"stylesheets/layout.css" #P"stylesheets/main.css" #P"stylesheets/dialog.css"&lt;br /&gt;#P"scripts/prototype.js" #P"scripts/scriptaculous.js" #P"scripts/shortcut.js"&lt;br /&gt;#P"scripts/weblocks.js" #P"scripts/dialog.js")&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;So that answers the question of where all those files come from. Set this list to nil and reload the page. You should get something like this:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_NJChfbPZ2fA/R4ReY4YZ7GI/AAAAAAAAABM/H4RerqO9-vU/s1600-h/Screenshot-2.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://3.bp.blogspot.com/_NJChfbPZ2fA/R4ReY4YZ7GI/AAAAAAAAABM/H4RerqO9-vU/s320/Screenshot-2.png" alt="" id="BLOGGER_PHOTO_ID_5153347655163309154" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;If you look at the HTML for the page (through Firebug) you will see something like this:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_NJChfbPZ2fA/R4Re24YZ7HI/AAAAAAAAABU/5dESZm3XFhg/s1600-h/Screenshot-3.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://3.bp.blogspot.com/_NJChfbPZ2fA/R4Re24YZ7HI/AAAAAAAAABU/5dESZm3XFhg/s320/Screenshot-3.png" alt="" id="BLOGGER_PHOTO_ID_5153348170559384690" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Still pretty noisy. I don't care much about the &lt;tt&gt;meta&lt;/tt&gt; tag, but I've got to get rid of the divs.&lt;br /&gt;&lt;br /&gt;For every request, the function &lt;tt&gt;weblocks:render-page&lt;/tt&gt; is called to actually generate the necessary HTML. This function just sends the head tag contents and is where all the external references are inserted into the output. It also opens the body tag but then calls the &lt;tt&gt;render-page-body&lt;/tt&gt; function to actually output the body of the page. I redefine the function from:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;(defmethod render-page-body (body-fn)&lt;br /&gt; (with-html&lt;br /&gt;   (:div :class "page-wrapper"&lt;br /&gt;   (render-extra-tags "page-extra-top-" 3)&lt;br /&gt;   (htm (str body-fn))&lt;br /&gt;   (render-extra-tags "page-extra-bottom-" 3))))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;CL-USER&gt; (defmethod weblocks:render-page-body (body-fn)&lt;br /&gt;          (weblocks:with-html&lt;br /&gt;            (cl-who:htm (cl-who:str body-fn))))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now if you reload the page, the HTML looks something like this:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_NJChfbPZ2fA/R4Rgm4YZ7II/AAAAAAAAABc/oiK4rraBmJU/s1600-h/Screenshot-4.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://3.bp.blogspot.com/_NJChfbPZ2fA/R4Rgm4YZ7II/AAAAAAAAABc/oiK4rraBmJU/s320/Screenshot-4.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5153350094704733314" /&gt;&lt;/a&gt;&lt;br /&gt;To get rid of the footer, redefine the after method on &lt;tt&gt;render-page-body&lt;/tt&gt; to do nothing (or remove it altogether);&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;CL-USER&gt; (defmethod weblocks:render-page-body :after (rendered-html))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now the HTML looks like:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_NJChfbPZ2fA/R4RiOoYZ7JI/AAAAAAAAABk/9_54208X2Ko/s1600-h/Screenshot-5.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://2.bp.blogspot.com/_NJChfbPZ2fA/R4RiOoYZ7JI/AAAAAAAAABk/9_54208X2Ko/s320/Screenshot-5.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5153351877116161170" /&gt;&lt;/a&gt;&lt;br /&gt;The divs come from our init-session function where we defined it to be:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;(defun init-user-session (comp)&lt;br /&gt;   (setf (weblocks:composite-widgets comp)&lt;br /&gt;         (list "Hello!")))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;That was enough to keep me happy however. Hope that helps!</content><link rel='replies' type='application/atom+xml' href='http://uint32t.blogspot.com/feeds/2253127633163245465/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=4168503339888369834&amp;postID=2253127633163245465' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/4168503339888369834/posts/default/2253127633163245465'/><link rel='self' type='application/atom+xml' href='http://uint32t.blogspot.com/feeds/posts/default/2253127633163245465'/><link rel='alternate' type='text/html' href='http://uint32t.blogspot.com/2008/01/weblocks-starting-with-blank-slate.html' title='Weblocks: starting with a blank slate'/><author><name>Sohail Somani</name><uri>https://plus.google.com/106964604256511867632</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-t-OCQSnHolk/AAAAAAAAAAI/AAAAAAAABRU/cl0ejUW86Yk/s32-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_NJChfbPZ2fA/R4Rb4IYZ7FI/AAAAAAAAABE/Do4-pVKLEi0/s72-c/Screenshot-1.png' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4168503339888369834.post-5809157720134275019</id><published>2008-01-09T00:21:00.000-05:00</published><updated>2008-01-09T12:00:36.035-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sbcl'/><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><title type='text'>Code coverage in SBCL</title><content type='html'>This is the most useful thing since macros: &lt;a href="http://www.sbcl.org/manual/sb_002dcover.html"&gt;sb-cover&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;No nonsense code coverage:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;;;; The code coverage module&lt;br /&gt;(require :sb-cover)&lt;br /&gt;&lt;br /&gt;;;; Enable instrumentation&lt;br /&gt;(declaim (optimize sb-cover:store-coverage-data))&lt;br /&gt;&lt;br /&gt;;;; Force recompilation with instrumentation&lt;br /&gt;(asdf:oos 'asdf:load-op :my-project-test :force t)&lt;br /&gt;&lt;br /&gt;;;; Run the tests!&lt;br /&gt;(my-project-test::run-all-tests)&lt;br /&gt;&lt;br /&gt;;;; Create the report&lt;br /&gt;(sb-cover:report "/tmp/report/")&lt;br /&gt;&lt;br /&gt;;;; Disable instrumentation&lt;br /&gt;(declaim (optimize (sb-cover:store-coverage-data 0)))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;See &lt;a href="http://jsnell.iki.fi/blog/archive/2007-05-03-code-coverage-tool-for-sbcl.html"&gt;Code coverage tool for SBCL&lt;/a&gt; for some nice output as well. I had no idea the cl-ppcre tests had such good coverage. Damn.</content><link rel='replies' type='application/atom+xml' href='http://uint32t.blogspot.com/feeds/5809157720134275019/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=4168503339888369834&amp;postID=5809157720134275019' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/4168503339888369834/posts/default/5809157720134275019'/><link rel='self' type='application/atom+xml' href='http://uint32t.blogspot.com/feeds/posts/default/5809157720134275019'/><link rel='alternate' type='text/html' href='http://uint32t.blogspot.com/2008/01/code-coverage-in-sbcl.html' title='Code coverage in SBCL'/><author><name>Sohail Somani</name><uri>https://plus.google.com/106964604256511867632</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-t-OCQSnHolk/AAAAAAAAAAI/AAAAAAAABRU/cl0ejUW86Yk/s32-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4168503339888369834.post-4948209936404284510</id><published>2008-01-05T01:34:00.000-05:00</published><updated>2008-01-05T02:35:52.922-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='asdf'/><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><title type='text'>Handling vendor/third-party libraries in Common Lisp projects</title><content type='html'>Any non-trivial application is going to have some third party dependencies. When working in C++, I have this very annoying habit to always have all third-party source in source control so that I could build it. And in fact, I would build the third-party libraries along with my own code. The benefits of this setup are:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt; &lt;li&gt;No extra package installation besides compilers and build tools (ideally, I'd put these in as well!)&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;Package versions are fixed.&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;Free to patch libraries because SVN would usually merge nicely on library upgrades.&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;As we know, &lt;a href="http://uint32t.blogspot.com/2007/12/compiling-common-lisp.html"&gt;Common Lisp is compiled&lt;/a&gt; so this annoying habit should carry over nicely. Unfortunately, until very recently, I just didn't get how I would do it. Tonight, I threw together a small hack that works well enough which is all you can hope for at the end of the day.&lt;br /&gt;&lt;br /&gt;Typically, my project layouts look like:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;sohail@dev:~/project$ find . -maxdepth 1&lt;br /&gt;.&lt;br /&gt;./src&lt;br /&gt;./test&lt;br /&gt;./project.asd&lt;br /&gt;./project-test.asd&lt;br /&gt;./vendor&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The vendor directory is where I usually stuff all the third-party dependencies. For example, you might have cl-fad as one of your dependencies. In the project's ASD file, you would add cl-fad as one of the modules you depend on. But the only problem is, how do you tell ASDF to look in &lt;tt&gt;~/project/vendor/cl-fad&lt;/tt&gt; for the asd files?&lt;br /&gt;&lt;br /&gt;Obviously, the answer is &lt;/tt&gt;asdf:*central-registry*&lt;/tt&gt;, a list of directory pathnames that ASDF searches when asked to load something. But the problem is that I can check out my project anywhere on the file system, so I can't hardcode the paths.&lt;br /&gt;&lt;br /&gt;So dynamically, we need to figure out the root of the checkout, call it &lt;tt&gt;*project-root*&lt;/tt&gt;, get a list of all the ASDF-loadable packages in vendor and add their respective directories to &lt;tt&gt;asdf:*central-registry*&lt;/tt&gt;. Simple enough, but I know more about pathnames than I ever wanted to know! Here is the code (put into project.asd):&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;(defparameter *project-root*&lt;br /&gt;  (make-pathname :directory (pathname-directory *load-truename*)))&lt;br /&gt;&lt;br /&gt;(defparameter *vendor-root*&lt;br /&gt;  (merge-pathnames "vendor/" *project-root*))&lt;br /&gt;&lt;br /&gt;(defparameter *asd-wildcard* &lt;br /&gt;  (merge-pathnames "*/*.asd" *vendor-root*))&lt;br /&gt;&lt;br /&gt;(defparameter *all-asd-files*&lt;br /&gt;  (directory *asd-wildcard*))&lt;br /&gt;&lt;br /&gt;(dolist (asd-file *all-asd-files*)&lt;br /&gt;  (pushnew (make-pathname :directory (directory-namestring asd-file))&lt;br /&gt;           asdf:*central-registry*&lt;br /&gt;           :test #'equal))&lt;br /&gt;&lt;br /&gt;(defsystem myproject&lt;br /&gt;   ...&lt;br /&gt;   :depends-on (#:cl-fad))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Fun! Hopefully someone knows a simpler way to do this but this makes me happy for now!</content><link rel='replies' type='application/atom+xml' href='http://uint32t.blogspot.com/feeds/4948209936404284510/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=4168503339888369834&amp;postID=4948209936404284510' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/4168503339888369834/posts/default/4948209936404284510'/><link rel='self' type='application/atom+xml' href='http://uint32t.blogspot.com/feeds/posts/default/4948209936404284510'/><link rel='alternate' type='text/html' href='http://uint32t.blogspot.com/2008/01/handling-vendorthird-party-libraries-in.html' title='Handling vendor/third-party libraries in Common Lisp projects'/><author><name>Sohail Somani</name><uri>https://plus.google.com/106964604256511867632</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-t-OCQSnHolk/AAAAAAAAAAI/AAAAAAAABRU/cl0ejUW86Yk/s32-c/photo.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4168503339888369834.post-6566673590944996126</id><published>2008-01-03T13:50:00.000-05:00</published><updated>2008-01-03T16:36:57.262-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='weblocks'/><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><title type='text'>A login widget for Weblocks</title><content type='html'>Update: Thanks to some comments from readers, I've made an updated version. Please see the code &lt;a href="http://taggedtype.net/~sohail/login.lisp"&gt;here&lt;/a&gt;. Specifically, the concept of the &lt;tt&gt;auth-provider&lt;/tt&gt; and the &lt;tt&gt;auth-login-fields&lt;/tt&gt; have gone the way of the dodo and been merged into a singular &lt;tt&gt;auth-method&lt;/tt&gt;.&lt;br /&gt;&lt;br /&gt;In this &lt;a href="http://uint32t.blogspot.com/2007/12/weblocks-doing-first-time-setup-for-web.html"&gt;earlier post&lt;/a&gt;, I abused the Weblocks dataform object to implement a widget for creating a new user.&lt;br /&gt;&lt;br /&gt;I have since been heads down coding but last night, I teased the login/authentication logic apart from the rest of my app and I have created a login widget that should be usable as a component. The main concept is the concept of an authentication provider, called the &lt;tt&gt;auth-provider&lt;/tt&gt;. This is the part that the application writer fills in. Here is a sample:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;;;; Not always just user/login :-)&lt;br /&gt;(def-auth-login-fields my-auth-login-fields&lt;br /&gt;  ((site&lt;br /&gt;    :initarg :site&lt;br /&gt;    :accessor my-auth-login-fields-site&lt;br /&gt;    :type string ; Weblocks types&lt;br /&gt;    :initform nil)&lt;br /&gt;   (login&lt;br /&gt;    :initarg :login&lt;br /&gt;    :accessor my-auth-login-fields-login&lt;br /&gt;    :type string&lt;br /&gt;    :initform nil)&lt;br /&gt;   (password&lt;br /&gt;    :initarg :password&lt;br /&gt;    :accessor my-auth-login-fields-password&lt;br /&gt;    :type password&lt;br /&gt;    :initform nil)))&lt;br /&gt;&lt;br /&gt;;;; In this instance, only a tag but could have state.&lt;br /&gt;(defclass my-auth-provider ()&lt;br /&gt;  ())&lt;br /&gt;&lt;br /&gt;;;; Helper function&lt;br /&gt;(defun make-my-auth-provider ()&lt;br /&gt;  (make-instance 'my-auth-provider))&lt;br /&gt;&lt;br /&gt;;;; The meat of it - return a generalized boolean. The result of this method is returned&lt;br /&gt;;;; to the user&lt;br /&gt;(defmethod auth-provider-authenticate ((map my-auth-provider) (fields my-auth-login-fields))&lt;br /&gt;  (user-find-match (site-find (my-auth-login-fields-site fields))&lt;br /&gt;                   (my-auth-login-fields-login fields)&lt;br /&gt;                   (my-auth-login-fields-password fields)))&lt;br /&gt;&lt;br /&gt;;;; The auth-provider and auth-login-fields are intimately connected.&lt;br /&gt;(defmethod auth-provider-make-fields ((map my-auth-provider))&lt;br /&gt;  (make-instance 'my-auth-login-fields :auth-provider map))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To use the login widget:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;(defmacro current-user ()&lt;br /&gt;  `(hunchentoot:session-value 'current-user))&lt;br /&gt;&lt;br /&gt;(defun init-user-session (comp)  &lt;br /&gt;  (with-flow (composite-widgets comp)&lt;br /&gt;    ;; The value returned here is whatever was returned by&lt;br /&gt;    ;; auth-provider-authenticate. The widget does not return&lt;br /&gt;    ;; until auth-provider-authenticate returns not nil.&lt;br /&gt;    (setf (current-user)&lt;br /&gt;          (yield (make-instance 'login&lt;br /&gt;                                :auth-provider (make-my-auth-provider))))&lt;br /&gt;    (unless (current-user)&lt;br /&gt;      (error "Um... User wasn't returned? This world is crazy. Atleast I still have my Lisp."))&lt;br /&gt;  ...&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Here is some code you should be able to copy-and-paste (you still need to write an &lt;tt&gt;auth-provider&lt;/tt&gt;.) Let me know if you think there are improvements to be made. I'd like to submit this to Slava when I get a round tuit.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;(defwidget login (weblocks:composite)&lt;br /&gt;  ((auth-provider&lt;br /&gt;    :accessor login-auth-provider&lt;br /&gt;    :initarg :auth-provider)))&lt;br /&gt;&lt;br /&gt;(defwidget login-form (weblocks:dataform)&lt;br /&gt;  ())&lt;br /&gt;&lt;br /&gt;(defmethod initialize-instance :after ((self login)&lt;br /&gt;                                       &amp;rest args&lt;br /&gt;                                       &amp;key auth-provider &lt;br /&gt;                                       (login-title "Login")&lt;br /&gt;                                       &amp;allow-other-keys)&lt;br /&gt;  (declare (ignore args))&lt;br /&gt;  (let ((fields (auth-provider-make-fields auth-provider)))&lt;br /&gt;    (setf (widget-name self) "login-composite")&lt;br /&gt;    (setf (composite-widgets self)&lt;br /&gt;          (list (lambda () (with-html (:h1 (str login-title))))&lt;br /&gt;                (make-instance 'login-form&lt;br /&gt;                               :name 'loginform&lt;br /&gt;                               :data fields&lt;br /&gt;                               :ui-state :form&lt;br /&gt;                               :allow-close-p nil&lt;br /&gt;                               :on-success&lt;br /&gt;                               (lambda (&amp;rest args)&lt;br /&gt;                                 (declare (ignore args))&lt;br /&gt;                                 (answer self (slot-value fields 'result))))))))&lt;br /&gt;&lt;br /&gt;(defclass auth-login-fields ()&lt;br /&gt;  ((auth-provider&lt;br /&gt;    :accessor auth-login-fields-auth-provider&lt;br /&gt;    :initarg :auth-provider)&lt;br /&gt;   (result)))&lt;br /&gt;&lt;br /&gt;(defun authenticate (provider fields)&lt;br /&gt;  (let ((result (auth-provider-authenticate provider fields)))&lt;br /&gt;    (if result&lt;br /&gt;        (progn&lt;br /&gt;          (tbnl:log-message* "Successful authentication")&lt;br /&gt;          (setf (slot-value fields 'result) result)&lt;br /&gt;          (values t nil))&lt;br /&gt;        (progn&lt;br /&gt;          (tbnl:log-message* "Failed authentication: ~A" fields)&lt;br /&gt;          (values nil '((foo "Authentication failed")))))))&lt;br /&gt;&lt;br /&gt;;;; Weblocks hooks&lt;br /&gt;(defmethod weblocks:update-object-from-request :around ((fields auth-login-fields)&lt;br /&gt;                                                        &amp;rest args)&lt;br /&gt;  (multiple-value-bind (success failed-slots)&lt;br /&gt;      (call-next-method)&lt;br /&gt;    (if success&lt;br /&gt;        (authenticate (auth-login-fields-auth-provider fields) fields)&lt;br /&gt;        (values success failed-slots))))&lt;br /&gt;&lt;br /&gt;(defmethod weblocks:render-form-controls ((obj auth-login-fields) &lt;br /&gt;                                          &amp;rest keys &lt;br /&gt;                                          &amp;key action &lt;br /&gt;                                          &amp;allow-other-keys)&lt;br /&gt;  (with-html&lt;br /&gt;    (:div :class "submit"&lt;br /&gt;   (render-button *submit-control-name* :value "Login"))))&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;(defmethod weblocks:dataform-submit-action ((obj login-form) data &amp;rest args)&lt;br /&gt;  (apply #'weblocks:update-object-from-request data :persist-object-p nil args))&lt;br /&gt;&lt;br /&gt;;;; AUTH-PROVIDER GENERIC&lt;br /&gt;&lt;br /&gt;(defgeneric auth-provider-authenticate (auth-provider fields)&lt;br /&gt;  (:documentation "Return a generalized boolean to indicate&lt;br /&gt;whether the fields provided authenticate a user."))&lt;br /&gt;&lt;br /&gt;(defgeneric auth-provider-make-fields (auth-provider)&lt;br /&gt;  (:documentation "Return the fields that auth-provider needs&lt;br /&gt;to authenticate users. This is in the form of a new CLOS object instance.&lt;br /&gt;&lt;br /&gt;The types of the fields should be specified to be one of the weblocks&lt;br /&gt;types (see weblocks/src/types/*.lisp)"))&lt;br /&gt;&lt;br /&gt;(defmacro def-auth-login-fields (name &amp;body body)&lt;br /&gt;  "A macro used to define login fields."&lt;br /&gt;  `(defclass ,name (auth-login-fields)&lt;br /&gt;     ,@body))&lt;br /&gt;&lt;/pre&gt;</content><link rel='replies' type='application/atom+xml' href='http://uint32t.blogspot.com/feeds/6566673590944996126/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=4168503339888369834&amp;postID=6566673590944996126' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/4168503339888369834/posts/default/6566673590944996126'/><link rel='self' type='application/atom+xml' href='http://uint32t.blogspot.com/feeds/posts/default/6566673590944996126'/><link rel='alternate' type='text/html' href='http://uint32t.blogspot.com/2008/01/login-widget-for-weblocks.html' title='A login widget for Weblocks'/><author><name>Sohail Somani</name><uri>https://plus.google.com/106964604256511867632</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-t-OCQSnHolk/AAAAAAAAAAI/AAAAAAAABRU/cl0ejUW86Yk/s32-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4168503339888369834.post-4118681113974870907</id><published>2007-12-30T18:59:00.000-05:00</published><updated>2007-12-30T21:03:43.875-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><title type='text'>Compiling Common Lisp</title><content type='html'>When a language is pre-compiled (like C or C++) there is a step of compiling and linking the source code into a form that can be executed directly on the target hardware. The assembling of source into an executable form is usually handled by a build system like &lt;a href="http://scons.org"&gt;SCons&lt;/a&gt; or Make.&lt;br /&gt;&lt;br /&gt;The CPython implementation of the Python language uses an interpreter. There is no compile step involved (it is implicitly compiled to bytecode).&lt;br /&gt;&lt;br /&gt;Common Lisp implementations are somewhere in between in that there is a compile step but you can still use them as interpreters. For example, &lt;a href="http://www.sbcl.org"&gt;SBCL&lt;/a&gt; creates these files called FASLs which seem to stand stand for "FASt Loading". Their use should be self-explanatory. The formats are implementation-dependent meaning that they are not portable between implementations (not like .class files for Java, for example.) I couldn't figure out what SBCL stores in it's fasls but my guess is that it is a bytecode.&lt;br /&gt;&lt;br /&gt;While developing a library, it is useful to have an interpreter-like environment. However, when you are using a library, you don't want to load and recompile the source every single time. What you want to do is compile the library's files into FASL format and have your implementation load them when necessary.&lt;br /&gt;&lt;br /&gt;That is where &lt;a href="http://constantly.at/lisp/asdf/"&gt;ASDF&lt;/a&gt; comes in. ASDF, which stands for "Another System Definition Facility, is a way to define your projects so that they can be compiled and loaded along with whatever they depend on, in the right order. The rest of this post covers what I did to get a project and a testing system setup. Installing ASDF is out of scope here but if you use SBCL, you already have it.&lt;br /&gt;&lt;br /&gt;First, the file system layout that I tend to use:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;sohail@dev:~$ cd project/&lt;br /&gt;sohail@dev:~/project$ find . -not -iname "*.fasl" -and -not -iname "*~"&lt;br /&gt;.&lt;br /&gt;./src&lt;br /&gt;./src/package.lisp&lt;br /&gt;./src/code.lisp&lt;br /&gt;./test&lt;br /&gt;./test/package.lisp&lt;br /&gt;./test/tests.lisp&lt;br /&gt;./project.asd&lt;br /&gt;./project-test.asd&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;ASDF looks for &lt;tt&gt;.asd&lt;/tt&gt; (a system definition?) files in whatever paths are defined in the list &lt;tt&gt;asdf:*central-registry*&lt;/tt&gt;. So the first thing you want to do is add the above path to the list. The way I did it (which is not optimal) is I modified &lt;tt&gt;~/.sbclrc&lt;/tt&gt; to include the following lines:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;(require :asdf)&lt;br /&gt;(push #p"/home/sohail/project/" asdf:*central-registry*)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Open up &lt;tt&gt;src/package.lisp&lt;/tt&gt; in Emacs and enter the following:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;(defpackage project&lt;br /&gt;  (:use common-lisp)&lt;br /&gt;  (:export some-function))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This uses the standard defpackage macro to define a Common Lisp package. A package is a mechanism to map symbols to names. The above exports a symbol called &lt;tt&gt;some-function&lt;/tt&gt;. Next, open up &lt;tt&gt;src/code.lisp&lt;/tt&gt; and enter the following:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;(in-package #:project)&lt;br /&gt;&lt;br /&gt;(defun some-function (a)&lt;br /&gt;  (format *standard-output* "a is: ~A" a))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This uses the standard &lt;tt&gt;in-package&lt;/tt&gt; macro to set the current package to be &lt;tt&gt;project&lt;/tt&gt;. If you are using Slime, you can try loading &lt;tt&gt;code.lisp&lt;/tt&gt; (C-c C-l) and if you haven't loaded package.lisp, you should get a message telling you that project does not designate a package. To make it work, load &lt;tt&gt;package.lisp&lt;/tt&gt; and then &lt;tt&gt;code.lisp&lt;/tt&gt;. In a large project, it would be impossible to remember which order to load things in.&lt;br /&gt;&lt;br /&gt;The next thing to do is to make this loadable via ASDF. To do this, open up &lt;tt&gt;project.asd&lt;/tt&gt; and enter the following:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;;;; Define a package to define our system definition&lt;br /&gt;(defpackage #:project-asd&lt;br /&gt;  (:use :cl :asdf)) ;; Use ASDF (obviously!)&lt;br /&gt;&lt;br /&gt;;;; All our definitions&lt;br /&gt;(in-package project-asd)&lt;br /&gt;&lt;br /&gt;;;; Our system is called project&lt;br /&gt;(defsystem #:project&lt;br /&gt;  :name "My project!"&lt;br /&gt;  :components ((:module "src" &lt;br /&gt;                        :components ((:file "package")&lt;br /&gt;                                     (:file "code"&lt;br /&gt;                                            :depends-on ("package"))))))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;We document that &lt;tt&gt;code.lisp&lt;/tt&gt; depends on &lt;tt&gt;package.lisp&lt;/tt&gt;. Another way to write the defsystem could have been:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;(defsystem #:project&lt;br /&gt;  :name "My project!"&lt;br /&gt;  :components ((:file "src/package")&lt;br /&gt;               (:file "src/code" :depends-on ("src/package"))))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I prefer using a module because if you have multiple modules that have dependencies, the dependencies are easier to define. For example, you might have a "model" module that depends on the "database" module.&lt;br /&gt;&lt;br /&gt;Now go to the REPL and type &lt;tt&gt;(asdf:oos 'asdf:load-op #:project)&lt;/tt&gt;. Assuming you set up &lt;tt&gt;asdf:*central-registry*&lt;/tt&gt; as above, you should get output like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;; compiling file "/home/sohail/project/src/package.lisp" (written 30 DEC 2007 05:23:41 PM):&lt;br /&gt;; compiling (DEFPACKAGE PROJECT ...)&lt;br /&gt;&lt;br /&gt;; /home/sohail/project/src/package.fasl written&lt;br /&gt;; compilation finished in 0:00:00&lt;br /&gt;; compiling file "/home/sohail/project/src/code.lisp" (written 30 DEC 2007 05:23:37 PM):&lt;br /&gt;; compiling (IN-PACKAGE #:PROJECT)&lt;br /&gt;; compiling (DEFUN SOME-FUNCTION ...)&lt;br /&gt;&lt;br /&gt;; /home/sohail/project/src/code.fasl written&lt;br /&gt;; compilation finished in 0:00:00&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Even though we told ASDF to "load" the system, since we hadn't compiled the files, ASDF compiled them for us, in the right order. Type &lt;tt&gt;(project:some-function 5)&lt;/tt&gt; if you want to convince yourself that it worked!&lt;br /&gt;&lt;br /&gt;Now we want to add a package to test our code. To do this, open up &lt;tt&gt;project-test.asd&lt;/tt&gt; and enter the following:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;(defpackage #:project-test-asd&lt;br /&gt;  (:use :cl :asdf))&lt;br /&gt;&lt;br /&gt;(in-package project-test-asd)&lt;br /&gt;&lt;br /&gt;(defsystem #:project-test&lt;br /&gt;  :name "Tests for my project!"&lt;br /&gt;  :depends-on (#:project)&lt;br /&gt;  :components ((:module "test"&lt;br /&gt;                        :components ((:file "package")&lt;br /&gt;                                     (:file "tests"&lt;br /&gt;                                            :depends-on ("package"))))))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The only new thing here is the use of the &lt;tt&gt;:depends-on&lt;/tt&gt; keyword argument to defsystem. Here, we are telling ASDF that before loading/compiling &lt;tt&gt;project-test&lt;/tt&gt;, the &lt;tt&gt;project&lt;/tt&gt; system must have done so successfully.&lt;br /&gt;&lt;br /&gt;Mechanically, we add the following to &lt;tt&gt;test/package.lisp&lt;/tt&gt;:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;(defpackage #:project-test&lt;br /&gt;  (:use :cl) ;; Could also use the project package but I like to qualify symbols&lt;br /&gt;  (:export run-tests))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And the following into &lt;tt&gt;test/tests.lisp&lt;/tt&gt;:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;(in-package #:project-test)&lt;br /&gt;&lt;br /&gt;(defmacro assert-string-equal (form1 form2)&lt;br /&gt;  `(if (string= ,form1 ,form2)&lt;br /&gt;       (print "Passed!")&lt;br /&gt;       (print "Failed!")))&lt;br /&gt;&lt;br /&gt;(defun run-tests ()&lt;br /&gt;  (let (output)&lt;br /&gt;    (let ((*standard-output*&lt;br /&gt;           (make-string-output-stream)))&lt;br /&gt;      (project:some-function 5)&lt;br /&gt;      (setq output (get-output-stream-string *standard-output*)))&lt;br /&gt;    (assert-string-equal "a is: 5"&lt;br /&gt;                         output)))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now save all those files, go back to the REPL and type: &lt;br /&gt;&lt;pre&gt;&lt;br /&gt;(asdf:oos 'asdf:load-op #:project-test)&lt;br /&gt;(project-test:run-tests)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;You should see "Passed!" output.&lt;br /&gt;&lt;br /&gt;Before you start writing your own test framework, take a look at &lt;a href="http://common-lisp.net/project/bese/FiveAM.html"&gt;FiveAM&lt;/a&gt;. You can also see &lt;a href="http://uint32t.blogspot.com/2007/12/my-thoughts-about-fiveam-common-lisp.html"&gt;my thoughts about FiveAM&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Happy Lisping!</content><link rel='replies' type='application/atom+xml' href='http://uint32t.blogspot.com/feeds/4118681113974870907/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=4168503339888369834&amp;postID=4118681113974870907' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/4168503339888369834/posts/default/4118681113974870907'/><link rel='self' type='application/atom+xml' href='http://uint32t.blogspot.com/feeds/posts/default/4118681113974870907'/><link rel='alternate' type='text/html' href='http://uint32t.blogspot.com/2007/12/compiling-common-lisp.html' title='Compiling Common Lisp'/><author><name>Sohail Somani</name><uri>https://plus.google.com/106964604256511867632</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-t-OCQSnHolk/AAAAAAAAAAI/AAAAAAAABRU/cl0ejUW86Yk/s32-c/photo.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4168503339888369834.post-9062121948302562332</id><published>2007-12-27T21:43:00.000-05:00</published><updated>2007-12-27T23:29:25.740-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><title type='text'>My thoughts about FiveAM - Common Lisp testing framework</title><content type='html'>I spent all day today adding some tests as I needed to do some refactoring. I wanted to choose a CL testing framework and came across Phil Gregory's great post: &lt;a href="http://aperiodic.net/phil/archives/Geekery/notes-on-lisp-testing-frameworks.html"&gt;Common Lisp Testing Frameworks&lt;/a&gt;. I read through his review and as you can tell by the title, I chose to go with &lt;a href="http://common-lisp.net/project/bese/FiveAM.html"&gt;FiveAM&lt;/a&gt;. I won't go over what Phil covered in his post except to say that FiveAM has what I expect in a testing framework and more.&lt;br /&gt;&lt;br /&gt;You can jump to the &lt;a href="#bottom-line"&gt;the bottom line&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Simple tests are defined simply:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;(5am:test my-test-case&lt;br /&gt;  (5am::is (= 2 (1+ 1)))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;FiveAM has test dependencies:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;(5am:test (my-other-test-case :depends-on my-test-case)&lt;br /&gt;  (5am::is (= 3 (1+ (1+ 1)))))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;FiveAM allows you to group tests using test suites:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;(def-suite arith-tests :description "Arithmetic tests")&lt;br /&gt;(in-suite arith-tests)&lt;br /&gt;(... above tests ...)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;However, the grouping is only useful for selecting which tests to run. You can't (for example) make one set of tests dependent on another set of tests. This makes the feature only useful for organizational purposes. It isn't a deal-killer especially since you can write a function to work around this limitation like the one below:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;(defun add-test-dependency (a b)&lt;br /&gt;  "Make test-suite a depend on test-suite b by making every test in a depend&lt;br /&gt;   on every test in b"&lt;br /&gt;  (let ((suite-a (safe-get-test a))&lt;br /&gt;        (suite-b (safe-get-test b))&lt;br /&gt;        suite-a-tests suite-b-tests)&lt;br /&gt;    (maphash #'(lambda (sym obj)&lt;br /&gt;                 (declare (ignore obj))&lt;br /&gt;                 (push sym suite-b-tests))&lt;br /&gt;             (5am::tests suite-b))&lt;br /&gt;    (maphash #'(lambda (sym obj)&lt;br /&gt;                 (declare (ignore sym))&lt;br /&gt;                 (push obj suite-a-tests))&lt;br /&gt;             (5am::tests suite-a))&lt;br /&gt;    (loop for test-name in suite-a-tests&lt;br /&gt;       do&lt;br /&gt;         (let* ((test (safe-get-test test-name))&lt;br /&gt;                (depends-on (5am::depends-on test)))&lt;br /&gt;           (let ((new-depends-on&lt;br /&gt;                  (if depends-on &lt;br /&gt;                      `(and ,depends-on ,suite-b-tests)&lt;br /&gt;                      `(and ,@suite-b-tests))))&lt;br /&gt;             (print test)&lt;br /&gt;             (print new-depends-on)&lt;br /&gt;             (setf (5am::depends-on test) new-depends-on))))))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;In hindsight, a smarter way to do this would be to introduce a pseudo-test in b that depended on every test in b and then every test in a would depend on this pseudo-test. Ah well.&lt;br /&gt;&lt;br /&gt;But my favourite feature is the fact that FiveAM lets you generate samples from a distribution of inputs and feed them into your functions to test. Here is one stupid example:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;(test encode-password&lt;br /&gt;  "Passwords are encoded as ALGO$SALT$HASHED-PASSWORD. This code tests&lt;br /&gt;   that the structure is correct and that the components of the encoding&lt;br /&gt;   pass sanity checks. In the case of encode-password the salt is randomly &lt;br /&gt;   generated."&lt;br /&gt;  (for-all ((raw-password (gen-string &lt;br /&gt;                           :length (gen-integer :min 5 :max 10)&lt;br /&gt;                           :elements (gen-character :code-limit (char-code #\~)&lt;br /&gt;                                                    :alphanumericp #'alphanumericp&lt;br /&gt;                                                    :code (gen-integer :min (char-code #\Space)&lt;br /&gt;                                                                       :max (char-code #\~))))))&lt;br /&gt;    (let ((encoded-pw (myapp::encode-password raw-password)))&lt;br /&gt;      (validate-encoded-password encoded-pw))))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The &lt;tt&gt;for-all&lt;/tt&gt; macro takes a list of generators and iterates through a set of samples for all the represented values. This is done through the use of generators (&lt;tt&gt;gen-string&lt;/tt&gt; and friends.) In this case, I am iterating through a distribution of strings that generates a string between 5 and 10 characters long the contents of which are in the "interesting" ASCII character range. The body of the &lt;tt&gt;for-all&lt;/tt&gt; macro is dedicated to encoding the password and validating that the encoding is sane. Although it isn't important, &lt;tt&gt;validate-encoded-password&lt;/tt&gt; looks like:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;(defun validate-encoded-password (encoded-pw)&lt;br /&gt;  (destructuring-bind (algo salt hash)&lt;br /&gt;      (split-sequence:split-sequence #\$ encoded-pw)&lt;br /&gt;    (is (string= "md5" algo))&lt;br /&gt;    (is (= 5 (length salt)))&lt;br /&gt;    (is (= 32 (length hash)))))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;h2&gt;&lt;a name="bottom-line"&gt;The Bottom Line&lt;/a&gt;&lt;/h2&gt;&lt;br /&gt;5am is very suitable for testing in CL but test groups should really have the ability to take part in dependencies.</content><link rel='replies' type='application/atom+xml' href='http://uint32t.blogspot.com/feeds/9062121948302562332/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=4168503339888369834&amp;postID=9062121948302562332' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/4168503339888369834/posts/default/9062121948302562332'/><link rel='self' type='application/atom+xml' href='http://uint32t.blogspot.com/feeds/posts/default/9062121948302562332'/><link rel='alternate' type='text/html' href='http://uint32t.blogspot.com/2007/12/my-thoughts-about-fiveam-common-lisp.html' title='My thoughts about FiveAM - Common Lisp testing framework'/><author><name>Sohail Somani</name><uri>https://plus.google.com/106964604256511867632</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-t-OCQSnHolk/AAAAAAAAAAI/AAAAAAAABRU/cl0ejUW86Yk/s32-c/photo.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4168503339888369834.post-5509277278364069211</id><published>2007-12-23T15:23:00.000-05:00</published><updated>2007-12-26T16:14:11.148-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='hunchentoot'/><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><title type='text'>(Ab)using Hunchentoot's dispatch mechanism to implement authentication</title><content type='html'>With any application in general, it is important to ensure that the user is allowed to use the system. This is known as authorization. The first step to authorizing a user is to authenticate the user or ensure the user is who they say they are. Once the user is authenticated, then the application can decide what operations/views the user is authorized to use. The de facto standard way of authenticating is by forcing the user to input a user name and password. &amp;lt;rant&amp;gt;I personally hate this.&amp;lt;/rant&amp;gt;&lt;br /&gt;&lt;br /&gt;With a stateful application, such as a desktop application, authentication is pretty straightforward: just authenticate at application launch.&lt;br /&gt;&lt;br /&gt;Unfortunately, HTTP is stateless (keep-alives aside.) Continuation-based frameworks such as &lt;a href="http://common-lisp.net/project/cl-weblocks/"&gt;Weblocks&lt;/a&gt; totally remove the problem by allowing you to write your app as if it were stateful. It is quite beautiful. Continuation-based frameworks have their own uses but if they don't fit your needs, then you need a different approach.&lt;br /&gt;&lt;br /&gt;The usual way to implement authentication for a web application to check if the client has been authenticated on each page request. Obviously, this is quite annoying if you have to do it yourself. Frameworks like ASP.NET handle this for you (\o/ frameworks) by some careful editing of XML files. As I understand it, you tell ASP.NET what resources are protected and how to authenticate when protected resources are accessed. You fill in the authentication blanks with some helper classes that MS wrote for you. ASP.NET then generates a random session ID and sets a cookie which is used in subsequent requests to the web application. Nothing special. This is wide open to &lt;a href="http://en.wikipedia.org/wiki/Man-in-the-middle_attack"&gt;MITM attacks&lt;/a&gt; if you don't use SSL or sufficiently secure your session ID.&lt;br /&gt;&lt;br /&gt;Hunchentoot does none of the above but it has all the ingredients to make it possible. The idea is that we want to intercept every request and check if a protected resource is being accessed. If a protected resource is being accessed, then we need to either force authentication or pass the request along if the session is authenticated. One way to do this is to insert the appropriate code in every page using macros. Another way is to use Hunchentoot's dispatch table. Personally, I'm partial to the method covered here because it doesn't require you to remember to secure your pages. Another benefit with this method is that you can also protect non-function resources such as when you serve static content.&lt;br /&gt;&lt;br /&gt;When Hunchentoot receives a request, it iterates through &lt;tt&gt;hunchentoot:*dispatch-table*&lt;/tt&gt; executing each dispatch function. Each of these functions is meant to return a function that will serve the request when applicable. Therefore, Hunchentoot executes the first such function returned while iterating. Hopefully the solution that I am thinking of is clear: insert a dispatch function that gets called before all others that checks for an authenticated session and redirects to a login page if one is not found. Here is an example of such a dispatch function:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;(defun check-login-dispatcher (request)&lt;br /&gt;  (unless (or hunchentoot:*session*&lt;br /&gt;              (starts-with (tbnl:script-name request)&lt;br /&gt;                           "/public"))&lt;br /&gt;    #'oopsie-need-to-login))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Where &lt;tt&gt;oopsie-need-to-login&lt;/tt&gt; looks like:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;(defun oopsie-need-to-login ()&lt;br /&gt;  (page-body&lt;br /&gt;    (:p "Before you can continue, you must login. For now,&lt;br /&gt;         just " (:a :href "/public/auto-login" "click here"))))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And &lt;tt&gt;public/auto-login&lt;/tt&gt; looks like:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;(defun auto-login ()&lt;br /&gt;  (hunchentoot:start-session)&lt;br /&gt;  (hunchentoot:redirect "/"))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;In real life, you would obviously not automatically log someone in and instead have the regular username / password form.&lt;br /&gt;&lt;br /&gt;Further, to prevent session hijacking with Hunchentoot, you need to do atleast the following:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt; &lt;li&gt;Use SSL (HTTPS)&lt;/li&gt;&lt;br /&gt; &lt;li&gt;&lt;pre&gt;;; See Hunchentoot documentation for the meaning of these variables&lt;br /&gt;(setf hunchentoot:*use-remote-addr-for-sessions* t)&lt;br /&gt;(setf hunchentoot:*use-user-agent-for-sessions* t)&lt;br /&gt;&lt;/pre&gt;&lt;/li&gt;&lt;br /&gt; &lt;li&gt;You need to also redefine &lt;tt&gt;hunchentoot::get-next-session-id&lt;/tt&gt; because it uses sequential session IDs which leaves you open to guessing attacks. Imagine an attacker logs in just before you and knows that the next session is N+1. Not fun.&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;The above method for authenticating a user is secure so long as the above three are implemented. I haven't done the third yet so I couldn't say what to do there. I think you need to generate a (theoretically) truly random number somewhere.&lt;br /&gt;&lt;br /&gt;Update: You do *not* need to redefine &lt;tt&gt;hunchentoot::get-next-session-id&lt;/tt&gt;. It turns out that information was made on a bad assumption that all the information going into the session id string was deterministic. On reading the code more, there are two elements of randomness inserted into the session string:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt; &lt;li&gt;The session start time&lt;/li&gt;&lt;br /&gt; &lt;li&gt;A random string generated once per server &lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;I think the above two are sufficient to make it secure for some value of secure. I believe the secrecy of the random string is important to the security. But I am no security expert!</content><link rel='replies' type='application/atom+xml' href='http://uint32t.blogspot.com/feeds/5509277278364069211/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=4168503339888369834&amp;postID=5509277278364069211' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/4168503339888369834/posts/default/5509277278364069211'/><link rel='self' type='application/atom+xml' href='http://uint32t.blogspot.com/feeds/posts/default/5509277278364069211'/><link rel='alternate' type='text/html' href='http://uint32t.blogspot.com/2007/12/abusing-hunchentoots-dispatch-mechanism.html' title='(Ab)using Hunchentoot&apos;s dispatch mechanism to implement authentication'/><author><name>Sohail Somani</name><uri>https://plus.google.com/106964604256511867632</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-t-OCQSnHolk/AAAAAAAAAAI/AAAAAAAABRU/cl0ejUW86Yk/s32-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4168503339888369834.post-4170014763613975051</id><published>2007-12-18T19:34:00.000-05:00</published><updated>2007-12-18T20:34:44.960-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='hunchentoot'/><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><title type='text'>RESTful handlers with Hunchentoot</title><content type='html'>I am not quite sure what REST is but I know that following REST practices gives you URLs like http://www.mycompany.com/resources/94182. Pretty isn't it?&lt;br /&gt;&lt;br /&gt;Django has a &lt;a href="http://www.djangoproject.com/documentation/url_dispatch/"&gt;URL dispatcher&lt;/a&gt; which allows you to specify regular expressions that match incoming URLs and call a specific handler. If you wanted function &lt;tt&gt;resource_page&lt;/tt&gt; to handle requests to URLs similar to the above, you would specify &lt;tt&gt;/resources/\d*&lt;/tt&gt; as the regular expression. The slashes at the beginning and end of the regular expression are optional. Effectively, you are writing &lt;tt&gt;/?resources/\d*/?&lt;/tt&gt;. This would match:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt; &lt;li&gt;&lt;tt&gt;/resources/&lt;/tt&gt;&lt;/li&gt;&lt;br /&gt; &lt;li&gt;&lt;tt&gt;/resources/123&lt;/tt&gt;&lt;/li&gt;&lt;br /&gt; &lt;li&gt;&lt;tt&gt;/resources/123/&lt;/tt&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;I'm not entirely sure that it would match &lt;tt&gt;/resources//&lt;/tt&gt; (try it to see!)&lt;br /&gt;&lt;br /&gt;This would not match &lt;tt&gt;/resources/abcd&lt;/tt&gt; and other similar URLs.&lt;br /&gt;&lt;br /&gt;In Django, the handler is written as:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;def resource_page(request,resourceid):&lt;br /&gt;  # ....&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And the dispatcher is registered like:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;urlpatterns = patterns('',&lt;br /&gt;  (r"/resources/\d*",resource_page),&lt;br /&gt;)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;A little thing I forgot to mention was that if you want the matches to be bound to function arguments, you have to make sure the regex remembers them. In this case, one would really want to write:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;urlpatterns = patterns('',&lt;br /&gt;  (r"/resources/(\d*)",resource_page),&lt;br /&gt;)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;As is my nature, I decided I want this functionality as a &lt;a href="http://weitz.de/hunchentoot/#handlers"&gt;Hunchentoot handler&lt;/a&gt;. Hunchentoot works pretty much the same way except you only specify dispatch handlers. When a request comes in, Hunchentoot iterates through the dispatch handlers and if one of them returns a function, that function is called to handle the request otherwise the default handler is called.&lt;br /&gt;&lt;br /&gt;So the solution is obvious (I think!): I want to write a handler that matches the requested URL against a regex, binds any matches to function parameters and returns that function.&lt;br /&gt;&lt;br /&gt;What I want to write in my code is something like:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;;;; I like to be explicit about the slashes myself&lt;br /&gt;(create-regex-dispatcher "^/resources/(\\d*)" #'resource-page)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This is obviously very simple once you have a regex engine. Fortunately, not only has &lt;a href="http://weitz.de/"&gt;Edi Weitz&lt;/a&gt; made a billion other libraries including Hunchentoot, but he has also written &lt;a href="http://weitz.de/cl-ppcre/#performance"&gt;one of the fastest regex libraries&lt;/a&gt;, surpassing even Perl. Crazy.&lt;br /&gt;&lt;br /&gt;Anyway, here is the code:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;(defun create-regex-dispatcher (regex page-function)&lt;br /&gt;  "Just like tbnl:create-regex-dispatcher except it extracts the matched values&lt;br /&gt;   and passes them onto PAGE-FUNCTION as arguments. You want to be explicit about&lt;br /&gt;   where the slashes go.&lt;br /&gt;&lt;br /&gt;   For example, given:&lt;br /&gt;   (defun blog-page (pagenum)&lt;br /&gt;     ... )&lt;br /&gt;&lt;br /&gt;   (push (create-regex-dispatcher \"^/blog/page(\\d+)\" #'blog-page)&lt;br /&gt;         tbnl:*dispatch-table*)&lt;br /&gt;&lt;br /&gt;   When the url blog/page5 is accessed, blog-page is called with pagenum &lt;br /&gt;   set to 5."&lt;br /&gt;  (let ((scanner (cl-ppcre:create-scanner regex)))&lt;br /&gt;    (lambda (request)&lt;br /&gt;      (multiple-value-bind (whole-match matched-registers)&lt;br /&gt;          (cl-ppcre:scan-to-strings scanner (tbnl:script-name request))&lt;br /&gt;        (when whole-match&lt;br /&gt;          (lambda ()&lt;br /&gt;            (apply page-function (coerce matched-registers 'list))))))))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And a simple example for using it:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;;;; If there is no match, id will be the empty string. &lt;br /&gt;;;; Should be a better way to handle this, but OK for now.&lt;br /&gt;(defun resource-function (&amp;optional id)&lt;br /&gt;           (cl-who:with-html-output-to-string(s)&lt;br /&gt;             (:html&lt;br /&gt;              (:body&lt;br /&gt;               (tbnl:log-message* "~A ~A")&lt;br /&gt;               (if (or (null id)&lt;br /&gt;                       (= 0 (length id)))&lt;br /&gt;                   (cl-who:htm&lt;br /&gt;                    (cl-who:str "No resource"))&lt;br /&gt;                   (cl-who:htm&lt;br /&gt;                    (cl-who:fmt "Resource ~A" id)))))))&lt;br /&gt;&lt;br /&gt;(push (create-regex-dispatcher "^/resources/(\\d*)" #'resource-function)&lt;br /&gt;      tbnl:*dispatch-table*)&lt;br /&gt;&lt;/pre&gt;</content><link rel='replies' type='application/atom+xml' href='http://uint32t.blogspot.com/feeds/4170014763613975051/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=4168503339888369834&amp;postID=4170014763613975051' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/4168503339888369834/posts/default/4170014763613975051'/><link rel='self' type='application/atom+xml' href='http://uint32t.blogspot.com/feeds/posts/default/4170014763613975051'/><link rel='alternate' type='text/html' href='http://uint32t.blogspot.com/2007/12/restful-handlers-with-hunchentoot.html' title='RESTful handlers with Hunchentoot'/><author><name>Sohail Somani</name><uri>https://plus.google.com/106964604256511867632</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-t-OCQSnHolk/AAAAAAAAAAI/AAAAAAAABRU/cl0ejUW86Yk/s32-c/photo.jpg'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4168503339888369834.post-8213910378298739968</id><published>2007-12-17T20:51:00.000-05:00</published><updated>2007-12-17T21:49:31.101-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><title type='text'>Python decorators in Lisp, Part 3</title><content type='html'>In this &lt;a href="http://uint32t.blogspot.com/2007/12/python-decorators-in-lisp-part-2.html"&gt;last post&lt;/a&gt;, I showed a way to implement Python's decorator syntax in Lisp which actually seemed to work for more than just myself!&lt;br /&gt;&lt;br /&gt;What I did not show is how you can use this in regular source files. As mentioned previously, one way to add new syntax into Lisp is to tell the reader (via &lt;tt&gt;*readtable*&lt;/tt&gt;) to call a reader macro when it encounters a particular pair of characters. So the answer to using this syntax in regular source files is to locally enable it by rebinding &lt;tt&gt;*readtable*&lt;/tt&gt;. As always, it helps to write out what you would like to do:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;(enable-py-decorator-syntax)&lt;br /&gt;&lt;br /&gt;#@my-decorator&lt;br /&gt;(defun my-function (x) ... )&lt;br /&gt;&lt;br /&gt;#@my-decorator&lt;br /&gt;(defun my-other-function (x) ... )&lt;br /&gt;&lt;br /&gt;#@my-decorator&lt;br /&gt;(defun yaf[yet-another-function] (x) ... )&lt;br /&gt;&lt;br /&gt;(disable-py-decorator-syntax)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Quite simply, &lt;tt&gt;enable-py-decorator-syntax&lt;/tt&gt; copies the current readtable and sets the dispatch function. It also assigns the original readtable to a variable so it can be reset. Conversely, &lt;tt&gt;disable-py-decorator-syntax&lt;/tt&gt; does the opposite: it sets the current readtable to the original readtable and sets the auxiliary variable to nil. Without further ado, the code for these functions:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;(defparameter *original-readtable* nil)&lt;br /&gt;&lt;br /&gt;(defmacro enable-py-decorator-syntax ()&lt;br /&gt;  "Turns on the decorator syntax"&lt;br /&gt;  '(eval-when (:compile-toplevel :load-toplevel :execute)&lt;br /&gt;    (%enable-py-decorator-syntax)))&lt;br /&gt;&lt;br /&gt;(defun %enable-py-decorator-syntax ()&lt;br /&gt;  (unless *original-readtable*&lt;br /&gt;    (setf *original-readtable* *readtable*&lt;br /&gt;          *readtable* (copy-readtable))&lt;br /&gt;    (set-dispatch-macro-character #\# #\@&lt;br /&gt;                                  #'|#@-reader|&lt;br /&gt;                                  *readtable*)))&lt;br /&gt;&lt;br /&gt;(defmacro disable-py-decorator-syntax ()&lt;br /&gt;  "Turns off the decorator syntax"&lt;br /&gt;  '(eval-when (:compile-toplevel :load-toplevel :execute)&lt;br /&gt;    (%disable-py-decorator-syntax)))&lt;br /&gt;&lt;br /&gt;(defun %disable-py-decorator-syntax ()&lt;br /&gt;  (when *original-readtable*&lt;br /&gt;    (setf *readtable* *original-readtable*&lt;br /&gt;          *original-readtable* nil)))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Thanks to popeix for submitting the original post to reddit (&lt;a href="http://programming.reddit.com/info/638fv/comments/"&gt;mod it up!&lt;/a&gt;) The code for enabling and disabling the syntax was based on &lt;a href="http://git.b9.com/cgi-bin/gitweb.cgi?p=clsql.git;a=blob;f=sql/syntax.lisp;h=436c224ccd9476b2aa3e46b88259a17ab175f037;hb=HEAD"&gt;syntax.lisp&lt;/a&gt;.</content><link rel='replies' type='application/atom+xml' href='http://uint32t.blogspot.com/feeds/8213910378298739968/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=4168503339888369834&amp;postID=8213910378298739968' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/4168503339888369834/posts/default/8213910378298739968'/><link rel='self' type='application/atom+xml' href='http://uint32t.blogspot.com/feeds/posts/default/8213910378298739968'/><link rel='alternate' type='text/html' href='http://uint32t.blogspot.com/2007/12/python-decorators-in-lisp-part-3.html' title='Python decorators in Lisp, Part 3'/><author><name>Sohail Somani</name><uri>https://plus.google.com/106964604256511867632</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-t-OCQSnHolk/AAAAAAAAAAI/AAAAAAAABRU/cl0ejUW86Yk/s32-c/photo.jpg'/></author><thr:total>2</thr:total></entry></feed>