You may be disappointed... I'm not using a library.
Since Sins code follows a very standardized pattern, I'm basically just reading files line-by-line with a stream. If I encounter unexpected input, my parser breaks, but it also says why and where. Otherwise it just continues on happily. That is kinda the cause for my problems with the BuffInstantActions though... I have to write the parser for each option by hand :/
Fortunately though, it should work fairly well once I finish all that. Just... having to do it in the first place is kinda excruciating.
Yeah, I thought to do the same in the beginning. Fortunately, I remembered Context Free Gammars from my first semester of University and subsequently found found and used pyparsing. And yes, I think excruciating fits perfectly well, especially considering that the Ironclad guys don't seem too fond of consistency...in some entities, some identifier are capitalized, in others they are not, in some the description string is called "desc" in others "description"...drove me crazy sometimes. And those instant actions were actually the first time I stopped and considered if I should can the idea.
Here is an example of how my parsers with pyparsing look like, with the brushes file as an example:
g_pixelBox = pp.Group(
co.g_linestart
+ pp.Keyword("pixelBox")("identifier")
+ co.g_space
+ co.g_pos4("box")
+ co.g_eol
)("pixelBox")
g_brushDef = (
co.genStringAttrib("fileName")("texture")
+ g_pixelBox
)
g_margin = (
co.genIntAttrib("left")("left")
+ co.genIntAttrib("right")("right")
+ co.genIntAttrib("top")("top")
+ co.genIntAttrib("bottom")("bottom")
)
g_margins = (
pp.Group(
co.genHeading("stretchMargins")
+ g_margin
)("stretch")
+ pp.Group(
co.genHeading("contentMargins")
+ g_margin
)("content")
)
g_states = (
pp.Group(
co.genHeading("Disabled")
+ g_brushDef
)("disabled")
+ pp.Group(
co.genHeading("Pressed")
+ g_brushDef
)("pressed")
+ pp.Group(
co.genHeading("CursorOver")
+ g_brushDef
)("cursorOver")
+ pp.Group(
co.genHeading("Focused")
+ g_brushDef
)("focused")
+ pp.Group(
co.genHeading("Normal")
+ g_brushDef
)("normal")
)
g_brushSimple = (
co.genKeyValPair("content","Simple")("brushType")
+ pp.Group(g_brushDef)("def")
)
g_brushMargins = (
co.genKeyValPair("content","Margins")("brushType")
+ g_margins
+ pp.Group(g_brushDef)("def")
)
g_brushStates = (
co.genKeyValPair("content","States")("brushType")
+ g_states
)
g_brushMarginsStates = (
co.genKeyValPair("content","MarginsAndStates")("brushType")
+ g_margins
+ g_states
)
g_brush = pp.Group(
co.genHeading("brush")
+ co.genStringAttrib("name")("name")
+ pp.Or([g_brushSimple,g_brushMargins,g_brushStates,
g_brushMarginsStates])
)
g_BrushFile = (
co.g_txt
+ co.genBoolAttrib("onlyLoadOnDemand")("loadOnDemand")
+ co.genListAttrib("brushCount",g_brush)("brushes")
+ pp.StringEnd().suppress()
)
Edit:
This is everything that's needed to parse a *.brushes file. Given such a file, I now only need to call
g_BrushFile.parseFile("testfile")
and get the parsing result as a disctionary/hashmap.