Browse Source

Parsing works on tests

pull/70/head
Louis 10 years ago
parent
commit
eff4286a77
  1. 13
      patacrep/content/song.py
  2. 4
      patacrep/songs/chordpro/__init__.py
  3. 293
      patacrep/songs/chordpro/ast.py
  4. 111
      patacrep/songs/chordpro/lexer.py
  5. 211
      patacrep/songs/chordpro/syntax.py
  6. 0
      patacrep/songs/chordpro/test/00.sgc
  7. 1
      patacrep/songs/chordpro/test/00.txt
  8. 1
      patacrep/songs/chordpro/test/01.sgc
  9. 4
      patacrep/songs/chordpro/test/01.txt
  10. 1
      patacrep/songs/chordpro/test/02.sgc
  11. 2
      patacrep/songs/chordpro/test/02.txt
  12. 4
      patacrep/songs/chordpro/test/03.sgc
  13. 1
      patacrep/songs/chordpro/test/03.txt
  14. 3
      patacrep/songs/chordpro/test/04.sgc
  15. 4
      patacrep/songs/chordpro/test/04.txt
  16. 3
      patacrep/songs/chordpro/test/05.sgc
  17. 4
      patacrep/songs/chordpro/test/05.txt
  18. 1
      patacrep/songs/chordpro/test/06.sgc
  19. 1
      patacrep/songs/chordpro/test/06.txt
  20. 3
      patacrep/songs/chordpro/test/07.sgc
  21. 4
      patacrep/songs/chordpro/test/07.txt
  22. 10
      patacrep/songs/chordpro/test/08.sgc
  23. 4
      patacrep/songs/chordpro/test/08.txt
  24. 15
      patacrep/songs/chordpro/test/09.sgc
  25. 5
      patacrep/songs/chordpro/test/09.txt
  26. 1
      patacrep/songs/chordpro/test/10.sgc
  27. 4
      patacrep/songs/chordpro/test/10.txt
  28. 1
      patacrep/songs/chordpro/test/11.sgc
  29. 4
      patacrep/songs/chordpro/test/11.txt
  30. 1
      patacrep/songs/chordpro/test/12.sgc
  31. 4
      patacrep/songs/chordpro/test/12.txt
  32. 5
      patacrep/songs/chordpro/test/13.sgc
  33. 6
      patacrep/songs/chordpro/test/13.txt
  34. 1
      patacrep/songs/chordpro/test/21.sgc
  35. 4
      patacrep/songs/chordpro/test/21.txt
  36. 1
      patacrep/songs/chordpro/test/22.sgc
  37. 2
      patacrep/songs/chordpro/test/22.txt
  38. 4
      patacrep/songs/chordpro/test/23.sgc
  39. 1
      patacrep/songs/chordpro/test/23.txt
  40. 3
      patacrep/songs/chordpro/test/24.sgc
  41. 4
      patacrep/songs/chordpro/test/24.txt
  42. 3
      patacrep/songs/chordpro/test/25.sgc
  43. 4
      patacrep/songs/chordpro/test/25.txt
  44. 1
      patacrep/songs/chordpro/test/26.sgc
  45. 1
      patacrep/songs/chordpro/test/26.txt
  46. 3
      patacrep/songs/chordpro/test/27.sgc
  47. 4
      patacrep/songs/chordpro/test/27.txt
  48. 10
      patacrep/songs/chordpro/test/28.sgc
  49. 4
      patacrep/songs/chordpro/test/28.txt
  50. 15
      patacrep/songs/chordpro/test/29.sgc
  51. 5
      patacrep/songs/chordpro/test/29.txt
  52. 1
      patacrep/songs/chordpro/test/30.sgc
  53. 0
      patacrep/songs/chordpro/test/__init__.py
  54. 44
      patacrep/songs/chordpro/test/greensleeves.sgc
  55. 51
      patacrep/songs/chordpro/test/greensleeves.txt
  56. 20
      patacrep/songs/chordpro/test/metadata.sgc
  57. 21
      patacrep/songs/chordpro/test/metadata.txt
  58. 36
      patacrep/songs/chordpro/test/test_parser.py

13
patacrep/content/song.py

@ -67,13 +67,7 @@ def parse(keyword, argument, contentlist, config):
for filename in glob.iglob(os.path.join(songdir.subpath, elem)):
LOGGER.debug('Parsing file "{}"'.format(filename))
extension = filename.split(".")[-1]
try:
renderer = SongRenderer(plugins[extension](
songdir.datadir,
filename,
config,
))
except KeyError:
if extension not in plugins:
LOGGER.warning((
'I do not know how to parse "{}". Ignored.'
).format(
@ -81,6 +75,11 @@ def parse(keyword, argument, contentlist, config):
)
)
continue
renderer = SongRenderer(plugins[extension](
songdir.datadir,
filename,
config,
))
songlist.append(renderer)
config["_languages"].update(renderer.song.languages)
if len(songlist) > before:

4
patacrep/songs/chordpro/__init__.py

@ -10,10 +10,8 @@ class ChordproSong(Song):
"""Parse content, and return the dictinory of song data."""
with encoding.open_read(self.fullpath, encoding=self.encoding) as song:
self.data = parse_song(song.read(), self.fullpath)
print(self.data)
print(type(self.data), self.data)
import sys; sys.exit(1)
self.titles = self.data['@titles']
del self.data['@titles']
self.languages = self.data['@languages']
del self.data['@languages']
self.authors = self.data['by']

293
patacrep/songs/chordpro/ast.py

@ -1,71 +1,264 @@
# -*- coding: utf-8 -*-
"""Abstract Syntax Tree for ChordPro code."""
import functools
def _indent(string):
return "\n".join([" {}".format(line) for line in string.split('\n')])
INLINE_PROPERTIES = {
"lilypond",
"comment",
"guitar_comment",
"image",
}
DIRECTIVE_SHORTCUTS = {
"t": "title",
"st": "subtitle",
"a": "album",
"by": "artist",
"c": "comment",
"gc": "guitar_comment",
}
def directive_name(text):
if text in DIRECTIVE_SHORTCUTS:
return DIRECTIVE_SHORTCUTS[text]
return text
class AST:
"""Base class for the tree."""
# pylint: disable=no-init
metadata = None
inline = False
class Line(AST):
"""A line is a sequence of (possibly truncated) words, spaces and chords."""
def __init__(self):
super().__init__()
self.line = []
def prepend(self, data):
self.line.insert(0, data)
return self
def __str__(self):
return "".join([str(item) for item in self.line])
def strip(self):
while True:
if not self.line:
return self
if isinstance(self.line[0], Space):
del self.line[0]
continue
if isinstance(self.line[-1], Space):
del self.line[-1]
continue
return self
class LineElement(AST):
"""Something present on a line."""
pass
class Word(LineElement):
"""A chunk of word."""
def __init__(self, value):
super().__init__()
self.value = value
@classmethod
def init_metadata(cls):
"""Clear metadata
def __str__(self):
return self.value
As this attribute is a class attribute, it as to be reset at each new
parsing.
"""
cls.metadata = {
'@languages': set(),
}
class Space(LineElement):
"""A space between words"""
class Expression(AST):
"""ChordPro expression"""
def __init__(self):
super().__init__()
def __str__(self):
return " "
class Chord(LineElement):
"""A chord."""
def __init__(self, value):
super().__init__()
self.content = [value]
self.value = value
def __str__(self):
return "[{}]".format(self.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)
class Verse(AST):
"""A verse (or bridge, or chorus)"""
type = "verse"
inline = True
def __init__(self, block=None):
super().__init__()
self.lines = [] # TODO check block
def prepend(self, data):
self.lines.insert(0, data)
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)
return '{{start_of_{type}}}\n{content}\n{{end_of_{type}}}'.format(
type = self.type,
content = _indent("\n".join([str(line) for line in self.lines])),
)
class Chorus(Verse):
type = 'chorus'
class Bridge(Verse):
type = 'bridge'
class Song(AST):
"""A song"""
METADATA_TYPE = {
"title": "add_title",
"subtitle": "add_subtitle",
"language": "add_language",
"artist": "add_author",
}
def __init__(self):
super().__init__()
self.content = []
self.meta = []
self._authors = []
self._titles = []
self._subtitles = []
self._languages = set()
def add(self, data):
if data is None:
if not (self.content and isinstance(self.content[0], Newline)):
self.content.insert(0, Newline())
elif isinstance(data, Line):
if not (self.content and isinstance(self.content[0], Verse)):
self.content.insert(0, Verse())
self.content[0].prepend(data.strip())
elif data.inline:
self.content.insert(0, data)
elif isinstance(data, Directive):
name = directive_name(data.keyword)
if name in self.METADATA_TYPE:
getattr(self, self.METADATA_TYPE[name])(*data.as_tuple)
else:
self.meta.append(data)
else:
self.init_short_form(name)
raise Exception()
return self
def str_meta(self):
for title in self.titles:
yield "{{title: {}}}".format(title)
for language in sorted(self.languages):
yield "{{language: {}}}".format(language)
for author in self.authors:
yield "{{by: {}}}".format(author)
for key in sorted(self.meta):
yield str(key)
def __str__(self):
return self.name
return (
"\n".join(self.str_meta()).strip()
+
"\n========\n"
+
"\n".join([str(item) for item in self.content]).strip()
)
def add_title(self, __ignored, title):
self._titles.insert(0, title)
def add_subtitle(self, __ignored, title):
self._subtitles.insert(0, title)
@property
def titles(self):
return self._titles + self._subtitles
def add_author(self, __ignored, title):
self._authors.insert(0, title)
def init_short_form(self, name):
self.type = ""
@property
def authors(self):
return self._authors
def init_long_form(self, name):
self.type = ""
def add_language(self, __ignored, language):
self._languages.add(language)
@property
def languages(self):
return self._languages
class Newline(AST):
def __str__(self):
return ""
@functools.total_ordering
class Directive(AST):
"""A directive"""
def __init__(self):
super().__init__()
self.keyword = ""
self.argument = None
@property
def keyword(self):
return self._keyword
@property
def inline(self):
return self.keyword in INLINE_PROPERTIES
@keyword.setter
def keyword(self, value):
self._keyword = value.strip()
def __str__(self):
if self.argument is not None:
return "{{{}: {}}}".format(
self.keyword,
self.argument,
)
else:
return "{{{}}}".format(self.keyword)
@property
def as_tuple(self):
return (self.keyword, self.argument)
def __eq__(self, other):
return self.as_tuple == other.as_tuple
def __lt__(self, other):
return self.as_tuple < other.as_tuple
class Tab(AST):
"""Tablature"""
inline = True
def __init__(self):
super().__init__()
self.content = []
def prepend(self, data):
self.content.insert(0, data)
return self
def __str__(self):
return '{{start_of_tab}}\n{}\n{{end_of_tab}}'.format(
_indent("\n".join(self.content)),
)

111
patacrep/songs/chordpro/lexer.py

@ -7,16 +7,21 @@ LOGGER = logging.getLogger()
#pylint: disable=invalid-name
tokens = (
#'LBRACKET',
#'RBRACKET',
'CHORD',
'LBRACE',
'RBRACE',
'NEWLINE',
#'COLON',
'WORD',
'SPACE',
#'NUMBER',
'LBRACE',
'RBRACE',
'CHORD',
'NEWLINE',
'COLON',
'WORD',
'SPACE',
'TEXT',
'KEYWORD',
'SOC',
'EOC',
'SOB',
'EOB',
'SOT',
'EOT',
)
class ChordProLexer:
@ -26,14 +31,51 @@ class ChordProLexer:
states = (
('chord', 'exclusive'),
('directive', 'exclusive'),
('directiveargument', 'exclusive'),
('tablature', 'exclusive'),
)
t_LBRACE = r'{'
t_RBRACE = r'}'
t_SPACE = r'[ \t]+'
#t_COLON = r':'
t_chord_CHORD = r'[A-G7#m]+' # TODO This can be refined
t_directive_SPACE = r'[ \t]+'
t_directive_KEYWORD = r'[a-zA-Z_]+'
t_directiveargument_TEXT = r'[^}]+'
def t_SOC(self, token):
r'{(soc|start_of_chorus)}'
return token
def t_EOC(self, token):
r'{(eoc|end_of_chorus)}'
return token
def t_SOB(self, token):
r'{(sob|start_of_bridge)}'
return token
def t_EOB(self, 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
def t_tablature_SPACE(self, 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)
@ -51,7 +93,7 @@ class ChordProLexer:
@staticmethod
def t_WORD(token):
r'[^\n\][\t ]+'
r'[^{}\n\][\t ]+'
return token
def t_LBRACKET(self, token):
@ -62,11 +104,26 @@ class ChordProLexer:
r'\]'
self.lexer.pop_state()
#@staticmethod
#def t_NUMBER(token):
# r'[0-9]+'
# token.value = int(token.value)
# return token
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 t_error(token):
@ -79,3 +136,19 @@ class ChordProLexer:
"""Manage errors"""
LOGGER.error("Illegal character '{}' in chord..".format(token.value[0]))
token.lexer.skip(1)
@staticmethod
def t_tablature_error(token):
"""Manage errors"""
LOGGER.error("Illegal character '{}' in tablature..".format(token.value[0]))
token.lexer.skip(1)
@staticmethod
def t_directive_error(token):
"""Manage errors"""
LOGGER.error("Illegal character '{}' in directive..".format(token.value[0]))
token.lexer.skip(1)
@staticmethod
def t_directiveargument_error(token):
return t_directive_error(token)

211
patacrep/songs/chordpro/syntax.py

@ -4,9 +4,10 @@
import logging
import ply.yacc as yacc
from patacrep.songs.chordpro.lexer import tokens, ChordProLexer
from patacrep.songs.chordpro import ast
from patacrep.errors import SongbookError
from patacrep.songs.chordpro import ast
from patacrep.songs.chordpro import ast
from patacrep.songs.chordpro.lexer import tokens, ChordProLexer
LOGGER = logging.getLogger()
@ -24,6 +25,8 @@ class ParsingError(SongbookError):
class Parser:
"""ChordPro parser class"""
start = "song"
def __init__(self, filename=None):
self.tokens = tokens
self.filename = filename
@ -39,7 +42,7 @@ class Parser:
def p_error(self, token):
"""Manage parsing errors."""
if token: # TODO remove this test
if token:
LOGGER.error("Error in file {}, line {}:{}.".format(
str(self.filename),
token.lineno,
@ -52,85 +55,213 @@ class Parser:
"""song : block song
| empty
"""
#if isinstance(symbols[1], str):
if len(symbols) == 2:
symbols[0] = ('song')
symbols[0] = ast.Song()
else:
symbols[0] = ('song', symbols[1], symbols[2])
symbols[0] = symbols[2].add(symbols[1])
#@staticmethod
#def p_song_next(symbols):
# """song_next : block song_next
# | empty
# """
# if len(symbols) == 2:
# symbols[0] = ast.Song()
# else:
# symbols[0] = symbols[2].add(symbols[1])
@staticmethod
def p_block(symbols):
"""block : directive NEWLINE newlines
| stanza NEWLINE newlines
"""block : SPACE block
| directive NEWLINE
| line NEWLINE
| chorus NEWLINE
| tab NEWLINE
| bridge NEWLINE
| NEWLINE
"""
symbols[0] = ('block', symbols[1])
if len(symbols) == 3 and isinstance(symbols[1], str):
symbols[0] = symbols[2]
elif (symbols[1] is None) or (len(symbols) == 2):
symbols[0] = None
else:
symbols[0] = symbols[1]
@staticmethod
def p_newlines(symbols):
"""newlines : NEWLINE newlines
| empty"""
symbols[0] = ('newlines')
def p_maybespace(symbols):
"""maybespace : SPACE
| empty
"""
symbols[0] = None
#@staticmethod
#def p_newlines(symbols):
# """newlines : NEWLINE newlines
# | empty"""
# symbols[0] = ('newlines')
@staticmethod
def p_directive(symbols):
"""directive : LBRACE WORD RBRACE"""
symbols[0] = ('directive', symbols[1])
"""directive : LBRACE KEYWORD directive_next RBRACE
| LBRACE SPACE KEYWORD directive_next RBRACE
"""
if len(symbols) == 5:
symbols[3].keyword = symbols[2]
symbols[0] = symbols[3]
else:
symbols[4].keyword = symbols[3]
symbols[0] = symbols[4]
@staticmethod
def p_directive_next(symbols):
"""directive_next : SPACE COLON TEXT
| COLON TEXT
| empty
"""
symbols[0] = ast.Directive()
if len(symbols) == 3:
symbols[0].argument = symbols[2].strip()
elif len(symbols) == 4:
symbols[0].argument = symbols[3].strip()
@staticmethod
def p_line(symbols):
"""line : WORD line_next
| CHORD line_next
| SPACE line_next
"""line : word line_next
| chord line_next
"""
symbols[0] = ('line', symbols[1], symbols[2])
symbols[0] = symbols[2].prepend(symbols[1])
@staticmethod
def p_line_next(symbols):
"""line_next : WORD line_next
| SPACE line_next
| CHORD line_next
"""line_next : word line_next
| space line_next
| chord line_next
| empty
"""
if len(symbols) == 2:
symbols[0] = ('line-next')
symbols[0] = ast.Line()
else:
symbols[0] = symbols[2].prepend(symbols[1])
@staticmethod
def p_word(symbols):
"""word : WORD"""
symbols[0] = ast.Word(symbols[1])
@staticmethod
def p_space(symbols):
"""space : SPACE"""
symbols[0] = ast.Space()
@staticmethod
def p_chord(symbols):
"""chord : CHORD"""
symbols[0] = ast.Chord(symbols[1])
#@staticmethod
#def p_verse(symbols):
# """verse : line NEWLINE verse_next
# """
# symbols[0] = symbols[3].prepend(symbols[1])
#@staticmethod
#def p_verse_next(symbols):
# """verse_next : line NEWLINE verse_next
# | empty
# """
# if len(symbols) == 2:
# symbols[0] = ast.Verse()
# else:
# symbols[0] = symbols[3].prepend(symbols[1])
@staticmethod
def p_chorus(symbols):
"""chorus : SOC maybespace NEWLINE chorus_content EOC maybespace
"""
symbols[0] = symbols[4]
@staticmethod
def p_chorus_content(symbols):
"""chorus_content : line NEWLINE chorus_content
| SPACE chorus_content
| empty
"""
if len(symbols) == 2:
symbols[0] = ast.Chorus()
elif len(symbols) == 3:
symbols[0] = symbols[2]
else:
symbols[0] = ('line-next', symbols[1], symbols[2])
symbols[0] = symbols[3].prepend(symbols[1])
@staticmethod
def p_stanza(symbols):
"""stanza : line NEWLINE stanza_next
def p_bridge(symbols):
"""bridge : SOB maybespace NEWLINE bridge_content EOB maybespace
"""
symbols[0] = ('stanza', symbols[1], symbols[3])
symbols[0] = symbols[4]
@staticmethod
def p_stanza_next(symbols):
"""stanza_next : line NEWLINE stanza_next
| empty
def p_bridge_content(symbols):
"""bridge_content : line NEWLINE bridge_content
| SPACE bridge_content
| empty
"""
if len(symbols) == 2:
symbols[0] = ('stanza-next')
symbols[0] = ast.Bridge()
elif len(symbols) == 3:
symbols[0] = symbols[2]
else:
symbols[0] = ('stanza-next', symbols[1], symbols[3])
symbols[0] = symbols[3].prepend(symbols[1])
#@staticmethod
#def p_braces(symbols):
# """braces : LBRACE expression COLON expression RBRACE"""
# symbols[0] = symbols[2]
#def p_bridge_next(symbols):
# """bridge_next : line NEWLINE bridge_next
# | empty
# """
# if len(symbols) == 2:
# symbols[0] = ast.Bridge()
# else:
# symbols[0] = symbols[3].prepend(symbols[1])
@staticmethod
def p_tab(symbols):
"""tab : SOT maybespace NEWLINE tab_content EOT maybespace
"""
symbols[0] = symbols[4]
@staticmethod
def p_tab_content(symbols):
"""tab_content : NEWLINE tab_content
| TEXT tab_content
| SPACE tab_content
| empty
"""
if len(symbols) == 2:
symbols[0] = ast.Tab()
else:
if symbols[1].strip():
symbols[2].prepend(symbols[1])
symbols[0] = symbols[2]
@staticmethod
def p_empty(symbols):
"""empty :"""
symbols[0] = None
#@staticmethod
#def p_comment(symbols):
# """comment : COMMENT"""
# symbols[0] = ('comment', symbols[1])
def lex_song(content):
# TODO delete
lex = ChordProLexer().lexer
lex.input(content)
while 1:
tok = lex.token()
if not tok: break
print(tok)
def parse_song(content, filename=None):
"""Parse song and return its metadata."""
return yacc.yacc(module=Parser(filename)).parse(
content,
debug=0,
lexer=ChordProLexer().lexer,
)

0
patacrep/songs/chordpro/test/00.sgc

1
patacrep/songs/chordpro/test/00.txt

@ -0,0 +1 @@
========

1
patacrep/songs/chordpro/test/01.sgc

@ -0,0 +1 @@
A verse line

4
patacrep/songs/chordpro/test/01.txt

@ -0,0 +1,4 @@
========
{start_of_verse}
A verse line
{end_of_verse}

1
patacrep/songs/chordpro/test/02.sgc

@ -0,0 +1 @@
{title : A directive}

2
patacrep/songs/chordpro/test/02.txt

@ -0,0 +1,2 @@
{title: A directive}
========

4
patacrep/songs/chordpro/test/03.sgc

@ -0,0 +1,4 @@

1
patacrep/songs/chordpro/test/03.txt

@ -0,0 +1 @@
========

3
patacrep/songs/chordpro/test/04.sgc

@ -0,0 +1,3 @@
{soc}
A one line chorus
{eoc}

4
patacrep/songs/chordpro/test/04.txt

@ -0,0 +1,4 @@
========
{start_of_chorus}
A one line chorus
{end_of_chorus}

3
patacrep/songs/chordpro/test/05.sgc

@ -0,0 +1,3 @@
{sob}
A one line bridge
{eob}

4
patacrep/songs/chordpro/test/05.txt

@ -0,0 +1,4 @@
========
{start_of_bridge}
A one line bridge
{end_of_bridge}

1
patacrep/songs/chordpro/test/06.sgc

@ -0,0 +1 @@
# A comment

1
patacrep/songs/chordpro/test/06.txt

@ -0,0 +1 @@
========

3
patacrep/songs/chordpro/test/07.sgc

@ -0,0 +1,3 @@
{sot}
A tab
{eot}

4
patacrep/songs/chordpro/test/07.txt

@ -0,0 +1,4 @@
========
{start_of_tab}
A tab
{end_of_tab}

10
patacrep/songs/chordpro/test/08.sgc

@ -0,0 +1,10 @@
# comment
# comment
A lot of new lines
# comment

4
patacrep/songs/chordpro/test/08.txt

@ -0,0 +1,4 @@
========
{start_of_verse}
A lot of new lines
{end_of_verse}

15
patacrep/songs/chordpro/test/09.sgc

@ -0,0 +1,15 @@
# comment
# comment
A lot of new lines
# comment
{title: and a directive}
# comment

5
patacrep/songs/chordpro/test/09.txt

@ -0,0 +1,5 @@
{title: and a directive}
========
{start_of_verse}
A lot of new lines
{end_of_verse}

1
patacrep/songs/chordpro/test/10.sgc

@ -0,0 +1 @@
A line[A] with a chord

4
patacrep/songs/chordpro/test/10.txt

@ -0,0 +1,4 @@
========
{start_of_verse}
A line[A] with a chord
{end_of_verse}

1
patacrep/songs/chordpro/test/11.sgc

@ -0,0 +1 @@
A line ending with a chord[A]

4
patacrep/songs/chordpro/test/11.txt

@ -0,0 +1,4 @@
========
{start_of_verse}
A line ending with a chord[A]
{end_of_verse}

1
patacrep/songs/chordpro/test/12.sgc

@ -0,0 +1 @@
[A]A line starting with a chord

4
patacrep/songs/chordpro/test/12.txt

@ -0,0 +1,4 @@
========
{start_of_verse}
[A]A line starting with a chord
{end_of_verse}

5
patacrep/songs/chordpro/test/13.sgc

@ -0,0 +1,5 @@
{sot}
A table
wit many # weir [
[ symbols
{eot}

6
patacrep/songs/chordpro/test/13.txt

@ -0,0 +1,6 @@
========
{start_of_tab}
A table
wit many # weir [
[ symbols
{end_of_tab}

1
patacrep/songs/chordpro/test/21.sgc

@ -0,0 +1 @@
A verse line

4
patacrep/songs/chordpro/test/21.txt

@ -0,0 +1,4 @@
========
{start_of_verse}
A verse line
{end_of_verse}

1
patacrep/songs/chordpro/test/22.sgc

@ -0,0 +1 @@
{title : A directive}

2
patacrep/songs/chordpro/test/22.txt

@ -0,0 +1,2 @@
{title: A directive}
========

4
patacrep/songs/chordpro/test/23.sgc

@ -0,0 +1,4 @@

1
patacrep/songs/chordpro/test/23.txt

@ -0,0 +1 @@
========

3
patacrep/songs/chordpro/test/24.sgc

@ -0,0 +1,3 @@
{soc}
A one line chorus
{eoc}

4
patacrep/songs/chordpro/test/24.txt

@ -0,0 +1,4 @@
========
{start_of_chorus}
A one line chorus
{end_of_chorus}

3
patacrep/songs/chordpro/test/25.sgc

@ -0,0 +1,3 @@
{sob}
A one line bridge
{eob}

4
patacrep/songs/chordpro/test/25.txt

@ -0,0 +1,4 @@
========
{start_of_bridge}
A one line bridge
{end_of_bridge}

1
patacrep/songs/chordpro/test/26.sgc

@ -0,0 +1 @@
# A comment

1
patacrep/songs/chordpro/test/26.txt

@ -0,0 +1 @@
========

3
patacrep/songs/chordpro/test/27.sgc

@ -0,0 +1,3 @@
{sot}
A tab
{eot}

4
patacrep/songs/chordpro/test/27.txt

@ -0,0 +1,4 @@
========
{start_of_tab}
A tab
{end_of_tab}

10
patacrep/songs/chordpro/test/28.sgc

@ -0,0 +1,10 @@
# comment
# comment
A lot of new lines
# comment

4
patacrep/songs/chordpro/test/28.txt

@ -0,0 +1,4 @@
========
{start_of_verse}
A lot of new lines
{end_of_verse}

15
patacrep/songs/chordpro/test/29.sgc

@ -0,0 +1,15 @@
# comment
# comment
A lot of new lines
# comment
{title: and a directive}
# comment

5
patacrep/songs/chordpro/test/29.txt

@ -0,0 +1,5 @@
{title: and a directive}
========
{start_of_verse}
A lot of new lines
{end_of_verse}

1
patacrep/songs/chordpro/test/30.sgc

@ -0,0 +1 @@
[A]A line starting with a chord

0
patacrep/songs/chordpro/test/__init__.py

44
patacrep/songs/chordpro/test/greensleeves.sgc

@ -0,0 +1,44 @@
{language : english}
{columns : 2}
{subtitle : Un sous titre}
{ title : Greensleeves}
{title : Un autre sous-titre}
{artist: Traditionnel}
{cover : traditionnel }
{album :Angleterre}
{partition : greensleeves.ly}
A[Am]las, my love, ye [G]do me wrong
To [Am]cast me oft dis[E]curteously
And [Am]I have loved [G]you so long
De[Am]lighting [E]in your [Am]companie
{start_of_chorus}
[C]Green[B]sleeves was [G]all my joy
[Am]Greensleeves was [E]my delight
[C]Greensleeves was my [G]heart of gold
And [Am]who but [E]Ladie [Am]Greensleeves
{end_of_chorus}
I [Am]have been ready [G]at your hand
To [Am]grant what ever [E]you would crave
I [Am]have both waged [G]life and land
Your [Am]love and [E]good will [Am]for to have
I [Am]bought thee kerchers [G]to thy head
That [Am]were wrought fine and [E]gallantly
I [Am]kept thee both at [G]boord and bed
Which [Am]cost my [E]purse well [Am]favouredly
I [Am]bought thee peticotes [G]of the best
The [Am]cloth so fine as [E]fine might be
I [Am]gave thee jewels [G]for thy chest
And [Am]all this [E]cost I [Am]spent on thee
Thy [Am]smock of silke, both [G]faire and white
With [Am]gold embrodered [E]gorgeously
Thy [Am]peticote of [G]sendall right
And [Am]this I [E]bought thee [Am]gladly

51
patacrep/songs/chordpro/test/greensleeves.txt

@ -0,0 +1,51 @@
{title: Greensleeves}
{title: Un autre sous-titre}
{title: Un sous titre}
{language: english}
{by: Traditionnel}
{album: Angleterre}
{columns: 2}
{cover: traditionnel}
{partition: greensleeves.ly}
========
{start_of_verse}
A[Am]las, my love, ye [G]do me wrong
To [Am]cast me oft dis[E]curteously
And [Am]I have loved [G]you so long
De[Am]lighting [E]in your [Am]companie
{end_of_verse}
{start_of_chorus}
[C]Green[B]sleeves was [G]all my joy
[Am]Greensleeves was [E]my delight
[C]Greensleeves was my [G]heart of gold
And [Am]who but [E]Ladie [Am]Greensleeves
{end_of_chorus}
{start_of_verse}
I [Am]have been ready [G]at your hand
To [Am]grant what ever [E]you would crave
I [Am]have both waged [G]life and land
Your [Am]love and [E]good will [Am]for to have
{end_of_verse}
{start_of_verse}
I [Am]bought thee kerchers [G]to thy head
That [Am]were wrought fine and [E]gallantly
I [Am]kept thee both at [G]boord and bed
Which [Am]cost my [E]purse well [Am]favouredly
{end_of_verse}
{start_of_verse}
I [Am]bought thee peticotes [G]of the best
The [Am]cloth so fine as [E]fine might be
I [Am]gave thee jewels [G]for thy chest
And [Am]all this [E]cost I [Am]spent on thee
{end_of_verse}
{start_of_verse}
Thy [Am]smock of silke, both [G]faire and white
With [Am]gold embrodered [E]gorgeously
Thy [Am]peticote of [G]sendall right
And [Am]this I [E]bought thee [Am]gladly
{end_of_verse}

20
patacrep/songs/chordpro/test/metadata.sgc

@ -0,0 +1,20 @@
{subtitle: Subtitle3}
{title: Title}
{title: Subtitle1}
{subtitle: Subtitle4}
{t: Subtitle2}
{st: Subtitle5}
{language: french}
{language: english}
{by: Author1}
{artist: Author2}
{album: Albom}
{copyright: Copyright}
{cover: Cover}
{vcover: VCover}
{capo: Capo}
{foo: Foo}
{comment: Comment}
{guitar_comment: GuitarComment}
{image: Image}
{lilypond: Lilypond}

21
patacrep/songs/chordpro/test/metadata.txt

@ -0,0 +1,21 @@
{title: Title}
{title: Subtitle1}
{title: Subtitle2}
{title: Subtitle3}
{title: Subtitle4}
{title: Subtitle5}
{language: english}
{language: french}
{by: Author1}
{by: Author2}
{album: Albom}
{capo: Capo}
{copyright: Copyright}
{cover: Cover}
{foo: Foo}
{vcover: VCover}
========
{comment: Comment}
{guitar_comment: GuitarComment}
{image: Image}
{lilypond: Lilypond}

36
patacrep/songs/chordpro/test/test_parser.py

@ -0,0 +1,36 @@
import glob
import os
import unittest
from patacrep.songs.chordpro import syntax as chordpro
class ParserTestCase(unittest.TestCase):
def test_txt(self):
for txt in sorted(glob.glob(os.path.join(
os.path.dirname(__file__),
'*.txt',
))):
basename = txt[:-len('.txt')]
with open("{}.sgc".format(basename), 'r', encoding='utf8') as sourcefile:
with open("{}.txt".format(basename), 'r', encoding='utf8') as expectfile:
#print(os.path.basename(sourcefile.name))
#with open("{}.txt.diff".format(basename), 'w', encoding='utf8') as difffile:
# difffile.write(
# str(chordpro.parse_song(
# sourcefile.read(),
# os.path.basename(sourcefile.name),
# )).strip()
# )
# sourcefile.seek(0)
self.assertMultiLineEqual(
str(chordpro.parse_song(
sourcefile.read(),
os.path.basename(sourcefile.name),
)).strip(),
expectfile.read().strip(),
)
def test_tex(self):
# TODO
pass
Loading…
Cancel
Save