Parsing strategy
Since ipuz is a very loosely defined spec, we have a similarly tolerant approach to parsing ipuz files. We try to map gobject properties to fields in the puzzle whenever possible. However, that’s a really bad fit for the board where we want one data structure for the board, and there can be multiple fields representing them.
Since the Json-glib::serializable interface doesn’t let us map multiple nodes
to a single property (such as mapping "puzzle"
and "solution"
to
a board
property) we have to do custom parsing.
When we call ipuz_puzzle_new_*
, we will build the json dom and then:
Confirm the ipuz file is a version of ipuz that we can handle (.v2)
Read the
kind
field and create a sub-class appropriately.Go through all members in the toplevel object in the file, and call
:load_node
on it:The default handler should handle setting properties with the same name with SCALAR json nodes (aka booleans, strings, ints, etc).
For more complex types, the
:load_node
handler should handle them manuallyBe sure to chain up to the super type!
We then call
:post_load_node
to catch any types that need loading after the rest of the cells have been called. As an example, both solutions and puzzle need a lot of previously parsed values (dimensions, block, etc)There is no need to chain up on
post_load_node
.
Next, there’s a
fixup
stage. This is for making everything that’s implicit, explicit. For example, calculating enumerations or cell areas.Lastly, there’s a
validate
stage that makes sure the puzzle makes sense. Catches nonsensical puzzles like puzzles without a grid or clues, etc.
Notes:
We ignore fields that are misformed or we don’t understand in the interest of compatibility. There is no strict parsing mode right now.
GError handling really only catches misformed files. We don’t provide a way to warn right now about unhandled elements
Boxed types (style and clue) have a
_load_node
convenience functionCells have
_parse_*
functions, becuase they’re allocated statically and we need to fill in their data.It’s possible to have a block in both the “puzzle” and the “solution”. This is confusing and can lead to conflicting puzzles. To make it simpler, we ignore the block in the solution field.