Source code for ly.lex.lilypond

# This file is part of python-ly, https://pypi.python.org/pypi/python-ly
#
# Copyright (c) 2008 - 2015 by Wilbert Berendsen
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
# See http://www.gnu.org/licenses/ for more information.

"""
Parses and tokenizes LilyPond input.
"""

from __future__ import unicode_literals

import itertools

from . import _token
from . import Parser, FallthroughParser

# an identifier allowing letters and single hyphens in between
re_identifier = r"[^\W\d_]+([_-][^\W\d_]+)*"

# the lookahead pattern for the end of an identifier (ref)
re_identifier_end = r"(?![_-]?[^\W\d])"

re_articulation = r"[-_^][_.>|!+^-]"
re_dynamic = (
    r"\\[<!>]|"
    r"\\(f{1,5}|p{1,5}"
    r"|mf|mp|fp|spp?|sff?|sfz|rfz|n"
    r"|cresc|decresc|dim|cr|decr"
    r")(?![A-Za-z])")

re_duration = rf"(\\(maxima|longa|breve){re_identifier_end}|(1|2|4|8|16|32|64|128|256|512|1024|2048)(?!\d))"

re_dot = r"\."
re_scaling = r"\*[\t ]*\d+(/\d+)?"



[docs] class Identifier(_token.Token): """A variable name, like ``some-variable``.""" rx = r"(?<![^\W\d])" + re_identifier + re_identifier_end
[docs] class IdentifierRef(_token.Token): r"""A reference to an identifier, e.g. ``\some-variable``.""" rx = r"\\" + re_identifier + re_identifier_end
[docs] class Variable(Identifier): pass
[docs] class UserVariable(Identifier): pass
[docs] class Value(_token.Item, _token.Numeric): pass
[docs] class DecimalValue(Value): rx = r"-?\d+(\.\d+)?"
[docs] class IntegerValue(DecimalValue): rx = r"\d+"
[docs] class Fraction(Value): rx = r"\d+/\d+"
[docs] class Delimiter(_token.Token): pass
[docs] class DotPath(Delimiter): """A dot in dotted path notation.""" rx = r"\."
[docs] class Error(_token.Error): pass
[docs] class Comment(_token.Comment): pass
[docs] class BlockCommentStart(Comment, _token.BlockCommentStart): rx = r"%{"
[docs] def update_state(self, state): state.enter(ParseBlockComment())
[docs] class BlockCommentEnd(Comment, _token.BlockCommentEnd, _token.Leaver): rx = r"%}"
[docs] class BlockComment(Comment, _token.BlockComment): pass
[docs] class LineComment(Comment, _token.LineComment): rx = r"%.*$"
[docs] class String(_token.String): pass
[docs] class StringQuotedStart(String, _token.StringStart): rx = r'"'
[docs] def update_state(self, state): state.enter(ParseString())
[docs] class StringQuotedEnd(String, _token.StringEnd): rx = r'"'
[docs] def update_state(self, state): state.leave() state.endArgument()
[docs] class StringQuoteEscape(_token.Character): rx = r'\\[\\"]'
[docs] class MusicItem(_token.Token): r"""A note, rest, spacer, ``\skip`` or ``q``."""
[docs] class Spacer(MusicItem): rx = r"s(?![A-Za-z])"
[docs] class Rest(MusicItem): rx = r"[Rr](?![A-Za-z])"
[docs] class Note(MusicItem): rx = r"[a-x]+(?![A-Za-z])"
[docs] class Q(MusicItem): rx = r"q(?![A-Za-z])"
[docs] class DrumNote(MusicItem): rx = r"[a-z]+(?![A-Za-z])"
[docs] class Octave(_token.Token): rx = r",+|'+"
[docs] class OctaveCheck(_token.Token): rx = r"=(,+|'+)?"
[docs] class Accidental(_token.Token): pass
[docs] class AccidentalReminder(Accidental): rx = r"!"
[docs] class AccidentalCautionary(Accidental): rx = r"\?"
[docs] class Duration(_token.Token): pass
[docs] class Length(Duration): rx = re_duration
[docs] def update_state(self, state): state.enter(ParseDuration())
[docs] class Dot(Duration): rx = re_dot
[docs] class Scaling(Duration): rx = re_scaling
[docs] class OpenBracket(Delimiter, _token.MatchStart, _token.Indent): """An open bracket, does not enter different parser, subclass or reimplement Parser.update_state().""" rx = r"\{" matchname = "bracket"
[docs] class CloseBracket(Delimiter, _token.MatchEnd, _token.Dedent): rx = r"\}" matchname = "bracket"
[docs] def update_state(self, state): state.leave() state.endArgument()
[docs] class OpenSimultaneous(Delimiter, _token.MatchStart, _token.Indent): """An open double French quote, does not enter different parser, subclass or reimplement Parser.update_state().""" rx = r"<<" matchname = "simultaneous"
[docs] class CloseSimultaneous(Delimiter, _token.MatchEnd, _token.Dedent): rx = r">>" matchname = "simultaneous"
[docs] def update_state(self, state): state.leave() state.endArgument()
[docs] class SequentialStart(OpenBracket):
[docs] def update_state(self, state): state.enter(ParseMusic())
[docs] class SequentialEnd(CloseBracket): pass
[docs] class SimultaneousStart(OpenSimultaneous):
[docs] def update_state(self, state): state.enter(ParseMusic())
[docs] class SimultaneousEnd(CloseSimultaneous): pass
[docs] class PipeSymbol(Delimiter): rx = r"\|"
[docs] class Articulation(_token.Token): """Base class for articulation things."""
[docs] class ArticulationCommand(Articulation, IdentifierRef):
[docs] @classmethod def test_match(cls, match): s = match.group()[1:] if '-' not in s: from .. import words for l in ( words.articulations, words.ornaments, words.fermatas, words.instrument_scripts, words.repeat_scripts, words.ancient_scripts, ): if s in l: return True return False
[docs] class Direction(_token.Token): rx = r"[-_^]"
[docs] def update_state(self, state): state.enter(ParseScriptAbbreviationOrFingering())
[docs] class ScriptAbbreviation(Articulation, _token.Leaver): rx = r"[+|!>._^-]"
[docs] class Fingering(Articulation, _token.Leaver): rx = r"\d+"
[docs] class StringNumber(Articulation): rx = r"\\\d+"
[docs] class Slur(_token.Token): pass
[docs] class SlurStart(Slur, _token.MatchStart): rx = r"\(" matchname = "slur"
[docs] class SlurEnd(Slur, _token.MatchEnd): rx = r"\)" matchname = "slur"
[docs] class PhrasingSlurStart(SlurStart): rx = r"\\\(" matchname = "phrasingslur"
[docs] class PhrasingSlurEnd(SlurEnd): rx = r"\\\)" matchname = "phrasingslur"
[docs] class Tie(Slur): rx = r"~"
[docs] class Beam(_token.Token): pass
[docs] class BeamStart(Beam, _token.MatchStart): rx = r"\[" matchname = "beam"
[docs] class BeamEnd(Beam, _token.MatchEnd): rx = r"\]" matchname = "beam"
[docs] class Ligature(_token.Token): pass
[docs] class LigatureStart(Ligature, _token.MatchStart): rx = r"\\\[" matchname = "ligature"
[docs] class LigatureEnd(Ligature, _token.MatchEnd): rx = r"\\\]" matchname = "ligature"
[docs] class Tremolo(_token.Token): pass
[docs] class TremoloColon(Tremolo): rx = r":"
[docs] def update_state(self, state): state.enter(ParseTremolo())
[docs] class TremoloDuration(Tremolo, _token.Leaver): rx = r"\b(8|16|32|64|128|256|512|1024|2048)(?!\d)"
[docs] class ChordItem(_token.Token): """Base class for chordmode items."""
[docs] class ChordModifier(ChordItem): rx = r"((?<![a-z])|^)(aug|dim|sus|min|maj|m)(?![a-z])"
[docs] class ChordSeparator(ChordItem): rx = r":|\^|/\+?"
[docs] class ChordStepNumber(ChordItem): rx = r"\d+[-+]?"
[docs] class DotChord(ChordItem): rx = r"\."
[docs] class VoiceSeparator(Delimiter): rx = r"\\\\"
[docs] class Dynamic(_token.Token): rx = re_dynamic
[docs] class Command(_token.Item, IdentifierRef):
[docs] @classmethod def test_match(cls, match): s = match.group()[1:] if '-' not in s: from .. import words return s in words.lilypond_music_commands return False
[docs] class Keyword(_token.Item, IdentifierRef):
[docs] @classmethod def test_match(cls, match): s = match.group()[1:] if '-' not in s: from .. import words return s in words.lilypond_keywords return False
[docs] class Specifier(_token.Token): # a specifier of a command e.g. the name of clef or repeat style. pass
[docs] class Score(Keyword): rx = r"\\score" + re_identifier_end
[docs] def update_state(self, state): state.enter(ExpectScore())
[docs] class Book(Keyword): rx = r"\\book" + re_identifier_end
[docs] def update_state(self, state): state.enter(ExpectBook())
[docs] class BookPart(Keyword): rx = r"\\bookpart" + re_identifier_end
[docs] def update_state(self, state): state.enter(ExpectBookPart())
[docs] class Paper(Keyword): rx = r"\\paper" + re_identifier_end
[docs] def update_state(self, state): state.enter(ExpectPaper())
[docs] class Layout(Keyword): rx = r"\\layout" + re_identifier_end
[docs] def update_state(self, state): state.enter(ExpectLayout())
[docs] class Midi(Keyword): rx = r"\\midi" + re_identifier_end
[docs] def update_state(self, state): state.enter(ExpectMidi())
[docs] class With(Keyword): rx = r"\\with" + re_identifier_end
[docs] def update_state(self, state): state.enter(ExpectWith())
[docs] class LayoutContext(Keyword): rx = r"\\context" + re_identifier_end
[docs] def update_state(self, state): state.enter(ExpectContext())
[docs] class Markup(_token.Item): """Base class for all markup commands."""
[docs] class MarkupStart(Markup, Command): rx = r"\\markup" + re_identifier_end
[docs] def update_state(self, state): state.enter(ParseMarkup(1))
[docs] class MarkupLines(Markup): rx = r"\\markuplines" + re_identifier_end
[docs] def update_state(self, state): state.enter(ParseMarkup(1))
[docs] class MarkupList(Markup): rx = r"\\markuplist" + re_identifier_end
[docs] def update_state(self, state): state.enter(ParseMarkup(1))
[docs] class MarkupCommand(Markup, IdentifierRef): """A markup command."""
[docs] @classmethod def test_match(cls, match): from .. import words return match.group()[1:] in words.markupcommands
[docs] def update_state(self, state): from .. import words command = self[1:] if command in words.markupcommands_nargs[0]: state.endArgument() else: for argcount in 2, 3, 4, 5: if command in words.markupcommands_nargs[argcount]: break else: argcount = 1 state.enter(ParseMarkup(argcount))
[docs] class MarkupScore(Markup): rx = r"\\score" + re_identifier_end
[docs] def update_state(self, state): state.enter(ExpectScore())
[docs] class MarkupUserCommand(Markup, IdentifierRef): """A user-defined markup (i.e. not in the words markupcommands list)."""
[docs] def update_state(self, state): state.endArgument()
[docs] class MarkupWord(_token.Item): rx = r'[^{}"\\\s#%]+'
[docs] class OpenBracketMarkup(OpenBracket):
[docs] def update_state(self, state): state.enter(ParseMarkup())
[docs] class CloseBracketMarkup(CloseBracket):
[docs] def update_state(self, state): # go back to the opening bracket, this is the ParseMarkup # parser with the 0 argcount while state.parser().argcount > 0: state.leave() state.leave() state.endArgument()
[docs] class Repeat(Command): rx = r"\\repeat" + re_identifier_end
[docs] def update_state(self, state): state.enter(ParseRepeat())
[docs] class RepeatSpecifier(Specifier): @_token.patternproperty def rx(): from .. import words return r"\b({0})(?![A-Za-z])".format("|".join(words.repeat_types))
[docs] class RepeatCount(IntegerValue, _token.Leaver): pass
[docs] class Tempo(Command): rx = r"\\tempo" + re_identifier_end
[docs] def update_state(self, state): state.enter(ParseTempo())
[docs] class TempoSeparator(Delimiter): rx = r"[-~](?=\s*\d)"
[docs] class Partial(Command): rx = r"\\partial" + re_identifier_end
[docs] class Override(Keyword): rx = r"\\override" + re_identifier_end
[docs] def update_state(self, state): state.enter(ParseOverride())
[docs] class Set(Override): rx = r"\\set" + re_identifier_end
[docs] def update_state(self, state): state.enter(ParseSet())
[docs] class Revert(Override): rx = r"\\revert" + re_identifier_end
[docs] def update_state(self, state): state.enter(ParseRevert())
[docs] class Unset(Keyword): rx = r"\\unset" + re_identifier_end
[docs] def update_state(self, state): state.enter(ParseUnset())
[docs] class Tweak(Keyword): rx = r"\\tweak" + re_identifier_end
[docs] def update_state(self, state): state.enter(ParseTweak())
[docs] class Translator(Command):
[docs] def update_state(self, state): state.enter(ParseTranslator())
[docs] class New(Translator): rx = r"\\new" + re_identifier_end
[docs] class Context(Translator): rx = r"\\context" + re_identifier_end
[docs] class Change(Translator): rx = r"\\change" + re_identifier_end
[docs] class AccidentalStyle(Command): rx = r"\\accidentalStyle" + re_identifier_end
[docs] def update_state(self, state): state.enter(ParseAccidentalStyle())
[docs] class AccidentalStyleSpecifier(Specifier): @_token.patternproperty def rx(): from .. import words return r"\b({0})(?!-?\w)".format("|".join(words.accidentalstyles))
[docs] class AlterBroken(Command): rx = r"\\alterBroken" + re_identifier_end
[docs] def update_state(self, state): state.enter(ParseAlterBroken())
[docs] class Clef(Command): rx = r"\\clef" + re_identifier_end
[docs] def update_state(self, state): state.enter(ParseClef())
[docs] class ClefSpecifier(Specifier): @_token.patternproperty def rx(): from .. import words return r"\b({0})\b".format("|".join(words.clefs_plain))
[docs] def update_state(self, state): state.leave()
[docs] class PitchCommand(Command): rx = r"\\(relative|transpose|transposition|key|octaveCheck)" + re_identifier_end
[docs] def update_state(self, state): argcount = 2 if self == '\\transpose' else 1 state.enter(ParsePitchCommand(argcount))
[docs] class KeySignatureMode(Command): @_token.patternproperty def rx(): from .. import words return r"\\({0})(?![A-Za-z])".format("|".join(words.modes))
[docs] class Hide(Keyword): rx = r"\\hide" + re_identifier_end
[docs] def update_state(self, state): state.enter(ParseHideOmit())
[docs] class Omit(Keyword): rx = r"\\omit" + re_identifier_end
[docs] def update_state(self, state): state.enter(ParseHideOmit())
[docs] class Unit(Command): rx = r"\\(mm|cm|in|pt|bp)" + re_identifier_end
[docs] class InputMode(Command): pass
[docs] class LyricMode(InputMode): rx = r"\\(lyricmode|((old)?add)?lyrics|lyricsto)" + re_identifier_end
[docs] def update_state(self, state): state.enter(ExpectLyricMode())
[docs] class Lyric(_token.Item): """Base class for Lyric items."""
[docs] class LyricText(Lyric): rx = r"[^\\\s\d\"]+"
[docs] class LyricHyphen(Lyric): rx = r"--(?=($|[\s\\]))"
[docs] class LyricExtender(Lyric): rx = r"__(?=($|[\s\\]))"
[docs] class LyricSkip(Lyric): rx = r"_(?=($|[\s\\]))"
[docs] class Figure(_token.Token): """Base class for Figure items."""
[docs] class FigureStart(Figure): rx = r"<"
[docs] def update_state(self, state): state.enter(ParseFigure())
[docs] class FigureEnd(Figure, _token.Leaver): rx = r">"
[docs] class FigureBracket(Figure): rx = r"[][]"
[docs] class FigureStep(Figure): """A step figure number or the underscore.""" rx = r"_|\d+"
[docs] class FigureAccidental(Figure): """A figure accidental.""" rx = r"[-+!]+"
[docs] class FigureModifier(Figure): """A figure modifier.""" rx = r"\\[\\!+]|/"
[docs] class NoteMode(InputMode): rx = r"\\(notes|notemode)" + re_identifier_end
[docs] def update_state(self, state): state.enter(ExpectNoteMode())
[docs] class ChordMode(InputMode): rx = r"\\(chords|chordmode)" + re_identifier_end
[docs] def update_state(self, state): state.enter(ExpectChordMode())
[docs] class DrumMode(InputMode): rx = r"\\(drums|drummode)" + re_identifier_end
[docs] def update_state(self, state): state.enter(ExpectDrumMode())
[docs] class FigureMode(InputMode): rx = r"\\(figures|figuremode)"+ re_identifier_end
[docs] def update_state(self, state): state.enter(ExpectFigureMode())
[docs] class UserCommand(IdentifierRef): pass
[docs] class SimultaneousOrSequentialCommand(Keyword): rx = r"\\(simultaneous|sequential)" + re_identifier_end
[docs] class SchemeStart(_token.Item): rx = "[#$](?![{}])"
[docs] def update_state(self, state): from . import scheme state.enter(scheme.ParseScheme(1))
[docs] class ContextName(_token.Token): @_token.patternproperty def rx(): from .. import words return r"\b({0})\b".format("|".join(words.contexts))
[docs] class BackSlashedContextName(ContextName): @_token.patternproperty def rx(): from .. import words return r"\\({0})\b".format("|".join(words.contexts))
[docs] class GrobName(_token.Token): @_token.patternproperty def rx(): from .. import data return r"\b({0})\b".format("|".join(data.grobs()))
[docs] class GrobProperty(Variable): rx = r"\b([a-z]+|[XY])(-([a-z]+|[XY]))*(?![\w])"
[docs] class ContextProperty(Variable): @_token.patternproperty def rx(): from .. import data return r"\b({0})\b".format("|".join(data.context_properties()))
[docs] class PaperVariable(Variable): """A variable inside Paper. Always follow this one by UserVariable."""
[docs] @classmethod def test_match(cls, match): from .. import words return match.group() in words.papervariables
[docs] class HeaderVariable(Variable): """A variable inside Header. Always follow this one by UserVariable."""
[docs] @classmethod def test_match(cls, match): from .. import words return match.group() in words.headervariables
[docs] class LayoutVariable(Variable): """A variable inside Header. Always follow this one by UserVariable."""
[docs] @classmethod def test_match(cls, match): from .. import words return match.group() in words.layoutvariables
[docs] class Chord(_token.Token): """Base class for Chord delimiters.""" pass
[docs] class ChordStart(Chord): rx = r"<"
[docs] def update_state(self, state): state.enter(ParseChord())
[docs] class ChordEnd(Chord, _token.Leaver): rx = r">"
[docs] class DrumChordStart(ChordStart):
[docs] def update_state(self, state): state.enter(ParseDrumChord())
[docs] class DrumChordEnd(ChordEnd): pass
[docs] class ErrorInChord(Error): rx = "|".join(( re_articulation, # articulation r"<<|>>", # double french quotes r"\\[\\\]\[\(\)()]", # slurs beams re_duration, # duration re_scaling, # scaling ))
[docs] class Name(UserVariable): r"""A variable name without \ prefix."""
[docs] class EqualSign(_token.Token): rx = r"="
# Parsers
[docs] class ParseLilyPond(Parser): mode = 'lilypond'
# basic stuff that can appear everywhere space_items = ( _token.Space, BlockCommentStart, LineComment, ) base_items = space_items + ( SchemeStart, StringQuotedStart, ) # items that represent commands in both toplevel and music mode command_items = ( Repeat, PitchCommand, Override, Revert, Set, Unset, Hide, Omit, Tweak, New, Context, Change, With, Clef, Tempo, Partial, KeySignatureMode, AccidentalStyle, AlterBroken, SimultaneousOrSequentialCommand, ChordMode, DrumMode, FigureMode, LyricMode, NoteMode, MarkupStart, MarkupLines, MarkupList, ArticulationCommand, Keyword, Command, SimultaneousOrSequentialCommand, UserCommand, ) # items that occur in toplevel, book, bookpart or score # no Leave-tokens! toplevel_base_items = base_items + ( SequentialStart, SimultaneousStart, ) + command_items # items that occur in music expressions music_items = base_items + ( Dynamic, Skip, Spacer, Q, Rest, Note, Fraction, Length, Octave, OctaveCheck, AccidentalCautionary, AccidentalReminder, PipeSymbol, VoiceSeparator, SequentialStart, SequentialEnd, SimultaneousStart, SimultaneousEnd, ChordStart, ContextName, GrobName, SlurStart, SlurEnd, PhrasingSlurStart, PhrasingSlurEnd, Tie, BeamStart, BeamEnd, LigatureStart, LigatureEnd, Direction, StringNumber, IntegerValue, ) + command_items # items that occur inside chords music_chord_items = ( ErrorInChord, ChordEnd, ) + music_items
[docs] class ParseGlobal(ParseLilyPond): """Parses LilyPond from the toplevel of a file.""" items = ( Book, BookPart, Score, MarkupStart, MarkupLines, MarkupList, Paper, Header, Layout, ) + toplevel_base_items + ( Name, DotPath, EqualSign, Fraction, DecimalValue, )
[docs] def update_state(self, state, token): if isinstance(token, EqualSign): state.enter(ParseGlobalAssignment())
[docs] class ParseGlobalAssignment(FallthroughParser, ParseLilyPond): items = space_items + ( Skip, Spacer, Q, Rest, Note, Length, Fraction, DecimalValue, Direction, StringNumber, Dynamic, )
[docs] class ExpectOpenBracket(FallthroughParser, ParseLilyPond): """Waits for an OpenBracket and then replaces the parser with the class set in the replace attribute. Subclass this to set the destination for the OpenBracket. """ default = Error items = space_items + ( OpenBracket, )
[docs] def update_state(self, state, token): if isinstance(token, OpenBracket): state.replace(self.replace())
[docs] class ExpectMusicList(FallthroughParser, ParseLilyPond): """Waits for an OpenBracket or << and then replaces the parser with the class set in the replace attribute. Subclass this to set the destination for the OpenBracket. """ items = space_items + ( OpenBracket, OpenSimultaneous, SimultaneousOrSequentialCommand, )
[docs] def update_state(self, state, token): if isinstance(token, (OpenBracket, OpenSimultaneous)): state.replace(self.replace())
[docs] class ParseScore(ParseLilyPond): r"""Parses the expression after ``\score {``, leaving at ``}`` """ items = ( CloseBracket, Header, Layout, Midi, With, ) + toplevel_base_items
[docs] class ExpectScore(ExpectOpenBracket): replace = ParseScore
[docs] class ParseBook(ParseLilyPond): r"""Parses the expression after ``\book {``, leaving at ``}`` """ items = ( CloseBracket, MarkupStart, MarkupLines, MarkupList, BookPart, Score, Paper, Header, Layout, ) + toplevel_base_items
[docs] class ExpectBook(ExpectOpenBracket): replace = ParseBook
[docs] class ParseBookPart(ParseLilyPond): r"""Parses the expression after ``\bookpart {``, leaving at ``}`` """ items = ( CloseBracket, MarkupStart, MarkupLines, MarkupList, Score, Paper, Header, Layout, ) + toplevel_base_items
[docs] class ExpectBookPart(ExpectOpenBracket): replace = ParseBookPart
[docs] class ParsePaper(ParseLilyPond): r"""Parses the expression after ``\paper {``, leaving at ``}`` """ items = base_items + ( CloseBracket, MarkupStart, MarkupLines, MarkupList, PaperVariable, UserVariable, EqualSign, DotPath, DecimalValue, Unit, )
[docs] class ExpectPaper(ExpectOpenBracket): replace = ParsePaper
[docs] class ParseHeader(ParseLilyPond): r"""Parses the expression after ``\header {``, leaving at ``}`` """ items = ( CloseBracket, MarkupStart, MarkupLines, MarkupList, HeaderVariable, UserVariable, EqualSign, DotPath, ) + toplevel_base_items
[docs] class ExpectHeader(ExpectOpenBracket): replace = ParseHeader
[docs] class ParseLayout(ParseLilyPond): r"""Parses the expression after ``\layout {``, leaving at ``}`` """ items = base_items + ( CloseBracket, LayoutContext, LayoutVariable, UserVariable, EqualSign, DotPath, DecimalValue, Unit, ContextName, GrobName, ) + command_items
[docs] class ExpectLayout(ExpectOpenBracket): replace = ParseLayout
[docs] class ParseMidi(ParseLilyPond): r"""Parses the expression after ``\midi {``, leaving at ``}`` """ items = base_items + ( CloseBracket, LayoutContext, LayoutVariable, UserVariable, EqualSign, DotPath, DecimalValue, Unit, ContextName, GrobName, ) + command_items
[docs] class ExpectMidi(ExpectOpenBracket): replace = ParseMidi
[docs] class ParseWith(ParseLilyPond): r"""Parses the expression after ``\with {``, leaving at ``}`` """ items = ( CloseBracket, ContextName, GrobName, ContextProperty, EqualSign, DotPath, ) + toplevel_base_items
[docs] class ExpectWith(ExpectOpenBracket): replace = ParseWith
[docs] class ParseContext(ParseLilyPond): r"""Parses the expression after (``\layout {``) ``\context {``, leaving at ``}`` """ items = ( CloseBracket, BackSlashedContextName, ContextProperty, EqualSign, DotPath, ) + toplevel_base_items
[docs] class ExpectContext(ExpectOpenBracket): replace = ParseContext
[docs] class ParseMusic(ParseLilyPond): """Parses LilyPond music expressions.""" items = music_items + ( TremoloColon, )
[docs] class ParseChord(ParseMusic): """LilyPond inside chords ``< >``""" items = music_chord_items
[docs] class ParseString(Parser): default = String items = ( StringQuotedEnd, StringQuoteEscape, )
[docs] class ParseBlockComment(Parser): default = BlockComment items = ( BlockCommentEnd, )
[docs] class ParseMarkup(Parser): items = ( MarkupScore, MarkupCommand, MarkupUserCommand, OpenBracketMarkup, CloseBracketMarkup, MarkupWord, ) + base_items
[docs] class ParseRepeat(FallthroughParser): items = space_items + ( RepeatSpecifier, StringQuotedStart, RepeatCount, )
[docs] class ParseTempo(FallthroughParser): items = space_items + ( MarkupStart, StringQuotedStart, SchemeStart, Length, EqualSign, )
[docs] def update_state(self, state, token): if isinstance(token, EqualSign): state.replace(ParseTempoAfterEqualSign())
[docs] class ParseTempoAfterEqualSign(FallthroughParser): items = space_items + ( IntegerValue, TempoSeparator, )
[docs] class ParseDuration(FallthroughParser): items = space_items + ( Dot, )
[docs] def fallthrough(self, state): state.replace(ParseDurationScaling())
[docs] class ParseDurationScaling(ParseDuration): items = space_items + ( Scaling, )
[docs] def fallthrough(self, state): state.leave()
[docs] class ParseOverride(ParseLilyPond): argcount = 0 items = ( ContextName, DotPath, GrobName, GrobProperty, EqualSign, ) + base_items
[docs] def update_state(self, state, token): if isinstance(token, EqualSign): state.replace(ParseDecimalValue())
[docs] class ParseRevert(FallthroughParser): r"""parse the arguments of ``\revert``""" # allow both the old scheme syntax but also the dotted 2.18+ syntax # allow either a dot between the GrobName and the property path or not # correctly fall through when one property path has been parsed # (uses ParseGrobPropertyPath and ExpectGrobProperty) # (When the old scheme syntax is used this parser also falls through, # assuming that the previous parser will handle it) items = space_items + ( ContextName, DotPath, GrobName, GrobProperty, )
[docs] def update_state(self, state, token): if isinstance(token, GrobProperty): state.replace(ParseGrobPropertyPath())
[docs] class ParseGrobPropertyPath(FallthroughParser): items = space_items + ( DotPath, )
[docs] def update_state(self, state, token): if isinstance(token, DotPath): state.enter(ExpectGrobProperty())
[docs] class ExpectGrobProperty(FallthroughParser): items = space_items + ( GrobProperty, )
[docs] def update_state(self, state, token): if isinstance(token, GrobProperty): state.leave()
[docs] class ParseSet(ParseLilyPond): argcount = 0 items = ( ContextName, DotPath, ContextProperty, EqualSign, Name, ) + base_items
[docs] def update_state(self, state, token): if isinstance(token, EqualSign): state.replace(ParseDecimalValue())
[docs] class ParseUnset(FallthroughParser): items = space_items + ( ContextName, DotPath, ContextProperty, Name, )
[docs] def update_state(self, state, token): if isinstance(token, ContextProperty) or token[:1].islower(): state.leave()
[docs] class ParseTweak(FallthroughParser): items = space_items + ( GrobName, DotPath, GrobProperty, )
[docs] def update_state(self, state, token): if isinstance(token, GrobProperty): state.replace(ParseTweakGrobProperty())
[docs] class ParseTweakGrobProperty(FallthroughParser): items = space_items + ( DotPath, DecimalValue, )
[docs] def update_state(self, state, token): if isinstance(token, DotPath): state.enter(ExpectGrobProperty()) elif isinstance(token, DecimalValue): state.leave()
[docs] class ParseTranslator(FallthroughParser): items = space_items + ( ContextName, Name, )
[docs] def update_state(self, state, token): if isinstance(token, (Name, ContextName)): state.replace(ExpectTranslatorId())
[docs] class ExpectTranslatorId(FallthroughParser): items = space_items + ( EqualSign, )
[docs] def update_state(self, state, token): if token == '=': state.replace(ParseTranslatorId())
[docs] class ParseTranslatorId(FallthroughParser): argcount = 1 items = space_items + ( Name, StringQuotedStart, )
[docs] def update_state(self, state, token): if isinstance(token, Name): state.leave()
[docs] class ParseClef(FallthroughParser): argcount = 1 items = space_items + ( ClefSpecifier, StringQuotedStart, )
[docs] class ParseHideOmit(FallthroughParser): items = space_items + ( ContextName, DotPath, GrobName, )
[docs] def update_state(self, state, token): if isinstance(token, GrobName): state.leave()
[docs] class ParseAccidentalStyle(FallthroughParser): items = space_items + ( ContextName, DotPath, AccidentalStyleSpecifier, )
[docs] def update_state(self, state, token): if isinstance(token, AccidentalStyleSpecifier): state.leave()
[docs] class ParseAlterBroken(FallthroughParser): items = space_items + ( GrobProperty, )
[docs] def update_state(self, state, token): if isinstance(token, GrobProperty): state.replace(ParseGrobPropertyPath())
[docs] class ParseScriptAbbreviationOrFingering(FallthroughParser): argcount = 1 items = space_items + ( ScriptAbbreviation, Fingering, )
[docs] class ParseInputMode(ParseLilyPond): """Base class for parser for mode-changing music commands."""
[docs] @classmethod def update_state(cls, state, token): if isinstance(token, (OpenSimultaneous, OpenBracket)): state.enter(cls())
[docs] class ParseLyricMode(ParseInputMode): r"""Parser for ``\lyrics``, ``\lyricmode``, ``\addlyrics``, etc.""" items = base_items + ( CloseBracket, CloseSimultaneous, OpenBracket, OpenSimultaneous, PipeSymbol, LyricHyphen, LyricExtender, LyricSkip, LyricText, Dynamic, Skip, Length, MarkupStart, MarkupLines, MarkupList, ) + command_items
[docs] class ExpectLyricMode(ExpectMusicList): replace = ParseLyricMode items = space_items + ( OpenBracket, OpenSimultaneous, SchemeStart, StringQuotedStart, Name, SimultaneousOrSequentialCommand, )
[docs] class ParseChordMode(ParseInputMode, ParseMusic): r"""Parser for ``\chords`` and ``\chordmode``.""" items = ( OpenBracket, OpenSimultaneous, ) + music_items + ( # TODO: specify items exactly, e.g. < > is not allowed ChordSeparator, )
[docs] def update_state(self, state, token): if isinstance(token, ChordSeparator): state.enter(ParseChordItems()) else: super(ParseChordMode, self).update_state(state, token)
[docs] class ExpectChordMode(ExpectMusicList): replace = ParseChordMode
[docs] class ParseNoteMode(ParseMusic): r"""Parser for ``\notes`` and ``\notemode``. Same as Music itself."""
[docs] class ExpectNoteMode(ExpectMusicList): replace = ParseNoteMode
[docs] class ParseDrumChord(ParseMusic): """LilyPond inside chords in drummode ``< >``""" items = base_items + ( ErrorInChord, DrumChordEnd, Dynamic, Skip, Spacer, Q, Rest, DrumNote, Fraction, Length, PipeSymbol, VoiceSeparator, SequentialStart, SequentialEnd, SimultaneousStart, SimultaneousEnd, ChordStart, ContextName, GrobName, SlurStart, SlurEnd, PhrasingSlurStart, PhrasingSlurEnd, Tie, BeamStart, BeamEnd, LigatureStart, LigatureEnd, Direction, StringNumber, IntegerValue, ) + command_items
[docs] class ParseDrumMode(ParseInputMode, ParseMusic): r"""Parser for ``\drums`` and ``\drummode``.""" items = ( OpenBracket, OpenSimultaneous, ) + base_items + ( Dynamic, Skip, Spacer, Q, Rest, DrumNote, Fraction, Length, PipeSymbol, VoiceSeparator, SequentialStart, SequentialEnd, SimultaneousStart, SimultaneousEnd, DrumChordStart, ContextName, GrobName, SlurStart, SlurEnd, PhrasingSlurStart, PhrasingSlurEnd, Tie, BeamStart, BeamEnd, LigatureStart, LigatureEnd, Direction, StringNumber, IntegerValue, ) + command_items
[docs] class ExpectDrumMode(ExpectMusicList): replace = ParseDrumMode
[docs] class ParseFigureMode(ParseInputMode, ParseMusic): r"""Parser for ``\figures`` and ``\figuremode``.""" items = base_items + ( CloseBracket, CloseSimultaneous, OpenBracket, OpenSimultaneous, PipeSymbol, FigureStart, Skip, Spacer, Rest, Length, ) + command_items
[docs] class ParseFigure(Parser): """Parse inside ``< >`` in figure mode.""" items = base_items + ( FigureEnd, FigureBracket, FigureStep, FigureAccidental, FigureModifier, MarkupStart, MarkupLines, MarkupList, )
[docs] class ExpectFigureMode(ExpectMusicList): replace = ParseFigureMode
[docs] class ParsePitchCommand(FallthroughParser): argcount = 1 items = space_items + ( Note, Octave, )
[docs] def update_state(self, state, token): if isinstance(token, Note): self.argcount -= 1 elif isinstance(token, _token.Space) and self.argcount <= 0: state.leave()
[docs] class ParseTremolo(FallthroughParser): items = (TremoloDuration,)
[docs] class ParseChordItems(FallthroughParser): items = ( ChordSeparator, ChordModifier, ChordStepNumber, DotChord, Note, )
[docs] class ParseDecimalValue(FallthroughParser): """Parses a decimal value without a # before it (if present).""" items = space_items + ( Fraction, DecimalValue, )