Browse Source

Directives are allowed only on their own line (with nothing else on this line)

Better error handling of the parser.
pull/112/head
Louis 10 years ago
parent
commit
33328e789f
  1. 6
      patacrep/data/examples/songs/tests/newline.sgc
  2. 6
      patacrep/songs/chordpro/ast.py
  3. 61
      patacrep/songs/chordpro/syntax.py
  4. 2
      test/test_chordpro/newline.html
  5. 2
      test/test_chordpro/newline.sgc
  6. 2
      test/test_chordpro/newline.source
  7. 2
      test/test_chordpro/newline.tex

6
patacrep/data/examples/songs/tests/newline.sgc

@ -32,4 +32,8 @@ New lines can also
{newline} {newline}
Be surrounded by spaces Be surrounded by spaces
New lines can {newline} appear in the middle of a line New lines cannot have text before them {newline}
{newline} New lines cannot have text after them
New lines cannot be {newline} surrounded by text.

6
patacrep/songs/chordpro/ast.py

@ -60,9 +60,9 @@ class Line(AST):
_template = "line" _template = "line"
def __init__(self): def __init__(self, *items):
super().__init__() super().__init__()
self.line = [] self.line = list(items)
def __iter__(self): def __iter__(self):
yield from self.line yield from self.line
@ -310,7 +310,7 @@ class Directive(AST):
return self.keyword return self.keyword
def __str__(self): def __str__(self):
return self.argument return str(self.argument)
@property @property
def inline(self): def inline(self):

61
patacrep/songs/chordpro/syntax.py

@ -1,5 +1,6 @@
"""ChordPro parser""" """ChordPro parser"""
import logging
import ply.yacc as yacc import ply.yacc as yacc
import re import re
@ -7,6 +8,8 @@ from patacrep.songs.syntax import Parser
from patacrep.songs.chordpro import ast from patacrep.songs.chordpro import ast
from patacrep.songs.chordpro.lexer import tokens, ChordProLexer from patacrep.songs.chordpro.lexer import tokens, ChordProLexer
LOGGER = logging.getLogger()
class ChordproParser(Parser): class ChordproParser(Parser):
"""ChordPro parser class""" """ChordPro parser class"""
# pylint: disable=too-many-public-methods # pylint: disable=too-many-public-methods
@ -18,6 +21,19 @@ class ChordproParser(Parser):
self.tokens = tokens self.tokens = tokens
self.song = ast.Song(filename) self.song = ast.Song(filename)
self.filename = filename self.filename = filename
self.parser = yacc.yacc(
module=self,
debug=0,
write_tables=0,
)
def parse(self, *args, **kwargs):
"""Parse file
This is a shortcut to `yacc.yacc(...).parse()`. The arguments are
transmitted to this method.
"""
return self.parser.parse(*args, **kwargs)
def p_song(self, symbols): def p_song(self, symbols):
"""song : block song """song : block song
@ -32,6 +48,7 @@ class ChordproParser(Parser):
def p_block(symbols): def p_block(symbols):
"""block : SPACE block """block : SPACE block
| line ENDOFLINE | line ENDOFLINE
| line_error ENDOFLINE
| chorus ENDOFLINE | chorus ENDOFLINE
| tab ENDOFLINE | tab ENDOFLINE
| bridge ENDOFLINE | bridge ENDOFLINE
@ -167,20 +184,35 @@ class ChordproParser(Parser):
else: else:
symbols[0] = None symbols[0] = None
@staticmethod
def p_line_error(symbols):
"""line_error : error directive"""
LOGGER.error("Directive can only be preceded or followed by spaces")
symbols[0] = ast.Line()
@staticmethod @staticmethod
def p_line(symbols): def p_line(symbols):
"""line : word line_next """line : word line_next
| chord line_next | chord line_next
| directive line_next | directive maybespace
""" """
symbols[0] = symbols[2].prepend(symbols[1]) if isinstance(symbols[2], ast.Line):
# Line with words, etc.
symbols[0] = symbols[2].prepend(symbols[1])
else:
# Directive
if symbols[1] is None:
# Meta directive. Nothing to do
symbols[0] = ast.Line()
else:
# Inline directive
symbols[0] = ast.Line(symbols[1])
@staticmethod @staticmethod
def p_line_next(symbols): def p_line_next(symbols):
"""line_next : word line_next """line_next : word line_next
| space line_next | space line_next
| chord line_next | chord line_next
| directive line_next
| empty | empty
""" """
if len(symbols) == 2: if len(symbols) == 2:
@ -212,6 +244,7 @@ class ChordproParser(Parser):
@staticmethod @staticmethod
def p_chorus_content(symbols): def p_chorus_content(symbols):
"""chorus_content : line ENDOFLINE chorus_content """chorus_content : line ENDOFLINE chorus_content
| line_error ENDOFLINE chorus_content
| SPACE chorus_content | SPACE chorus_content
| empty | empty
""" """
@ -231,6 +264,7 @@ class ChordproParser(Parser):
@staticmethod @staticmethod
def p_bridge_content(symbols): def p_bridge_content(symbols):
"""bridge_content : line ENDOFLINE bridge_content """bridge_content : line ENDOFLINE bridge_content
| line_error ENDOFLINE bridge_content
| SPACE bridge_content | SPACE bridge_content
| empty | empty
""" """
@ -267,13 +301,18 @@ class ChordproParser(Parser):
"""empty :""" """empty :"""
symbols[0] = None symbols[0] = None
def p_error(self, token):
super().p_error(token)
while True:
token = self.parser.token()
if not token or token.type == "ENDOFLINE":
break
self.parser.errok()
return token
def parse_song(content, filename=None): def parse_song(content, filename=None):
"""Parse song and return its metadata.""" """Parse song and return its metadata."""
return yacc.yacc( return ChordproParser(filename).parse(
module=ChordproParser(filename), content,
debug=0, lexer=ChordProLexer(filename=filename).lexer,
write_tables=0, )
).parse(
content,
lexer=ChordProLexer(filename=filename).lexer,
)

2
test/test_chordpro/newline.html

@ -45,6 +45,6 @@
</p> </p>
<p class="verse"> <p class="verse">
New lines can't appear in the middle of a line New lines cannot
</p> </p>
</div> </div>

2
test/test_chordpro/newline.sgc

@ -38,4 +38,4 @@ New lines can also
Be surrounded by spaces Be surrounded by spaces
New lines can {newline} appear in the middle of a line New lines cannot

2
test/test_chordpro/newline.source

@ -29,4 +29,4 @@ New lines can also
{newline} {newline}
Be surrounded by spaces Be surrounded by spaces
New lines can {newline} appear in the middle of a line New lines cannot {newline} appear in the middle of a line

2
test/test_chordpro/newline.tex

@ -55,7 +55,7 @@
\begin{verse} \begin{verse}
New lines can ~\\ appear in the middle of a line New lines cannot
\end{verse} \end{verse}
\endsong \endsong

Loading…
Cancel
Save