mirror of https://github.com/patacrep/patacrep.git
Luthaf
10 years ago
3 changed files with 166 additions and 3 deletions
@ -0,0 +1,71 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
"""Abstract Syntax Tree for ChordPro code.""" |
||||
|
|
||||
|
class AST: |
||||
|
"""Base class for the tree.""" |
||||
|
# pylint: disable=no-init |
||||
|
metadata = None |
||||
|
|
||||
|
@classmethod |
||||
|
def init_metadata(cls): |
||||
|
"""Clear metadata |
||||
|
|
||||
|
As this attribute is a class attribute, it as to be reset at each new |
||||
|
parsing. |
||||
|
""" |
||||
|
cls.metadata = { |
||||
|
'@languages': set(), |
||||
|
} |
||||
|
|
||||
|
class Expression(AST): |
||||
|
"""ChordPro expression""" |
||||
|
|
||||
|
def __init__(self, value): |
||||
|
super().__init__() |
||||
|
self.content = [value] |
||||
|
|
||||
|
def prepend(self, value): |
||||
|
"""Add a value at the beginning of the content list.""" |
||||
|
if value is not None: |
||||
|
self.content.insert(0, value) |
||||
|
return self |
||||
|
|
||||
|
def __str__(self): |
||||
|
return "".join([str(item) for item in self.content]) |
||||
|
|
||||
|
class SongPart(AST): |
||||
|
"""ChordPro start_of/end_of command |
||||
|
|
||||
|
{start_of_chorus}, {end_of_tab}, {eov} ... |
||||
|
""" |
||||
|
|
||||
|
class Type: |
||||
|
CHORUS = ("chorus", |
||||
|
"start_of_chorus", "end_of_chorus", |
||||
|
"soc", "eoc") |
||||
|
VERSE = ("verse", |
||||
|
"start_of_verse", "end_of_verse", |
||||
|
"sov", "eov") |
||||
|
BRIDGE = ("bridge", |
||||
|
"start_of_bridge", "end_of_bridge", |
||||
|
"sob", "eob") |
||||
|
TAB = ("tab", |
||||
|
"start_of_tab", "end_of_tab", |
||||
|
"sot", "eot") |
||||
|
|
||||
|
def __init__(self, name): |
||||
|
if "_" in name: |
||||
|
self.init_long_form(name) |
||||
|
else: |
||||
|
self.init_short_form(name) |
||||
|
|
||||
|
def __str__(self): |
||||
|
return self.name |
||||
|
|
||||
|
def init_short_form(self, name): |
||||
|
self.type = "" |
||||
|
|
||||
|
def init_long_form(self, name): |
||||
|
self.type = "" |
||||
|
|
||||
|
|
@ -0,0 +1,88 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
"""ChordPro parser""" |
||||
|
|
||||
|
import logging |
||||
|
import ply.yacc as yacc |
||||
|
|
||||
|
from patacrep.chordpro.lexer import tokens, ChordProLexer |
||||
|
from patacrep.chordpro import ast |
||||
|
from patacrep.errors import SongbookError |
||||
|
|
||||
|
LOGGER = logging.getLogger() |
||||
|
|
||||
|
class ParsingError(SongbookError): |
||||
|
"""Parsing error.""" |
||||
|
|
||||
|
def __init__(self, message): |
||||
|
super().__init__(self) |
||||
|
self.message = message |
||||
|
|
||||
|
def __str__(self): |
||||
|
return self.message |
||||
|
|
||||
|
|
||||
|
class Parser: |
||||
|
"""ChordPro parser class""" |
||||
|
|
||||
|
def __init__(self, filename=None): |
||||
|
self.tokens = tokens |
||||
|
self.filename = filename |
||||
|
|
||||
|
@staticmethod |
||||
|
def __find_column(token): |
||||
|
"""Return the column of ``token``.""" |
||||
|
last_cr = token.lexer.lexdata.rfind('\n', 0, token.lexpos) |
||||
|
if last_cr < 0: |
||||
|
last_cr = 0 |
||||
|
column = (token.lexpos - last_cr) + 1 |
||||
|
return column |
||||
|
|
||||
|
def p_error(self, token): |
||||
|
"""Manage parsing errors.""" |
||||
|
LOGGER.error("Error in file {}, line {}:{}.".format( |
||||
|
str(self.filename), |
||||
|
token.lineno, |
||||
|
self.__find_column(token), |
||||
|
) |
||||
|
) |
||||
|
|
||||
|
@staticmethod |
||||
|
def p_expression(symbols): |
||||
|
"""expression : brackets expression |
||||
|
| braces expression |
||||
|
| command expression |
||||
|
| NEWLINE expression |
||||
|
| word expression |
||||
|
| SPACE expression |
||||
|
| empty |
||||
|
""" |
||||
|
if len(symbols) == 3: |
||||
|
if symbols[2] is None: |
||||
|
symbols[0] = ast.Expression(symbols[1]) |
||||
|
else: |
||||
|
symbols[0] = symbols[2].prepend(symbols[1]) |
||||
|
else: |
||||
|
symbols[0] = None |
||||
|
|
||||
|
@staticmethod |
||||
|
def p_empty(__symbols): |
||||
|
"""empty :""" |
||||
|
return None |
||||
|
|
||||
|
@staticmethod |
||||
|
def p_brackets(symbols): |
||||
|
"""brackets : LBRACKET expression RBRACKET""" |
||||
|
symbols[0] = symbols[2] |
||||
|
|
||||
|
@staticmethod |
||||
|
def p_braces(symbols): |
||||
|
"""braces : LBRACE expression COLON expression RBRACE""" |
||||
|
symbols[0] = symbols[2] |
||||
|
|
||||
|
|
||||
|
def parsesong(string, filename=None): |
||||
|
"""Parse song and return its metadata.""" |
||||
|
return yacc.yacc(module=Parser(filename)).parse( |
||||
|
string, |
||||
|
lexer=ChordProLexer().lexer, |
||||
|
) |
Loading…
Reference in new issue