"
" a "
")) paraphed)))) "Step 5. Define a few nifty-looking colors for rendering output Clojure code: " (def string-color "#889") (def keyword-color "#458") (def splice-color "#485") (def reserved-word-color "#c50") (def punctuation-color "#666") (def definition-color "#d80") (def symbol-color "#059") "Step 6. A function to color strings, keywords, symbols, punctuation, splices, definitions. This function also takes the previous colored word so as to render definition names in bold (need to detect that the previously colored word was 'def', 'defn' or 'defmacro'):" (defn color[st previous] (cond (= "" (.trim st)) "" (.startsWith st "\"") (str "" (htmlize st) "") (.startsWith st ":") (str "" (htmlize st) "") (.startsWith st "~") (str "" (htmlize st) "") (contains? reserved (. st trim)) (str "" (htmlize st) "") (contains? punc (. st trim)) (str "" (htmlize st) "") :default (if (contains? definitions previous) (str "" (htmlize st) "") (str "" (htmlize st) "")))) "Finally, I define input and output streams and parse the input, transform to HTML and send it to the output. This piece of code is a state-aware recursive loop into the Clojure structure. I define three states: symbol, string and escape. While I'm in :symbol mode, I use the (,),[,],{ and } as word separators. As soon as a double-quote \" is encountered, I switch to string mode and collect all characters into a string. If while in string mode I encounter a backslash \\, I switch to escape mode for a single following character that will be added to the string wether it's a double quote or not. From escape mode I can only go to string mode, which encountering another double-quote \" will switch back down to symbol mode and send the string to the rendering engine. The loop keeps an account of opening and closing punctuation so as to maintain information of the nesting level of any word: this is to process zero-level strings differently: instead of rendering them surrounded by double-quotes I will render them as HTML paragraphs. This is the literate programming that I was mentionning earlier: the code comments will become the text of the blog entry. " (let [out (java.io.PrintStream. (java.io.FileOutputStream. output-file))] (. out println (str "" (loop [buffer "" state :symbol word "" previous-word "" level 0] (let [next-char-read (char (.read input)) next-char (str "" next-char-read)] (if (= next-char-read (char -1)) buffer (cond (= :escaping state) (recur buffer :string (str word next-char) "" level) (= :string state) (cond (= next-char "\\") (recur buffer :escaping word "" level) (= next-char "\"") (if (= 0 level) (recur (text (str buffer word)) :symbol "" "" level) (recur (str buffer (color (str "\"" word "\"") "")) :symbol "" "" level)) :default (recur buffer state (str word next-char) "" level)) (= :symbol state) (cond (= next-char "\"") (recur (str buffer (color word previous-word)) :string "" word level) (= next-char "\n") (recur (str buffer (color word previous-word) "
") state "" word level) (= next-char " ") (recur (str buffer (color word previous-word) " ") state "" word level) (or (= next-char "[") (= next-char "{") (= next-char "(")) (recur (str buffer (color word previous-word)(color next-char "")) state "" word (+ level 1)) (or (= next-char "]") (= next-char ")") (= next-char "}") ) (recur (str buffer (color word previous-word)(color next-char "")) state "" word (- level 1)) (= next-char "\t") (recur (str buffer " " (color word previous-word)) state "" word level) :default (recur buffer state (str word next-char) previous-word level)))))) "