<?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:blogger='http://schemas.google.com/blogger/2008' 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-7570364353928786565</id><updated>2025-12-08T17:48:26.638-08:00</updated><category term="plt scheme"/><category term="SHP"/><category term="web"/><category term="dbi"/><category term="net"/><category term="JSMGR"/><category term="date"/><category term="timezone"/><category term="http"/><category term="javascript"/><category term="parser combinator"/><category term="filesystem"/><category term="network"/><category term="parser"/><category term="ajax"/><category term="flex"/><category term="memcached"/><category term="mysql"/><category term="postgresql"/><category term="sqlite"/><category term="yui"/><category term="encoded word"/><category term="protocol"/><category term="proxy"/><category term="session"/><category term="sql"/><category term="tcp"/><category term="top down"/><category term="admin"/><category term="chrome"/><category term="concurrency"/><category term="continuation"/><category term="css"/><category term="erlang"/><category term="flapjax; function reactive programming"/><category term="otp"/><category term="planet"/><category term="thread"/><category term="webapi"/><category term="xml"/><category term="yinsochen.com"/><title type='text'>Web, Development, Functional Programming, and Scheme</title><subtitle type='html'>My experiences in software development (primarily web development), functional programming, and learning of scheme.  This is primarily a technical blog.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://weblambda.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7570364353928786565/posts/default?redirect=false'/><link rel='alternate' type='text/html' href='http://weblambda.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/7570364353928786565/posts/default?start-index=26&amp;max-results=25&amp;redirect=false'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>80</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-7570364353928786565.post-6879481286250288779</id><published>2011-03-03T19:10:00.000-08:00</published><updated>2011-03-03T19:10:21.687-08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="flapjax; function reactive programming"/><title type='text'>Flapjax - Functional Reactive Programming in Javascript</title><content type='html'>&lt;span class=&quot;Apple-style-span&quot; style=&quot;color: #274e13; font-family: &#39;Helvetica Neue&#39;, Arial, Helvetica, sans-serif; font-size: x-small;&quot;&gt;[This preview is posted here &lt;a href=&quot;http://weblambda.blogspot.com/2011/03/administrivia-moving-of-weblambda-to.html&quot;&gt;during the transition phase&lt;/a&gt;; once the transition is complete I will stop posting here]&amp;nbsp;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;color: #444444; font-family: Georgia, &#39;Times New Roman&#39;, &#39;Bitstream Charter&#39;, Times, serif; font-size: 13px; line-height: 19px;&quot;&gt;&lt;/span&gt;&lt;br /&gt;
&lt;div style=&quot;color: #444444; font-family: Georgia, &#39;Bitstream Charter&#39;, serif; font-size: 16px; line-height: 1.5; margin-bottom: 24px;&quot;&gt;What if programming can be as simple as writing spreadsheet formulas?&lt;/div&gt;&lt;div style=&quot;color: #444444; font-family: Georgia, &#39;Bitstream Charter&#39;, serif; font-size: 16px; line-height: 1.5; margin-bottom: 24px;&quot;&gt;Now - there are some complex spreadsheets out there for sure, but the power of spreadsheet comes from that the values are self-updating, i.e. changing value in one cell will automatically cause all its dependent cells to reflect the change.&amp;nbsp; If spreadsheets cannot do the auto-propagation, even the simplest spredsheet will be much more complex.&lt;/div&gt;&lt;div style=&quot;color: #444444; font-family: Georgia, &#39;Bitstream Charter&#39;, serif; font-size: 16px; line-height: 1.5; margin-bottom: 24px;&quot;&gt;Most programming languages do not have the auto-propagating capability, but the ones that do are called&amp;nbsp;&lt;a href=&quot;http://en.wikipedia.org/wiki/Reactive_programming&quot; mce_href=&quot;http://en.wikipedia.org/wiki/Reactive_programming&quot; style=&quot;color: #0066cc; font-family: Georgia, &#39;Bitstream Charter&#39;, serif; line-height: 1.5;&quot;&gt;reactive programming languages&lt;/a&gt;.&amp;nbsp; And if the language is also functional, then it&#39;s called a&amp;nbsp;&lt;a href=&quot;http://en.wikipedia.org/wiki/Functional_reactive_programming&quot; mce_href=&quot;http://en.wikipedia.org/wiki/Functional_reactive_programming&quot; style=&quot;color: #0066cc; font-family: Georgia, &#39;Bitstream Charter&#39;, serif; line-height: 1.5;&quot;&gt;functional reactive programming language&lt;/a&gt;.&lt;/div&gt;&lt;div style=&quot;color: #444444; font-family: Georgia, &#39;Bitstream Charter&#39;, serif; font-size: 16px; line-height: 1.5; margin-bottom: 24px;&quot;&gt;Since most of the mainstream languages did not have reactive features built-in, the feature need to be &quot;bolted on&quot;, as a library or a transformer.&amp;nbsp; The one for Javascript is called&amp;nbsp;&lt;a href=&quot;http://flapjax-lang.org/&quot; mce_href=&quot;http://flapjax-lang.org/&quot; style=&quot;color: #0066cc; font-family: Georgia, &#39;Bitstream Charter&#39;, serif; line-height: 1.5;&quot;&gt;Flapjax&lt;/a&gt;.&lt;/div&gt;&lt;div style=&quot;color: #444444; font-family: Georgia, &#39;Bitstream Charter&#39;, serif; font-size: 16px; line-height: 1.5; margin-bottom: 24px;&quot;&gt;&lt;a href=&quot;http://yinsochen.com/flapjax-functional-reactive-programming-in-javascrip/&quot;&gt;[Continue to read the rest of the post @ the new site....]&lt;/a&gt;&amp;nbsp;&lt;/div&gt;&lt;div style=&quot;color: #444444; font-family: Georgia, &#39;Bitstream Charter&#39;, serif; font-size: 16px; line-height: 1.5; margin-bottom: 24px;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://weblambda.blogspot.com/feeds/6879481286250288779/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://weblambda.blogspot.com/2011/03/flapjax-functional-reactive-programming.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7570364353928786565/posts/default/6879481286250288779'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7570364353928786565/posts/default/6879481286250288779'/><link rel='alternate' type='text/html' href='http://weblambda.blogspot.com/2011/03/flapjax-functional-reactive-programming.html' title='Flapjax - Functional Reactive Programming in Javascript'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7570364353928786565.post-1628985242969228108</id><published>2011-03-01T22:38:00.000-08:00</published><updated>2011-03-01T22:38:03.927-08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="admin"/><category scheme="http://www.blogger.com/atom/ns#" term="yinsochen.com"/><title type='text'>Administrivia: Moving of Weblambda to New Blog</title><content type='html'>I am consolidating my blogging over to my personal site, &lt;a href=&quot;http://yinsochen.com/&quot;&gt;http://yinsochen.com&lt;/a&gt;, in the near future.&lt;br /&gt;
&lt;br /&gt;
The main site will contain more than just scheme development - if that interests you, that&#39;s great. &amp;nbsp;You can grab the whole feed from &lt;a href=&quot;http://yinsochen.com/feed/&quot;&gt;http://yinsochen.com/feed/&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
In case you just want to have scheme-related posts - use &lt;a href=&quot;http://yinsochen.com/tag/scheme&quot;&gt;http://yinsochen.com/tag/scheme&lt;/a&gt; (RSS feed at&amp;nbsp;&lt;a href=&quot;http://yinsochen.com/tag/scheme/feed/&quot;&gt;http://yinsochen.com/tag/scheme/feed/&lt;/a&gt;).&lt;br /&gt;
&lt;br /&gt;
There will be time to make the switch - I will be linking from here to the new site for the next few posts, before I fully make the move.&lt;br /&gt;
&lt;br /&gt;
Cheers.</content><link rel='replies' type='application/atom+xml' href='http://weblambda.blogspot.com/feeds/1628985242969228108/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://weblambda.blogspot.com/2011/03/administrivia-moving-of-weblambda-to.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7570364353928786565/posts/default/1628985242969228108'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7570364353928786565/posts/default/1628985242969228108'/><link rel='alternate' type='text/html' href='http://weblambda.blogspot.com/2011/03/administrivia-moving-of-weblambda-to.html' title='Administrivia: Moving of Weblambda to New Blog'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7570364353928786565.post-1503242042419167733</id><published>2010-07-18T04:27:00.000-07:00</published><updated>2010-07-18T04:29:07.326-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="plt scheme"/><category scheme="http://www.blogger.com/atom/ns#" term="SHP"/><category scheme="http://www.blogger.com/atom/ns#" term="web"/><category scheme="http://www.blogger.com/atom/ns#" term="webapi"/><title type='text'>BZLIB/SHP Web API &amp; Converters</title><content type='html'>The &lt;a href=&quot;http://weblambda.blogspot.com/2010/07/bzlibshpplt-04-now-available-web-api.html&quot;&gt;previous post describes the basics on how to use the web API&lt;/a&gt;.&amp;nbsp; This post will focus on integrating your module with the web API. &lt;br /&gt;
&lt;br /&gt;
As shown before, you can create a web API by creating an SHP script as follows: &lt;br /&gt;
&lt;pre class=&quot;scheme&quot;&gt;&lt;code&gt;
;; /api/add2 
(:api-args (a &lt;b&gt;number?&lt;/b&gt;) (b &lt;b&gt;number?&lt;/b&gt;)) 
(+ a b) 
&lt;/code&gt;&lt;/pre&gt;Where both &lt;code&gt;a&lt;/code&gt; and &lt;code&gt;b&lt;/code&gt; are validated as &lt;code&gt;number?&lt;/code&gt;.&amp;nbsp; It would be nice if we can validate &lt;u&gt;any type of scheme values&lt;/u&gt;, as long as the value can be created via the request input.&amp;nbsp; &lt;br /&gt;
&lt;br /&gt;
For example - let&#39;s say that you have a struct with the following definition: &lt;br /&gt;
&lt;pre class=&quot;scheme&quot;&gt;&lt;code&gt;(define-struct foo (bar baz)) 
&lt;/code&gt;&lt;/pre&gt;We want to do the following: &lt;br /&gt;
&lt;pre class=&quot;scheme&quot;&gt;&lt;code&gt;(:api-args (foo &lt;b&gt;foo?&lt;/b&gt;)) ;; takes in the foo struct 
&lt;/code&gt;&lt;/pre&gt;And let web API handle the rest.&amp;nbsp;&amp;nbsp; This is achieved via &lt;b&gt;converters&lt;/b&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Converters &lt;/h3&gt;The mappings between the request and the api args are done via &lt;b&gt;converters&lt;/b&gt;, which maps the parameter key against the type&#39;s test function (such as &lt;code&gt;number?&lt;/code&gt;).&amp;nbsp;&amp;nbsp; And when you want to use the converter in the &lt;code&gt;api-args&lt;/code&gt; expression, you specify the &lt;code&gt;&amp;lt;test?&amp;gt;&lt;/code&gt; function in the parameter position in one of the following forms: &lt;a name=&#39;more&#39;&gt;&lt;/a&gt; &lt;br /&gt;
&lt;pre class=&quot;scheme&quot;&gt;&lt;code&gt;
(&amp;lt;name&amp;gt; &amp;lt;test?&amp;gt; &amp;lt;default&amp;gt;) ;; this form means this parameter is an optional parameter, and if no values are passed in it will return the default value.

(&amp;lt;name&amp;gt; &amp;lt;test?&amp;gt;) ;; this form means the parameter is a required parameter – if no value passed in it will error.&amp;nbsp; Any value passed in will be validated and converted (if it fails the conversion it will error)

&amp;lt;name&amp;gt; ;; this form means the parameter does not have any validation – any value will be passed verbatim. 
&lt;/code&gt;&lt;/pre&gt;Because the &lt;code&gt;&amp;lt;test?&amp;gt;&lt;/code&gt; function is used as the mapping to the actual underlying converter object, we will call the underlying converter object as the &quot;&lt;code&gt;&amp;lt;test?&amp;gt;&lt;/code&gt; converter&quot;. i.e., the actual converter mapped by &lt;code&gt;number?&lt;/code&gt; is called the &quot;&lt;code&gt;number?&lt;/code&gt; converter&quot; (the quotes are dropped going forward). &lt;br /&gt;
&lt;br /&gt;
You can define your own converters to use in the &lt;code&gt;api-args&lt;/code&gt; expression to validate your own objects.&amp;nbsp; To do so, you can define either a &lt;b&gt;scalar&lt;/b&gt; or a &lt;b&gt;struct&lt;/b&gt; converter as of bzlib/shp:1:3.&amp;nbsp; &lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Scalar vs. Struct Converters&lt;/h3&gt;&lt;br /&gt;
There are two different types of converters - &lt;b&gt;scalar&lt;/b&gt; and &lt;b&gt;struct&lt;/b&gt;. They differ in the type of input required.  This is best explained with a JSON request. &lt;br /&gt;
&lt;br /&gt;
Scalar maps to the JSON number and strings, while struct maps to JSON objects (JSON arrays are automatically mapped to lists). &lt;br /&gt;
&lt;br /&gt;
As an example - the following JSON object has a scalar value for both &lt;code&gt;foo&lt;/code&gt; and &lt;code&gt;bar&lt;/code&gt; key, but a struct value for the &lt;code&gt;baz&lt;/code&gt; key: &lt;br /&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;
{ foo : 1 , bar : &quot;test me&quot; , baz : { abc : 1 , def : 2 } } 
&lt;/code&gt;&lt;/pre&gt;So we will use a scalar converter for both &lt;code&gt;foo&lt;/code&gt; and &lt;code&gt;bar&lt;/code&gt; field (&lt;code&gt;number?&lt;/code&gt; and &lt;code&gt;string?&lt;/code&gt; respectively), and we will use a struct converter for the &lt;code&gt;baz&lt;/code&gt; field that takes in an &lt;code&gt;abc&lt;/code&gt; and &lt;code&gt;def&lt;/code&gt; fields as &lt;code&gt;number?&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
The mapping is pretty straightforward for XMLRPC request as well, except that XMLRPC request method call does not handle named parameters, so we will map the arguments by positions.  Otherwise, XMLRPC&#39;s string and integers maps to scalars, and struct maps to struct converters (the XMLRPC array maps to lists automatically). &lt;br /&gt;
&lt;br /&gt;
The mapping in query-based request is a bit more complicated, since the query string is only consisted of key/value pairs, it does not handle nested hierarchy of objects by default.  We solve this problem by using the dot notation (familiar to most OOP developers) to simulate the hierarchy in the key name themselves.  As an example, the query string below maps to the JSON object above: &lt;br /&gt;
&lt;pre class=&quot;http&quot;&gt;&lt;code&gt;
&amp;foo=1&amp;bar=test%20me&amp;&lt;b&gt;baz.abc&lt;/b&gt;=1&amp;&lt;b&gt;baz.def&lt;/b&gt;=2
&lt;/code&gt;&lt;/pre&gt;The &lt;code&gt;baz&lt;/code&gt; JSON object is flattened into &lt;b&gt;&lt;code&gt;baz.abc&lt;/code&gt;&lt;/b&gt; and &lt;code&gt;&lt;b&gt;baz.def&lt;/b&gt;&lt;/code&gt; key/value pairs. This flattening can be done for arbitrary levels.&lt;br /&gt;
&lt;br /&gt;
Hence - you need to ensure that the request are constructed correctly based on the above rules.  The rules for JSON &amp; XMLRPC are specified in their corresponding specs, and the query-string rules are specified here. &lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Define a Scalar Converter&lt;/h3&gt;&lt;br /&gt;
To define a scalar converter, we use the &lt;code&gt;define-scalar-converter!&lt;/code&gt; syntax.&lt;br /&gt;
&lt;pre class=&quot;scheme&quot;&gt;&lt;code&gt;
(define-scalar-converter! &amp;lt;test?&amp;gt; 
  (&amp;lt;type?&amp;gt; &amp;lt;transform-from-type?&amp;gt;) ...) 
&lt;/code&gt;&lt;/pre&gt;For example – the following is the definition of the &lt;code&gt;number?&lt;/code&gt; converter: &lt;br /&gt;
&lt;pre class=&quot;scheme&quot;&gt;&lt;code&gt;
(define-scalar-converter! number? (string? string-&amp;gt;number)) &lt;/code&gt;&lt;/pre&gt;It roughly means the following: &lt;br /&gt;
&lt;pre class=&quot;scheme&quot;&gt;&lt;code&gt;
(lambda (x) 
&amp;nbsp; (cond ((number? x) x) 
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ((string? x) 
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (let ((x (string-&amp;gt;number x))) 
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (if x
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; x 
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (error &#39;invalid-conversion &quot;~s&quot; x))))
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (else (error &#39;invalid-type &quot;~s&quot; x))))
&lt;/code&gt;&lt;/pre&gt;If the passed in value is already the desired type, we just let it pass through.&amp;nbsp; Otherwise we test to see if it is one of the known types (or error out), and try to convert the value.&amp;nbsp; If the converter succeeds, return the value, otherwise, throw an exception.  &lt;br /&gt;
&lt;br /&gt;
Although the other type of converters is called struct converter, we can define a scalar converter for a struct as well, as long as the struct can be mapped from a scalar value.  NOTE - you cannot define both a scalar and struct converter for a struct, since all converters shares the same namespace. &lt;br /&gt;
&lt;br /&gt;
Here&#39;s an example of a scalar converter for a struct – we will create a scalar converter for &lt;code&gt;url?&lt;/code&gt; from &lt;code&gt;net/url&lt;/code&gt;.&lt;br /&gt;
&lt;pre class=&quot;scheme&quot;&gt;&lt;code&gt;
(define-scalar-converter! url? 
  (string? string-&amp;gt;url) 
  (bytes? (compose string-&amp;gt;url bytes-&amp;gt;string/utf-8))
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
&lt;h3&gt;Define Struct Converters&lt;/h3&gt;&lt;br /&gt;
To define a struct converter, we use the &lt;code&gt;define-struct-converter!&lt;/code&gt; syntax. &lt;br /&gt;
&lt;pre class=&quot;scheme&quot;&gt;&lt;code&gt;
(define-struct-converter! name ((field &lt;b&gt;converter?&lt;/b&gt;) ...)) 
&lt;/code&gt;&lt;/pre&gt;For example – the following defines a struct converter for the foo struct above: &lt;br /&gt;
&lt;pre class=&quot;scheme&quot;&gt;&lt;code&gt;
(define-struct-converter! foo ((bar &lt;b&gt;number?&lt;/b&gt;) (baz &lt;b&gt;string?&lt;/b&gt;))) 
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
The struct converter looks like the struct form in &lt;a href=&quot;http://docs.racket-lang.org/reference/Attaching_Contracts_to_Values.html#%28form._%28%28lib._racket/contract/private/provide..rkt%29._provide/contract%29%29&quot;&gt;&lt;code&gt;provide/contract&lt;/code&gt;&lt;/a&gt;, but instead of contract expressions, each field is accompanied by a converter instead. &lt;br /&gt;
&lt;br /&gt;
The field converter must already have been defined, or else the definition will fail and throw an exception. &lt;br /&gt;
&lt;br /&gt;
The field converter can of course be either a scalar converter or a struct converter as well. &lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Converter Definition Orders and Other Design Decisions&lt;/h3&gt;&lt;br /&gt;
Converters should be defined in the order from the most general types to the  most specific types, similarly to how object hierarchies are defined.&amp;nbsp; For example, if you have a sub struct, you should define the converter for the sub struct &lt;b&gt;after&lt;/b&gt; you have defined the converter for the parent struct.&amp;nbsp; And if you want to have converter for both &lt;code&gt;number?&lt;/code&gt; and &lt;code&gt;integer?&lt;/code&gt;, you should first define the &lt;code&gt;number?&lt;/code&gt; converter and then define the &lt;code&gt;integer?&lt;/code&gt; converter.&lt;br /&gt;
&lt;br /&gt;
One of the design decisions for the converters is that once a converter is defined it is &lt;b&gt;immutable&lt;/b&gt; – future definitions are simply ignored. &lt;br /&gt;
&lt;br /&gt;
Furthermore, both scalar converter and struct converter shares the same namespace.&lt;br /&gt;
&lt;br /&gt;
The above two points means that you can only define either a scalar converter or a struct converter for any given type, and once it is defined it cannot be changed. &lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Where to Define Converters&lt;/h3&gt;&lt;br /&gt;
Since the design philosophy behind &lt;code&gt;bzlib/shp&lt;/code&gt; is that shp scripts should be used for presentations and the application logics should reside in regular PLT Scheme/Racket module/packages, converters are best defined in the respective modules. &lt;br /&gt;
&lt;br /&gt;
However, for ad hoc purposes, the required script (the single shp script that is responsible for loading the required modules) has been extended with &lt;code&gt;bzlib/shp&lt;/code&gt; 0.4 so it can now handle ad hoc defintions as well.  So if you want to define converters in the required script you can do so, but remember that if it seems like these definition should be shared they might need to eventually migrate into their respective modules. &lt;br /&gt;
&lt;br /&gt;
That&#39;s it for now.&amp;nbsp; Feel free to leave comment if there are any questions.&amp;nbsp; Enjoy.</content><link rel='replies' type='application/atom+xml' href='http://weblambda.blogspot.com/feeds/1503242042419167733/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://weblambda.blogspot.com/2010/07/bzlibshp-web-api-converters.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7570364353928786565/posts/default/1503242042419167733'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7570364353928786565/posts/default/1503242042419167733'/><link rel='alternate' type='text/html' href='http://weblambda.blogspot.com/2010/07/bzlibshp-web-api-converters.html' title='BZLIB/SHP Web API &amp; Converters'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7570364353928786565.post-1583379803792840301</id><published>2010-07-08T12:31:00.000-07:00</published><updated>2010-07-08T12:33:12.122-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="plt scheme"/><category scheme="http://www.blogger.com/atom/ns#" term="SHP"/><title type='text'>BZLIB/SHP.plt 0.4 now available - Web API</title><content type='html'>A new version of &lt;a href=&quot;http://planet.plt-scheme.org/display.ss?package=shp.plt&amp;amp;owner=bzlib&quot;&gt;SHP.plt is now available via planet&lt;/a&gt;.&amp;nbsp; This is a major rewrite of SHP and provides two main upgrades: &lt;br /&gt;
&lt;ul&gt;&lt;li&gt;a &quot;web API&quot; interface - your web script can now be exposed as an &quot;API&quot; (think XMLRPC/JSON), and it automatically works with either XMLRPC or JSON (details below) &lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;general performance enhancement - the scripts are now compiled and cached to reduce disk IO.&amp;nbsp; If the scripts are updated then they are automatically recompiled &lt;br /&gt;
&lt;/li&gt;
&lt;/ul&gt;As usual, the code is released under LGPL.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Installation&amp;nbsp;&lt;/h3&gt;&lt;pre class=&quot;scheme&quot;&gt;&lt;code&gt;(require (planet bzlib/shp:1:3)) &lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
SHP requires some newer dependencies (&lt;a href=&quot;http://planet.plt-scheme.org/display.ss?package=base.plt&amp;amp;owner=bzlib&quot;&gt;bzlib/base:1:6&lt;/a&gt;, &lt;a href=&quot;http://planet.plt-scheme.org/display.ss?package=date.plt&amp;amp;owner=bzlib&quot;&gt;bzlib/date:1:3&lt;/a&gt;, &lt;a href=&quot;http://planet.plt-scheme.org/display.ss?package=parseq.plt&amp;amp;owner=bzlib&quot;&gt;bzlib/parseq:1:3&lt;/a&gt;, &lt;a href=&quot;http://planet.plt-scheme.org/display.ss?package=xml.plt&amp;amp;owner=bzlib&quot;&gt;bzlib/xml:1:3&lt;/a&gt;, &lt;a href=&quot;http://planet.plt-scheme.org/display.ss?package=mime.plt&amp;amp;owner=bzlib&quot;&gt;bzlib/mime:1:0&lt;/a&gt;), and the current versions of PLT Scheme and Racket have issues with version dependencies (&lt;a href=&quot;http://lists.racket-lang.org/dev/archive/2009-September/001285.html&quot;&gt;the link: module mismatch bug&lt;/a&gt;), so you might have to clear out the planet cache and recompile them again.&lt;br /&gt;
&lt;br /&gt;
As usual, SHP comes with a small example site that you can play with under the example sub directory - cd to the example directory and run &lt;code&gt;(require &quot;web.ss&quot;)&lt;/code&gt; will start the example site.&amp;nbsp; The example site is still just trivial code right now - it will eventually be enhanced and separated into its own package. &lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Cached Compiled Script &lt;/h3&gt;All of the scripts are now compiled and cached.&amp;nbsp; This has some potential performance benefit, since we will only access the file content when the file timestamp changes (meaning the file has been touched and/or modified).&amp;nbsp; As this is a non-visible feature, we won&#39;t spend much time discussing it, except to note that the change is not just done for performance reasons - it is also done to enable and simplify the design of web api, which is discussed below. &lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Web API&lt;/h3&gt;Under the example site you can find the script &lt;code&gt;shp/api/add2&lt;/code&gt;, which contains the following:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;scheme&quot;&gt;&lt;code&gt;
;; -*- scheme -*- -p 
(:api-args (a number?) (b number?)) 
(+ a b)&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
This is the new *web api* - it takes in 2 numbers, &lt;code&gt;a&lt;/code&gt; and &lt;code&gt;b&lt;/code&gt;, and return the added result. To write an api script, you must use the &lt;code&gt;:api-args&lt;/code&gt; expression, and then supply the arguments inside. The arguments can be specified in the following forms: &lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;scheme&quot;&gt;&lt;code&gt;
(:api-args a b) ;; both a &amp;amp; b are non-validating and you get what&#39;s passed in

(:api-args (a number?) (b string?)) ;; a expects a number, and b expects a string 

(:api-args (a number? 3) (b number? 5)) ;; a &amp;amp; b both expect numbers, and both have default values if they are not passed in (a defaults to 3, and b defaults to 5). &lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
When you run the example site you can access the api via the following http call: &lt;br /&gt;
&lt;pre class=&quot;http&quot;&gt;&lt;code&gt;
GET /api/add2 HTTP/1.0 &lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
When running the above in browser you should get back an XMLRPC response: &lt;br /&gt;
&lt;pre class=&quot;http&quot;&gt;&lt;code&gt;
Content-Type: text/xml; charset=utf-8&amp;nbsp;

&amp;lt;methodResponse&amp;gt;
&amp;lt;fault&amp;gt;
&amp;lt;value&amp;gt;
&amp;lt;string&amp;gt;required: a&amp;lt;/string&amp;gt;
&amp;lt;/value&amp;gt;
&amp;lt;/fault&amp;gt;
&amp;lt;/methodResponse&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
XMLRPC is the default response mode for web APIs.&amp;nbsp; What it returns by default as shown above is an error message, because neither &lt;code&gt;a&lt;/code&gt; or &lt;code&gt;b&lt;/code&gt; is passed in. &lt;br /&gt;
&lt;br /&gt;
To pass in the values - you just need to specify them in the query string as following: &lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;http&quot;&gt;&lt;code&gt;
GET /api/add2?&amp;amp;&lt;b&gt;a=50&amp;amp;b=90 &lt;/b&gt;HTTP/1.0 &lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
which will return the following: &lt;br /&gt;
&lt;pre class=&quot;http&quot;&gt;&lt;code&gt;
Content-Type: text/xml; charset=utf-8

&amp;lt;methodResponse&amp;gt;
&amp;lt;params&amp;gt;
&amp;lt;param&amp;gt;
&amp;lt;value&amp;gt;
&lt;b&gt;&amp;lt;int&amp;gt;140&amp;lt;/int&amp;gt;&lt;/b&gt;
&amp;lt;/value&amp;gt;
&amp;lt;/param&amp;gt;
&amp;lt;/params&amp;gt;
&amp;lt;/methodResponse&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
The &lt;code&gt;add2&lt;/code&gt; script contains args of &lt;code&gt;(a number?)&lt;/code&gt; and &lt;code&gt;(b number?)&lt;/code&gt;, which mean that both &lt;code&gt;a&lt;/code&gt; &amp;amp; &lt;code&gt;b&lt;/code&gt; expects a number input.&amp;nbsp; So if we pass a non-number to the api &lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;http&quot;&gt;&lt;code&gt;
GET /api/add2?&amp;amp;a=not-a-number&amp;amp;b=also-not-a-number HTTP/1.0 &lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
we get the following: &lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;http&quot;&gt;&lt;code&gt;
&amp;lt;methodResponse&amp;gt;
&amp;lt;fault&amp;gt;
&amp;lt;value&amp;gt;
&amp;lt;string&amp;gt;invalid-conversion: &quot;not-a-number&quot;&amp;lt;/string&amp;gt;
&amp;lt;/value&amp;gt;
&amp;lt;/fault&amp;gt;
&amp;lt;/methodResponse&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
Which shows that the validation takes place for each of the arguments.&amp;nbsp; We will talk about how the underlying validation magic works later. &lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;XMLRPC Request Payload &lt;/h3&gt;Now the API doesn&#39;t just work for query string parameters - you can pass in an XMLRPC payload as well.&amp;nbsp; To do so you need to use POST instead of GET, and put the following XMLRPC request into the payload:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;http&quot;&gt;&lt;code&gt;
POST /api/add2 HTTP/1.0 
Content-Type: text/xml; charset=utf-8 

&amp;lt;methodCall&amp;gt;
&amp;lt;methodName&amp;gt;add2&amp;lt;/methodName&amp;gt;
&amp;lt;params&amp;gt;
&amp;lt;param&amp;gt;&amp;lt;name&amp;gt;a&amp;lt;/name&amp;gt;&amp;lt;value&amp;gt;&amp;lt;int&amp;gt;50&amp;lt;/int&amp;gt;&amp;lt;/value&amp;gt;&amp;lt;/param&amp;gt;
&amp;lt;param&amp;gt;&amp;lt;name&amp;gt;b&amp;lt;/name&amp;gt;&amp;lt;value&amp;gt;&amp;lt;int&amp;gt;90&amp;lt;/int&amp;gt;&amp;lt;/value&amp;gt;&amp;lt;/param&amp;gt;
&amp;lt;/params&amp;gt;
&amp;lt;/methodCall&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
Which will return the same result: &lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;http&quot;&gt;&lt;code&gt;
Content-Type: text/xml; charset=utf-8

&amp;lt;methodResponse&amp;gt;
&amp;lt;params&amp;gt;
&amp;lt;param&amp;gt;
&amp;lt;value&amp;gt;
&lt;b&gt;&amp;lt;int&amp;gt;140&amp;lt;/int&amp;gt;&lt;/b&gt;
&amp;lt;/value&amp;gt;
&amp;lt;/param&amp;gt;
&amp;lt;/params&amp;gt;
&amp;lt;/methodResponse&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
&lt;h3&gt;Partial Path Dispatch with XMLRPC methodName &lt;/h3&gt;You might have noticed in the above that the token &lt;code&gt;add2&lt;/code&gt; is specified &lt;b&gt;twice&lt;/b&gt; in the request - once in the path, and once in the &lt;code&gt;methodName&lt;/code&gt; parameter in the payload.&amp;nbsp; And if you were to use a bogus method name such such as &lt;code&gt;add3&lt;/code&gt; in the &lt;code&gt;methodName&lt;/code&gt; parameter, you will see that it is being ignored - the path has precedence. &lt;br /&gt;
&lt;br /&gt;
So - the dispatch rule is - &lt;u&gt;if the path matches the script exactly, the &lt;code&gt;methodName&lt;/code&gt; parameter will be ignored&lt;/u&gt;. &lt;br /&gt;
&lt;br /&gt;
This is designed to follow the same dispatching rule of regular SHP scripts, and to ensure that one script compiles into one api.&amp;nbsp; &lt;br /&gt;
&lt;br /&gt;
But if you like to use the regular XMLRPC dispatch, it also works - you just need to remove the &lt;code&gt;add2&lt;/code&gt; from the path, as follows: &lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;http&quot;&gt;&lt;code&gt;
POST &lt;b&gt;/api&lt;/b&gt; HTTP/1.0 # notice - no add2 here 
Content-Type: text/xml; charset=utf-8 

&amp;lt;methodCall&amp;gt;
&amp;lt;methodName&amp;gt;add2&amp;lt;/methodName&amp;gt;
&amp;lt;params&amp;gt;
&amp;lt;param&amp;gt;&amp;lt;name&amp;gt;a&amp;lt;/name&amp;gt;&amp;lt;value&amp;gt;&amp;lt;int&amp;gt;50&amp;lt;/int&amp;gt;&amp;lt;/value&amp;gt;&amp;lt;/param&amp;gt;
&amp;lt;param&amp;gt;&amp;lt;name&amp;gt;b&amp;lt;/name&amp;gt;&amp;lt;value&amp;gt;&amp;lt;int&amp;gt;90&amp;lt;/int&amp;gt;&amp;lt;/value&amp;gt;&amp;lt;/param&amp;gt;
&amp;lt;/params&amp;gt;
&amp;lt;/methodCall&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
So instead of posting to &lt;code&gt;/api/add2&lt;/code&gt;, just post to &lt;code&gt;/api&lt;/code&gt;, and specify &lt;code&gt;add2&lt;/code&gt; in the &lt;code&gt;methodName&lt;/code&gt; parameter.&amp;nbsp; This is called &lt;u&gt;partial path dispatch&lt;/u&gt;, for the lack of a better term for now. &lt;br /&gt;
&lt;br /&gt;
NOTE - in order for partial dispatch to work, the &lt;code&gt;/api&lt;/code&gt; directory must not contain an &lt;code&gt;index&lt;/code&gt; script, because otherwise the &lt;code&gt;index&lt;/code&gt; script will be matched first prior to the partial dispatch kicking in. &lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Partial Path Dispatch With Query String &lt;/h3&gt;There is another way of doing partial path dispatch, and that involve the using of query string.&amp;nbsp; Yes, you can specify query string with POST requests, and the query string values will be parsed properly in SHP. &lt;br /&gt;
&lt;br /&gt;
The way to do it is to specify a query key that starts with &lt;b&gt;&lt;code&gt;**&lt;/code&gt;&lt;/b&gt;.&amp;nbsp; So for example, if we want to dispatch to &lt;code&gt;/api/add2&lt;/code&gt;, we can dispatch as follows: &lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;http&quot;&gt;&lt;code&gt;
POST /api?&lt;b&gt;**add2&lt;/b&gt; HTTP/1.0 # note the **add2 key in the query string. 
Content-Type: text/xml; charset=utf-8 

&amp;lt;methodCall&amp;gt;
&amp;lt;methodName&amp;gt;add3&amp;lt;/methodName&amp;gt;&amp;lt;!-- note the wrong method name --&amp;gt; 
&amp;lt;params&amp;gt;
&amp;lt;param&amp;gt;&amp;lt;name&amp;gt;a&amp;lt;/name&amp;gt;&amp;lt;value&amp;gt;&amp;lt;int&amp;gt;50&amp;lt;/int&amp;gt;&amp;lt;/value&amp;gt;&amp;lt;/param&amp;gt;
&amp;lt;param&amp;gt;&amp;lt;name&amp;gt;b&amp;lt;/name&amp;gt;&amp;lt;value&amp;gt;&amp;lt;int&amp;gt;90&amp;lt;/int&amp;gt;&amp;lt;/value&amp;gt;&amp;lt;/param&amp;gt;
&amp;lt;/params&amp;gt;
&amp;lt;/methodCall&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
The above has exactly the same effect as if you have done a direct dispatch - the &lt;code&gt;**&lt;/code&gt; prefix will be stripped, and then appended to the path.&amp;nbsp; And this rule, along with the direct dispatch, supersedes the method name in the xmlrpc payload. &lt;br /&gt;
&lt;br /&gt;
This design exists for a reason - to deal with web forms that have &lt;b&gt;multiple submit buttons&lt;/b&gt;.&amp;nbsp; &lt;br /&gt;
&lt;br /&gt;
Web forms that have multiple submit buttons usually have each button mapped to different actions, and instead of requiring you to do manual dispatch on the server side based on which button is clicked (the name &amp;amp; value of the submit button clicked gets submitted to the server side along with the data), you can partition the API calls based on the name of the button (partition on name instead of the value is better, because that way it allows you to localize the value without worrying about breaking the script).&amp;nbsp; &lt;br /&gt;
&lt;br /&gt;
For example, if you have a server-side-based calculator that has the &lt;code&gt;+&lt;/code&gt;, &lt;code&gt;-&lt;/code&gt;, &lt;code&gt;*&lt;/code&gt;, &lt;code&gt;/&lt;/code&gt;, &lt;code&gt;=&lt;/code&gt; submit buttons, you can theoretically have the following scripts: &lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;
/api/add 
/api/subtract
/api/multiply
/api/divide
/api/equal &lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
And then have the buttons mapped to the name of &lt;code&gt;**add&lt;/code&gt;, &lt;code&gt;**subtract&lt;/code&gt;, &lt;code&gt;**multiply&lt;/code&gt;, &lt;code&gt;**divide&lt;/code&gt;, and &lt;code&gt;**equal&lt;/code&gt;, and map the form&#39;s action to &lt;code&gt;/api&lt;/code&gt;.&amp;nbsp; SHP will do the dispatching for you correctly. &lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;JSON Request &amp;amp; Responses &lt;/h3&gt;Besides working with query strings and XMLRPC request and responses, the same api script also can handle JSON requset &amp;amp; responses - you just need to POST the payload with the appropriate content-type, which is text/json. &lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;http&quot;&gt;&lt;code&gt;
POST /api/add2 HTTP/1.0 
Content-Type: text/json; charset=utf-8 

{ a : 50 , b : 90 } &lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
And the response will automatically be a JSON as well: &lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;http&quot;&gt;&lt;code&gt;
Content-Type: text/json; charset=utf-8 

140 &lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
Since JSON does not have a method name parameter, it does not have a corresponding partial path dispatch rule, but the query string&#39;s partial path dispatch rule remains in effect.&amp;nbsp; &lt;br /&gt;
&lt;br /&gt;
And of course if you are using JSON you will want to use JSONP, which you can by specifying an additional &lt;code&gt;~jsonp&lt;/code&gt; query string parameter as follows: &lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;http&quot;&gt;&lt;code&gt;
POST /api/add2?&lt;b&gt;~jsonp=myCallback&lt;/b&gt; HTTP/1.0 
Content-Type: text/json; charset=utf-8 

{ a : 50 , b : 90 } &lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
Which will generate the following response as a JSONP result: &lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;http&quot;&gt;&lt;code&gt;
Content-Type: text/json; charset=utf-8

return myCallback( 140 ); &lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
That explains the basic rules with regards to using the web api.&amp;nbsp; The next post will discuss the syntax of the web API, as well as how to extend it so you can use other types.</content><link rel='replies' type='application/atom+xml' href='http://weblambda.blogspot.com/feeds/1583379803792840301/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://weblambda.blogspot.com/2010/07/bzlibshpplt-04-now-available-web-api.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7570364353928786565/posts/default/1583379803792840301'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7570364353928786565/posts/default/1583379803792840301'/><link rel='alternate' type='text/html' href='http://weblambda.blogspot.com/2010/07/bzlibshpplt-04-now-available-web-api.html' title='BZLIB/SHP.plt 0.4 now available - Web API'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7570364353928786565.post-2945446479244175690</id><published>2010-01-18T18:48:00.000-08:00</published><updated>2010-01-18T19:45:35.038-08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="planet"/><category scheme="http://www.blogger.com/atom/ns#" term="plt scheme"/><category scheme="http://www.blogger.com/atom/ns#" term="proxy"/><title type='text'>BZLIB/PLANET.plt - A Local PLANET Proxy/Repository Server</title><content type='html'>&lt;a href=&quot;http://planet.plt-scheme.org/&quot;&gt;PLT&#39;s planet system&lt;/a&gt; is &lt;b&gt;great&lt;/b&gt; - if you want to install a planet module, you just declare it in the code, and it will automatically manage the download and the install for you, without you having to separately run another module installation programs like &lt;a href=&quot;http://www.cpan.org/&quot;&gt;Perl&#39;s CPAN&lt;/a&gt;, &lt;a href=&quot;http://docs.rubygems.org/&quot;&gt;Ruby&#39;s GEM&lt;/a&gt;, &lt;a href=&quot;http://pear.php.net/&quot;&gt;PHP&#39;s PEAR&lt;/a&gt;, etc.&lt;br /&gt;
&lt;br /&gt;
However, planet can still be improved.  Specifically, as there is currently only a single central repository, you might experience some inconvenient server outage from time to time.  And it is difficult to take advantage of the planet&#39;s automatic code distribution power unless you plan on releasing the code for public consumption.&lt;br /&gt;
&lt;br /&gt;
That is - until today. &lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;http://planet.plt-scheme.org/display.ss?package=planet.plt&amp;owner=bzlib&quot;&gt;&lt;code&gt;BZLIB/PLANET.plt&lt;/code&gt;&lt;/a&gt; is designed to solve exactly the issue of a single planet repository.  Going forward, you can install &lt;code&gt;bzlib/planet&lt;/code&gt; and run a local planet proxy and repository. &lt;code&gt;bzlib/planet&lt;/code&gt; is, of course as usual, available via planet under LGPL ;) &lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Usage&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;bzlib/planet&lt;/code&gt; contains a proxy server that you can setup and run.  The first thing to do is to install it via require:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(require (planet &lt;a href=&quot;http://planet.plt-scheme.org/display.ss?package=planet.plt&amp;owner=bzlib&quot;&gt;bzlib/planet/proxy&lt;/a&gt;)) 
&lt;/code&gt;&lt;/pre&gt;&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
After you have installed proxy, you can start the proxy server via:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(parameterize ((current-command-line-arguments #(&quot;-r&quot; &quot;/path/to/local/planet/repo&quot; &quot;-p&quot; &quot;9000&quot;)))
    (start!))
&lt;/code&gt;&lt;/pre&gt;Alternatively, you can also choose to start it via the included unix script (in the root directory of the installed location for &lt;code&gt;bzlib/planet.plt&lt;/code&gt;) called &quot;proxy&quot; if you are in Unix or Mac OSX.  And you can start it with something like:&lt;br /&gt;
&lt;pre class=&#39;shell&#39;&gt;&lt;code&gt;
# assume you are in the package root directory
# you might have to run chmod +x proxy first 
$ chmod +x proxy 
$ ./proxy -r &amp;lt;/path/to/local/planet/repo&amp;gt; -p &amp;lt;port=9000&amp;gt;
&lt;/code&gt;&lt;/pre&gt;which will parameterize the command line arguments and call &lt;code&gt;start!&lt;/code&gt; for you.&lt;br /&gt;
&lt;br /&gt;
The &lt;code&gt;-r&lt;/code&gt; argument points to the local repository path - it defaults to &lt;code&gt;/var/data/plt/planet-repo&lt;/code&gt;, and can be controlled via &lt;code&gt;-r&lt;/code&gt; or via environment variable named &lt;code&gt;BZLIB_PLANET_REPO_PATH&lt;/code&gt;.  Make sure the path you have created exists. &lt;br /&gt;
&lt;br /&gt;
The &lt;code&gt;-p&lt;/code&gt; argument holds the port number that the server will bind against, and it defaults to 9000.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;PLT Scheme Planet Configuration&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
To have your DrScheme or mzscheme call the planet proxy instead of the central planet repository, on version 4.2.3 or later, you can do so via the environent variable &lt;code&gt;PLTPLANETURL&lt;/code&gt;.  So let&#39;s say you have your planet proxy running on &lt;code&gt;http://planet-proxy:9000&lt;/code&gt;, you should then set the environment variable of &lt;code&gt;PLTPLANETURL&lt;/code&gt; to &lt;code&gt;http://planet-proxy:9000&lt;/code&gt;. &lt;br /&gt;
&lt;br /&gt;
If you are running a prior version of PLT, you can make manual modifications to the &lt;code&gt;collects/planet/config.ss&lt;/code&gt;, and change the parameter &lt;code&gt;HTTP-DOWNLOAD-SERVLET-URL&lt;/code&gt; to the url of your planet proxy. &lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Proxy Server Behavior&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
That&#39;s all you have to do to setup and use the proxy.  Your regular require statements would do exactly the same thing with the proxy. When you make a request of an uninstalled module, the planet module system will now call the proxy server, and the proxy server will check to see if the package is available locally, if so, and if the version constraint matches, the package will be sent to the planet module system, otherwise, the proxy server will make a call to the central repository to make the request.  If the central repository holds the module, it will be downloaded, stored in the repository and sent the to planet module system. &lt;br /&gt;
&lt;br /&gt;
Hence, subsequent calls will not require another trip to the central repository.  This decouples the proxy from the central repository so you do not need to maintain a live internet connection.  The downside is that the download count on the central repository for your module will no longer be accurate going forward ;) &lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Private Packages&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
A derived benefit for running a local planet proxy is that you now can take advantage of the automatic code distribution power without having to make your code publicly available, i.e., you can release private code via planet now.&lt;br /&gt;
&lt;br /&gt;
The way to do it is to simply create the appropriate directory structures, and drop your planet packages in them.&lt;br /&gt;
&lt;br /&gt;
The planet repository have the following directory structures:&lt;br /&gt;
&lt;code&gt;/&amp;lt;planet-user&amp;gt;/&amp;lt;package-name&amp;gt;/&amp;lt;major-version&amp;gt;/&amp;lt;minor-version&amp;gt;/&amp;lt;package-name&amp;gt;&lt;/code&gt;.  As an example, &lt;code&gt;bzlib/planet.plt&lt;/code&gt; has the following directory structure: &lt;code&gt;/bzlib/planet.plt/1/0/planet.plt&lt;/code&gt;. So just drop your packages in the appropriate directories, and the proxy will treat them as if they were released through the central repository. &lt;br /&gt;
&lt;br /&gt;
There are other enhancements that we can add to the package, such as mirroring the central repository, or automate the drop process, etc.  I would love to hear them if you have them.  For now - enjoy.</content><link rel='replies' type='application/atom+xml' href='http://weblambda.blogspot.com/feeds/2945446479244175690/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://weblambda.blogspot.com/2010/01/bzlibplanetplt-local-planet.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7570364353928786565/posts/default/2945446479244175690'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7570364353928786565/posts/default/2945446479244175690'/><link rel='alternate' type='text/html' href='http://weblambda.blogspot.com/2010/01/bzlibplanetplt-local-planet.html' title='BZLIB/PLANET.plt - A Local PLANET Proxy/Repository Server'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7570364353928786565.post-122741893811139242</id><published>2010-01-08T19:00:00.000-08:00</published><updated>2010-01-08T19:00:01.440-08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="parser"/><category scheme="http://www.blogger.com/atom/ns#" term="parser combinator"/><category scheme="http://www.blogger.com/atom/ns#" term="plt scheme"/><title type='text'>BZLIB/PARSEQ.plt - (4) Token-Based Parsers API</title><content type='html'>Previously we have looked at &lt;a href=&quot;http://weblambda.blogspot.com/2010/01/bzlibparseqplt-monadic-parser.html&quot;&gt;fundamental parsers &lt;/a&gt;and &lt;a href=&quot;http://weblambda.blogspot.com/2010/01/bzlibparseqplt-2-parser-combinators-api.html&quot;&gt;the combinators API&lt;/a&gt;, as well as &lt;a href=&quot;http://weblambda.blogspot.com/2010/01/bzlibparseqplt-3-common-parsers-api.html&quot;&gt;common parsers for character, number, and string&lt;/a&gt;, now it is time to look at token-based parsers provided by bzlib/parseq.&lt;br /&gt;
&lt;br /&gt;
A huge class of parsing involves tokenizing the streams by skipping over whitespaces.  For example, if we want to parse for a list of 3 integer, separated by comma, we currently have to write:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define three-int-by-comma 
  (seq whitespaces 
       i1 &lt;- integer 
       whitespaces 
       #\, 
       whitespaces 
       i2 &lt;- integer 
       whitespaces 
       #\, 
       whitespaces 
       i3 &lt;- integer 
       (return (list i1 i2 i3)))) 
&lt;/code&gt;&lt;/pre&gt;The code above looks messy, and it would be nice if we do not have to explicitly specify the parsing of whitespaces. &lt;code&gt;token&lt;/code&gt; allows us to abstract away the parsing of whitespaces:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define (token parser (delim whitespaces)) 
  (seq delim 
       t &lt;- parser 
       (return t))) 
&lt;/code&gt;&lt;/pre&gt;The above code can now be rewritten as: &lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define three-int-by-comma2 
  (seq i1 &lt;- (token integer) 
       (token #\,) 
       i2 &lt;- (token integer) 
       (token #\,) 
       i3 &lt;- (token integer) 
       (return (list i1 i2 i3)))) 
&lt;/code&gt;&lt;/pre&gt;Which looks a lot better.  But given tokenizing is such a common parsing task, we have a shorthand for the above called &lt;code&gt;tokens&lt;/code&gt;:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define-macro (tokens . exps) 
  (define (body exps) 
    (match exps 
      ((list exp) (list exp)) 
      ((list-rest v &#39;&lt;- exp rest) 
       `(,v &lt;- (token ,exp) . ,(body rest)))
      ((list-rest exp rest) 
       `((token ,exp) . ,(body rest)))))
  `(seq . ,(body exps)))
&lt;/code&gt;&lt;/pre&gt;Which will reduce the above parsing to the following:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define three-int-by-comma3
  (tokens i1 &lt;- integer 
          #\,
          i2 &lt;- integer 
          #\, 
          i3 &lt;- integer 
          (return (list i1 i2 i3)))) 
&lt;/code&gt;&lt;/pre&gt;There is a case insensitive version of &lt;code&gt;tokens&lt;/code&gt; called &lt;code&gt;tokens-ci&lt;/code&gt; that allows the character and string token to be parsed in case insensitive fashion.&lt;br /&gt;
&lt;br /&gt;
Besides tokenizing, another common need in token-based parsing is to handle delimited sets.  In the above example, the 3 integers are delimited by commas.  &lt;code&gt;delimited&lt;/code&gt; generalize the pattern:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define (delimited parser delim) 
  (tokens v &lt;- parser 
          v2 &lt;- (zero-many (tokens v3 &lt;- delim
                                   v4 &lt;- parser
                                   (return v4)))
          (return (cons v v2))))
&lt;/code&gt;&lt;/pre&gt;The following parses a list of comma-delimited integers: &lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(delimited integer #\,) 
&lt;/code&gt;&lt;/pre&gt;Another common pattern is to parse for brackets that surrounds the value that you need.  Just about all programming languages have such constructs.  And &lt;code&gt;bracket&lt;/code&gt; handles such parses:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define (bracket open parser close) 
  (tokens open
          v &lt;- parser 
          close 
          (return v))) 
&lt;/code&gt;&lt;/pre&gt;And &lt;code&gt;bracket/delimited&lt;/code&gt; combines the case where you need to parse a bracketed delimited values:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define (bracket/delimited open parser delim close) 
  (tokens open ;; even the parser is optional...  
          v &lt;- (zero-one (delimited parser delim) &#39;()) 
          close 
          (return v)))
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
That&#39;s it for the &lt;code&gt;bzlib/parseq&lt;/code&gt; API.  If you find anything missing, please let me know.&lt;br /&gt;
&lt;br /&gt;
Enjoy.</content><link rel='replies' type='application/atom+xml' href='http://weblambda.blogspot.com/feeds/122741893811139242/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://weblambda.blogspot.com/2010/01/bzlibparseqplt-4-token-based-parsers.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7570364353928786565/posts/default/122741893811139242'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7570364353928786565/posts/default/122741893811139242'/><link rel='alternate' type='text/html' href='http://weblambda.blogspot.com/2010/01/bzlibparseqplt-4-token-based-parsers.html' title='BZLIB/PARSEQ.plt - (4) Token-Based Parsers API'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7570364353928786565.post-4392870323554293952</id><published>2010-01-08T11:03:00.000-08:00</published><updated>2010-01-08T11:03:39.958-08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="parser"/><category scheme="http://www.blogger.com/atom/ns#" term="parser combinator"/><category scheme="http://www.blogger.com/atom/ns#" term="plt scheme"/><title type='text'>BZLIB/PARSEQ.plt - (3) Common Parsers API</title><content type='html'>Previously we have looked at &lt;a href=&quot;http://weblambda.blogspot.com/2010/01/bzlibparseqplt-monadic-parser.html&quot;&gt;fundamental parsers &lt;/a&gt;and &lt;a href=&quot;http://weblambda.blogspot.com/2010/01/bzlibparseqplt-2-parser-combinators-api.html&quot;&gt;the combinators API&lt;/a&gt;, now it is time to look at some common parsers provided by bzlib/parseq.&lt;br /&gt;
&lt;br /&gt;
In this case, since we are constructing these parsers on top of the fundamental parsers and combinators, we will show the definitions accordingly.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Character Category Parsers&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;digit&lt;/code&gt; is a character between &lt;code&gt;#\0&lt;/code&gt; and &lt;code&gt;#\9&lt;/code&gt;.&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define digit (char-between #\0 #\9)) 
&lt;/code&gt;&lt;/pre&gt;&lt;code&gt;not-digit&lt;/code&gt; is a character not between &lt;code&gt;#\0&lt;/code&gt; and &lt;code&gt;#\9&lt;/code&gt;.&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define not-digit (char-not-between #\0 #\9))
&lt;/code&gt;&lt;/pre&gt;&lt;code&gt;lower-case&lt;/code&gt; is a character beween &lt;code&gt;#\a&lt;/code&gt; and &lt;code&gt;#\z&lt;/code&gt;.&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define lower-case (char-between #\a #\z)) 
&lt;/code&gt;&lt;/pre&gt;&lt;code&gt;upper-case&lt;/code&gt; is a character between &lt;code&gt;#\A&lt;/code&gt; and &lt;code&gt;#\Z&lt;/code&gt;.&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define upper-case (char-between #\A #\Z))
&lt;/code&gt;&lt;/pre&gt;&lt;code&gt;alpha&lt;/code&gt; is either an &lt;code&gt;lower-case&lt;/code&gt; or &lt;code&gt;upper-case&lt;/code&gt; character.&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define alpha (choice lower-case upper-case)) 
&lt;/code&gt;&lt;/pre&gt;&lt;code&gt;alphanumeric&lt;/code&gt; is either an &lt;code&gt;alpha&lt;/code&gt; character or a &lt;code&gt;digit&lt;/code&gt; character.&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define alphanumeric (choice alpha digit)) 
&lt;/code&gt;&lt;/pre&gt;&lt;code&gt;whitespace&lt;/code&gt; is either a space, return, newline, tab, or vertical tab.&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define whitespace (char-in &#39;(#\space #\return #\newline #\tab #\vtab)))
&lt;/code&gt;&lt;/pre&gt;&lt;code&gt;not-whitespace&lt;/code&gt; is a character that is not a whitespace.&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define not-whitespace (char-not-in &#39;(#\space #\return #\newline #\tab #\vtab)))
&lt;/code&gt;&lt;/pre&gt;&lt;code&gt;whitespaces&lt;/code&gt; parses for zero or more whitespace characters:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define whitespaces (zero-many whitespace))
&lt;/code&gt;&lt;/pre&gt;&lt;code&gt;ascii&lt;/code&gt; is a charater bewteen 0 to 127:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define ascii (char-between (integer-&gt;char 0) (integer-&gt;char 127)))
&lt;/code&gt;&lt;/pre&gt;&lt;code&gt;word&lt;/code&gt; is either an alphanumeric or an underscore:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define word (choice alphanumeric (char= #\_)))
&lt;/code&gt;&lt;/pre&gt;&lt;code&gt;not-word&lt;/code&gt; is a character that is not a word:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define not-word (char-when (lambda (c) 
                              (not (or (char&lt;=? #\a c #\z)
                                       (char&lt;=? #\A c #\Z)
                                       (char&lt;=? #\0 c #\9) 
                                       (char=? c #\_))))))
&lt;/code&gt;&lt;/pre&gt;Finally, &lt;code&gt;newline&lt;/code&gt; parses for either CR, LF, or CRLF:&lt;br /&gt;
&lt;! -- more --&gt; &lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define newline 
  (choice (seq r &lt;- (char= #\return) 
               n &lt;- (char= #\newline)
               (return (list r n)))
          (char= #\return)
          (char= #\newline)))
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
&lt;b&gt;Number Parsers&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;sign&lt;/code&gt; parses for either + or -, and defaults to +.&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define sign (zero-one (char= #\-) #\+))
&lt;/code&gt;&lt;/pre&gt;&lt;code&gt;natural&lt;/code&gt; parses for 1+ digits: &lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define natural (one-many digit)) 
&lt;/code&gt;&lt;/pre&gt;&lt;code&gt;decimal&lt;/code&gt; parses for a number with decimal points:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define decimal (seq number &lt;- (zero-many digit)
                     point &lt;- (char= #\.)
                     decimals &lt;- natural 
                     (return (append number (cons point decimals)))))
&lt;/code&gt;&lt;/pre&gt;&lt;code&gt;positive&lt;/code&gt; parses for either natural or decimal.  Note decimal needs to be placed first since natural will succeed when parsing a decimal:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define positive (choice decimal natural)) 
&lt;/code&gt;&lt;/pre&gt;The above parsers returns the characters that represents the positive numbers.  To get it to return numbers, as well as parsing for both positive and negative numbers, we have a couple of helpers:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
;; make-signed will parse for the sign and the number.
(define (make-signed parser)
  (seq +/- &lt;- sign
       number &lt;- parser 
       (return (cons +/- number)))) 

;; make-number will convert the parsed digits into number. 
(define (make-number parser)
  (seq n &lt;- parser 
       (return (string-&gt;number (list-&gt;string n)))))
&lt;/code&gt;&lt;/pre&gt;Then &lt;code&gt;natural-number&lt;/code&gt; parses and returns a natural number:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define natural-number (make-number natural))
&lt;/code&gt;&lt;/pre&gt;&lt;code&gt;integer&lt;/code&gt; will parse and returns an integer (signed): &lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define integer (make-number (make-signed natural))) 
&lt;/code&gt;&lt;/pre&gt;&lt;code&gt;positive-number&lt;/code&gt; will parse and return a positive number (integer or real): &lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define positive-number (make-number positive)) 
&lt;/code&gt;&lt;/pre&gt;&lt;code&gt;real-number&lt;/code&gt; will parse and return a signed number, integer or real:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define positive-number (make-number positive)) 
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
&lt;b&gt;String Parsers&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
The following parsers parses for quoted string and returns the inner content as a string. &lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;escaped-char&lt;/code&gt; parses for characters that were part of an escaped sequence.  This exists for characters such as &lt;code&gt;\n&lt;/code&gt; (which should return a &lt;code&gt;#\newline&lt;/code&gt;), and character such as &lt;code&gt;\&quot;&lt;/code&gt; (which should return just &lt;code&gt;&quot;&lt;/code&gt;): &lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define (escaped-char escape char (as #f)) 
  (seq (char= escape) 
       c &lt;- (if (char? char) (char= char) char)
       (return (if as as c)))) 

;; e-newline 
(define e-newline (escaped-char #\\ #\n #\newline)) 

;; e-return 
(define e-return (escaped-char #\\ #\r #\return)) 

;; e-tab 
(define e-tab (escaped-char #\\ #\t #\tab)) 

;; e-backslash 
(define e-backslash (escaped-char #\\ #\\))
&lt;/code&gt;&lt;/pre&gt;&lt;code&gt;quoted&lt;/code&gt; parses for the quoted string pattern (including escapes): &lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
;; quoted 
;; a specific string-based bracket parser 
(define (quoted open close escape)
  (seq (char= open) 
       atoms &lt;- (zero-many (choice e-newline 
                                   e-return 
                                   e-tab 
                                   e-backslash 
                                   (escaped-char escape close) 
                                   (char-not-in  (list close #\\)))) 
       (char= close)
       (return atoms)))
&lt;/code&gt;&lt;/pre&gt;&lt;code&gt;make-quoted-string&lt;/code&gt; abstracts the use of &lt;code&gt;quoted&lt;/code&gt;.&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define (make-quoted-string open (close #f) (escape #\\)) 
  (seq v &lt;- (quoted open (if close close open) escape)
       (return (list-&gt;string v))))
&lt;/code&gt;&lt;/pre&gt;Then &lt;code&gt;single-quoted-string&lt;/code&gt; and &lt;code&gt;double-quoted-string&lt;/code&gt; look like the following:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define single-quoted-string (make-quoted-string #\&#39;))

(define double-quoted-string (make-quoted-string #\&quot;))
&lt;/code&gt;&lt;/pre&gt;Finally, &lt;code&gt;quoted-string&lt;/code&gt; will parse both &lt;code&gt;single-quoted-string&lt;/code&gt; and &lt;code&gt;double-quoted-string&lt;/code&gt;:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define quoted-string 
  (choice single-quoted-string double-quoted-string))
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
That is it for now - we will talk about parsing tokens next. Enjoy.</content><link rel='replies' type='application/atom+xml' href='http://weblambda.blogspot.com/feeds/4392870323554293952/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://weblambda.blogspot.com/2010/01/bzlibparseqplt-3-common-parsers-api.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7570364353928786565/posts/default/4392870323554293952'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7570364353928786565/posts/default/4392870323554293952'/><link rel='alternate' type='text/html' href='http://weblambda.blogspot.com/2010/01/bzlibparseqplt-3-common-parsers-api.html' title='BZLIB/PARSEQ.plt - (3) Common Parsers API'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7570364353928786565.post-5582151141777261323</id><published>2010-01-06T18:14:00.000-08:00</published><updated>2010-01-06T18:14:24.984-08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="parser"/><category scheme="http://www.blogger.com/atom/ns#" term="parser combinator"/><category scheme="http://www.blogger.com/atom/ns#" term="plt scheme"/><title type='text'>BZLIB/PARSEQ.PLT - (2) Parser Combinators API</title><content type='html'>[&lt;a href=&quot;http://weblambda.blogspot.com/2010/01/bzlibparseqplt-monadic-parser.html&quot;&gt;Continuing the previous post&lt;/a&gt; on the API of &lt;code&gt;bzlib/parseq&lt;/code&gt;]&lt;br /&gt;
&lt;br /&gt;
Previously we have looked at &lt;a href=&quot;http://weblambda.blogspot.com/2010/01/bzlibparseqplt-monadic-parser.html&quot;&gt;the basic parsers that peeked into inputs&lt;/a&gt;, now it&#39;s time to look at the combinators.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Basic Parser Combinators&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;bind&lt;/code&gt; is the &quot;bind operator&quot; for the parsers, with the following signature: &lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(-&gt; Parser/c (-&gt; any/c Parser/c) Parser/c) 
&lt;/code&gt;&lt;/pre&gt;It takes in a parser, a &quot;transform&quot; function that will consume the returned value from the first parser and transform the value into another parser, and combine both into another parser. &lt;br /&gt;
&lt;br /&gt;
As there are other more specialized combinators, you should not need to use &lt;code&gt;bind&lt;/code&gt; directly unless you are trying to compose functions during run-time.&lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;result&lt;/code&gt; simplifies the transform function you need to write so your transform function only need to return the value, not another parser. Below is the definition of &lt;code&gt;result&lt;/code&gt;:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define (result parser helper)
  (bind parser 
        (lambda (v) 
          (if v 
              (return (helper v))  
              fail))))
&lt;/code&gt;&lt;/pre&gt;&lt;code&gt;result*&lt;/code&gt; works like &lt;code&gt;result&lt;/code&gt;, but it only works with a return value that is a list, and it applies the transform against the value.  This comes in handy when you know the parser returns a list and you want to bind the list to individual arguments in your transform function.  Below is an example (&lt;code&gt;sequence*&lt;/code&gt; combinator is explained later): &lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(result* (sequence* (char= #\b) (char= #\a) (char= #\r)) 
         (lambda (b a r) ;; b maps to #\b, a maps to #\a, and r maps to #\r 
            (list-&gt;string (list b a r))))
&lt;/code&gt;&lt;/pre&gt;&lt;b&gt;Multi-Parser Combinators&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;seq&lt;/code&gt; is a macro-based combinator that bounds multiple parsers in succession, and returns the results only if all parsers succeed.  The design of this parser is inspired by &lt;a href=&quot;http://shaurz.wordpress.com/2008/03/11/haskell-style-parser-combinators-in-scheme/&quot;&gt;Shaurz&#39;s Haskell-style parser combinator&lt;/a&gt;.&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
The default form of &lt;code&gt;seq&lt;/code&gt; is &lt;code&gt;(seq &amp;lt;parser&amp;gt;)&lt;/code&gt;, which is equivalent of &lt;code&gt;&amp;lt;parser&amp;gt;&lt;/code&gt;. An &lt;code&gt;seq&lt;/code&gt; expression must end in the first form. &lt;br /&gt;
&lt;br /&gt;
The second form of &lt;code&gt;seq&lt;/code&gt; allows you to create lexical binding against the value of the intermediate parser so you can manipulate the result with the last parser.  An example to parse 2 separate digit and sum by with the first being multiplied by 2 and the second multiplied by 4: &lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(seq &lt;b&gt;a &amp;lt;-&lt;/b&gt; (char= #\0 #\9) 
     &lt;b&gt;b &amp;lt;-&lt;/b&gt; (char= #\0 #\9) 
     (return (+ (* 2 (string-&gt;number (string &lt;b&gt;a&lt;/b&gt;))) 
                (* 4 (string-&gt;number (string &lt;b&gt;b&lt;/b&gt;)))))) 
&lt;/code&gt;&lt;/pre&gt;This form, however, cannot be the last statement in &lt;code&gt;seq&lt;/code&gt;, i.e., you must have other parser statements after a binding (otherwise why do you need the binding?).  &lt;br /&gt;
&lt;br /&gt;
The last form of &lt;code&gt;seq&lt;/code&gt; behaves like the second form but does not create a binding, so it looks like the first form.  &lt;br /&gt;
&lt;br /&gt;
An example is to parse away the parentheses surrounding a digit: &lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(seq (char= #\() ;; 3rd form - no lexical binding 
     &lt;b&gt;digit &amp;lt;-&lt;/b&gt; (char= #\0 #\9) ;; 2nd form - binds digit 
     (char= #\)) ;; 3rd form - no lexical binding 
     (return (string-&gt;number (string &lt;b&gt;digit&lt;/b&gt;))) ;; 1st form 
     )
&lt;/code&gt;&lt;/pre&gt;&lt;code&gt;seq&lt;/code&gt; is perhaps the most versatile combinator that you will use to create other combinators and parsers. &lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;sequence&lt;/code&gt; is the functional version of &lt;code&gt;seq&lt;/code&gt; that takes a list of parsers as its argument:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(sequence (list parser ...)) 
&lt;/code&gt;&lt;/pre&gt;&lt;code&gt;sequence&lt;/code&gt; has the following limitations:  &lt;br /&gt;
&lt;ul&gt;&lt;li&gt;&lt;code&gt;sequence&lt;/code&gt; do not allow for custom bindings&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sequence&lt;/code&gt; cannot be used to define recursive parser (its inner parser must be defined first)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sequence&lt;/code&gt; do not handle custom transformation of the results - you should use it with &lt;code&gt;result*&lt;/code&gt; to bind the transformation (see above).&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
&lt;code&gt;sequence*&lt;/code&gt; is the variable args version of &lt;code&gt;sequence&lt;/code&gt;, i.e. &lt;code&gt;(sequence* parser ...)&lt;/code&gt;. &lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;choice&lt;/code&gt; is a macro combinator that combines mutliple parsers so the first one that succeeds will return the value: &lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
;; returns if the next char is either #\a, #\b, or #\c.  Else fail. 
(choice (char= #\a) (char= #\b) (char= #\c)) 
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
&lt;code&gt;one-of&lt;/code&gt; is the functional version of &lt;code&gt;choice&lt;/code&gt; that takes a list of parsers as its argument: &lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(one-of (list parser ...)) 
&lt;/code&gt;&lt;/pre&gt;Since this is an function combinator, unlike &lt;code&gt;choice&lt;/code&gt;, you cannot define recursive parser with it.&lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;one-of*&lt;/code&gt; is the variable arg version of &lt;code&gt;one-of&lt;/code&gt;, i.e., &lt;code&gt;(one-of* parser ...)&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;all-of&lt;/code&gt; requires all of the inner parsers to succeed, and &lt;b&gt;it returns the result from the last parser&lt;/b&gt;:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
;; an contrived example: parse successfully if the next char is #\e 
(all-of (list alpha (char-between #\d #\f) (char= #\e))) 
&lt;/code&gt;&lt;/pre&gt;Remember it is the last result being returned, so if you inner parsers requires different amount of bytes matched, make sure the one that peek the most bytes gets matched last. &lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;all-of*&lt;/code&gt; is the variable arg list version of &lt;code&gt;all-of&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Repetition-Based Combinators&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;repeat&lt;/code&gt; is the most flexible repetition-based combinators:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(repeat &amp;lt;parser&amp;gt; &amp;lt;minimum&amp;gt; &amp;lt;maximum&amp;gt;) 
&lt;/code&gt;&lt;/pre&gt;The &lt;code&gt;minimum&lt;/code&gt; argument defaults to 1, and the &lt;code&gt;maximum&lt;/code&gt; argument defaults to positive infinity, which corresponds to the &lt;code&gt;one-many&lt;/code&gt; combinator:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define (one-many parser) 
  (repeat parser 1 +inf.0)) 
&lt;/code&gt;&lt;/pre&gt;And &lt;code&gt;zero-many&lt;/code&gt; maps to:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define (zero-many parser) 
  (repeat parser 0 +inf.0)) 
&lt;/code&gt;&lt;/pre&gt;Note that all three parsers will return a list that contains each of the successful parses.  &lt;code&gt;zero-many&lt;/code&gt; will return a list even if the inner parser does not match, so &lt;code&gt;zero-many&lt;/code&gt; will always successfully parse. &lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;zero-one&lt;/code&gt; is a special repetition-based combinator in that it does not return a list.  Instead, it allows you to parse for a single occurence, and if it fails, you can specify a default value to substitute for the failure: &lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
;; example of parsing for &quot;foo&quot; but returns &quot;bar&quot; if the parse failed. 
(zero-one (string= &quot;foo&quot;) &quot;bar&quot;) 
&lt;/code&gt;&lt;/pre&gt;Hence &lt;code&gt;zero-one&lt;/code&gt; will always succeed as well. &lt;br /&gt;
&lt;br /&gt;
That&#39;s it for the combinators. We&#39;ll discuss some pre-defined parsers in the next post.  Stay tuned.</content><link rel='replies' type='application/atom+xml' href='http://weblambda.blogspot.com/feeds/5582151141777261323/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://weblambda.blogspot.com/2010/01/bzlibparseqplt-2-parser-combinators-api.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7570364353928786565/posts/default/5582151141777261323'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7570364353928786565/posts/default/5582151141777261323'/><link rel='alternate' type='text/html' href='http://weblambda.blogspot.com/2010/01/bzlibparseqplt-2-parser-combinators-api.html' title='BZLIB/PARSEQ.PLT - (2) Parser Combinators API'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7570364353928786565.post-7456340305354779277</id><published>2010-01-05T18:53:00.000-08:00</published><updated>2010-01-06T14:38:11.701-08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="parser"/><category scheme="http://www.blogger.com/atom/ns#" term="parser combinator"/><category scheme="http://www.blogger.com/atom/ns#" term="plt scheme"/><title type='text'>BZLIB/PARSEQ.plt - a Monadic Parser Combinator Library</title><content type='html'>&lt;a href=&quot;http://planet.plt-scheme.org/display.ss?package=parseq.plt&amp;owner=bzlib&quot;&gt;BZLIB/PARSEQ.plt is now available via PLANET&lt;/a&gt;.  Inspired by &lt;a href=&quot;http://www.haskell.org/haskellwiki/Parsec&quot;&gt;Haskell&#39;s Parsec&lt;/a&gt; and &lt;a href=&quot;http://shaurz.wordpress.com/2008/03/11/haskell-style-parser-combinators-in-scheme/&quot;&gt;Shaurz&#39;s Haskell-style parser combinator&lt;/a&gt;, bzlib/parsec provides a monadic parser combinator library that can handle both character and binary data parsing. &lt;br /&gt;
&lt;br /&gt;
If you need a refresher on parser combinators, you can read &lt;a href=&quot;http://weblambda.blogspot.com/2009/10/building-parser-combinators-in-scheme.html&quot;&gt;my previous posts&lt;/a&gt; for &lt;a href=&quot;http://weblambda.blogspot.com/2009/10/building-parser-combinators-in-scheme-2.html&quot;&gt;a quick tour&lt;/a&gt;. &lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Installation&lt;/b&gt; &lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(require (planet &lt;a href=&quot;http://planet.plt-scheme.org/display.ss?package=parseq.plt&amp;owner=bzlib&quot;&gt;bzlib/parseq&lt;/a&gt;)) 
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
The package includes the following examples that you can inspect for the usage of the API:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(require (planet &lt;a href=&quot;http://planet.plt-scheme.org/package-source/bzlib/parseq.plt/1/1/example/csv.ss&quot;&gt;bzlib/parseq/example/csv&lt;/a&gt;) ;; a customizable csv parser 
         (planet &lt;a href=&quot;http://planet.plt-scheme.org/package-source/bzlib/parseq.plt/1/1/example/calc.ss&quot;&gt;bzlib/parseq/example/calc&lt;/a&gt;) ;; a simple calculator with parens 
         (planet &lt;a href=&quot;http://planet.plt-scheme.org/package-source/bzlib/parseq.plt/1/1/example/regex.ss&quot;&gt;bzlib/parseq/example/regex&lt;/a&gt;) ;; a simplified regular expression parser &amp; evaluator 
         (planet &lt;a href=&quot;http://planet.plt-scheme.org/package-source/bzlib/parseq.plt/1/1/example/sql.ss&quot;&gt;bzlib/parseq/example/sql&lt;/a&gt;) ;; parsing SQL&#39;s create table statement 
         (planet &lt;a href=&quot;http://planet.plt-scheme.org/package-source/bzlib/parseq.plt/1/1/example/json.ss&quot;&gt;bzlib/parseq/example/json&lt;/a&gt;) ;; parses JSON data format 
         ) 
&lt;/code&gt;&lt;/pre&gt;&lt;b&gt;Parser Type Signature &amp; Input&lt;/b&gt;&lt;br /&gt;
A parser is a function that has the following signature: &lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(-&gt; Input/c (values any/c Input/c)) ;; returns the value and the next input 
&lt;/code&gt;&lt;/pre&gt;If the value is &lt;code&gt;#f&lt;/code&gt; then the parse has failed.  This might be changed in the future to another value so you can return #f as a parsed value. &lt;br /&gt;
&lt;br /&gt;
The &lt;code&gt;input&lt;/code&gt; is a struct with the following structure: &lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define-struct input (source pos) #:prefab) 
&lt;/code&gt;&lt;/pre&gt;It is an abstraction over an &lt;code&gt;input-port&lt;/code&gt; so you can keep track of the current position on the port.  The function &lt;code&gt;make-input&lt;/code&gt; will take in an input-port, a string, or a byte and return an &lt;code&gt;input&lt;/code&gt; struct with the position initiated to 0. &lt;br /&gt;
&lt;br /&gt;
During the parsing, the values are peeked instead of read so we can backtrack to the beginning.  That means when you finished parsing, all of the data are still in the port.  &lt;code&gt;make-reader&lt;/code&gt; wraps over a parser so you can just pass an &lt;code&gt;input-port&lt;/code&gt; instead of needing to create an &lt;code&gt;input&lt;/code&gt; struct, and it also consumed the bytes if the parse is successful:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(make-reader parser) ;; =&gt; (-&gt; input-port? any/c) 
&lt;/code&gt;&lt;/pre&gt;&lt;b&gt;Fundamental Parsers (input not peeked)&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
The following parsers do not peek into the input. &lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;(return &amp;lt;v&amp;gt;)&lt;/code&gt; returns &lt;code&gt;&amp;lt;v&amp;gt;&lt;/code&gt; that you specify. &lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;fail&lt;/code&gt; returns a &lt;code&gt;failed&lt;/code&gt; struct that includes the position of the parse when the parser fails.  The &lt;code&gt;failed&lt;/code&gt; struct is currently defined as follows: &lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define-struct failed (pos) #:prefab) 
&lt;/code&gt;&lt;/pre&gt;&lt;code&gt;succeeded?&lt;/code&gt; and &lt;code&gt;failed?&lt;/code&gt; tests whether the returned value is a &lt;code&gt;failed&lt;/code&gt; struct (&lt;code&gt;succeeded?&lt;/code&gt; equals &lt;code&gt;(compose not failed?)&lt;/code&gt;). &lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;SOF&lt;/code&gt; (start of file) will return &lt;code&gt;&#39;sof&lt;/code&gt; if it is the start of the input (i.e., position = 0). &lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Fundamental Parsers (input peeked)&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;item&lt;/code&gt; peeks the input, test the input to ensure it satisfies a criteria, and if so, returns the value and advance the port by the size of the peeked value:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(item &amp;lt;peek&amp;gt; &amp;lt;isa?&amp;gt; &amp;lt;satisfy?&amp;gt; &amp;lt;size&amp;gt;) 
peek =&gt; (-&gt; Input/c any/c) 
isa? =&gt; (-&gt; any/c boolean?) 
satisfy? =&gt; (-&gt; any/c any) 
size =&gt; (-&gt; any/c exact-integer?) 
&lt;/code&gt;&lt;/pre&gt;&lt;code&gt;isa?&lt;/code&gt; tests for the return value&#39;s type so you can simplify the writing of &lt;code&gt;satisfy?&lt;/code&gt;, which can assume the value is of the right type.  &lt;br /&gt;
&lt;br /&gt;
You use &lt;code&gt;item&lt;/code&gt; only when you need to create new parsers that the library do not already provide. &lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Non-Character Type Parsers&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;bzlib/parseq&lt;/code&gt; allows non-character parsers so you can use it to parse binary data instead of just text streams.  You can mix them together of course. &lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;(bytes= &amp;lt;bytes&amp;gt;)&lt;/code&gt; returns when the next set of bytes equals the passed in bytes.  For example: &lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
&gt; ((bytes= #&quot;foo&quot;) (make-input &quot;foo bar&quot;))
#&quot;foo&quot;
#s(input #&lt;input-port:string&gt; 3)
&lt;/code&gt;&lt;/pre&gt;&lt;code&gt;(string= &amp;lt;string&amp;gt;)&lt;/code&gt; returns when the next set of bytes (in string) equals the passed in string.&lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;(string-ci= &amp;lt;string&amp;gt;)&lt;/code&gt; is the case-insensitive version of &lt;code&gt;string=&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;(byte= &amp;lt;byte&amp;gt;)&lt;/code&gt; returns when the next byte equals the passed in byte. &lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;(bits= &amp;lt;bits&amp;gt;)&lt;/code&gt; returns the next byte when it equals the passed in bits (a list of 8 0&#39;s or 1&#39;s). &lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;byte=&lt;/code&gt; and &lt;code&gt;bits=&lt;/code&gt; are built on top of &lt;code&gt;byte-when&lt;/code&gt;, which is built on top of &lt;code&gt;item&lt;/code&gt;.  &lt;code&gt;byte-when&lt;/code&gt; has the following signature: &lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(byte-when &amp;lt;satisfy?&amp;gt; (&amp;lt;isa?&amp;gt; byte?) (&amp;lt;size&amp;gt; (the-number 1))) 
;; (the-number &amp;lt;n&amp;gt;) returns a lambda that returns &amp;lt;n&amp;gt;  
&lt;/code&gt;&lt;/pre&gt;&lt;code&gt;EOF&lt;/code&gt; is also built on top of &lt;code&gt;byte-when&lt;/code&gt; as &lt;code&gt;(byte-when identity eof-object? (the-number 0))&lt;/code&gt;. &lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Character-Based Parsers&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
The counterpart to &lt;code&gt;byte-when&lt;/code&gt; for character-based parsers is &lt;code&gt;char-when&lt;/code&gt;, which has the following signature: &lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(item &amp;lt;satisfy?&amp;gt; (&amp;lt;isa?&amp;gt; char?) char-utf-8-length)
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
The following are built on top of &lt;code&gt;char-when&lt;/code&gt;: &lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;(char= &amp;lt;c&amp;gt;)&lt;/code&gt; returns &lt;code&gt;&amp;lt;c&amp;gt;&lt;/code&gt; when the next character equals &lt;code&gt;&amp;lt;c&amp;gt;&lt;/code&gt;. &lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;(char-not= &amp;lt;c&amp;gt;)&lt;/code&gt; is the opposite of &lt;code&gt;char=&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;(char-ci=? &amp;lt;c&amp;gt;)&lt;/code&gt; is the ci (case-insensitive) version of &lt;code&gt;char=&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;(char-not-ci=? &amp;lt;c&amp;gt;)&lt;/code&gt; is the opposite of &lt;code&gt;char-ci=?&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;(char-between &amp;lt;lc&amp;gt; &amp;lt;hc&amp;gt;)&lt;/code&gt; returns the next char when it falls between &lt;code&gt;&amp;lt;lc&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;hc&amp;gt;&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;(char-not-between &amp;lt;lc&amp;gt; &amp;lt;hc&amp;gt;)&lt;/code&gt; is the opposite of &lt;code&gt;char-between&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;(char-ci-between &amp;lt;lc&amp;gt; &amp;lt;hc&amp;gt;)&lt;/code&gt; is the ci version of &lt;code&gt;char-between&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;(char-ci-not-between &amp;lt;lc&amp;gt; &amp;lt;hc&amp;gt;)&lt;/code&gt; is the opposite of &lt;code&gt;char-ci-between&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;(char-in &amp;lt;chars&amp;gt;)&lt;/code&gt; returns the next char when it is one of the characters in &lt;code&gt;&amp;lt;chars&amp;gt;&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;(char-not-in &amp;lt;chars&amp;gt;)&lt;/code&gt; is the opposite of &lt;code&gt;char-in&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;(char-ci-in &amp;lt;chars&amp;gt;)&lt;/code&gt; is the ci version of &lt;code&gt;char-in&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;(char-ci-not-in &amp;lt;chars&amp;gt;)&lt;/code&gt; is the opposite of &lt;code&gt;char-ci-in&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Literal Parsers&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;literal&lt;/code&gt; is used to abstract the parsers that basically performs an equal comparison (e.g., &lt;code&gt;string=&lt;/code&gt;, &lt;code&gt;byte=&lt;/code&gt;, etc), as well as allowing an inner parser to pass through, so you do not have to explicitly choose between &lt;code&gt;char=&lt;/code&gt;, &lt;code&gt;string=&lt;/code&gt;, etc., based on the argument.  Example:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(literal #\a) ;; =&gt; (char= #\a) 
(literal &quot;abc&quot;) ;; =&gt; (string= &quot;abc&quot;) 
(literal any-byte) =&gt; any-byte 
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
&lt;code&gt;literal-ci&lt;/code&gt; is the case-insensitive version of &lt;code&gt;literal&lt;/code&gt;, the difference is that it will return the case-insensitive parser for character and string:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(literal-ci #\a) ;; =&gt; (char-ci= #\a) 
(literal-ci &quot;abc&quot;) ;; =&gt; (string-ci= &quot;abc&quot;) 
(literal-ci #&quot;abc&quot;) ;; =&gt; (literal #&quot;abc&quot;) 
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
That about sums it up for the basic parsers.  The next post will document the combinators.  Stay tuned.</content><link rel='replies' type='application/atom+xml' href='http://weblambda.blogspot.com/feeds/7456340305354779277/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://weblambda.blogspot.com/2010/01/bzlibparseqplt-monadic-parser.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7570364353928786565/posts/default/7456340305354779277'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7570364353928786565/posts/default/7456340305354779277'/><link rel='alternate' type='text/html' href='http://weblambda.blogspot.com/2010/01/bzlibparseqplt-monadic-parser.html' title='BZLIB/PARSEQ.plt - a Monadic Parser Combinator Library'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7570364353928786565.post-7579996122494745555</id><published>2009-12-22T14:47:00.000-08:00</published><updated>2009-12-22T14:50:55.428-08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="http"/><category scheme="http://www.blogger.com/atom/ns#" term="plt scheme"/><category scheme="http://www.blogger.com/atom/ns#" term="xml"/><title type='text'>BZLIB/XML.plt - An XML Utility for Xexpr and SXML</title><content type='html'>&lt;a href=&quot;http://planet.plt-scheme.org/display.ss?package=xml.plt&amp;owner=bzlib&quot;&gt;BZLIB/XML.plt&lt;/a&gt; is now available via PLANET, it provides the following utilities to help with XML manipulation:&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;convert to/from xexpr to sxml&lt;/li&gt;
&lt;li&gt;reading sxml/xexpr from html or xml sources&lt;/li&gt;
&lt;li&gt;managing html and xml enities&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
&lt;b&gt;Installation&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(require (planet bzlib/xml)) 
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
&lt;b&gt;Xexpr and SXML&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
Although Xexpr is the default xml representation in PLT Scheme (and the web-server), it lacks the toolkits that SXML enjoys.  bzlib/xml helps by providing conversion functions to convert between sxml and xexpr:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
;; convert from xexpr to sxml 
(xexpr-&gt;sxml `(p ((class &quot;default&quot;)) &quot;an xexpr instance&quot;))
;; =&gt; `(p (@ (class &quot;default&quot;)) &quot;an xexpr instance&quot;) 

; convert from sxml to xexpr 
(sxml-&gt;xexpr `(p (@ (class &quot;default&quot;)) &quot;an xexpr instance&quot;))
;; =&gt; `(p ((class &quot;default&quot;)) &quot;an xexpr instance&quot;) 
&lt;/code&gt;&lt;/pre&gt;Converting from xexpr to sxml will allow you to use the facilities such as sxpath, ssax, and sxml-match with xexpr, and converting from sxml to xexpr will allow you to feed sxml into web-server for to generate output based on sxml.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Reading and Writing Xexpr/SXML&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;bzlib/xml&lt;/code&gt; provides &lt;code&gt;read-xexpr&lt;/code&gt; and &lt;code&gt;read-sxml&lt;/code&gt; to simplify the conversion from html sources to either xexpr or sxml:&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
;; reading xexpr 
(read-xexpr &amp;lt;input-port?&amp;gt;) 

;; reading sxml
(read-sxml &amp;lt;input-port?&amp;gt;) 
&lt;/code&gt;&lt;/pre&gt;The &lt;code&gt;&amp;lt;input-port?&amp;gt;&lt;/code&gt; can be an &lt;a href=&quot;http://weblambda.blogspot.com/2009/08/shp-02-and-jsmgr-01-http-01-are-now.html&quot;&gt;&lt;code&gt;http-client-response&lt;/code&gt; structure defined in &lt;code&gt;bzlib/http&lt;/code&gt;&lt;/a&gt;, which provides an content-type header that helps aid the determination of whether this is an html or xml document, for example: &lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(read-sxml (http-get &quot;http://www.google.com/&quot;)) 
&lt;/code&gt;&lt;/pre&gt;There are corresponding &lt;code&gt;write-sxml&lt;/code&gt; and &lt;code&gt;write-xexpr&lt;/code&gt; functions:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
;; write-xexpr 
(write-xexpr &amp;lt;xexpr?&amp;gt; &amp;lt;output-port?&amp;gt;) 
;; write-sxml
(write-sxml &amp;lt;sxml?&amp;gt; &amp;lt;output-port?&amp;gt;) 
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
&lt;b&gt;Managing Entities&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
As part of converting from xexpr to sxml you&#39;ll need to deal with normalizing the xml entities.  Since xexpr simply converts entities into symbols and numeric entities into numbers instead of converting them into final strings, bzlib/xml provides a &lt;code&gt;entity-&gt;string&lt;/code&gt; routine that&#39;ll convert the entity into strings for you.&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(entity-&gt;string &amp;lt;symbol or number entity&amp;gt;) 
&lt;/code&gt;&lt;/pre&gt;This is automatically called by &lt;code&gt;xexpr-&gt;sxml&lt;/code&gt;, &lt;code&gt;read-xepxr&lt;/code&gt;, and &lt;code&gt;read-sxml&lt;/code&gt;, so you generally do not have to use it explicitly, except to extend the entity mapping.&lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;entity-&gt;string&lt;/code&gt; converts entities by mapping numeric entities via against the unicode character map, and symbol entities via two separate entity mapping tables, one for predefined HTML entities, and the other is a parameterizable XML entities.&lt;br /&gt;
&lt;br /&gt;
The HTML entity table contains a set of pre-defined HTML entities that were mapped to the underlying character numeric code.  Generally you should not have to modify this set of entities, but if you need to, you can do so via &lt;code&gt;set-html-entities!&lt;/code&gt;:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(set-html-entities! &amp;lt;list of symbol/integer pairs&amp;gt;) 
;; example
(set-html-entities! `((nbsp . 160) (lt . 60) (gt . 62))) 
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
The XML entity table (&lt;code&gt;xml-entities&lt;/code&gt;) is parameterizable, and it takes a list of symbol and string pairs:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(parameterize ((xml-entities &#39;((lt . &quot;&lt;&quot;) (gt . &quot;&gt;&quot;)))) 
  (read-sxml ...)) 
&lt;/code&gt;&lt;/pre&gt;If you use the same symbol entity in both tables, the &lt;code&gt;xml-entities&lt;/code&gt; takes precedence.  &lt;br /&gt;
&lt;br /&gt;
Any unknown entity is mapped to the null character.&lt;br /&gt;
&lt;br /&gt;
That&#39;s it for now.  Enjoy.</content><link rel='replies' type='application/atom+xml' href='http://weblambda.blogspot.com/feeds/7579996122494745555/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://weblambda.blogspot.com/2009/12/bzlibxmlplt-xml-utility.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7570364353928786565/posts/default/7579996122494745555'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7570364353928786565/posts/default/7579996122494745555'/><link rel='alternate' type='text/html' href='http://weblambda.blogspot.com/2009/12/bzlibxmlplt-xml-utility.html' title='BZLIB/XML.plt - An XML Utility for Xexpr and SXML'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7570364353928786565.post-5910891301501022338</id><published>2009-11-15T22:31:00.000-08:00</published><updated>2009-11-15T22:32:24.783-08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="plt scheme"/><category scheme="http://www.blogger.com/atom/ns#" term="session"/><category scheme="http://www.blogger.com/atom/ns#" term="web"/><title type='text'>BZLIB/SESSION.plt - a Session Store via BZLIB/DBI.plt</title><content type='html'>I originally planned to write a series on &lt;a href=&quot;http://weblambda.blogspot.com/2009/11/building-web-session-store-1.html&quot;&gt;the development of a session store&lt;/a&gt;, but it turned out that there aren&#39;t that many things to write about, so I am just going to release the code via planet.  As usual, this is released under LGPL.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Installation&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
To download the planet package:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(require (planet bzlib/session)) 
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
The package comes with three separate database installation scripts (one each for sqlite, mysql, and postgresql).  You can call them via the following:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
;; installing to a sqlite database
(require (planet bzlib/session/setup/jsqlite)) 
(setup-session-store/jsqlite! &amp;lt;path-to-sqlite-db&amp;gt;) 

;; installing to a mysql database 
(require (planet bzlib/session/setup/jazmysql)) 
(setup-session-store/jazmysql! &amp;lt;host&amp;gt; &amp;lt;port&amp;gt; &amp;lt;user&amp;gt;  
                               &amp;lt;password&amp;gt; &amp;lt;schema&amp;gt;)

;; installing to a postgresql database 
(require (planet bzlib/session/setup/spgsql)) 
(setup-session-store/spgsql! &amp;lt;host&amp;gt; &amp;lt;port&amp;gt; &amp;lt;user&amp;gt;
                             &amp;lt;password&amp;gt; &amp;lt;database&amp;gt;) 
&lt;/code&gt;&lt;/pre&gt;&lt;b&gt;Known Issue&lt;/b&gt;: the script currently can only be run once and it assumes the table &lt;code&gt;session_t&lt;/code&gt; does not exist in the target database.  This will be rectified in the future.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Session ID&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
The session store uses uuid for session IDs.  &lt;code&gt;bzlib/base&lt;/code&gt; provides API for manipulation of uuids.&lt;br /&gt;
&lt;br /&gt;
To create an uuid, just run &lt;code&gt;(make-uuid)&lt;/code&gt;.  It can optionally takes in a parameter that are either an uuid in string, and then create the corresponding uuid structure (it can also takes in another uuid structure and make an equivalent uuid structure). &lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
&gt; (require (planet bzlib/base))
&gt; (make-uuid)
#&amp;lt;uuid:4ba52eac-a0b4-415a-88f5-57d1fadd1aba&amp;gt;
&gt; (make-uuid &quot;4ba52eac-a0b4-415a-88f5-57d1fadd1aba&quot;)
#&amp;lt;uuid:4ba52eac-a0b4-415a-88f5-57d1fadd1aba&amp;gt;
&gt; (make-uuid (make-uuid &quot;4ba52eac-a0b4-415a-88f5-57d1fadd1aba&quot;))
#&amp;lt;uuid:4ba52eac-a0b4-415a-88f5-57d1fadd1aba&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
&lt;code&gt;bzlib/session.plt&lt;/code&gt; does not handle parsing cookies into uuids.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Creating a Session Object&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
In order to create a session object, you first need to make a &lt;code&gt;dbi&lt;/code&gt; handle with either &lt;code&gt;dbd-spgsql&lt;/code&gt;, &lt;code&gt;dbd-jazmysql&lt;/code&gt;, or &lt;code&gt;dbd-jsqlite&lt;/code&gt; driver to where you have setup the &lt;code&gt;session_t&lt;/code&gt; table, and you need to pass in the corresponding query script so the prepared statements (&lt;code&gt;&#39;make-session!&lt;/code&gt;, &lt;code&gt;&#39;load-session&lt;/code&gt;, &lt;code&gt;&#39;save-session!&lt;/code&gt;, and &lt;code&gt;destroy-session!&lt;/code&gt;) can be loaded:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(require (planet bzlib/session) (planet bzlib/dbi)) 
;; loading &#39;jsqlite 
(require (planet bzlib/dbd-jsqlite)) 
(define h (connect &#39;jsqlite &amp;lt;path-to-db&amp;gt; 
                   &lt;b&gt;&#39;#:load (session-query-path/sqlite)&lt;/b&gt;)) 
;; loading &#39;spgsql 
(require (planet bzlib/dbd-spgsql)) 
(define h (connect &#39;spgsql &amp;lt;spgsql-parameters&amp;gt; ... 
                   &lt;b&gt;&#39;#:load (session-query-path/postgres)&lt;/b&gt;))
;; loading &#39;jazmysql
(require (planet bzlib/dbd-jazmysql)) 
(define h (connect &#39;jazmysql &amp;lt;jazmysql-parameters&amp;gt; ...
                   &lt;b&gt;&#39;#:load (session-query-path/mysql)&lt;/b&gt;)) 
&lt;/code&gt;&lt;/pre&gt;Then you can pass the handle to &lt;code&gt;build-session&lt;/code&gt; to create the session object.&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
;; create a new session with a new uuid 
(define s (build-session h)) 
;; create a new session with a known uuid 
(define s (build-session h &amp;lt;uuid&amp;gt;)) 
&lt;/code&gt;&lt;/pre&gt;As soon as &lt;code&gt;build-session&lt;/code&gt; is called, you&#39;ll find a corresponding session record in &lt;code&gt;session_t&lt;/code&gt;. &lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Accessing Session Key/Value Pairs&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;session-ref&lt;/code&gt;, &lt;code&gt;session-set!&lt;/code&gt;, &lt;code&gt;session-del!&lt;/code&gt; modifies the values of session key/value pairs:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(session-ref &amp;lt;session&amp;gt; &amp;lt;key&amp;gt; &amp;lt;optional-default-value&amp;gt;) 

(session-set! &amp;lt;session&amp;gt; &amp;lt;key&amp;gt; &amp;lt;value&amp;gt;) 

(session-del! &amp;lt;session&amp;gt; &amp;lt;key&amp;gt;) 
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
&lt;b&gt;Writing Out Sessions&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;save-session!&lt;/code&gt; saves the sessions out to the database:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(save-session! &amp;lt;session&amp;gt;)
&lt;/code&gt;&lt;/pre&gt;&lt;code&gt;refresh-session!&lt;/code&gt; will reload the session from the database:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(refresh-session! &amp;lt;session&amp;gt;) 
&lt;/code&gt;&lt;/pre&gt;And &lt;code&gt;destroy-session!&lt;/code&gt; will delete the session record from &lt;code&gt;session_t&lt;/code&gt;:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(destroy-session! &amp;lt;session&amp;gt;) 
&lt;/code&gt;&lt;/pre&gt;&lt;code&gt;call-with-session&lt;/code&gt; and &lt;code&gt;with-session&lt;/code&gt; will help you manage the call to &lt;code&gt;save-session!&lt;/code&gt; so you do not have to write it with every request:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(call-with-session (build-session h) 
  (lambda (session) 
    &amp;lt;... do something with session ...&amp;gt;))

(with-session (build-session h) 
  (lambda () 
    &amp;lt;... do something with session via (current-session) ...&amp;gt;)) 
&lt;/code&gt;&lt;/pre&gt;&lt;code&gt;with-session&lt;/code&gt; works via &lt;code&gt;(current-session)&lt;/code&gt;, which is a parameter that holds either &lt;code&gt;#f&lt;/code&gt; or a session structure.  By passing the session object via &lt;code&gt;with-session&lt;/code&gt; it will automatically parameterize &lt;code&gt;current-session&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Session Expirations&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
You can use &lt;code&gt;session-expired?&lt;/code&gt; to test whether the session object has expired:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(session-expired? &amp;lt;session&amp;gt;) 
&lt;/code&gt;&lt;/pre&gt;The expiration value is stored as a number (in Julian days) in &lt;code&gt;session_t&lt;/code&gt;, which by default will be 14 days in the future from the time when &lt;code&gt;save-session!&lt;/code&gt; is called. &lt;br /&gt;
&lt;br /&gt;
The default session expiration value of 14 days can be controlled via the &lt;code&gt;session-expiration-interval&lt;/code&gt; parameter.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Web Server Continuation&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
To simplify the usage of &lt;code&gt;bzlib/session&lt;/code&gt; with &lt;code&gt;web-server&lt;/code&gt; continuation calls, the following wrappers are exported to replace the &lt;code&gt;send/suspend&lt;/code&gt; family:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(require (planet bzlib/session/web-server)) 
;; you then have access to... 
&lt;a href=&quot;http://docs.plt-scheme.org/web-server/servlet.html#(def._((lib._web-server/servlet/web..ss)._send/back))&quot;&gt;send/back&lt;/a&gt; 
&lt;a href=&quot;http://docs.plt-scheme.org/web-server/servlet.html#(def._((lib._web-server/servlet/web..ss)._send/finish))&quot;&gt;send/finish&lt;/a&gt;
&lt;a href=&quot;http://docs.plt-scheme.org/web-server/servlet.html#(def._((lib._web-server/servlet/web..ss)._send/suspend))&quot;&gt;send/suspend&lt;/a&gt;
&lt;a href=&quot;http://docs.plt-scheme.org/web-server/servlet.html#(def._((lib._web-server/servlet/web..ss)._send/suspend/url))&quot;&gt;send/suspend/url&lt;/a&gt;
&lt;a href=&quot;http://docs.plt-scheme.org/web-server/servlet.html#(def._((lib._web-server/servlet/web..ss)._send/forward))&quot;&gt;send/forward&lt;/a&gt;
&lt;a href=&quot;http://docs.plt-scheme.org/web-server/servlet.html#(def._((lib._web-server/servlet/web..ss)._send/suspend/dispatch))&quot;&gt;send/suspend/dispatch&lt;/a&gt;
&lt;a href=&quot;http://docs.plt-scheme.org/web-server/servlet.html#(def._((lib._web-server/servlet/web..ss)._redirect/get))&quot;&gt;redirect/get&lt;/a&gt;
&lt;a href=&quot;http://docs.plt-scheme.org/web-server/servlet.html#(def._((lib._web-server/servlet/web..ss)._redirect/get/forget))&quot;&gt;redirect/get/forget&lt;/a&gt;
&lt;/code&gt;&lt;/pre&gt;These wrappers have the same corresponding APIs from &lt;code&gt;web-server&lt;/code&gt; itself, and they&#39;ll call &lt;code&gt;save-session!&lt;/code&gt; before making the continuation, and call &lt;code&gt;refresh-session!&lt;/code&gt; before returning from the continuation.&lt;br /&gt;
&lt;br /&gt;
That&#39;s it for now, enjoy.</content><link rel='replies' type='application/atom+xml' href='http://weblambda.blogspot.com/feeds/5910891301501022338/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://weblambda.blogspot.com/2009/11/bzlibsessionplt-session-store-via.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7570364353928786565/posts/default/5910891301501022338'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7570364353928786565/posts/default/5910891301501022338'/><link rel='alternate' type='text/html' href='http://weblambda.blogspot.com/2009/11/bzlibsessionplt-session-store-via.html' title='BZLIB/SESSION.plt - a Session Store via BZLIB/DBI.plt'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7570364353928786565.post-2250839939443586700</id><published>2009-11-09T20:54:00.000-08:00</published><updated>2009-11-09T20:54:36.844-08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="plt scheme"/><category scheme="http://www.blogger.com/atom/ns#" term="session"/><category scheme="http://www.blogger.com/atom/ns#" term="web"/><title type='text'>Building a Web Session Store (1)</title><content type='html'>Given that we have previously &lt;a href=&quot;http://weblambda.blogspot.com/2009/11/web-sessions-vs-continuations.html&quot;&gt;determined the need for a web session store even if we are using continuations&lt;/a&gt;, we&#39;ll go ahead and build it on top of our DBI stack, so the session data can be persisted as long as necessary.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Quick Word About Session Store Performance&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
One thing to note about session data is that its data usage is both read and write intensive, and such data can put strain on the database.  It&#39;s write-intensive because with each request we&#39;ll extend the expiration time on the session itself, and it&#39;s read-intensive because the data is needed for every request, but it changes with every request.  &lt;br /&gt;
&lt;br /&gt;
For now we&#39;ll assume that our database is capable of handling such needs (and it will until you have a sufficiently large amount of traffic), but it&#39;s something to keep in mind.  The nice thing of building the session logic on top of DBI is that when we need to deal with the performance issue, &lt;a href=&quot;http://weblambda.blogspot.com/2009/09/create-driver-for-bzlibdbi-1-dbi.html&quot;&gt;we can add logics into the DBI tier easily with developing a customer driver&lt;/a&gt;, for example, by &lt;a href=&quot;http://weblambda.blogspot.com/2009/09/create-driver-for-bzlibdbi-1-dbi.html&quot;&gt;integrating memcached as a intermediate store&lt;/a&gt; that&#39;ll flush out the changes to the database once a while instead of with every request.  &lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Active Record&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
The &lt;a href=&quot;http://en.wikipedia.org/wiki/Active_record_pattern&quot;&gt;active record pattern&lt;/a&gt; are not just for OOP fanatics - we schemers know that you can craft your own OOP with FP easily. In DBI today there is a base structure for active record definition:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define active-record (handle id)) 
&lt;/code&gt;&lt;/pre&gt;Such definition is a lot simpler than the usual OOP representations, which usually try to construct the data model in memory, along with dynamically constructed SQL statements.  Although such OOP records provide simplicity for the simple cases, it has proven to be a leaky abstraction due to the object vs relational paradigm mismatch, as well as a significant performance overhead.  Our simple definition will do us just fine right now. &lt;br /&gt;
&lt;br /&gt;
What would our session API look like then? &lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
;; expiration a julian-day 
;; store is a hash table 
(define-struct (session active-record) (expiration store) #:mutable) 

;; the session key/value manipulation calls... 
(define (session-ref session key (default #f)) ...) 
(define (session-set! session key val) ...) 
(define (session-del! session key) ...) 
;; the persistence calls 
(define (build-session handle ...) ...) 
(define (save-session! session) ...) 
(define (refresh-session! session) ...) 
(define (destroy-session! session) ...) 
&lt;/code&gt;&lt;/pre&gt;&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
We&#39;ll go through and flesh out the definitions in details.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;The Store in Memory&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
Hashtable is a good internal representation of the key/value pairs that session will hold (for now we&#39;ll assume the held data are serializable... we&#39;ll deal with this problem later), and this immediately tell us what &lt;code&gt;session-ref&lt;/code&gt;, &lt;code&gt;session-set!&lt;/code&gt;, and &lt;code&gt;session-del!&lt;/code&gt; will look like: &lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define (session-ref session key (default #f)) 
  (hash-ref (session-store session) key default)) 

(define (session-set! session key val) 
  (set-session-store! session 
                      (hash-set (session-store session) key val)))

(define (session-del! session key) 
  (set-session-store! session 
                      (hash-remove session key)))
&lt;/code&gt;&lt;/pre&gt;And yes - we are using immutable hash rather than mutable hash. &lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;When to Persist&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
You probably have noticed that &lt;code&gt;session-set!&lt;/code&gt; and &lt;code&gt;session-del!&lt;/code&gt; do not persist out to the database.  So if you have multiple concurrent connections for the same session, it might be possible for the session object to get out of the sync.&lt;br /&gt;
&lt;br /&gt;
While this is possible, the chance of it happening isn&#39;t great, since for the majority of the time users are going to make one main request at a time, with many auxiliary requests for accompanying images and css files that should not modify session values. &lt;br /&gt;
&lt;br /&gt;
On the other hand, saving every changes with each single &lt;code&gt;session-set!&lt;/code&gt; call could drastically increase the read &amp; write access for the session object (what&#39;s the point of saving with each write if you are not doing the same for read?) and could have detrimental impact on performance unless we are ready to implement an intermediate cache.  &lt;br /&gt;
&lt;br /&gt;
And finally such decoupling actually simplify the code (I have written the code with the other approach for comparison) and makes it look more refactored.  So for now we&#39;ll go with this approach.&lt;br /&gt;
&lt;br /&gt;
Hence we&#39;ll persist at the end of the request with a call to &lt;code&gt;save-session!&lt;/code&gt;.  A simple wrapper so you do not have to explicitly write the separate call would be:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define (call-with-session session proc) 
  (dynamic-wind void 
                (lambda () 
                  (proc session)) 
                (lambda ()
                  (save-session! session))))
&lt;/code&gt;&lt;/pre&gt;And with a &lt;code&gt;current-session&lt;/code&gt; parameter we can simplify it as:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define current-session (make-parameter #f))

(define (with-session session proc) 
  (call-with-session session 
                     (lambda (session)
                       (parameterize ((current-session session)) 
                         (proc)))))
&lt;/code&gt;&lt;/pre&gt;Except for one bug, the above will work as you expected in web-server environment.  If you have an idea of what the bug will be - please feel free to make a comment.  I&#39;ll discuss the bug and how to fix it in the next post for the series.</content><link rel='replies' type='application/atom+xml' href='http://weblambda.blogspot.com/feeds/2250839939443586700/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://weblambda.blogspot.com/2009/11/building-web-session-store-1.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7570364353928786565/posts/default/2250839939443586700'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7570364353928786565/posts/default/2250839939443586700'/><link rel='alternate' type='text/html' href='http://weblambda.blogspot.com/2009/11/building-web-session-store-1.html' title='Building a Web Session Store (1)'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7570364353928786565.post-436184400823140423</id><published>2009-11-07T17:36:00.000-08:00</published><updated>2009-11-15T22:29:41.340-08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="continuation"/><category scheme="http://www.blogger.com/atom/ns#" term="session"/><category scheme="http://www.blogger.com/atom/ns#" term="web"/><title type='text'>Web Sessions vs. Continuations</title><content type='html'>The &lt;a href=&quot;http://list.cs.brown.edu/pipermail/plt-scheme/2009-November/036500.html&quot;&gt;&quot;session info in web server applications&quot; thread&lt;/a&gt; recently in plt-scheme list has an undertone that continuations are equivalent of web sessions as understood in other languages and frameworks.  This undertone is highlighted by the lack of a session-like capability within the web-server collection that exists in other web frameworks.  &lt;br /&gt;
&lt;br /&gt;
This got me to think: &lt;b&gt;are continuations equivalent of sessions?&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
The &lt;a href=&quot;http://www.cs.brown.edu/~sk/Publications/Papers/Published/khmgpf-impl-use-plt-web-server-journal/&quot;&gt;original intent (indicated in Shriram&#39;s research paper) of web-server&#39;s continuation&lt;/a&gt; is to correctly and succinctly model interactive web application&#39;s application flow.  The paper sites examples of incorrectly implemented web apps that would do something like the following:&lt;br /&gt;
&lt;ol&gt;&lt;li&gt;user browse a list of goods&lt;/li&gt;
&lt;li&gt;user opens new window to get the details of goods A&lt;/li&gt;
&lt;li&gt;user goes back to original window&lt;/li&gt;
&lt;li&gt;user then opens another new window to get the details of goods B&lt;/li&gt;
&lt;li&gt;user then goes to goods A and click &quot;Buy Now&quot;&lt;/li&gt;
&lt;li&gt;incorrectly implemented app will cause the user to buy goods B instead of goods A&lt;/li&gt;
&lt;/ol&gt;The traditional solution to the above interaction would be to use sessions, and since continuation models such interactions as well, there is no question that in this case continuations supplant the needs of sessions. &lt;br /&gt;
&lt;br /&gt;
But for other scenarios involving sessions it might be more natural to model the computations by using the traditional session concepts.  &lt;br /&gt;
&lt;br /&gt;
For example - identifying the user across visits after significant time lapse (this is generally toggled by a &quot;remember me&quot; checkbox during login).  Normally web sites accomplish this by persisting the user&#39;s authenticators via cookies or sessions.&lt;br /&gt;
&lt;br /&gt;
This process is awkward to model with continuations, since the user likely come back to the site via a top level link that has no captured continuations, instead of digging up the last continuation url for the site, and the continuations might have expired between the visits if you use stateful servlets. &lt;br /&gt;
&lt;br /&gt;
If you use web-server&#39;s stateless servlet language, an approach is probably to serialize the continuation into a cookie so it can model the above scenario, but you&#39;ll have to write your code in the stateless language or convert your code over, and it feels like a more complex solution compared to simply having a regular session capability.  This is similar to using continuations to model non-interactive web links - it can work, but it does not follow Occam&#39;s razor. &lt;br /&gt;
&lt;br /&gt;
Furthermore - if your site uses extensive ajax, your use of continuations will decrease, since Ajax models the interactions as well and supplants the needs for continuations.  and in such case you might regain the needs for sessions that was reduced by continuations. &lt;br /&gt;
&lt;br /&gt;
So, as far as I can tell, continuations is not equivalent to web sessions and do not eliminate the needs for session capabilities.</content><link rel='replies' type='application/atom+xml' href='http://weblambda.blogspot.com/feeds/436184400823140423/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://weblambda.blogspot.com/2009/11/web-sessions-vs-continuations.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7570364353928786565/posts/default/436184400823140423'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7570364353928786565/posts/default/436184400823140423'/><link rel='alternate' type='text/html' href='http://weblambda.blogspot.com/2009/11/web-sessions-vs-continuations.html' title='Web Sessions vs. Continuations'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7570364353928786565.post-8373075491210291455</id><published>2009-11-06T23:37:00.000-08:00</published><updated>2009-11-06T23:37:49.715-08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="dbi"/><category scheme="http://www.blogger.com/atom/ns#" term="mysql"/><category scheme="http://www.blogger.com/atom/ns#" term="plt scheme"/><category scheme="http://www.blogger.com/atom/ns#" term="postgresql"/><category scheme="http://www.blogger.com/atom/ns#" term="sqlite"/><title type='text'>Using DBI to Run Scripts &amp; Load Prepared Statement Scripts</title><content type='html'>There are a couple of utility functions that I have designed into DBI that was not previously discussed.  They are both oriented to work with SQL scripts. &lt;br /&gt;
&lt;br /&gt;
I don&#39;t know about you, but I like to write SQL statements in SQL scripts: &lt;pre class=&#39;sql&#39;&gt;&lt;code&gt;
-- a sql query inside a sql script
select * 
  from table1;
&lt;/code&gt;&lt;/pre&gt;instead of embedding them as strings in programming languages:&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
;; a sql query embedded in scheme code 
(exec handle &quot;select * from table1&quot;)
&lt;/code&gt;&lt;/pre&gt;- it just looks so much nicer.&lt;br /&gt;
&lt;br /&gt;
If you have a ton of complex prepared statements, you&#39;ll find you&#39;ll have such statements littered everywhere, which makes them difficult to maintain. &lt;br /&gt;
&lt;br /&gt;
Of course - a possible solution to this problem is to create a DSL so the SQL strings can be generated.  We might eventually entertain such solution, but it&#39;s clear that any such DSL will be non-trivial.  &lt;br /&gt;
&lt;br /&gt;
A simpler but equally as powerful of an idea is to move all such queries into SQL scripts, and have the database handle load the scripts and convert them into prepared statements.  &lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Loading Prepared Statement Scripts&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
You can supply a &lt;code&gt;#:load&lt;/code&gt; parameter to the three RDBMS drivers&#39; &lt;code&gt;connect&lt;/code&gt; procedure:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
;; example - connect to spgsql 
(define handle 
  (connect &#39;spgsql &amp;lt;regular-args&amp;gt; ... 
           &#39;#:load &amp;lt;path-string? or (listof path-string?)&amp;gt;))
&lt;/code&gt;&lt;/pre&gt;&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
The &lt;code&gt;#:load&lt;/code&gt; parameter takes either a &lt;code&gt;path-string?&lt;/code&gt; or a list of &lt;code&gt;path-string?&lt;/code&gt;, all of which needs to point to valid prepared statement scripts.  The &lt;code&gt;connect&lt;/code&gt; procedure will then load each of the scripts and convert them into prepared statements. &lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Format of the Prepared Statement Scripts&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
In order for this to work - you&#39;ll need to follow some conventions:&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;You can have multiple prepared statements in a single script, and it must follow the order of &amp;lt;name-of-the-statement&amp;gt;\n+ &amp;lt;statement&amp;gt;\n+ ...&lt;/li&gt;
&lt;li&gt;the name of the statement must be on a line starting with &lt;b&gt;&lt;code&gt;--;;-- &lt;/code&gt;&lt;/b&gt;, follow by the name itself, and then nothing else (besides additional whitespace) &lt;/li&gt;
&lt;li&gt;the name of the statement is basically a regular scheme symbol, but more restricted - like a regular scheme function name, which would consist of alphanumeric, &lt;code&gt;-&lt;/code&gt;, &lt;code&gt;_&lt;/code&gt;, &lt;code&gt;?&lt;/code&gt;, and &lt;code&gt;!&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;An example will make it more clear - below is a sample script that contains 2 prepared statements:&lt;br /&gt;
&lt;pre class=&#39;sql&#39;&gt;&lt;code&gt;
--;;-- session-ref 
select session_value
  from session_value_t 
 where session_uuid = ?uuid 
   and session_key = ?key 

--;;-- session-update! 
update session_value_t 
   set session_value = ?val
 where session_uuid = ?uuid 
   and session_key = ?key
&lt;/code&gt;&lt;/pre&gt;The first statement&#39;s name is &lt;code&gt;session-ref&lt;/code&gt;, and the second is &lt;code&gt;session-update!&lt;/code&gt;.  If you need more in a single script - just keep extending it with the same format.  You can add other regular SQL comment lines in between, just make sure it does not start with &lt;code&gt;--;;--&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
Because the &lt;code&gt;#:load&lt;/code&gt; parameter can load a list of scripts, you can organize your scripts however you want.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Run a SQL Script&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
The other script-related capability that DBI has is to run a SQL script.  This is useful if you want to have installation scripts that has a set of SQL queries that needs to be executed sequentially (like creating a bunch of tables).  All you need to do is to write up the script, and then:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(run-script! handle &amp;lt;path-to-script&amp;gt; &amp;lt;query-args&amp;gt;) 
&lt;/code&gt;&lt;/pre&gt;The args have the same format as the args for &lt;code&gt;query&lt;/code&gt; (a list of key/value pairs).  This means that your script can also contain placeholders like your prepared statements - they&#39;ll get passed to the statements as usual.  Note because such script will contain multiple queries, if you do not want to pass the same value to a subsequent query, you must use a different placeholder name for the subsequent query.&lt;br /&gt;
&lt;br /&gt;
Now - such a script also has a special format to ensure it works correctly.  Basically, you must use the semicolon (&lt;code&gt;;&lt;/code&gt;) terminator for the queries - below is an example script - notice the semicolon:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
﻿create table session_t 
( session_id integer primary key auto_increment not null 
, session_uuid varchar(32) unique not null 
, expiration date not null) &lt;b&gt;;&lt;/b&gt; -- query terminator 

create table session_value_t 
( session_value_id integer primary key auto_increment not null 
, session_uuid varchar (32) not null 
, session_key varchar (128) not null 
, session_value text null 
, unique ( session_uuid , session_key ) 
)
&lt;/code&gt;&lt;/pre&gt;You&#39;ll also notice that the second statement does not have a terminator so there won&#39;t be an empty query. &lt;br /&gt;
&lt;br /&gt;
This is capability is only available since planet version (1 3), i.e. &lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(require (planet bzlib/dbi:1:3)) 
&lt;/code&gt;&lt;/pre&gt;The two capability should help you a lot in writing complex sql queries and scripts.  &lt;br /&gt;
&lt;br /&gt;
That&#39;s it for now.  Enjoy.</content><link rel='replies' type='application/atom+xml' href='http://weblambda.blogspot.com/feeds/8373075491210291455/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://weblambda.blogspot.com/2009/11/using-dbi-to-run-scripts-load-prepared.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7570364353928786565/posts/default/8373075491210291455'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7570364353928786565/posts/default/8373075491210291455'/><link rel='alternate' type='text/html' href='http://weblambda.blogspot.com/2009/11/using-dbi-to-run-scripts-load-prepared.html' title='Using DBI to Run Scripts &amp; Load Prepared Statement Scripts'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7570364353928786565.post-3359773086604982883</id><published>2009-11-05T23:07:00.000-08:00</published><updated>2009-11-05T23:07:32.158-08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="dbi"/><category scheme="http://www.blogger.com/atom/ns#" term="mysql"/><category scheme="http://www.blogger.com/atom/ns#" term="plt scheme"/><category scheme="http://www.blogger.com/atom/ns#" term="postgresql"/><category scheme="http://www.blogger.com/atom/ns#" term="sql"/><category scheme="http://www.blogger.com/atom/ns#" term="sqlite"/><title type='text'>Latest DBI.plt Available - Handling Last Inserted ID and Side Effects</title><content type='html'>The newest version of DBI (and the 3 RDBMS drivers) has now been made available on planet - it addresses &lt;a href=&quot;http://list.cs.brown.edu/pipermail/plt-scheme/2009-October/035892.html&quot;&gt;the issue of side effects and last inserted id&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
As usual - they are released under LGPL.&lt;br /&gt;
&lt;br /&gt;
To download &amp; install - use require: &lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(require &lt;a href=&quot;http://planet.plt-scheme.org/display.ss?package=dbi.plt&amp;owner=bzlib&quot;&gt;(planet bzlib/dbi:1:3)&lt;/a&gt; 
         &lt;a href=&quot;http://planet.plt-scheme.org/display.ss?package=dbd-jazmysql.plt&amp;owner=bzlib&quot;&gt;(planet bzlib/dbd-jazmysql:1:2)&lt;/a&gt; 
         &lt;a href=&quot;http://planet.plt-scheme.org/display.ss?package=dbd-jsqlite.plt&amp;owner=bzlib&quot;&gt;(planet bzlib/dbd-jsqlite:1:3)&lt;/a&gt; 
         &lt;a href=&quot;http://planet.plt-scheme.org/display.ss?package=dbd-spgsql.plt&amp;owner=bzlib&quot;&gt;(planet bzlib/dbd-spgsql:1:2)&lt;/a&gt;)
&lt;/code&gt;&lt;/pre&gt;Unifying the side effects and the last inserted id turns out to be a non-trivial task - &lt;a href=&quot;http://list.cs.brown.edu/pipermail/plt-scheme/2009-November/036499.html&quot;&gt;I have already voiced the options on the plt-scheme list&lt;/a&gt;, repeating here for the sake of completeness: &lt;br /&gt;
&lt;ul&gt;&lt;li&gt;the underlying drivers returns different values for side effects&lt;/li&gt;
&lt;li&gt;there might be legacy code that are utilizing the underlying side effect results - which can increase effort if porting to another database&lt;/li&gt;
&lt;li&gt;not all drivers provide ways to retrieve all side effect values&lt;/li&gt;
&lt;li&gt;last inserted id does not work consistently when dealing with the multi-records-insert-at-once scenario - luckily this is generally not how insertion is used&lt;/li&gt;
&lt;li&gt;for postgresql - we&#39;ll need to derive the underlying table to sequence name mapping in order to determine the last inserted id, and this requires a separate query that you might not want to &quot;pay for&quot; unless you have needs for last inserted id&lt;/li&gt;
&lt;/ul&gt;Based on the above design constraints, I have chosen the following: &lt;br /&gt;
&lt;ul&gt;&lt;li&gt;make available multiple drivers for each database - one for different types of the side effect results&lt;/li&gt;
&lt;li&gt;default the side effect results to an unified &lt;code&gt;effect&lt;/code&gt; structure, which is inspired by &lt;a href=&quot;http://planet.plt-scheme.org/display.ss?package=mysql.plt&amp;owner=jaz&quot;&gt;&lt;code&gt;jazmysql&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;provide last-inserted-id for the 3 RDBMS drivers (SPGSQL has a special requirement, described below)&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
&lt;b&gt;The Different Side Effects&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
There are 3 separate side effect types:&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;&lt;code&gt;past-thru-effect&lt;/code&gt; - this is the side effect available as a backward compatibility.  Basically whatever the side effect objects are returned by the underlying driver are direct returned; and you can use the underlying driver&#39;s code to access the values&lt;/li&gt;
&lt;li&gt;&lt;code&gt;effect&lt;/code&gt; - this is the unified side effect object and the default going forward&lt;/li&gt;
&lt;li&gt;&lt;code&gt;effect-set&lt;/code&gt; - this converts the &lt;code&gt;effect&lt;/code&gt; structure into a result set&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
For example, if you want to make a connection with the first side effect type with &lt;code&gt;bzlib/dbd-spgsql&lt;/code&gt;, you can do the following:&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(connect &lt;b&gt;&#39;spgsql/past-thru-effect&lt;/b&gt; &amp;lt;rest-of-args&amp;gt; ...) 
&lt;/code&gt;&lt;/pre&gt;And for the other two types:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
;; use the &lt;code&gt;effect&lt;/code&gt; structure
(connect &lt;b&gt;&#39;spgsql/effect&lt;/b&gt; &amp;lt;rest-of-args&amp;gt; ...)
;; use result set as the effect structure
(connect &lt;b&gt;&#39;spgsql/effect-set&lt;/b&gt; &amp;lt;rest-of-args&amp;gt; ...)
&lt;/code&gt;&lt;/pre&gt;The default is &lt;code&gt;&#39;spgsql/effect&lt;/code&gt;.  That means when you pass in &lt;code&gt;&#39;spgsql&lt;/code&gt; as the driver name, you are passing in the equivalent of &lt;code&gt;&#39;spgsql/effect&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
The same goes for the other two drivers: &lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(connect &lt;b&gt;&#39;jsqlite/past-thru-effect&lt;/b&gt; ...)
(connect &lt;b&gt;&#39;jsqlite/effect&lt;/b&gt; ...) ;; the default; same as &lt;b&gt;&#39;jsqlite&lt;/b&gt;
(connect &lt;b&gt;&#39;jsqlite/effect-set&lt;/b&gt; ...) 

(connect &lt;b&gt;&#39;jazmysql/past-thru-effect&lt;/b&gt; ...)
(connect &lt;b&gt;&#39;jazmysql/effect&lt;/b&gt; ...) ;; the default; same as &lt;b&gt;&#39;jazmysql&lt;/b&gt;
(connect &lt;b&gt;&#39;jazmysql/effect-set&lt;/b&gt; ...) 
&lt;/code&gt;&lt;/pre&gt;If you chooes &lt;code&gt;*/past-thru-effect&lt;/code&gt; you&#39;ll have to use the side effect structures returned by the underlying driver - I won&#39;t discuss this since this is meant for backward compatibility - if you are writing new code I would encourage to either use &lt;code&gt;*/effect&lt;/code&gt; or &lt;code&gt;*/effect-set&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;The &lt;code&gt;effect&lt;/code&gt; Structure&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
Inspired by &lt;code&gt;jaz/mysql&lt;/code&gt;, the &lt;code&gt;effect&lt;/code&gt; structure has the following definition:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define-struct effect 
  (rows ;; the # of rows affected or #f
   insert-id ;; the last inserted id or #f
   status ;; the status of the underlying connection or #f
   warning-count ;; the warning count or #f 
   message ;; the message returned with the query or #f 
   error ;; the error message (or exception object) or #f 
  ))
&lt;/code&gt;&lt;/pre&gt;You can use the appropriate struct accessor functions to access the values if you use the &lt;code&gt;*/effect&lt;/code&gt; drivers.  &lt;br /&gt;
&lt;br /&gt;
In &lt;code&gt;*/effect-set&lt;/code&gt; drivers, the returned &lt;code&gt;effect&lt;/code&gt; object are converted to results, with the first row being the column names of:&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;&lt;code&gt;affected rows&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;insert id&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;status&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;warning count&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;message&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;error&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;And the second row would contain the value converted from the &lt;code&gt;effect&lt;/code&gt; structure, but with &lt;code&gt;#f&lt;/code&gt; mapped to &lt;code&gt;&#39;()&lt;/code&gt;, based on the convention of the result set&#39;s handling of &lt;code&gt;NULL&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Last Inserted ID&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
For both &lt;code&gt;dbd-jsqlite&lt;/code&gt; and &lt;code&gt;dbd-jazmysql&lt;/code&gt;, the &lt;code&gt;*/effect&lt;/code&gt; &amp; &lt;code&gt;*/effect-set&lt;/code&gt; correctly captures the last inserted ID in the &lt;code&gt;effect&lt;/code&gt; structure&#39;s &lt;code&gt;insert-id&lt;/code&gt; field.  They are also correctly returned in the &lt;code&gt;*/past-thru-effect&lt;/code&gt; version, since the underlying driver directly supports the concept of last inserted id (the &lt;code&gt;jsqlite/past-thru-effect&lt;/code&gt; will return the last-inserted-id as a number, and &lt;code&gt;jazmysql/past-thru-effect&lt;/code&gt; will return the last-inserted-id contained as part of the &lt;code&gt;side-effect&lt;/code&gt; structure). &lt;br /&gt;
&lt;br /&gt;
The &lt;code&gt;dbd-spgsql&lt;/code&gt; driver is more complicated, however.  The &lt;code&gt;spgsql/past-thru-effect&lt;/code&gt; does not return the last-inserted-id, because in postgresql you need to know the underlying sequence object name that the table uses to manages the auto increment, and you also need to make a secondary query, which adds additional overhead. &lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;dbd-spgsql&lt;/code&gt; handles this issue by taking in an additional parameter, identified by keyword &lt;code&gt;#:t2s&lt;/code&gt; (table to sequence), when you make the connection:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(connect &#39;spgsql/effect &amp;lt;arg&amp;gt; ... &lt;b&gt;&#39;#:t2s 
         &amp;lt;procedure-to-translate-table-name-to-sequence-name&amp;gt;&lt;/b&gt;) 
&lt;/code&gt;&lt;/pre&gt;The &lt;code&gt;#:t2s&lt;/code&gt; parameter takes a procedure that takes in a string (the table name) and returns a string (the sequence name).  If you supply the parameter, and the query is an &lt;code&gt;insert&lt;/code&gt; query, then the driver will help you to automatically make the subsequent query to retrieve the last inserted id.  If you do not supply the parameter, then no overhead for accessing the last inserted id will be incurred.&lt;br /&gt;
&lt;br /&gt;
That&#39;s it for now.  Enjoy.</content><link rel='replies' type='application/atom+xml' href='http://weblambda.blogspot.com/feeds/3359773086604982883/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://weblambda.blogspot.com/2009/11/latest-dbiplt-available-handling-last.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7570364353928786565/posts/default/3359773086604982883'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7570364353928786565/posts/default/3359773086604982883'/><link rel='alternate' type='text/html' href='http://weblambda.blogspot.com/2009/11/latest-dbiplt-available-handling-last.html' title='Latest DBI.plt Available - Handling Last Inserted ID and Side Effects'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7570364353928786565.post-2115262405132314043</id><published>2009-10-20T14:05:00.000-07:00</published><updated>2009-10-20T14:08:55.919-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="date"/><category scheme="http://www.blogger.com/atom/ns#" term="plt scheme"/><category scheme="http://www.blogger.com/atom/ns#" term="timezone"/><title type='text'>BZLIB/DATE &amp; BZLIB/DATE-TZ 0.2 Now Available</title><content type='html'>&lt;a href=&quot;http://planet.plt-scheme.org/display.ss?package=date.plt&amp;owner=bzlib&quot;&gt;&lt;code&gt;bzlib/date&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;http://planet.plt-scheme.org/display.ss?package=date-tz.plt&amp;owner=bzlib&quot;&gt;&lt;code&gt;bzlib/date-tz&lt;/code&gt;&lt;/a&gt; are now available via planet.  They are released under LGPL.  You can find &lt;a href=&quot;http://weblambda.blogspot.com/2009/10/introducing-bzlibdate-bzlibdate-tz-date.html&quot;&gt;the previous documentation&lt;/a&gt; for previous usage.&lt;br /&gt;
&lt;br /&gt;
The changes included are:&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;re-exports for SRFI-19 functions&lt;/li&gt;
&lt;li&gt;Wrapper functions for PLT date objects (you can use the functions with PLT date objects instead of with SRFI date objects)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;day&lt;/b&gt; comparison functions&lt;/li&gt;
&lt;li&gt;RFC822 date parsers and generators&lt;/li&gt;
&lt;li&gt;additional date manipulation functions&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
&lt;b&gt;SRFI19 Re-export&lt;/b&gt;&lt;br /&gt;
Previous you have to explicitly include &lt;code&gt;srfi/19&lt;/code&gt; to use the functions within SRFI19 as follows:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(require srfi/19 (planet bzlib/date)) 
&lt;/code&gt;&lt;/pre&gt;Now you just have to do the following:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(require (planet bzlib/date/srfi)) 
&lt;/code&gt;&lt;/pre&gt;And almost all srfi/19 functions will be re-exported along with the functions within &lt;code&gt;bzlib/date&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
The exceptions are &lt;code&gt;date-&gt;string&lt;/code&gt; and &lt;code&gt;string-&gt;date&lt;/code&gt;, neither of which are exported from srfi/19.  This is because we may want to use those names for our own date parsers and generator functions. I&#39;ll examine the details before deciding whether to re-export those or create our own. &lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;PLT Date Wrappers&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
You now can use PLT date objects instead of srfi/19 date objects (I do not really know why they are different date objects in the first place...).  You can just do the following:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(require (planet bzlib/date/plt)) 
&lt;/code&gt;&lt;/pre&gt;&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
Which will export functions with the same name, but takes (and returns) PLT date objects instead of SRFI date objects.  Because the exports are the same name - you cannot require it along with the SRFI-date version. &lt;br /&gt;
&lt;br /&gt;
Besides wrapper over all of the &lt;code&gt;bzlib/date&lt;/code&gt; functions, it also wraps over the &lt;code&gt;srfi/19&lt;/code&gt; functions, so you can use, for example, &lt;code&gt;current-date&lt;/code&gt; and it&#39;ll now return a PLT date object. &lt;br /&gt;
&lt;br /&gt;
This version also does not export &lt;code&gt;string-&gt;date&lt;/code&gt; and &lt;code&gt;date-&gt;string&lt;/code&gt;. &lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;bzlib/date-tz&lt;/code&gt; also exports its functions in PLT wrapper form:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(require (planet bzlib/date-tz/plt)) 
&lt;/code&gt;&lt;/pre&gt;It does not re-export &lt;code&gt;bzlib/date/plt&lt;/code&gt; so you will need to explicitly require it if you want to use its functions.&lt;br /&gt;
&lt;br /&gt;
There is a matching module in &lt;code&gt;bzlib/date-tz/srfi&lt;/code&gt; but it is exactly the same as &lt;code&gt;bzlib/date-tz&lt;/code&gt;.  This is provided so you might write mirroring code:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
;; in one file... 
(require (planet bzlib/date/&lt;b&gt;srfi&lt;/b&gt;)
         (planet bzlib/date-tz/&lt;b&gt;srfi&lt;/b&gt;)) 

;; then you can change it to 
(require (planet bzlib/date/&lt;b&gt;plt&lt;/b&gt;)
         (planet bzlib/date-tz/&lt;b&gt;plt&lt;/b&gt;)) 
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
&lt;b&gt;Different Types of Date Comparisons&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
The date comparison functions (&lt;code&gt;date=?&lt;/code&gt;, &lt;code&gt;date&amp;lt;?&lt;/code&gt;, &lt;code&gt;date&amp;gt;?&lt;/code&gt;, &lt;code&gt;date&amp;lt;=?&lt;/code&gt;, &lt;code&gt;date&amp;gt=?&lt;/code&gt;, &lt;code&gt;date!=?&lt;/code&gt;) can now be used to compare the dates in the following fashions:&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;You can now compare multiple dates at once (previously - just two)&lt;/li&gt;
&lt;li&gt;You can use it to compare for:&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;&lt;b&gt;&lt;code&gt;day-only&lt;/code&gt;&lt;/b&gt; - just compare the day (year month and day)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;day+time&lt;/code&gt;&lt;/b&gt; - compare both the day &amp; the time (hour minute second), but without comparing timezone&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;date&lt;/code&gt;&lt;/b&gt; - this is the default behavior - compare the date &amp; time as well as accounting for the timezone.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;date+tz&lt;/code&gt;&lt;/b&gt; - this would require the dates being compared all have the same time zone&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;To control the behavior, you use the &lt;code&gt;date-comp-type&lt;/code&gt; parameter:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(parameterize ((date-comp-type &#39;day-only ;; or &#39;day+time &#39;date &#39;date+tz 
                ))
  (date&amp;lt;? d1 d2 d3 ...)) 
&lt;/code&gt;&lt;/pre&gt;&lt;code&gt;day=?&lt;/code&gt;, &lt;code&gt;day!=?&lt;/code&gt;, &lt;code&gt;day&amp;lt;?&lt;/code&gt;, &lt;code&gt;day&amp;lt;=?&lt;/code&gt;, &lt;code&gt;day&amp;gt;?&lt;/code&gt;, &lt;code&gt;day&amp;gt=?&lt;/code&gt; are provided as helper functions that parameterize the &lt;code&gt;date-comp-type&lt;/code&gt; to &lt;code&gt;&#39;day-only&lt;/code&gt; for you.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Additional Utility Functions for Timezone&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;current-date/tz&lt;/code&gt; returns a date based on the optional tz value (which defaults to &lt;code&gt;(current-tz)&lt;/code&gt;:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
&gt; (list (parameterize ((current-tz &quot;America/Los_Angeles&quot;))
          (current-date/tz))
        (parameterize ((current-tz &quot;Europe/Brussels&quot;))
          (current-date/tz)))
;; notice the date difference... 
(#(struct:tm:date 0 56 18 13 20 10 2009 -25200)
 #(struct:tm:date 0 56 18 22 20 10 2009 7200))
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
&lt;code&gt;daylight-saving-time?&lt;/code&gt; returns true or false depending on the optional date and tz (default to the current-date &amp; current-tz):&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
&gt; (list (parameterize ((current-tz &quot;America/Los_Angeles&quot;))
          (daylight-saving-time?))
        (parameterize ((current-tz &quot;Asia/Taipei&quot;))
          (daylight-saving-time?)))
(#t #f)
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
That&#39;s it for now. Enjoy.</content><link rel='replies' type='application/atom+xml' href='http://weblambda.blogspot.com/feeds/2115262405132314043/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://weblambda.blogspot.com/2009/10/bzlibdate-bzlibdate-tz-02-now-available.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7570364353928786565/posts/default/2115262405132314043'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7570364353928786565/posts/default/2115262405132314043'/><link rel='alternate' type='text/html' href='http://weblambda.blogspot.com/2009/10/bzlibdate-bzlibdate-tz-02-now-available.html' title='BZLIB/DATE &amp; BZLIB/DATE-TZ 0.2 Now Available'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7570364353928786565.post-3293551660821506373</id><published>2009-10-19T00:58:00.000-07:00</published><updated>2009-10-19T01:00:49.061-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="parser combinator"/><category scheme="http://www.blogger.com/atom/ns#" term="plt scheme"/><category scheme="http://www.blogger.com/atom/ns#" term="top down"/><title type='text'>Building Parser Combinators in Scheme (2) - Higher Order Combinators</title><content type='html'>Previously we started &lt;a href=&quot;http://weblambda.blogspot.com/2009/10/building-parser-combinators-in-scheme.html&quot;&gt;building parser combinators for parsing a symbol&lt;/a&gt; that has the following signature &lt;code&gt;(seq alpha (zero-more (one-of alpha numeric)))&lt;/code&gt;, and we stopped at refactoring the &lt;code&gt;numeric&lt;/code&gt; and &lt;code&gt;alpha&lt;/code&gt; parsers, and ended up with &lt;code&gt;return&lt;/code&gt;, &lt;code&gt;fail&lt;/code&gt;, and &lt;code&gt;char-test&lt;/code&gt;. &lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;&lt;code&gt;char-test&lt;/code&gt; Expanded&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;char-test&lt;/code&gt; gives us a good base for building a bunch of other character-based parser:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
;; char= returns a parser to test whether the next char equals c
(define (char= c)
  (char-test (lambda (it)
               (char=? it c)))) 

;; in-chars returns a parser to match against the list of chars
(define (in-chars lst)
  (char-test (lambda (it) 
               (member it lst)))) 

;; not-in-chars is the reverse of in-chars 
(define (not-in-chars lst)
  (char-test (lambda (it) 
               (not (member it lst))))) 

;; in-char-range returns a parser to match char between from &amp; to chars
(define (in-char-range from to)
  (char-test (lambda (it)
               (char&lt;=? from it to))))

;; the oppposite of in-char-range
(define (not-in-char-range from to)
  (char-test (lambda (it)
               (not (char&lt;=? from it to)))))
&lt;/code&gt;&lt;/pre&gt;So we can build parsers such as the following:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
;; a parser to test for the backslash
(define backslash (char= #\\)) 
;; writing numeric using in-chars
(define numeric (in-chars &#39;(#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)))
;; writing equivalent to regexp \S (non-whitespace) 
(define non-whitespace (not-in-chars &#39;(#\tab #\return #\newline #\space #\vtab)))
;; lower-case alpha using in-char-range 
(define lower-case (in-char-range #\a #\z)))
;; upper-case alpha using in-char-range
(define upper-case (in-char-range #\A #\Z))) 
&lt;/code&gt;&lt;/pre&gt;It would be nice to write alpha in terms of &lt;code&gt;upper-case&lt;/code&gt; and &lt;code&gt;lower-case&lt;/code&gt; as defined above:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define alpha (one-of upper-case lower-case)) 
&lt;/code&gt;&lt;/pre&gt;So let&#39;s see how we can write &lt;code&gt;one-of&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Higher order Parsers&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
The idea of &lt;code&gt;one-of&lt;/code&gt; is straight forward - take in a list of the parsers to test against the input, one parser at a time.  The first one that succeeded would be returned:&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define (one-of . test)
  (lambda (in (skip 0)) 
    (let loop ((rest test))
      (if (null? rest)
          (fail in skip)
          (let-values (((v count)
                        ((car rest) in skip)))
            (if (not count)
                (loop (cdr rest))
                ((return v) in count)))))))
&lt;/code&gt;&lt;/pre&gt;Now we can then write the following:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define alpha (one-of (in-char-range #\a #\z)
                      (in-char-range #\A #\Z)))

(define alpha-numeric (one-of numeric alpha)) 
&lt;/code&gt;&lt;/pre&gt;&lt;code&gt;one-of&lt;/code&gt; is basically a parser combinator that acts like an &lt;code&gt;or&lt;/code&gt; expression.  We can also have a combinator that acts like an &lt;code&gt;and&lt;/code&gt;:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define (all-of . test)
  (lambda (in (skip 0)) 
    (let loop ((rest test)
               (v #f)
               (count skip))
      (if (null? rest)
          ((return v) in count)
          (let-values (((v count)
                        ((car rest) in skip)))
            (if (not count)
                (fail in skip)
                (loop (cdr rest) v count)))))))
&lt;/code&gt;&lt;/pre&gt;Notice that &lt;code&gt;all-of&lt;/code&gt; will return the value and the count from the last match (the same behavior as &lt;code&gt;and&lt;/code&gt;), but all the previous tests also need to match.  It would of course be the user&#39;s responsibility to construct valid combination that will pass. &lt;br /&gt;
&lt;br /&gt;
If we want to parse multiple matches in succession (i.e. one after another), we need a sequence combinator:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define (seq . test)
  (lambda (in (skip 0)) 
    (let loop ((rest test)
               (acc &#39;()) 
               (count skip))
      (if (null? rest)
          ((return (reverse acc)) in count)
          (let-values (((v count)
                        ((car rest) in count)))
            (if (not count) ;; we are done! 
                (fail in skip)
                (loop (cdr rest) (cons v acc) count)))))))
&lt;/code&gt;&lt;/pre&gt;This will allow us to parse a sequence of tokens, for example, the below demonstrates parsing a social security number in &lt;code&gt;nnn-nn-nnnn&lt;/code&gt; form:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define dash (char= #\-)) 
&lt;b&gt;(define ssn (seq numeric numeric numeric dash numeric numeric dash numeric numeric numeric numeric))&lt;/b&gt; 
&lt;/code&gt;&lt;/pre&gt;Now it can be bothersome to write repeated numerics like above, so we can have a repeat parser:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define (repeat test n) 
  (lambda (in (skip 0)) 
    (let loop ((i 0)
               (acc &#39;())
               (count skip))
      (let-values (((v count)
                    (test in count))) 
        (cond ((not count) ;; failed before reaching n
               (fail in skip))
              ((= i (sub1 n)) ;; succeeded
               ((return (reverse (cons v acc))) in count))
              (else
               (loop (add1 i) (cons v acc) count)))))))
&lt;/code&gt;&lt;/pre&gt;Then &lt;code&gt;ssn&lt;/code&gt; can be written as:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define NN (repeat numeric 2)) 
(define NNN (repeat numeric 3)) 
(define NNNN (repeat numeric 4)) 
(define ssn (seq NNN dash NN dash NNNNN)) 
&lt;/code&gt;&lt;/pre&gt;&lt;code&gt;repeat&lt;/code&gt; parses for fixed numbers of repeats - what if we want to have unbounded repeats?  Let&#39;s try to build &lt;code&gt;zero-many&lt;/code&gt; that&#39;ll match for zero or more occurrences:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define (zero-many test) 
  (lambda (in (skip 0)) 
    (let loop ((acc &#39;())
               (count skip)) 
      (let-values (((v new-count)
                    (test in count)))
        (if (not new-count) ;; we are done... 
            ((return (reverse acc)) in count)
            (loop (cons v acc) new-count))))))

&lt;/code&gt;&lt;/pre&gt;Which we can then use to build &lt;code&gt;one-many&lt;/code&gt; that must have at least one match:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define (one-many test)
  (lambda (in (skip 0))
    (let-values (((v count)
                  (test in skip))) 
      (if (not count)
          (fail in skip)
          (let-values (((out count)
                        ((zero-many test) in count)))
            ((return (cons v out)) in count))))))
&lt;/code&gt;&lt;/pre&gt;And of course there is a special test of zero or one occurrence:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define (zero-one test)
  (lambda (in (skip 0)) 
    (let-values (((v count)
                  (test in skip))) 
      (if (not count) 
          ((return #f) in skip)
          ((return v) in count)))))
&lt;/code&gt;&lt;/pre&gt;With all the above we now finally can construct the symbol parse:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define symbol (seq alpha (zero-many (one-of alpha numeric))))
&lt;/code&gt;&lt;/pre&gt;Which will return the following when parsing a symbol:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
&gt; (symbol (open-input-string &quot;asymbol1 &quot;))
(#\a (#\s #\y #\m #\b #\o #\l #\1))
8
&lt;/code&gt;&lt;/pre&gt;The first is the read value - notice that they are listed according to the position within the sequence (&lt;code&gt;#\a&lt;/code&gt; matches &lt;code&gt;alpha&lt;/code&gt;, and &lt;code&gt;(#\s #\y #\m #\b #\o #\l #\1)&lt;/code&gt; matches &lt;code&gt;(zero-many (one-of alpha numeric))&lt;/code&gt;). And the second value indicating the bytes &quot;peeked&quot;, which is 8. &lt;br /&gt;
&lt;br /&gt;
The final step is to have a parser that allow us to take in the value and transform them according to our needs:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define (make-parser test return)
  (lambda (in (skip 0)) 
    (let-values (((v count)
                  (test in skip))) 
      (cond ((not count)
             (values #f #f))
            (else
             (read-bytes count in)
             (values (apply return v) count))))))
&lt;/code&gt;&lt;/pre&gt;So we can build the final symbol parser as following:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define parse-symbol 
  (make-parser (seq alpha (zero-many (one-of alpha numeric)))
    (lambda (alpha lst) 
      (list-&gt;string (cons alpha lst)))))
&lt;/code&gt;&lt;/pre&gt;which will return a string for us instead of the above args:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
&gt; (parse-symbol (open-input-string &quot;asymbol1 &quot;))
&quot;asymbol1&quot;
8
&lt;/code&gt;&lt;/pre&gt;At this point we have the most of the basic parser combinators constructed, and the rest is to fill in the details as necessary.  We&#39;ll take a look at how to improve the parser to handle more complex scenarios in the future posts.  Stay tuned.</content><link rel='replies' type='application/atom+xml' href='http://weblambda.blogspot.com/feeds/3293551660821506373/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://weblambda.blogspot.com/2009/10/building-parser-combinators-in-scheme-2.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7570364353928786565/posts/default/3293551660821506373'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7570364353928786565/posts/default/3293551660821506373'/><link rel='alternate' type='text/html' href='http://weblambda.blogspot.com/2009/10/building-parser-combinators-in-scheme-2.html' title='Building Parser Combinators in Scheme (2) - Higher Order Combinators'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7570364353928786565.post-8023167153652454545</id><published>2009-10-16T11:09:00.000-07:00</published><updated>2009-10-16T11:09:00.237-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="parser"/><category scheme="http://www.blogger.com/atom/ns#" term="parser combinator"/><category scheme="http://www.blogger.com/atom/ns#" term="plt scheme"/><category scheme="http://www.blogger.com/atom/ns#" term="top down"/><title type='text'>Building Parser Combinators in Scheme</title><content type='html'>I do not write code for the sake of writing code, but sometimes the best way to understand a concept is to write it up.  I can think of many situations where I have no clue what the heck is going on by reading the documentations or even the tutorials, but I start to understand what&#39;s going on once I start to hack up with a solution.&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;http://en.wikipedia.org/wiki/Parser_combinator&quot;&gt;Parser combinator&lt;/a&gt; is one of those things for me.  Based on what I can find, Haskell was where the term is coined and expanded, but unfortunately I cannot read Haskell well enough to fully follow the excellent monadic parser combinator paper, let along &lt;a href=&quot;http://www.haskell.org/haskellwiki/Parsec&quot;&gt;trying to understand Parsec&lt;/a&gt;, and think about how it would be translated into scheme. &lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;http://pre.plt-scheme.org/plt/collects/combinator-parser/doc.txt&quot;&gt;PLT Scheme contains a parser combinator library&lt;/a&gt;, but its documentation is written for someone who is completely familiar with the parser combinator and how it would work within scheme, so I couldn&#39;t start with it (in contrast, I was able to figure out &lt;a href=&quot;http://docs.plt-scheme.org/parser-tools/&quot;&gt;&lt;code&gt;parser-tools&lt;/code&gt;&lt;/a&gt; in short order, even though my previous experience with lex &amp; yacc isn&#39;t a lot either).&lt;br /&gt;
&lt;br /&gt;
Searching online, there are a couple of parser combinator written in scheme:&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;http://alexicalmistake.com/2008/06/parsers-and-combinators/&quot;&gt;A Lexical Mistake: Parsers and Combinators&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://shaurz.wordpress.com/2008/03/11/haskell-style-parser-combinators-in-scheme/&quot;&gt;Haskell-style Parser Combinators in Scheme&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;It wasn&#39;t much, but it was something that I can build on.  Let&#39;s get started... &lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Immediate Obstacle - Reading from Port Instead of String or List&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
Parser combinator promises top-down parser development in almost BNF-like fashion, including infinitely look ahead and unlimited backtracking, all of which are extremely fascinating powers.  &lt;br /&gt;
&lt;br /&gt;
But reading the &lt;a href=&quot;http://www.cs.nott.ac.uk/~gmh/monparsing.pdf&quot;&gt;monadic parser combinator&lt;/a&gt; leaves me scratching my head wondering how this would work with ports.  The parser combinator appears to derive its backtracking power via reading the string into a list of characters.  This is nice and all but it doesn&#39;t work with ports, since once a character is read from a port it is gone and you lose the ability to backtrack (yes - with some ports it is possible to backtrack but this does not work with all ports).&lt;br /&gt;
&lt;br /&gt;
I wasn&#39;t able to find an answer to this problem via the above URLs either, so I&#39;ll have to come up with my own solution. &lt;br /&gt;
&lt;br /&gt;
What I came up with was the following:&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;we&#39;ll use the peek-* functions instead of the read-* functions to access the port data &lt;/li&gt;
&lt;li&gt;we&#39;ll keep track of the byte counts that we have accessed so far, and we&#39;ll pass the byte count as a skip-ahead to the next parser&lt;/li&gt;
&lt;li&gt;we&#39;ll only do a single read at the end to remove everything we have found so far - i.e. if the parse failed we&#39;ll never remove a single character from the port&lt;/li&gt;
&lt;/ul&gt;By adhering to the above approach we now can backtrack without side-effect on ports as well (the side-effect only occur once at the end of the parse when we have a successful match). &lt;br /&gt;
&lt;br /&gt;
With the above in mind - let&#39;s start building our parser combinators.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;A Simple Example&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
Let&#39;s try to parse a very simple example - a symbol that contains only alphanumeric characters, with the first character being alpha:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(parse-symbol (open-input-string &quot;asymbol1 &quot;)) ;; =&gt; &quot;asymbol1&quot; 
&lt;/code&gt;&lt;/pre&gt;So what should parse token look like? If it looks something like the following it would be good. &lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(make-parser &lt;b&gt;(seq alpha (zero-more (one-of alpha numeric)))&lt;/b&gt; 
  (lambda (a1 lst) 
    (list-&gt;string (cons a1 lst))) 
&lt;/code&gt;&lt;/pre&gt;Look at the above bolded line - you can almost read it as the definition of the token: a sequence of an alpha character, followed by zero or more of either an alpha or a numeric character.  All we need to do is to build the &lt;code&gt;seq&lt;/code&gt;, &lt;code&gt;alpha&lt;/code&gt;, &lt;code&gt;numeric&lt;/code&gt;, &lt;code&gt;one-of&lt;/code&gt;, and &lt;code&gt;zero-more&lt;/code&gt;. &lt;br /&gt;
&lt;br /&gt;
We should start with the simplest of the above, which would be the &lt;code&gt;numeric&lt;/code&gt; parser - we can implement it as the following:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define (numeric in (skip 0)) 
  (let ((c (peek-char in skip)))
    (if (char&lt;=? #\0 c #\9)
        (values c (add1 skip)) 
        (values #f #f)))) 
&lt;/code&gt;&lt;/pre&gt;Basically - if the next character is one of the numeric characters, we will return the character and the updated count (which is increment of the skip count).  Otherwise we return &lt;code&gt;#f&lt;/code&gt; for both, with the second indicating that we did not read anything.  Note it is the second value indicating whether or not we have a successful parse, because we want to allow &lt;code&gt;#f&lt;/code&gt; as a legal return value from a successful parse.  This means if you want to backtrack you&#39;ll have to keep track of the skip in your calling function in case the parse fails. &lt;br /&gt;
&lt;br /&gt;
Although this example is &quot;simple&quot; - it already tells us exactly what a parser would look like:&lt;br /&gt;
&lt;ol&gt;&lt;li&gt;peek something from the port&lt;/li&gt;
&lt;li&gt;do some test to determine whether the peek returns the desired data&lt;/li&gt;
&lt;li&gt;if it does - return the data and update the count appropriately - this is a &quot;success&quot;&lt;/li&gt;
&lt;li&gt;otherwise return #f #f - this is a &quot;fail&quot;&lt;/li&gt;
&lt;/ol&gt;All of the parsers will basically follow this structure. &lt;br /&gt;
&lt;br /&gt;
Now - both the success and the fail can be refactored out as their own parsers:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define (fail in (skip 0))
  (values #f #f)) 

(define (return v) ;; we return the value without consuming from the port 
  (lambda (in (skip 0)) 
    (values v skip))) 
&lt;/code&gt;&lt;/pre&gt;Then &lt;code&gt;numeric&lt;/code&gt; can be rewritten as:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define (numeric in (skip 0)) 
  (let ((c (peek-char in skip)))
    (if (char&amp;lt;=? #\0 c #\9)
        &lt;b&gt;((return c) in (add1 skip))
        (fail in skip)&lt;/b&gt;))) 
&lt;/code&gt;&lt;/pre&gt;This makes it more flexible to construct simpler higher level parser combinators. &lt;br /&gt;
&lt;br /&gt;
The &lt;code&gt;alpha&lt;/code&gt; can be written as follows:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define (alpha in (skip 0)) 
  (let ((c (peek-char in skip))) 
    (if (or (char&amp;lt;=? #\a c #\z) (char&amp;lt;=? #\A c #\Z))
        ((return c) in (add1 skip)) 
        (fail in skip)))) 
&lt;/code&gt;&lt;/pre&gt;It ought to be clear that we can refactor &lt;code&gt;alpha&lt;/code&gt; and &lt;code&gt;numeric&lt;/code&gt; to make it more succinct:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define (char-test test?) 
  (lambda (in (skip 0)) 
    (let ((c (peek-char in skip)))
      (if (and (char? c) (test? c)) 
          ((return c) in (add1 skip)) 
          (fail in skip)))))

(define alpha 
  (char-test (lambda (c) 
               (or (char=&amp;lt;? #\a c #\z) (char=&amp;lt;? #\A c #\Z)))))

(define numeric 
  (char-test (lambda (c)
               (char=&amp;lt;? #\0 c #\9))))
&lt;/code&gt;&lt;/pre&gt;In this case - &lt;code&gt;char-test&lt;/code&gt; is a higher order parser that takes in a predicate to create a parser.  We now have a base to create more character-based parsers.  We&#39;ll do so in the next post.  Stay tuned.</content><link rel='replies' type='application/atom+xml' href='http://weblambda.blogspot.com/feeds/8023167153652454545/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://weblambda.blogspot.com/2009/10/building-parser-combinators-in-scheme.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7570364353928786565/posts/default/8023167153652454545'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7570364353928786565/posts/default/8023167153652454545'/><link rel='alternate' type='text/html' href='http://weblambda.blogspot.com/2009/10/building-parser-combinators-in-scheme.html' title='Building Parser Combinators in Scheme'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7570364353928786565.post-7710615739247188564</id><published>2009-10-15T16:02:00.000-07:00</published><updated>2009-10-15T16:02:00.127-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="encoded word"/><category scheme="http://www.blogger.com/atom/ns#" term="net"/><category scheme="http://www.blogger.com/atom/ns#" term="plt scheme"/><title type='text'>Parsing &quot;Encoded Word&quot; in RFC Headers (3) - Parsing &amp; Decoding</title><content type='html'>In the previous two posts we have &lt;a href=&quot;http://weblambda.blogspot.com/2009/10/parsing-encoded-word-in-rfc-headers.html&quot;&gt;built capabilities&lt;/a&gt; to &lt;a href=&quot;http://weblambda.blogspot.com/2009/10/parsing-encoded-word-in-rfc-headers-2.html&quot;&gt;generate encoded words&lt;/a&gt;, now it&#39;s time to decode them. We&#39;ll start with detecting whether a string is an encoded word.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Testing for Encoded Word&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
Remember that an encoded word has the following format:&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;
&lt;b&gt;=?&lt;/b&gt;&amp;lt;charset&amp;gt;&lt;b&gt;?&lt;/b&gt;&amp;lt;&lt;b&gt;Q&lt;/b&gt; or &lt;b&gt;B&lt;/b&gt;&amp;gt;&lt;b&gt;?&lt;/b&gt;&amp;lt;encoded data&amp;gt;&lt;b&gt;?=&lt;/b&gt;
&lt;/code&gt;&lt;/pre&gt;The above format can be succintly specified via regular expression:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define encoded-word-regexp &lt;b&gt;#px&quot;=\\?([^\\?]+)\\?(?i:(b|q))\\?([^\\?]+)\\?=&quot;&lt;/b&gt;)

(define (encoded-word? str)
  (regexp-match encoded-word-regexp str))
&lt;/code&gt;&lt;/pre&gt;Since the format is not recursive, regular expression is good enough, even though it can be considered as ugly.  You can certainly try using other approaches, such as a hand written lexer, or using &lt;a href=&quot;http://docs.plt-scheme.org/parser-tools/&quot;&gt;&lt;code&gt;parser-tools&lt;/code&gt;&lt;/a&gt; to do the job.  For now we keep things simple. &lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Decoding&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
Once we can test whether a string is an encoded word, we can then use it to handle the decoding:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define (encoded-word-&gt;string str)
  (if-it (encoded-word? str)
         (apply decode-encoded-word (cdr it))
         str))
&lt;/code&gt;&lt;/pre&gt;If the string is an encoded word, we decode it, otherwise we return it verbatim.  This way it allows regular string to be passed into this function.&lt;br /&gt;
&lt;br /&gt;
The &lt;code&gt;decode-encoded-word&lt;/code&gt; function looks like the following:&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define (decode-encoded-word charset encode str) 
  (bytes/charset-&gt;string ((cond ((string-ci=? encode &quot;q&quot;) q-decode)
                                ((string-ci=? encode &quot;b&quot;) b-decode)) (string-&gt;bytes/utf-8 str))
                         (string-downcase charset) 
                         ))
&lt;/code&gt;&lt;/pre&gt;Of course - if the charset and the bytes do not match, it would error out, which is a sensible choice since the only time that would have occurred would be due to bugs in the generation. &lt;br /&gt;
&lt;br /&gt;
Now that we can handle decoding a single encoded word, we need to handle decoding a string with multiple encoded words intermixing with non encoded words. &lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Decoding Multiple Encoded Words&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
While RFC822 does not define an actual maximum length for the header values, it considers headers &gt; 72 characters as &quot;long&quot; since the users wanted (back then) to be able to read the headers in a terminal setting, and hence &lt;a href=&quot;http://tools.ietf.org/html/rfc822#section-3.4&quot;&gt;they build in the ability to &quot;fold&quot; a line into multiple lines with the use of LFWS&lt;/a&gt; (&lt;code&gt;\r\n\s&lt;/code&gt;). &lt;br /&gt;
&lt;br /&gt;
So a line of&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;
&quot;this is a line and it continues \r\n
 on the next line&quot;
&lt;/code&gt;&lt;/pre&gt;Should be folded into &lt;br /&gt;
&lt;pre&gt;&lt;code&gt;
&quot;this is a line and it continues on the next line&quot;
&lt;/code&gt;&lt;/pre&gt;And since an encoded word can have maximum length of 72 bytes, having multiple of them means that the line will most likely be folded, with a high likelihood that each single line within consists of a single encoded word (or it is not enocoded). &lt;br /&gt;
&lt;br /&gt;
We have previously discussed on how to fold such a line with &lt;br /&gt;
&lt;a href=&quot;http://weblambda.blogspot.com/2009/08/continuing-of-integration-features-http.html&quot;&gt;&lt;code&gt;read-folded-line&lt;/code&gt;&lt;/a&gt;, so we can use it as a basis for reading in the folded line first and then try to parse out the encoded words from the folded line, but this requires quite a bit of work since:&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;our regex test for encoded word will consumed and throw away bytes that are not encoded words, which is not what we want&lt;/li&gt;
&lt;li&gt;if we do not want to throw away the bytes we will have to look for a different approach - either writing a custom lexer or use &lt;code&gt;parser-tools&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;if we take that approach then what we have written so far is useless&lt;/li&gt;
&lt;/ul&gt;Or is it? Let&#39;s see how far we can salvage what we have before having to look for another solution.&lt;br /&gt;
&lt;br /&gt;
As we stated above, a very likely scenario for multi-encoded-word line is that each encoded word will be on its own line (and if one of the line is not encoded it should not have encoded words), so a very simple approach would be to let &lt;code&gt;decode-encoded-word&lt;/code&gt; handle the conversion while &lt;code&gt;read-folded-line&lt;/code&gt; is accumulating and folding over the lines.  This will require us to modify &lt;code&gt;read-folded-line&lt;/code&gt;:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define (read-folded-line in &lt;b&gt;(convert identity)&lt;/b&gt;) 
  (define (folding? c)
    (or (equal? c #\space)
        (equal? c #\tab)))
  (define (return lines) 
    (apply string-append &quot;&quot; (reverse lines)))
  (define (convert-folding lines)
    (let ((c (peek-char in)))
      (cond ((folding? c) 
             (read-char in)
             (convert-folding lines))
            (else
             (helper lines)))))
  (define (helper lines)
    (let ((l (read-line in &#39;return-linefeed)))
      (if (eof-object? l) 
          (return lines)
          (let ((c (peek-char in)))
            (if (folding? c) ;; we should keep going but first let&#39;s convert all folding whitespaces... 
                (convert-folding (cons &lt;b&gt;(convert l)&lt;/b&gt; lines))
                ;; otherwise we are done... 
                (return (cons &lt;b&gt;(convert l)&lt;/b&gt; lines)))))))
  (helper &#39;()))
&lt;/code&gt;&lt;/pre&gt;Then we can write the decoder as follows:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define (encoded-word-string-&gt;string str)
  (read-folded-line (open-input-string str) encoded-word-&gt;string))
&lt;/code&gt;&lt;/pre&gt;Which will handle encoded word string that is generated &quot;normally&quot; where each encoded word will reside on its own line.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Handling General Case of Multiple Encoded Words on the same Line&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
While the above &lt;code&gt;encoded-word-string-&gt;string&lt;/code&gt; should handle normally generated encoded word string out there, it still cannot handle situations where multiple encoded words resides on the same line, or if encoded words coincide with non encoded words on the same line.  Such situation can occur if the generation strategy is to encode each word individually (in a way this is why it&#39;s called &quot;encoded word&quot;) - it&#39;s there in the RFC1342 example:&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;
... 
CC: &lt;b&gt;=?ISO-8859-1?Q?Andr=E9_?=&lt;/b&gt; &lt;b&gt;Pirard&lt;/b&gt; &lt;PIRARD@vm1.ulg.ac.be&gt;
... 
&lt;/code&gt;&lt;/pre&gt;If we try to decode it with what we have we&#39;ll lose the non encoded word:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
&gt; (encoded-word-string-&gt;string &quot;=?ISO-8859-1?Q?Andr=E9_?= Pirard&quot;)
&lt;b&gt;&quot;André &quot; ;; we lost Pirard&lt;/b&gt;
&lt;/code&gt;&lt;/pre&gt;How can we solve this problem?  Can we push what we have further or do we need to buckle down and look at using &lt;code&gt;parser-tools&lt;/code&gt;?&lt;br /&gt;
&lt;br /&gt;
Fortunately the format of encoded words helps us out.  As defined in RFC1342, only way the above situation would exist is if they are separated by either spaces (which are significant) on the same line.  Hence we can split the line by space, and then decode the individual word, and then join back by space:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define (encoded-word-string-&gt;string str)
  &lt;b&gt;(define (helper line)
    (string-join (map encoded-word-&gt;string (regexp-split #px&quot; &quot; line)) 
                 &quot; &quot;))&lt;/b&gt;
  (read-folded-line (open-input-string str) helper))
&lt;/code&gt;&lt;/pre&gt;That&#39;s it - now we can generate and parse encoded words in RFC message headers.  Enjoy.</content><link rel='replies' type='application/atom+xml' href='http://weblambda.blogspot.com/feeds/7710615739247188564/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://weblambda.blogspot.com/2009/10/parsing-encoded-word-in-rfc-headers-3.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7570364353928786565/posts/default/7710615739247188564'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7570364353928786565/posts/default/7710615739247188564'/><link rel='alternate' type='text/html' href='http://weblambda.blogspot.com/2009/10/parsing-encoded-word-in-rfc-headers-3.html' title='Parsing &quot;Encoded Word&quot; in RFC Headers (3) - Parsing &amp; Decoding'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7570364353928786565.post-4809362444551479427</id><published>2009-10-14T18:53:00.000-07:00</published><updated>2009-10-14T18:53:13.668-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="encoded word"/><category scheme="http://www.blogger.com/atom/ns#" term="net"/><category scheme="http://www.blogger.com/atom/ns#" term="plt scheme"/><title type='text'>Parsing &quot;Encoded Word&quot; in RFC Headers (2) - Charset Handling &amp; Multiple Encoded Words</title><content type='html'>In the previous post we discussed the &lt;code&gt;Q&lt;/code&gt; and &lt;code&gt;B&lt;/code&gt; encodings, and ended with &lt;a href=&quot;http://weblambda.blogspot.com/2009/10/parsing-encoded-word-in-rfc-headers.html&quot;&gt;a bug on mismatching charset if the charset is not &lt;code&gt;utf-8&lt;/code&gt;&lt;/a&gt;, let&#39;s try to fix the bug here. &lt;br /&gt;
&lt;br /&gt;
It would be nice if we can use local charsets such as &lt;code&gt;iso-8559-1&lt;/code&gt; or &lt;code&gt;big5&lt;/code&gt; if we know for sure that the charset contains all of the characters that appears in the string (of course, it is the developer&#39;s responsibility to choose the right charset; the code will error out if the charset does not match the data).&lt;br /&gt;
&lt;br /&gt;
PLT Scheme provides a &lt;a href=&quot;http://docs.plt-scheme.org/reference/port-lib.html#%28def._%28%28lib._scheme/port..ss%29._convert-stream%29%29&quot;&gt;&lt;code&gt;convert-stream&lt;/code&gt;&lt;/a&gt; to help handle converting bytes from one charset to another.  We can build helpers that takes strings or bytes and return string or bytes on top of this function.  What we want are something like:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(bytes/charset-&gt;string #&quot;this is a string&quot; &quot;ascii&quot;) ;; =&gt; returns a string
(bytes/charset-&gt;bytes/utf-8 &amp;lt;bytes&amp;gt; &amp;lt;charset&amp;gt;) ;; =&gt; returns a bytes
&lt;/code&gt;&lt;/pre&gt;The idea is that we&#39;ll convert the input data to input-port, and then retrieve the data from the output-port, which will be a bytes port. &lt;br /&gt;
&lt;br /&gt;
So let&#39;s start with a helper function that&#39;ll take in an input-port, and the charsets and then return a bytes:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define (port-&gt;bytes/charset in charset-in charset-out)
  (call-with-output-bytes 
   (lambda (out)
     (convert-stream charset-in in charset-out out))))
&lt;/code&gt;&lt;/pre&gt;Then we can have the following:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define (bytes-&gt;bytes/charset bytes charset-in charset-out)
  (port-&gt;bytes/charset (open-input-bytes bytes) charset-in charset-out))
&lt;/code&gt;&lt;/pre&gt;And we can define converting bytes to and from utf-8:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define (bytes/charset-&gt;bytes/utf-8 bytes charset)
  (bytes-&gt;bytes/charset bytes charset &quot;utf-8&quot;)) 

(define (bytes/utf-8-&gt;bytes/charset bytes charset)
  (bytes-&gt;bytes/charset bytes &quot;utf-8&quot; charset))
&lt;/code&gt;&lt;/pre&gt;And finally we can then return strings on top of these two functions:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
;; there are more to handle (specifically charsets).
(define (bytes/charset-&gt;string bytes charset)
  (bytes-&gt;string/utf-8 (bytes/charset-&gt;bytes/utf-8 bytes charset)))

(define (string-&gt;bytes/charset string charset)
  (bytes/utf-8-&gt;bytes/charset (string-&gt;bytes/utf-8 string) charset))
&lt;/code&gt;&lt;/pre&gt;With the above functions, we can now ensure to convert the encoded word into the correct charset:&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define (encode-encoded-word charset encode str)
  (format &quot;=?~a?~a?~a?=&quot; 
          (string-downcase charset)
          (string-upcase encode)
          ((cond ((string-ci=? encode &quot;q&quot;) q-encode)
                 ((string-ci=? encode &quot;b&quot;) b-encode)) 
           &lt;b&gt;(string-&gt;bytes/charset str charset)&lt;/b&gt;)))
&lt;/code&gt;&lt;/pre&gt;Notice now that converting the same string with different charset will result in different encoded word:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
&gt; (encode-encoded-word &quot;iso-8859-1&quot; &quot;q&quot; &quot;Keld Jørn Simonsen&quot;)
&quot;=?iso-8859-1?Q?Keld_J&lt;b&gt;=F8&lt;/b&gt;rn_Simonsen?=&quot;
&gt; (encode-encoded-word &quot;utf-8&quot; &quot;q&quot; &quot;Keld Jørn Simonsen&quot;)
&quot;=?utf-8?Q?Keld_J&lt;b&gt;=C3=B8&lt;/b&gt;rn_Simonsen?=&quot;
&lt;/code&gt;&lt;/pre&gt;So now the bug is fixed.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Convert a String of Arbitrary Length into Encoded Word String&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
In cases of a string exceeding the encoded word length of 75, we&#39;ll need to convert the string into multiple encoded words, separated by linear folding whitespace (&lt;code&gt;\r\n\s&lt;/code&gt;).&lt;br /&gt;
&lt;br /&gt;
Since both &lt;code&gt;Q&lt;/code&gt; and &lt;code&gt;B&lt;/code&gt; encoding will lengthen the actual bytes (increasing by 33% in case of &lt;code&gt;B&lt;/code&gt;), we will not be able to encode 75 bytes; instead, we can only encode 75 bytes minus the delimiters (12 bytes) and divide by 133%, which will give us total of 48 bytes of characters per encoded word.&lt;br /&gt;
&lt;br /&gt;
Also - since some of the characters will be multi-bytes, we want to make sure we do not break up the string right in the middle of a character.  We want to make sure we break around the characters.&lt;br /&gt;
&lt;br /&gt;
Let&#39;s get started.&lt;br /&gt;
&lt;br /&gt;
The following function will split a string up according to a maximum bytes length:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define (split-string-by-bytes-count str num)
  (define (maker chars)
    (list-&gt;string (reverse chars)))
  (define (helper str i chars blen acc)
    (if (= i (string-length str)) ;; we are done here!!!... 
        (reverse (if (null? chars) acc
                     (cons (maker chars) acc)))
        (let* ((c (string-ref str i))
               (count (char-utf-8-length c))) 
          (if (&gt; (+ count blen) num) ;; we are done with this version....
              (if (= blen 0) ;; this means the character itself is greater than the count.  
                  (helper str (add1 i) &#39;() 0 (cons (maker (cons c chars)) acc))
                  (helper str i &#39;() 0 (cons (maker chars) acc)))
              (helper str (add1 i) (cons c chars) (+ count blen) acc)))))
  (helper str 0 &#39;() 0 &#39;()))
&lt;/code&gt;&lt;/pre&gt;What it does is to accumulate the characters according to the maximum bytes count, and if the addition of the next character&#39;s bytes length exceeds the maximum bytes count, then we do not include that character in the current split.  In the case where the maximum bytes count is lower than the character&#39;s bytes length, that character gets its own string (i.e. if you pass in 0 you&#39;ll get per character split).&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
&gt; (split-string-by-bytes-count &quot;孫中山畢業於香港西醫書院&quot; 0)
(&quot;孫&quot; &quot;中&quot; &quot;山&quot; &quot;畢&quot; &quot;業&quot; &quot;於&quot; &quot;香&quot; &quot;港&quot; &quot;西&quot; &quot;醫&quot; &quot;書&quot; &quot;院&quot;)
&lt;/code&gt;&lt;/pre&gt;Once we can split the string according to maximum bytes count, we can now separately encode the splitted strings (and then join them together with &lt;code&gt;\r\n\s&lt;/code&gt;):&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define (string-&gt;encoded-words s charset)
  (define (helper s)
    (case (string-type s)
      ((ascii) s)
      ((latin-1) (encode-encoded-word &quot;iso-8859-1&quot; &quot;q&quot; s))
      (else (encode-encoded-word charset &quot;b&quot; s))))
  (map helper (split-string-by-bytes-count s 48))) 

(define (string-&gt;encoded-word-string s (charset &quot;utf-8&quot;))
  (string-join (string-&gt;encoded-words s charset) &quot;\r\n &quot;))
&lt;/code&gt;&lt;/pre&gt;Notice that in the above we have tests to see whether the string is an ascii string or a latin-1 string, because we do not have to encode ascii, and &lt;code&gt;Q&lt;/code&gt; is a better encoding for latin-1 string. Also notice that charset only impacts the encoding of strings that containing characters outside of latin-1 characters.&lt;br /&gt;
&lt;br /&gt;
The definition of &lt;code&gt;string-type&lt;/code&gt; is defined as follows:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define (char-type c)
  (let ((i (char-&gt;integer c))) 
    (cond ((&lt; i 128) &#39;ascii)
          ((&lt; i 256) &#39;latin-1)
          (else &#39;unicode))))

(define (string-type s)
  (define (helper len i prev)
    (if (= len i) prev
        (let ((type (char-type (string-ref s i))))
          (case type 
            ((unicode) type)
            ((latin-1) 
             (helper len (add1 i) (case prev
                                    ((ascii) type)
                                    (else prev))))
            (else (helper len (add1 i) prev))))))
  (helper (string-length s) 0 &#39;ascii))
&lt;/code&gt;&lt;/pre&gt;With the above, we can now encode strings into encoded words:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
&gt; (string-&gt;encoded-word-string &quot;Keld Jørn Simonsen&quot;)
;; =&gt; 
=?iso-8859-1?Q?Keld_J=F8rn_Simonsen?=
&gt; (string-&gt;encoded-word-string &quot;伦敦(英文:London,讀音:/ˈlʌndən/ 文件-播放)是英格蘭和英國的首都、第一大城及第一大港&quot;)
;; =&gt; 
=?utf-8?B?5Lym5pWmKOiLseaWhzpMb25kb24s6K6A6Z+zOi/LiGzKjG5kyZluLyDmlofku7Yt?=
 =?utf-8?B?5pKt5pS+KeaYr+iLseagvOiYreWSjOiLseWci+eahOmmlumDveOAgeesrOS4gA==?=
 =?utf-8?B?5aSn5Z+O5Y+K56ys5LiA5aSn5riv?=
&gt; (string-&gt;encoded-word-string &quot;China (simplified Chinese: 中国; traditional Chinese: 中國; Hanyu Pinyin: zh-zhongguo.ogg Zhōngguó (help·info); Tongyong Pinyin: Jhongguó; Wade-Giles: Chung1kuo2) is a cultural region, an ancient civilization, and, depending on perspective, a national or multinational entity extending over a large area in East Asia.&quot;)
;; =&gt; 
=?utf-8?B?Q2hpbmEgKHNpbXBsaWZpZWQgQ2hpbmVzZTog5Lit5Zu9OyB0cmFkaXRpb25hbCBD?=
 =?utf-8?B?aGluZXNlOiDkuK3lnIs7IEhhbnl1IFBpbnlpbjogemgtemhvbmdndW8ub2dnIFpo?=
 =?utf-8?B?xY1uZ2d1w7MgKGhlbHDCt2luZm8pOyBUb25neW9uZyBQaW55aW46IEpob25nZ3U=?=
 =?iso-8859-1?Q?=F3;_Wade-Giles:_Chung1kuo2=)_is_a_cultural_region?=
 , an ancient civilization, and, depending on per
 spective, a national or multinational entity ext
 ending over a large area in East Asia.
&lt;/code&gt;&lt;/pre&gt;At this point, the generation of encoded word string is complete.  Our next step is to parse such an encoded word string back into its original form.  Stay tuned.</content><link rel='replies' type='application/atom+xml' href='http://weblambda.blogspot.com/feeds/4809362444551479427/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://weblambda.blogspot.com/2009/10/parsing-encoded-word-in-rfc-headers-2.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7570364353928786565/posts/default/4809362444551479427'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7570364353928786565/posts/default/4809362444551479427'/><link rel='alternate' type='text/html' href='http://weblambda.blogspot.com/2009/10/parsing-encoded-word-in-rfc-headers-2.html' title='Parsing &quot;Encoded Word&quot; in RFC Headers (2) - Charset Handling &amp; Multiple Encoded Words'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7570364353928786565.post-8033511953037658201</id><published>2009-10-13T23:08:00.000-07:00</published><updated>2009-10-14T03:39:02.850-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="encoded word"/><category scheme="http://www.blogger.com/atom/ns#" term="net"/><category scheme="http://www.blogger.com/atom/ns#" term="plt scheme"/><title type='text'>Parsing &quot;Encoded Word&quot; in RFC Headers</title><content type='html'>If you want to correctly handle internet message headers &lt;a href=&quot;http://tools.ietf.org/html/rfc822&quot;&gt;as defined in RFC822&lt;/a&gt; or &lt;a href=&quot;http://tools.ietf.org/html/rfc2822&quot;&gt;as improved by RFC2822&lt;/a&gt;, you&#39;ll find that you currently have no way of handling &lt;a href=&quot;http://tools.ietf.org/html/rfc1342&quot;&gt;&lt;b&gt;encoded word&lt;/b&gt;s, which is defined separately in RFC1342&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Below is the example of encoded words in message headers from RFC1342:&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;
From: &lt;b&gt;=?US-ASCII?Q?Keith_Moore?=&lt;/b&gt; &amp;lt;moore@cs.utk.edu&amp;gt;
To: &lt;b&gt;=?ISO-8859-1?Q?Keld_J=F8rn_Simonsen?=&lt;/b&gt; &amp;lt;keld@dkuug.dk&amp;gt;
CC: &lt;b&gt;=?ISO-8859-1?Q?Andr=E9_?=&lt;/b&gt; Pirard &amp;lt;PIRARD@vm1.ulg.ac.be&amp;gt;
Subject: &lt;b&gt;=?ISO-8859-1?B?SWYgeW91IGNhbiByZWFkIHRoaXMgeW8=?=
 =?ISO-8859-2?B?dSB1bmRlcnN0YW5kIHRoZSBleGFtcGxlLg==?=&lt;/b&gt;
&lt;/code&gt;&lt;/pre&gt;which should be decoded into &lt;br /&gt;
&lt;pre&gt;&lt;code&gt;
From: &lt;b&gt;Keith Moore&lt;/b&gt; &amp;lt;moore@cs.utk.edu&amp;gt;
To: &lt;b&gt;Keld Jørn Simonsen&lt;/b&gt; &amp;lt;keld@dkuug.dk&amp;gt;
CC: &lt;b&gt;André&lt;/b&gt; Pirard &amp;lt;PIRARD@vm1.ulg.ac.be&amp;gt;
Subject: &lt;b&gt;If you can read this you understand the example.&lt;/b&gt;
&lt;/code&gt;&lt;/pre&gt;But currently, &lt;a href=&quot;http://docs.plt-scheme.org/net/head.html&quot;&gt;&lt;code&gt;net/head&lt;/code&gt;&lt;/a&gt; cannot handle the encode words and they are not parsed:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(&lt;a href=&quot;http://docs.plt-scheme.org/net/head.html#(def._((lib._net/head..ss)._extract-field))&quot;&gt;extract-all-fields&lt;/a&gt; &amp;lt;the-above-string&amp;gt;)
;; =&gt; 
&#39;((&quot;From&quot; . &quot;=?US-ASCII?Q?Keith_Moore?= &lt;moore@cs.utk.edu&gt;&quot;)
 (&quot;To&quot; . &quot;=?ISO-8859-1?Q?Keld_J=F8rn_Simonsen?= &lt;keld@dkuug.dk&gt;&quot;)
 (&quot;CC&quot; . &quot;=?ISO-8859-1?Q?Andr=E9_?= Pirard &lt;PIRARD@vm1.ulg.ac.be&gt;&quot;)
 (&quot;Subject&quot;
  .
  &quot;=?ISO-8859-1?B?SWYgeW91IGNhbiByZWFkIHRoaXMgeW8=?=\r\n =?ISO-8859-2?B?dSB1bmRlcnN0YW5kIHRoZSBleGFtcGxlLg==?=&quot;))
&lt;/code&gt;&lt;/pre&gt;So we&#39;ll need to handle it ourselves.  Let&#39;s get started. &lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;The Format of an Encoded Word&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
An encoded word has the following format:&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;
&lt;b&gt;=?&lt;/b&gt;&amp;lt;charset&amp;gt;&lt;b&gt;?&lt;/b&gt;&amp;lt;&lt;b&gt;Q&lt;/b&gt; or &lt;b&gt;B&lt;/b&gt;&amp;gt;&lt;b&gt;?&lt;/b&gt;&amp;lt;encoded data&amp;gt;&lt;b&gt;?=&lt;/b&gt;
&lt;/code&gt;&lt;/pre&gt;And an encoded word should not exceed 75 bytes (including all the delimiters).  If the string being encoded cannot fit in the length, then multiple encoded words should be separated by space or linear folding whitespace (&lt;code&gt;\r\n\s*&lt;/code&gt;). Encoded words can coexist with plain text in the same header (shown above in the Cc header). &lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
There are only two encodings defined for the encoded words, &lt;code&gt;Q&lt;/code&gt; and &lt;code&gt;B&lt;/code&gt;.  They are almost identical to &lt;code&gt;quoted-printable&lt;/code&gt; and &lt;code&gt;base64&lt;/code&gt;, with some minor exceptions:&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;&lt;code&gt;Q&lt;/code&gt; use &lt;code&gt;_&lt;/code&gt; to substitute for space&lt;/li&gt;
&lt;li&gt;&lt;code&gt;B&lt;/code&gt; is not terminated by \r\n&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
We&#39;ll first generate encoded words, and then we&#39;ll parse them back.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Q Encoding&lt;/b&gt;&lt;br /&gt;
Since &lt;code&gt;Q&lt;/code&gt; more or less work the same as &lt;code&gt;quoted-printable&lt;/code&gt;, we can use &lt;code&gt;net/qp&lt;/code&gt; as the base, and wrap around &lt;a href=&quot;http://docs.plt-scheme.org/net/qp.html#(def._((lib._net/qp..ss)._qp-encode))&quot;&gt;&lt;code&gt;qp-encode&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;http://docs.plt-scheme.org/net/qp.html#(def._((lib._net/qp..ss)._qp-decode))&quot;&gt;&lt;code&gt;qp-decode&lt;/code&gt;&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
The decoding would be more straight forward since we just have to first replace &lt;code&gt;_&lt;/code&gt; with &lt;code&gt;#x20&lt;/code&gt;, which translates to space in ASCII:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define (&lt;b&gt;q-decode&lt;/b&gt; bstr)
  ;; convert all _ to #\space first...
  (qp-decode (regexp-replace* #px&quot;_&quot; bstr (list-&gt;bytes (list #x20)))))
&lt;/code&gt;&lt;/pre&gt;The encoding also works similarly, except that we need to encode more characters than &lt;code&gt;qp-encode&lt;/code&gt;, since the encoding need to avoid conflict with the encoded word delimiters (&lt;code&gt;=&lt;/code&gt;, &lt;code&gt;?&lt;/code&gt;), and it cannot include spaces, tabs, newlines, etc:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;

;; convert the integer to bytes... 
(define (char-&gt;q-bytes c)
  (bytes-&gt;list (string-&gt;bytes/utf-8 (string #\= c))))

(define BYTE:_ (char-&gt;integer #\_))
(define Q-BYTES:_ (char-&gt;q-bytes #\_))
(define BYTE:space (char-&gt;integer #\space))
(define Q-BYTES:space (list (char-&gt;integer #\_)))
(define BYTE:tab (char-&gt;integer #\tab))
(define Q-BYTES:tab (char-&gt;q-bytes #\tab))
(define BYTE:open-paren (char-&gt;integer #\())
(define Q-BYTES:open-paren (char-&gt;q-bytes #\())
(define BYTE:close-paren (char-&gt;integer #\)))
(define Q-BYTES:close-paren (char-&gt;q-bytes #\)))
(define BYTE:? (char-&gt;integer #\?))
(define Q-BYTES:? (char-&gt;q-bytes #\?))

(define (&lt;b&gt;q-encode&lt;/b&gt; bstr)
  (define (push bytes acc)
    (cond ((null? bytes) acc)
          (else
           (push (cdr bytes) (cons (car bytes) acc)))))
  (define (helper in acc)
    (let ((c (read-byte in)))
      (cond ((eof-object? c) ;; we are done...
             (list-&gt;bytes (reverse acc)))
            ((= c BYTE:_)
             (helper in (push Q-BYTES:_ acc)))
            ((= c BYTE:space)
             (helper in (push Q-BYTES:space acc)))
            ((= c BYTE:tab)
             (helper in (push Q-BYTES:tab acc)))
            ((= c BYTE:open-paren)
             (helper in (push Q-BYTES:open-paren acc)))
            ((= c BYTE:close-paren)
             (helper in (push Q-BYTES:close-paren acc)))
            ((= c BYTE:?)
             (helper in (push Q-BYTES:? acc)))
            (else (helper in (cons c acc))))))
  (helper (open-input-bytes (qp-encode bstr)) &#39;())) 
&lt;/code&gt;&lt;/pre&gt;&lt;b&gt;B Encoding&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
Similarly, &lt;code&gt;B&lt;/code&gt; works almost the same as &lt;a href=&quot;http://docs.plt-scheme.org/net/base64.html&quot;&gt;&lt;code&gt;base64&lt;/code&gt;&lt;/a&gt;, which is provided by &lt;code&gt;net/base64&lt;/code&gt;.  The decode works exactly the same so we just rename &lt;code&gt;base64-decode&lt;/code&gt; to &lt;code&gt;b-decode&lt;/code&gt;, and we just need to trim the &lt;code&gt;\r\n&lt;/code&gt; at the end of a base64 encoding:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define (&lt;b&gt;b-encode&lt;/b&gt; bstr)
  (let ((bout (base64-encode bstr)))
    (subbytes bout 0 (- (bytes-length bout) 2))))
&lt;/code&gt;&lt;/pre&gt;With the encoding mechanism now being available, it would be straight forward to generate a single encoded word:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define (encode-encoded-word charset encode str)
  (format &quot;=?~a?~a?~a?=&quot; 
          (string-downcase charset)
          (string-upcase encode)
          ((cond ((string-ci=? encode &quot;q&quot;) q-encode)
                 ((string-ci=? encode &quot;b&quot;) b-encode)) (string-&gt;bytes/utf-8 str))))
&lt;/code&gt;&lt;/pre&gt;Calling it would generate the following result:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
&gt; (encode-encoded-word &quot;utf-8&quot; &quot;q&quot; &quot;Keld Jørn Simonsen&quot;)
&quot;=?utf-8?Q?Keld_J=C3=B8rn_Simonsen?=&quot;
&gt; (encode-encoded-word &quot;utf-8&quot; &quot;b&quot; &quot;If you can read this you understand the example.&quot;)
&quot;=?utf-8?B?SWYgeW91IGNhbiByZWFkIHRoaXMgeW91IHVuZGVyc3RhbmQgdGhlIGV4YW1wbGUu?=&quot;
&lt;/code&gt;&lt;/pre&gt;The above code, however, has a bug - and that is that the charset will not match up with the actual charset of the string if the charset is not &lt;code&gt;utf-8&lt;/code&gt;. &lt;br /&gt;
&lt;br /&gt;
Generally speaking this is not a big issue, since &lt;code&gt;utf-8&lt;/code&gt; is really superior to just about every other charset and under normal situation that is the proper choice.  However, we should not have such bug in our code, so we&#39;ll fix it in the next post.  Stay tuned.</content><link rel='replies' type='application/atom+xml' href='http://weblambda.blogspot.com/feeds/8033511953037658201/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://weblambda.blogspot.com/2009/10/parsing-encoded-word-in-rfc-headers.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7570364353928786565/posts/default/8033511953037658201'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7570364353928786565/posts/default/8033511953037658201'/><link rel='alternate' type='text/html' href='http://weblambda.blogspot.com/2009/10/parsing-encoded-word-in-rfc-headers.html' title='Parsing &quot;Encoded Word&quot; in RFC Headers'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7570364353928786565.post-3550705676062190365</id><published>2009-10-05T11:50:00.000-07:00</published><updated>2009-10-05T11:50:00.308-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="date"/><category scheme="http://www.blogger.com/atom/ns#" term="plt scheme"/><category scheme="http://www.blogger.com/atom/ns#" term="timezone"/><title type='text'>Introducing BZLIB/DATE &amp; BZLIB/DATE-TZ - Date Time Manipulation Libraries</title><content type='html'>BZLIB/DATE &amp; BZLIB/DATE-TZ are now available on planet. They provide additional date manipulation capability on top of SRFI/19, including &lt;a href=&quot;http://weblambda.blogspot.com/2009/09/handling-time-zone-in-scheme-motivation.html&quot;&gt;timezone manipulation&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
They are released under LGPL.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Usage and Installation&lt;/b&gt; &lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(require (planet bzlib/date)) 
(require (planet bzlib/date-tz)) 
&lt;/code&gt;&lt;/pre&gt;&lt;code&gt;bzlib/date&lt;/code&gt; provides date manipulations.  &lt;code&gt;bzlib/date&lt;/code&gt; provides timezone manipulations. Their usages are separately discussed below. &lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;&lt;code&gt;bzlib/date&lt;/code&gt;&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
To create a date object, you can use &lt;code&gt;bulid-date&lt;/code&gt;, which provides a more natural year/month/day/hour/minute/second order of the parameters:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(build-date &amp;lt;year&amp;gt; &amp;lt;month&amp;gt; &amp;lt;day&amp;gt; &amp;lt;hour&amp;gt; &amp;lt;minute&amp;gt; &amp;lt;second&amp;gt; #:tz &amp;lt;offset&amp;gt;) 
&lt;/code&gt;&lt;/pre&gt;And it would do the right thing if you enter February 31st:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(build-date 2009 2 31) ;; =&gt; #(struct:tm:date 0 0 0 0 3 3 2009 0) 
&lt;/code&gt;&lt;/pre&gt;By default, the tz offset is 0, which equates to GMT (see below for timezone support).  Only &lt;code&gt;year&lt;/code&gt;, &lt;code&gt;month&lt;/code&gt;, and &lt;code&gt;day&lt;/code&gt; are required. &lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Date Comparisons&lt;/b&gt;&lt;br /&gt;
The following function compares two dates to determine their orders:&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;&lt;code&gt;(date&gt;? &amp;lt;d1&amp;gt; &amp;lt;g2&amp;gt;)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;(date&amp;lt;? &amp;lt;d1&amp;gt; &amp;lt;g2&amp;gt;)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;(date&gt;=? &amp;lt;d1&amp;gt; &amp;lt;g2&amp;gt;)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;(date&amp;lt;=? &amp;lt;d1&amp;gt; &amp;lt;g2&amp;gt;)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;(day=? &amp;lt;d1&amp;gt; &amp;lt;g2&amp;gt;)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;(date!=? &amp;lt;d1&amp;gt; &amp;lt;g2&amp;gt;)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;(date===? &amp;lt;d1&amp;gt; &amp;lt;g2&amp;gt;)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;code&gt;day=?&lt;/code&gt; only compares the year/month/day values, and &lt;code&gt;date===?&lt;/code&gt; means they are the same date with the same tz offset. &lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Conversions&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
You can convert between date and seconds with &lt;code&gt;(date-&gt;seconds &amp;lt;date&amp;gt;)&lt;/code&gt; and &lt;code&gt;(seconds-&gt;date &amp;lt;seconds&amp;gt;)&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
You can add to a date with &lt;code&gt;(date+ &amp;lt;date&amp;gt; &amp;lt;number-of-days&amp;gt;)&lt;/code&gt;.  The number of day can be a non-integer. &lt;br /&gt;
&lt;br /&gt;
You can find out the gaps between two dates with &lt;code&gt;(date- &amp;lt;date1&amp;gt; &amp;lt;date2&amp;gt;)&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
You can create an alarm event with date via &lt;code&gt;(date-&gt;alarm &amp;lt;date&amp;gt;)&lt;/code&gt; or &lt;code&gt;(date-&gt;future-alarm &amp;lt;date&amp;gt;)&lt;/code&gt;.  The difference between the two is that &lt;code&gt;date-&gt;future-alarm&lt;/code&gt; will return false if the date is in the past.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Dealing with Weekdays&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
To find out the weekday of a particular date, you can use &lt;code&gt;(week-day &amp;lt;date&amp;gt;)&lt;/code&gt;. &lt;br /&gt;
&lt;br /&gt;
To find out the date of the nth-weekday (e.g., first sunday, 3rd wednesday, last Friday) of a particular month, use &lt;code&gt;nth-week-day&lt;/code&gt;:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(nth-week-day &amp;lt;year&amp;gt; &amp;lt;month&amp;gt; &amp;lt;week-day&amp;gt; &amp;lt;nth&amp;gt; &amp;lt;hour&amp;gt; &amp;lt;minute&amp;gt; &amp;lt;second&amp;gt; #:tz &amp;lt;offset&amp;gt;) 
&lt;/code&gt;&lt;/pre&gt;For the &lt;code&gt;week-day&lt;/code&gt; argument, use 0 for Sunday, and 6 for Saturday.  For the &lt;code&gt;nth&lt;/code&gt; argument, use 1, 2, 3, 4, 5, or &lt;code&gt;&#39;last&lt;/code&gt;.  &lt;code&gt;hour&lt;/code&gt;, &lt;code&gt;minute&lt;/code&gt;, &lt;code&gt;second&lt;/code&gt;, and &lt;code&gt;offset&lt;/code&gt; are optional (same as build-date, and the other functions below that have them).  &lt;br /&gt;
&lt;br /&gt;
To find out the date of a particular weekday relative to another date, use one of the following:&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;&lt;code&gt;week-day&gt;=mday&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;week-day&amp;lt;=mday&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;week-day&gt;mday&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;week-day&amp;lt;mday&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;They all share the same arguments, which are &lt;code&gt;year&lt;/code&gt;, &lt;code&gt;month&lt;code&gt;, &lt;code&gt;week-day&lt;/code&gt;, &lt;code&gt;month-day&lt;/code&gt;, &lt;code&gt;hour&lt;/code&gt;, &lt;code&gt;minute&lt;/code&gt;, &lt;code&gt;second&lt;/code&gt;, and &lt;code&gt;offset&lt;/code&gt;.&lt;br /&gt;
The usage is something like:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
;; the sunday after May 15th, 2009
(week-day&gt;mday 2009 5 0 15) ;; 5/17/2009 
;; the friday before September 22nd, 2008 
(week-day&amp;lt;mday 2009 9 5 22) ;; 9/19/2009 
&lt;/code&gt;&lt;/pre&gt;The &lt;code&gt;hour&lt;/code&gt;, &lt;code&gt;minute&lt;/code&gt;, &lt;code&gt;second&lt;/code&gt;, and &lt;code&gt;offset&lt;/code&gt; parameters are there for you to customize the return values:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
;; the sunday after May 15th, 2009
(week-day&gt;mday 2009 5 0 15 15 0 0 #:tz -28800) ;; 5/17/2009 15:00:00-28800
;; the friday before September 22nd, 2008 
(week-day&amp;lt;mday 2009 9 5 22 8 30 25 #:tz 14400) ;; 9/19/2009 08:00:00+14400
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
&lt;b&gt;bzlib/date-tz&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
By default, you need to parameterize the &lt;code&gt;current-tz&lt;/code&gt; parameter, which defaults to &lt;code&gt;America/Los_Angeles&lt;/code&gt;.  The timezone names are the available names from the olson database.&lt;br /&gt;
&lt;br /&gt;
To determine the offset of any date for a particular timezone, use &lt;code&gt;tz-offset&lt;/code&gt;:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(parameterize ((current-tz &quot;America/New_York&quot;)) 
  (tz-offset (build-date 2008 3 9))) ;; =&gt; -18800 
(parameterize ((current-tz &quot;America/New_York&quot;)) 
  (tz-offset (build-date 2008 3 10))) ;; =&gt; -14400 
&lt;/code&gt;&lt;/pre&gt;If you want to separate between the standard offset and the daylight saving offset, you can use &lt;code&gt;tz-standard-offset&lt;/code&gt; or &lt;code&gt;tz-daylight-saving-offset&lt;/code&gt;:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(let ((d1 (build-date 2008 3 9))
        (d2 (build-date 2008 3 10)))
    (parameterize ((current-tz &quot;America/New_York&quot;))
      (values (tz-standard-offset d1)
              (tz-daylight-saving-offset d1)
              (tz-daylight-saving-offset d2))))
;; =&gt; -18800 (std)
;; =&gt; 0 (dst on 3/9/2008)
;; =&gt; 3600 (dst on 3/10/2008) 
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
&lt;b&gt;Conversion&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
To reset a date&#39;s tz offset, you can use the helper function &lt;code&gt;date-&gt;tz&lt;/code&gt;, which will reset the offset for you:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(let ((date (build-date 2008 3 10 #:tz -18800))) 
  (parameterize ((current-tz &quot;America/New_York&quot;)) 
    (date-&gt;tz date))) 
;; =&gt; #(struct:tm:date 0 0 0 0 10 3 2008 -14400)
&lt;/code&gt;&lt;/pre&gt;This function is meant for you to fix the offsets for dates that belong to a particular timezone but did not correctly account for the offset - it does not switch the timezone for you. &lt;br /&gt;
&lt;br /&gt;
Couple other functions makes it even easier to work with timezone.  &lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(build-date/tz &amp;lt;year&amp;gt; &amp;lt;month&amp;gt; ...)
(date+/tz &amp;lt;date&amp;gt; &amp;lt;number-of-days&amp;gt;)
&lt;/code&gt;&lt;/pre&gt;They basically creates the date object and calls &lt;code&gt;date-&gt;tz&lt;/code&gt; so the offset is properly adjusted based on the timezone. &lt;br /&gt;
&lt;br /&gt;
Besides using &lt;code&gt;current-tz&lt;/code&gt;, you can also pass it explicitly to &lt;code&gt;tz-offset&lt;/code&gt;, &lt;code&gt;tz-standard-offset&lt;/code&gt;, &lt;code&gt;tz-daylight-saving-offset&lt;/code&gt;, &lt;code&gt;date-&gt;tz&lt;/code&gt;, &lt;code&gt;build-date/tz&lt;/code&gt;, and &lt;code&gt;date+/tz&lt;/code&gt;.  You pass it in in the following forms:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(tz-offset &amp;lt;date&amp;gt; &lt;b&gt;&quot;America/Los_Angeles&quot;&lt;/b&gt;)
(tz-daylight-saving-offset &amp;lt;date&amp;gt; &lt;b&gt;&quot;Asia/Kolkata&quot;&lt;/b&gt;)
(tz-standard-offset &amp;lt;date&amp;gt; &lt;b&gt;&quot;Europe/London&quot;&lt;/b&gt;)
(date-&gt;tz &amp;lt;date&amp;gt; &lt;b&gt;&quot;Europe/London&quot;&lt;/b&gt;)
(build-date/tz &amp;lt;year&amp;gt; &amp;lt;month&amp;gt; &amp;lt;day&amp;gt; &lt;b&gt;#:tz &quot;America/New_York&quot;&lt;/b&gt;)
(date+/tz &amp;lt;date&amp;gt; &amp;lt;number-of-days&amp;gt; &lt;b&gt;&quot;America/Los_Angeles&quot;&lt;/b&gt;)
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
&lt;b&gt;Convert from One Timezone to Another&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
To covert the timezone of a date so you get the same date in a different timezone, use &lt;code&gt;tz-convert&lt;/code&gt;:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(tz-convert &amp;lt;date&amp;gt; &amp;gt;from-timezone&amp;gt; &amp;lt;to-timezone&amp;gt;)
&lt;/code&gt;&lt;/pre&gt;All parameters are required.  &lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(tz-convert (build-date 2008 3 10 15) &quot;America/New_York&quot; &quot;America/Los_Angeles&quot;)
;; =&gt; #(struct:tm:date 0 0 0 12 10 3 2008 -25200) ;; 2008/3/10 12:00:00-25200
(tz-convert (build-date 2008 3 10 15) &quot;America/New_York&quot; &quot;GMT&quot;)
;; ==&gt; #(struct:tm:date 0 0 0 19 10 3 2008 0) ;; 2008/10/10 19:00:00+00:00
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
That&#39;s it for now - enjoy.</content><link rel='replies' type='application/atom+xml' href='http://weblambda.blogspot.com/feeds/3550705676062190365/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://weblambda.blogspot.com/2009/10/introducing-bzlibdate-bzlibdate-tz-date.html#comment-form' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7570364353928786565/posts/default/3550705676062190365'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7570364353928786565/posts/default/3550705676062190365'/><link rel='alternate' type='text/html' href='http://weblambda.blogspot.com/2009/10/introducing-bzlibdate-bzlibdate-tz-date.html' title='Introducing BZLIB/DATE &amp; BZLIB/DATE-TZ - Date Time Manipulation Libraries'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7570364353928786565.post-8814324520334189958</id><published>2009-10-01T07:42:00.000-07:00</published><updated>2009-10-01T07:42:00.229-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="date"/><category scheme="http://www.blogger.com/atom/ns#" term="plt scheme"/><category scheme="http://www.blogger.com/atom/ns#" term="timezone"/><title type='text'>Handling Time Zone in Scheme (5): Pre-Convert the Rules</title><content type='html'>This is part five of the timezone series - you can find the previous posts on this subject to get up to speed on the details:&lt;br /&gt;
&lt;ol&gt;&lt;li&gt;&lt;a href=&quot;http://weblambda.blogspot.com/2009/09/handling-time-zone-in-scheme-motivation.html&quot;&gt;motivation and overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://weblambda.blogspot.com/2009/09/handling-time-zone-in-scheme-parsing.html&quot;&gt;parsing the zoneinfo database&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://weblambda.blogspot.com/2009/09/handling-time-zone-in-scheme-3.html&quot;&gt;compute the offsets&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://weblambda.blogspot.com/2009/09/handling-time-zone-in-scheme-rule.html&quot;&gt;convert the rules into dates&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;&lt;br /&gt;
We previously have finished the first draft of &lt;code&gt;tz-offset&lt;/code&gt;, and it works correctly in majority of the situations.  But unfortunately, there is one issue with it:&lt;br /&gt;
&lt;blockquote&gt;The code will not work correctly when there are gaps between the years where the rules are applicable. &lt;/blockquote&gt;Fortunately, under regular use of the code, we will not encounter this issue.  But the issue definitely exists.  Below is the US rule: &lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
  (&quot;US&quot;
   (1918 1919 - 3 (last 0) (2 0 0 w) 3600 &quot;D&quot;)
   (1918 1919 - 10 (last 0) (2 0 0 w) 0 &quot;S&quot;)
   (1942 1942 - 2 9 (2 0 0 w) 3600 &quot;W&quot;)
   (1945 1945 - 8 14 (23 0 0 u) 3600 &quot;P&quot;)
   (1945 1945 - 9 30 (2 0 0 w) 0 &quot;S&quot;)
   (1967 2006 - 10 (last 0) (2 0 0 w) 0 &quot;S&quot;)
   (1967 1973 - 4 (last 0) (2 0 0 w) 3600 &quot;D&quot;)
   (1974 1974 - 1 6 (2 0 0 w) 3600 &quot;D&quot;)
   (1975 1975 - 2 23 (2 0 0 w) 3600 &quot;D&quot;)
   (1976 1986 - 4 (last 0) (2 0 0 w) 3600 &quot;D&quot;)
   (1987 2006 - 4 (match 0 &gt;= 1) (2 0 0 w) 3600 &quot;D&quot;)
   (2007 +inf.0 - 3 (match 0 &gt;= 8) (2 0 0 w) 3600 &quot;D&quot;)
   (2007 +inf.0 - 11 (match 0 &gt;= 1) (2 0 0 w) 0 &quot;S&quot;))
&lt;/code&gt;&lt;/pre&gt;You can see that there is a gap between 1945 and 1967 for applicable rules.  You probably are not going to calculate the offsets for 1948 most of the time, but it would be nice if we do not have to face the potential issues. &lt;br /&gt;
&lt;br /&gt;
What we want is to have the last rule of 1945 continue to be applicable until 1967 in this case.  And that means we need to be able to skip over multiple years, which our current design does not account for.  The nice thing is that the situation is not as dire as it sounds, since most of the timezones that I inspected visually do not make use of the &quot;US&quot; rule during this gap.  But it would be nice to know such potential bug will not exist. &lt;br /&gt;
&lt;br /&gt;
As we have found out in the previous posts, inferring the &quot;previous&quot; rules with the data format is difficult, and it&#39;s easier to compute the applicable rules for a given year, the easiest solution is to &lt;b&gt;pre-compute the dates for every year that we need to worry about&lt;/b&gt;.  In this case, any of the gaps will automatically be filled with the previously applicable rules, with the applicable years. &lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;The Applicable Years&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
It should be obvious that the timezone concept has definite bound toward the past, as &lt;a href=&quot;http://en.wikipedia.org/wiki/Time_zone#History&quot;&gt;GMT was not established until 1675, and US does not adopt the timezone until 1918&lt;/a&gt;.  The minimum year from the zoneinfo database is 1916:&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define (zone-year (aggregate min))
  (define (until-helper zone) 
    (let ((until (zone-until zone))) 
      (if (date? until) (date-year until) #f)))
  (let ((rules (filter identity 
                       (flatten 
                        (hash-map zones
                                  (lambda (key zone)
                                    (map zone-rule zone))))))
        (untils (hash-map zones (lambda (key zone)
                                  (map until-helper zone)))))
    (apply aggregate 
           (filter (lambda (date)
                     (and (number? date) 
                          (not (equal? date +inf.0))
                          (not (equal? date -inf.0)))) 
                   (flatten (append untils 
                                    (map rule-from rules)
                                    (map rule-to rules)))))))

(zone-year min) ;; ==&gt; 1916. 
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
And while there is no clear upper bound for the time zones, we can follow some conventions to help establish such upper bounds to avoid infinite time zone generation, which would have likely to be incorrect anyways, since timezone and daylight saving times can easily be changed in the future at the whim of politicians and governments.  &lt;a href=&quot;http://en.wikipedia.org/wiki/Year_2038_problem&quot;&gt;A good upper bound is 2038, since that&#39;s the Y2K problem for Unix&lt;/a&gt;, and it will give us plenty of time to update the library.  This number coincidentally is also the largest number in the zoneinfo database:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(zone-year max) ;; ==&gt; 2038 
&lt;/code&gt;&lt;/pre&gt;The two years now forms our range for calculation all applicable rules:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define ZIC-MIN (zone-year min)) 
(define ZIC-MAX (zone-year max)) 

(define (all-applicable-rule/years rules)
  (sort (apply append 
               (for/list ((year (in-range ZIC-MIN ZIC-MAX))) 
                 (applicable-rules-by-year rules year)))
        rule/year&lt;?))
&lt;/code&gt;&lt;/pre&gt;With the above we&#39;ll be able to convert zones with rules.  What about the zones without rules?  We&#39;ll just have to fill out the list of the rules ourselves:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define (zone-&gt;rule/years zone)
  (if (zone-rule zone)
      (all-applicable-rule/years (zone-rule zone)) 
      (for/list ((year (in-range ZIC-MIN ZIC-MAX))) 
        (cons (make-rule year year 1 1 &#39;- &#39;(0 0 0 w) 0 &quot;S&quot;) year))))
&lt;/code&gt;&lt;/pre&gt;Now each list of the zones are basically mapped to a full list of the rule/years pairs. We want the final outcome to be a list of structure that contains the boundary (a date), the standard offset, and the daylight saving offset:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define-struct normalized (bound std dst)) 
&lt;/code&gt;&lt;/pre&gt;And our rule/year pair can be converted to &lt;code&gt;normalized&lt;/code&gt; with the following:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define (rule/year-&gt;normalized rule/year std-offset offset)
  (define (helper rule year)
    (let ((date (on/year-&gt;date rule year)))
      (match (rule-time rule) 
        ((list hour minute second type) ;; wall clock 
         (make-normalized (build-date (date-year date)
                                      (date-month date)
                                      (date-day date)
                                      hour 
                                      minute
                                      second 
                                      #:tz (case type 
                                             ((g u z) 0 0)
                                             ((s) std-offset)
                                             (else ;; wall-clock requires the previous rule...  
                                              (+ std-offset offset))))
                          std-offset
                          (rule-offset rule))))))
  (helper (car rule/year) (cdr rule/year)))
&lt;/code&gt;&lt;/pre&gt;Which looks quite similar to &lt;code&gt;rule/year-&gt;date/offset&lt;/code&gt;, which we might retrofit with this newer function.&lt;br /&gt;
&lt;br /&gt;
The next challenge will then to be convert from zone into a list of &lt;code&gt;normalized&lt;/code&gt;,  following the offset dependency between the rules, as well as filtering out the upper and the lower bounds (the lower bounds comes from the previous zone). &lt;br /&gt;
&lt;br /&gt;
Without filtering it looks like the following:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define (zone-&gt;normalized zone (prev &#39;()))
  (define (helper rule/years acc) 
    (cond ((null? rule/years) ;; we are done... 
           acc)
          (else
           (let ((normalized (rule/year-&gt;normalized (car rule/years)
                                                    (zone-offset zone)
                                                    (if (null? acc) 
                                                        0
                                                        (normalized-dst (car acc))))))
             (helper (cdr rule/years) (cons normalized acc))))))
  (helper (zone-&gt;rule/years zone) prev))
&lt;/code&gt;&lt;/pre&gt;To filter for upper bound, we&#39;ll have to test against the &lt;code&gt;UNTIL&lt;/code&gt; field.  If it&#39;s &lt;code&gt;#f&lt;/code&gt;, it means no upper bound. Otherwise we should first test for the year value to quickly filter away the ones that exceed the year, and if it&#39;s not the same year, we can keep the rule.  For the boundary in the same year as &lt;code&gt;UNTIL&lt;/code&gt; we&#39;ll convert &lt;code&gt;UNTIL&lt;/code&gt; into a date object for comparison by using the boundary&#39;s wall time:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define (zone-&gt;normalized zone (prev &#39;()))
  &lt;b&gt;(define (until-helper offset)
    (define (helper year month day hour minute second type)
      (build-date year month day hour minute second
                                    #:tz (case type 
                                           ((u g z) 0)
                                           ((s) (zone-offset zone))
                                           ((w) (+ offset (zone-offset zone))))))
    (apply helper (zone-until zone)))&lt;/b&gt;
  (define (helper rule/years acc) 
    (cond ((null? rule/years) ;; we are done... 
           acc)
          &lt;b&gt;((and (zone-until zone) ;; if until &amp; date &gt; until&#39;s year. 
                (&gt; (cdar rule/years) (car (zone-until zone))))
           (helper (cdr rule/years) acc))&lt;/b&gt;
          (else
           (let ((normalized (rule/year-&gt;normalized (car rule/years)
                                                    (zone-offset zone)
                                                    (if (null? acc) 
                                                        0
                                                        (normalized-dst (car acc))))))
             &lt;b&gt;(cond ((not (zone-until zone)) ;; infinite upper bound 
                    (helper (cdr rule/years) (cons normalized acc)))
                   ((&lt; (cdar rule/years) (car (zone-until zone))) ;; less than until&#39;s year 
                    (helper (cdr rule/years) (cons normalized acc)))
                   (else ;; we need to ensure the bound is lower than the year
                    (let ((until (until-helper (if (null? acc) 
                                                   0
                                                   (normalized-dst (car acc))))))
                      (if (&lt; until (normalized-bound normalized))
                          (helper (cdr rule/years) (cons normalized acc))
                          (helper (cdr rule/years) acc)))))&lt;/b&gt;))))
  (helper (zone-&gt;rule/years zone) prev))
&lt;/code&gt;&lt;/pre&gt;To filter out the lower bound, we will test to see if the new normalized bound is greater than the previous batch&#39;s bound:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define (zone-&gt;normalized zone (prev &#39;()))
  ... 
  (define (helper rule/years acc) 
    (cond ...
          (else
           (let ((normalized (rule/year-&gt;normalized (car rule/years)
                                                    (zone-offset zone)
                                                    (if (null? acc) 
                                                        0
                                                        (normalized-dst (car acc))))))
             (cond &lt;b&gt;((date&amp;lt;? (normalized-bound normalized) 
                            (if (null? prev) 
                                (build-date 1 1 1) 
                                (normalized-bound (car prev)))) 
                    (helper (cdr rule/years) acc))&lt;/b&gt;
                   ...)))))
  (helper (zone-&gt;rule/years zone) prev)) 
&lt;/code&gt;&lt;/pre&gt;With the above, we can just fold over the list of zones to get to the list of normalized:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define (zones-&gt;normalized zones)
  (foldl zone-&gt;normalized 
         &#39;()
         zones))
&lt;/code&gt;&lt;/pre&gt;From this point on, the rest is to convert the serialization to serialize out the list, and then to modify &lt;code&gt;tz-offset&lt;/code&gt; to take use the new list.  Stay tuned.</content><link rel='replies' type='application/atom+xml' href='http://weblambda.blogspot.com/feeds/8814324520334189958/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://weblambda.blogspot.com/2009/10/handling-time-zone-in-scheme-5-pre.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7570364353928786565/posts/default/8814324520334189958'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7570364353928786565/posts/default/8814324520334189958'/><link rel='alternate' type='text/html' href='http://weblambda.blogspot.com/2009/10/handling-time-zone-in-scheme-5-pre.html' title='Handling Time Zone in Scheme (5): Pre-Convert the Rules'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7570364353928786565.post-6093465900786489233</id><published>2009-09-30T11:56:00.000-07:00</published><updated>2009-09-30T11:56:00.147-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="dbi"/><category scheme="http://www.blogger.com/atom/ns#" term="mysql"/><category scheme="http://www.blogger.com/atom/ns#" term="plt scheme"/><category scheme="http://www.blogger.com/atom/ns#" term="postgresql"/><category scheme="http://www.blogger.com/atom/ns#" term="sql"/><category scheme="http://www.blogger.com/atom/ns#" term="sqlite"/><title type='text'>DBI and SQL Escape</title><content type='html'>&lt;a href=&quot;http://www.defgroovy.com/&quot;&gt;Scott Hickey&lt;/a&gt; has discovered a bug in DBI: &lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
;; assume you have a table1 with an id and a date field. 
(exec h &quot;insert into table1 values (?id , ?date)&quot; `((id . 1) (date . ,(srfi19:current-date))))
;; =&gt; regexp-replace*: expects type &lt;string or byte string&gt; as 2nd argument, given:
;;    #(struct:tm:date 9150000 8 19 0 30 9 2009 -18000); other arguments were: #px&quot;\\&#39;&quot; &quot;&#39;&#39;&quot;
&lt;/code&gt;&lt;/pre&gt;This issue is now fixed and the newer version of the DBI and are now available through planet:&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;http://planet.plt-scheme.org/display.ss?package=dbi.plt&amp;owner=bzlib&quot;&gt;bzlib/dbi:1:2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://planet.plt-scheme.org/display.ss?package=dbd-jazmysql.plt&amp;owner=bzlib&quot;&gt;bzlib/dbd-jazmysql:1:1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://planet.plt-scheme.org/display.ss?package=dbd-jsqlite.plt&amp;owner=bzlib&quot;&gt;bzlib/dbd-jsqlite:1:2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://planet.plt-scheme.org/display.ss?package=dbd-spgsql.plt&amp;owner=bzlib&quot;&gt;bzlib/dbd-spgsql:1:1&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;This post documents the issue and the resolution of the bug. &lt;br /&gt;
&lt;br /&gt;
This issue is caused by the default SQL escape code that does not know how to handle srfi date objects.  The default SQL escape code is quite primitive for the reasons below. &lt;br /&gt;
&lt;br /&gt;
To work around the problem - you can use prepared statements:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(prepare h &#39;insert-table1 &quot;insert into table1 values (?id , ?date)&quot;)
(exec h &#39;insert-table1 `((id . 1) (date . ,(srfi19:current-date))))
&lt;/code&gt;&lt;/pre&gt;The default preparation code exists as a prepared statement proxy for those database drivers that have no prepared statement capabilities.  This is one of the selling points of DBI over other API - the query always allow parameterizations.  But that means DBI cannot delegate the task of SQL escaping back to the user.&lt;br /&gt;
&lt;br /&gt;
Because my usage of database has always surround prepared statements, I did not write an extensive SQL escaping library (and hence the bug). Plus, there were technical reasons that prepared statements are superior:&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;SQL escapes does not work uniformly across all databases, especially for types such as blobs and date objects, which each database have their own syntax (and some basically discourage using SQL escapes for blobs) &lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;SQL escapes are prone to SQL injections if done poorly.  One of my previous gigs was to weed out SQL injection bugs in client code base and while the concept is simple many implementations still got it wrong &lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;In general prepared statements will have better performances (but this unfortunately is not always true if the cached query plan results in a miss by the server) for multiple uses&lt;/li&gt;
&lt;/ul&gt;Prepared statements (and stored procedures) are superior to SQL escapes in just about all aspects, including performance and security.  There are only three downsides that I am aware of for prepared statements:&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;it might cause the database to hold onto the referred objects so it cannot be dropped - this mainly impacts development environment, since that actually helps your production environment from having tragic accident of dropping tables, views, etc.&lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;it might not work well for code that creates dynamic SQL statements that refers to tables with unique prefixes (wordpress and a bunch of PHP code falls into this design style), since there might be thousands of such unique prefixes in a given database.  In general, such design really should be discouraged, since databases are designed more for few large tables instead of many small tables&lt;/li&gt;
&lt;li&gt;it&#39;s not all that useful and can potentially be slower for one-call statements, but most of the time this is a non-issue&lt;/li&gt;
&lt;/ul&gt;Anyhow - the reason I am highlighting the merit of prepared statements over SQL escapes is that I believe going toward prepared statements is the way to go, especially for databases that already have them.  So I decide to make the database drivers for &lt;code&gt;dbd-spgsql&lt;/code&gt;, &lt;code&gt;dbd-jsqlite&lt;/code&gt;, and &lt;code&gt;dbd-jazmysql&lt;/code&gt; to implicitly create prepared statements if you do not want to explicitly name the query via the &lt;code&gt;prepare&lt;/code&gt; call. &lt;br /&gt;
&lt;br /&gt;
So - you can just write: &lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(exec h &quot;insert into table1 values (?id , ?date)&quot; `((id . 1) (date . ,(srfi19:current-date))))
&lt;/code&gt;&lt;/pre&gt;And it will behave as if you do the following:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
;; note - prepare only takes symbol as a key - so you cannot do this manually yourself
(prepare h &quot;insert into table1 values (?id , ?date)&quot; &quot;insert into table1 values (?id , ?date)&quot;)
(exec h &quot;insert into table1 values (?id , ?date)&quot; `((id . 1) (date . ,(srfi19:current-date))))
&lt;/code&gt;&lt;/pre&gt;So &lt;code&gt;dbd-spgsql&lt;/code&gt;, &lt;code&gt;dbd-jazmysql&lt;/code&gt;, &lt;code&gt;dbd-jsqlite&lt;/code&gt; will no longer use SQL escape for parameterization going forward. They have been made available via planet.&lt;br /&gt;
&lt;br /&gt;
Thank you Scott for discovering and reporting this issue.</content><link rel='replies' type='application/atom+xml' href='http://weblambda.blogspot.com/feeds/6093465900786489233/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://weblambda.blogspot.com/2009/09/dbi-and-sql-escape.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7570364353928786565/posts/default/6093465900786489233'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7570364353928786565/posts/default/6093465900786489233'/><link rel='alternate' type='text/html' href='http://weblambda.blogspot.com/2009/09/dbi-and-sql-escape.html' title='DBI and SQL Escape'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7570364353928786565.post-3028319067280708541</id><published>2009-09-30T09:34:00.000-07:00</published><updated>2009-09-30T09:34:00.103-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="date"/><category scheme="http://www.blogger.com/atom/ns#" term="plt scheme"/><category scheme="http://www.blogger.com/atom/ns#" term="timezone"/><title type='text'>Handling Time Zone in Scheme (4): Rule Conversion</title><content type='html'>Previously we have discussed timezone handling in a series of posts:&lt;br /&gt;
&lt;ol&gt;&lt;li&gt;&lt;a href=&quot;http://weblambda.blogspot.com/2009/09/handling-time-zone-in-scheme-motivation.html&quot;&gt;motivation and overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://weblambda.blogspot.com/2009/09/handling-time-zone-in-scheme-parsing.html&quot;&gt;parsing zoneinfo database&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;c&lt;a href=&quot;http://weblambda.blogspot.com/2009/09/handling-time-zone-in-scheme-3.html&quot;&gt;alculating offsets&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;To continue from the third post where we are in the midst of calculating daylight saving offsets, we have figured out the applicable rules, and we now need to convert them into date structs so we can determine the exact boundary.  &lt;br /&gt;
&lt;br /&gt;
Going back to our two rule examples for America/Los_Angeles:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
  (2007 +inf.0 - 3 (match 0 &gt;= 8) (2 0 0 w) 3600 &quot;D&quot;)
  (2007 +inf.0 - 11 (match 0 &gt;= 1) (2 0 0 w) 0 &quot;S&quot;)
&lt;/code&gt;&lt;/pre&gt;We want to convert them into the applicable values (for both 2009 and the previous year - 2008): &lt;br /&gt;
&lt;pre&gt;&lt;code&gt;
2009/3/8 02:00:00-08:00 
2009/11/1 02:00:00-07:00
2008/3/9 02:00:00-08:00 
2008/11/2 02:00:00-07:00 
&lt;/code&gt;&lt;/pre&gt;In order to do so, we&#39;ll first have to be able to convert the &lt;code&gt;ON&lt;/code&gt; (day of the month) into the correct date value, and then we&#39;ll have to convert the &lt;code&gt;AT&lt;/code&gt; (time of the date) into the correct time value.  Let&#39;s get started. &lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Day of the Month&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
The simplest &lt;code&gt;ON&lt;/code&gt; format is a day number (ranging from 1-31), and for that we do not have to do too much.  But there are also two other formats that are based on weekdays:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
&#39;(last 0) ;; =&gt; last sunday (sunday = 0, monday = 1 ..., saturday = 6) 
&#39;(match 0 &gt;= 5) ;; =&gt; sunday on or after 5th 
&#39;(match 2 &lt;= 10) ;; =&gt; tuesday on or before 10th 
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
That means we need to be able to convert them to the appropriate day of the month based on the year and the month. &lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Doomsday Algorithm and Weekday Calculation&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
To be able to calculate the weekday-based date values, we first need to be able to calculate the weekday of a particular date.  For that we can make use of the&lt;a href=&quot;http://en.wikipedia.org/wiki/Doomsday_rule&quot;&gt; doomsday algorithm, which are based on the concept that there is a doomsday every month&lt;/a&gt;, and they are easy to remember based on a moniker (4/4, 6/6, 8/8, 10/10, 12/12, ...).  The linked explanation makes it sounds more complicated than it actually is - below is the oneline doomsday algorithm in scheme:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define (doomsday y)
  (modulo (+ 2 (floor (+ y (/ y 4) (- (/ y 100)) (/ y 400)))) 7))
&lt;/code&gt;&lt;/pre&gt;Then with doomsday we can calculate the weekday of a date:&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define (leap-year? year)
  ;; every 4 year is a leap year
  (or (and (= (modulo year 4) 0)
           ;; unless it&#39;s divisble by 100 
           (not (= (modulo year 100) 0)))
       ;; but if it&#39;s divisible by 400 then we&#39;ll be fine.
      (= (modulo year 400) 0)))

(define (week-day d)
  (modulo (+ (doomsday (date-year d)) (year-day d)
             (if (leap-year? (date-year d)) -3 -2)) 7))
&lt;/code&gt;&lt;/pre&gt;Then to figure out the weekday on or greater than a particular date, we just need to do the following:&lt;br /&gt;
&lt;ol&gt;&lt;li&gt;figure out the weekday of the date&lt;/li&gt;
&lt;li&gt;figure out the difference between the weekday of the date and the weekday of your choice&lt;/li&gt;
&lt;li&gt;add the differences to the date&lt;/li&gt;
&lt;/ol&gt;The following figures out the difference between 2 weekdays&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define (week-day-diff to from)
  (modulo (- to from) 7))
&lt;/code&gt;&lt;/pre&gt;And assuming we have a &lt;code&gt;date+&lt;/code&gt; that takes in a date and a number as the days to add, then the following will determine the date on or after a particular date by weekday:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define (week-day&gt;=? year month wday mday) 
  (define (helper date)
    (date+ date (week-day-diff wday (week-day date))))
  (helper (build-date year month mday)))
&lt;/code&gt;&lt;/pre&gt;The other combinations (&lt;code&gt;week-day&gt;?&lt;/code&gt;, &lt;code&gt;week-day&amp;lt;=?&lt;/code&gt;, and &lt;code&gt;week-day&amp;lt;?&lt;/code&gt;) are left as exercises.&lt;br /&gt;
&lt;br /&gt;
To determine the last (or nth) weekday of the month, we can employ a similar algorithm:&lt;br /&gt;
&lt;ol&gt;&lt;li&gt;figure out the weekday of the first of the month&lt;/li&gt;
&lt;li&gt;figure out the difference between that weekday and the weekday of your choice&lt;/li&gt;
&lt;li&gt;add the number of weeks on top of the date to get the desired date&lt;/li&gt;
&lt;li&gt;if the date exceeds the month, subtract a week to get to the last weekday within the month boundary&lt;/li&gt;
&lt;/ol&gt;The following accomplish the above:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define (nth-week-day year month wday nth)
  ;; the way to do so is to figure out the weekday for the first of the month, and then work toward 
  ;; the nth wday 
  (define (date-helper date)
    (if (not (= (date-month date) month))
        (date+ date -7)
        date))
  (define (helper date)
    (date-helper 
     (date+ date 
            (+ (week-day-diff wday (week-day date)) 
               (* (sub1 (case nth
                          ((first) 1)
                          ((last) 5)
                          (else nth))) 
                  7)))))
  (helper (build-date year month 1)))
&lt;/code&gt;&lt;/pre&gt;With the above, we can now finally convert the &lt;code&gt;ON&lt;/code&gt; field into the correct date value:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define (on/year-&gt;date rule year) 
  (match (rule-date rule) 
    ((? integer? date)
     (build-date year (rule-month rule) date))
    ((list &#39;last (? number? wday))
     (nth-weekday year (rule-month rule) wday &#39;last)) 
    ((list &#39;match (? number? wday) (? symbol? test) (? number? day))
     ((case test 
        ((&gt;=) week-day&gt;=?)
        ((&gt;) week-day&gt;?)
        ((&amp;lt;=) week-day&amp;lt;=?)
        ((&amp;lt;) week-day&amp;lt;?)) year (rule-month rule) wday day))))
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
&lt;b&gt;Determining &quot;Wall Clock&quot; Time&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
We are almost able to convert an applicable rule into a date object, but we first still have to fully convert the &lt;code&gt;AT&lt;/code&gt; field into the corresponding time of the day value.&lt;br /&gt;
&lt;br /&gt;
Unfortunately, &lt;code&gt;AT&lt;/code&gt; field holds more than just a representation of hour:minute:seconds.  It also holds the type of the clock, which can be one of the following:&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;universal time - no offsets&lt;/li&gt;
&lt;li&gt;standard time - time-zone offsets only; no daylight saving offsets&lt;/li&gt;
&lt;li&gt;&quot;wall clock&quot; time - time-zone offsets + daylight saving offsets&lt;/li&gt;
&lt;/ul&gt;The first two are straight forward - universal time has an offset of 0, and the standard time has the default offsets that we should pass in from the zone.  But the &quot;wall clock&quot; time is more complicated.  It basically means we need to know what the previous rule is at the moment of the rule coming into effect, since at the moment of daylight saving transition, the previous rules would have been effecting the wall clock. &lt;br /&gt;
&lt;br /&gt;
Yes - it means that &lt;b&gt;in order for us to arrive at the correct wall-clock time, we need to figure out the previous rule&#39;s offsets&lt;/b&gt;. &lt;br /&gt;
&lt;br /&gt;
Serendipitously, we have already generated the previous-year&#39;s applicable rules.  But since the previous year&#39;s rules also depend on its own previous year&#39;s rules, we will need to calculate the dates 2-years-prior to ensure we get the wall clock time correctly for the previous year (we can drop the 2-years-prior from the final consideration once they aid in calculating the offsets). &lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define (applicable-rules date rules)
  ... 
  (let ((year (date-year date))) 
    (append (by-year year rules)
            (by-year (sub1 year) rules) 
            (by-year (- year 2) rules))))
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
Let&#39;s first convert a single rule/year pair to be a date, based on the previous applicable rule:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define (rule/year-&gt;date rule year prev-rule std-offset) 
  (let ((date (on/year-&gt;date rule year)))
    (match (rule-time rule) 
      ((list hour minute second type) ;; wall clock 
       (build-date (date-year date)
                   (date-month date)
                   (date-day date)
                   hour 
                   minute
                   second 
                   #:tz (case type 
                          ((g u z) 0 0)
                          ((s) std-offset)
                          (else ;; wall-clock requires the previous rule...  
                           (+ std-offset (rule-offset prev-rule)))))))))
&lt;/code&gt;&lt;/pre&gt;Then we will sort the rule/year pairs according to their precedence, and then call &lt;code&gt;rule/year-&gt;date&lt;/code&gt; by passing in the rules and the previous rules.  &lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define (rule/year&gt;? r/y1 r/y2) 
  (define (date-helper r1 r2 year) 
    (date&gt;? (on/year-&gt;date r1 year) (on/year-&gt;date r2 year)))
  (define (month-helper r1 r2 year)
    (cond ((&gt; (rule-month r1) (rule-month r2)) #t)
          ((= (rule-month r1) (rule-month r2))
           (date-helper r1 r2 year)) 
          (else #f))) 
  (let ((r1 (car r/y1)) 
        (y1 (cdr r/y1))
        (r2 (car r/y2))
        (y2 (cdr r/y2))) 
    (cond ((&gt; y1 y2) #t)
          ((= y1 y2) 
           (month-helper r1 r2 y1)) 
          (else #f))))

(define (rule/years-&gt;date/offsets rule/years std-offset) 
  (define (helper rest acc)
    (cond ((null? rest) (reverse acc)) 
          ((null? (cdr rest)) ;; we have the last one... 
           (reverse acc)) 
          (else ;; we&#39;ll 
           (helper (cdr rest)
                   (cons (cons (rule/year-&gt;date (caar rest) (cdar rest) (caadr rest) std-offset)
                               (rule-offset (caar rest)))
                         acc)))))
  (helper (sort rule/years rule/year&gt;?)
          &#39;()))
&lt;/code&gt;&lt;/pre&gt;With &lt;code&gt;rule/years-&gt;date/offsets&lt;/code&gt; we finally were able to map rules into an ordered pairs of date boundaries and offsets that we can use to determine the correct offset:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define (tz-rules-offset date rules std-offset) 
  (define (helper date/offsets)
    (cond ((null? date/offsets) 0) 
          ((date&gt;? date (caar date/offsets)) 
           (cdar date/offsets))
          (else
           (helper (cdr date/offsets)))))
  (helper (rule/years-&gt;date/offsets (applicable-rules date rules) std-offset)))
&lt;/code&gt;&lt;/pre&gt;And &lt;code&gt;tz-daylight-saving-offset&lt;/code&gt; needs to be updated accordingly since &lt;code&gt;tz-rules-offset&lt;/code&gt; now requires an additional std-offset:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define (tz-daylight-saving-offset date zone-name)
  (define (until/rules-helper until offset rules)
    (define (until-&gt;date year month day hour minute second type)
      (date-&gt;seconds (build-date year month day hour minute second #:tz offset)))
    &lt;b&gt;(list (if (not until) 
              +inf.0
              (apply until-&gt;date until))
          rules
          offset)&lt;/b&gt;)
  (define (match-until/rules-helper date u/r)
    (cond ((null? u/r) 
           (error &#39;tz-standard-offset &quot;invalid zone ~a for date ~a&quot; zone-name date))
          ((&lt;= date (caar u/r))
           &lt;b&gt;(let ((until/rules (car u/r)))
             (tz-rules-offset (seconds-&gt;date date) (cadr until/rules) 
                              (caddr until/rules)))) &lt;/b&gt;
          (else
           (match-until/rules-helper date (cdr u/r)))))
  ...) 
&lt;/code&gt;&lt;/pre&gt;Now we can combine the &lt;code&gt;tz-daylight-saving-offset&lt;/code&gt; and &lt;code&gt;tz-standard-offset&lt;/code&gt; to determine the actual offset for a particular date:&lt;br /&gt;
&lt;pre class=&#39;scheme&#39;&gt;&lt;code&gt;
(define (tz-offset date tz)
  (+ (tz-standard-offset date tz)
     (tz-daylight-saving-offset date tz))) 
&lt;/code&gt;&lt;/pre&gt;Now we can finally correctly calculate the actual offsets.  Stay tuned.</content><link rel='replies' type='application/atom+xml' href='http://weblambda.blogspot.com/feeds/3028319067280708541/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://weblambda.blogspot.com/2009/09/handling-time-zone-in-scheme-rule.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7570364353928786565/posts/default/3028319067280708541'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7570364353928786565/posts/default/3028319067280708541'/><link rel='alternate' type='text/html' href='http://weblambda.blogspot.com/2009/09/handling-time-zone-in-scheme-rule.html' title='Handling Time Zone in Scheme (4): Rule Conversion'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>