< index < 7. File parser < 7.2 Declaring the 'structures' | ===================================== 7.3 Running the parser ===================================== | > 7.4 Standard data types

10.3.1 Creating a listener
10.3.2 Handling 'newStruct' events
10.3.3 Handling 'newFlag' events
10.3.4 Handling 'newProperty' events
10.3.5 Handling 'endStruct' events
10.3.6 Handling errors
10.3.7 Running the parser
10.3.8 Destroying the parser

### Creating a listener

NB : for basic config files, you don't have to write a listener. Instead, use the default listener. The parser uses a SAX-like approach during the parsing of the file. This means that the whole file is not stored in memory in a tree structure. Instead, it works like a stream parser and raises events. Each event has an associated callback that is provided by a listener :

    C++ :
    	class ITCODParserListener {
    	public :
    		virtual bool parserNewStruct(TCODParser *parser,const TCODParserStruct *str,const char *name)=0;
    		virtual bool parserFlag(TCODParser *parser,const char *name)=0;
    		virtual bool parserProperty(TCODParser *parser,const char *name, TCOD_value_type_t type, TCOD_value_t value)=0;
    		virtual bool parserEndStruct(TCODParser *parser,const TCODParserStruct *str, const char *name)=0;
    		virtual void error(const char *msg) = 0;
    	};
    C   :
    	typedef struct {
    		bool (*new_struct)(TCOD_parser_struct_t str,const char *name);
    		bool (*new_flag)(const char *name);
    		bool (*new_property)(const char *name, TCOD_value_type_t type, TCOD_value_t value);
    		bool (*end_struct)(TCOD_parser_struct_t str, const char *name);
    		void (*error)(const char *msg);
    	} TCOD_parser_listener_t;
    Py  :
    	class ParserListener :
    		def new_struct(str,name) : ...
    		def new_flag(name) : ...
    		def new_property(name,type,value) : ...
    		def end_struct(self, struct, name) : ...
    		def error(msg) : ...

Before running the parser, you have to build a listener :

    C++ :
    	class MyListener : public ITCODParserListener {
    		bool parserNewStruct(TCODParser *parser,const TCODParserStruct *str,const char *name) {
    			printf ("new structure type '%s' with name '%s'\n",str->getname(),name ? name : "NULL");
    			return true;
    		}
    		bool parserFlag(TCODParser *parser,const char *name) {
    			printf ("found new flag '%s'\n",name);
    			return true;
    		}
    		bool parserProperty(TCODParser *parser,const char *name, TCOD_value_type_t type, TCOD_value_t value) {
    			printf ("found new property '%s'\n",name);
    			return true;
    		}
    		bool parserEndStruct(TCODParser *parser,const TCODParserStruct *str,const char *name) {
    			printf ("end of structure type '%s'\n",name);
    			return true;
    		}
    		void error(char *msg) {
    			fprintf(stderr,msg);
    			exit(1);
    		}
    	};
    C   :
    	bool my_parser_new_struct(TCOD_parser_struct_t str, const char *name) {
    		printf ("new structure type '%s' with name '%s'\n",TCOD_struct_get_name(str),name ? name : "NULL");
    		return true;
    	}
    	bool my_parser_flag(const char *name) {
    		printf ("found new flag '%s'\n",name);
    		return true;
    	}
    	bool my_parser_property(const char *name, TCOD_value_type_t type, TCOD_value_t value) {
    		printf ("found new property '%s'\n",name);
    		return true;
    	}
    	bool my_parser_end_struct(TCOD_parser_struct_t str, const char *name) {
    		printf ("end of structure type '%s'\n",name);
    		return true;
    	}
    	void my_parser_error(const char *msg) {
    		fprintf(stderr,msg);
    		exit(1);
    	}
    	TCOD_parser_listener_t my_listener = {
    		my_parser_new_struct,
    		my_parser_flag,
    		my_parser_property,
    		my_parser_end_struct,
    		my_parser_error
    	};
    Py  :
        class MyListener:
            def new_struct(self, struct, name):
                print 'new structure type', libtcod.struct_get_name(struct), \
                      ' named ', name
                return True
            def new_flag(self, name):
                print 'new flag named ', name
                return True
            def new_property(self,name, typ, value):
                type_names = ['NONE', 'BOOL', 'CHAR', 'INT', 'FLOAT', 'STRING', \
                              'COLOR', 'DICE']
                if typ == libtcod.TYPE_COLOR :
                    print 'new property named ', name,' type ',type_names[typ], \
                          ' value ', value.r, value.g, value.b
                elif typ == libtcod.TYPE_DICE :
                    print 'new property named ', name,' type ',type_names[typ], \
                          ' value ', value.nb_dices, value.nb_faces, \
                          value.multiplier, value.addsub
                else:
                    print 'new property named ', name,' type ',type_names[typ], \
                          ' value ', value
                return True
            def end_struct(self, struct, name):
                print 'end structure type', libtcod.struct_get_name(struct), \
                      ' named ', name
                return True
            def error(self,msg):
                print 'error : ', msg
                return True

### Handling 'newStruct' events

This callback is called each time the parser find a new structure declaration in the file. Example :

    item_type "blade" { // <= newStruct event here
    	...
    }

It must return true if everything is right, false if there is an error and the parser must exit.

    C++ : bool ITCODParserListener::parserNewStruct(TCODParser *parser,TCODParserStruct *str,const char *name)
    C   : bool new_struct(TCOD_parser_struct_t str,const char *name)
    Py  : new_struct(str,name)

Parameter | Description
--- | ---
parser | In the C++ version, the parser object, returned by TCODParser constructor. It's used for error handling.
str | The structure type. Can be used to retrieve the type's name with getName. In the example above, this would be "item_type".
name | The name of the structure or NULL if no name is present in the file. In the example above, this would be "blade".

### Handling 'newFlag' events

This callback is called each time the parser find a new flag in the file. Example :

    item_type "blade" {
    	abstract  // <= newFlag event here
    }

It must return true if everything is right, false if there is an error and the parser must exit.

    C++ : bool ITCODParserListener::parserFlag(TCODParser *parser,const char *name)
    C   : bool new_flag(const char *name)
    Py  : new_flag(name)

Parameter | Description
--- | ---
parser | In the C++ version, the parser object, returned by TCODParser constructor. It's used for error handling.
name | The name of the flag. In the example, this would be "abstract".

### Handling 'newProperty' events

This callback is called each time the parser find a new property in the file. Example :

    item_type "blade" {
    	abstract
    	cost=300 // <= newProperty event here
    }

It must return true if everything is right, false if there is an error and the parser must exit.

    C++ : bool ITCODParserListener::parserProperty(TCODParser *parser,const char *name, TCOD_value_type_t type, TCOD_value_t value)
    C   : bool new_property(const char *name, TCOD_value_type_t type, TCOD_value_t value)
    Py  : new_property(name,type,value)

Parameter | Description
--- | ---
parser | In the C++ version, the parser object, returned by TCODParser constructor. It's used for error handling.
name | The name of the property. In the example, this would be "cost".
type | The type of the property as defined when you called addProperty or addValueList. In the example, this would be TCOD_TYPE_INT.
value | The value of the property, stored in a generic value structure. In the example, we would have value.i == 300. In the case of a value-list property, the type would reflect the list id (between TCOD_TYPE_VALUELIST00 and TCOD_TYPE_VALUELIST15) and value.s would contain the actual string.

### Handling 'endStruct' events

This callback is called each time the parser find the end of a structure declaration in the file. Example :

    item_type "blade" {
    	...
    } // <= endStruct event here

It must return true if everything is right, false if there is an error and the parser must exit.

    C++ : bool ITCODParserListener::parserEndStruct(TCODParser *parser,TCODParserStruct *str,const char *name)
    C   : bool end_struct(TCOD_parser_struct_t str,const char *name)
    Py  : end_struct(str,name)

Parameter | Description
--- | ---
parser | In the C++ version, the parser object, returned by TCODParser constructor. It's used for error handling.
str | The structure type. Can be used to retrieve the type's name with getName. In the example above, this would be "item_type".
name | The name of the structure or NULL if no name is present in the file. In the example above, this would be "blade".

### Handling errors

There are two kind of errors :

- Errors that are detected by the parser itself (malformed file, bad value syntax for a property, missing mandatory property in a structure, ...).
- Errors that you detect in your callbacks.

When the parser finds an error in the file, it will call the error callback and stop :

    C++ : void ITCODParserListener::error(const char *msg)
    C   : void error(const char *msg)
    Py  : error(msg)

Parameter | Description
--- | ---
msg | The error message from the parser with the file name and the line number.

If you find an error in your callback, you have to call the parser error function. It will add the file name and line number to your error message, and then call your error callback :

    C++ : void TCODParser::error(const char *msg, ...)
    C   : void TCOD_parser_error(const char *msg, ...)
    Py  : parser_error(msg)

Parameter | Description
--- | ---
msg | printf-like format string for your error message.

Example :

    C++ : parser->error("Bad cost value %d. Cost must be between 0 and 1000", value.i);
    C   : TCOD_parser_error("Bad cost value %d. Cost must be between 0 and 1000", value.i);
    Py  : libtcod.parser_error("Bad cost value %d. Cost must be between 0 and 1000"%( value ))

The previous code will result in your error callback called with the following string :

    "error in <filename> line <line_number> : Bad cost value %d. Cost must be between 0 and 1000"

### Running the parser

Once you defined all the structure types and created your listener, you can start the actual parsing of the file :

    C++ : void TCODParser::run(const char *filename, ITCODParserListener *listener)
    C   : void TCOD_parser_run(TCOD_parser_t parser, const char *filename, TCOD_parser_listener_t *listener)
    Py  : parser_run(parser, filename, listener=0)

Parameter | Description
--- | ---
parser | In the C version, the parser handler, returned by TCOD_parser_new.
filename | The name of the text file to parse, absolute or relative to current directory.
listener | The listener containing the callbacks. Use NULL for the default listener

Example :

    C++ : myParser.run("config.txt",new MyListener());
    C   : TCOD_parser_run(my_parser,"config.txt", my_listener);
    Py  : libtcod.parser_run(my_parser,"config.txt", MyListener())

### Destroying the parser

Once you've done with the file parsing, you can release the resources used by the parser :

    C++ : TCODParser::~TCODParser()
    C   : void TCOD_parser_delete(TCOD_parser_t parser)
    Py  : parser_delete(parser)

Parameter | Description
--- | ---
parser | In the C version, the parser handler, returned by TCOD_parser_new.