<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://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:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-37596622</id><updated>2012-01-23T21:33:01.159-08:00</updated><category term='Furnace'/><category term='beer'/><category term='tools'/><category term='path'/><category term='documentation'/><category term='hooks'/><category term='singletons'/><category term='Cygwin'/><category term='breadth'/><category term='ARM'/><category term='SEH'/><category term='Windows'/><category term='summerofcode'/><category term='symlinks'/><category term='Factor'/><category term='Romannumerals'/><category term='partitioning'/><category term='win32'/><category term='Code'/><category term='processes'/><category term='heaps'/><category term='darcs'/><category term='depth'/><category term='queues'/><category term='WM5'/><category term='SOC'/><category term='math'/><category term='wee-url.com'/><category term='id3'/><category term='refactoring'/><category term='dlists'/><category term='random'/><category term='bsd'/><category term='sorting'/><category term='files'/><category term='lisp'/><category term='editors'/><category term='destructors'/><category term='assocs'/><category term='prng'/><category term='MinGW'/><category term='file-systems'/><category term='Programming Languages'/><category term='mac'/><category term='server'/><category term='mp3'/><category term='network'/><category term='primitives'/><category term='MSYS'/><category term='WinCE'/><category term='google'/><title type='text'>The Awesome Factor</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://code-factor.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://code-factor.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Doug Coleman</name><uri>http://www.blogger.com/profile/09416611825627835802</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>38</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-37596622.post-4674495479537040162</id><published>2011-11-01T15:11:00.000-07:00</published><updated>2011-11-02T19:09:22.176-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='tools'/><category scheme='http://www.blogger.com/atom/ns#' term='Factor'/><category scheme='http://www.blogger.com/atom/ns#' term='math'/><title type='text'>Code coverage tool</title><content type='html'>&lt;h2&gt;Motivation for writing a coverage tool&lt;/h2&gt;In &lt;a href="http://factorcode.org/"&gt;Factor&lt;/a&gt;, the old code-is-data adage is true and can be used to our advantage when writing runtime tools. Code is stored in what we call a &lt;a href="http://docs.factorcode.org/content/article-0cf98fdd68d86b42e181e37521f38cc1d4795978.html"&gt;quotation&lt;/a&gt;, which is just a sequence of functions (words) and data literals and can be called at runtime. A function (or word) in Factor is simply a named quotation, and typing that word name causes  the quotation to run.&lt;br /&gt;&lt;br /&gt;Since quotations in Factor do not terminate early unless an exception is thrown, we know that if each quotation gets run, then we have run all of the code. While this won't tell us if there are logic errors, and while we can still have code fail due to unexpected inputs, at least we can be sure that all of the code is getting exercise and there are no paths that never get run. We can use the results of this tool to write better unit tests.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Demo of the coverage tool&lt;/h2&gt;As a simple demo, there's a fun algorithm that produces a sequence of numbers from a start number, where the sequence always seems to end at one. For any integer greater than zero, the the &lt;a href="http://en.wikipedia.org/wiki/Collatz_conjecture"&gt;Collatz conjecture&lt;/a&gt; states that this sequence will eventually reach the number one. The algorithm goes as follows: take any counting number (1, 2, ...) and either multiply by three and add 1, or divide by two, if the number is odd or even, respectively, and record the sequence of numbers until you reach the number one. This conjecture has been found to be true experimentally for every number up to 10^18, but no proof exists that it's true for all possible inputs.&lt;br /&gt;&lt;br /&gt;For example, the Collatz sequence for 3 is: { 3 10 5 16 8 4 2 1 }&lt;br /&gt;&lt;br /&gt;We can come up with a solution pretty easily (partially taken from the Project Euler #14, in extra/).&lt;br /&gt;&lt;br /&gt;We do the command: "collatz" scaffold-work&lt;br /&gt;&lt;br /&gt;Click on the link, edit the file ~/factor/extra/collatz/collatz.factor&lt;pre&gt;USING: combinators.short-circuit kernel make math ;&lt;br /&gt;IN: collatz&lt;br /&gt;&lt;br /&gt;: next-collatz ( n -- n )&lt;br /&gt;    dup even? [ 2 / ] [ 3 * 1 + ] if ;&lt;br /&gt;&lt;br /&gt;: collatz-unsafe ( n -- seq )&lt;br /&gt;    [ [ dup 1 &gt; ] [ dup , next-collatz ] while , ] { } make ;&lt;br /&gt;&lt;br /&gt;ERROR: invalid-collatz-input n ;&lt;br /&gt;&lt;br /&gt;: collatz ( n -- seq )&lt;br /&gt;    dup { [ integer? ] [ 1 &gt;= ] } 1&amp;&amp;&lt;br /&gt;    [ collatz-unsafe ]&lt;br /&gt;    [ invalid-collatz-input ] if ;&lt;/pre&gt;We're going to be extra careful here to demonstrate the code coverage tool, so I added error checking to make sure it's an integer greater than or equal to one.&lt;br /&gt;&lt;br /&gt;Let's write some unit tests to make sure it works.&lt;br /&gt;&lt;br /&gt;Run the command: "collatz" scaffold-tests&lt;br /&gt;Now click on the link to edit the file it created, ~/factor/extra/collatz/collatz-tests.factor&lt;pre&gt;USING: tools.test collatz ;&lt;br /&gt;IN: collatz.tests&lt;br /&gt;&lt;br /&gt;[ { 1 } ] [ 1 collatz ] unit-test&lt;br /&gt;[ { 2 1 } ] [ 2 collatz ] unit-test&lt;br /&gt;[ { 3 10 5 16 8 4 2 1 } ] [ 3 collatz ] unit-test&lt;/pre&gt;If we run "collatz" test, we see that all tests pass.&lt;br /&gt;&lt;br /&gt;Now, let's see how well we did with code coverage.&lt;br /&gt;&lt;br /&gt;We run the command: &lt;pre&gt;IN: scratchpad USE: tools.coverage "collatz" test-coverage .&lt;br /&gt;Loading resource:work/collatz/collatz-tests.factor&lt;br /&gt;Unit Test: { [ { 1 } ] [ 1 collatz ] }&lt;br /&gt;Unit Test: { [ { 2 1 } ] [ 2 collatz ] }&lt;br /&gt;Unit Test: { [ { 3 10 5 16 8 4 2 1 } ] [ 3 collatz ] }&lt;br /&gt;{&lt;br /&gt;    { next-collatz { } }&lt;br /&gt;    { collatz { [ invalid-collatz-input ] } }&lt;br /&gt;    {&lt;br /&gt;        invalid-collatz-input&lt;br /&gt;        { [ \ invalid-collatz-input boa throw ] }&lt;br /&gt;    }&lt;br /&gt;    { collatz-unsafe { } }&lt;br /&gt;}&lt;/pre&gt;What this tells us is we had quotations in the collatz word and the invalid-collatz-input words that did not get called. Of course -- we never passed it anything other than valid inputs. How about passing it a string or a negative integer?&lt;br /&gt;&lt;br /&gt;Now the result looks better:&lt;pre&gt;IN: scratchpad "collatz" test-coverage .&lt;br /&gt;Loading resource:work/collatz/collatz-tests.factor&lt;br /&gt;Unit Test: { [ { 1 } ] [ 1 collatz ] }&lt;br /&gt;Unit Test: { [ { 2 1 } ] [ 2 collatz ] }&lt;br /&gt;Unit Test: { [ { 3 10 5 16 8 4 2 1 } ] [ 3 collatz ] }&lt;br /&gt;Must Fail With: { [ "hello world" collatz ] [ invalid-collatz-input? ] }&lt;br /&gt;Must Fail With: { [ -50 collatz ] [ invalid-collatz-input? ] }&lt;br /&gt;{&lt;br /&gt;    { next-collatz { } }&lt;br /&gt;    { collatz { } }&lt;br /&gt;    { invalid-collatz-input { } }&lt;br /&gt;    { collatz-unsafe { } }&lt;br /&gt;}&lt;/pre&gt;We can even get a number for how well tests cover a vocabulary:&lt;pre&gt;"collatz" %coverage .&lt;br /&gt;1.0&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;The implementation of the coverage tool&lt;/h2&gt;Every word in Factor stores its definition in the `def' slot. If we examine this slot, we see that it's a quotation that may contain other quotations. Using the annotation vocabulary, we can add code that executes before and after the code in the quotation. What the coverage tool does is adds a container that stores a boolean flag at the beginning of each quotation, and when the quotation gets run, the flag is set to true. This tool can be turned on and off, independent of the containers being in place, with the coverage-on and coverage-off words.&lt;br /&gt;&lt;br /&gt;Here's a word that turns a Roman numeral into an integer:&lt;pre&gt;\ roman&gt; def&gt;&gt; .&lt;br /&gt;[ &gt;lower [ roman&gt;= ] monotonic-split [ (roman&gt;) ] map-sum ]&lt;/pre&gt;After annotating it, the new definition looks like:&lt;pre&gt;\ roman&gt; add-coverage \ roman&gt; def&gt;&gt; .&lt;br /&gt;[&lt;br /&gt;    T{ coverage } flag-covered &gt;lower&lt;br /&gt;    [ T{ coverage } flag-covered roman&gt;= ] monotonic-split&lt;br /&gt;    [ T{ coverage } flag-covered (roman&gt;) ] map-sum&lt;br /&gt;]&lt;/pre&gt;The flags are all set to false right now. After turning on the flag to let enable the coverage code and running the word, we see a change:&lt;pre&gt;coverage-on "iii" roman&gt; drop \ roman&gt; def&gt;&gt; .[&lt;br /&gt;    T{ coverage { executed? t } } flag-covered &gt;lower&lt;br /&gt;    [ T{ coverage { executed? t } } flag-covered roman&gt;= ]&lt;br /&gt;    monotonic-split&lt;br /&gt;    [ T{ coverage { executed? t } } flag-covered (roman&gt;) ]&lt;br /&gt;    map-sum&lt;br /&gt;]&lt;/pre&gt;Notice that all of the coverage containers have been executed. To generate the report, we simply iterate over each word and collect all of the quotations where this flag has not been set -- these quotations never ran.&lt;br /&gt;&lt;br /&gt;In writing this article, I realized that each quotation should have a flag on exit as well, in case an exception gets thrown in the middle of executing this quotation and control never reaches the end. Partially-executed quotations will soon be reported by the tool, after I make this fix.&lt;br /&gt;&lt;br /&gt;I hope you can use this tool to improve your Factor code. Have fun!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37596622-4674495479537040162?l=code-factor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://code-factor.blogspot.com/feeds/4674495479537040162/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=37596622&amp;postID=4674495479537040162' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/4674495479537040162'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/4674495479537040162'/><link rel='alternate' type='text/html' href='http://code-factor.blogspot.com/2011/11/code-coverage-tool.html' title='Code coverage tool'/><author><name>Doug Coleman</name><uri>http://www.blogger.com/profile/09416611825627835802</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-37596622.post-8838331351556693609</id><published>2010-09-04T13:31:00.000-07:00</published><updated>2010-09-05T21:59:24.765-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='files'/><category scheme='http://www.blogger.com/atom/ns#' term='Factor'/><title type='text'>Filling a file with zeros</title><content type='html'>In this blog post I'll demonstrate a few of ways to fill a file with zeros in Factor.  The goal is to write a some number of bytes to file in the least amount of time and using only a small amount of RAM; writing a large file should not fail.&lt;h2&gt;Filling a file with zeros by seeking&lt;/h2&gt;The best way of writing a file full of zeros is to seek to one byte from the end of the file, write a zero, and close the file.  Here's the code:&lt;pre&gt;: (zero-file) ( n path -- )&lt;br /&gt;    binary &lt;br /&gt;    [ 1 - seek-absolute seek-output 0 write1 ] with-file-writer ;&lt;br /&gt;&lt;br /&gt;ERROR: invalid-file-size n path ;&lt;br /&gt;&lt;br /&gt;: zero-file ( n path -- )   &lt;br /&gt;    {&lt;br /&gt;        { [ over 0 &amp;lt; ] [ invalid-file-size ] }&lt;br /&gt;        { [ over 0 = ] [ nip touch-file ] }&lt;br /&gt;        [ (zero-file) ]&lt;br /&gt;    } cond ;&lt;/pre&gt;The first thing you'll notice about the &lt;code&gt;zero-file&lt;/code&gt; is that we special-case negative and zero file sizes.  Special-casing zero file length is necessary to avoid seeking to -1, which does everything correctly but throws an error in the process instead of returning normally.  Special-casing negative file sizes is important because it's always an error, and though the operation fails overall, the file-system can become littered with zero-length files that are created before the exception is thrown.&lt;br /&gt;&lt;br /&gt;To call the new word:&lt;pre&gt;123,456,789 "/Users/erg/zeros.bin" zero-file&lt;br /&gt;"/Users/erg/zeros.bin" file-info size&gt;&gt; .&lt;br /&gt;123456789&lt;/pre&gt;&lt;h2&gt;Copying a zero-stream&lt;/h2&gt;With Factor's stream protocol, you can write new kinds of streams that, when read from or written to, do whatever you want.  I wrote a read-only &lt;code&gt;zero-stream&lt;/code&gt; below that returns zeros whenever you read from it.  Wrapping a &lt;code&gt;limit-stream&lt;/code&gt; around it, you can give the inexhaustible &lt;code&gt;zero-stream&lt;/code&gt; an artificial length, so that copying it reaches an end and terminates.&lt;pre&gt;TUPLE: zero-stream ;&lt;br /&gt;&lt;br /&gt;C: &amp;lt;zero-stream&gt; zero-stream&lt;br /&gt;&lt;br /&gt;M: zero-stream stream-read drop &amp;lt;byte-array&gt; ;&lt;br /&gt;M: zero-stream stream-read1 drop 0 ;&lt;br /&gt;M: zero-stream stream-read-partial stream-read ;&lt;br /&gt;M: zero-stream dispose drop ;&lt;br /&gt;&lt;br /&gt;:: zero-file2 ( n path -- )&lt;br /&gt;    &amp;lt;zero-stream&gt; n limit-stream &lt;br /&gt;    path binary &amp;lt;file-writer&gt; stream-copy ;&lt;/pre&gt;The drawback to this approach is that it creates 8kb byte-arrays in memory that it immediately writes to disk.&lt;h2&gt;Setting the contents of a file directly&lt;/h2&gt;Using the &lt;code&gt;set-file-contents&lt;/code&gt; word, you can just assign a file's contents to be a sequence.  However, this sequence has to fit into memory, so this solution is not as good for our use case.&lt;pre&gt;:: zero-file3 ( n path -- )&lt;br /&gt;    n &amp;lt;byte-array&gt; path binary set-file-contents ;&lt;/pre&gt;&lt;h2&gt;Bonus: writing random data to a file&lt;/h2&gt;The canonical way of copying random data to a file in Unix systems is to use the dd tool to read from /dev/urandom and write to a file.  But what about on Windows, where there is no /dev/urandom?  We can come up with a cross-platform solution that uses method number two from above, but instead of a &lt;code&gt;zero-stream&lt;/code&gt;, we have a &lt;code&gt;random-stream&lt;/code&gt;.  But then what about efficiency?  Well, it turns out that Factor's Mersenne Twister implementation generates random numbers faster than /dev/urandom on my Macbook -- writing a 100MB file from /dev/urandom is about twice as slow as a Factor-only solution.  So not only is the Factor solution cross-platform, it's also more efficient.&lt;pre&gt;TUPLE: random-stream ;&lt;br /&gt;&lt;br /&gt;C: &amp;lt;random-stream&gt; random-stream&lt;br /&gt;&lt;br /&gt;M: random-stream stream-read drop random-bytes ;&lt;br /&gt;M: random-stream stream-read1 drop 256 random ;&lt;br /&gt;M: random-stream stream-read-partial stream-read ;&lt;br /&gt;M: random-stream dispose drop ;&lt;br /&gt;&lt;br /&gt;:: stream-copy-n ( from to n -- )&lt;br /&gt;    from n limit-stream to stream-copy ;&lt;br /&gt;&lt;br /&gt;:: random-file ( n path -- )&lt;br /&gt;    &lt;random-stream&gt; &lt;br /&gt;    path binary &amp;lt;file-writer&gt; n stream-copy-n ;&lt;br /&gt;&lt;br /&gt;! Read from /dev/urandom&lt;br /&gt;:: random-file-urandom ( n path -- )&lt;br /&gt;    [&lt;br /&gt;        &lt;random-stream&gt; path&lt;br /&gt;        binary &amp;lt;file-writer&gt; n stream-copy-n&lt;br /&gt;    ] with-system-random ;&lt;/pre&gt;Here are the results:&lt;pre&gt;$ dd if=/dev/urandom of=here.bin bs=100000000 count=1&lt;br /&gt;1+0 records in&lt;br /&gt;1+0 records out&lt;br /&gt;100000000 bytes transferred in 17.384370 secs (5752294 bytes/sec)&lt;br /&gt;&lt;br /&gt;100,000,000 "there.bin" random-file&lt;br /&gt;Running time: 5.623136439 seconds&lt;/pre&gt;&lt;h2&gt;Conclusion&lt;/h2&gt;Since Factor has high-level libraries that wrap the low-level libc and system calls used for nonblocking i/o, we don't have to deal with platform-specific quirks at this level of abstraction like handling EINTR, error codes, or resource cleanup at the operating system level.  When calls get interrupted, when errno is set to EINTR after the call returns, the i/o operation is simply tried again behind the scenes, and only serious i/o errors get thrown.  There are many options for correct resource cleanup should an error occur, but the error handling code we used here is incorporated into the &lt;code&gt;stream-copy&lt;/code&gt; and &lt;code&gt;with-file-writer&lt;/code&gt; words--resources are cleaned up regardless of what happens.  We also demonstrated that a Factor word is preferable to a shell script or the dd command for making files full of random data because it's more portable and faster, and that custom streams are easy to define.&lt;br /&gt;&lt;br /&gt;Finally, there's actually a faster way to create huge files full of zeros, and that's by using sparse files.  Sparse files can start off using virtually no file-system blocks, but can appear to be as large as you wish, and only start to consume more blocks as parts of the file are written.  However, support for this is file-system dependent and, overall, sparse files are of questionable use.  On Unix file-systems that support sparse files, the first method above should automatically creates them with no extra work.  Note that on MacOSX, sparse file-systems are supported but not enabled by default.  On Windows, however, you have to make a call to &lt;code&gt;DeviceIoControl&lt;/code&gt;.  If someone wants to have a small contribution to the Factor project, they are welcome to implement creation of sparse files for Windows.&lt;br /&gt;&lt;br /&gt;Edit: Thanks to one of the commenters, I rediscovered that there's a Unix syscall &lt;code&gt;truncate&lt;/code&gt; that creates zero-length files in constant time on my Mac.  This is indeed the best solution for making files full of zeros, and although unportable, a Factor library would have no problem using a hook on the OS variable to call truncate on Unix and another method on Windows.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37596622-8838331351556693609?l=code-factor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://code-factor.blogspot.com/feeds/8838331351556693609/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=37596622&amp;postID=8838331351556693609' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/8838331351556693609'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/8838331351556693609'/><link rel='alternate' type='text/html' href='http://code-factor.blogspot.com/2010/09/filling-file-with-zeros.html' title='Filling a file with zeros'/><author><name>Doug Coleman</name><uri>http://www.blogger.com/profile/09416611825627835802</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-37596622.post-7918927583801441160</id><published>2009-11-19T18:59:00.001-08:00</published><updated>2010-09-04T17:35:59.414-07:00</updated><title type='text'>Monotonic timers</title><content type='html'>Factor has had a calendar library for several years now.  While it's great for converting timestamps to &lt;a href="http://gitweb.factorcode.org/gitweb.cgi?p=factor/.git;a=blob;f=basis/calendar/format/format.factor;hb=HEAD"&gt;human-readable formats&lt;/a&gt;, &lt;a href="http://gitweb.factorcode.org/gitweb.cgi?p=factor/.git;a=blob;f=extra/calendar/holidays/us/us.factor;hb=HEAD"&gt;calculating holidays&lt;/a&gt;, and finding the number of days between two dates, it's the wrong concept to use for timing code, alarms, and thread switching.  In such cases where you don't need an actual date, you should use monotonic timers, which are counters that always increment from an unspecified time in the past and aren't affected by changes to the system time.  Even if the user changes the clock, these monotonic timers don't go back in time -- they keep increasing.  Let's look at the implementation.&lt;h2&gt;Implementation of monotonic timers&lt;/h2&gt;Although I originally implemented monotonic timers as a Factor library, I moved the code into the C++ VM as a primitive called &lt;code&gt;nano-count&lt;/code&gt;.  To distinguish the usage of this word from the word formerly known as &lt;code&gt;micros&lt;/code&gt;, I renamed &lt;code&gt;micros&lt;/code&gt; to &lt;code&gt;system-micros&lt;/code&gt;.  Having the word "system" in the name of one time-returning word, and having "count" in the other, hopefully leads to less confusion on the user's part.&lt;h3&gt;Windows&lt;/h3&gt;The code I came up with for Windows looks like this:&lt;pre&gt;u64 nano_count()&lt;br /&gt;{&lt;br /&gt;    static double scale_factor;&lt;br /&gt;&lt;br /&gt;    static u32 hi = 0;&lt;br /&gt;    static u32 lo = 0;&lt;br /&gt;&lt;br /&gt;    LARGE_INTEGER count;&lt;br /&gt;    BOOL ret = QueryPerformanceCounter(&amp;count);&lt;br /&gt;    if(ret == 0)&lt;br /&gt;        fatal_error("QueryPerformanceCounter", 0);&lt;br /&gt;&lt;br /&gt;    if(scale_factor == 0.0)&lt;br /&gt;    {&lt;br /&gt;        LARGE_INTEGER frequency;&lt;br /&gt;        BOOL ret = QueryPerformanceFrequency(&amp;frequency);&lt;br /&gt;        if(ret == 0)&lt;br /&gt;            fatal_error("QueryPerformanceFrequency", 0);&lt;br /&gt;        scale_factor = (1000000000.0 / frequency.QuadPart);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;#ifdef FACTOR_64&lt;br /&gt;    hi = count.HighPart;&lt;br /&gt;#else&lt;br /&gt;    /* On VirtualBox, QueryPerformanceCounter does not increment&lt;br /&gt;    the high part every time the low part overflows.  Workaround. */&lt;br /&gt;    if(lo &gt; count.LowPart)&lt;br /&gt;        hi++;&lt;br /&gt;#endif&lt;br /&gt;    lo = count.LowPart;&lt;br /&gt;&lt;br /&gt;    return (u64)((((u64)hi &lt;&lt; 32) | (u64)lo) * scale_factor);&lt;br /&gt;}&lt;/pre&gt;It could probably be optimized by only calling &lt;code&gt;QueryPerformanceFrequency&lt;/code&gt; once, but I don't set the processor affinity yet, so I'm not convinced it will work in every case.  As you can see, it's pretty simple: the performance counter is queried and returns a number of clock cycles since some arbitrary beginning epoch, and then that time is scaled by the clock frequency to get nanoseconds.&lt;br /&gt;Edit: This code contains a workaround for a VirtualBox counter bug.&lt;h3&gt;Some Unix systems&lt;/h3&gt;&lt;pre&gt;u64 nano_count()&lt;br /&gt;{&lt;br /&gt;    struct timespec t;&lt;br /&gt;    int ret;&lt;br /&gt;    ret = clock_gettime(CLOCK_MONOTONIC,&amp;t);&lt;br /&gt;    if(ret != 0)&lt;br /&gt;        fatal_error("clock_gettime failed", 0);&lt;br /&gt;    return t.tv_sec * 1000000000 + t.tv_nsec;&lt;br /&gt;}&lt;/pre&gt;Calling &lt;code&gt;clock_gettime&lt;/code&gt; from the &lt;i&gt;librt&lt;/i&gt; library or, on some platforms, as a system call, gives you the number of nanoseconds since an arbitrary start point in the past.  The timespec struct has a seconds and a nanoseconds slots, while the timeval struct (used by &lt;code&gt;system-micros&lt;/code&gt;) has seconds and microseconds.&lt;h3&gt;Mac OSX&lt;/h3&gt;&lt;pre&gt;u64 nano_count()&lt;br /&gt;{&lt;br /&gt;    u64 time = mach_absolute_time();&lt;br /&gt;&lt;br /&gt;    static u64 scaling_factor = 0;&lt;br /&gt;    if(!scaling_factor)&lt;br /&gt;    {&lt;br /&gt;        mach_timebase_info_data_t info;&lt;br /&gt;        kern_return_t ret = mach_timebase_info(&amp;info);&lt;br /&gt;        if(ret != 0)&lt;br /&gt;            fatal_error("mach_timebase_info failed",ret);&lt;br /&gt;        scaling_factor = info.numer/info.denom;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    return time * scaling_factor;&lt;br /&gt;}&lt;/pre&gt;The MacOSX code is a bit different because Apple didn't implement &lt;code&gt;clock_gettime&lt;/code&gt;.  Instead, they have a couple of Mach functions that function just like the Windows code, with one returning a count and the other returning clock frequency information.&lt;h2&gt;Upgraded alarms&lt;/h2&gt;The alarms vocabulary now uses monotonic timers instead of system time for scheduling alarms.  Previously, the API for scheduling an alarm was the following, where passing &lt;code&gt;f&lt;/code&gt; as the last input parameter would schedule a one-time alarm.&lt;pre&gt;add-alarm ( quot start-timestamp interval-duration/f -- alarm )&lt;/pre&gt;However, this design is bad because the system time could change, resulting in a huge backlog of alarms to run.  Also, most alarms were scheduled for less than a second into the future, which makes timestamps pretty useless since no date calculations are being performed.  The new API takes a duration:&lt;pre&gt;add-alarm ( quot start-duration interval-duration/f -- alarm)&lt;/pre&gt;Note that duration can be things like &lt;ul&gt;&lt;li&gt;300 milliseconds&lt;/li&gt;&lt;li&gt;5 seconds&lt;/li&gt;&lt;li&gt;200 nanoseconds&lt;/li&gt;&lt;/ul&gt;&lt;h2&gt;Using monotonic timers&lt;/h2&gt;&lt;h3&gt;Mouse drag alarm&lt;/h3&gt;Here's an example of using an alarm from the mouse handling code:&lt;pre&gt;: start-drag-timer ( -- )&lt;br /&gt;    hand-buttons get-global empty? [&lt;br /&gt;        [ drag-gesture ] 300 milliseconds 100 milliseconds&lt;br /&gt;        add-alarm drag-timer get-global &gt;box&lt;br /&gt;    ] when ;&lt;/pre&gt;The &lt;code&gt;drag-gesture&lt;/code&gt; word gets called 300 milliseconds after a mouse button has been clicked, and again every 100 milliseconds afterwards until the alarm gets cancelled when the user releases a mouse button.  The alarm is put into a global box because storing into a full box throws an error, which in this case would represent impossibility of the user dragging two things at once.  Once dragging stops, the alarm gets cancelled with a call to &lt;code&gt;cancel-alarm&lt;/code&gt;.  You can look at the full source &lt;a href="http://gitweb.factorcode.org/gitweb.cgi?p=factor/.git;a=blob;f=basis/ui/gestures/gestures.factor;hb=HEAD"&gt;here&lt;/a&gt;.&lt;h3&gt;Benchmark word&lt;/h3&gt;The &lt;code&gt;benchmark&lt;/code&gt; word times a quotation and returns the number of nanoseconds that its execution took.  Its implementation follows:&lt;pre&gt;: benchmark ( quot -- runtime )&lt;br /&gt;    nano-count [ call nano-count ] dip - ; inline&lt;/pre&gt;This word simply gets the count from the monotonic timer, calls the quotation, gets a new count, and finds the elapsed time by subtraction.&lt;h3&gt;Rescheduling alarms&lt;/h3&gt;After repeated alarms execute, they must be rescheduled to run again.&lt;pre&gt;: reschedule-alarm ( alarm -- )&lt;br /&gt;    dup interval&gt;&gt; nano-count + &gt;&gt;start register-alarm ;&lt;/pre&gt;The alarm gets rescheduled &lt;code&gt;interval&gt;&gt;&lt;/code&gt; nanoseconds into the future.&lt;h2&gt;Remaining issues&lt;/h2&gt;Putting the computer to sleep on Snow Leopard in the middle of bootstrap and then resuming does not affect timing.  However, is this the case with other operating systems such as &lt;a href="http://en.wikipedia.org/wiki/Windows_7"&gt;Snow Vista&lt;/a&gt; or Linux?  If not, it might not be worth worrying about.  If someone wanted to test, just start a Factor bootstrap and then put the computer to sleep for awhile and see if bootstrap time increases.  Otherwise, I'll get to it eventually.&lt;br&gt;Update: Someone on the &lt;a href="https://lists.sourceforge.net/lists/listinfo/factor-talk"&gt;Factor mailing list&lt;/a&gt; reported that putting the computer to sleep on bootstrap in Linux did not mess up the timing.  Thank you!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37596622-7918927583801441160?l=code-factor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://code-factor.blogspot.com/feeds/7918927583801441160/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=37596622&amp;postID=7918927583801441160' title='37 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/7918927583801441160'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/7918927583801441160'/><link rel='alternate' type='text/html' href='http://code-factor.blogspot.com/2009/11/monotonic-timers.html' title='Monotonic timers'/><author><name>Doug Coleman</name><uri>http://www.blogger.com/profile/09416611825627835802</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>37</thr:total></entry><entry><id>tag:blogger.com,1999:blog-37596622.post-2226521751479849777</id><published>2009-08-17T18:13:00.000-07:00</published><updated>2009-08-17T22:18:28.718-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='beer'/><category scheme='http://www.blogger.com/atom/ns#' term='Factor'/><title type='text'>Twin Cities Lisp Factor Presentation</title><content type='html'>At the next &lt;a href="http://tclispers.org"&gt;TC Lisp&lt;/a&gt; meeting, I'll be explaining as much as I can about Factor in a one-hour presentation.  Arrive before 6pm for the happy hour beer prices.  All are welcome!&lt;br /&gt;&lt;br /&gt;Place: Common Roots Cafe&lt;br /&gt;Date: August 18, 2009&lt;br /&gt;Time: 6pm&lt;br /&gt;&lt;br /&gt;&lt;iframe width="425" height="350" frameborder="0" scrolling="no" marginheight="0" marginwidth="0" src="http://maps.google.com/maps?f=q&amp;amp;source=s_q&amp;amp;hl=en&amp;amp;geocode=&amp;amp;q=common+roots+cafe&amp;amp;sll=37.0625,-95.677068&amp;amp;sspn=32.885543,47.988281&amp;amp;ie=UTF8&amp;amp;ll=44.973664,-93.279934&amp;amp;spn=0.047847,0.076218&amp;amp;z=13&amp;amp;iwloc=A&amp;amp;cid=11605740676571333977&amp;amp;output=embed"&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;small&gt;&lt;a href="http://maps.google.com/maps?f=q&amp;amp;source=embed&amp;amp;hl=en&amp;amp;geocode=&amp;amp;q=common+roots+cafe&amp;amp;sll=37.0625,-95.677068&amp;amp;sspn=32.885543,47.988281&amp;amp;ie=UTF8&amp;amp;ll=44.973664,-93.279934&amp;amp;spn=0.047847,0.076218&amp;amp;z=13&amp;amp;iwloc=A&amp;amp;cid=11605740676571333977" style="color:#0000FF;text-align:left"&gt;View Larger Map&lt;/a&gt;&lt;/small&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37596622-2226521751479849777?l=code-factor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://code-factor.blogspot.com/feeds/2226521751479849777/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=37596622&amp;postID=2226521751479849777' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/2226521751479849777'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/2226521751479849777'/><link rel='alternate' type='text/html' href='http://code-factor.blogspot.com/2009/08/twin-cities-lisp-factor-presentation.html' title='Twin Cities Lisp Factor Presentation'/><author><name>Doug Coleman</name><uri>http://www.blogger.com/profile/09416611825627835802</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-37596622.post-1095216931888961917</id><published>2009-05-29T09:45:00.000-07:00</published><updated>2009-05-30T18:40:27.161-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='server'/><category scheme='http://www.blogger.com/atom/ns#' term='network'/><category scheme='http://www.blogger.com/atom/ns#' term='Factor'/><title type='text'>A new library to manage client connections, and a proof of concept chat server in Factor</title><content type='html'>Some thirty seconds into play-testing Joe Groff's &lt;a href="http://duriansoftware.com/joe/The-Factor-game-framework.html"&gt;terrain demo game&lt;/a&gt;, I realized there were no NPCs, no double-barreled shotguns or hand-cannons, and most disturbingly, no other players.  Sure you can fly around, but you can't gib your friends!  So I decided to do start to do something about it and write a managed server vocabulary with a generic protocol that you can extend for writing your own servers.  Hey, I'm not much of an artist -- the guns will have to wait.&lt;h2&gt;Managed-server infrastructure&lt;/h2&gt;The HTTP server already uses a library called &lt;a href="http://docs.factorcode.org/content/article-io.servers.connection.html"&gt;io.servers.connections&lt;/a&gt; which implements a threaded-server with SSL support in 164 lines of code.  A threaded-server listens for a set number of clients on an open port and handles each one individually; no client needs to know about any other.  To get the code so concise, it uses libraries for concurrency, logging, sockets, and SSL that are themselves reused elsewhere.&lt;br /&gt;Features of the threaded-server vocabulary:&lt;ul&gt;&lt;li&gt;the best nonblocking I/O code on every platform (completion ports, kqueue, epoll, not select())&lt;/li&gt;&lt;li&gt;connection/error logging, log rotation&lt;/li&gt;&lt;li&gt;correct error handling and resource cleanup&lt;/li&gt;&lt;li&gt;SSL support on Unix platforms&lt;/il&gt;&lt;li&gt;IPv4 and IPv6 support by default&lt;/li&gt;&lt;/ul&gt;For an HTTP or FTP server, handling each connection individually is what you want.  However, for games or chat servers, you really want your users to interact.  Building on top of this thread-server, I made a new tuple called a managed-server that tracks a list of connecting and disconnecting clients.  You still get all of the features threaded-server implements, but now there's a new client handler that maintains a list of connected clients keyed by a username and utility words to send data to all clients.&lt;br /&gt;You can also use this code to make custom binary protocols, and I'm mostly through implemented an SRP6 library to allow secure unencrypted logins after you create an account through an SSL connection.  UDP support for first-person shooter and faster-paced games will also be supported when someone needs it.&lt;h2&gt;The implementation of managed-server&lt;/h2&gt;A &lt;a href="http://gitweb.factorcode.org/gitweb.cgi?p=factor/.git;a=blob_plain;f=extra/managed-server/managed-server.factor;hb=HEAD"&gt;managed-server&lt;/a&gt; inherits from threaded-server class and adds a new slot called &lt;code&gt;clients&lt;/code&gt; to store connections.  Each connection's state -- the input/output streams, the local/remote socket addresses, username, a slot for passing quit messages, and a quit flag -- is wrapped inside a &lt;code&gt;managed-client&lt;/code&gt; tuple and stored into the clients hashtable with the username as the key.  In this way, it's easy to look up another client's stream and send it a message:&lt;pre&gt;"wally" "hi wally!" send-client&lt;/pre&gt;You can also send a message to all connected clients,&lt;code&gt;send-everyone&lt;/code&gt;, or to all but yourself:&lt;pre&gt;"This one goes out to all the ladies." send-everyone-else&lt;/pre&gt;Here's what the tuple classes code looks like:&lt;pre&gt;TUPLE: managed-server &amp;lt; threaded-server clients ;&lt;br /&gt;&lt;br /&gt;TUPLE: managed-client&lt;br /&gt;input-stream output-stream local-address remote-address&lt;br /&gt;username object quit? ;&lt;/pre&gt;&lt;h3&gt;The managed-server protocol&lt;/h3&gt;A managed-server has some generics in place to guide you in creating your own servers.  The first two generics are required, but the others default to no-ops unless you want to handle these events.  Of course, the clients are still tracked no matter what your method does on the client-join or client-disconnect generics.  The default method for &lt;code&gt;handle-already-logged-in&lt;/code&gt; throws an error to prevent a new client from taking over the other client's session or logging in multiple times.  You can override this behavior with your own perversions.&lt;br /&gt;Here's the protocol:&lt;pre&gt;&lt;br /&gt;HOOK: handle-login threaded-server ( -- username )&lt;br /&gt;HOOK: handle-managed-client* managed-server ( -- )&lt;br /&gt;HOOK: handle-already-logged-in managed-server ( -- )&lt;br /&gt;HOOK: handle-client-join managed-server ( -- )&lt;br /&gt;HOOK: handle-client-disconnect managed-server ( -- )&lt;/pre&gt;&lt;h2&gt;The implementation of a chat server using managed-server&lt;/h2&gt;Eventually someone will use managed-server for the networking code in a game, but until then I've implemented a simple chat server.  Writing the &lt;a href="http://gitweb.factorcode.org/gitweb.cgi?p=factor/.git;a=blob_plain;f=extra/managed-server/chat/chat.factor;hb=HEAD"&gt;chat server&lt;/a&gt; was fun and helped me to iron out a couple of bugs, which I wrote about below.&lt;br /&gt;&lt;h3&gt; A walkthrough of the chat server protocol&lt;/h3&gt;&lt;br /&gt;The chat server code begins by inheriting from the managed-server tuple:&lt;pre&gt;TUPLE: chat-server &amp;lt; managed-server ;&lt;/pre&gt;From here you go about implementing required parts of the protocol, &lt;code&gt;handle-login&lt;/code&gt; and &lt;code&gt;handle-managed-client*&lt;/code&gt;, so let's start there.&lt;pre&gt;M: chat-server handle-login&lt;br /&gt;    "Username: " write flush&lt;br /&gt;    readln ;&lt;/pre&gt;The current input/output streams are bound to the client connection, so calling &lt;code&gt;write&lt;/code&gt; will send them the login prompt.  To read back the username, &lt;code&gt;readln&lt;/code&gt; reads until a newline is sent.  If you were to connect with telnet at this point, you would see the prompt and could send back a username.  Then the server would kick you off because there's no implementation of &lt;code&gt;handle-managed-client*&lt;/code&gt;.&lt;pre&gt;M: chat-server handle-managed-client*&lt;br /&gt;    readln dup f = [ t client (&gt;&gt;quit?) ] when&lt;br /&gt;    [&lt;br /&gt;        "/" ?head [ handle-command ] [ handle-chat ] if&lt;br /&gt;    ] unless-empty ;&lt;/pre&gt;This word handles every other message the client sends apart from the login code.  Calling &lt;code&gt;readln&lt;/code&gt; reads the client's message one line at a time and returns false when the stream closes.  The quit flag is set in such a case and will be explained later.  For now, suffice to say that you're quitting if &lt;code&gt;readln&lt;/code&gt; returns false.  Next, the message is checked for any content -- both false and an empty string can be safely ignored here by the &lt;code&gt;unless-empty&lt;/code&gt; combinator.  Inside the quotation, the leading slash is stripped from the input, if any, and a boolean returned by &lt;code&gt;?head&lt;/code&gt; decides if the message was intended for the server or the chat room.&lt;pre&gt;: handle-command ( string -- )&lt;br /&gt;    dup " " split1 swap &gt;lower commands get at* [&lt;br /&gt;        call( string -- ) drop&lt;br /&gt;    ] [&lt;br /&gt;        2drop "Unknown command: " prepend print flush&lt;br /&gt;    ] if ;&lt;/pre&gt;Commands sent to the server are normalized by converting to lower case and then looked up in the commands table.  If you send a successful command such as /who or /nick then it gets executed; if not you get the generic "Unknown command" error.&lt;pre&gt;: handle-chat ( string -- )&lt;br /&gt;    [&lt;br /&gt;        [ username ": " ] dip&lt;br /&gt;    ] "" append-outputs-as send-everyone ;&lt;/pre&gt;Sending a message to the chat room is the alternative to server commands.  I'm using &lt;code&gt;append-outputs-as&lt;/code&gt; here to append together a bunch of strings, although i could easily have used &lt;code&gt;3append&lt;/code&gt; instead.  I left this in because it's easier to change the look of the chat if you don't have to keep track of how many strings you're appending and you just let the compiler infer.  Please take note: smart combinators in Factor are analogous to applying a function to a list or parameters in Lisp in that you don't need to know the number of parameters.  The following two snippets will demonstrate what I mean:&lt;pre&gt;(+ 1 2 10 1200)&lt;/pre&gt;&lt;pre&gt;[ 1 2 10 1200 ] sum-outputs&lt;/pre&gt;That's pretty much the essence of the chat server since everything else was just added for fun.&lt;br /&gt;&lt;h3&gt;Fun with default encodings&lt;/h3&gt;Default encodings are terrible!  Of course, you can change the encoding of a stream whenever you want, but the encoding for threaded-servers defaulted to ASCII until I changed it this evening.  When I made my chat server yesterday, I forgot to set the encoding to what I wanted -- UTF8.  Sending a character above 127 caused the server to throw an exception since ASCII is only 7 bits wide, and the sender would get disconnected.  The FTP server I wrote started out with this bug as well, before I changed it to latin1.  But now that threaded-server takes an encoding on the stack, this bug can never happen again.&lt;br /&gt;So what's wrong with picking a different default encoding, maybe UTF8?  Well, if I'm making a binary server, the UTF8 decoder will replace bad bit sequences with replacement characters -- another latent bug!  What about binary as the default encoding, i.e. no encoding?  Binary is the best option for a default, but then people who need to use UTF8 or latin1 might not know that the stream protocol supports encodings at all, and will end up doing a lot of work by hand which should be handled by the stream implementation.  So not having a default encoding 1) prevents latent bugs and 2) forces the programmer to think about what they really want in each situation -- surely a good idea.&lt;br /&gt;&lt;h3&gt;Quitting gracefully with quit flag&lt;/h3&gt;My first thought was just to throw an exception when I wanted to disconnect a client and cause the error handler to clean up the resources.  Hopefully it's common knowledge that control flow implemented with exceptions is inefficient and bad design, in the general case.  Maybe just this once?  Nope, in my case the logging framework logs all exceptions, so the majority of the logs would be filled up with useless disconnect error messages. Clearly something better was needed -- the quit flag.  Managed clients have a quit flag slot that is checked every time the server processes some data.  Clients can quit gracefully by setting this flag to true and returning control back to the stream reader loop, and quits caused by exceptions are logged and worthy of further investigation.&lt;h2&gt;Live coding on the server&lt;/h2&gt;After the chat server was up and running, I could add features without restarting.  One of the first requested features was "/help", which required a redesign of how slash commands were handled.  Instead of a case statement, now there's a word &lt;code&gt;add-command&lt;/code&gt; that takes the implementation, the documentation, and the name of the command you want to add.  Adding a command stores the code and the docs in symbols holding hashtables, indexed by the name of the command.&lt;pre&gt;SYMBOL: commands&lt;br /&gt;commands [ H{ } clone ] initialize&lt;br /&gt;&lt;br /&gt;SYMBOL: chat-docs&lt;br /&gt;chat-docs [ H{ } clone ] initialize&lt;br /&gt;&lt;br /&gt;:: add-command ( quot docs key -- )&lt;br /&gt;    quot key commands get set-at&lt;br /&gt;    docs key chat-docs get set-at ;&lt;br /&gt;&lt;/pre&gt;I added a time command for fun:&lt;pre&gt;[ drop gmt timestamp&gt;rfc822 print flush ]&lt;br /&gt;&lt;" Syntax: /time&lt;br /&gt;Returns the current GMT time."&gt; "time" add-command&lt;/pre&gt;Someone else wanted a "/who" command -- easy enough.&lt;pre&gt;[ drop clients keys [ "``" "''" surround ] map ", " join print flush ]&lt;br /&gt;&lt;" Syntax: /who&lt;br /&gt;Shows the list of connected users."&gt;&lt;br /&gt;"who" add-command&lt;/pre&gt;There last feature I implemented was a way to change your nickname without reconnecting:&lt;pre&gt;: handle-nick ( string -- )&lt;br /&gt;    [&lt;br /&gt;        "nick" usage&lt;br /&gt;    ] [&lt;br /&gt;        dup clients key? [&lt;br /&gt;            username-taken-string print flush&lt;br /&gt;        ] [&lt;br /&gt;            [ username swap warn-name-changed ]&lt;br /&gt;            [ username clients rename-at ]&lt;br /&gt;            [ client (&gt;&gt;username) ] tri&lt;br /&gt;        ] if&lt;br /&gt;    ] if-empty ;&lt;br /&gt;&lt;br /&gt;[ handle-nick ]&lt;br /&gt;&lt;" Syntax: /nick nickname&lt;br /&gt;Changes your nickname."&gt;&lt;br /&gt;"nick" add-command&lt;/pre&gt;Changing your nickname is straightforward but takes the most steps of all the commands I implemented.  Try to understand the code -- "string" in the stack effect is the requested nickname and the clients word returns a hashtable of connections, indexed by nicknames.  If the user didn't supply a nickname, remind them of the syntax for using /nick.  If they did supply a nickname, check if it's in use and, if so, refuse to change their name.  Otherwise, the nick change succeeded, so tell all the users of the nickname change, apply the nick change in the clients hashtable, and set the new nickname in the client.&lt;br /&gt;&lt;h2&gt;Chat server running live&lt;/h2&gt;You can try out the chat server by downloading Factor and running this command:&lt;pre&gt;USING: managed-server.chat io.servers.connection ; 8889 &amp;lt;chat-server&gt; start-server&lt;/pre&gt;Or you can connect to my running chat server:&lt;pre&gt;telnet trifocus.net 8889&lt;/pre&gt;It's just a demo and I didn't implement any limits on your nickname or what you can send, though it would be easy enough to do so.  Have fun, and please let me know if you can find any bugs.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37596622-1095216931888961917?l=code-factor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://code-factor.blogspot.com/feeds/1095216931888961917/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=37596622&amp;postID=1095216931888961917' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/1095216931888961917'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/1095216931888961917'/><link rel='alternate' type='text/html' href='http://code-factor.blogspot.com/2009/05/new-library-to-manage-client.html' title='A new library to manage client connections, and a proof of concept chat server in Factor'/><author><name>Doug Coleman</name><uri>http://www.blogger.com/profile/09416611825627835802</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-37596622.post-228239114476485761</id><published>2009-05-24T14:10:00.000-07:00</published><updated>2009-05-24T14:10:00.404-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mp3'/><category scheme='http://www.blogger.com/atom/ns#' term='id3'/><category scheme='http://www.blogger.com/atom/ns#' term='Factor'/><title type='text'>An ID3 Parser in Factor</title><content type='html'>A new contributor, Tim Wawrzynczak, wrote an ID3 parser as his first Factor program a couple of months back.  The code looked pretty good, so it was easy to refactor the way ID3v1 and ID3v2 tags are represented and to add some utility words for managing directories of MP3s.  The library still needs some work, but now it can take a directory tree and recursively parse all of the ID3 headers.  I also realized that Factor's mmap implementation always tried to map a file read/write, so for ID3 parsing I added a read-only mmap.  The finished code is &lt;a href="http://gitweb.factorcode.org/gitweb.cgi?p=factor/.git;a=blob_plain;f=extra/id3/id3.factor;hb=be41611667c2da002e54ae71a96ce0e25e5fc3da"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;ID3v1 format&lt;/h2&gt;&lt;br /&gt;ID3 tags come in two flavors -- the old ID3v1 format, and the newer ID3v2 one.  ID3v1 has a fixed length of 128 bytes and, if present, is the last 128 bytes of an MP3 file.  The bytes begin with "TAG" and follow with the song title (30 bytes), artist (30 bytes), album (30 bytes), year (4 bytes), comment (30 bytes), and the genre (1 byte).  The problem with this approach is that you are limited in the length of all the fields and in which data you can represent.&lt;br /&gt;&lt;br /&gt;To implement the parser, we use the &lt;a href="http://docs.factorcode.org/content/article-io.mmap.html"&gt;Memory-mapped files&lt;/a&gt; vocabulary to open the file.  You can treat the file as an array of bytes that obeys the &lt;a href="http://docs.factorcode.org/content/article-sequence-protocol.html"&gt;sequence protocol&lt;/a&gt;.  Checking if a file has ID3v1 headers becomes:&lt;pre&gt;: id3v1? ( mmap -- ? )&lt;br /&gt;    { [ length 128 &gt;= ] [ 128 tail-slice* "TAG" head? ] } 1&amp;&amp; ;&lt;/pre&gt;The logic here is straightforward: the sequence (mmapped file) is checked to make sure it's long enough to contain a header, and then the last 128 bytes are checked against the magic bytes "TAG".  Since this is a short-circuit combinator, if the length test fails then the computation will end early.  In this way, you can string together long computations that use short-circuit &lt;b&gt;and&lt;/b&gt; and &lt;b&gt;or&lt;/b&gt;.  Factor's usual &lt;code&gt;and&lt;/code&gt;/&lt;code&gt;or&lt;/code&gt; words take two already-evaluated objects, so the short-circuit behavior is implemented as a library of macros instead.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;ID3v2 format&lt;/h2&gt;&lt;br /&gt;The newer standard, ID3v2, has more room for metadata and can store anything up to 256 MB.  Its use is indicated when the MP3 begins with "ID3" or when the bytes "3DI" are present 10 bytes from the end of the file or 10 bytes before the ID3v1 header.  For parsing the header, the first two bytes are the version, then a flag byte, and finally four bytes for the size of the header.  The size bytes are encoded as a synchsafe number, which means that the top bit is discarded and the lower seven bits are the data.  In our case, 28 bits of data are the length, which is why the maximum length is 256MB.&lt;br /&gt;&lt;br /&gt;The actual data we want to parse is stored in frames.  Each frame has a tag, which is four ascii or utf16 bytes, followed by another 4 byte synchsafe integer for the length, two bytes of flag data, and the frame data.  There are many different types of frames, but some of the more common ones are tagged TALB (album title), TIT2 (title), TRCK (track), COMM (comment), TPE1 (performer).  All of the frame are added to a hashtable and keyed by the tag.  Looking up the title frame becomes as easy as &lt;code&gt;"TIT2" find-id3-frame&lt;/code&gt; on an ID3 tuple.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Future work&lt;/h2&gt;&lt;br /&gt;The ID3v1 "TAG+" format is not supported yet and apparently it's hardly ever used.  ID3v2 tags are only looked for at the beginning of an MP3 but may be present at the end too as of version four.  Most of the ID3 tags are not parsed into meaningful data besides the raw bytes. Lastly, writing out ID3 tags is not implemented yet and would be a good first program to write in Factor.  Volunteers?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37596622-228239114476485761?l=code-factor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://code-factor.blogspot.com/feeds/228239114476485761/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=37596622&amp;postID=228239114476485761' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/228239114476485761'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/228239114476485761'/><link rel='alternate' type='text/html' href='http://code-factor.blogspot.com/2009/04/blog-post.html' title='An ID3 Parser in Factor'/><author><name>Doug Coleman</name><uri>http://www.blogger.com/profile/09416611825627835802</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-37596622.post-1676894315361986326</id><published>2009-01-13T16:01:00.000-08:00</published><updated>2009-01-13T18:17:08.659-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='files'/><category scheme='http://www.blogger.com/atom/ns#' term='tools'/><title type='text'>Files and file-systems in Factor, part 2</title><content type='html'>In my &lt;a href="http://code-factor.blogspot.com/2009/01/files-and-file-systems-in-factor-part-1.html"&gt;previous post&lt;/a&gt; I wrote about the &lt;code&gt;file-info&lt;/code&gt; and &lt;code&gt;file-system-info&lt;/code&gt; words.  Using those words, it's possible to write a portable program for directory listing (dir or ls) and one for file-systems listing (df).  Uses for directory listing include file-system utility programs and FTP servers.&lt;h2&gt;File listing tool&lt;/h2&gt;The file-listing tool is in the Factor git repository as &lt;i&gt;basis/tools/files/files.factor&lt;/i&gt;.  Which slots you see depend on how it is configured and on which platform it's running.  Directory listings can be &lt;a href="http://code-factor.blogspot.com/2009/01/sorting-by-tuple-slots.html"&gt;sorted&lt;/a&gt; by slot and the default sort is by name.&lt;br /&gt;&lt;br /&gt;On Unix platforms, it's configured like this:&lt;pre&gt;    &amp;lt;listing-tool&gt;&lt;br /&gt;       { permissions nlinks user group file-size file-date file-name } &gt;&gt;specs&lt;br /&gt;       { { directory-entry&gt;&gt; name&gt;&gt; &amp;lt;=&gt; } } &gt;&gt;sort&lt;/pre&gt;Listing a directory on MacOSX:&lt;pre&gt;-rw-r--r-- 1  erg staff 27185 Nov 28  2008 #factor.el#&lt;br /&gt;drwxr-xr-x 6  erg staff 204   Nov 17  2008 Factor.tmbundle&lt;br /&gt;-rw-r--r-- 1  erg staff 30282 Nov 30  2008 factor.el&lt;br /&gt;-rw-r--r-- 1  erg staff 18037 Nov 17  2008 factor.vim&lt;br /&gt;-rw-r--r-- 1  erg staff 12496 Nov 17  2008 factor.vim.fgen&lt;br /&gt;drwxr-xr-x 26 erg staff 884   Jan 13 11:17 fuel&lt;br /&gt;drwxr-xr-x 8  erg staff 272   Nov 17  2008 icons&lt;/pre&gt;Windows platforms take the look of the ``dir'' command by default:&lt;pre&gt;2009-01-14 00:00:53 &amp;lt;DIR&gt;                Factor.tmbundle&lt;br /&gt;2009-01-14 00:00:53                30282 factor.el&lt;br /&gt;2009-01-14 00:00:53                18037 factor.vim&lt;br /&gt;2009-01-14 00:00:53                12496 factor.vim.fgen&lt;br /&gt;2009-01-14 00:00:53 &amp;lt;DIR&gt;                fuel&lt;br /&gt;2009-01-14 00:00:53 &amp;lt;DIR&gt;                icons&lt;/pre&gt;&lt;h2&gt;File-systems tool&lt;/h2&gt;A tool for disk usage and mounted devices was easy to write once file-system tuples worked everywhere.  Once again, it's configurable for whatever you want to see.  The default file-system word is:&lt;pre&gt;: file-systems. ( -- )&lt;br /&gt;    {&lt;br /&gt;        device-name available-space free-space used-space&lt;br /&gt;        total-space percent-used mount-point&lt;br /&gt;    } print-file-systems ;&lt;/pre&gt;File-systems on a Mac:&lt;pre&gt;device-name   available-space free-space   used-space   total-space  percent-used mount-point&lt;br /&gt;/dev/disk0s2  118599725056    118861869056 200867090432 319728959488 62           /&lt;br /&gt;fdesc         0               0            1024         1024         100          /dev&lt;br /&gt;fdesc         0               0            1024         1024         100          /dev&lt;br /&gt;map -hosts    0               0            0            0            0            /net&lt;br /&gt;map auto_home 0               0            0            0            0            /home&lt;/pre&gt;&lt;br /&gt;On Windows:&lt;pre&gt;device-name      available-space free-space used-space total-space percent-used mount-point&lt;br /&gt;                 3814506496      3814506496 6911225856 10725732352 64           C:\&lt;br /&gt;VBOXADDITIONS_2. 0               0          27983872   27983872    100          D:\&lt;br /&gt;                 0               0          0          0           0            A:\&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37596622-1676894315361986326?l=code-factor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://code-factor.blogspot.com/feeds/1676894315361986326/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=37596622&amp;postID=1676894315361986326' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/1676894315361986326'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/1676894315361986326'/><link rel='alternate' type='text/html' href='http://code-factor.blogspot.com/2009/01/files-and-file-systems-in-factor-part-2.html' title='Files and file-systems in Factor, part 2'/><author><name>Doug Coleman</name><uri>http://www.blogger.com/profile/09416611825627835802</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-37596622.post-5926187127951204494</id><published>2009-01-09T13:05:00.000-08:00</published><updated>2009-01-09T13:08:31.666-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='files'/><category scheme='http://www.blogger.com/atom/ns#' term='file-systems'/><category scheme='http://www.blogger.com/atom/ns#' term='symlinks'/><title type='text'>Files and file-systems in Factor, part 1</title><content type='html'>Factor now has an easy way access to get information about files and file-systems in a high-level way across all the &lt;a href="http://factorcode.org/"&gt;platforms&lt;/a&gt; that it supports.  The API is really simple -- pass a pathname and get information back about the file or file-system as a tuple.  The second part of this post will demonstrate a clone of the Unix tools ls, for listing files, and df, for listing file-systems.&lt;h2&gt;File-info&lt;/h2&gt;There are now words to get information about files and symlinks, using &lt;code&gt;file-info&lt;/code&gt; and &lt;code&gt;link-info&lt;/code&gt; which map to C system calls &lt;code&gt;stat&lt;/code&gt; and &lt;code&gt;lstat&lt;/code&gt;. Some slots are shared across all platforms while others are only present on a particular platform.  There are symbols representing all of the file types, like +regular-file+, +directory+, and +symbolic-link+.  Here are some examples.&lt;br /&gt;&lt;b&gt;MacOSX&lt;/b&gt;&lt;pre&gt;( scratchpad ) "resource:license.txt" file-info .&lt;br /&gt;T{ bsd-file-info&lt;br /&gt;    { type +regular-file+ }&lt;br /&gt;    { size 1252 }&lt;br /&gt;    { permissions 33188 }&lt;br /&gt;    { created&lt;br /&gt;        T{ timestamp&lt;br /&gt;            { year 2008 }&lt;br /&gt;            { month 11 }&lt;br /&gt;            { day 17 }&lt;br /&gt;            { hour 23 }&lt;br /&gt;            { minute 34 }&lt;br /&gt;            { second 5 }&lt;br /&gt;            { gmt-offset T{ duration { hour -6 } } }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;    { modified&lt;br /&gt;        T{ timestamp&lt;br /&gt;            { year 2008 }&lt;br /&gt;            { month 11 }&lt;br /&gt;            { day 17 }&lt;br /&gt;            { hour 23 }&lt;br /&gt;            { minute 34 }&lt;br /&gt;            { second 5 }&lt;br /&gt;            { gmt-offset T{ duration { hour -6 } } }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;    { accessed&lt;br /&gt;        T{ timestamp&lt;br /&gt;            { year 2008 }&lt;br /&gt;            { month 12 }&lt;br /&gt;            { day 9 }&lt;br /&gt;            { hour 12 }&lt;br /&gt;            { minute 34 }&lt;br /&gt;            { second 8 }&lt;br /&gt;            { gmt-offset T{ duration { hour -6 } } }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;    { uid 501 }&lt;br /&gt;    { gid 20 }&lt;br /&gt;    { dev 234881026 }&lt;br /&gt;    { ino 992362 }&lt;br /&gt;    { nlink 1 }&lt;br /&gt;    { rdev 0 }&lt;br /&gt;    { blocks 8 }&lt;br /&gt;    { blocksize 4096 }&lt;br /&gt;    { birth-time&lt;br /&gt;        T{ timestamp&lt;br /&gt;            { year 2008 }&lt;br /&gt;            { month 11 }&lt;br /&gt;            { day 17 }&lt;br /&gt;            { hour 23 }&lt;br /&gt;            { minute 34 }&lt;br /&gt;            { second 5 }&lt;br /&gt;            { gmt-offset T{ duration { hour -6 } } }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;    { flags 0 }&lt;br /&gt;    { gen 0 }&lt;br /&gt;}&lt;/pre&gt;&lt;b&gt;Windows XP&lt;/b&gt;&lt;pre&gt;( scratchpad ) "resource:license.txt" file-info .&lt;br /&gt;T{ windows-file-info&lt;br /&gt;    { type +regular-file+ }&lt;br /&gt;    { size 1252 }&lt;br /&gt;    { permissions 32 }&lt;br /&gt;    { created&lt;br /&gt;        T{ timestamp&lt;br /&gt;            { year 2008 }&lt;br /&gt;            { month 3 }&lt;br /&gt;            { day 23 }&lt;br /&gt;            { hour 23 }&lt;br /&gt;            { minute 28 }&lt;br /&gt;            { second 12 }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;    { modified&lt;br /&gt;        T{ timestamp&lt;br /&gt;            { year 2008 }&lt;br /&gt;            { month 3 }&lt;br /&gt;            { day 27 }&lt;br /&gt;            { hour 23 }&lt;br /&gt;            { minute 24 }&lt;br /&gt;            { second 12 }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;    { accessed&lt;br /&gt;        T{ timestamp&lt;br /&gt;            { year 2008 }&lt;br /&gt;            { month 9 }&lt;br /&gt;            { day 19 }&lt;br /&gt;            { hour 23 }&lt;br /&gt;            { minute 8 }&lt;br /&gt;            { second 41 }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;    { attributes { +archive+ } }&lt;br /&gt;}&lt;/pre&gt;&lt;b&gt;FreeBSD&lt;/b&gt;&lt;pre&gt;( scratchpad ) "resource:license.txt" file-info .&lt;br /&gt;T{ bsd-file-info&lt;br /&gt;    { type +regular-file+ }&lt;br /&gt;    { size 1252 }&lt;br /&gt;    { permissions 33188 }&lt;br /&gt;    { created&lt;br /&gt;        T{ timestamp&lt;br /&gt;            { year 2008 }&lt;br /&gt;            { month 4 }&lt;br /&gt;            { day 6 }&lt;br /&gt;            { hour 12 }&lt;br /&gt;            { minute 6 }&lt;br /&gt;            { second 53 }&lt;br /&gt;            { gmt-offset T{ duration { hour -6 } } }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;    { modified&lt;br /&gt;        T{ timestamp&lt;br /&gt;            { year 2008 }&lt;br /&gt;            { month 4 }&lt;br /&gt;            { day 6 }&lt;br /&gt;            { hour 12 }&lt;br /&gt;            { minute 6 }&lt;br /&gt;            { second 53 }&lt;br /&gt;            { gmt-offset T{ duration { hour -6 } } }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;    { accessed&lt;br /&gt;        T{ timestamp&lt;br /&gt;            { year 2008 }&lt;br /&gt;            { month 4 }&lt;br /&gt;            { day 6 }&lt;br /&gt;            { hour 12 }&lt;br /&gt;            { minute 6 }&lt;br /&gt;            { second 59 }&lt;br /&gt;            { gmt-offset T{ duration { hour -6 } } }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;    { uid 1002 }&lt;br /&gt;    { gid 1002 }&lt;br /&gt;    { dev 89 }&lt;br /&gt;    { ino 343452 }&lt;br /&gt;    { nlink 1 }&lt;br /&gt;    { rdev 1348575 }&lt;br /&gt;    { blocks 4 }&lt;br /&gt;    { blocksize 4096 }&lt;br /&gt;    { birth-time&lt;br /&gt;        T{ timestamp&lt;br /&gt;            { year 2008 }&lt;br /&gt;            { month 4 }&lt;br /&gt;            { day 6 }&lt;br /&gt;            { hour 12 }&lt;br /&gt;            { minute 6 }&lt;br /&gt;            { second 53 }&lt;br /&gt;            { gmt-offset T{ duration { hour -6 } } }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;    { flags 0 }&lt;br /&gt;    { gen 0 }&lt;br /&gt;}&lt;/pre&gt;&lt;h2&gt;File Systems&lt;/h2&gt;The &lt;code&gt;file-system&lt;/code&gt; utility word above works on file-system tuples that contain cross-platform information like the device name, the mount point, the number of free, used, and total bytes.  A file-system tuple on Unix has all of the file-system information found in both &lt;code&gt;statfs&lt;/code&gt; and &lt;code&gt;statvfs&lt;/code&gt; while a Windows file-system object has the device-id, volume name, and byte usage slots.  There is not a single win32 API call that gives as much information as on Unix systems -- instead I call a combination of &lt;code&gt;GetDiskFreeSpaceEx&lt;/code&gt;, &lt;code&gt;FindFirstVolume&lt;/code&gt;, &lt;code&gt;GetVolumePathNamesForVolumeName&lt;/code&gt;, &lt;code&gt;GetVolumeInformation&lt;/code&gt;.&lt;br /&gt;On every Unix besides Linux, there is a member of the &lt;code&gt;statfs&lt;/code&gt; or &lt;code&gt;statvfs&lt;/code&gt; structure that gives you the file-system that contains the file.  So, I had to roll my own for it to work the same way across all platforms.  The algorithm is pretty simple: the &lt;code&gt;follow-links&lt;/code&gt; word follows links up to 10 times (configurable) and once it stops, finds the parent directory and follows the links again until the directory is a member of the directories in the /etc/mtab file.  If there is circularity or a broken link, it throws an error.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;FreeBSD&lt;/b&gt;&lt;pre&gt;( scratchpad ) "/" file-system-info .&lt;br /&gt;T{ freebsd-file-system-info&lt;br /&gt;    { device-name "/dev/da0s1a" }&lt;br /&gt;    { mount-point "/" }&lt;br /&gt;    { type "ufs" }&lt;br /&gt;    { available-space 368117760 }&lt;br /&gt;    { free-space 409702400 }&lt;br /&gt;    { used-space 110110720 }&lt;br /&gt;    { total-space 519813120 }&lt;br /&gt;    { block-size 2048 }&lt;br /&gt;    { preferred-block-size 2048 }&lt;br /&gt;    { blocks 253815 }&lt;br /&gt;    { blocks-free 200050 }&lt;br /&gt;    { blocks-available 179745 }&lt;br /&gt;    { files 65790 }&lt;br /&gt;    { files-free 61501 }&lt;br /&gt;    { files-available 61501 }&lt;br /&gt;    { name-max 255 }&lt;br /&gt;    { flags 20480 }&lt;br /&gt;    { id { 0 0 } }&lt;br /&gt;    { version 537068824 }&lt;br /&gt;    { io-size 16384 }&lt;br /&gt;    { owner 0 }&lt;br /&gt;    { syncreads 0 }&lt;br /&gt;    { syncwrites 0 }&lt;br /&gt;    { asyncreads 0 }&lt;br /&gt;    { asyncwrites 0 }&lt;br /&gt;}&lt;/pre&gt;&lt;b&gt;Windows XP 64&lt;/b&gt;&lt;pre&gt;( scratchpad ) "k:\\" file-system-info .&lt;br /&gt;T{ win32-file-system-info&lt;br /&gt;    { device-name "" }&lt;br /&gt;    { mount-point "k:\\" }&lt;br /&gt;    { type "NTFS" }&lt;br /&gt;    { available-space 174530142208 }&lt;br /&gt;    { free-space 174530142208 }&lt;br /&gt;    { used-space 225547444224 }&lt;br /&gt;    { total-space 400077586432 }&lt;br /&gt;    { max-component 255 }&lt;br /&gt;    { flags 459007 }&lt;br /&gt;    { device-serial 3695676537 }&lt;br /&gt;}&lt;/pre&gt;The Factor build farm will start using &lt;code&gt;file-system-info&lt;/code&gt; to report when a drive fills up pretty soon.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37596622-5926187127951204494?l=code-factor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://code-factor.blogspot.com/feeds/5926187127951204494/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=37596622&amp;postID=5926187127951204494' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/5926187127951204494'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/5926187127951204494'/><link rel='alternate' type='text/html' href='http://code-factor.blogspot.com/2009/01/files-and-file-systems-in-factor-part-1.html' title='Files and file-systems in Factor, part 1'/><author><name>Doug Coleman</name><uri>http://www.blogger.com/profile/09416611825627835802</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-37596622.post-4789460695402732054</id><published>2009-01-09T08:21:00.000-08:00</published><updated>2009-01-09T13:02:02.627-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='partitioning'/><category scheme='http://www.blogger.com/atom/ns#' term='sorting'/><title type='text'>Sorting by tuple slots</title><content type='html'>Factor's sorting has been extended to support sorting tuples by multiple slots, one after the other. In a music player application, you may wish to sort your songs first by artist, then by album, then track number.  If you had a tuple representing every song, the code for such a sort is now very easy:&lt;pre&gt;{ { artist&gt;&gt; &lt;=&gt; } { album&gt;&gt; &lt;=&gt; } { track&gt;&gt; &lt;=&gt; } } sort-by-slots&lt;/pre&gt;The main word at work is &lt;code&gt;sort-by-slots ( seq sort-specs -- seq' )&lt;/code&gt;, where a &lt;i&gt;sort-spec&lt;/i&gt; is an accessor paired with a comparator, one of &lt;code&gt;&amp;lt;=&gt;&lt;/code&gt;, &lt;code&gt;&gt;=&amp;lt;&lt;/code&gt;, &lt;code&gt;human-&amp;lt;=&gt;&lt;/code&gt;, &lt;code&gt;human-&gt;=&amp;lt;&lt;/code&gt;.  Human sort first converts consecutive digits to integers and then makes the comparison, e.g. &lt;code&gt;{ "a1" "a10" "a03" "a2" }&lt;/code&gt; sorts as &lt;code&gt;{ "a1" "a2" "a03" "a10" }&lt;/code&gt; instead of &lt;code&gt;{ "a03" "a1" "a10" "a2" }&lt;/code&gt;, as it would with &lt;code&gt;natural-sort&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;Sorting in reverse order is possible with the new operator &lt;code&gt;&gt;=&amp;lt;&lt;/code&gt;, which inverts the result of the &lt;code&gt;&amp;lt;=&gt;&lt;/code&gt; comparator.  To sort a playlist by the most played songs in reverse order (most played first):&lt;pre&gt;{ { play-count&gt;&gt; &gt;=&lt; } } sort-by-slots&lt;/pre&gt;&lt;h2&gt;Source code for sort-by-slots&lt;/h2&gt;&lt;pre&gt;: slot-comparator ( accessor comparator -- quot )&lt;br /&gt;    '[ [ _ execute ] bi@ _ execute dup +eq+ eq? [ drop f ] when ] ;&lt;br /&gt;&lt;br /&gt;MACRO: compare-slots ( sort-specs -- &lt;=&gt; )&lt;br /&gt;    #! sort-spec: { accessor comparator }&lt;br /&gt;    [ first2 slot-comparator ] map '[ _ 2|| +eq+ or ] ;&lt;br /&gt;&lt;br /&gt;: sort-by-slots ( seq sort-specs -- seq' )&lt;br /&gt;    '[ _ compare-slots ] sort ;&lt;/pre&gt;First, a bit about how Factor's &lt;code&gt;sort ( seq quot -- sortedseq )&lt;/code&gt; word works.  It expects a quotation that compares two objects lexicographically, which is element by element in dictionary order.  Thus, the macro &lt;code&gt;compare-slots&lt;/code&gt; expands the sort-specs into a quotation that compares tuples slot-by-slot until there is a difference.  The macro-expansion for the first example looks like this:&lt;pre&gt;( scratchpad ) [ { { artist&gt;&gt; &lt;=&gt; } { album&gt;&gt; &lt;=&gt; } { track&gt;&gt; &lt;=&gt; } } compare-slots ] expand-macros .&lt;br /&gt;    f 2 [&lt;br /&gt;        \ artist&gt;&gt; \ &lt;=&gt; [ [ execute ] curry bi@ ] dip execute&lt;br /&gt;        dup +eq+ eq? [ drop f ] when&lt;br /&gt;    ] [ [ drop ] dip ndup ] dip call dup&lt;br /&gt;    [ 2 [ ndrop ] curry dip ] [&lt;br /&gt;        2 [&lt;br /&gt;            \ album&gt;&gt; \ &lt;=&gt; [ [ execute ] curry bi@ ] dip execute&lt;br /&gt;            dup +eq+ eq? [ drop f ] when&lt;br /&gt;        ] [ [ drop ] dip ndup ] dip call dup&lt;br /&gt;        [ 2 [ ndrop ] curry dip ] [&lt;br /&gt;            2 [&lt;br /&gt;                \ track&gt;&gt; \ &lt;=&gt; [ [ execute ] curry bi@ ] dip&lt;br /&gt;                execute dup +eq+ eq? [ drop f ] when&lt;br /&gt;            ] [ [ drop ] dip ndup ] dip call dup&lt;br /&gt;            [ 2 [ ndrop ] curry dip ]&lt;br /&gt;            [ 2 [ drop ] dip ndrop t [ f ] [ no-cond ] if ] if&lt;br /&gt;        ] if&lt;br /&gt;    ] if +eq+ or&lt;/pre&gt;&lt;br /&gt;The macro first extracts the slots and compares with the first comparator.  If the comparator returns &lt;code&gt;+lt+&lt;/code&gt; or &lt;code&gt;+gt+&lt;/code&gt; then the comparison is over, but if the comparator returns &lt;code&gt;+eq+&lt;/code&gt; then the next comparison is invoked and the sort continues in the same way.  Notice that the &lt;code&gt;slot-comparator&lt;/code&gt; word replaces &lt;code&gt;+eq+&lt;/code&gt; with &lt;code&gt;f&lt;/code&gt; to avoid short-circuiting the iteration done by &lt;code&gt;2||&lt;/code&gt;.  The final &lt;code&gt;sort-by-slots&lt;/code&gt; word is a trivial call to &lt;code&gt;sort&lt;/code&gt; with the new comparator word we just defined.&lt;br /&gt;&lt;h2&gt;Algorithmic complexity and ideas for improvement&lt;/h2&gt;The sorting bound is still O(n log n) for time, but of course the more comparators that you need to break ties, the longer the algorithm will take to run.&lt;br /&gt;If your data has to be sorted anyway, it's possible to sort and then split the data into related slices using a sequence of accessors like the above.  You could then compute the playing time of every album, or find your most-played song from every artist.  Splitting with the same accessors takes O(n) time, but the odds are that you probably won't have already sorted the data.  So for unsorted data, there is a more efficient way to extract features -- you can instead partition it in O(n) time and find features on each partition, avoiding sorting altogether.  This will be the subject of a future blog post.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37596622-4789460695402732054?l=code-factor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://code-factor.blogspot.com/feeds/4789460695402732054/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=37596622&amp;postID=4789460695402732054' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/4789460695402732054'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/4789460695402732054'/><link rel='alternate' type='text/html' href='http://code-factor.blogspot.com/2009/01/sorting-by-tuple-slots.html' title='Sorting by tuple slots'/><author><name>Doug Coleman</name><uri>http://www.blogger.com/profile/09416611825627835802</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-37596622.post-3801728555615789993</id><published>2008-10-19T15:42:00.000-07:00</published><updated>2008-10-19T16:03:32.537-07:00</updated><title type='text'>Moving primitives out of the Factor VM: Factor vs C</title><content type='html'>&lt;p&gt;In a previous lifetime, I &lt;a href='http://code-factor.blogspot.com/2008/04/adding-new-primitive.html'&gt;added environment variable primitives&lt;/a&gt; to the VM.  Well, it turns out that a better place for them is in the Factor basis vocabulary root, so this post is about moving them again. &lt;/p&gt;&lt;p&gt;To move a primitive out of the VM, implement its functionality in Factor code and replace usages with your word if necessary, remove it from &lt;em&gt;vm/primitives.c&lt;/em&gt; and &lt;em&gt;core/bootstrap/primitives.factor&lt;/em&gt;, remove the primitive code from the VM, make a new image, recompile Factor, and bootstrap.  Basically, do the inverse of the previous post.&lt;/p&gt;&lt;p&gt;What's more interesting, unless you have to actually remove a primitive someday and need this reference, is comparing the code for each version side-by-side.  The C code has to call functions to prevent the garbage collector from moving data around when it shouldn't, but it's written without the usual ifdefs you will find in most cross-platform C code, so overall it's fairly clean.  The Factor version has a high-level protocol that is implemented by both backends across separate files, with one-liners for most of the Unix definitions and high-level combinators for the Windows ones.  I find the Factor version much easier to understand and I believe it's more maintainable.  Factor is a better C than C.&lt;/p&gt;&lt;h2&gt; High-level environment variable interface &lt;/h2&gt;  &lt;p&gt;Factor's high-level environment variable words let you get a single variable or all of them, set a single variable or all of them, and unset a variable.  On Windows you cannot set all of the variables at once, and on Windows CE the whole concept of environment variables does not exist. &lt;/p&gt;&lt;p&gt;Here is the code for the main vocabulary.  Notice that there are hooks on the &lt;code&gt;os&lt;/code&gt; word, which will be a value like &lt;strong&gt;macosx&lt;/strong&gt; or &lt;strong&gt;winnt&lt;/strong&gt; or &lt;strong&gt;linux&lt;/strong&gt;.  The boilerplate at the bottom is for loading the platform-specific code. &lt;/p&gt;&lt;pre&gt;&lt;span class='MARKUP'&gt;USING: &lt;/span&gt;&lt;span class='LITERAL2'&gt;assocs&lt;/span&gt;&lt;span class='LITERAL2'&gt; &lt;/span&gt;&lt;span class='LITERAL2'&gt;combinators&lt;/span&gt;&lt;span class='LITERAL2'&gt; &lt;/span&gt;&lt;span class='LITERAL2'&gt;kernel&lt;/span&gt;&lt;span class='LITERAL2'&gt; &lt;/span&gt;&lt;span class='LITERAL2'&gt;sequences&lt;/span&gt;&lt;span class='LITERAL2'&gt; &lt;/span&gt;&lt;span class='LITERAL2'&gt;splitting&lt;/span&gt;&lt;span class='LITERAL2'&gt; &lt;/span&gt;&lt;span class='LITERAL2'&gt;system&lt;/span&gt;&lt;br /&gt;&lt;span class='LITERAL2'&gt;vocabs.loader&lt;/span&gt;&lt;span class='LITERAL2'&gt; &lt;/span&gt;&lt;span class='MARKUP'&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class='MARKUP'&gt;IN: environment&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;HOOK: os-env os &lt;span class='COMMENT3'&gt;( &lt;/span&gt;&lt;span class='COMMENT4'&gt;key&lt;/span&gt;&lt;span class='COMMENT4'&gt; &lt;/span&gt;&lt;span class='COMMENT3'&gt;--&lt;/span&gt;&lt;span class='COMMENT4'&gt; &lt;/span&gt;&lt;span class='COMMENT4'&gt;value&lt;/span&gt;&lt;span class='COMMENT4'&gt; &lt;/span&gt;&lt;span class='COMMENT3'&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;HOOK: set-os-env os &lt;span class='COMMENT3'&gt;( &lt;/span&gt;&lt;span class='COMMENT4'&gt;value&lt;/span&gt;&lt;span class='COMMENT4'&gt; &lt;/span&gt;&lt;span class='COMMENT4'&gt;key&lt;/span&gt;&lt;span class='COMMENT4'&gt; &lt;/span&gt;&lt;span class='COMMENT3'&gt;--&lt;/span&gt;&lt;span class='COMMENT4'&gt; &lt;/span&gt;&lt;span class='COMMENT3'&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;HOOK: unset-os-env os &lt;span class='COMMENT3'&gt;( &lt;/span&gt;&lt;span class='COMMENT4'&gt;key&lt;/span&gt;&lt;span class='COMMENT4'&gt; &lt;/span&gt;&lt;span class='COMMENT3'&gt;--&lt;/span&gt;&lt;span class='COMMENT4'&gt; &lt;/span&gt;&lt;span class='COMMENT3'&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;HOOK: (os-envs) os &lt;span class='COMMENT3'&gt;( &lt;/span&gt;&lt;span class='COMMENT3'&gt;--&lt;/span&gt;&lt;span class='COMMENT4'&gt; &lt;/span&gt;&lt;span class='COMMENT4'&gt;seq&lt;/span&gt;&lt;span class='COMMENT4'&gt; &lt;/span&gt;&lt;span class='COMMENT3'&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;HOOK: (set-os-envs) os &lt;span class='COMMENT3'&gt;( &lt;/span&gt;&lt;span class='COMMENT4'&gt;seq&lt;/span&gt;&lt;span class='COMMENT4'&gt; &lt;/span&gt;&lt;span class='COMMENT3'&gt;--&lt;/span&gt;&lt;span class='COMMENT4'&gt; &lt;/span&gt;&lt;span class='COMMENT3'&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class='MARKUP'&gt;: os-envs&lt;/span&gt; &lt;span class='COMMENT3'&gt;( &lt;/span&gt;&lt;span class='COMMENT3'&gt;--&lt;/span&gt;&lt;span class='COMMENT4'&gt; &lt;/span&gt;&lt;span class='COMMENT4'&gt;assoc&lt;/span&gt;&lt;span class='COMMENT4'&gt; &lt;/span&gt;&lt;span class='COMMENT3'&gt;)&lt;/span&gt;&lt;br /&gt;    (os-envs) &lt;span class='OPERATOR'&gt;[&lt;/span&gt; &lt;span class='LITERAL1'&gt;"&lt;/span&gt;&lt;span class='LITERAL1'&gt;=&lt;/span&gt;&lt;span class='LITERAL1'&gt;"&lt;/span&gt; split1 &lt;span class='OPERATOR'&gt;]&lt;/span&gt; H&lt;span class='OPERATOR'&gt;{&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt; map&amp;gt;assoc &lt;span class='MARKUP'&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class='MARKUP'&gt;: set-os-envs&lt;/span&gt; &lt;span class='COMMENT3'&gt;( &lt;/span&gt;&lt;span class='COMMENT4'&gt;assoc&lt;/span&gt;&lt;span class='COMMENT4'&gt; &lt;/span&gt;&lt;span class='COMMENT3'&gt;--&lt;/span&gt;&lt;span class='COMMENT4'&gt; &lt;/span&gt;&lt;span class='COMMENT3'&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class='OPERATOR'&gt;[&lt;/span&gt; &lt;span class='LITERAL1'&gt;"&lt;/span&gt;&lt;span class='LITERAL1'&gt;=&lt;/span&gt;&lt;span class='LITERAL1'&gt;"&lt;/span&gt; swap 3append &lt;span class='OPERATOR'&gt;]&lt;/span&gt; &lt;span class='OPERATOR'&gt;{&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt; assoc&amp;gt;map (set-os-envs) &lt;span class='MARKUP'&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class='OPERATOR'&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class='OPERATOR'&gt;{&lt;/span&gt; &lt;span class='OPERATOR'&gt;[&lt;/span&gt; os unix? &lt;span class='OPERATOR'&gt;]&lt;/span&gt; &lt;span class='OPERATOR'&gt;[&lt;/span&gt; &lt;span class='LITERAL1'&gt;"&lt;/span&gt;&lt;span class='LITERAL1'&gt;environment.unix&lt;/span&gt;&lt;span class='LITERAL1'&gt;"&lt;/span&gt; require &lt;span class='OPERATOR'&gt;]&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt;&lt;br /&gt;    &lt;span class='OPERATOR'&gt;{&lt;/span&gt; &lt;span class='OPERATOR'&gt;[&lt;/span&gt; os winnt? &lt;span class='OPERATOR'&gt;]&lt;/span&gt; &lt;span class='OPERATOR'&gt;[&lt;/span&gt; &lt;span class='LITERAL1'&gt;"&lt;/span&gt;&lt;span class='LITERAL1'&gt;environment.winnt&lt;/span&gt;&lt;span class='LITERAL1'&gt;"&lt;/span&gt; require &lt;span class='OPERATOR'&gt;]&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt;&lt;br /&gt;    &lt;span class='OPERATOR'&gt;{&lt;/span&gt; &lt;span class='OPERATOR'&gt;[&lt;/span&gt; os wince? &lt;span class='OPERATOR'&gt;]&lt;/span&gt; &lt;span class='OPERATOR'&gt;[&lt;/span&gt; &lt;span class='OPERATOR'&gt;]&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class='OPERATOR'&gt;}&lt;/span&gt; cond&lt;br /&gt;&lt;/pre&gt;  &lt;h2&gt; Unix environment variables, before and after &lt;/h2&gt;  &lt;pre&gt;&lt;span class='FUNCTION'&gt;DEFINE_PRIMITIVE&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;os_env&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class='OPERATOR'&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class='KEYWORD3'&gt;char&lt;/span&gt; &lt;span class='OPERATOR'&gt;*&lt;/span&gt;name &lt;span class='OPERATOR'&gt;=&lt;/span&gt; &lt;span class='FUNCTION'&gt;unbox_char_string&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;span class='OPERATOR'&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class='KEYWORD3'&gt;char&lt;/span&gt; &lt;span class='OPERATOR'&gt;*&lt;/span&gt;value &lt;span class='OPERATOR'&gt;=&lt;/span&gt; &lt;span class='FUNCTION'&gt;getenv&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;name&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;span class='OPERATOR'&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class='KEYWORD1'&gt;if&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;value &lt;span class='OPERATOR'&gt;=&lt;/span&gt;&lt;span class='OPERATOR'&gt;=&lt;/span&gt; &lt;span class='LITERAL2'&gt;NULL&lt;/span&gt;&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;br /&gt;        &lt;span class='FUNCTION'&gt;dpush&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;F&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;span class='OPERATOR'&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class='KEYWORD1'&gt;else&lt;/span&gt;&lt;br /&gt;        &lt;span class='FUNCTION'&gt;box_char_string&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;value&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;span class='OPERATOR'&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class='OPERATOR'&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class='FUNCTION'&gt;DEFINE_PRIMITIVE&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;os_envs&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class='OPERATOR'&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class='FUNCTION'&gt;GROWABLE_ARRAY&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;result&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;span class='OPERATOR'&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class='FUNCTION'&gt;REGISTER_ROOT&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;result&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;span class='OPERATOR'&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class='KEYWORD3'&gt;char&lt;/span&gt; &lt;span class='OPERATOR'&gt;*&lt;/span&gt;&lt;span class='OPERATOR'&gt;*&lt;/span&gt;env &lt;span class='OPERATOR'&gt;=&lt;/span&gt; environ&lt;span class='OPERATOR'&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class='KEYWORD1'&gt;while&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;&lt;span class='OPERATOR'&gt;*&lt;/span&gt;env&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class='OPERATOR'&gt;{&lt;/span&gt;&lt;br /&gt;        CELL string &lt;span class='OPERATOR'&gt;=&lt;/span&gt; &lt;span class='FUNCTION'&gt;tag_object&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;&lt;span class='FUNCTION'&gt;from_char_string&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;&lt;span class='OPERATOR'&gt;*&lt;/span&gt;env&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;span class='OPERATOR'&gt;;&lt;/span&gt;&lt;br /&gt;        &lt;span class='FUNCTION'&gt;GROWABLE_ARRAY_ADD&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;result&lt;span class='OPERATOR'&gt;,&lt;/span&gt;string&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;span class='OPERATOR'&gt;;&lt;/span&gt;&lt;br /&gt;        env&lt;span class='OPERATOR'&gt;+&lt;/span&gt;&lt;span class='OPERATOR'&gt;+&lt;/span&gt;&lt;span class='OPERATOR'&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class='OPERATOR'&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class='FUNCTION'&gt;UNREGISTER_ROOT&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;result&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;span class='OPERATOR'&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class='FUNCTION'&gt;GROWABLE_ARRAY_TRIM&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;result&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;span class='OPERATOR'&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class='FUNCTION'&gt;dpush&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;result&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;span class='OPERATOR'&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class='OPERATOR'&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class='FUNCTION'&gt;DEFINE_PRIMITIVE&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;set_os_env&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class='OPERATOR'&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class='KEYWORD3'&gt;char&lt;/span&gt; &lt;span class='OPERATOR'&gt;*&lt;/span&gt;key &lt;span class='OPERATOR'&gt;=&lt;/span&gt; &lt;span class='FUNCTION'&gt;unbox_char_string&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;span class='OPERATOR'&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class='FUNCTION'&gt;REGISTER_C_STRING&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;key&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;span class='OPERATOR'&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class='KEYWORD3'&gt;char&lt;/span&gt; &lt;span class='OPERATOR'&gt;*&lt;/span&gt;value &lt;span class='OPERATOR'&gt;=&lt;/span&gt; &lt;span class='FUNCTION'&gt;unbox_char_string&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;span class='OPERATOR'&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class='FUNCTION'&gt;UNREGISTER_C_STRING&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;key&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;span class='OPERATOR'&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class='FUNCTION'&gt;setenv&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;key&lt;span class='OPERATOR'&gt;,&lt;/span&gt; value&lt;span class='OPERATOR'&gt;,&lt;/span&gt; &lt;span class='DIGIT'&gt;1&lt;/span&gt;&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;span class='OPERATOR'&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class='OPERATOR'&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class='FUNCTION'&gt;DEFINE_PRIMITIVE&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;unset_os_env&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class='OPERATOR'&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class='KEYWORD3'&gt;char&lt;/span&gt; &lt;span class='OPERATOR'&gt;*&lt;/span&gt;key &lt;span class='OPERATOR'&gt;=&lt;/span&gt; &lt;span class='FUNCTION'&gt;unbox_char_string&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;span class='OPERATOR'&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class='FUNCTION'&gt;unsetenv&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;key&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;span class='OPERATOR'&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class='OPERATOR'&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class='FUNCTION'&gt;DEFINE_PRIMITIVE&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;set_os_envs&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class='OPERATOR'&gt;{&lt;/span&gt;&lt;br /&gt;    F_ARRAY &lt;span class='OPERATOR'&gt;*&lt;/span&gt;array &lt;span class='OPERATOR'&gt;=&lt;/span&gt; &lt;span class='FUNCTION'&gt;untag_array&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;&lt;span class='FUNCTION'&gt;dpop&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;span class='OPERATOR'&gt;;&lt;/span&gt;&lt;br /&gt;    CELL size &lt;span class='OPERATOR'&gt;=&lt;/span&gt; &lt;span class='FUNCTION'&gt;array_capacity&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;array&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;span class='OPERATOR'&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class='COMMENT1'&gt;/*&lt;/span&gt;&lt;span class='COMMENT1'&gt; &lt;/span&gt;&lt;span class='COMMENT1'&gt;Memory&lt;/span&gt;&lt;span class='COMMENT1'&gt; &lt;/span&gt;&lt;span class='COMMENT1'&gt;leak&lt;/span&gt;&lt;span class='COMMENT1'&gt; &lt;/span&gt;&lt;span class='COMMENT1'&gt;*/&lt;/span&gt;&lt;br /&gt;    &lt;span class='KEYWORD3'&gt;char&lt;/span&gt; &lt;span class='OPERATOR'&gt;*&lt;/span&gt;&lt;span class='OPERATOR'&gt;*&lt;/span&gt;env &lt;span class='OPERATOR'&gt;=&lt;/span&gt; &lt;span class='FUNCTION'&gt;calloc&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;size &lt;span class='OPERATOR'&gt;+&lt;/span&gt; &lt;span class='DIGIT'&gt;1&lt;/span&gt;&lt;span class='OPERATOR'&gt;,&lt;/span&gt;&lt;span class='KEYWORD1'&gt;sizeof&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;CELL&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;span class='OPERATOR'&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    CELL i&lt;span class='OPERATOR'&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class='KEYWORD1'&gt;for&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;i &lt;span class='OPERATOR'&gt;=&lt;/span&gt; &lt;span class='DIGIT'&gt;0&lt;/span&gt;&lt;span class='OPERATOR'&gt;;&lt;/span&gt; i &lt;span class='OPERATOR'&gt;&amp;lt;&lt;/span&gt; size&lt;span class='OPERATOR'&gt;;&lt;/span&gt; i&lt;span class='OPERATOR'&gt;+&lt;/span&gt;&lt;span class='OPERATOR'&gt;+&lt;/span&gt;&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class='OPERATOR'&gt;{&lt;/span&gt;&lt;br /&gt;        F_STRING &lt;span class='OPERATOR'&gt;*&lt;/span&gt;string &lt;span class='OPERATOR'&gt;=&lt;/span&gt; &lt;span class='FUNCTION'&gt;untag_string&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;&lt;span class='FUNCTION'&gt;array_nth&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;array&lt;span class='OPERATOR'&gt;,&lt;/span&gt;i&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;span class='OPERATOR'&gt;;&lt;/span&gt;&lt;br /&gt;        CELL length &lt;span class='OPERATOR'&gt;=&lt;/span&gt; &lt;span class='FUNCTION'&gt;to_fixnum&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;string&lt;span class='OPERATOR'&gt;-&lt;/span&gt;&lt;span class='OPERATOR'&gt;&amp;gt;&lt;/span&gt;length&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;span class='OPERATOR'&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;        &lt;span class='KEYWORD3'&gt;char&lt;/span&gt; &lt;span class='OPERATOR'&gt;*&lt;/span&gt;chars &lt;span class='OPERATOR'&gt;=&lt;/span&gt; &lt;span class='FUNCTION'&gt;malloc&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;length &lt;span class='OPERATOR'&gt;+&lt;/span&gt; &lt;span class='DIGIT'&gt;1&lt;/span&gt;&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;span class='OPERATOR'&gt;;&lt;/span&gt;&lt;br /&gt;        &lt;span class='FUNCTION'&gt;char_string_to_memory&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;string&lt;span class='OPERATOR'&gt;,&lt;/span&gt;chars&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;span class='OPERATOR'&gt;;&lt;/span&gt;&lt;br /&gt;        chars&lt;span class='OPERATOR'&gt;[&lt;/span&gt;length&lt;span class='OPERATOR'&gt;]&lt;/span&gt; &lt;span class='OPERATOR'&gt;=&lt;/span&gt; &lt;span class='LITERAL1'&gt;'&lt;/span&gt;&lt;span class='LITERAL1'&gt;\0&lt;/span&gt;&lt;span class='LITERAL1'&gt;'&lt;/span&gt;&lt;span class='OPERATOR'&gt;;&lt;/span&gt;&lt;br /&gt;        env&lt;span class='OPERATOR'&gt;[&lt;/span&gt;i&lt;span class='OPERATOR'&gt;]&lt;/span&gt; &lt;span class='OPERATOR'&gt;=&lt;/span&gt; chars&lt;span class='OPERATOR'&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class='OPERATOR'&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    environ &lt;span class='OPERATOR'&gt;=&lt;/span&gt; env&lt;span class='OPERATOR'&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class='OPERATOR'&gt;}&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;b&gt;Factor&lt;/b&gt;  &lt;pre&gt;&lt;span class='MARKUP'&gt;USING: &lt;/span&gt;&lt;span class='LITERAL2'&gt;alien&lt;/span&gt;&lt;span class='LITERAL2'&gt; &lt;/span&gt;&lt;span class='LITERAL2'&gt;alien.c-types&lt;/span&gt;&lt;span class='LITERAL2'&gt; &lt;/span&gt;&lt;span class='LITERAL2'&gt;alien.strings&lt;/span&gt;&lt;span class='LITERAL2'&gt; &lt;/span&gt;&lt;span class='LITERAL2'&gt;alien.syntax&lt;/span&gt;&lt;span class='LITERAL2'&gt; &lt;/span&gt;&lt;span class='LITERAL2'&gt;kernel&lt;/span&gt;&lt;br /&gt;&lt;span class='LITERAL2'&gt;layouts&lt;/span&gt;&lt;span class='LITERAL2'&gt; &lt;/span&gt;&lt;span class='LITERAL2'&gt;sequences&lt;/span&gt;&lt;span class='LITERAL2'&gt; &lt;/span&gt;&lt;span class='LITERAL2'&gt;system&lt;/span&gt;&lt;span class='LITERAL2'&gt; &lt;/span&gt;&lt;span class='LITERAL2'&gt;unix&lt;/span&gt;&lt;span class='LITERAL2'&gt; &lt;/span&gt;&lt;span class='LITERAL2'&gt;environment&lt;/span&gt;&lt;span class='LITERAL2'&gt; &lt;/span&gt;&lt;span class='LITERAL2'&gt;io.encodings.utf8&lt;/span&gt;&lt;br /&gt;&lt;span class='LITERAL2'&gt;unix.utilities&lt;/span&gt;&lt;span class='LITERAL2'&gt; &lt;/span&gt;&lt;span class='LITERAL2'&gt;vocabs.loader&lt;/span&gt;&lt;span class='LITERAL2'&gt; &lt;/span&gt;&lt;span class='LITERAL2'&gt;combinators&lt;/span&gt;&lt;span class='LITERAL2'&gt; &lt;/span&gt;&lt;span class='LITERAL2'&gt;alien.accessors&lt;/span&gt;&lt;span class='LITERAL2'&gt; &lt;/span&gt;&lt;span class='MARKUP'&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class='MARKUP'&gt;IN: environment.unix&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;HOOK: environ os &lt;span class='COMMENT3'&gt;( &lt;/span&gt;&lt;span class='COMMENT3'&gt;--&lt;/span&gt;&lt;span class='COMMENT4'&gt; &lt;/span&gt;&lt;span class='COMMENT4'&gt;void&lt;/span&gt;&lt;span class='COMMENT4'&gt;*&lt;/span&gt;&lt;span class='COMMENT4'&gt; &lt;/span&gt;&lt;span class='COMMENT3'&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;M: unix environ &lt;span class='COMMENT3'&gt;( &lt;/span&gt;&lt;span class='COMMENT3'&gt;--&lt;/span&gt;&lt;span class='COMMENT4'&gt; &lt;/span&gt;&lt;span class='COMMENT4'&gt;void&lt;/span&gt;&lt;span class='COMMENT4'&gt;*&lt;/span&gt;&lt;span class='COMMENT4'&gt; &lt;/span&gt;&lt;span class='COMMENT3'&gt;)&lt;/span&gt; &lt;span class='LITERAL1'&gt;"&lt;/span&gt;&lt;span class='LITERAL1'&gt;environ&lt;/span&gt;&lt;span class='LITERAL1'&gt;"&lt;/span&gt; &lt;span class='LITERAL4'&gt;f&lt;/span&gt; dlsym &lt;span class='MARKUP'&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;M: unix os-env &lt;span class='COMMENT3'&gt;( &lt;/span&gt;&lt;span class='COMMENT4'&gt;key&lt;/span&gt;&lt;span class='COMMENT4'&gt; &lt;/span&gt;&lt;span class='COMMENT3'&gt;--&lt;/span&gt;&lt;span class='COMMENT4'&gt; &lt;/span&gt;&lt;span class='COMMENT4'&gt;value&lt;/span&gt;&lt;span class='COMMENT4'&gt; &lt;/span&gt;&lt;span class='COMMENT3'&gt;)&lt;/span&gt; getenv &lt;span class='MARKUP'&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;M: unix set-os-env &lt;span class='COMMENT3'&gt;( &lt;/span&gt;&lt;span class='COMMENT4'&gt;value&lt;/span&gt;&lt;span class='COMMENT4'&gt; &lt;/span&gt;&lt;span class='COMMENT4'&gt;key&lt;/span&gt;&lt;span class='COMMENT4'&gt; &lt;/span&gt;&lt;span class='COMMENT3'&gt;--&lt;/span&gt;&lt;span class='COMMENT4'&gt; &lt;/span&gt;&lt;span class='COMMENT3'&gt;)&lt;/span&gt; swap &lt;span class='DIGIT'&gt;1&lt;/span&gt; setenv io-error &lt;span class='MARKUP'&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;M: unix unset-os-env &lt;span class='COMMENT3'&gt;( &lt;/span&gt;&lt;span class='COMMENT4'&gt;key&lt;/span&gt;&lt;span class='COMMENT4'&gt; &lt;/span&gt;&lt;span class='COMMENT3'&gt;--&lt;/span&gt;&lt;span class='COMMENT4'&gt; &lt;/span&gt;&lt;span class='COMMENT3'&gt;)&lt;/span&gt; unsetenv io-error &lt;span class='MARKUP'&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;M: unix (os-envs) &lt;span class='COMMENT3'&gt;( &lt;/span&gt;&lt;span class='COMMENT3'&gt;--&lt;/span&gt;&lt;span class='COMMENT4'&gt; &lt;/span&gt;&lt;span class='COMMENT4'&gt;seq&lt;/span&gt;&lt;span class='COMMENT4'&gt; &lt;/span&gt;&lt;span class='COMMENT3'&gt;)&lt;/span&gt;&lt;br /&gt;    environ *void* utf8 alien&amp;gt;strings &lt;span class='MARKUP'&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class='MARKUP'&gt;: set-void*&lt;/span&gt; &lt;span class='COMMENT3'&gt;( &lt;/span&gt;&lt;span class='COMMENT4'&gt;value&lt;/span&gt;&lt;span class='COMMENT4'&gt; &lt;/span&gt;&lt;span class='COMMENT4'&gt;alien&lt;/span&gt;&lt;span class='COMMENT4'&gt; &lt;/span&gt;&lt;span class='COMMENT3'&gt;--&lt;/span&gt;&lt;span class='COMMENT4'&gt; &lt;/span&gt;&lt;span class='COMMENT3'&gt;)&lt;/span&gt; &lt;span class='DIGIT'&gt;0&lt;/span&gt; set-alien-cell &lt;span class='MARKUP'&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;M: unix (set-os-envs) &lt;span class='COMMENT3'&gt;( &lt;/span&gt;&lt;span class='COMMENT4'&gt;seq&lt;/span&gt;&lt;span class='COMMENT4'&gt; &lt;/span&gt;&lt;span class='COMMENT3'&gt;--&lt;/span&gt;&lt;span class='COMMENT4'&gt; &lt;/span&gt;&lt;span class='COMMENT3'&gt;)&lt;/span&gt;&lt;br /&gt;    utf8 strings&amp;gt;alien malloc-byte-array environ set-void* &lt;span class='MARKUP'&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;os &lt;span class='OPERATOR'&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class='OPERATOR'&gt;{&lt;/span&gt; macosx &lt;span class='OPERATOR'&gt;[&lt;/span&gt; &lt;span class='LITERAL1'&gt;"&lt;/span&gt;&lt;span class='LITERAL1'&gt;environment.unix.macosx&lt;/span&gt;&lt;span class='LITERAL1'&gt;"&lt;/span&gt; require &lt;span class='OPERATOR'&gt;]&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt;&lt;br /&gt;    &lt;span class='OPERATOR'&gt;[&lt;/span&gt; drop &lt;span class='OPERATOR'&gt;]&lt;/span&gt;&lt;br /&gt;&lt;span class='OPERATOR'&gt;}&lt;/span&gt; case&lt;br /&gt;&lt;/pre&gt;  &lt;h2&gt; MacOSX environment variables, before and after &lt;/h2&gt;  &lt;p&gt;On OSX, we have to use a function to access the environment variable. &lt;/p&gt;&lt;pre&gt;&lt;span class='KEYWORD2'&gt;#&lt;/span&gt;&lt;span class='MARKUP'&gt;ifndef&lt;/span&gt; environ&lt;br /&gt;    &lt;span class='KEYWORD2'&gt;extern&lt;/span&gt; &lt;span class='KEYWORD3'&gt;char&lt;/span&gt; &lt;span class='OPERATOR'&gt;*&lt;/span&gt;&lt;span class='OPERATOR'&gt;*&lt;/span&gt;&lt;span class='OPERATOR'&gt;*&lt;/span&gt;&lt;span class='FUNCTION'&gt;_NSGetEnviron&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;&lt;span class='KEYWORD1'&gt;void&lt;/span&gt;&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;span class='OPERATOR'&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class='KEYWORD2'&gt;#&lt;/span&gt;&lt;span class='MARKUP'&gt;define&lt;/span&gt; environ &lt;span class='OPERATOR'&gt;(&lt;/span&gt;&lt;span class='OPERATOR'&gt;*&lt;/span&gt;&lt;span class='FUNCTION'&gt;_NSGetEnviron&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class='KEYWORD2'&gt;#&lt;/span&gt;&lt;span class='MARKUP'&gt;endif&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;b&gt;Factor&lt;/b&gt;  &lt;pre&gt;&lt;span class='MARKUP'&gt;USING: &lt;/span&gt;&lt;span class='LITERAL2'&gt;alien.syntax&lt;/span&gt;&lt;span class='LITERAL2'&gt; &lt;/span&gt;&lt;span class='LITERAL2'&gt;system&lt;/span&gt;&lt;span class='LITERAL2'&gt; &lt;/span&gt;&lt;span class='LITERAL2'&gt;environment.unix&lt;/span&gt;&lt;span class='LITERAL2'&gt; &lt;/span&gt;&lt;span class='MARKUP'&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class='MARKUP'&gt;IN: environment.unix.macosx&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;FUNCTION: void* _NSGetEnviron &lt;span class='COMMENT3'&gt;( &lt;/span&gt;&lt;span class='COMMENT3'&gt;)&lt;/span&gt; &lt;span class='MARKUP'&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;M: macosx environ _NSGetEnviron &lt;span class='MARKUP'&gt;;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;  &lt;h2&gt; Windows NT environment variables, before and after &lt;/h2&gt;  &lt;p&gt;Draw your own conclusions. &lt;/p&gt;&lt;pre&gt;&lt;span class='FUNCTION'&gt;DEFINE_PRIMITIVE&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;os_env&lt;span class='OPERATOR'&gt;)&lt;/span&gt; &lt;br /&gt;&lt;span class='OPERATOR'&gt;{&lt;/span&gt; &lt;br /&gt;    F_CHAR &lt;span class='OPERATOR'&gt;*&lt;/span&gt;key &lt;span class='OPERATOR'&gt;=&lt;/span&gt; &lt;span class='FUNCTION'&gt;unbox_u16_string&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;span class='OPERATOR'&gt;;&lt;/span&gt; &lt;br /&gt;    F_CHAR &lt;span class='OPERATOR'&gt;*&lt;/span&gt;value &lt;span class='OPERATOR'&gt;=&lt;/span&gt; &lt;span class='FUNCTION'&gt;safe_malloc&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;MAX_UNICODE_PATH &lt;span class='OPERATOR'&gt;*&lt;/span&gt; &lt;span class='DIGIT'&gt;2&lt;/span&gt;&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;span class='OPERATOR'&gt;;&lt;/span&gt; &lt;br /&gt;    &lt;span class='KEYWORD3'&gt;int&lt;/span&gt; ret&lt;span class='OPERATOR'&gt;;&lt;/span&gt; &lt;br /&gt;    ret &lt;span class='OPERATOR'&gt;=&lt;/span&gt; &lt;span class='FUNCTION'&gt;GetEnvironmentVariable&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;key&lt;span class='OPERATOR'&gt;,&lt;/span&gt; value&lt;span class='OPERATOR'&gt;,&lt;/span&gt; MAX_UNICODE_PATH &lt;span class='OPERATOR'&gt;*&lt;/span&gt; &lt;span class='DIGIT'&gt;2&lt;/span&gt;&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;span class='OPERATOR'&gt;;&lt;/span&gt; &lt;br /&gt;    &lt;span class='KEYWORD1'&gt;if&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;ret &lt;span class='OPERATOR'&gt;=&lt;/span&gt;&lt;span class='OPERATOR'&gt;=&lt;/span&gt; &lt;span class='DIGIT'&gt;0&lt;/span&gt;&lt;span class='OPERATOR'&gt;)&lt;/span&gt; &lt;br /&gt;        &lt;span class='FUNCTION'&gt;dpush&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;F&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;span class='OPERATOR'&gt;;&lt;/span&gt; &lt;br /&gt;    &lt;span class='KEYWORD1'&gt;else&lt;/span&gt; &lt;br /&gt;        &lt;span class='FUNCTION'&gt;dpush&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;&lt;span class='FUNCTION'&gt;tag_object&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;&lt;span class='FUNCTION'&gt;from_u16_string&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;value&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;span class='OPERATOR'&gt;;&lt;/span&gt; &lt;br /&gt;    &lt;span class='FUNCTION'&gt;free&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;value&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;span class='OPERATOR'&gt;;&lt;/span&gt; &lt;br /&gt;&lt;span class='OPERATOR'&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class='FUNCTION'&gt;DEFINE_PRIMITIVE&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;os_envs&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class='OPERATOR'&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class='FUNCTION'&gt;GROWABLE_ARRAY&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;result&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;span class='OPERATOR'&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class='FUNCTION'&gt;REGISTER_ROOT&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;result&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;span class='OPERATOR'&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    TCHAR &lt;span class='OPERATOR'&gt;*&lt;/span&gt;env &lt;span class='OPERATOR'&gt;=&lt;/span&gt; &lt;span class='FUNCTION'&gt;GetEnvironmentStrings&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;span class='OPERATOR'&gt;;&lt;/span&gt;&lt;br /&gt;    TCHAR &lt;span class='OPERATOR'&gt;*&lt;/span&gt;finger &lt;span class='OPERATOR'&gt;=&lt;/span&gt; env&lt;span class='OPERATOR'&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class='KEYWORD1'&gt;for&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;&lt;span class='OPERATOR'&gt;;&lt;/span&gt;&lt;span class='OPERATOR'&gt;;&lt;/span&gt;&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class='OPERATOR'&gt;{&lt;/span&gt;&lt;br /&gt;        TCHAR &lt;span class='OPERATOR'&gt;*&lt;/span&gt;scan &lt;span class='OPERATOR'&gt;=&lt;/span&gt; finger&lt;span class='OPERATOR'&gt;;&lt;/span&gt;&lt;br /&gt;        &lt;span class='KEYWORD1'&gt;while&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;&lt;span class='OPERATOR'&gt;*&lt;/span&gt;scan &lt;span class='OPERATOR'&gt;!&lt;/span&gt;&lt;span class='OPERATOR'&gt;=&lt;/span&gt; &lt;span class='LITERAL1'&gt;'&lt;/span&gt;&lt;span class='LITERAL1'&gt;\0&lt;/span&gt;&lt;span class='LITERAL1'&gt;'&lt;/span&gt;&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;br /&gt;            scan&lt;span class='OPERATOR'&gt;+&lt;/span&gt;&lt;span class='OPERATOR'&gt;+&lt;/span&gt;&lt;span class='OPERATOR'&gt;;&lt;/span&gt;&lt;br /&gt;        &lt;span class='KEYWORD1'&gt;if&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;scan &lt;span class='OPERATOR'&gt;=&lt;/span&gt;&lt;span class='OPERATOR'&gt;=&lt;/span&gt; finger&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;br /&gt;            &lt;span class='KEYWORD1'&gt;break&lt;/span&gt;&lt;span class='OPERATOR'&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;        CELL string &lt;span class='OPERATOR'&gt;=&lt;/span&gt; &lt;span class='FUNCTION'&gt;tag_object&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;&lt;span class='FUNCTION'&gt;from_u16_string&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;finger&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;span class='OPERATOR'&gt;;&lt;/span&gt;&lt;br /&gt;        &lt;span class='FUNCTION'&gt;GROWABLE_ARRAY_ADD&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;result&lt;span class='OPERATOR'&gt;,&lt;/span&gt;string&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;span class='OPERATOR'&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;        finger &lt;span class='OPERATOR'&gt;=&lt;/span&gt; scan &lt;span class='OPERATOR'&gt;+&lt;/span&gt; &lt;span class='DIGIT'&gt;1&lt;/span&gt;&lt;span class='OPERATOR'&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class='OPERATOR'&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class='FUNCTION'&gt;FreeEnvironmentStrings&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;env&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;span class='OPERATOR'&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class='FUNCTION'&gt;UNREGISTER_ROOT&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;result&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;span class='OPERATOR'&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class='FUNCTION'&gt;GROWABLE_ARRAY_TRIM&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;result&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;span class='OPERATOR'&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class='FUNCTION'&gt;dpush&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;result&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;span class='OPERATOR'&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class='OPERATOR'&gt;}&lt;/span&gt;&lt;br /&gt; &lt;br /&gt;&lt;span class='FUNCTION'&gt;DEFINE_PRIMITIVE&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;set_os_env&lt;span class='OPERATOR'&gt;)&lt;/span&gt; &lt;br /&gt;&lt;span class='OPERATOR'&gt;{&lt;/span&gt; &lt;br /&gt;    F_CHAR &lt;span class='OPERATOR'&gt;*&lt;/span&gt;key &lt;span class='OPERATOR'&gt;=&lt;/span&gt; &lt;span class='FUNCTION'&gt;unbox_u16_string&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;span class='OPERATOR'&gt;;&lt;/span&gt; &lt;br /&gt;    &lt;span class='FUNCTION'&gt;REGISTER_C_STRING&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;key&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;span class='OPERATOR'&gt;;&lt;/span&gt; &lt;br /&gt;    F_CHAR &lt;span class='OPERATOR'&gt;*&lt;/span&gt;value &lt;span class='OPERATOR'&gt;=&lt;/span&gt; &lt;span class='FUNCTION'&gt;unbox_u16_string&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;span class='OPERATOR'&gt;;&lt;/span&gt; &lt;br /&gt;    &lt;span class='FUNCTION'&gt;UNREGISTER_C_STRING&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;key&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;span class='OPERATOR'&gt;;&lt;/span&gt; &lt;br /&gt;    &lt;span class='KEYWORD1'&gt;if&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;&lt;span class='OPERATOR'&gt;!&lt;/span&gt;&lt;span class='FUNCTION'&gt;SetEnvironmentVariable&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;key&lt;span class='OPERATOR'&gt;,&lt;/span&gt; value&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;span class='OPERATOR'&gt;)&lt;/span&gt; &lt;br /&gt;        &lt;span class='FUNCTION'&gt;general_error&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;ERROR_IO&lt;span class='OPERATOR'&gt;,&lt;/span&gt; &lt;span class='FUNCTION'&gt;tag_object&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;&lt;span class='FUNCTION'&gt;get_error_message&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;span class='OPERATOR'&gt;,&lt;/span&gt; F&lt;span class='OPERATOR'&gt;,&lt;/span&gt; &lt;span class='LITERAL2'&gt;NULL&lt;/span&gt;&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;span class='OPERATOR'&gt;;&lt;/span&gt; &lt;br /&gt;&lt;span class='OPERATOR'&gt;}&lt;/span&gt; &lt;br /&gt; &lt;br /&gt;&lt;span class='FUNCTION'&gt;DEFINE_PRIMITIVE&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;unset_os_env&lt;span class='OPERATOR'&gt;)&lt;/span&gt; &lt;br /&gt;&lt;span class='OPERATOR'&gt;{&lt;/span&gt; &lt;br /&gt;    &lt;span class='KEYWORD1'&gt;if&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;&lt;span class='OPERATOR'&gt;!&lt;/span&gt;&lt;span class='FUNCTION'&gt;SetEnvironmentVariable&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;&lt;span class='FUNCTION'&gt;unbox_u16_string&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;span class='OPERATOR'&gt;,&lt;/span&gt; &lt;span class='LITERAL2'&gt;NULL&lt;/span&gt;&lt;span class='OPERATOR'&gt;)&lt;/span&gt; &lt;br /&gt;        &lt;span class='OPERATOR'&gt;&amp;amp;&lt;/span&gt;&lt;span class='OPERATOR'&gt;&amp;amp;&lt;/span&gt; &lt;span class='FUNCTION'&gt;GetLastError&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;&lt;span class='OPERATOR'&gt;)&lt;/span&gt; &lt;span class='OPERATOR'&gt;!&lt;/span&gt;&lt;span class='OPERATOR'&gt;=&lt;/span&gt; ERROR_ENVVAR_NOT_FOUND&lt;span class='OPERATOR'&gt;)&lt;/span&gt; &lt;br /&gt;        &lt;span class='FUNCTION'&gt;general_error&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;ERROR_IO&lt;span class='OPERATOR'&gt;,&lt;/span&gt; &lt;span class='FUNCTION'&gt;tag_object&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;&lt;span class='FUNCTION'&gt;get_error_message&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;span class='OPERATOR'&gt;,&lt;/span&gt; F&lt;span class='OPERATOR'&gt;,&lt;/span&gt; &lt;span class='LITERAL2'&gt;NULL&lt;/span&gt;&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;span class='OPERATOR'&gt;;&lt;/span&gt; &lt;br /&gt;&lt;span class='OPERATOR'&gt;}&lt;/span&gt; &lt;br /&gt; &lt;br /&gt;&lt;span class='FUNCTION'&gt;DEFINE_PRIMITIVE&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;set_os_envs&lt;span class='OPERATOR'&gt;)&lt;/span&gt; &lt;br /&gt;&lt;span class='OPERATOR'&gt;{&lt;/span&gt; &lt;br /&gt;    &lt;span class='FUNCTION'&gt;not_implemented_error&lt;/span&gt;&lt;span class='OPERATOR'&gt;(&lt;/span&gt;&lt;span class='OPERATOR'&gt;)&lt;/span&gt;&lt;span class='OPERATOR'&gt;;&lt;/span&gt; &lt;br /&gt;&lt;span class='OPERATOR'&gt;}&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;b&gt;Factor&lt;/b&gt; &lt;pre&gt;&lt;span class='MARKUP'&gt;USING: &lt;/span&gt;&lt;span class='LITERAL2'&gt;alien.strings&lt;/span&gt;&lt;span class='LITERAL2'&gt; &lt;/span&gt;&lt;span class='LITERAL2'&gt;fry&lt;/span&gt;&lt;span class='LITERAL2'&gt; &lt;/span&gt;&lt;span class='LITERAL2'&gt;io.encodings.utf16&lt;/span&gt;&lt;span class='LITERAL2'&gt; &lt;/span&gt;&lt;span class='LITERAL2'&gt;kernel&lt;/span&gt;&lt;br /&gt;&lt;span class='LITERAL2'&gt;splitting&lt;/span&gt;&lt;span class='LITERAL2'&gt; &lt;/span&gt;&lt;span class='LITERAL2'&gt;windows&lt;/span&gt;&lt;span class='LITERAL2'&gt; &lt;/span&gt;&lt;span class='LITERAL2'&gt;windows.kernel32&lt;/span&gt;&lt;span class='LITERAL2'&gt; &lt;/span&gt;&lt;span class='MARKUP'&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class='MARKUP'&gt;IN: environment.winnt&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;M: winnt os-env &lt;span class='COMMENT3'&gt;( &lt;/span&gt;&lt;span class='COMMENT4'&gt;key&lt;/span&gt;&lt;span class='COMMENT4'&gt; &lt;/span&gt;&lt;span class='COMMENT3'&gt;--&lt;/span&gt;&lt;span class='COMMENT4'&gt; &lt;/span&gt;&lt;span class='COMMENT4'&gt;value&lt;/span&gt;&lt;span class='COMMENT4'&gt; &lt;/span&gt;&lt;span class='COMMENT3'&gt;)&lt;/span&gt;&lt;br /&gt;    MAX_UNICODE_PATH &lt;span class='LITERAL1'&gt;"&lt;/span&gt;&lt;span class='LITERAL1'&gt;TCHAR&lt;/span&gt;&lt;span class='LITERAL1'&gt;"&lt;/span&gt; &amp;lt;c-array&amp;gt;&lt;br /&gt;    &lt;span class='OPERATOR'&gt;[&lt;/span&gt; dup length GetEnvironmentVariable &lt;span class='OPERATOR'&gt;]&lt;/span&gt; keep over &lt;span class='DIGIT'&gt;0&lt;/span&gt; = &lt;span class='OPERATOR'&gt;[&lt;/span&gt;&lt;br /&gt;        2drop &lt;span class='LITERAL4'&gt;f&lt;/span&gt;&lt;br /&gt;    &lt;span class='OPERATOR'&gt;]&lt;/span&gt; &lt;span class='OPERATOR'&gt;[&lt;/span&gt;&lt;br /&gt;        nip utf16n alien&amp;gt;string&lt;br /&gt;    &lt;span class='OPERATOR'&gt;]&lt;/span&gt; if &lt;span class='MARKUP'&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;M: winnt set-os-env &lt;span class='COMMENT3'&gt;( &lt;/span&gt;&lt;span class='COMMENT4'&gt;value&lt;/span&gt;&lt;span class='COMMENT4'&gt; &lt;/span&gt;&lt;span class='COMMENT4'&gt;key&lt;/span&gt;&lt;span class='COMMENT4'&gt; &lt;/span&gt;&lt;span class='COMMENT3'&gt;--&lt;/span&gt;&lt;span class='COMMENT4'&gt; &lt;/span&gt;&lt;span class='COMMENT3'&gt;)&lt;/span&gt;&lt;br /&gt;    swap SetEnvironmentVariable win32-error=0/f &lt;span class='MARKUP'&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;M: winnt unset-os-env &lt;span class='COMMENT3'&gt;( &lt;/span&gt;&lt;span class='COMMENT4'&gt;key&lt;/span&gt;&lt;span class='COMMENT4'&gt; &lt;/span&gt;&lt;span class='COMMENT3'&gt;--&lt;/span&gt;&lt;span class='COMMENT4'&gt; &lt;/span&gt;&lt;span class='COMMENT3'&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class='LITERAL4'&gt;f&lt;/span&gt; SetEnvironmentVariable &lt;span class='DIGIT'&gt;0&lt;/span&gt; = &lt;span class='OPERATOR'&gt;[&lt;/span&gt;&lt;br /&gt;        GetLastError ERROR_ENVVAR_NOT_FOUND =&lt;br /&gt;        &lt;span class='OPERATOR'&gt;[&lt;/span&gt; win32-error &lt;span class='OPERATOR'&gt;]&lt;/span&gt; unless&lt;br /&gt;    &lt;span class='OPERATOR'&gt;]&lt;/span&gt; when &lt;span class='MARKUP'&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;M: winnt (os-envs) &lt;span class='COMMENT3'&gt;( &lt;/span&gt;&lt;span class='COMMENT3'&gt;--&lt;/span&gt;&lt;span class='COMMENT4'&gt; &lt;/span&gt;&lt;span class='COMMENT4'&gt;seq&lt;/span&gt;&lt;span class='COMMENT4'&gt; &lt;/span&gt;&lt;span class='COMMENT3'&gt;)&lt;/span&gt;&lt;br /&gt;    GetEnvironmentStrings &lt;span class='OPERATOR'&gt;[&lt;/span&gt;&lt;br /&gt;        &amp;lt;memory-stream&amp;gt; &lt;span class='OPERATOR'&gt;[&lt;/span&gt;&lt;br /&gt;            utf16n decode-input&lt;br /&gt;            &lt;span class='OPERATOR'&gt;[&lt;/span&gt; &lt;span class='LITERAL1'&gt;"&lt;/span&gt;&lt;span class='LITERAL1'&gt;\0&lt;/span&gt;&lt;span class='LITERAL1'&gt;"&lt;/span&gt; read-until drop dup empty? not &lt;span class='OPERATOR'&gt;]&lt;/span&gt;&lt;br /&gt;            &lt;span class='OPERATOR'&gt;[&lt;/span&gt; &lt;span class='OPERATOR'&gt;]&lt;/span&gt; &lt;span class='OPERATOR'&gt;[&lt;/span&gt; drop &lt;span class='OPERATOR'&gt;]&lt;/span&gt; produce&lt;br /&gt;        &lt;span class='OPERATOR'&gt;]&lt;/span&gt; with-input-stream*&lt;br /&gt;    &lt;span class='OPERATOR'&gt;]&lt;/span&gt; &lt;span class='OPERATOR'&gt;[&lt;/span&gt; FreeEnvironmentStrings win32-error=0/f &lt;span class='OPERATOR'&gt;]&lt;/span&gt; bi &lt;span class='MARKUP'&gt;;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37596622-3801728555615789993?l=code-factor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://code-factor.blogspot.com/feeds/3801728555615789993/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=37596622&amp;postID=3801728555615789993' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/3801728555615789993'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/3801728555615789993'/><link rel='alternate' type='text/html' href='http://code-factor.blogspot.com/2008/10/moving-primtives-out-of-factor-vm.html' title='Moving primitives out of the Factor VM: Factor vs C'/><author><name>Doug Coleman</name><uri>http://www.blogger.com/profile/09416611825627835802</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-37596622.post-94350105557918199</id><published>2008-10-18T13:03:00.000-07:00</published><updated>2008-10-19T01:09:23.245-07:00</updated><title type='text'>Introducing the Factor database library</title><content type='html'>&lt;h2&gt; Background &lt;/h2&gt; &lt;p&gt;One of the first libraries I wrote in Factor was a binding to PostgreSQL library in May 2005.  Factor makes it incredibly easy to bind to C functions -- just add a &lt;code&gt;FUNCTION:&lt;/code&gt; declaration and call the C function. For example, &lt;pre&gt;&lt;span class='COMMENT3'&gt;( &lt;/span&gt;&lt;span class='COMMENT4'&gt;scratchpad&lt;/span&gt;&lt;span class='COMMENT4'&gt; &lt;/span&gt;&lt;span class='COMMENT3'&gt;)&lt;/span&gt; FUNCTION: int getuid &lt;span class='COMMENT3'&gt;( &lt;/span&gt;&lt;span class='COMMENT3'&gt;)&lt;/span&gt; &lt;span class='MARKUP'&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class='COMMENT3'&gt;( &lt;/span&gt;&lt;span class='COMMENT4'&gt;scratchpad&lt;/span&gt;&lt;span class='COMMENT4'&gt; &lt;/span&gt;&lt;span class='COMMENT3'&gt;)&lt;/span&gt; getuid .&lt;br /&gt;&lt;span class='DIGIT'&gt;0&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt; At about the same time, &lt;a href='http://bluishcoder.co.nz'&gt;Chris Double&lt;/a&gt; wrote a SQLite binding and a high-level library he called tuple-db.  It had some good ideas, such as query-by-example and prepared SQL statements for better security. &lt;/p&gt;&lt;p&gt;Earlier this year, I took both of these libraries and merged them to come up with the the current database library.  The supported backends are PostgreSQL and SQLite and there are protocols for implementing other backends with relative ease.  Contributions are welcome. &lt;/p&gt;&lt;h2&gt; Overview &lt;/h2&gt;  &lt;p&gt;Briefly, Factor tuples correspond one-to-one to tables in the database through a mapping defined with the &lt;code&gt;define-persistent&lt;/code&gt; word.  Tuples are then manipulated through insert, update, delete, and select words.  Upon calling the insert word, all of the filled-in slots in a tuple will be saved to the database.  The process is reversible, and by filling in slots for a tuple and calling &lt;code&gt;select-tuples&lt;/code&gt;, the libary generates a select statement from the filled-in slots (query-by-example) and returns tuples in a sequence.  More advanced queries and lower level raw SQL statements are possible as well. &lt;/p&gt;&lt;p&gt;First, we'll look at how to connect to a database. &lt;/p&gt;&lt;h2&gt; Connecting to a database &lt;/h2&gt;  &lt;p&gt;Every database has its own setup which Factor encapsultes in a tuple. SQLite just needs a path to a file on disk, but since PostgreSQL has a server/client model, the setup is more complex. After making your database tuple, connecting works the same for any database by using the &lt;code&gt;with-db&lt;/code&gt; word. This word takes a quotation (a block of code) and your tuple object, then calls the database open routine and your quotation. After running your quotation, the database is closed, even if your code throws an exception. Having an interface like this means you can simply swap out your SQLite database for a networked PostgreSQL one if you suddenly need more scalability or networked database access. &lt;/p&gt;&lt;p&gt;You should generally make a custom combinator with your project's connection information. Here are a couple of examples. &lt;/p&gt;&lt;p&gt;SQLite example combinator: &lt;pre&gt;&lt;span class='MARKUP'&gt;USING: &lt;/span&gt;&lt;span class='LITERAL2'&gt;db.sqlite&lt;/span&gt;&lt;span class='LITERAL2'&gt; &lt;/span&gt;&lt;span class='LITERAL2'&gt;db&lt;/span&gt;&lt;span class='LITERAL2'&gt; &lt;/span&gt;&lt;span class='LITERAL2'&gt;io.files&lt;/span&gt;&lt;span class='LITERAL2'&gt; &lt;/span&gt;&lt;span class='MARKUP'&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class='MARKUP'&gt;: with-sqlite-db&lt;/span&gt; &lt;span class='COMMENT3'&gt;( &lt;/span&gt;&lt;span class='COMMENT4'&gt;quot&lt;/span&gt;&lt;span class='COMMENT4'&gt; &lt;/span&gt;&lt;span class='COMMENT3'&gt;--&lt;/span&gt;&lt;span class='COMMENT4'&gt; &lt;/span&gt;&lt;span class='COMMENT3'&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class='LITERAL1'&gt;"&lt;/span&gt;&lt;span class='LITERAL1'&gt;my-database.db&lt;/span&gt;&lt;span class='LITERAL1'&gt;"&lt;/span&gt; temp-file &amp;lt;sqlite-db&amp;gt; swap with-db &lt;span class='MARKUP'&gt;;&lt;/span&gt; inline&lt;br /&gt;&lt;/pre&gt; &lt;/p&gt;&lt;p&gt;PostgreSQL example combinator:&lt;br /&gt;&lt;pre&gt;&lt;span class='MARKUP'&gt;USING: &lt;/span&gt;&lt;span class='LITERAL2'&gt;db.postgresql&lt;/span&gt;&lt;span class='LITERAL2'&gt; &lt;/span&gt;&lt;span class='LITERAL2'&gt;db&lt;/span&gt;&lt;span class='LITERAL2'&gt; &lt;/span&gt;&lt;span class='MARKUP'&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class='MARKUP'&gt;: with-postgresql-db&lt;/span&gt; &lt;span class='COMMENT3'&gt;( &lt;/span&gt;&lt;span class='COMMENT4'&gt;quot&lt;/span&gt;&lt;span class='COMMENT4'&gt; &lt;/span&gt;&lt;span class='COMMENT3'&gt;--&lt;/span&gt;&lt;span class='COMMENT4'&gt; &lt;/span&gt;&lt;span class='COMMENT3'&gt;)&lt;/span&gt;&lt;br /&gt;    &amp;lt;postgresql-db&amp;gt;&lt;br /&gt;        &lt;span class='LITERAL1'&gt;"&lt;/span&gt;&lt;span class='LITERAL1'&gt;localhost&lt;/span&gt;&lt;span class='LITERAL1'&gt;"&lt;/span&gt; &amp;gt;&amp;gt;host&lt;br /&gt;        &lt;span class='DIGIT'&gt;5432&lt;/span&gt; &amp;gt;&amp;gt;port&lt;br /&gt;        &lt;span class='LITERAL1'&gt;"&lt;/span&gt;&lt;span class='LITERAL1'&gt;user&lt;/span&gt;&lt;span class='LITERAL1'&gt;"&lt;/span&gt; &amp;gt;&amp;gt;username&lt;br /&gt;        &lt;span class='LITERAL1'&gt;"&lt;/span&gt;&lt;span class='LITERAL1'&gt;seeecrets?&lt;/span&gt;&lt;span class='LITERAL1'&gt;"&lt;/span&gt; &amp;gt;&amp;gt;password&lt;br /&gt;        &lt;span class='LITERAL1'&gt;"&lt;/span&gt;&lt;span class='LITERAL1'&gt;factor-test&lt;/span&gt;&lt;span class='LITERAL1'&gt;"&lt;/span&gt; &amp;gt;&amp;gt;database&lt;br /&gt;    swap with-db &lt;span class='MARKUP'&gt;;&lt;/span&gt; inline&lt;br /&gt;&lt;/pre&gt;  To make sure your database connection works, you can test it with an empty quotation:&lt;br /&gt;&lt;pre&gt; &lt;span class='OPERATOR'&gt;[&lt;/span&gt; &lt;span class='OPERATOR'&gt;]&lt;/span&gt; with-postgresql-db &lt;br /&gt;&lt;/pre&gt; &lt;/p&gt;&lt;p&gt;If that line of code doesn't throw an error, you can safely assume connecting to the database worked. &lt;/p&gt;&lt;h2&gt; Defining persistent tuples &lt;/h2&gt; &lt;p&gt;The highest-level database abstraction Factor offers relates tuples directly to database tables. Tuples must map one-to-one to a database table, but foreign keys to other tables are allowed. &lt;/p&gt;&lt;h3&gt; Primary keys &lt;/h3&gt;  &lt;p&gt;Each tuple needs a primary key for indexing by the database.  The primary key can be an increasing integer assigned by the database (a +db-assigned-id+), an object assigned by the user (a +user-defined-id+), a random number, or even a compound key consisting of multiple values used together as a key. &lt;/p&gt;&lt;p&gt;Database defined primary keys are automatically set on the tuple after insertion.  While SQLite makes this feature easy to implement with the &lt;code&gt;sqlite3_last_insert_rowid&lt;/code&gt; library call, PostgreSQL lacks such a feature and instead, in the backend, the inserts are done through a SQL function that queries the most recently inserted row. &lt;/p&gt;&lt;p&gt;User-assigned ids can be anything that the programmer knows will be unique for each object in the table.  The same is true for compound keys, but only one of the values has to differ in this case for it to be unique. &lt;/p&gt;&lt;p&gt;Sometimes, a randomly generated id is useful, and the database library makes it easy to associate a tuple with its random key.  By default, it generates a 64-bit random integer and in the unlikely event that it collides with an existing entry, it tries again up to ten times.  The random integer is then set in the primary key slot of the tuple. &lt;/p&gt;&lt;h3&gt; Database types &lt;/h3&gt;  &lt;p&gt;Data should have a type to allow the database to optimize its queries and storage.  The supported data types are booleans, varchars, text, integers, big-integers, doubles, reals, timestamps, byte-arrays, URLs, and Factor blobs, which store arbitrary Factor objects.  Types can have default values, unique qualifiers, and not-null restrictions.  The database framework does all of the marshalling of objects for you transparently. &lt;/p&gt;&lt;h2&gt; Creating and dropping tables &lt;/h2&gt;  &lt;p&gt;There are quite a few options when it comes to creating tables.  The most basic is the &lt;code&gt;create-table&lt;/code&gt; word.  The problem is that it throws an exception when the table exists, which happens often.  So one alternative is &lt;code&gt;ensure-table&lt;/code&gt; -- we make sure a table exists and silently ignore errors.  However, if we're just developing an application and we want to make sure the table is always the latest version of the code, we might use &lt;code&gt;recreate-table&lt;/code&gt;, which drops and creates the table without throwing errors, and of course drops all the data with it.  To simply drop a table, use the &lt;code&gt;drop-table&lt;/code&gt; word. &lt;/p&gt;&lt;h2&gt; Inserting tuples &lt;/h2&gt;  &lt;p&gt;Tuples are inserted one at a time with the &lt;code&gt;insert-tuple&lt;/code&gt; word.  SQL insert commands are generated directly from the Factor object and its filled-in slots.  An example will follow. &lt;/p&gt;&lt;h2&gt; Selecting tuples &lt;/h2&gt;  &lt;p&gt;Select statements are generated from exemplar tuples, or tuples with certain slots filled in with any value besides &lt;code&gt;f&lt;/code&gt;.  Passing an empty exemplar tuple will select all tuples from the table and return them as a sequence.  A useful feature we support is querying by ranges, sequences, or intervals, as shown in following demonstration.&lt;br /&gt;&lt;/p&gt;&lt;h2&gt; Exams demo &lt;/h2&gt;  &lt;p&gt;About the simplest example of interest is one that matches students names with their grades on a particular exam.  Here's aa exam tuple, its mapping to the database, and a utility word to generate random exam objects.&lt;br /&gt;&lt;/p&gt;&lt;pre&gt;&lt;span class='MARKUP'&gt;USING: &lt;/span&gt;&lt;span class='LITERAL2'&gt;math.ranges&lt;/span&gt;&lt;span class='LITERAL2'&gt; &lt;/span&gt;&lt;span class='LITERAL2'&gt;random&lt;/span&gt;&lt;span class='LITERAL2'&gt; &lt;/span&gt;&lt;span class='LITERAL2'&gt;db.types&lt;/span&gt;&lt;span class='LITERAL2'&gt; &lt;/span&gt;&lt;span class='MARKUP'&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;TUPLE: exam id name score &lt;span class='MARKUP'&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;exam &lt;span class='LITERAL1'&gt;"&lt;/span&gt;&lt;span class='LITERAL1'&gt;EXAM&lt;/span&gt;&lt;span class='LITERAL1'&gt;"&lt;/span&gt;    &lt;span class='OPERATOR'&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class='OPERATOR'&gt;{&lt;/span&gt; &lt;span class='LITERAL1'&gt;"&lt;/span&gt;&lt;span class='LITERAL1'&gt;id&lt;/span&gt;&lt;span class='LITERAL1'&gt;"&lt;/span&gt; &lt;span class='LITERAL1'&gt;"&lt;/span&gt;&lt;span class='LITERAL1'&gt;ID&lt;/span&gt;&lt;span class='LITERAL1'&gt;"&lt;/span&gt; +db-assigned-id+ &lt;span class='OPERATOR'&gt;}&lt;/span&gt;&lt;br /&gt;    &lt;span class='OPERATOR'&gt;{&lt;/span&gt; &lt;span class='LITERAL1'&gt;"&lt;/span&gt;&lt;span class='LITERAL1'&gt;name&lt;/span&gt;&lt;span class='LITERAL1'&gt;"&lt;/span&gt; &lt;span class='LITERAL1'&gt;"&lt;/span&gt;&lt;span class='LITERAL1'&gt;NAME&lt;/span&gt;&lt;span class='LITERAL1'&gt;"&lt;/span&gt; TEXT &lt;span class='OPERATOR'&gt;}&lt;/span&gt;&lt;br /&gt;    &lt;span class='OPERATOR'&gt;{&lt;/span&gt; &lt;span class='LITERAL1'&gt;"&lt;/span&gt;&lt;span class='LITERAL1'&gt;score&lt;/span&gt;&lt;span class='LITERAL1'&gt;"&lt;/span&gt; &lt;span class='LITERAL1'&gt;"&lt;/span&gt;&lt;span class='LITERAL1'&gt;SCORE&lt;/span&gt;&lt;span class='LITERAL1'&gt;"&lt;/span&gt; INTEGER &lt;span class='OPERATOR'&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class='OPERATOR'&gt;}&lt;/span&gt; define-persistent&lt;br /&gt;&lt;br /&gt;&lt;span class='MARKUP'&gt;: random-exam&lt;/span&gt; &lt;span class='COMMENT3'&gt;( &lt;/span&gt;&lt;span class='COMMENT3'&gt;--&lt;/span&gt;&lt;span class='COMMENT4'&gt; &lt;/span&gt;&lt;span class='COMMENT4'&gt;exam&lt;/span&gt;&lt;span class='COMMENT4'&gt; &lt;/span&gt;&lt;span class='COMMENT3'&gt;)&lt;/span&gt;&lt;br /&gt;        &lt;span class='LITERAL4'&gt;f&lt;/span&gt;&lt;br /&gt;        &lt;span class='DIGIT'&gt;6&lt;/span&gt; &lt;span class='OPERATOR'&gt;[&lt;/span&gt; &lt;span class='LITERAL2'&gt;CHAR: a&lt;/span&gt; &lt;span class='LITERAL2'&gt;CHAR: z&lt;/span&gt; &lt;span class='OPERATOR'&gt;[&lt;/span&gt;a,b&lt;span class='OPERATOR'&gt;]&lt;/span&gt; random &lt;span class='OPERATOR'&gt;]&lt;/span&gt; replicate &amp;gt;string&lt;br /&gt;        &lt;span class='DIGIT'&gt;100&lt;/span&gt; &lt;span class='OPERATOR'&gt;[&lt;/span&gt;0,b&lt;span class='OPERATOR'&gt;]&lt;/span&gt; random&lt;br /&gt;    exam boa &lt;span class='MARKUP'&gt;;&lt;/span&gt; &lt;br /&gt;&lt;/pre&gt; &lt;p&gt;     I'll use a trick for opening a database to use it interactively without putting a &lt;code&gt;with-db&lt;/code&gt; wrapper around every call. &lt;/p&gt;&lt;pre&gt;&lt;span class='LITERAL1'&gt;"&lt;/span&gt;&lt;span class='LITERAL1'&gt;my-database.db&lt;/span&gt;&lt;span class='LITERAL1'&gt;"&lt;/span&gt; temp-file &amp;lt;sqlite-db&amp;gt; db-open db set&lt;br /&gt;&lt;/pre&gt;  &lt;p&gt;Note that the database handle should be cleaned up with &lt;code&gt;db get dispose&lt;/code&gt; if you don't want to leak memory. &lt;/p&gt;&lt;p&gt;Let's create the table and add some random exams to the database: &lt;/p&gt;&lt;pre&gt;exam create-table&lt;br /&gt;&lt;br /&gt;&lt;span class='DIGIT'&gt;25&lt;/span&gt; &lt;span class='OPERATOR'&gt;[&lt;/span&gt; random-exam insert-tuple &lt;span class='OPERATOR'&gt;]&lt;/span&gt; times&lt;br /&gt;&lt;/pre&gt;  &lt;p&gt;Now to demonstrate selects.  You can select all the exams: &lt;pre&gt;T&lt;span class='OPERATOR'&gt;{&lt;/span&gt; exam &lt;span class='OPERATOR'&gt;}&lt;/span&gt; select-tuples .&lt;br /&gt;&lt;span class='OPERATOR'&gt;{&lt;/span&gt;&lt;br /&gt;    T&lt;span class='OPERATOR'&gt;{&lt;/span&gt; exam &lt;span class='OPERATOR'&gt;{&lt;/span&gt; id &lt;span class='DIGIT'&gt;1&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt; &lt;span class='OPERATOR'&gt;{&lt;/span&gt; name &lt;span class='LITERAL1'&gt;"&lt;/span&gt;&lt;span class='LITERAL1'&gt;qklkzk&lt;/span&gt;&lt;span class='LITERAL1'&gt;"&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt; &lt;span class='OPERATOR'&gt;{&lt;/span&gt; score &lt;span class='DIGIT'&gt;38&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt;&lt;br /&gt;    T&lt;span class='OPERATOR'&gt;{&lt;/span&gt; exam &lt;span class='OPERATOR'&gt;{&lt;/span&gt; id &lt;span class='DIGIT'&gt;2&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt; &lt;span class='OPERATOR'&gt;{&lt;/span&gt; name &lt;span class='LITERAL1'&gt;"&lt;/span&gt;&lt;span class='LITERAL1'&gt;feeuwv&lt;/span&gt;&lt;span class='LITERAL1'&gt;"&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt; &lt;span class='OPERATOR'&gt;{&lt;/span&gt; score &lt;span class='DIGIT'&gt;38&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt;&lt;br /&gt;    T&lt;span class='OPERATOR'&gt;{&lt;/span&gt; exam &lt;span class='OPERATOR'&gt;{&lt;/span&gt; id &lt;span class='DIGIT'&gt;3&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt; &lt;span class='OPERATOR'&gt;{&lt;/span&gt; name &lt;span class='LITERAL1'&gt;"&lt;/span&gt;&lt;span class='LITERAL1'&gt;hlzwuu&lt;/span&gt;&lt;span class='LITERAL1'&gt;"&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt; &lt;span class='OPERATOR'&gt;{&lt;/span&gt; score &lt;span class='DIGIT'&gt;51&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt;&lt;br /&gt;    T&lt;span class='OPERATOR'&gt;{&lt;/span&gt; exam &lt;span class='OPERATOR'&gt;{&lt;/span&gt; id &lt;span class='DIGIT'&gt;4&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt; &lt;span class='OPERATOR'&gt;{&lt;/span&gt; name &lt;span class='LITERAL1'&gt;"&lt;/span&gt;&lt;span class='LITERAL1'&gt;liiptp&lt;/span&gt;&lt;span class='LITERAL1'&gt;"&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt; &lt;span class='OPERATOR'&gt;{&lt;/span&gt; score &lt;span class='DIGIT'&gt;52&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt;&lt;br /&gt;    T&lt;span class='OPERATOR'&gt;{&lt;/span&gt; exam &lt;span class='OPERATOR'&gt;{&lt;/span&gt; id &lt;span class='DIGIT'&gt;5&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt; &lt;span class='OPERATOR'&gt;{&lt;/span&gt; name &lt;span class='LITERAL1'&gt;"&lt;/span&gt;&lt;span class='LITERAL1'&gt;mwzlmv&lt;/span&gt;&lt;span class='LITERAL1'&gt;"&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt; &lt;span class='OPERATOR'&gt;{&lt;/span&gt; score &lt;span class='DIGIT'&gt;74&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt;&lt;br /&gt;    ...&lt;br /&gt;&lt;span class='OPERATOR'&gt;}&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt; &lt;/p&gt;&lt;p&gt;Ranges can be used with any datatype that makes sense, like integers or timestamps. &lt;/p&gt;&lt;p&gt;A range of passing exams: &lt;pre&gt;T&lt;span class='OPERATOR'&gt;{&lt;/span&gt; exam &lt;span class='OPERATOR'&gt;}&lt;/span&gt; &lt;span class='DIGIT'&gt;70&lt;/span&gt; &lt;span class='DIGIT'&gt;100&lt;/span&gt; &lt;span class='OPERATOR'&gt;[&lt;/span&gt;a,b&lt;span class='OPERATOR'&gt;]&lt;/span&gt; &amp;gt;&amp;gt;score select-tuples .&lt;br /&gt;&lt;span class='OPERATOR'&gt;{&lt;/span&gt;&lt;br /&gt;    T&lt;span class='OPERATOR'&gt;{&lt;/span&gt; exam &lt;span class='OPERATOR'&gt;{&lt;/span&gt; id &lt;span class='DIGIT'&gt;5&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt; &lt;span class='OPERATOR'&gt;{&lt;/span&gt; name &lt;span class='LITERAL1'&gt;"&lt;/span&gt;&lt;span class='LITERAL1'&gt;mwzlmv&lt;/span&gt;&lt;span class='LITERAL1'&gt;"&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt; &lt;span class='OPERATOR'&gt;{&lt;/span&gt; score &lt;span class='DIGIT'&gt;74&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt;&lt;br /&gt;    T&lt;span class='OPERATOR'&gt;{&lt;/span&gt; exam &lt;span class='OPERATOR'&gt;{&lt;/span&gt; id &lt;span class='DIGIT'&gt;6&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt; &lt;span class='OPERATOR'&gt;{&lt;/span&gt; name &lt;span class='LITERAL1'&gt;"&lt;/span&gt;&lt;span class='LITERAL1'&gt;ftxhuw&lt;/span&gt;&lt;span class='LITERAL1'&gt;"&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt; &lt;span class='OPERATOR'&gt;{&lt;/span&gt; score &lt;span class='DIGIT'&gt;90&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt;&lt;br /&gt;    T&lt;span class='OPERATOR'&gt;{&lt;/span&gt; exam &lt;span class='OPERATOR'&gt;{&lt;/span&gt; id &lt;span class='DIGIT'&gt;11&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt; &lt;span class='OPERATOR'&gt;{&lt;/span&gt; name &lt;span class='LITERAL1'&gt;"&lt;/span&gt;&lt;span class='LITERAL1'&gt;rorbyd&lt;/span&gt;&lt;span class='LITERAL1'&gt;"&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt; &lt;span class='OPERATOR'&gt;{&lt;/span&gt; score &lt;span class='DIGIT'&gt;83&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt;&lt;br /&gt;    T&lt;span class='OPERATOR'&gt;{&lt;/span&gt; exam &lt;span class='OPERATOR'&gt;{&lt;/span&gt; id &lt;span class='DIGIT'&gt;16&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt; &lt;span class='OPERATOR'&gt;{&lt;/span&gt; name &lt;span class='LITERAL1'&gt;"&lt;/span&gt;&lt;span class='LITERAL1'&gt;ttvkar&lt;/span&gt;&lt;span class='LITERAL1'&gt;"&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt; &lt;span class='OPERATOR'&gt;{&lt;/span&gt; score &lt;span class='DIGIT'&gt;78&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt;&lt;br /&gt;    T&lt;span class='OPERATOR'&gt;{&lt;/span&gt; exam &lt;span class='OPERATOR'&gt;{&lt;/span&gt; id &lt;span class='DIGIT'&gt;17&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt; &lt;span class='OPERATOR'&gt;{&lt;/span&gt; name &lt;span class='LITERAL1'&gt;"&lt;/span&gt;&lt;span class='LITERAL1'&gt;nnzkvs&lt;/span&gt;&lt;span class='LITERAL1'&gt;"&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt; &lt;span class='OPERATOR'&gt;{&lt;/span&gt; score &lt;span class='DIGIT'&gt;99&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt;&lt;br /&gt;    T&lt;span class='OPERATOR'&gt;{&lt;/span&gt; exam &lt;span class='OPERATOR'&gt;{&lt;/span&gt; id &lt;span class='DIGIT'&gt;21&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt; &lt;span class='OPERATOR'&gt;{&lt;/span&gt; name &lt;span class='LITERAL1'&gt;"&lt;/span&gt;&lt;span class='LITERAL1'&gt;izoiqi&lt;/span&gt;&lt;span class='LITERAL1'&gt;"&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt; &lt;span class='OPERATOR'&gt;{&lt;/span&gt; score &lt;span class='DIGIT'&gt;83&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class='OPERATOR'&gt;}&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt; &lt;/p&gt;&lt;p&gt;You can search for specific grades by using a sequence: &lt;pre&gt;T&lt;span class='OPERATOR'&gt;{&lt;/span&gt; exam &lt;span class='OPERATOR'&gt;}&lt;/span&gt; &lt;span class='OPERATOR'&gt;{&lt;/span&gt; &lt;span class='DIGIT'&gt;10&lt;/span&gt; &lt;span class='DIGIT'&gt;20&lt;/span&gt; &lt;span class='DIGIT'&gt;30&lt;/span&gt; &lt;span class='DIGIT'&gt;40&lt;/span&gt; &lt;span class='DIGIT'&gt;50&lt;/span&gt; &lt;span class='DIGIT'&gt;60&lt;/span&gt; &lt;span class='DIGIT'&gt;70&lt;/span&gt; &lt;span class='DIGIT'&gt;80&lt;/span&gt; &lt;span class='DIGIT'&gt;90&lt;/span&gt; &lt;span class='DIGIT'&gt;100&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt; &amp;gt;&amp;gt;score select-tuples .&lt;br /&gt;&lt;span class='OPERATOR'&gt;{&lt;/span&gt;&lt;br /&gt;    T&lt;span class='OPERATOR'&gt;{&lt;/span&gt; exam &lt;span class='OPERATOR'&gt;{&lt;/span&gt; id &lt;span class='DIGIT'&gt;6&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt; &lt;span class='OPERATOR'&gt;{&lt;/span&gt; name &lt;span class='LITERAL1'&gt;"&lt;/span&gt;&lt;span class='LITERAL1'&gt;ftxhuw&lt;/span&gt;&lt;span class='LITERAL1'&gt;"&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt; &lt;span class='OPERATOR'&gt;{&lt;/span&gt; score &lt;span class='DIGIT'&gt;90&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt;&lt;br /&gt;    T&lt;span class='OPERATOR'&gt;{&lt;/span&gt; exam &lt;span class='OPERATOR'&gt;{&lt;/span&gt; id &lt;span class='DIGIT'&gt;9&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt; &lt;span class='OPERATOR'&gt;{&lt;/span&gt; name &lt;span class='LITERAL1'&gt;"&lt;/span&gt;&lt;span class='LITERAL1'&gt;bdoztd&lt;/span&gt;&lt;span class='LITERAL1'&gt;"&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt; &lt;span class='OPERATOR'&gt;{&lt;/span&gt; score &lt;span class='DIGIT'&gt;30&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt;&lt;br /&gt;    T&lt;span class='OPERATOR'&gt;{&lt;/span&gt; exam &lt;span class='OPERATOR'&gt;{&lt;/span&gt; id &lt;span class='DIGIT'&gt;20&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt; &lt;span class='OPERATOR'&gt;{&lt;/span&gt; name &lt;span class='LITERAL1'&gt;"&lt;/span&gt;&lt;span class='LITERAL1'&gt;lnmxfq&lt;/span&gt;&lt;span class='LITERAL1'&gt;"&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt; &lt;span class='OPERATOR'&gt;{&lt;/span&gt; score &lt;span class='DIGIT'&gt;60&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class='OPERATOR'&gt;}&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt; &lt;/p&gt;&lt;p&gt;An interval query.  Nobody should have above a 100: &lt;pre&gt;&lt;span class='MARKUP'&gt;USE: math.intervals&lt;/span&gt; T&lt;span class='OPERATOR'&gt;{&lt;/span&gt; exam &lt;span class='OPERATOR'&gt;}&lt;/span&gt; &lt;span class='DIGIT'&gt;100&lt;/span&gt; 1/0. (a,b) &amp;gt;&amp;gt;score select-tuples .&lt;br /&gt;&lt;span class='OPERATOR'&gt;{&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt; &lt;/p&gt;&lt;p&gt;Let's order by the grade: &lt;pre&gt;&amp;lt;query&amp;gt; T&lt;span class='OPERATOR'&gt;{&lt;/span&gt; exam &lt;span class='OPERATOR'&gt;}&lt;/span&gt; &amp;gt;&amp;gt;tuple &lt;span class='LITERAL1'&gt;"&lt;/span&gt;&lt;span class='LITERAL1'&gt;score&lt;/span&gt;&lt;span class='LITERAL1'&gt;"&lt;/span&gt; &amp;gt;&amp;gt;order select-tuples .&lt;br /&gt;...&lt;br /&gt;    T&lt;span class='OPERATOR'&gt;{&lt;/span&gt; exam &lt;span class='OPERATOR'&gt;{&lt;/span&gt; id &lt;span class='DIGIT'&gt;21&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt; &lt;span class='OPERATOR'&gt;{&lt;/span&gt; name &lt;span class='LITERAL1'&gt;"&lt;/span&gt;&lt;span class='LITERAL1'&gt;izoiqi&lt;/span&gt;&lt;span class='LITERAL1'&gt;"&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt; &lt;span class='OPERATOR'&gt;{&lt;/span&gt; score &lt;span class='DIGIT'&gt;83&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt;&lt;br /&gt;    T&lt;span class='OPERATOR'&gt;{&lt;/span&gt; exam &lt;span class='OPERATOR'&gt;{&lt;/span&gt; id &lt;span class='DIGIT'&gt;6&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt; &lt;span class='OPERATOR'&gt;{&lt;/span&gt; name &lt;span class='LITERAL1'&gt;"&lt;/span&gt;&lt;span class='LITERAL1'&gt;ftxhuw&lt;/span&gt;&lt;span class='LITERAL1'&gt;"&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt; &lt;span class='OPERATOR'&gt;{&lt;/span&gt; score &lt;span class='DIGIT'&gt;90&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt;&lt;br /&gt;    T&lt;span class='OPERATOR'&gt;{&lt;/span&gt; exam &lt;span class='OPERATOR'&gt;{&lt;/span&gt; id &lt;span class='DIGIT'&gt;17&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt; &lt;span class='OPERATOR'&gt;{&lt;/span&gt; name &lt;span class='LITERAL1'&gt;"&lt;/span&gt;&lt;span class='LITERAL1'&gt;nnzkvs&lt;/span&gt;&lt;span class='LITERAL1'&gt;"&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt; &lt;span class='OPERATOR'&gt;{&lt;/span&gt; score &lt;span class='DIGIT'&gt;99&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class='OPERATOR'&gt;}&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt; &lt;/p&gt;&lt;p&gt;Or by (random-generated) name: &lt;pre&gt;&amp;lt;query&amp;gt; T&lt;span class='OPERATOR'&gt;{&lt;/span&gt; exam &lt;span class='OPERATOR'&gt;}&lt;/span&gt; &amp;gt;&amp;gt;tuple &lt;span class='LITERAL1'&gt;"&lt;/span&gt;&lt;span class='LITERAL1'&gt;name&lt;/span&gt;&lt;span class='LITERAL1'&gt;"&lt;/span&gt; &amp;gt;&amp;gt;order select-tuples .&lt;br /&gt;&lt;span class='OPERATOR'&gt;{&lt;/span&gt;&lt;br /&gt;    T&lt;span class='OPERATOR'&gt;{&lt;/span&gt; exam &lt;span class='OPERATOR'&gt;{&lt;/span&gt; id &lt;span class='DIGIT'&gt;13&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt; &lt;span class='OPERATOR'&gt;{&lt;/span&gt; name &lt;span class='LITERAL1'&gt;"&lt;/span&gt;&lt;span class='LITERAL1'&gt;aagnof&lt;/span&gt;&lt;span class='LITERAL1'&gt;"&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt; &lt;span class='OPERATOR'&gt;{&lt;/span&gt; score &lt;span class='DIGIT'&gt;61&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt;&lt;br /&gt;    T&lt;span class='OPERATOR'&gt;{&lt;/span&gt; exam &lt;span class='OPERATOR'&gt;{&lt;/span&gt; id &lt;span class='DIGIT'&gt;36&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt; &lt;span class='OPERATOR'&gt;{&lt;/span&gt; name &lt;span class='LITERAL1'&gt;"&lt;/span&gt;&lt;span class='LITERAL1'&gt;ailftz&lt;/span&gt;&lt;span class='LITERAL1'&gt;"&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt; &lt;span class='OPERATOR'&gt;{&lt;/span&gt; score &lt;span class='DIGIT'&gt;41&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt;&lt;br /&gt;    T&lt;span class='OPERATOR'&gt;{&lt;/span&gt; exam &lt;span class='OPERATOR'&gt;{&lt;/span&gt; id &lt;span class='DIGIT'&gt;9&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt; &lt;span class='OPERATOR'&gt;{&lt;/span&gt; name &lt;span class='LITERAL1'&gt;"&lt;/span&gt;&lt;span class='LITERAL1'&gt;bdoztd&lt;/span&gt;&lt;span class='LITERAL1'&gt;"&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt; &lt;span class='OPERATOR'&gt;{&lt;/span&gt; score &lt;span class='DIGIT'&gt;30&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt;&lt;br /&gt;    ...&lt;br /&gt;&lt;/pre&gt; &lt;/p&gt;&lt;p&gt;Updates and deletes are also by example and may use sequences and intervals in the 'where' clause in the same way as the selects with the &lt;code&gt;update-tuples&lt;/code&gt; and &lt;code&gt;delete-tuples&lt;/code&gt; words.&lt;br /&gt;&lt;/p&gt;&lt;h2&gt; Raw SQL &lt;/h2&gt;  &lt;p&gt;You can drop down into SQL if you want to do anything complex or things that are not supported by the library.  A drawback is that the SQL you write may not work across SQL databases.  Notice that, for a select, you have to convert the data to Factor tuples yourself since the data is returned as an array of strings. &lt;/p&gt;&lt;pre&gt;&lt;span class='LITERAL1'&gt;"&lt;/span&gt;&lt;span class='LITERAL1'&gt;select&lt;/span&gt;&lt;span class='LITERAL1'&gt; &lt;/span&gt;&lt;span class='LITERAL1'&gt;*&lt;/span&gt;&lt;span class='LITERAL1'&gt; &lt;/span&gt;&lt;span class='LITERAL1'&gt;from&lt;/span&gt;&lt;span class='LITERAL1'&gt; &lt;/span&gt;&lt;span class='LITERAL1'&gt;exam&lt;/span&gt;&lt;span class='LITERAL1'&gt; &lt;/span&gt;&lt;span class='LITERAL1'&gt;where&lt;/span&gt;&lt;span class='LITERAL1'&gt; &lt;/span&gt;&lt;span class='LITERAL1'&gt;name&lt;/span&gt;&lt;span class='LITERAL1'&gt; &lt;/span&gt;&lt;span class='LITERAL1'&gt;like&lt;/span&gt;&lt;span class='LITERAL1'&gt; &lt;/span&gt;&lt;span class='LITERAL1'&gt;'%a%'&lt;/span&gt;&lt;span class='LITERAL1'&gt;"&lt;/span&gt; sql-query .&lt;br /&gt;&lt;span class='OPERATOR'&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class='OPERATOR'&gt;{&lt;/span&gt; &lt;span class='LITERAL1'&gt;"&lt;/span&gt;&lt;span class='LITERAL1'&gt;8&lt;/span&gt;&lt;span class='LITERAL1'&gt;"&lt;/span&gt; &lt;span class='LITERAL1'&gt;"&lt;/span&gt;&lt;span class='LITERAL1'&gt;rxoyga&lt;/span&gt;&lt;span class='LITERAL1'&gt;"&lt;/span&gt; &lt;span class='LITERAL1'&gt;"&lt;/span&gt;&lt;span class='LITERAL1'&gt;53&lt;/span&gt;&lt;span class='LITERAL1'&gt;"&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt;&lt;br /&gt;    &lt;span class='OPERATOR'&gt;{&lt;/span&gt; &lt;span class='LITERAL1'&gt;"&lt;/span&gt;&lt;span class='LITERAL1'&gt;10&lt;/span&gt;&lt;span class='LITERAL1'&gt;"&lt;/span&gt; &lt;span class='LITERAL1'&gt;"&lt;/span&gt;&lt;span class='LITERAL1'&gt;fchhha&lt;/span&gt;&lt;span class='LITERAL1'&gt;"&lt;/span&gt; &lt;span class='LITERAL1'&gt;"&lt;/span&gt;&lt;span class='LITERAL1'&gt;1&lt;/span&gt;&lt;span class='LITERAL1'&gt;"&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt;&lt;br /&gt;    &lt;span class='OPERATOR'&gt;{&lt;/span&gt; &lt;span class='LITERAL1'&gt;"&lt;/span&gt;&lt;span class='LITERAL1'&gt;13&lt;/span&gt;&lt;span class='LITERAL1'&gt;"&lt;/span&gt; &lt;span class='LITERAL1'&gt;"&lt;/span&gt;&lt;span class='LITERAL1'&gt;aagnof&lt;/span&gt;&lt;span class='LITERAL1'&gt;"&lt;/span&gt; &lt;span class='LITERAL1'&gt;"&lt;/span&gt;&lt;span class='LITERAL1'&gt;61&lt;/span&gt;&lt;span class='LITERAL1'&gt;"&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt;&lt;br /&gt;    &lt;span class='OPERATOR'&gt;{&lt;/span&gt; &lt;span class='LITERAL1'&gt;"&lt;/span&gt;&lt;span class='LITERAL1'&gt;16&lt;/span&gt;&lt;span class='LITERAL1'&gt;"&lt;/span&gt; &lt;span class='LITERAL1'&gt;"&lt;/span&gt;&lt;span class='LITERAL1'&gt;ttvkar&lt;/span&gt;&lt;span class='LITERAL1'&gt;"&lt;/span&gt; &lt;span class='LITERAL1'&gt;"&lt;/span&gt;&lt;span class='LITERAL1'&gt;78&lt;/span&gt;&lt;span class='LITERAL1'&gt;"&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt;&lt;br /&gt;    &lt;span class='OPERATOR'&gt;{&lt;/span&gt; &lt;span class='LITERAL1'&gt;"&lt;/span&gt;&lt;span class='LITERAL1'&gt;22&lt;/span&gt;&lt;span class='LITERAL1'&gt;"&lt;/span&gt; &lt;span class='LITERAL1'&gt;"&lt;/span&gt;&lt;span class='LITERAL1'&gt;yhqkav&lt;/span&gt;&lt;span class='LITERAL1'&gt;"&lt;/span&gt; &lt;span class='LITERAL1'&gt;"&lt;/span&gt;&lt;span class='LITERAL1'&gt;28&lt;/span&gt;&lt;span class='LITERAL1'&gt;"&lt;/span&gt; &lt;span class='OPERATOR'&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class='OPERATOR'&gt;}&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;  &lt;p&gt;Raw SQL that does not return results is possible too:&lt;br /&gt;&lt;pre&gt;&lt;span class='LITERAL1'&gt;"&lt;/span&gt;&lt;span class='LITERAL1'&gt;delete&lt;/span&gt;&lt;span class='LITERAL1'&gt; &lt;/span&gt;&lt;span class='LITERAL1'&gt;from&lt;/span&gt;&lt;span class='LITERAL1'&gt; &lt;/span&gt;&lt;span class='LITERAL1'&gt;exam&lt;/span&gt;&lt;span class='LITERAL1'&gt; &lt;/span&gt;&lt;span class='LITERAL1'&gt;where&lt;/span&gt;&lt;span class='LITERAL1'&gt; &lt;/span&gt;&lt;span class='LITERAL1'&gt;name&lt;/span&gt;&lt;span class='LITERAL1'&gt; &lt;/span&gt;&lt;span class='LITERAL1'&gt;like&lt;/span&gt;&lt;span class='LITERAL1'&gt; &lt;/span&gt;&lt;span class='LITERAL1'&gt;'%a%'&lt;/span&gt;&lt;span class='LITERAL1'&gt;"&lt;/span&gt; sql-command&lt;br /&gt;&lt;span class='LITERAL1'&gt;"&lt;/span&gt;&lt;span class='LITERAL1'&gt;select&lt;/span&gt;&lt;span class='LITERAL1'&gt; &lt;/span&gt;&lt;span class='LITERAL1'&gt;*&lt;/span&gt;&lt;span class='LITERAL1'&gt; &lt;/span&gt;&lt;span class='LITERAL1'&gt;from&lt;/span&gt;&lt;span class='LITERAL1'&gt; &lt;/span&gt;&lt;span class='LITERAL1'&gt;exam&lt;/span&gt;&lt;span class='LITERAL1'&gt; &lt;/span&gt;&lt;span class='LITERAL1'&gt;where&lt;/span&gt;&lt;span class='LITERAL1'&gt; &lt;/span&gt;&lt;span class='LITERAL1'&gt;name&lt;/span&gt;&lt;span class='LITERAL1'&gt; &lt;/span&gt;&lt;span class='LITERAL1'&gt;like&lt;/span&gt;&lt;span class='LITERAL1'&gt; &lt;/span&gt;&lt;span class='LITERAL1'&gt;'%a%'&lt;/span&gt;&lt;span class='LITERAL1'&gt;"&lt;/span&gt; sql-query length .&lt;br /&gt;    &lt;span class='DIGIT'&gt;0&lt;/span&gt;&lt;/pre&gt; &lt;/p&gt;&lt;h2&gt; Another tutorial &lt;/h2&gt;  &lt;p&gt;Here is a &lt;a href='http://docs.factorcode.org/content/article-db-tuples-tutorial.html'&gt;tutorial&lt;/a&gt; from the database documentation that shows moref basic usage of the library.  If you run this from within the Factor environment itself, you can click on the code blocks and run them one by one without copy/pasting or typing them in yourself.&lt;br /&gt;&lt;/p&gt;&lt;h2&gt; Real-world usage &lt;/h2&gt;  &lt;p&gt;The &lt;a href='http://www.concatenative.org'&gt;Factor Wiki&lt;/a&gt; uses the database library, as does Factor's web framework, Furnace.  Expect to see more useful websites and applications using it in the future. &lt;/p&gt;&lt;p&gt;Lastly, if anyone can suggest a catchy name for the database library, please let me know. &lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37596622-94350105557918199?l=code-factor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://code-factor.blogspot.com/feeds/94350105557918199/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=37596622&amp;postID=94350105557918199' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/94350105557918199'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/94350105557918199'/><link rel='alternate' type='text/html' href='http://code-factor.blogspot.com/2008/10/introducing-factor-database-library.html' title='Introducing the Factor database library'/><author><name>Doug Coleman</name><uri>http://www.blogger.com/profile/09416611825627835802</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-37596622.post-6537074946584828978</id><published>2008-09-03T18:36:00.001-07:00</published><updated>2008-09-04T10:14:31.654-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='tools'/><category scheme='http://www.blogger.com/atom/ns#' term='documentation'/><category scheme='http://www.blogger.com/atom/ns#' term='Factor'/><title type='text'>Vocabulary and documentation tool: tools.scaffold</title><content type='html'>The Factor project is on a documentation binge until all of the core and basis vocabularies are documented.  I noticed, as anyone tasked to write a bunch of documenation would, that quite a bit of it could be automated.  To begin writing docs for a vocabulary that already exists, you can just run the tool in it and edit the generated help file.&lt;br /&gt;&lt;br /&gt;I actually ended up writing two tools -- one for the docs, and another to create new vocabularies with a simple command.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Scaffold tool to create new vocabularies&lt;/h2&gt;&lt;br /&gt;First, an aside.  Factor's module system is based on a directory layout, with several &lt;i&gt;vocabulary roots&lt;/i&gt; which are stored in the &lt;code&gt;vocab-roots&lt;/code&gt; symbol.&lt;br /&gt;&lt;pre&gt;( scratchpad ) USE: vocabs.loader vocab-roots get .&lt;br /&gt;V{ "resource:core" "resource:basis" "resource:extra" "resource:work" }&lt;/pre&gt;Knowing this, you can now create a new vocabulary and boilerplate empty Factor files from inside of Factor and click the links to edit them in your favorite editor.&lt;pre&gt;( scratchpad ) "extra" "alchemy" scaffold-vocab&lt;br /&gt;Creating scaffolding for P" resource:extra/alchemy/alchemy.factor"&lt;br /&gt;Creating scaffolding for P" resource:extra/alchemy/alchemy-tests.factor"&lt;br /&gt;Creating scaffolding for P" resource:extra/alchemy/authors.txt"&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;( scratchpad ) "extra" "alchemy" scaffold-help&lt;br /&gt;Creating scaffolding for P" resource:extra/alchemy/alchemy-docs.factor"&lt;/pre&gt;The scaffold tool is programmed not to overwrite files if they already exist.&lt;h2&gt;Scaffold tool for documenation&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;Say I have a Factor word that turns lead into gold:&lt;pre&gt;: lead&gt;gold ( lead -- gold ) ... ;&lt;/pre&gt;&lt;br /&gt;The implementation of this word is left as an exercise to the reader.  We don't have to understand how it works to document what it does.&lt;br /&gt;&lt;br /&gt;Without an automation tool, you would begin by creating a new file (extra/alchemy/alchemy-docs.factor) in the same directory as your code file (extra/alchemy/alchemy.factor).  What follows is the standard bolierplate for documentation.  Notice that documenation goes in the same vocabulary as your actual code, but comes from different files on disk.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;! Copyright (C) 2008 Doug Coleman.&lt;br /&gt;! See http://factorcode.org/license.txt for BSD license.&lt;br /&gt;USING: help.markup help.syntax ;&lt;br /&gt;IN: alchemy&lt;/pre&gt;Every doc file has such a header based on the date, who you are, and the vocabulary name.  With those three pieces of information, the above can be auto-generated.  I added a new symbol &lt;code&gt;developer-name&lt;/code&gt; in tools.scaffold that should be set to your name when writing docs.&lt;br /&gt;&lt;br /&gt;Now let's look at the documenation for &lt;code&gt;lead&amp;gt;gold&lt;/code&gt;:&lt;pre&gt;HELP: lead&amp;gt;gold&lt;br /&gt;{ $values { "lead" "boring lead" } { "gold" "shiny gold" } }&lt;br /&gt;{ $description "Turns lead into gold." } ;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Notice that we know the word name, the first strings in each stack effect in the $values array, and the markup structure.&lt;br /&gt;&lt;br /&gt;Here is the generated scafford:&lt;pre&gt;HELP: lead&amp;gt;gold&lt;br /&gt;{ $values { "lead" null } { "gold" null } }&lt;br /&gt;{ $description } ;&lt;/pre&gt;This is much less wrist punishment than typing manually.  The scaffold tool can even generate the documentation with their Factor types given correct code and standard type names like "quot" and "hashtable".  Types that are not known are given the &lt;code&gt;null&lt;/code&gt; class.  Documenation correctness tools can later search for &lt;code&gt;null&lt;/code&gt; objects and report them as errors, since no words should operate on this type.&lt;br /&gt;&lt;br /&gt;I'll finish by including a snippet of the output of running the scaffold help generator on itself.&lt;pre&gt;! Copyright (C) 2008 Doug Coleman.&lt;br /&gt;! See http://factorcode.org/license.txt for BSD license.&lt;br /&gt;USING: arrays help.markup help.syntax io.streams.string kernel strings words ;&lt;br /&gt;IN: tools.scaffold&lt;br /&gt;&lt;br /&gt;HELP: check-root&lt;br /&gt;{ $values&lt;br /&gt;     { "string" string }&lt;br /&gt;     { "string" string } }&lt;br /&gt;{ $description } ;&lt;br /&gt;&lt;br /&gt;HELP: check-scaffold&lt;br /&gt;{ $values&lt;br /&gt;     { "vocab-root" "a vocabulary root string" } { "string" string }&lt;br /&gt;     { "vocab-root" "a vocabulary root string" } { "string" string } }&lt;br /&gt;{ $description } ;&lt;br /&gt;&lt;br /&gt;HELP: check-vocab-name&lt;br /&gt;{ $values&lt;br /&gt;     { "string" string }&lt;br /&gt;     { "string" string } }&lt;br /&gt;{ $description } ;&lt;br /&gt;&lt;br /&gt;HELP: developer-name&lt;br /&gt;{ $description } ;&lt;br /&gt;&lt;br /&gt;HELP: help.&lt;br /&gt;{ $values&lt;br /&gt;     { "word" word } }&lt;br /&gt;{ $description } ;&lt;br /&gt;&lt;br /&gt;HELP: lookup-type&lt;br /&gt;{ $values&lt;br /&gt;     { "string" string }&lt;br /&gt;     { "object/string" null } { "?" "a boolean" } }&lt;br /&gt;{ $description } ;&lt;br /&gt;&lt;br /&gt;HELP: main-file-string&lt;br /&gt;{ $values&lt;br /&gt;     { "vocab" "a vocabulary specifier" }&lt;br /&gt;     { "string" string } }&lt;br /&gt;{ $description } ;&lt;br /&gt;&lt;br /&gt;HELP: not-a-vocab-root&lt;br /&gt;{ $values&lt;br /&gt;     { "string" string } }&lt;br /&gt;{ $description } ;&lt;br /&gt;&lt;br /&gt;HELP: not-a-vocab-root?&lt;br /&gt;{ $values&lt;br /&gt;     { "object" object }&lt;br /&gt;     { "?" "a boolean" } }&lt;br /&gt;{ $description } ;&lt;br /&gt;&lt;br /&gt;HELP: root?&lt;br /&gt;{ $values&lt;br /&gt;     { "string" string }&lt;br /&gt;     { "?" "a boolean" } }&lt;br /&gt;{ $description } ;&lt;br /&gt;&lt;br /&gt;HELP: scaffold-authors&lt;br /&gt;{ $values&lt;br /&gt;     { "path" "a pathname string" } }&lt;br /&gt;{ $description } ;&lt;br /&gt;&lt;br /&gt;HELP: scaffold-copyright&lt;br /&gt;{ $description } ;&lt;br /&gt;&lt;br /&gt;HELP: tests-file-string&lt;br /&gt;{ $values&lt;br /&gt;     { "vocab" "a vocabulary specifier" }&lt;br /&gt;     { "string" string } }&lt;br /&gt;{ $description } ;&lt;br /&gt;&lt;br /&gt;HELP: using&lt;br /&gt;{ $description } ;&lt;br /&gt;&lt;br /&gt;HELP: vocab&amp;gt;scaffold-path&lt;br /&gt;{ $values&lt;br /&gt;     { "vocab-root" "a vocabulary root string" } { "string" string }&lt;br /&gt;     { "path" "a pathname string" } }&lt;br /&gt;{ $description } ;&lt;br /&gt;&lt;br /&gt;ARTICLE: "tools.scaffold" "tools.scaffold"&lt;br /&gt;;&lt;br /&gt;&lt;br /&gt;ABOUT: "tools.scaffold"&lt;/pre&gt;I hope this new vocabulary makes documenation a lot less tedious and error-prone.  Now to go write some docs...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37596622-6537074946584828978?l=code-factor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://code-factor.blogspot.com/feeds/6537074946584828978/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=37596622&amp;postID=6537074946584828978' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/6537074946584828978'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/6537074946584828978'/><link rel='alternate' type='text/html' href='http://code-factor.blogspot.com/2008/09/vocabulary-and-documentation-tool.html' title='Vocabulary and documentation tool: tools.scaffold'/><author><name>Doug Coleman</name><uri>http://www.blogger.com/profile/09416611825627835802</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-37596622.post-1804137998036837171</id><published>2008-04-28T10:49:00.000-07:00</published><updated>2008-04-28T11:10:23.332-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='refactoring'/><category scheme='http://www.blogger.com/atom/ns#' term='Factor'/><title type='text'>Word renaming (part 2)</title><content type='html'>Here are the word names that have changed:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;find* -&gt; find-from&lt;/li&gt;&lt;li&gt;find-last* -&gt; find-last-from&lt;/li&gt;&lt;li&gt;index* -&gt; index-from&lt;/li&gt;&lt;li&gt;last-index* -&gt; last-index-from&lt;/li&gt;&lt;li&gt;subset -&gt; filter&lt;/li&gt;&lt;/ul&gt;New shorthand words:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;1 tail -&gt; rest&lt;/li&gt;&lt;li&gt;1 tail-slice -&gt; rest-slice&lt;/li&gt;&lt;li&gt;swap compose -&gt; prepose&lt;/li&gt;&lt;/ul&gt;Changes to existing word behavior:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;reverse stack effect of assoc-diff, diff&lt;/li&gt;&lt;li&gt;before? after? before=? after=? are now generic&lt;/li&gt;&lt;li&gt;min, max can compare more objects than before, such as timestamps&lt;br /&gt;&lt;/li&gt;&lt;li&gt;between? can compare more objects&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;=&gt; returns symbols&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;There are several motivations at work here.  One is that words named &lt;code&gt;foo*&lt;/code&gt; are a variant of &lt;code&gt;foo&lt;/code&gt;, but otherwise the * is no help to what the word actually does differently.  We're trying to move away from word names with stars to something more meaningful.&lt;br /&gt;&lt;br /&gt;Code with common patterns that come up a lot, like &lt;code&gt;1 tail&lt;/code&gt; and &lt;code&gt;swap compose&lt;/code&gt;, is more clearly understood if these patterns are given a single name.&lt;br /&gt;&lt;br /&gt;Factor's subset is not equivalent to the mathematical definition of subset, so it was renamed to &lt;code&gt;filter&lt;/code&gt; to avoid confusion.  Along these same lines, &lt;code&gt;diff&lt;/code&gt; and &lt;code&gt;assoc-diff&lt;/code&gt; are now the more mathematically intuitive; you can think of diff like set subtraction now, &lt;code&gt;seq1 seq2 diff&lt;/code&gt; is like seq1 - seq2.&lt;br /&gt;&lt;br /&gt;Finally, the "UFO operator" &lt;code&gt;&amp;lt;=&amp;gt;&lt;/code&gt; now returns symbols &lt;code&gt;+lt+ +eq+ +gt+&lt;/code&gt; instead of negative, zero, and positive numbers.  The &lt;code&gt;before?&lt;/code&gt; and &lt;code&gt;after?&lt;/code&gt; words can compare anything that defines a method on this operator.  Since &lt;code&gt;between?&lt;/code&gt;, &lt;code&gt;min&lt;/code&gt;, and &lt;code&gt;max&lt;/code&gt;&lt;br /&gt;are defined in terms of these comparison words, they also work on more objects.&lt;br /&gt;&lt;br /&gt;Please let me know if you have any more suggestions for things words that have awkward argument orders, imprecise names, or if you can suggest alternate names for words with stars in their names.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37596622-1804137998036837171?l=code-factor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://code-factor.blogspot.com/feeds/1804137998036837171/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=37596622&amp;postID=1804137998036837171' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/1804137998036837171'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/1804137998036837171'/><link rel='alternate' type='text/html' href='http://code-factor.blogspot.com/2008/04/word-renaming-part-2.html' title='Word renaming (part 2)'/><author><name>Doug Coleman</name><uri>http://www.blogger.com/profile/09416611825627835802</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-37596622.post-4917749608279549089</id><published>2008-04-14T12:25:00.001-07:00</published><updated>2008-04-14T12:32:37.863-07:00</updated><title type='text'>Word renaming</title><content type='html'>Several words have been renamed and moved around to make Factor more consistent:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;new -&gt; new-sequence&lt;/li&gt;&lt;li&gt;construct-empty -&gt; new&lt;/li&gt;&lt;li&gt;construct-boa -&gt; boa&lt;/li&gt;&lt;li&gt;diff -&gt; assoc-diff&lt;/li&gt;&lt;li&gt;union -&gt; assoc-union&lt;/li&gt;&lt;li&gt;intersect -&gt; assoc-intersect&lt;/li&gt;&lt;li&gt;seq-diff -&gt; diff&lt;/li&gt;&lt;li&gt;seq-intersect -&gt; intersect&lt;/li&gt;&lt;/ul&gt;To make things symmetrical, a new word &lt;code&gt;union&lt;/code&gt; operates on sequences.&lt;br /&gt;&lt;br /&gt;Somehow, &lt;code&gt;seq-diff&lt;/code&gt; and &lt;code&gt;seq-intersect&lt;/code&gt; were implemented as O(n^2) algorithms.  Now, they use hashtables and are O(n).&lt;br /&gt;&lt;br /&gt;Lastly, a new vocabulary named ``sets'' contains the set theoretic words, along with a new word &lt;code&gt;unique&lt;/code&gt; that converts a sequence to a hash table whose keys and values are the same.  An efficient union and intersect are implemented in terms of this word.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37596622-4917749608279549089?l=code-factor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://code-factor.blogspot.com/feeds/4917749608279549089/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=37596622&amp;postID=4917749608279549089' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/4917749608279549089'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/4917749608279549089'/><link rel='alternate' type='text/html' href='http://code-factor.blogspot.com/2008/04/word-renaming.html' title='Word renaming'/><author><name>Doug Coleman</name><uri>http://www.blogger.com/profile/09416611825627835802</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-37596622.post-2578616072261866484</id><published>2008-04-13T14:20:00.000-07:00</published><updated>2008-04-13T12:21:53.935-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='primitives'/><category scheme='http://www.blogger.com/atom/ns#' term='Factor'/><title type='text'>Adding a new primitive</title><content type='html'>I added two primitives to the Factor VM to allow setting and unsetting of environment variables.  It's not that hard to do, but you have to edit several C files in the VM and a couple .factor files in the core.  Really they should not be primitives, so eventually they will be moved into the core.&lt;br /&gt;&lt;br /&gt;The primitives that I added are defined as follows:&lt;pre&gt;IN: system&lt;br /&gt;PRIMITIVE: set-os-env ( value key -- )&lt;br /&gt;PRIMITIVE: unset-os-env ( key -- )&lt;/pre&gt;&lt;div&gt;&lt;h2&gt;Adding a primitive to vm/&lt;/h2&gt;Since Factor's datatypes are not the same as C's datatypes, and because of the garbage collector, there are C functions for accessing and manipulating Factor objects.  The data conversion functions are not documented yet, so here's a sampling of a few of them:&lt;ul&gt;&lt;li&gt;unbox_u16_string() - pop a Factor string off the datastack and return it as a F_CHAR*&lt;/li&gt;&lt;li&gt;from_u16_string() - convert a C string to a Factor object&lt;/li&gt;&lt;li&gt;REGISTER_C_STRING() - register a C string with Factor's garbage collector&lt;/li&gt;&lt;li&gt;UNREGISTER_C_STRING() - unregister a registered C string&lt;/li&gt;&lt;li&gt;dpush() - push an object onto the datastack&lt;/li&gt;&lt;li&gt;dpop() - pop an object off of the datastack&lt;/li&gt;&lt;/ul&gt;Registering a C string with the garbage collector is required when VM code calls code that may trigger a garbage collection (gc).  Any call to Factor from the VM might trigger a gc, and if that happened the object could be moved, thus invalidating your C pointer.  When a pointer is unregistered, it's popped from a gc stack with the corrected pointer value.&lt;br /&gt;&lt;br /&gt;Here is the call to &lt;word&gt;set-os-env:&lt;/word&gt;&lt;pre&gt;DEFINE_PRIMITIVE(set_os_env)&lt;br /&gt;{&lt;br /&gt; F_CHAR *key = unbox_u16_string();&lt;br /&gt; REGISTER_C_STRING(key);&lt;br /&gt; F_CHAR *value = unbox_u16_string();&lt;br /&gt; UNREGISTER_C_STRING(key);&lt;br /&gt; if(!SetEnvironmentVariable(key, value))&lt;br /&gt;     general_error(ERROR_IO, tag_object(get_error_message()), F, NULL);&lt;br /&gt;}&lt;/pre&gt;The function is defined with a macro &lt;code&gt;DEFINE_PRIMITIVE&lt;/code&gt; that takes only the function name.  A corresponding &lt;code&gt;DECLARE_PRIMITIVE&lt;/code&gt; goes in run.h as your function declaration.  Not all primitives use these C preprocessor macros, for instance bignums don't because it doesn't improve the performance.  Parameters to your primitive are popped or unboxed off the data stack, so a primitive's declaration expands to:&lt;pre&gt;F_FASTCALL primitive_set_os_env_impl(void);&lt;br /&gt;&lt;br /&gt;F_FASTCALL void primitive_set_os_env(CELL word, F_STACK_FRAME *callstack_top) {&lt;br /&gt;    save_callstack_top(callstack_top);&lt;br /&gt;    primitive_set_os_env_impl();&lt;br /&gt;}&lt;br /&gt;INLINE void primitive_set_os_env_impl(void)&lt;/pre&gt;F_FASTCALL is a wrapper around FASTCALL, which on x86 will pass the first two arguments in registers as an optimization.  Note that while it declares that it takes no arguments (void), most primitives will do something to the data stack.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Since unbox_u16_string() allocates memory for the Factor object, it could trigger a gc, so it's registered as a string.  You can also register values using &lt;code&gt;REGISTER_ROOT&lt;/code&gt; for cells, &lt;code&gt;REGISTER_BIGNUM&lt;/code&gt; for bignums, and &lt;code&gt;REGISTER_UNTAGGED&lt;/code&gt; for arrays, words, and other Factor object pointers for which the type is known.  The key string can immediately be unregistered after calling unbox on the next stack value since the rest of the function will not cause a gc.  If the win32 call fails, there's a function &lt;code&gt;general_error()&lt;/code&gt; that throws an exception.  In this case, it's an ERROR_IO that calls a helper function to return the Windows error message.&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;Now that the function is written, you have to add it to the list of primitives in primitives.c.  The important thing is that this list remains in the same order as the list in core/ which you will edit in the next section.  Also, add a prototype to the run.h file.&lt;br /&gt;&lt;div&gt;&lt;h2&gt;Adding a primitive to core/&lt;/h2&gt;Everything in Factor compiles down to primitives.  Because they are by definition "primitive", the compiler cannot infer the stack effect and argument types.  To make a primitive's stack effect "known", edit &lt;i&gt;core/inference/known-words/known-words.factor&lt;/i&gt;:&lt;pre&gt;\ set-os-env { string string } { } &amp;lt;effect&amp;gt; set-primitive-effect&lt;/pre&gt;The next step is to put your word in the file &lt;i&gt;core/bootstrap/primitives.factor&lt;/i&gt; in the same order as in &lt;i&gt;vm/primitives.c&lt;/i&gt;.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Sometime in the future there might be a &lt;code&gt;PRIMITIVE:&lt;/code&gt; word that will reduce the number of different places to edit to add a primitive.  If it used Factor's FFI, you could add a new primitive without even having to bootstrap again.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37596622-2578616072261866484?l=code-factor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://code-factor.blogspot.com/feeds/2578616072261866484/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=37596622&amp;postID=2578616072261866484' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/2578616072261866484'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/2578616072261866484'/><link rel='alternate' type='text/html' href='http://code-factor.blogspot.com/2008/04/adding-new-primitive.html' title='Adding a new primitive'/><author><name>Doug Coleman</name><uri>http://www.blogger.com/profile/09416611825627835802</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-37596622.post-7647170389418587483</id><published>2008-03-28T11:51:00.000-07:00</published><updated>2008-03-28T13:05:38.706-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='random'/><category scheme='http://www.blogger.com/atom/ns#' term='prng'/><category scheme='http://www.blogger.com/atom/ns#' term='bsd'/><category scheme='http://www.blogger.com/atom/ns#' term='path'/><title type='text'>Recent Changes in Factor</title><content type='html'>Here are some things I've been working collaboratively with some other Factor devs lately.&lt;br /&gt;&lt;h2&gt;cwd, cd, and pathname changes&lt;/h2&gt;The old way to change the current working directory was to call the &lt;code&gt;cd&lt;/code&gt; word.  This is now obsolete, and code that does this won't get the expected behavior anymore.  A new word, &lt;code&gt;normalize-pathname&lt;/code&gt;, is called before every top level word using paths and adjusts the pathname based on a new dynamic variable called &lt;code&gt;current-directory&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;To get pathnames relative to the Factor directory you used to use the words &lt;code&gt;?resource-path&lt;/code&gt; and &lt;code&gt;resource-path&lt;/code&gt;.   The code &lt;code&gt;"resource:foo.txt" file-contents&lt;/code&gt; now does what &lt;code&gt;"foo.txt" resource-path file-contents&lt;/code&gt; used to do, so user code should avoid calling &lt;code&gt;resource-path&lt;/code&gt; on path literals.&lt;br /&gt;&lt;br /&gt;Also, there's a new word called &lt;code&gt;with-directory ( path quot -- )&lt;/code&gt; that calls your quotation with a new current working directory and restores the old one when done.  All of words that deal with reading and writing files have been updated to respect this change.&lt;br /&gt;&lt;br /&gt;Using the &lt;code&gt;cd&lt;/code&gt; word is still useful sometimes, like when calling &lt;code&gt;with-fork ( parent-quot child-quot -- )&lt;/code&gt; when you want the child process to inherit the current-directory variable as its current directory.&lt;br /&gt;&lt;br /&gt;Pathnames in Factor are still strings, but may be either strings or pathname objects in the future.&lt;br /&gt;&lt;h2&gt;IO Launcher Priorities&lt;/h2&gt;Since some Factor developers still use Windows XP on single-core laptops from 2004, tools.deploy now sets the child process to have a lower priority so that the system is still usable when deploying images.&lt;br /&gt;&lt;br /&gt;Example:&lt;pre&gt;&amp;lt;process&amp;gt;&lt;br /&gt;    { "cmd" "/c" "dir" "/S" "\\" } &amp;gt;&amp;gt;command&lt;br /&gt;    +low-priority+ &amp;gt;&amp;gt;priority&lt;br /&gt;run-process&lt;/pre&gt;&lt;h2&gt;BSD Port&lt;/h2&gt;Factor now supports FreeBSD, NetBSD, and OpenBSD 32 and 64bit versions.  Please inform us of any bugs you find.  Binary releases should happen within a week.&lt;br /&gt;&lt;br /&gt;Fixing the Solaris port is next up, followed by the ARM Linux and WinCE port.&lt;br /&gt;&lt;h2&gt;Random number generator protocol&lt;/h2&gt;Because we want to generate secure random numbers for the new web framework, the Mersenne twister algorithm just won't do as Factor's sole pseudo random number generator.  So until we implement a Yarrow prng, the default prng for cryptography is /dev/random on Unix systems and the &lt;code&gt;CryptGenRandom&lt;/code&gt; call on Windows.  Does anyone have a suggestion as to which provider to use?  I'm using PROV_RSA_AES right now, but I just chose it randomly from the list.&lt;br /&gt;&lt;br /&gt;The random protocol is really simple.&lt;pre&gt;GENERIC: seed-random ( tuple seed -- )&lt;br /&gt;GENERIC: random-32* ( tuple -- r )&lt;br /&gt;GENERIC: random-bytes* ( tuple n -- bytes )&lt;br /&gt;&lt;br /&gt;M: object random-bytes* ( tuple n -- byte-array )&lt;br /&gt;    [ drop random-32* ] with map &amp;gt;c-uint-array ;&lt;br /&gt;&lt;br /&gt;M: object random-32* ( tuple -- n )&lt;br /&gt;    4 random-bytes* le&amp;gt; ;&lt;/pre&gt;There are two ways to get randomness -- either 32 bits at a time, or several bytes at a time.  The cool thing is that you only have to put a method on one of these words, and the other one is taken care of by the default method!  Of course, if you don't define either, the callstack will overflow.  You can define methods on both for efficiency if it makes sense.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37596622-7647170389418587483?l=code-factor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://code-factor.blogspot.com/feeds/7647170389418587483/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=37596622&amp;postID=7647170389418587483' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/7647170389418587483'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/7647170389418587483'/><link rel='alternate' type='text/html' href='http://code-factor.blogspot.com/2008/03/recent-changes-in-factor.html' title='Recent Changes in Factor'/><author><name>Doug Coleman</name><uri>http://www.blogger.com/profile/09416611825627835802</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-37596622.post-2409892529029078932</id><published>2008-03-12T13:48:00.000-07:00</published><updated>2008-03-12T19:01:12.023-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='singletons'/><category scheme='http://www.blogger.com/atom/ns#' term='Factor'/><title type='text'>Singletons in Factor</title><content type='html'>A singleton is a design pattern that only allows for a unique class that is only instantiated once.  In other languages, singletons can contain global data, but why not just use a global to store global state instead?&lt;br /&gt;&lt;br /&gt;In Factor, a singleton is simply a predicate class whose predicate tests if the class is identical to itself.  Singletons are defined like this:&lt;pre&gt;SINGLETON: factor&lt;/pre&gt;That's it!  No messy boilerplate to copy and paste, no subtle reasons why your singleton might be wrong in some corner case.  Using the singleton, we can replace some of the less elegant parts of the Factor core code and implement things in a simpler way.&lt;br /&gt;&lt;br /&gt;For instance, until now the core words &lt;code&gt;os&lt;/code&gt; and &lt;code&gt;cpu&lt;/code&gt; have returned strings based on how the Factor binary is compiled.  Soon, these strings will be parsed into whichever singleton they represent, allowing for generic dispatch.  I wanted to have a cross-platform library for finding out basic hardware information about your computer, like how many cpus/cores, what speed, and how much RAM is in a machine.  To do this without singletons, I had to redefine information already available as strings (the cpu and os words) as symbols.  With singletons, this duplication can be removed.  The same applies to the compiler code and loading libraries.&lt;br /&gt;&lt;br /&gt;Here is the way the core supported operating systems can be defined using singletons.&lt;pre&gt;SINGLETON: winnt&lt;br /&gt;SINGLETON: wince&lt;br /&gt;SINGLETON: macosx&lt;br /&gt;SINGLETON: linux&lt;br /&gt;UNION: windows winnt wince ;&lt;/pre&gt;Now we can dispatch on these to find the number of cores:&lt;pre&gt;HOOK: #cpus os ( -- n )&lt;br /&gt;M: macosx #cpus { 6 3 } sysctl-query-uint ;&lt;br /&gt;M: winnt #cpus system-info SYSTEM_INFO-dwNumberOfProcessors ;&lt;/pre&gt;For loading code, the current idiom is this:&lt;pre&gt;&lt;&lt; "alut" {&lt;br /&gt;        { [ win32? ]  [ "alut.dll" ] }&lt;br /&gt;        { [ macosx? ] [ "/System/Library/Frameworks/OpenAL.framework/OpenAL" ] }&lt;br /&gt;        { [ unix?  ]  [ "libalut.so" ] }&lt;br /&gt;    } cond "cdecl" add-library &gt;&gt;&lt;/pre&gt;Using singletons, we can shorten this to:&lt;pre&gt;"alut" {&lt;br /&gt;    { win32 "alut.dll" }&lt;br /&gt;    { macosx "/System/Library/Frameworks/OpenAL.framework/OpenAL" }&lt;br /&gt;    { unix "libalut.so" }&lt;br /&gt;} add-library&lt;/pre&gt;The library is assumed to be 'cdecl', but if it were 'stdcall' you could specify this by adding a "stdcall" string after the libary name, thus making a triple instead of a pair.  The amount of boilerplate is reduced and the programmer can be more productive and write fewer bugs.&lt;br /&gt;&lt;br /&gt;The implementation of singleton is:&lt;pre&gt;: define-singleton-class ( class -- )&lt;br /&gt;    \ word swap&lt;br /&gt;    dup [ eq? ] curry define-predicate-class ;&lt;/pre&gt;This generates code that looks like:&lt;pre&gt;PREDICATE: word winnt \ winnt eq? ;&lt;/pre&gt;It makes a predicate class with a superclass of 'word' that you can dispatch on and only a single instance exists.  Why are singletons so hard to define in &lt;a href="http://en.wikipedia.org/wiki/Singleton_pattern"&gt;some other languages&lt;/a&gt;?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37596622-2409892529029078932?l=code-factor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://code-factor.blogspot.com/feeds/2409892529029078932/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=37596622&amp;postID=2409892529029078932' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/2409892529029078932'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/2409892529029078932'/><link rel='alternate' type='text/html' href='http://code-factor.blogspot.com/2008/03/singletons-in-factor.html' title='Singletons in Factor'/><author><name>Doug Coleman</name><uri>http://www.blogger.com/profile/09416611825627835802</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-37596622.post-7769948007854558107</id><published>2008-03-06T13:15:00.000-08:00</published><updated>2008-03-12T12:19:46.961-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SOC'/><category scheme='http://www.blogger.com/atom/ns#' term='summerofcode'/><category scheme='http://www.blogger.com/atom/ns#' term='google'/><category scheme='http://www.blogger.com/atom/ns#' term='Factor'/><title type='text'>Google Summer of Code 2008 Project Ideas</title><content type='html'>The Factor project is applying for Summer of Code.  Here are our project ideas.&lt;br /&gt;&lt;h2&gt;Applications&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;Write a structure editor&lt;/li&gt;&lt;li&gt;Write a text editor&lt;/li&gt;&lt;li&gt;Write an IRC client&lt;/li&gt;&lt;li&gt;Write a package manager for Factor&lt;/li&gt;&lt;li&gt;Write a file manager&lt;/li&gt;&lt;/ul&gt;&lt;h2&gt;Enterprise Factor&lt;/h2&gt; &lt;ul&gt;&lt;li&gt;Update the bindings to MySQL, Oracle, ODBC for the latest DB framework in extra/db&lt;/li&gt;&lt;li&gt;Add bindings for DB2, Informix, SQL Server, Sybase, etc&lt;/li&gt;&lt;li&gt;Write a MySQL binding in Factor instead of calling the C library to permit nonblocking I/O operation and avoid GPL licensing issues&lt;/li&gt;&lt;li&gt;Write high-level wrapper for OpenSSL binding which implements encrypted Factor streams&lt;/li&gt;&lt;li&gt;Write a SOAP stack&lt;/li&gt;&lt;li&gt;Improve the continuation-based web framework with ideas from &lt;a href="http://www.seaside.st/"&gt;Seaside&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2&gt;Embedded Languages&lt;/h2&gt; &lt;ul&gt; &lt;li&gt;Improve the Prolog implementation in extra/prolog&lt;/li&gt; &lt;li&gt;Revive extra/lisp&lt;/li&gt; &lt;li&gt;Write an assembler to a microcontroller and cross-compile applications from within Factor&lt;/li&gt; &lt;li&gt;Write an infix math DSL&lt;/li&gt;&lt;li&gt;Write a 'language X' to Factor compiler, where X is C, Ruby, Python, Javascript, Elisp, etc&lt;/li&gt; &lt;/ul&gt; &lt;h2&gt;Miscellaneous Libraries&lt;/h2&gt; &lt;ul&gt; &lt;li&gt;Write a binding and high level interface to zlib or implement gzip compression in Factor&lt;/li&gt; &lt;li&gt;Finish the tar library&lt;/li&gt; &lt;li&gt;Finish the packet sniffer libraries&lt;/li&gt; &lt;li&gt;Implement the math algorithm of your choice for extra/math&lt;/li&gt; &lt;li&gt;Implement the data structures described in &lt;a href="http://www.amazon.com/Purely-Functional-Structures-Chris-Okasaki/dp/0521663504/ref=pd_bbs_sr_1?ie=UTF8&amp;amp;s=books&amp;amp;qid=1204838850&amp;amp;sr=8-1"&gt;Purely Functional Data Structures&lt;/a&gt;&lt;/li&gt; &lt;li&gt;Write a COM interface&lt;/li&gt; &lt;li&gt;Improve the JNI library&lt;/li&gt; &lt;li&gt;Write a JNI-like bridge for Android&lt;/li&gt;&lt;li&gt;Write a wrapper for Windows Mobile libraries to make calls and send SMSs&lt;/li&gt;&lt;li&gt;Integrate the &lt;a href="http://en.wikipedia.org/wiki/Zoneinfo"&gt;zoneinfo&lt;/a&gt; timezone database with Factor's calendar library&lt;/li&gt;&lt;li&gt;Add more calendars to the library, such as Persian, Hebrew, Islamic, Chinese, Hindu, Buddhist&lt;br /&gt;&lt;/li&gt; &lt;/ul&gt; &lt;h2&gt;Factor VM&lt;/h2&gt; &lt;ul&gt; &lt;li&gt;Write a GC profiler&lt;/li&gt;&lt;li&gt;Make GC memory areas shrink after garbage collection&lt;/li&gt; &lt;li&gt;Implement dtrace probes&lt;/li&gt;&lt;li&gt;Make continuations serializable&lt;/li&gt; &lt;/ul&gt;&lt;h2&gt;User Interface&lt;/h2&gt; &lt;ul&gt; &lt;li&gt;Write a binding to an image manipulation library&lt;/li&gt; &lt;li&gt;Finish bitmap library&lt;/li&gt; &lt;li&gt;Improve the look of Factor's UI gadgets&lt;/li&gt; &lt;li&gt;Add drag and drop support&lt;/li&gt; &lt;li&gt;Add any of the following gadgets: tabs, syntax highlighting text editor using the xmode library, combo boxes, tables, trees, spinners&lt;br /&gt;&lt;/li&gt; &lt;li&gt;Implement native font rendering on Windows&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37596622-7769948007854558107?l=code-factor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://code-factor.blogspot.com/feeds/7769948007854558107/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=37596622&amp;postID=7769948007854558107' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/7769948007854558107'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/7769948007854558107'/><link rel='alternate' type='text/html' href='http://code-factor.blogspot.com/2008/03/google-summer-of-code-2008-project.html' title='Google Summer of Code 2008 Project Ideas'/><author><name>Doug Coleman</name><uri>http://www.blogger.com/profile/09416611825627835802</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-37596622.post-4811166919677053382</id><published>2008-02-14T21:41:00.000-08:00</published><updated>2008-02-14T22:07:39.233-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='processes'/><category scheme='http://www.blogger.com/atom/ns#' term='Factor'/><category scheme='http://www.blogger.com/atom/ns#' term='hooks'/><title type='text'>Disassembler Vocabulary "ported" to Windows</title><content type='html'>Slava &lt;a href="http://factor-language.blogspot.com/2008/02/invoking-gdb-disassembler-to.html"&gt;wrote a vocabulary&lt;/a&gt; to send gdb a process id and a range of addresses to disassemble in order to streamline the process of writing compiler optimizations.  The original code only worked on Unix, requiring a call unix:getpid, which is a Unix system call.  The Windows equivalent is GetCurrentProcessId.  Since we need to call the Unix version on MacOSX and Linux, and the Windows API call on Windows XP, we use a Factor design pattern -- the HOOK:.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;The HOOK:&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;The word in question is called &lt;code&gt;make-disassemble-cmd&lt;/code&gt;.  Behold its majesty:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;QUALIFIED: unix&lt;br /&gt;&lt;br /&gt;M: pair make-disassemble-cmd&lt;br /&gt;    in-file [&lt;br /&gt;        "attach " write&lt;br /&gt;        unix:getpid number&gt;string print&lt;br /&gt;        "disassemble " write&lt;br /&gt;        [ number&gt;string write bl ] each&lt;br /&gt;    ] with-file-out ;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;You can see where it's calling unix:getpid.  Knowing the Windows API call from above, it's easy to write a version that works on Windows:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;M: pair make-disassemble-cmd&lt;br /&gt;    in-file [&lt;br /&gt;        "attach " write&lt;br /&gt;         GetCurrentProcessId number&gt;string print&lt;br /&gt;        "disassemble " write&lt;br /&gt;        [ number&gt;string write bl ] each&lt;br /&gt;    ] with-file-out ;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Obviously, this is unacceptable, because now it doesn't work on Unix!  If we rename the &lt;code&gt;make-disassemble-cmd&lt;/code&gt; word for the new platform, then there are still two copies of the exact same word, and you'll be loading them both on platforms where they shouldn't be loaded.  We really just want to rename the one word that changed, so...&lt;br /&gt;&lt;br /&gt;Let's make a HOOK:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;! in io.launcher&lt;br /&gt;HOOK: current-process-handle io-backend ( -- handle )&lt;br /&gt;&lt;br /&gt;! in io.unix.launcher&lt;br /&gt;M: unix-io current-process-handle ( -- handle ) getpid ;&lt;br /&gt;&lt;br /&gt;! in io.windows.launcher&lt;br /&gt;M: windows-io current-process-handle ( -- handle ) GetCurrentProcessId ;&lt;br /&gt;&lt;br /&gt;! in tools.disassembler&lt;br /&gt;M: pair make-disassemble-cmd&lt;br /&gt;    in-file [&lt;br /&gt;        "attach " write&lt;br /&gt;        current-process-handle number&gt;string print&lt;br /&gt;        "disassemble " write&lt;br /&gt;        [ number&gt;string write bl ] each&lt;br /&gt;    ] with-file-out ;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now, there is just one word that will do the work on both platforms.  The relevant code is only loaded into the image on the correct platform, and the problem is solved without renaming lots of words, without #ifdefs, and without copy/pasting.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37596622-4811166919677053382?l=code-factor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://code-factor.blogspot.com/feeds/4811166919677053382/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=37596622&amp;postID=4811166919677053382' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/4811166919677053382'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/4811166919677053382'/><link rel='alternate' type='text/html' href='http://code-factor.blogspot.com/2008/02/disassembler-vocabulary-ported-to.html' title='Disassembler Vocabulary &quot;ported&quot; to Windows'/><author><name>Doug Coleman</name><uri>http://www.blogger.com/profile/09416611825627835802</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-37596622.post-961091938331762594</id><published>2008-02-12T10:19:00.002-08:00</published><updated>2008-02-12T10:41:06.498-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='breadth'/><category scheme='http://www.blogger.com/atom/ns#' term='editors'/><category scheme='http://www.blogger.com/atom/ns#' term='depth'/><category scheme='http://www.blogger.com/atom/ns#' term='Factor'/><title type='text'>Text Editor Integration and Directory Traversal</title><content type='html'>To edit a word in Factor you pass the &lt;code&gt;edit&lt;/code&gt; word a defspec, like &lt;code&gt;\ + edit&lt;/code&gt; or &lt;code&gt;{ float + } edit&lt;/code&gt;, or you press CTRL-SHIFT-E on a word.  If an editor path is already set, then the word definition should pop up in your editor at the line of the definition.  However, if you haven't configured your text editor, a restart will be thrown and you can select it from a list.  If you selected gvim, previously it would use a braindead algorithm that makes a list of all paths in \Program Files and then traverses that list looking for the gvim.exe binary.  This caused it to hang while doing tons of disk IO.&lt;br /&gt;&lt;br /&gt;Now it's better in two ways -- it only looks in \Program Files\vim\ (oops), and secondly you can search with &lt;code&gt;find-file-breadth&lt;/code&gt; or &lt;code&gt;find-file-depth&lt;/code&gt;.  The implementation is pretty simple -- breadth first search uses a dlist, pushes the new traversal results to the back of the list, and traverses the list in order.  Depth first also pushes newly found directories to the end of the list, but it pops them from the end.&lt;br /&gt;&lt;br /&gt;The next step is to extract out the depth/breadth first algorithm so it's generally useful, but I need to continue working on the database library.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37596622-961091938331762594?l=code-factor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://code-factor.blogspot.com/feeds/961091938331762594/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=37596622&amp;postID=961091938331762594' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/961091938331762594'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/961091938331762594'/><link rel='alternate' type='text/html' href='http://code-factor.blogspot.com/2008/02/text-editor-integration-and-directory.html' title='Text Editor Integration and Directory Traversal'/><author><name>Doug Coleman</name><uri>http://www.blogger.com/profile/09416611825627835802</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-37596622.post-1386074806026608264</id><published>2008-02-11T14:48:00.000-08:00</published><updated>2008-02-11T16:33:24.654-08:00</updated><title type='text'>Factor Tax Library</title><content type='html'>I've been doing my own taxes for a few years, so I decided to start writing some Factor words to help with the calculations.  This library does FICA tax, Medicare tax, and federal income tax, along with Minnesota state tax.&lt;br /&gt;&lt;br /&gt;To get paid by an employer you fill out a &lt;a href="http://www.irs.gov/pub/irs-pdf/fw4.pdf"&gt;Form W-4&lt;/a&gt; which they keep on file.  The three relevant pieces of information are the tax year (2008), the number of allowances, and whether or not you are married.&lt;br /&gt;&lt;br /&gt;In Factor this looks like:&lt;br /&gt;&lt;pre&gt;TUPLE: w4 year allowances married? ;&lt;br /&gt;C: &amp;lt;w4&amp;gt; w4&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;To calculate your withholding, or the amount your employer should withhold from your salary every pay period, the library starts with your yearly salary, a Form W-4, and a tax collector entity, such as &amp;lt;federal&amp;gt; or &amp;lt;minnesota&amp;gt; and calls the withholding word.&lt;br /&gt;&lt;br /&gt;For the average single male programmer making $70k per year, the calculation looks like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;( scratchpad ) 70000   2008 3 f &amp;lt;w4&amp;gt; &amp;lt;federal&amp;gt; withholding dup money.&lt;br /&gt;$16,054.00&lt;br /&gt;( scratchpad ) biweekly money.&lt;br /&gt;$617.46&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The government's cut of your paycheck isn't too hard to calculate.  The employer withholds 6.2% of your paycheck for FICA (social security) and contributes a matching 6.2% that is over and above your salary, for a total of 12.4%.  The FICA tax has what's known as a base rate, which is a ceiling on the amount of your salary that this tax is applied to.  For 2008, the base rate is $102,000, which means that the FICA tax is not applied above this amount.  Additionally, you and your employer pay Medicare tax, which is 1.45% each for a total of 3.9%, and this is applied to all income (no base rate).&lt;br /&gt;&lt;br /&gt;Now for the Federal income tax.  Take your salary and subtract $3500 times the number of allowances you claimed on your Form W-4.  For instance, three allowances (working only one job, claiming self as a dependent, and head of household) will give you a $10,500 allowance, which will leave  $59,500 taxable income.  (Note: FICA and Medicare tax apply to the entire salary, while Federal income tax applies only to the salary - allowances)  There are several equivalent charts for looking up the federal income tax withholding, so this library uses the annual one because it was easiest to encode.&lt;br /&gt;&lt;br /&gt;So for the example salary of $70,000 with three allowances, the calculation looks like:&lt;br /&gt;&lt;pre&gt;( scratchpad ) 70000 2008 3 f &amp;lt;w4&amp;gt; &amp;lt;federal&amp;gt; federal-tax money.&lt;br /&gt;$10,699.00&lt;br /&gt;( scratchpad ) 70000  2008 3 f &amp;lt;w4&amp;gt; fica-tax money.&lt;br /&gt;$4,340.00&lt;br /&gt;( scratchpad ) 70000  2008 3 f &amp;lt;w4&amp;gt; &amp;lt;federal&amp;gt; medicare-tax money.&lt;br /&gt;$1,015.00&lt;br /&gt;( scratchpad ) { 10699 4340 1015 } sum money.&lt;br /&gt;$16,054.00&lt;br /&gt;( scratchpad ) 70000  2008 3 f &amp;lt;w4&amp;gt; &amp;lt;federal&amp;gt; withholding money.&lt;br /&gt;$16,054.00&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Salaries are taxed at different rates as they increase.  For example, the first $2,650 dollars you make are taxed at 0% federal income tax rate.  This does not mean you pay no tax at all; FICA and Medicare still apply.  From $2,650 to $10,300 the tax rate is 10%.  So, if somehow you were making $8,650 per year after deducting allowances, you would be in the second tax bracket and your taxable income would be $6000, taxed at 10%, for a total of $600.  Your employer would withhold this amount from your paychecks and pay it to the government quarterly.  (Note: the numbers above are for single people, married people have a more advantageous table)&lt;br /&gt;&lt;br /&gt;Minnesota has one of the highest state taxes.  To calculate this tax, take the same allowances from your Form W-4 and calculate the allowance per the federal income tax, $3,500 per allowance.  Again, there are two tables -- single and married.  The tax amounts are 5.35%, 7.05%, and 7.85% in the highest bracket.&lt;br /&gt;&lt;pre&gt;( scratchpad ) 70000  2008 3 f &amp;lt;w4&amp;gt; &amp;lt;minnesota&amp;gt; withholding money.&lt;br /&gt;$3,686.68&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;So, altogether, the employer withholds your federal and state taxes from the paycheck.  You can calculate this:&lt;br /&gt;&lt;pre&gt;( scratchpad ) 70000  2008 3 f &amp;lt;w4&amp;gt; &lt;minnesota&gt; employer-withhold dup money. biweekly money.&lt;br /&gt;$19,740.68&lt;br /&gt;$759.26&lt;br /&gt;( scratchpad ) 70000  2008 3 f &amp;lt;w4&amp;gt; &amp;lt;minnesota&amp;gt; net money. biweekly money.&lt;br /&gt;$50,259.33&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;So if you make $70k in Minnesota, your employer should withhold $759.26 per paycheck, to be split between the federal and state government and paid quarterly (or monthly).  You would get to keep $50,259.33 and the employer would give you a Form W-2 with the relevant details.  You could the file income tax returns on April 15th, where you could possibly get some of that back from various deductions and refunds.&lt;br /&gt;&lt;br /&gt;All values are calculated in rational numbers, so they are exact until the end, where  the cents are rounded to the nearest cent.  Percentages are in the source as decimal numbers, and are parsed using the &lt;code&gt;DECIMAL:&lt;/code&gt; word.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;( scratchpad ) DECIMAL: .1 .&lt;br /&gt;1/10&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Please feel free to submit patches for other states and to help me correct any bugs.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37596622-1386074806026608264?l=code-factor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://code-factor.blogspot.com/feeds/1386074806026608264/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=37596622&amp;postID=1386074806026608264' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/1386074806026608264'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/1386074806026608264'/><link rel='alternate' type='text/html' href='http://code-factor.blogspot.com/2008/02/factor-tax-library.html' title='Factor Tax Library'/><author><name>Doug Coleman</name><uri>http://www.blogger.com/profile/09416611825627835802</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-37596622.post-3166534528391462121</id><published>2008-02-02T20:44:00.001-08:00</published><updated>2008-02-02T21:08:46.199-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='assocs'/><category scheme='http://www.blogger.com/atom/ns#' term='Factor'/><title type='text'>Parsing HTTP headers in Factor with multi-assocs</title><content type='html'>The implementation of setting and parsing http headers in Factor has previously used a hashtable with a single key/value pair.  However, this is broken because certain fields can be sent twice, e.g. set-cookie.  The new implementation is a hashtable with keys/vectors to store multiple values for the same key.&lt;br /&gt;&lt;br /&gt;I originally tried to make this obey the assoc protocol so that you could convert from a hashtable of vectors back to any type of assoc (hashtable/alist/AVL tree/etc) but this turned out to be a really bad idea because not only was it not useful, but it breaks the semantics of the assoc protocol if set-at inserts an element instead of sets it.&lt;br /&gt;&lt;br /&gt;So the implementation is in assocs.lib as a few helper words:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;: insert-at ( value key assoc -- )&lt;br /&gt;    [ ?push ] change-at ;&lt;br /&gt;&lt;br /&gt;: peek-at* ( key assoc -- obj ? )&lt;br /&gt;    at* dup [ &amp;gt;r peek r&amp;gt; ] when ;&lt;br /&gt;&lt;br /&gt;: peek-at ( key assoc -- obj )&lt;br /&gt;    peek-at* drop ;&lt;br /&gt;&lt;br /&gt;: &amp;gt;multi-assoc ( assoc -- new-assoc )&lt;br /&gt;    [ 1vector ] assoc-map ;&lt;br /&gt;&lt;br /&gt;: multi-assoc-each ( assoc quot -- )&lt;br /&gt;    [ with each ] curry assoc-each ; inline&lt;br /&gt;&lt;br /&gt;: insert ( value variable -- ) namespace insert-at ;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Of course, &lt;code&gt;set-at&lt;/code&gt; and &lt;code&gt;at&lt;/code&gt; still set and access the values, but there are a couple new utility words.  The &lt;code&gt;insert-at&lt;/code&gt; word has the same stack effect as &lt;code&gt;set-at&lt;/code&gt; but pushes a value instead of setting it.  &lt;code&gt;peek-at&lt;/code&gt; will give you the last value set for a given key, and this is the standard way of accessing values when you only care about the last one.&lt;br /&gt;&lt;br /&gt;To turn an assoc into a multi-assoc, call &lt;code&gt;&amp;gt;multi-assoc&lt;/code&gt;.  To iterate over &lt;i&gt;all&lt;/i&gt; the key/value pairs, use &lt;code&gt;multi-assoc-each&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;The &lt;code&gt;insert&lt;/code&gt; word is for use with the &lt;code&gt;make-assoc&lt;/code&gt; word, which executes inside a new namespace and outputs the variables you set as a hashtable.&lt;br /&gt;&lt;br /&gt;Here's an example of what the headers look like for a website:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;( scratchpad ) USE: http.client "amazon.com" http-get drop .&lt;br /&gt;H{&lt;br /&gt;    { "connection" V{ "close" } }&lt;br /&gt;    { "content-type" V{ "text/html; charset=ISO-8859-1" } }&lt;br /&gt;    { "server" V{ "Server" } }&lt;br /&gt;    { "x-amz-id-2" V{ "L0oid1yo1Z6cuq+VgwWCv0G/UdPov/0v" } }&lt;br /&gt;    { "x-amz-id-1" V{ "15CPXN68HXB35FXE62CX" } }&lt;br /&gt;    {&lt;br /&gt;        "set-cookie"&lt;br /&gt;        V{&lt;br /&gt;            "skin=noskin; path=/; domain=.amazon.com; expires=Sun, 03-Feb-2008 04:57:59 GMT"&lt;br /&gt;            "session-id-time=1202544000l; path=/; domain=.amazon.com; expires=Sat Feb 09 08:00:00 2008 GMT"&lt;br /&gt;            "session-id=002-3595241-4867224; path=/; domain=.amazon.com; expires=Sat Feb 09 08:00:00 2008 GMT"&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;    { "vary" V{ "Accept-Encoding,User-Agent" } }&lt;br /&gt;    { "date" V{ "Sun, 03 Feb 2008 04:57:59 GMT" } }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I have normalized the keys by converting them to all lower case.  For some reason, Amazon sends two headers as Set-Cookie and the last one as Set-cookie, which is pretty weird.&lt;br /&gt;&lt;br /&gt;Since the prettyprinter outputs valid Factor code, you can copy/paste the above headers into a Factor listener and run some of the multi-assoc words on them.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37596622-3166534528391462121?l=code-factor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://code-factor.blogspot.com/feeds/3166534528391462121/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=37596622&amp;postID=3166534528391462121' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/3166534528391462121'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/3166534528391462121'/><link rel='alternate' type='text/html' href='http://code-factor.blogspot.com/2008/02/parsing-http-headers-in-factor-with.html' title='Parsing HTTP headers in Factor with multi-assocs'/><author><name>Doug Coleman</name><uri>http://www.blogger.com/profile/09416611825627835802</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-37596622.post-6422515228356838227</id><published>2007-12-13T09:41:00.000-08:00</published><updated>2007-12-13T12:19:56.815-08:00</updated><title type='text'>Sorting Filenames Containing Numbers</title><content type='html'>Jeff Atwood &lt;a href="http://www.codinghorror.com/blog/archives/001018.html"&gt;articulated a problem&lt;/a&gt; I usually run into when looking at files in Explorer.  If you have a bunch of files with numbers, file "a100.txt" will come before file "a2.txt" when sorted by name.  Of course, two is less than 100, so you'd expect "a2.txt" to come first.&lt;br /&gt;&lt;br /&gt;Here's a solution in Factor:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;: human-sort ( seq -- newseq )&lt;br /&gt;    [ [ digit? ] cut3 &gt;r string&gt;number r&gt; 3array ] map natural-sort&lt;br /&gt;    [ first3 &gt;r number&gt;string r&gt; 3append ] map ;&lt;br /&gt;&lt;br /&gt;{ "a100.txt" "a10.txt" } human-sort .&lt;br /&gt;    { "a10.txt" "a100.txt" }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The algorithm is simple: split a filename at the first number, convert that from a string to a number, and let Factor's &lt;code&gt;natural-sort&lt;/code&gt; do the rest.&lt;br /&gt;&lt;br /&gt;It maps over the sequence, { "a100.txt" "a10.txt" }, and cuts it at the first number inside each element so, after cut3, you get the result on the stack like "a" "100" ".txt", and "a" "10" ".txt".  You dip under the top element and convert the to a number, &lt;code&gt;&gt;r string&gt;number r&gt;&lt;/code&gt;, and &lt;code&gt;3array&lt;/code&gt; to make a sequence of sequences { { "a" 100 ".txt" } { "a" 10 ".txt" } }.  You then call &lt;code&gt;natural-sort&lt;/code&gt; on this.  Natural-sort knows how to compare element by element, so seeing the "a" "a" is equal it moves onto the next comparison, 100 10.  Now that it's in the right order, you still need to turn your sequence back into strings.  natural-sort returned { { "a" 10 ".txt" } { "a" 100 ".txt" } }, so map (do something to each element in the sequence) over this and do &lt;code&gt;first3 &gt;r number&gt;string r&gt; 3append&lt;/code&gt;, which explodes the array, converts back to a string, and appends three strings together to get the filenames.  The dot at the end prints it.&lt;br /&gt;&lt;br /&gt;I had to write cut3, but it's a generally useful word for partitioning a sequence that I put into sequences.lib.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;: cut3 ( seq pred -- head match tail )&lt;br /&gt;    2dup find drop [&lt;br /&gt;        rot swap cut rot [ not ] compose&lt;br /&gt;        dupd find drop [ cut ] [ f ] if*&lt;br /&gt;    ] [&lt;br /&gt;        drop nip f like f f&lt;br /&gt;    ] if* ; &lt;br /&gt; &lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;UPDATE:&lt;br /&gt;&lt;br /&gt;I generalized this to work on { "a100b200.txt" "a100b2.txt" } with some suggestions from Slava.  If you understood the previous code, this should be pretty understandable too.  It keeps calling cut3 until it returns no matches and accumulates the result in a sequence, all the while applying a quotation to whatever matched the predicate.  It then compares with &lt;code&gt;natural-sort&lt;/code&gt; like before, and transforms these sequences back into filenames.  &lt;code&gt;cut3&lt;/code&gt; is cleaned up in this version.  I ended up with two general words, &lt;code&gt;cut3&lt;/code&gt; and &lt;code&gt;cut-all&lt;/code&gt; that can be put into a library, sequences.lib, and used elsewhere. &lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;: cut-find ( seq pred -- before after )&lt;br /&gt;    dupd find drop dup [ cut ] when ;&lt;br /&gt;&lt;br /&gt;: cut3 ( seq pred -- first mid last )&lt;br /&gt;    [ cut-find ] keep [ not ] compose cut-find ;&lt;br /&gt;    &lt;br /&gt;: (cut-all) ( seq pred quot -- )&lt;br /&gt;    [ &gt;r cut3 r&gt; dip &gt;r &gt;r , r&gt; [ , ] when* r&gt; ] 2keep&lt;br /&gt;    pick [ (cut-all) ] [ 3drop ] if ;&lt;br /&gt;&lt;br /&gt;: cut-all ( seq pred quot -- seq )&lt;br /&gt;    [ (cut-all) ] { } make ;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;: human-sort ( seq -- newseq )&lt;br /&gt;    [ [ digit? ] [ string&gt;number ] cut-all ] map natural-sort&lt;br /&gt;    [ [ dup string? [ number&gt;string ] unless ] map concat ] map ;&lt;br /&gt;&lt;br /&gt;{ "a100b2.txt" "a100b200.txt" } human-sort .&lt;br /&gt;    { "a100b2.txt" "a100b200.txt" }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;UPDATE TWO:&lt;br /&gt;&lt;br /&gt;Here is a much better algorithm that doesn't reconstruct the original filenames and thus can handle leading zeros.  It constructs keys that can be sorted by &lt;code&gt;sort-values&lt;/code&gt; or &lt;code&gt;sort-keys&lt;/code&gt; (&lt;code&gt;sort-values&lt;/code&gt; is just as efficient here and it lets you use &lt;code&gt;dup&lt;/code&gt; in the map instead of a &lt;code&gt;[ ] keep&lt;/code&gt;, which makes the code slightly more legible).&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;: human-sort ( seq -- newseq )&lt;br /&gt;    [ dup [ digit? ] [ string&gt;number ] cut-all ] { } map&gt;assoc&lt;br /&gt;    sort-values keys ;&lt;br /&gt;&lt;br /&gt;{ "000a.txt" "00a.txt" } human-sort .&lt;br /&gt;    { "00a.txt" "000a.txt" }&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37596622-6422515228356838227?l=code-factor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://code-factor.blogspot.com/feeds/6422515228356838227/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=37596622&amp;postID=6422515228356838227' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/6422515228356838227'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/6422515228356838227'/><link rel='alternate' type='text/html' href='http://code-factor.blogspot.com/2007/12/sorting-filenames-containing-numbers.html' title='Sorting Filenames Containing Numbers'/><author><name>Doug Coleman</name><uri>http://www.blogger.com/profile/09416611825627835802</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-37596622.post-2858418589918848264</id><published>2007-11-26T15:50:00.001-08:00</published><updated>2007-11-26T16:12:09.151-08:00</updated><title type='text'>Cross-platform Factor Install Script</title><content type='html'>Recently, I wrote an installer/updater bash script that downloads the git repository, compiles, and bootstraps Factor.  On Linux, it compiles Factor for console-only if any of the libraries required for the graphical interface are missing (freetype, GL, GLU, X11).&lt;br /&gt;&lt;br /&gt;To install Factor to a directory: ./factor.sh install&lt;br /&gt;To update your Factor repository: ./misc/factor.sh update&lt;br /&gt;&lt;br /&gt;The script source code is available for &lt;a href="http://www.double.co.nz/cgi-bin/gitweb.cgi?p=factor.git;a=blob;f=misc/factor.sh;h=98f9104549e68ecf01a6149d28866bad8d5ef6bd;hb=HEAD"&gt;browsing&lt;/a&gt; or for &lt;a href="http://www.double.co.nz/cgi-bin/gitweb.cgi?p=factor.git;a=blob_plain;f=misc/factor.sh;hb=HEAD"&gt;download&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Tested on Windows XP/Vista in Cygwin, Debian and Ubuntu Linux, and Mac OS X.&lt;br /&gt;&lt;br /&gt;Please send bug reports and improvements.  Thanks!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37596622-2858418589918848264?l=code-factor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://code-factor.blogspot.com/feeds/2858418589918848264/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=37596622&amp;postID=2858418589918848264' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/2858418589918848264'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/2858418589918848264'/><link rel='alternate' type='text/html' href='http://code-factor.blogspot.com/2007/11/cross-platform-factor-install-script.html' title='Cross-platform Factor Install Script'/><author><name>Doug Coleman</name><uri>http://www.blogger.com/profile/09416611825627835802</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-37596622.post-1410884734456616604</id><published>2007-11-18T14:19:00.000-08:00</published><updated>2007-11-18T14:26:56.695-08:00</updated><title type='text'>Where's my Raptor!??</title><content type='html'>My Macbook's dock displayed the default icon instead of a raptor.  The fix was to move the move the .app directory to another name, then to move it back.  Now I have a raptor!&lt;br /&gt;&lt;br /&gt;Does 10.4 do some caching?  It probably had it cached wrong since we got the new icon.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37596622-1410884734456616604?l=code-factor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://code-factor.blogspot.com/feeds/1410884734456616604/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=37596622&amp;postID=1410884734456616604' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/1410884734456616604'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/1410884734456616604'/><link rel='alternate' type='text/html' href='http://code-factor.blogspot.com/2007/11/wheres-my-raptor.html' title='Where&apos;s my Raptor!??'/><author><name>Doug Coleman</name><uri>http://www.blogger.com/profile/09416611825627835802</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-37596622.post-7941824172954125881</id><published>2007-11-07T14:45:00.000-08:00</published><updated>2007-11-07T20:19:19.791-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='dlists'/><category scheme='http://www.blogger.com/atom/ns#' term='Factor'/><category scheme='http://www.blogger.com/atom/ns#' term='queues'/><category scheme='http://www.blogger.com/atom/ns#' term='heaps'/><title type='text'>Doubly-linked Lists, Queues, and Heaps</title><content type='html'>&lt;h2&gt;Doubly-linked Lists and Queues&lt;/h2&gt;&lt;br /&gt;Factor has had doubly-linked lists for years now, but they were not well-documented or polished.  Now, they're documented and have replaced the queues library.&lt;br /&gt;&lt;br /&gt;An example of a dlist usage:&lt;br /&gt;&lt;pre&gt;&amp;lt;dlist&gt; "red" over push-front "blue" over push-front dup pop-back .&lt;br /&gt;    "red"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;You can add/remove nodes of a dlist with &lt;code&gt;push-front&lt;/code&gt;, &lt;code&gt;push-back&lt;/code&gt;, &lt;code&gt;pop-front&lt;/code&gt;, &lt;code&gt;pop-back&lt;/code&gt;, &lt;code&gt;delete-node&lt;/code&gt;, and search with &lt;code&gt;dlist-find&lt;/code&gt;, &lt;code&gt;dlist-contains?&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;Finding the length of a dlist is O(1) since it stores the length as &lt;code&gt;dlist-length&lt;/code&gt;, a tuple slot.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Heaps&lt;/h2&gt;&lt;br /&gt;Heaps have been updated to allow for &lt;code&gt;&amp;lt;min-heap&gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;max-heap&gt;&lt;/code&gt; data structures.  Adding elements to a heap is achieved with &lt;code&gt;heap-push ( value key heap -- )&lt;/code&gt;, while popping elements is &lt;code&gt;heap-pop ( heap -- value key )&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;Factor's green threads implementation had been using a hack for the sleep-queue: each time a new entry was added it would modify a non-growable array, which would then be sorted by the smallest timeout.  Adding a sequence of sleep continuations would take O(n^2 log n) time!  Running &lt;code&gt;10000 [ [ 100 sleep ] in-thread ] times&lt;/code&gt; should spawn 10000 threads and sleep for 100 ms in each one, and with the old sleep-queue implementation it takes over a minute on my Macbook.  Now it's just O(n log n), which takes a second or two.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37596622-7941824172954125881?l=code-factor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://code-factor.blogspot.com/feeds/7941824172954125881/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=37596622&amp;postID=7941824172954125881' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/7941824172954125881'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/7941824172954125881'/><link rel='alternate' type='text/html' href='http://code-factor.blogspot.com/2007/11/doubly-linked-lists-queues-and-heaps.html' title='Doubly-linked Lists, Queues, and Heaps'/><author><name>Doug Coleman</name><uri>http://www.blogger.com/profile/09416611825627835802</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-37596622.post-1401635304800556805</id><published>2007-09-24T20:45:00.000-07:00</published><updated>2007-09-27T21:49:37.984-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Windows'/><category scheme='http://www.blogger.com/atom/ns#' term='SEH'/><category scheme='http://www.blogger.com/atom/ns#' term='Factor'/><title type='text'>Windows SEH Revisited</title><content type='html'>It turns out there are Win32 API calls to set up a structured exception handler (SEH) on Windows NT, which is easier and more reliable to use than inline assembly.  To install an exception handler, call &lt;a href="http://msdn2.microsoft.com/en-us/library/ms679274.aspx"&gt;AddVectoredExceptionHandler&lt;/a&gt;.  An exception handler gets passed a &lt;code&gt;PEXCEPTION_POINTERS&lt;/code&gt; struct, which is a &lt;code&gt;PEXCEPTION_RECORD&lt;/code&gt; and a &lt;code&gt;CONTEXT*&lt;/code&gt;, both of which are defined in the header files.  The context structure is different on every platform because it lists the contents of the CPU registers at the time of the exception, while the &lt;code&gt;ExceptionRecord&lt;/code&gt; struct is the same, and contains the &lt;code&gt;ExceptionCode&lt;/code&gt; and the address where it occurred (in &lt;code&gt;ExceptionInformation[1]&lt;/code&gt;).&lt;br /&gt;&lt;br /&gt;The job of the exception handler is to determine where program execution should proceed after it returns.  For Factor, the errors we handle are memory access errors, which happen when a stack overflows or underflows and hits a guard page, division by zero, and 'any other error'.  We can't just jump to these Factor exception handlers inside the Win32 exception handler, but we can set the instruction pointer, the EIP register, to our handler and return  &lt;code&gt; EXCEPTION_CONTINUE_EXECUTION&lt;/code&gt;.  In this way, we can let the operating system catch errors and report them to the user as "Data stack underflow", "Division by zero", and continue running Factor without expensive checks for each memory access or division.&lt;br /&gt;&lt;br /&gt;The stack pointer at the time of the exception is also important.  If we were executing compiled Factor code, as determined by checking the fault address against the C predicate &lt;code&gt;in_code_heap_p&lt;/code&gt;, then we set a global with this address to continue execution after the exception is handled.  However, if we are in C code, then the global is left as NULL.&lt;br /&gt;&lt;br /&gt;Here is the code--much cleaner, and more correct, than before.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;void c_to_factor_toplevel(CELL quot)&lt;br /&gt;{&lt;br /&gt;    AddVectoredExceptionHandler(0, (void*)exception_handler);&lt;br /&gt;    c_to_factor(quot);&lt;br /&gt;    RemoveVectoredExceptionHandler((void*)exception_handler);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;long exception_handler(PEXCEPTION_POINTERS pe)&lt;br /&gt;{&lt;br /&gt;    PEXCEPTION_RECORD e = (PEXCEPTION_RECORD)pe-&gt;ExceptionRecord;&lt;br /&gt;    CONTEXT *c = (CONTEXT*)pe-&gt;ContextRecord;&lt;br /&gt;&lt;br /&gt;    if(in_code_heap_p(c-&gt;Eip))&lt;br /&gt;        signal_callstack_top = (void*)c-&gt;Esp;&lt;br /&gt;    else&lt;br /&gt;        signal_callstack_top = NULL;&lt;br /&gt;&lt;br /&gt;    if(e-&gt;ExceptionCode == EXCEPTION_ACCESS_VIOLATION)&lt;br /&gt;    {&lt;br /&gt;        signal_fault_addr = e-&gt;ExceptionInformation[1];&lt;br /&gt;        c-&gt;Eip = (CELL)memory_signal_handler_impl;&lt;br /&gt;    }&lt;br /&gt;    else if(e-&gt;ExceptionCode == EXCEPTION_FLT_DIVIDE_BY_ZERO&lt;br /&gt;            || e-&gt;ExceptionCode == EXCEPTION_INT_DIVIDE_BY_ZERO)&lt;br /&gt;    {&lt;br /&gt;        signal_number = ERROR_DIVIDE_BY_ZERO;&lt;br /&gt;        c-&gt;Eip = (CELL)divide_by_zero_signal_handler_impl;&lt;br /&gt;    }&lt;br /&gt;    else&lt;br /&gt;    {&lt;br /&gt;        signal_number = 11;&lt;br /&gt;        c-&gt;Eip = (CELL)misc_signal_handler_impl;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    return EXCEPTION_CONTINUE_EXECUTION;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void memory_signal_handler_impl(void)&lt;br /&gt;{&lt;br /&gt;    memory_protection_error(signal_fault_addr,signal_callstack_top);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void divide_by_zero_signal_handler_impl(void)&lt;br /&gt;{&lt;br /&gt;    general_error(ERROR_DIVIDE_BY_ZERO,F,F,signal_callstack_top);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void misc_signal_handler_impl(void)&lt;br /&gt;{&lt;br /&gt;    signal_error(signal_number,signal_callstack_top);&lt;br /&gt;}&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37596622-1401635304800556805?l=code-factor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://code-factor.blogspot.com/feeds/1401635304800556805/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=37596622&amp;postID=1401635304800556805' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/1401635304800556805'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/1401635304800556805'/><link rel='alternate' type='text/html' href='http://code-factor.blogspot.com/2007/09/windows-seh-revisited.html' title='Windows SEH Revisited'/><author><name>Doug Coleman</name><uri>http://www.blogger.com/profile/09416611825627835802</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-37596622.post-7988781081642929887</id><published>2007-09-08T18:23:00.000-07:00</published><updated>2007-09-08T22:08:41.867-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='win32'/><category scheme='http://www.blogger.com/atom/ns#' term='destructors'/><title type='text'>Destructors in Factor</title><content type='html'>After spending way too much time trying to perfect Factor's win32 api code, I wrote a word I should have written long ago: &lt;code&gt;with-destructors&lt;/code&gt;.  What this allows you to do is allocate a system resource, add a destructor, and automate the resource cleanup, even when an exception is thrown.  Take this buggy code as an example of resource leaks.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;TUPLE: mallocs one two three ;&lt;br /&gt;&lt;br /&gt;: three-mallocs-buggy ( -- obj )&lt;br /&gt;    100 malloc&lt;br /&gt;    200 malloc&lt;br /&gt;    300 malloc&lt;br /&gt;    \ mallocs construct-boa ;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Any one of these calls to malloc could fail.  If the first one fails, an error is thrown and no resources are lost.  However, if the second or third fail, nothing will ever clean up after the successful allocations, and resources are leaked!&lt;br /&gt;&lt;br /&gt;One alternative is to put each malloc into a tuple slot as they succeed.  This solution is quite verbose and needs an extra cleanup word (boilerplate).&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;TUPLE: mallocs one two three ;&lt;br /&gt;&lt;br /&gt;: cleanup-mallocs ( mallocs -- )&lt;br /&gt;    dup mallocs-one [ free ] when*&lt;br /&gt;    dup mallocs-two [ free ] when*&lt;br /&gt;    dup mallocs-three [ free ] when* ;&lt;br /&gt;&lt;br /&gt;: three-mallocs-verbose ( -- obj )&lt;br /&gt;    \ mallocs construct-empty&lt;br /&gt;    f&lt;br /&gt;    [&lt;br /&gt;        drop&lt;br /&gt;        100 malloc over set-mallocs-one&lt;br /&gt;        200 malloc over set-mallocs-two&lt;br /&gt;        300 malloc over set-mallocs-three&lt;br /&gt;        t&lt;br /&gt;    ] [&lt;br /&gt;        [ cleanup-mallocs ] unless&lt;br /&gt;    ] cleanup ;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;We need a boolean because we only want to cleanup up resources if something fails.  See how we save each malloc as it's created?  Otherwise it could get lost.  This tedious method is how much of the win32 native io (io completion ports) is implemented right now.  Notice that the cleanup word doesn't even set all the slots in the tuple to f, so if you called cleanup-mallocs twice somehow, your program would hopefully crash (sooner rather than later!).  More boilerplate would fix it.&lt;br /&gt;&lt;br /&gt;Instead, let's wrap each returned resource in a destructor.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;TUPLE: mallocs one two three ;&lt;br /&gt;&lt;br /&gt;: three-mallocs ( -- obj )&lt;br /&gt;    [&lt;br /&gt;        100 malloc dup [ free ] f add-destructor&lt;br /&gt;        200 malloc dup [ free ] f add-destructor&lt;br /&gt;        300 malloc dup [ free ] f add-destructor&lt;br /&gt;        \ mallocs construct-boa&lt;br /&gt;    ] with-destructors ;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Ah!  This is marginally more work than the first example, but is 100% correct.  The word &lt;code&gt;add-destructor ( obj quot always? -- )&lt;/code&gt; takes an arbitrary object, a destructor quotation (some code), and a boolean to tell it under which circumstances to cleanup the resource.  Calling &lt;code&gt;add-destructor&lt;/code&gt; with &lt;code&gt;t&lt;/code&gt; will always clean up the resource; calling it with &lt;code&gt;f&lt;/code&gt; will only clean up if the quotation passed to &lt;code&gt;with-destructors&lt;/code&gt; fails.  Thus, a cleanup routine is required elsewhere, but we can worry about that later.  The duplicated code could be factored out if you find yourself using it often, but I have chosen not to here because of the tricky boolean flag for &lt;code&gt;add-destructor&lt;/code&gt;.  In practice, I need to save about half of the resources and to destroy the other half very soon after creation.  However, it still might be best to factor out the duplicate code:&lt;br /&gt;&lt;pre&gt;: destruct-malloc-on-fail ( obj -- ) [ free ] f add-destructor ;&lt;/pre&gt;&lt;br /&gt;This example is trivial compared to using win32 for memory mapped io, which requires: escalating two privileges, opening a file, creating a file mapping, calling map view of file, and lowering both privileges, any of which could fail!  This series of calls allocates two file handles and requires unmapping the file during cleanup.  The four calls to the privileges routines call malloc, and this could also leak resources!&lt;br /&gt;&lt;br /&gt;This complexity is the norm when writing code for performance and reliability in win32.&lt;br /&gt;&lt;br /&gt;The destructor implementation is simple:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;USING: continuations kernel namespaces sequences vectors ;&lt;br /&gt;IN: destructors&lt;br /&gt;&lt;br /&gt;SYMBOL: destructors&lt;br /&gt;SYMBOL: errored?&lt;br /&gt;TUPLE: destructor obj quot always? ;&lt;br /&gt;&lt;br /&gt;&amp;lt;PRIVATE&lt;br /&gt;&lt;br /&gt;: filter-destructors ( -- )&lt;br /&gt;    errored? get [&lt;br /&gt;        destructors [ [ destructor-always? ] subset ] change&lt;br /&gt;    ] unless ;&lt;br /&gt;&lt;br /&gt;: call-destructors ( -- )&lt;br /&gt;    destructors get [&lt;br /&gt;        dup destructor-obj swap destructor-quot call&lt;br /&gt;    ] each ;&lt;br /&gt;&lt;br /&gt;PRIVATE&gt;&lt;br /&gt;&lt;br /&gt;: add-destructor ( obj quot always? -- )&lt;br /&gt;    \ destructor construct-boa destructors [ ?push ] change ;&lt;br /&gt;&lt;br /&gt;: with-destructors ( quot -- )&lt;br /&gt;    [&lt;br /&gt;        [ call ] [ errored? on ] recover&lt;br /&gt;        filter-destructors call-destructors&lt;br /&gt;        errored? get [ rethrow ] when&lt;br /&gt;    ] with-scope ; inline&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;code&gt;with-destructors&lt;/code&gt; and &lt;code&gt;add-destructor&lt;/code&gt; make up the main interface.  If the quotation passed to &lt;code&gt;with-destructors&lt;/code&gt; succeeds, the always-destructs are filtered out of the destructor sequence, and &lt;code&gt;call-destructors&lt;/code&gt; destroys the objects that are left.&lt;br /&gt;&lt;br /&gt;Hopefully this library will make dealing with system resources in Factor all but trivial.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37596622-7988781081642929887?l=code-factor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://code-factor.blogspot.com/feeds/7988781081642929887/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=37596622&amp;postID=7988781081642929887' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/7988781081642929887'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/7988781081642929887'/><link rel='alternate' type='text/html' href='http://code-factor.blogspot.com/2007/09/destructors-in-factor.html' title='Destructors in Factor'/><author><name>Doug Coleman</name><uri>http://www.blogger.com/profile/09416611825627835802</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-37596622.post-6228699858888886000</id><published>2007-08-31T23:10:00.000-07:00</published><updated>2007-09-01T00:20:22.244-07:00</updated><title type='text'>Managed malloc and free</title><content type='html'>The Factor Windows backend has to use manual memory management for Windows runtime callbacks.  This opens up a small amount of code to double free errors.  And crashes.  But Factor is better than that; why crash when you can easily prevent it?&lt;br /&gt;&lt;br /&gt;The new malloc adds an entry to a global hashtable, mallocs, whenever new memory is allocated.  Upon calling free, it checks that the buffer is still allocated before making the actual memory cleanup call.  This managed version of malloc is now the default.  To get plain old libc memory allocation, call (malloc) and (free), though the overhead is negligible compared to having your program crash.&lt;br /&gt;&lt;br /&gt;Does it work?  Yes!  Opening a new UI window adds two new mallocs, and closing it takes them away.  After running test-all there were two mallocs that went unchecked; both were bugs and corrected in five minutes by inspection.  Finally, there is no way to call free twice on a pointer using the managed malloc/free since, if that pointer is not in the mallocs hashtable, an error is thrown.  Now, to check that you balance malloc/free calls, you can run your code and make sure the malloc hash doesn't have extra entries.  Here's a snippet to count the entries:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;mallocs get-global assoc-size .&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;The code works for calloc and realloc as well, and can be perused &lt;a href="http://factorcode.org/repos/Factor/core/libc/libc.factor"&gt;here&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37596622-6228699858888886000?l=code-factor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://code-factor.blogspot.com/feeds/6228699858888886000/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=37596622&amp;postID=6228699858888886000' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/6228699858888886000'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/6228699858888886000'/><link rel='alternate' type='text/html' href='http://code-factor.blogspot.com/2007/08/managed-malloc-and-free.html' title='Managed malloc and free'/><author><name>Doug Coleman</name><uri>http://www.blogger.com/profile/09416611825627835802</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-37596622.post-697801287102871023</id><published>2007-06-20T22:58:00.000-07:00</published><updated>2007-08-02T14:26:28.088-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Factor'/><category scheme='http://www.blogger.com/atom/ns#' term='Romannumerals'/><title type='text'>Roman Numeral Conversion in Factor</title><content type='html'>I whipped up some words to convert integers to Roman numerals.  The word &lt;code&gt;&amp;lt;PRIVATE&lt;/code&gt; changes the &lt;code&gt;IN:&lt;/code&gt; vocabulary to &lt;code&gt;roman.private&lt;/code&gt; to hide the implementation from the library user.  Of course you can access these private words, like all words in Factor, with a &lt;code&gt;USE:&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;The algorithm is simple.  Going from high to low, iterate the Roman numeral values and &lt;code&gt;/mod&lt;/code&gt; (integer division with remainder) each with the input, outputting the divisor and replacing the input with the remainder.  This algorithm treats 4s and 9s as digits, just as it treats single letters as digits (i, v, x, etc).  Without the 4s and 9s, you end up getting longer answers that, while logical, are wrong, e.g. 9 is "ix", not "viv".  (I found this bug in the first iteration while writing unit tests.)&lt;br /&gt;&lt;br /&gt;The words we care about, &lt;code&gt;&gt;roman&lt;/code&gt; and &lt;code&gt;&gt;ROMAN&lt;/code&gt;, are placed &lt;code&gt;IN: roman&lt;/code&gt; because of the &lt;code&gt;PRIVATE&gt;&lt;/code&gt; word, which drops back to the public vocabulary &lt;code&gt;roman&lt;/code&gt;.  The &lt;bold&gt;&gt;&lt;/bold&gt; in a word's name is a convention for words that do conversions; the parentheses around the word &lt;code&gt;(&gt;roman)&lt;/code&gt; mean it's an implementation word; you should never have a &lt;code&gt;(&gt;roman)&lt;/code&gt; without also having a &lt;code&gt;&gt;roman&lt;/code&gt;.  Picking these names is done purely by convention--the only forbidden word names are numbers and words that start with a ", which parse as strings.   Everything until whitespace is a word name.&lt;br /&gt;&lt;br /&gt;The conversion from Roman numerals back to integers and roman+, roman*, etc are in &lt;a href="http://factorcode.org/repos/Factor/extra/roman/roman.factor"&gt;roman.factor&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;USING: arrays assocs kernel math math.vectors namespaces&lt;br /&gt;quotations sequences sequences.private strings ;&lt;br /&gt;IN: roman&lt;br /&gt;&lt;br /&gt;&amp;lt;PRIVATE&lt;br /&gt;&lt;br /&gt;: roman-digits ( -- seq )&lt;br /&gt;    { "m" "cm" "d" "cd" "c" "xc" "l" "xl" "x" "ix" "v" "iv" "i" } ;&lt;br /&gt;&lt;br /&gt;: roman-values ( -- seq )&lt;br /&gt;    { 1000 900 500 400 100 90 50 40 10 9 5 4 1 } ;&lt;br /&gt;&lt;br /&gt;TUPLE: roman-range-error n ;&lt;br /&gt;&lt;br /&gt;: roman-range-check ( n -- )&lt;br /&gt;    dup 1 3999 between? [&lt;br /&gt;        drop&lt;br /&gt;    ] [&lt;br /&gt;        &amp;lt;roman-range-error&gt; throw&lt;br /&gt;    ] if ;&lt;br /&gt;&lt;br /&gt;: (&gt;roman) ( n -- )&lt;br /&gt;    roman-values roman-digits [&lt;br /&gt;        &gt;r /mod swap r&gt; &amp;lt;repetition&gt; concat %&lt;br /&gt;    ] 2each drop ;&lt;br /&gt;&lt;br /&gt;PRIVATE&gt;&lt;br /&gt;&lt;br /&gt;: &gt;roman ( n -- str )&lt;br /&gt;    dup roman-range-check [&lt;br /&gt;        (&gt;roman)&lt;br /&gt;    ] "" make ;&lt;br /&gt;&lt;br /&gt;: &gt;ROMAN ( n -- str ) &gt;roman &gt;upper ;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37596622-697801287102871023?l=code-factor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://code-factor.blogspot.com/feeds/697801287102871023/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=37596622&amp;postID=697801287102871023' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/697801287102871023'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/697801287102871023'/><link rel='alternate' type='text/html' href='http://code-factor.blogspot.com/2007/06/roman-numeral-conversion-in-factor.html' title='Roman Numeral Conversion in Factor'/><author><name>Doug Coleman</name><uri>http://www.blogger.com/profile/09416611825627835802</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-37596622.post-2877629337890607034</id><published>2007-04-27T00:35:00.000-07:00</published><updated>2007-06-20T20:08:45.301-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ARM'/><category scheme='http://www.blogger.com/atom/ns#' term='SEH'/><category scheme='http://www.blogger.com/atom/ns#' term='WinCE'/><category scheme='http://www.blogger.com/atom/ns#' term='WM5'/><category scheme='http://www.blogger.com/atom/ns#' term='Factor'/><title type='text'>Windows CE SEH for ARM with GCC</title><content type='html'>Structure exception handling (SEH) is a low-level way to handle software and hardware exceptions in Microsoft Windows.  Other exception handling mechanisms are built on top of SEH.  Factor uses SEH in order to report stack underflow/overflow errors on Windows.  Microsoft Visual Studio (VS) would usually take care of the tricky implementation details by supplying &lt;code&gt;__try&lt;/code&gt;, &lt;code&gt;__except&lt;/code&gt;, and &lt;code&gt;__finally&lt;/code&gt; keywords, but Factor's compiler relies on globally assigning the datastack and retainstack to specific registers, a feature which VS does not support.  Conversely, GCC does not support &lt;code&gt;__try&lt;/code&gt;, so we are left to implement the exception handler in assembly.&lt;br /&gt;&lt;br /&gt;The internal implementation is both OS and architecture dependent, meaning that we have to support SEH on Windows NT on x86, and Windows CE for x86 and ARM.  Digging through MSDN leads to a page called &lt;a href="http://msdn2.microsoft.com/en-us/library/ms925504.aspx"&gt;SEH in RISC Environments&lt;/a&gt;, where &lt;a href="http://en.wikipedia.org/wiki/RISC"&gt;RISC&lt;/a&gt; standing for "Reduced Instruction Set Computer".  (You are just supposed to know that ARM is RISC and x86 is &lt;a href="http://en.wikipedia.org/wiki/Complex_instruction_set_computer"&gt;CISC&lt;/a&gt;).  I recommend reading about SEH on MSDN, but it basically says that SEH on RISC uses Virtual Unwinding and that you will need to set the PDATA structure for your function in order to set up the exception handler.  Virtual unwinding traverses the framestack until it finds a frame with an exception handler, at which time it will actually unwind the framestack to that point and call the exception handler.&lt;br /&gt;&lt;br /&gt;All we need to do is define &lt;code&gt;void run_toplevel(void){...}&lt;/code&gt; to install our &lt;code&gt;exception_handler&lt;/code&gt; and call Factor's "main" subroutine, &lt;code&gt;run&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;vm/os-windows-ce.c&lt;/i&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;long exception_handler(PEXCEPTION_RECORD rec, void *frame, void *ctx, void *disp&lt;br /&gt;atch)&lt;br /&gt;{&lt;br /&gt;    memory_protection_error(&lt;br /&gt;        rec-&gt;ExceptionInformation[1] &amp; 0x1ffffff, // mask off process slot&lt;br /&gt;        native_stack_pointer());&lt;br /&gt;    return -1; /* unreachable */&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;i&gt;vm/os-windows-ce-arm.S&lt;/i&gt;&lt;pre&gt;&lt;br /&gt; .text&lt;br /&gt;&lt;br /&gt; .globl run_toplevel&lt;br /&gt;&lt;br /&gt; .word exception_handler&lt;br /&gt; .word 0&lt;br /&gt;&lt;br /&gt;run_toplevel:&lt;br /&gt; ldr pc, _Prun&lt;br /&gt;&lt;br /&gt;_Prun:  .word run&lt;br /&gt;&lt;br /&gt; .section .pdata&lt;br /&gt; .word run_toplevel&lt;br /&gt; .word 0xc0000002 | (0xFFFFF &amp;lt;&amp;lt; 8)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The C code passes the memory fault address, stored in &lt;code&gt;ExceptionInformation[1]&lt;/code&gt;, and the native stack pointer (another assembly function that simply moves ESP -&gt; EAX) to a function that converts the fault address into a Factor stack error message, e.g. "Datastack underflow".  The fault address is actually a slotized address, meaning that it is relative to some process slot which must be masked off.  Annoyingly, the exceptions happen at different address ranges on the emulator and on a real mobile device.  Slotized addresses in this context are not well documented on MSDN, but the Windows CE Blog Team quickly answered my email.  (Thanks!)&lt;br /&gt;&lt;br /&gt;The assembly code is mostly boilerplate.  The &lt;b&gt;.text&lt;/b&gt; directive starts us in the executable code section.  We're going to be exporting &lt;code&gt;void run_toplevel(void){...}&lt;/code&gt; in order to call it from C, so we declare it as a &lt;b&gt;.globl&lt;/b&gt; and start the definition.  We simply call our "main" function, &lt;code&gt;void run(void){...}&lt;/code&gt;, which is the platform-dependent &lt;code&gt;run&lt;/code&gt; function to start the Factor interpreter.&lt;br /&gt;&lt;br /&gt;Hopefully, if someone else has to do this it will take much less time.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37596622-2877629337890607034?l=code-factor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://code-factor.blogspot.com/feeds/2877629337890607034/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=37596622&amp;postID=2877629337890607034' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/2877629337890607034'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/2877629337890607034'/><link rel='alternate' type='text/html' href='http://code-factor.blogspot.com/2007/04/windows-ce-seh-for-arm-with-gcc.html' title='Windows CE SEH for ARM with GCC'/><author><name>Doug Coleman</name><uri>http://www.blogger.com/profile/09416611825627835802</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-37596622.post-6700677976005788717</id><published>2007-04-12T11:10:00.000-07:00</published><updated>2007-04-12T14:40:53.887-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Cygwin'/><category scheme='http://www.blogger.com/atom/ns#' term='Windows'/><category scheme='http://www.blogger.com/atom/ns#' term='SEH'/><category scheme='http://www.blogger.com/atom/ns#' term='Factor'/><title type='text'>Building Factor in Cygwin now supported</title><content type='html'>Factor has only compiled on MinGW since we made the Windows Factor port a couple of years ago.  The problem with &lt;a href="http://www.cygwin.com/"&gt;Cygwin&lt;/a&gt; has been its dependence on cygwin1.dll, which slows your program.  Also, the Cygwin installer doesn't add it to your path, so that is one more step (and not standard).   Additionally, structured exception handling (&lt;a href="http://www.gamedev.net/reference/programming/features/sehbasics/"&gt;SEH&lt;/a&gt;) works the first time but hangs on the second time it triggers with the dll, e.g. on the second stack underflow.&lt;br /&gt;&lt;br /&gt;However, Eric Mertens, a new Factor user, found a compiler flag to disable cygwin.dll:&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;-mno-cygwin&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Three cheers for new users!&lt;br /&gt;&lt;br /&gt;This flag fixed both the dll dependency and the SEH bug, allowing all unit tests to pass.  The only other change I had to make for the port was to &lt;span style="font-style: italic;"&gt;#include &amp;lt;wchar.h&gt;&lt;/span&gt;  for the compiler to find &lt;span style="font-weight: bold;"&gt;wcslen&lt;/span&gt;&lt;span style="font-weight: bold;"&gt;()&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;So now you can compile Factor with either MinGW or Cygwin, and both versions are officially supported.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37596622-6700677976005788717?l=code-factor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://code-factor.blogspot.com/feeds/6700677976005788717/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=37596622&amp;postID=6700677976005788717' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/6700677976005788717'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/6700677976005788717'/><link rel='alternate' type='text/html' href='http://code-factor.blogspot.com/2007/04/building-factor-in-cygwin-now-supported.html' title='Building Factor in Cygwin now supported'/><author><name>Doug Coleman</name><uri>http://www.blogger.com/profile/09416611825627835802</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-37596622.post-2176334608232290334</id><published>2006-12-30T11:12:00.000-08:00</published><updated>2006-12-30T11:49:02.957-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Furnace'/><category scheme='http://www.blogger.com/atom/ns#' term='wee-url.com'/><category scheme='http://www.blogger.com/atom/ns#' term='Factor'/><title type='text'>Serialization To Disk</title><content type='html'>For fun, I wrote a &lt;a href="http://tinyurl.com/"&gt;TinyURL&lt;/a&gt; clone in Factor.  You can try it out at &lt;a href="http://wee-url.com/"&gt;http://wee-url.com&lt;/a&gt;.  I needed a way to save the data to a file, so I used  &lt;code&gt;libs/serialize&lt;/code&gt; to serialize the hashtable containing the links.  There was yet another bug involving vector literals -- the serialization code would reuse a vector, so the first time you serialized the output is correct, but the next time it would produce garbage.&lt;br /&gt;&lt;pre&gt;  scratchpad SYMBOL: aa H{ { 1 2 } } aa set&lt;br /&gt;  scratchpad aa get .&lt;br /&gt;H{ { 1 2 } }&lt;br /&gt;  scratchpad aa get [ [ serialize ] with-serialized ] string-out .&lt;br /&gt;"hp\0\0\0\u0001Dap\0\0\0\u0001Ep\0\0\0\u0001\u0001ap\0\0..."&lt;br /&gt;  scratchpad aa get [ [ serialize ] with-serialized ] string-out .&lt;br /&gt;"op\0\0\0\u0001:"&lt;/pre&gt;The fix was to change a &lt;code&gt;V{ }&lt;/code&gt; to a &lt;code&gt;V{ } clone&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Serialization to Disk Code&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;: save-fstore ( variable path -- )&lt;br /&gt;    &lt;file-writer&gt; [ [ get serialize ] with-serialized ] with-stream ;&lt;br /&gt;&lt;br /&gt;: load-fstore ( variable path -- )&lt;br /&gt;    dup exists? [&lt;br /&gt;        &lt;file-reader&gt; [ [ deserialize ] with-serialized ] with-stream&lt;br /&gt;        swap&lt;br /&gt;    ] [&lt;br /&gt;        drop &gt;r H{ } clone r&gt;&lt;br /&gt;    ] if set-global ;&lt;br /&gt;&lt;br /&gt;: fstore-set ( variable fstore -- )&lt;br /&gt;    get &gt;r [ get ] keep r&gt; set-hash ;&lt;br /&gt;&lt;br /&gt;: fstore-get ( default variable fstore -- )&lt;br /&gt;    get dupd hash* [ swap set-global drop ] [ drop set-global ] if ;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;code&gt;save-fstore&lt;/code&gt; and &lt;code&gt;load-fstore&lt;/code&gt; save/load a hashtable to disk.  The hashtable keys are variable names and the values are the variable values.  &lt;code&gt;fstore-set&lt;/code&gt; puts your variable in the hashtable, so you should call this directly before saving to disk.  &lt;code&gt;fstore-get&lt;/code&gt; loads the data from your store.  If there is no fstore or if the key does not exist, it uses your default value.&lt;br /&gt;&lt;br /&gt;The loading code is pretty simple:&lt;br /&gt;&lt;pre&gt;: fstore-name "wee-url.fstore" ;&lt;br /&gt;SYMBOL: wee-shortcuts&lt;br /&gt;SYMBOL: wee-fstore&lt;br /&gt;wee-fstore fstore-name load-fstore&lt;br /&gt;H{ } clone wee-shortcuts wee-fstore fstore-get&lt;/pre&gt;The variable &lt;code&gt;wee-shortcuts&lt;/code&gt; stores the encoded URLs, while &lt;code&gt;wee-fstore&lt;/code&gt; is the hashtable that the above code reads/writes to disk.&lt;br /&gt;&lt;br /&gt;Finally, to write the store:&lt;br /&gt;&lt;pre&gt;: save-shortcuts ( -- )&lt;br /&gt;    wee-shortcuts wee-fstore fstore-set&lt;br /&gt;    wee-fstore fstore-name save-fstore ;&lt;/pre&gt;&lt;br /&gt;Notice that we add &lt;code&gt;wee-shortcuts&lt;/code&gt; to the hashtable before saving to disk.&lt;br /&gt;&lt;br /&gt;I'm going to rewrite the wee-url code to use &lt;span style="font-weight: bold;"&gt;Furnace&lt;/span&gt; instead of callback-responder.  Once I get it looking nice, I'll show you how it works.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37596622-2176334608232290334?l=code-factor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://code-factor.blogspot.com/feeds/2176334608232290334/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=37596622&amp;postID=2176334608232290334' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/2176334608232290334'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/2176334608232290334'/><link rel='alternate' type='text/html' href='http://code-factor.blogspot.com/2006/12/serialization-to-disk.html' title='Serialization To Disk'/><author><name>Doug Coleman</name><uri>http://www.blogger.com/profile/09416611825627835802</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-37596622.post-1425713299260911790</id><published>2006-11-23T15:50:00.000-08:00</published><updated>2006-11-30T18:19:51.634-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='darcs'/><category scheme='http://www.blogger.com/atom/ns#' term='mac'/><category scheme='http://www.blogger.com/atom/ns#' term='Factor'/><title type='text'>Compiling Factor on the Intel Mac</title><content type='html'>To compile Factor for Mac you will need:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Installation DVDs that came with your Mac (for &lt;span style="font-weight: bold;"&gt;X11&lt;/span&gt;)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;darwin-ports&lt;/li&gt;&lt;/ul&gt;&lt;ol&gt;&lt;li&gt;Run the installation disk that came with your Mac in order to install &lt;span style="font-weight: bold;"&gt;X11&lt;/span&gt;.  Since Apple does not offer X11 as a download for OS X 10.4 for Intel computers, you have to install from the DVD.&lt;/li&gt;&lt;li&gt;Download the &lt;a href="http://glozer.net/darcs/"&gt;darcs binary&lt;/a&gt; and place it in &lt;span style="font-weight: bold;"&gt;/usr/local/bin&lt;/span&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Open terminal (Finder-&gt;Applications-&gt;Utilities-&gt;Terminal) and go to your home directory.  To get the Factor source code, run:&lt;br /&gt;&lt;pre&gt;darcs get http://factorcode.org/repos&lt;/pre&gt;&lt;/li&gt;&lt;li&gt;To compile, move into the directory darcs created with &lt;span style="font-weight: bold;"&gt;cd repos/Factor&lt;/span&gt; and run the command &lt;span style="font-weight: bold;"&gt;make macosx-x86&lt;/span&gt;&lt;/li&gt;&lt;li&gt;Download the boot image at &lt;a href="http://factorcode.org/images/latest/boot.image.pentium4"&gt;&lt;span style="font-weight: bold;"&gt;http://factorcode.org/images/latest/boot.image.pentium4&lt;br /&gt;&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;./f boot.image.pentium4&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;make macosx.app&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-style: italic;"&gt;(Optional)&lt;/span&gt; Create a shell script to start Factor in your terminal window.  I called mine go.&lt;pre&gt;#!/bin/sh&lt;br /&gt;DIR=~/repos/Factor/Factor.app/Contents&lt;br /&gt;$DIR/MacOS/Factor $@&lt;br /&gt;&lt;br /&gt;Save the file and run &lt;span style="font-weight: bold;"&gt;chmod a+x go&lt;/span&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li&gt;&lt;span&gt;&lt;span style="font-style: italic;"&gt;(Optional)&lt;/span&gt; You can instead run &lt;span style="font-weight: bold;"&gt;open Factor.app&lt;/span&gt; and use &lt;span style="font-weight: bold;"&gt;Console.app&lt;/span&gt; to view runtime output.&lt;br /&gt;&lt;/span&gt;&lt;/li&gt;&lt;/ol&gt;To start the UI, run &lt;span style="font-weight: bold;"&gt;./go&lt;/span&gt; or &lt;span style="font-weight: bold;"&gt;open Factor.app&lt;/span&gt;; run &lt;span style="font-weight: bold;"&gt;./f&lt;/span&gt; to start Factor in the shell.  If you run Factor in the terminal, I suggest using rlwrap, which adds a history buffer&lt;br /&gt;&lt;br /&gt;Now you can code anything!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37596622-1425713299260911790?l=code-factor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://code-factor.blogspot.com/feeds/1425713299260911790/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=37596622&amp;postID=1425713299260911790' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/1425713299260911790'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/1425713299260911790'/><link rel='alternate' type='text/html' href='http://code-factor.blogspot.com/2006/11/compiling-factor-on-intel-mac-mac-pro.html' title='Compiling Factor on the Intel Mac'/><author><name>Doug Coleman</name><uri>http://www.blogger.com/profile/09416611825627835802</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-37596622.post-8810469431316508311</id><published>2006-11-16T11:07:00.000-08:00</published><updated>2006-11-16T12:09:43.801-08:00</updated><title type='text'>Bootstrapping, reset-modified, and F8</title><content type='html'>Factor's module system has the ability to reload changed files from the UI, in the correct order, by calling &lt;code&gt;reload-modules&lt;/code&gt;, which is bound to &lt;b&gt;F8&lt;/b&gt;.  When you &lt;a href="http://factorcode.org/images/latest"&gt;download an image&lt;/a&gt; someone else created, the timestamps won't match up and when you reload the modules it will try to reload &lt;i&gt;all&lt;/i&gt; of the Factor library.  You don't want this.&lt;br /&gt;&lt;br /&gt;There are several ways to fix the problem.  First, you could generate a new image &lt;code&gt;USE: image "x86" make-image&lt;/code&gt; and bootstrap again.  Another way is to call &lt;code&gt;reset-modified&lt;/code&gt; and then &lt;code&gt;save&lt;/code&gt; the image.  However, until you bootstrap from an image that you made yourself, will have the same problem.&lt;br /&gt;&lt;br /&gt;If you make a module, don't forget to &lt;code&gt;PROVIDE: contrib/my-module ;&lt;/code&gt; at the very least, or else &lt;b&gt;F8&lt;/b&gt; will not reload the changes you make after &lt;code&gt;require&lt;/code&gt;ing it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37596622-8810469431316508311?l=code-factor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://code-factor.blogspot.com/feeds/8810469431316508311/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=37596622&amp;postID=8810469431316508311' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/8810469431316508311'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/8810469431316508311'/><link rel='alternate' type='text/html' href='http://code-factor.blogspot.com/2006/11/bootstrapping-reset-modified-and-f8.html' title='Bootstrapping, reset-modified, and F8'/><author><name>Doug Coleman</name><uri>http://www.blogger.com/profile/09416611825627835802</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-37596622.post-6989440273065950841</id><published>2006-11-15T15:04:00.000-08:00</published><updated>2007-02-07T08:58:21.371-08:00</updated><title type='text'>Using .factor-boot-rc and .factor-rc</title><content type='html'>Update: I changed the syntax to work with the new directory layout.&lt;br /&gt;&lt;br /&gt;The two resource files Factor tries to run, if they exist, are &lt;b&gt;.factor-boot-rc&lt;/b&gt; and &lt;b&gt;.factor-rc&lt;/b&gt;, at bootstrap and run-time respectively.  You have to create these files yourself and place them in your home directory, e.g. the directory returned by the &lt;code&gt;home&lt;/code&gt; word.&lt;br /&gt;&lt;br /&gt;If I used &lt;a href="http://macromates.com/"&gt;Textmate&lt;/a&gt; on a Mac, my &lt;b&gt;.factor-boot-rc&lt;/b&gt; would look like this:&lt;pre&gt;USING: modules namespaces ;&lt;br /&gt;REQUIRES: libs/textmate ;&lt;/pre&gt;&lt;br&gt;Here is the boot file for &lt;a href="http://www.vim.org/index.php"&gt;gvim&lt;/a&gt; in Windows XP:&lt;pre&gt;USING: modules namespaces ;&lt;br /&gt;REQUIRES: libs/vim ;&lt;br /&gt;USE: vim&lt;br /&gt;"c:\\program files\\vim\\vim70\\gvim.exe" vim-path set-global&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now for loading libraries.  Some of the modules in &lt;b&gt;contrib/&lt;/b&gt; require that you load your library before running.  Here are three examples of a &lt;b&gt;.factor-rc&lt;/b&gt; that loads libraries.&lt;br /&gt;&lt;br /&gt;Loading &lt;a href="http://www.postgresql.org/"&gt;PostgreSQL&lt;/a&gt; on a Mac:&lt;pre&gt;USING: alien ;&lt;br /&gt;"postgresql" "/opt/local/lib/postgresql81/libpq.dylib" "cdecl" add-library&lt;/pre&gt;&lt;br /&gt;PostgreSQL and &lt;a href="http://www.sqlite.org/"&gt;SQLite&lt;/a&gt; on Windows:&lt;pre&gt;USING: alien ;&lt;br /&gt;"postgresql" "C:\\Program Files\\PostgreSQL\\8.1\\bin\\libpq.dll"&lt;br /&gt;    "cdecl" add-library&lt;br /&gt;"sqlite" "C:\\Program Files\\sqlite\\sqlite3.dll"&lt;br /&gt;    "cdecl" add-library&lt;/pre&gt;SQLite on Linux:&lt;pre&gt;USING: alien ;&lt;br /&gt;"sqlite" "libsqlite3.so" "cdecl" add-library&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37596622-6989440273065950841?l=code-factor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://code-factor.blogspot.com/feeds/6989440273065950841/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=37596622&amp;postID=6989440273065950841' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/6989440273065950841'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/6989440273065950841'/><link rel='alternate' type='text/html' href='http://code-factor.blogspot.com/2006/11/using-factor-boot-rc-and-factor-rc.html' title='Using .factor-boot-rc and .factor-rc'/><author><name>Doug Coleman</name><uri>http://www.blogger.com/profile/09416611825627835802</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-37596622.post-7107737895177230587</id><published>2006-11-14T19:28:00.000-08:00</published><updated>2007-03-21T07:14:56.912-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='MSYS'/><category scheme='http://www.blogger.com/atom/ns#' term='darcs'/><category scheme='http://www.blogger.com/atom/ns#' term='MinGW'/><category scheme='http://www.blogger.com/atom/ns#' term='Windows'/><category scheme='http://www.blogger.com/atom/ns#' term='Factor'/><category scheme='http://www.blogger.com/atom/ns#' term='Programming Languages'/><title type='text'>Compiling Factor on Windows</title><content type='html'>To compile &lt;a href="http://factorcode.org"&gt;Factor&lt;/a&gt; you will need:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;MinGW&lt;br /&gt;&lt;/li&gt;&lt;li&gt;MSYS&lt;br /&gt;&lt;/li&gt;&lt;li&gt;darcs&lt;/li&gt;&lt;li&gt;freetype6.dll&lt;/li&gt;&lt;li&gt;zlib1.dll&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Download &lt;span style="TEXT-DECORATION: underline"&gt;&lt;/span&gt;&lt;a href="http://superb-west.dl.sourceforge.net/sourceforge/mingw/MinGW-5.0.3.exe"&gt;MinGW&lt;/a&gt; and install it.&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;Download &lt;a href="http://prdownloads.sf.net/mingw/MSYS-1.0.11-2004.04.30-1.exe?download"&gt;MSYS&lt;/a&gt; and install it. Make sure to tell the batch script about your MinGW install.&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;Download the latest package version of &lt;a href="http://zooko.com/darcs/darcsdir-w32-1.0.7.zip"&gt;darcs&lt;/a&gt; and unzip it to c:\darcsdir-w32. darcs 1.0.7 is &lt;span style="FONT-WEIGHT: bold"&gt;broken&lt;/span&gt; on Windows, so you must also grab the &lt;a href="http://glozer.net/darcs/darcs-1.0.8-win32.zip"&gt;1.0.8 binary&lt;/a&gt; and copy it into your darcs install directory over the broken version.&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;Set your &lt;b&gt;path&lt;/b&gt; to include darcs. Run:&lt;br /&gt;&lt;pre&gt;start-&gt;Control Panel-&gt;System-&gt;Advanced-&gt;Environment Variables&lt;/pre&gt;&lt;br /&gt;In &lt;b&gt;System Variables&lt;/b&gt;, edit PATH, and append c:\darcsdir-w32 so that your path resembles:&lt;br /&gt;&lt;pre&gt;c:\Program Files\sqlite;c:\darcsdir-w32&lt;/pre&gt;&lt;br /&gt;Start cmd.exe from the Run box and try out your darcs installation:&lt;br /&gt;&lt;pre&gt;C:\Documents and Settings\Administrator&gt;darcs --version&lt;br /&gt;1.0.8 (release)&lt;/pre&gt;&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;&lt;i&gt;(Optional)&lt;/i&gt; To push patches to a remote server, you will need to generate an RSA key with puttygen unless you already have a public key. Run &lt;b&gt;puttygen&lt;/b&gt; (installed with the darcs bundle), click generate, and move the mouse around to make your key. Save both your public and private keys in c:\darcsdir-w32. Adding a passphrase is optional (I didn't do it).&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;&lt;i&gt;(Optional)&lt;/i&gt; On your remote Linux box running &lt;b&gt;sshd&lt;/b&gt;, edit the file &lt;b&gt;~/.ssh/authorized_keys2&lt;/b&gt; and paste your public key so that it looks something like this (but all on one line):&lt;pre&gt;&lt;br /&gt;ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAgMPa1vvlSi4ZrS7hrk/XJNwd9trM+TyvJjhkMzf3fhks&lt;br /&gt;n8TFin7dhrRfFhVdjka0ep0+RGJrUI/ja6nDyG2SvzG1ka5Ml/Nes2PtpHWcwK7fwU9gBUZNzlvXCkOk&lt;br /&gt;UKO7X7N65LnyCPjJDZa+8LPqrBeESjbVmstG4cw7uh0= doug@windows&lt;/pre&gt;&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;&lt;i&gt;(Optional)&lt;/i&gt; So that darcs can find your key, start &lt;b&gt;Regedit&lt;/b&gt; and add a key for &lt;b&gt;Pageant&lt;/b&gt;:&lt;br /&gt;&lt;pre&gt;HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run&lt;br /&gt;Name: Pageant&lt;br /&gt;Data: c:\darcsdir-w32\pageant.exe c:\darcsdir-w32\private.ppk&lt;/pre&gt;&lt;br /&gt;When you reboot or run the above command, pageant will find your key and you can begin using darcs to push patches to your Linux box.&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;To get the Factor source, run this command wherever you want your local copy, perhaps from c:\:&lt;br /&gt;&lt;pre&gt;darcs get http://factorcode.org/repos&lt;/pre&gt;&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;Additionally, put these &lt;b&gt;dlls&lt;/b&gt; in your Factor directory (repos/Factor/ on a default checkout):&lt;br /&gt;&lt;pre&gt;&lt;a href="http://factorcode.org/dlls/freetype6.dll"&gt;freetype6.dll&lt;/a&gt;&lt;br /&gt;&lt;a href="http://factorcode.org/dlls/zlib1.dll"&gt;zlib1.dll&lt;/a&gt;&lt;/pre&gt;&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;Start &lt;b&gt;MSYS&lt;/b&gt; (the blue M link) and change to your Factor directory. Mine is at c:\repos\Factor, so the command is:&lt;br /&gt;&lt;pre&gt;cd /c/repos/Factor&lt;/pre&gt;&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;From here you should be able to &lt;pre&gt;make windows&lt;/pre&gt;&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;From &lt;b&gt;cmd.exe&lt;/b&gt;, change to the Factor directory and bootstrap with an image (&lt;a href="http://factorcode.org/images/latest/boot.image.x86"&gt;x86&lt;/a&gt;) from factorcode.org:&lt;br /&gt;&lt;pre&gt;f -i=boot.image.x86&lt;/pre&gt;&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;br /&gt;Now you can run Factor!&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;f&lt;/pre&gt;&lt;br /&gt;I recommend bootstrapping and running Factor from cmd.exe because MSYS messes up stderr--you will not notice for a while if Factor has an internal error because there will be no output.&lt;br /&gt;&lt;br /&gt;As a final note, the latest version from darcs is usually the best. If the boot image of Factor is stale and bootstrap fails, contact one of the devs in #concatenative on irc.freenode.org.&lt;br /&gt;&lt;br /&gt;Happy Windows Factoring!&lt;br /&gt;&lt;br /&gt;Update: Troubleshooting&lt;br /&gt;&lt;p&gt;Q: When starting Factor, why do I get the error 'alien-invoke-error-library "freetype", alien-invoke-error-symbol "FT_Init_FreeType"'?&lt;/p&gt;&lt;p&gt;A: You forgot to put &lt;a href="http://factorcode.org/dlls/freetype6.dll"&gt;freetype6.dll&lt;/a&gt; and &lt;a href="http://factorcode.org/dlls/zlib1.dll"&gt;zlib1.dll&lt;/a&gt; in the Factor directory. Download these and bootstrap again.&lt;/p&gt;&lt;p&gt;Q: When I bootstrap, why does Factor spew "*** Data GC (5 minor, 24 cards)" and error out with 'Word not found in current vocabulary search path delegate f no-word-name "\u000c"'?&lt;/p&gt;&lt;p&gt;A: On bootstrap you must pass the '-i' command line argument. The correct command line is:&lt;/p&gt;&lt;p&gt;&lt;em&gt;./f -i=boot.image.x86&lt;/em&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37596622-7107737895177230587?l=code-factor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://code-factor.blogspot.com/feeds/7107737895177230587/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=37596622&amp;postID=7107737895177230587' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/7107737895177230587'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/7107737895177230587'/><link rel='alternate' type='text/html' href='http://code-factor.blogspot.com/2006/11/compiling-factor-on-windows.html' title='Compiling Factor on Windows'/><author><name>Doug Coleman</name><uri>http://www.blogger.com/profile/09416611825627835802</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-37596622.post-116356058237621127</id><published>2006-11-14T18:57:00.001-08:00</published><updated>2006-11-14T20:30:58.918-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Factor'/><category scheme='http://www.blogger.com/atom/ns#' term='Programming Languages'/><category scheme='http://www.blogger.com/atom/ns#' term='Code'/><title type='text'>Factor 0.86 released</title><content type='html'>&lt;a href="http://factor-language.blogspot.com/2006/11/factor-086-released.html"&gt;Factor 0.86&lt;/a&gt; has been released.  We took about a week to test it before releasing this time, and it promises to be the most stable release ever.  Factor for Windows has a few improvements too, as a long-standing bug with the native i/o has been fixed, allowing Factor to be run in the console while still supporting native i/o with the command: &lt;pre&gt;f -shell=tty&lt;/pre&gt;Furthermore, in 0.85, Windows Factor took several minutes to slurp through a 600 MB file; the new version takes five seconds and will speed up again when continuations compile.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37596622-116356058237621127?l=code-factor.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://code-factor.blogspot.com/feeds/116356058237621127/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=37596622&amp;postID=116356058237621127' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/116356058237621127'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/37596622/posts/default/116356058237621127'/><link rel='alternate' type='text/html' href='http://code-factor.blogspot.com/2006/11/factor-086-released_14.html' title='Factor 0.86 released'/><author><name>Doug Coleman</name><uri>http://www.blogger.com/profile/09416611825627835802</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>
