# Writing a parser

A new baggianalysis parser should inherit from {class}`~baggianalysis.core.BaseParser` and overload its {meth}`~baggianalysis.core.BaseParser._parse_file` method, which takes the name of the configuration file as its only parameter. The new method should initialise a new {class}`~baggianalysis.core.System` and add {class}`~baggianalysis.core.Particle` to it. The parser should also set the {attr}`~baggianalysis.core.System.box` and the {attr}`~baggianalysis.core.System.time`, but this is not mandatory. 

**Nota Bene:** custom Python parsers cannot be used to generate trajectories from trajectory files through the {meth}`~baggianalysis.core.BaseTrajectory.initialise_from_trajectory_file` method.

We now write a parser that takes a GROMACS dump file of a [TIP4P/ICE](http://www.sklogwiki.org/SklogWiki/index.php/TIP4P/Ice_model_of_water) system. The system is composed of water molecules made of 4 atoms (one oxygen, two hydrogens and one additional charge site). A configuration file has the following format:

```
water TIP4P/Ice
 1200
    1water  OW1    1   0.127   1.043   1.001
    1water  HW2    2   0.104   1.121   0.950
    1water  HW3    3   0.065   1.043   1.074
    1water  MW4    4   0.115   1.054   1.004
    2water  OW1    5   1.841   0.692   0.574
    2water  HW2    6   1.833   0.600   0.549
    2water  HW3    7   1.933   0.713   0.562
    2water  MW4    8   1.852   0.683   0.569
    ...
   2.05839   2.05839   2.05839
```

where the first line should be skipped, the second line contains the number of atoms and then there are four lines for each molecule, with the three rightmost columns specifying that site's x, y and z coordinates. The last line contains the size of the box edges. 
In this particular file, the time at which the configuration was printed is not stored in the file, but in the name of the file itself, which is sometime linke `QnfT188tXXX`, where `XXX` is the timestep.

Here is the code for the new parser:

```python
class Tip4pIceParser(ba.BaseParser):
    def __init__(self):
        ba.BaseParser.__init__(self)
    
    def _parse_file(self, conf):
        syst = ba.System()
        
        with open(conf) as f:
            # we use the first line to check whether we reached the EOF
            first_line = f.readline()
            
            if len(first_line) == 0:
                return None
            
            syst.time = int(conf.split("t")[1])
            
            N_atoms = int(f.readline().strip())
            for _ in range(N_atoms):
                line = f.readline()
                spl = [x.strip() for x in line.split()]
                if spl[1] == "OW1":
                    pos = [float(x) for x in spl[3:6]]
                    particle = ba.Particle(syst.available_index(), "0", pos)
                    syst.add_particle(particle)
                    
            syst.box = [float(x.strip()) for x in f.readline().split()]
            
        return syst
```

Parsers are responsible for constructing valid {class}`~baggianalysis.core.Particle`s, which also means giving new particles valid indexes. Sometimes the indexes are stored in the file to be parsed, but if this is not the case, or if the user does not care about setting indexes manually, parsers can use the {class}`~baggianalysis.core.System`'s utility function {meth}`~baggianalysis.core.System.available_index()` to obtain a valid index. 
