in this parser we want to ignore spurious whitespace that may occur between tokensand we want to do so regardless of the state the lexer is in this can be achieved by creating t_ignore variableand by giving it state of any which means it is active in any statet_any_ignore \ \nthis will ensure that any whitespace between tokens is safely and conveniently ignored we have also defined two functionst_any_newline(and t_any_error()these have exactly the same bodies as the t_newline(and t_error(functions defined in the previous subsection ( )--so neither are shown here--but include the state of any in their names so that they are active no matter what state the lexer is in songs [title seconds none lexer ply lex lex(lexer input(fh read()for token in lexerif token type ="seconds"seconds token value elif token type ="title"title token value elif token type ="filename"if title is not none and seconds is not nonesongs append(song(titlesecondstoken value)title seconds none elseprint("failedfilename '{ }without title/durationformat(token value)we use the lexer in the same way as we did for the pls lexeriterating over the tokensaccumulating values (for the seconds and title)and whenever we get filename to go with the seconds and titleadding new song to the song list as beforeat the end (not shown)we return the key_values dictionary to the caller parsing the blocks domain-specific language handcrafted blocks parser |the blocks format is more sophisticated than the key-value-based pls format or the format since it allows blocks to be nested inside each other this presents no problems to plyand in fact the definitions of the tokens can be done wholly using regexes without requiring any functions or states at all