Browse Source

Parsing works on tests

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

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