Skip to content

Parser Generation

Stefan Baumann edited this page Apr 19, 2020 · 2 revisions

Most of the parsing code for chunks and custom structs in GameBox files is not written by hand, but automatically generated using attributes on property definitions as described here.

Class analysis

To generate a parser, the properties in the class that the parsing code is being generated for are analyzed via reflection via the Fields.GetFields<T>() method. To be included in the parsing code, a property must be decorated with the Property attribute. For more advanced data structures, the Array, Condition attributes are also available which further specify the parsing behaviour. If the parsing of a specific property is too complex for the attribute-based system, a custom parsing method can be provided via the CustomParserMethod attribute. All of this information is stored in a Field object for each property and returned collectively.

Code generation

From this collection of Field instances, the parsing code for the chunk is generated. First, code for creating the instance of the chunk/custom struct is generated, then the code for parsing each of the fields is generated. There are two methods for code generation included in ManiaPlanetSharp

Dynamically compiling parser generation

This method uses Linq Expressions to generate expression trees that represent the parsing code. These are then compiled dynamically and loaded into memory. This API can be used to generate parsers for arbitrary chunk/custom struct layouts at runtime.

Code generation

This method emits a string containing plain C# code for parsing the specified chunk/custom struct. This API is used for generating the pre-compiled parsers.

Pre-compiled parsers

ManiaPlanetSharp includes the ManiaPlanetSharp.GameBox.AutoGenerated project, which is used for providing pre-compiled parsers for all chunks and custom structs implemented in ManiaPlanetSharp. This project uses T4 to load the ManiaPlanetSharp library after its compilation, create a list of all present chunks and custom structs and then emit parsing code for all of them and then compiles them into the ManiaPlanetSharp.GameBox.AutoGenerated library. When the ParserFactory class of the main ManiaPlanetSharp library, which manages parsers for chunks and custom structs, is initialized, this library is loaded (if present) and the parser classes are then served via the various methods of the ParserFactory class instead of generating them at runtime. This helps reducing the memory footprint of the application significantly, as each chunk and struct parser would be compiled into a separate assembly if the pre-compiled parsers are not included. Performance is also improved, as the chunk parsers don't have to be generated and compiled at runtime.

For most use cases, the pre-compiled parsers are the better way to go - the dynamically compiled parsers are primarily meant for development purposes where you might want to try out different chunk layouts at runtime without having to recompile the project or for projects which implement their own chunks or structs on top of the ones that ManiaPlanetSharp provides. For the latter use case, including the pre-compiled parsers still makes sense to speed up the parsing of the chunks that are implemented in ManiaPlanetSharp - for the ones which are not included in ManiaPlanetSharp, the parsers will be generated at runtime automatically, so you still get most of the performance advantages while being able to add your own chunks.