Hello
I wonder if I could ask for advice on how to construct a lazy sequence. My 
application might be of interest to some of you.

The context for this is that I have an embedded system with very limited 
facilities for proper debugging.
I have inserted a 'trace' facility in the embedded code that is able to emit 
binary bytes that I can capture over a serial comm channel.
So, what I get is a stream of bytes that is a sort of mini-language.

For example, I might get the values as in this seq:

  (def bSeq [1 20 30 4 1 7 8 5 60 5 70])

where the 'sentences' of this language are as follows:

1 a b (that is, a 1 is followed by two values),
4 (that is, a 4 is on its own)
5 x (that is, a 5 is followed by one value),  and so on.

Now, examining this stream of values is tedious to say the least, so I want 
to parse it and print meaningful versions of the sentences (after all, I 
know what they really mean :).
So, I construct a set of specifications for the sentences, like this:

(defstruct Spec :nArgs :fmt)

(def specs {
    1 (struct Spec 2 "[1] a=%d b=%d\n")
    4 (struct Spec 0 "[4]\n")
    5 (struct Spec 1 "[5] z=%d\n")
    })

The map of Specs is keyed by the value that starts the sentence.
The Spec itself comprises :nArgs (how many values follow)
and :fmt (a string for the format function, expecting :nArgs values, which 
will render the sentence in a useful way).

I want to do the parsing of the byte sequence as follows, using the magic of 
reduce:

(reduce parser {} bSeq)

where the parser function is this:

  (defn parser [m a]
    (if (empty? m)
      (if-let [spec (specs a)]
        (if (zero? (spec :nArgs))
          (do
            (print (format (spec :fmt)))
            {})
          {:spec spec :args []})
        (throw (Error. (format "Input %d has no format spec" a))))
      (let [newArgs (conj (m :args) a)]
        (if (= ((m :spec) :nArgs) (count newArgs))
          (do
            (print (apply format ((m :spec) :fmt) newArgs))
            {})
          (assoc m :args newArgs)))))

To explain a bit: the args to parser are m, a map with keys:
  :spec -- the spec of the sentence that is currently being parsed
  :args -- a vector of the args to this sentence
or the empty map if the next thing expected is the start value of a 
sentence;
and a the next value in the sequence.

Lo and behold, this produces formatted output like this:

[1] a=20 b=30
[4]
[1] a=7 b=8
[5] z=60
[5] z=70

So far so good, but here comes the real question of this post.
The data arrives from the serial port in variable sized byte arrays at 
unpredictable moments in time; these are placed in a queue, like this:

(def inputQ (java.util.concurrent.LinkedBlockingQueue.))
(defn inputArrives [byteArray] (.put inputQ byteArray))

It is of course very easy to read the things out of the queue by means of 
(.take inputQ), and then read the bytes one-by-one; of course this may block 
waiting for more inputs.

So - how do I make a function that returns a lazy sequence that internally 
is getting the byte arrays off this queue and feeding the bytes contained 
therein to the caller of the lazy sequence - one byte at a time?

And - a similar question (easier to answer I imagine) how do I construct a 
lazy sequence from the bytes read from a file (possibly with some 
translation between reading the raw bytes from the file and handing them 
over to the caller of the lazy sequence)?

Of course, I could (and have) implemented all the above in a much more 
procedural kind of way, but it is so much more elegant (and easier to 
understand) with a lazy sequence :)

Looking forward to some remarkable, simple solutions :)

Regards, Tim

PS Thinking about this makes me wonder about the existence, or non-existence 
of a 'super-reduce' function that could do context-free grammar parsing.
Is such a thing possible? Is there-such a thing as a lazy back-trackable 
sequence? Now that I've asked - I'm not even sure what that means :))

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en

Reply via email to