Closed arkanoid87 closed 3 years ago
I've refined the solution by adding this template to my project
template parserEqualOperator*(T: untyped) =
proc `==`*(a, b: T): bool =
for _, f1, f2 in fieldPairs(a[], b[]):
when (f1 is ref) or (f2 is ref):
if f1.isNil() and f2.isNil():
return true
elif f1.isNil() or f2.isNil():
return false
if f1 != f2:
return false
return true
and applying it below a group of nested parsers (struct) generator like
parserEqualOperator(RefMainObj | RefChildObj)
Thanks for the issue.
template peekcondget*(parse, parsed, output, prefix: untyped) =
let save = s.getPosition
let peek = s.readStr(8*prefix.len)
s.setPosition(save)
if peek == prefix:
parse
output = parsed
template peekcondput*(encode, encoded, output, prefix: untyped) = if not encoded.isNil: output = encoded encode
If peeked bytes doesn't match the sub-parser result is nil and when it matches a sub-parser type object is returned. Behavior is correct but makes comparison of decoded object not obvious as equality operator `==` doesn't work on values for nested ref types, so a custom `==` operator on bynarylang generated type is used to restore old tuple-based behaviour. Consider this testcase:
```nim
import binarylang, bitstreams
import libparse
struct(dateParser):
s: _ = "DATE"
s: year(4)
s: month(2)
s: day(2)
struct(timeParser):
s: _ = "TIME"
s: hours(2)
s: minutes(2)
s: seconds(2)
struct(tempParser):
s: _ = "TEMP"
s: temperature(3)
struct(windDirParser):
s: _ = "WINDDIR"
s: azimuth(3)
struct(windSpeedParser):
s: _ = "WINDSPEED"
s: kmh(3)
struct(mainParser):
*dateParser {peekcond("DATE")}: date
*timeParser {peekcond("TIME")}: time
*tempParser {peekcond("TEMP")}: temp
*windDirParser {peekcond("WINDDIR")}: winddir
*windSpeedParser {peekcond("WINDSPEED")}: windspeed
parserEqualOperator(MainParser | WindSpeedParser | WindDirParser |
TempParser | TimeParser | DateParser)
proc decode(data: string): MainParser = mainParser.get(newStringBitStream(data))
proc encode(data: MainParser): string =
let encodedStream = newStringBitStream()
mainParser.put(encodedStream, data)
encodedStream.seek(0)
encodedStream.readAll
proc decodeRencodeTest(data, name: string) =
let decoded = data.decode
assert decoded == decoded, name & " decode comparison"
let reencoded = decoded.encode
assert data == reencoded, name & " reencoded comparison"
"".decodeRencodeTest "Empty"
"DATE20210314TIME135056TEMP055WINDDIR071WINDSPEED122".decodeRencodeTest "All Options"
"DATE20210314TIME135056WINDDIR071WINDSPEED122".decodeRencodeTest "Missing one option"
"DATE20210314".decodeRencodeTest "Missing all but head"
"WINDSPEED122".decodeRencodeTest "Missing all but tail"
"TIME135056WINDDIR071".decodeRencodeTest "Missing all but center"
"DATE20210314TEMP055WINDSPEED122".decodeRencodeTest "Missing some"
I'm quite happy with the ref object based approach and my code looks much better without typegetter, what I am trying to say it that I think that comparison of decoded object generated by binarylang should could be made working out-of-the-box/easier without my cumberstone template approach.
I am closing this issue since this is being discussed at #13.
I've upgraded binarylang in my project and I'd like to share some feedback that could improve this wonderful project.
About docs:
*
to export names works for both object name and field names, but it was not clear in docs so I had to experimentAbout code: before 1.0 I was using == operator to check parsed data correctness in my test cases and it was easy as decoded stuff was just a tuple hierarchy. Now with ref types I can still test flat objects easily with
but it won't work with nested parsers as it would compare by ref, so I had to solve with a custom == operator like in
For quite complex parsers the size of the type OR gets quite noisy, it would be easier if all types generated by binarylang were subclass of a common object or something like that. I'm also facing the issue when some subparsers are decoded as nil