[cl-who-devel] Generating html tree with dynamic structure

Hello anyone, I have a simple problem, but I can't find an elegant solution. Basically I have a message and two flags, the first one signals message should be rendered in <strong> tag and the second - in <italic>. So there are four possibilities. Here is the actual code: (defmethod p-render-html ((s-tree tree) (p text-piece) s) (with-html-output (s nil :prologue nil) (let ((style (style-of p)) (txt (text-of p))) (if style (if (tree-find-prop s-tree style #'bold) (htm (:strong (if (tree-find-prop s-tree style #'italic) (htm (:italic (str txt))) (str txt)))) (if (tree-find-prop s-tree style #'italic) (htm (:italic (str txt))) (str txt))) (str txt))))) I had to test for each case and it resulted in code duplication. Can it be avoided? What if I had 5 flags or so?

Hi Dmitry, I had the same problem about a year ago. After looking at cl-who, my conclusion was that it's really designed for efficiently writing fixed structure with variable content, not variable structure. My first attempt was simply string concatenation on the result of multiple with-html-output-to-string calls. It worked fine, but with bigger inputs it became unusably slow due to all the consing. A second option I considered was to slap a big ol' EVAL (or MACROEXPAND? I can't remember, offhand) in there. This is a little weird, since cl-who transforms the input sexp into code to efficiently write it, but then I'm only using it once. Also, due to the weird call structure (to-macro-and-back), I recall it being somewhat hard to debug. The thing I finally settled on was writing my own function that took as input something that looked like cl-who's input, and wrote it out to a stream. It was surprisingly simple to write (I think it was 10-15 lines, but I don't have it in front of me right now), though it obviously doesn't handle every case cl-who does. It wouldn't handle htm/str/txt or prologues or inline code or whatever -- I just pass in a complete sexp, and it walks the tree and writes to a stream. In your case, you'd just need to generate a value like (:strong (:italic "text")) to pass in -- and Lisp tends to be pretty good at letting you build lists. :-) (I hooked it up to write to my web server's response stream directly, and it screamed. Well, at least compared to the old "cons a few gigabytes every request" method.) I can dig up that function tonight if it sounds useful to you. - Ken

Ken Harris wrote:
Hi Dmitry,
I had the same problem about a year ago. After looking at cl-who, my conclusion was that it's really designed for efficiently writing fixed structure with variable content, not variable structure.
I have the same feeling now
My first attempt was simply string concatenation on the result of multiple with-html-output-to-string calls. It worked fine, but with bigger inputs it became unusably slow due to all the consing.
A second option I considered was to slap a big ol' EVAL (or MACROEXPAND? I can't remember, offhand) in there. This is a little weird, since cl-who transforms the input sexp into code to efficiently write it, but then I'm only using it once. Also, due to the weird call structure (to-macro-and-back), I recall it being somewhat hard to debug.
The thing I finally settled on was writing my own function that took as input something that looked like cl-who's input, and wrote it out to a stream. It was surprisingly simple to write (I think it was 10-15 lines, but I don't have it in front of me right now), though it obviously doesn't handle every case cl-who does. It wouldn't handle htm/str/txt or prologues or inline code or whatever -- I just pass in a complete sexp, and it walks the tree and writes to a stream.
In your case, you'd just need to generate a value like (:strong (:italic "text")) to pass in -- and Lisp tends to be pretty good at letting you build lists. :-)
(I hooked it up to write to my web server's response stream directly, and it screamed. Well, at least compared to the old "cons a few gigabytes every request" method.)
I can dig up that function tonight if it sounds useful to you.
Yes, please, share it. I thought about such approach and was sure someone already faced that problem and has a solution. P.S. After some search I found htmlgen library. It seems to have built in functions for generate html from s-expressions. Probably it better suited for my use case.

this is a my first shot. I do not know what is an s-tree, thus the implementation of 'text-style' (defmethod p-render-html ((s-tree tree) (p text-piece) stream) (with-html-output (stream nil :prologue nil) (let ((text (text-of p))) (if (styled-p text-piece) (styled-str text ) (str text))))) (defun styled-p (text-piece) (if (style-of text-piece) t nil)) (defun styled-str (text) (dolist (style (text-styles text)) (setf text (apply-style style text))) text) ;; or you can use recursive style ;; this is only for light testing (defun text-styles (text) (list 'italic 'bold)) ;; you may change the internals to a macro (aka defstyle ) based for example on a hastable of lambdas ;; rather than using and maintaining/updating ecase form manually (defun apply-style (style text) (with-output-to-string (out) (with-html-output (out nil :prologue nil) (ecase style ('italic (htm (:italic (str text)))) ('bold (htm (:strong (str text)))))))) Regards, Ala'a (cmo-0) On Mon, Nov 23, 2009 at 8:37 PM, Dmitry V'yal <akamaus@gmail.com> wrote:
Hello anyone,
I have a simple problem, but I can't find an elegant solution.
Basically I have a message and two flags, the first one signals message should be rendered in <strong> tag and the second - in <italic>. So there are four possibilities.
Here is the actual code:
(defmethod p-render-html ((s-tree tree) (p text-piece) s) (with-html-output (s nil :prologue nil) (let ((style (style-of p)) (txt (text-of p))) (if style (if (tree-find-prop s-tree style #'bold) (htm (:strong (if (tree-find-prop s-tree style #'italic) (htm (:italic (str txt))) (str txt)))) (if (tree-find-prop s-tree style #'italic) (htm (:italic (str txt))) (str txt))) (str txt)))))
I had to test for each case and it resulted in code duplication. Can it be avoided? What if I had 5 flags or so?
_______________________________________________ cl-who-devel site list cl-who-devel@common-lisp.net http://common-lisp.net/mailman/listinfo/cl-who-devel
-- It does not matter how fast your code is, if it does not work!
participants (3)
-
Ala'a (cmo-0)
-
Dmitry V'yal
-
Ken Harris