"""ChordPro lexer""" import logging import ply.lex as lex LOGGER = logging.getLogger() #pylint: disable=invalid-name tokens = ( 'LBRACE', 'RBRACE', 'NEWLINE', 'COLON', 'WORD', 'SPACE', 'CHORD', 'TEXT', 'KEYWORD', 'SOC', 'EOC', 'SOB', 'EOB', 'SOT', 'EOT', ) class ChordProLexer: """ChordPro Lexer class""" # pylint: disable=too-many-public-methods tokens = tokens states = ( ('chord', 'exclusive'), ('directive', 'exclusive'), ('directiveargument', 'exclusive'), ('tablature', 'exclusive'), ) t_SPACE = r'[ \t]+' t_chord_CHORD = r'[A-G#bmajdisus2-9/ ]+' t_directive_SPACE = r'[ \t]+' t_directive_KEYWORD = r'[a-zA-Z_]+' t_directiveargument_TEXT = r'[^}]+' @staticmethod def t_SOC(token): r'{(soc|start_of_chorus)}' return token @staticmethod def t_EOC(token): r'{(eoc|end_of_chorus)}' return token @staticmethod def t_SOB(token): r'{(sob|start_of_bridge)}' return token @staticmethod def t_EOB(token): r'{(eob|end_of_bridge)}' return token def t_SOT(self, token): r'{(sot|start_of_tab)}' self.lexer.push_state('tablature') return token def t_tablature_EOT(self, token): r'{(eot|end_of_tab)}' self.lexer.pop_state() return token @staticmethod def t_tablature_SPACE(token): r'[ \t]+' return token t_tablature_TEXT = r'[^\n]+' t_tablature_NEWLINE = r'\n' def __init__(self): self.__class__.lexer = lex.lex(module=self) # Define a rule so we can track line numbers @staticmethod def t_NEWLINE(token): r'[\n\r]' token.lexer.lineno += 1 return token @staticmethod def t_COMMENT(token): r'\#.*' pass @staticmethod def t_WORD(token): r'[^{}\n\][\t ]+' return token def t_LBRACKET(self, __token): r'\[' self.lexer.push_state('chord') def t_chord_RBRACKET(self, __token): r'\]' self.lexer.pop_state() def t_LBRACE(self, token): r'{' self.lexer.push_state('directive') return token def t_directive_RBRACE(self, token): r'}' self.lexer.pop_state() return token def t_directiveargument_RBRACE(self, token): r'}' self.lexer.pop_state() self.lexer.pop_state() return token def t_directive_COLON(self, token): r':' self.lexer.push_state('directiveargument') return token @staticmethod def error(token, more=""): """Display error message, and skip illegal token.""" LOGGER.error( "Line {line}: Illegal character '{char}'{more}.".format( line=token.lexer.lineno, char=token.value[0], more=more, ) ) token.lexer.skip(1) def t_error(self, token): """Manage errors""" self.error(token) def t_chord_error(self, token): """Manage errors""" self.error(token, more=" in chord") def t_tablature_error(self, token): """Manage errors""" self.error(token, more=" in tablature") def t_directive_error(self, token): """Manage errors""" self.error(token, more=" in directive") def t_directiveargument_error(self, token): """Manage errors""" return self.t_directive_error(token)