Hi, I have yet to write anything in TH, although I've been toying with it a little bit and asking some questions on IRC. I have come across a problem that doesn't seem to have a good solution without using some sort of preprocessor. I want to take a simple spec and generate some marshalling code using a straighforward transformation. The spec should look something like:
$(declPkt "ip" [ ("Word8", "v_hl", "0x45"), ("Word8", "tos", "0"), ("Word16", "len", "0"), ("Word16", "id", "0"), ("Word16", "off", "0"), ("Word8", "ttl", "64"), ("Word8", "p", "6"), ("Word16", "sum", "0"), ("Word32", "src", "0"), ("Word32", "dst", "0")])
and should generate something like:
data IP = IP { ip_v_hl :: Word8, ip_tos :: Word8, ip_len :: Word16, ip_id :: Word16, ip_off :: Word16, ip_ttl :: Word8, ip_p :: Word8, ip_sum :: Word16, ip_src :: Word32, ip_dst :: Word32 } deriving Show newIP = IP 0x45 0 20 0 0 64 6 0 0 0
instance ByteContainer IP where getByteAt b 0 = getByteAt (ip_v_hl b) 0 getByteAt b 1 = getByteAt (ip_tos b) 0 getByteAt b 2 = getByteAt (ip_len b) 0 getByteAt b 3 = getByteAt (ip_len b) 1 getByteAt b 4 = getByteAt (ip_id b) 0 getByteAt b 5 = getByteAt (ip_id b) 1 getByteAt b 6 = getByteAt (ip_off b) 0 getByteAt b 7 = getByteAt (ip_off b) 1 getByteAt b 8 = getByteAt (ip_ttl b) 0 getByteAt b 9 = getByteAt (ip_p b) 0 getByteAt b 10 = getByteAt (ip_sum b) 0 getByteAt b 11 = getByteAt (ip_sum b) 1 getByteAt b 12 = getByteAt (ip_src b) 0 getByteAt b 13 = getByteAt (ip_src b) 1 getByteAt b 14 = getByteAt (ip_src b) 2 getByteAt b 15 = getByteAt (ip_src b) 3 getByteAt b 16 = getByteAt (ip_dst b) 0 getByteAt b 17 = getByteAt (ip_dst b) 1 getByteAt b 18 = getByteAt (ip_dst b) 2 getByteAt b 19 = getByteAt (ip_dst b) 3
containerLength b = 20
makeContainer b = IP { ip_v_hl = makeContainer (Slice b 0), ip_tos = makeContainer (Slice b 1), ip_len = makeContainer (Slice b 2), ip_id = makeContainer (Slice b 4), ip_off = makeContainer (Slice b 6), ip_ttl = makeContainer (Slice b 8), ip_p = makeContainer (Slice b 9), ip_sum = makeContainer (Slice b 10), ip_src = makeContainer (Slice b 12), ip_dst = makeContainer (Slice b 16) }
This definitely looks feasible using TH. However, it looks like I might have to mess with ASTs manually. My questions is -- is there an easy way to perform this transformation making use of quasi-quotes for much of the code, or will it be necessary to construct all of the ASTs manually? This seems like something that should have a fairly simple solution but so far I haven't been able to come up with one :(. Tim Newsham http://www.lava.net/~newsham/
Hello Tim, Thursday, January 19, 2006, 4:27:50 AM, you wrote: TN> This definitely looks feasible using TH. However, it looks like I TN> might have to mess with ASTs manually. imho, it's a sort of problem which easier to solve without TH - just generate text file with all code you need why you don't use some serialization library? -- Best regards, Bulat mailto:bulatz@HotPOP.com
imho, it's a sort of problem which easier to solve without TH - just generate text file with all code you need
Writing this as a text preprocessor is pretty easy, and I have written a prototype generator using python. However, I was hoping to get some tighter integration with the language. Here's a good example of why thats desirable. It should be possible to generate marshallers for records with any field types, so long as every field is also of ByteContainer type. To generate the marshaller, the size of each field must be known. ByteContainer's have a method for returning their size (a constant). If a text preprocessor is used to generate marshallers, then it must be smart enough to cross reference with any previous generation that is used as a record field in order to compute the size. Which brings me to a question I have.... I have most of the code for a TH generator done. The only bit I haven't done is something that returns the proper size of each field. I have a record name and a field name (and a field type name), all as strings. I need some way to get the result "containerLength (fieldname x)" for a record instance "x". How can I evaluate such a call at template compute time (ie. compile time)?
why you don't use some serialization library?
Because I haven't yet seen one thats any good :( My goals are to have a serialization library that allows for the specification of big and little endian fields, and that specifies the layout of each type without specifying a process. It is important that it is specified in a way that allows for lazy evaluation. What I have so far is a method that lets me declare an item as a ByteContainer, and convert data back and forth between different ByteContainers (ie. an array of bytes could be a byte container and an IP header could be another, making it possible to convert IP headers into arrays and vice versa). Because lazy evaluation is allowed, it should be possible to extract parts of a component even if there is not enough room for an entire component (ie. I can convert a 4-byte array to an IP record, and extract the first three fields of the record without causing an exception). Here's what this looks like so far: http://lava.net/~newsham/x/Pkts5.lhs.txt If anyone has comments or suggestions on this, I'd love to hear them. (Though perhaps offtopic for this list, feel free to mail me directly).
Bulat mailto:bulatz@HotPOP.com
Tim Newsham http://www.lava.net/~newsham/
participants (2)
-
Bulat Ziganshin -
Tim Newsham