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


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

re_duration = r"(\\(maxima|longa|breve)\b|(1|2|4|8|16|32|64|128|256|512|1024|2048)(?!\d))"
re_dot = r"\."
re_scaling = r"\*[\t ]*\d+(/\d+)?"

# 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])"


[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\b"
[docs] def update_state(self, state): state.enter(ExpectScore())
[docs]class Book(Keyword): rx = r"\\book\b"
[docs] def update_state(self, state): state.enter(ExpectBook())
[docs]class BookPart(Keyword): rx = r"\\bookpart\b"
[docs] def update_state(self, state): state.enter(ExpectBookPart())
[docs]class Paper(Keyword): rx = r"\\paper\b"
[docs] def update_state(self, state): state.enter(ExpectPaper())
[docs]class Layout(Keyword): rx = r"\\layout\b"
[docs] def update_state(self, state): state.enter(ExpectLayout())
[docs]class Midi(Keyword): rx = r"\\midi\b"
[docs] def update_state(self, state): state.enter(ExpectMidi())
[docs]class With(Keyword): rx = r"\\with\b"
[docs] def update_state(self, state): state.enter(ExpectWith())
[docs]class LayoutContext(Keyword): rx = r"\\context\b"
[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\b"
[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(?![A-Za-z])"
[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\b"
[docs] def update_state(self, state): state.enter(ParseTempo())
[docs]class TempoSeparator(Delimiter): rx = r"[-~](?=\s*\d)"
[docs]class Partial(Command): rx = r"\\partial\b"
[docs]class Override(Keyword): rx = r"\\override\b"
[docs] def update_state(self, state): state.enter(ParseOverride())
[docs]class Set(Override): rx = r"\\set\b"
[docs] def update_state(self, state): state.enter(ParseSet())
[docs]class Revert(Override): rx = r"\\revert\b"
[docs] def update_state(self, state): state.enter(ParseRevert())
[docs]class Unset(Keyword): rx = r"\\unset\b"
[docs] def update_state(self, state): state.enter(ParseUnset())
[docs]class Tweak(Keyword): rx = r"\\tweak\b"
[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\b"
[docs]class Context(Translator): rx = r"\\context\b"
[docs]class Change(Translator): rx = r"\\change\b"
[docs]class AccidentalStyle(Command): rx = r"\\accidentalStyle\b"
[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\b"
[docs] def update_state(self, state): state.enter(ParseAlterBroken())
[docs]class Clef(Command): rx = r"\\clef\b"
[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)\b"
[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\b"
[docs] def update_state(self, state): state.enter(ParseHideOmit())
[docs]class Omit(Keyword): rx = r"\\omit\b"
[docs] def update_state(self, state): state.enter(ParseHideOmit())
[docs]class Unit(Command): rx = r"\\(mm|cm|in|pt)\b"
[docs]class InputMode(Command): pass
[docs]class LyricMode(InputMode): rx = r"\\(lyricmode|((old)?add)?lyrics|lyricsto)\b"
[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)\b"
[docs] def update_state(self, state): state.enter(ExpectNoteMode())
[docs]class ChordMode(InputMode): rx = r"\\(chords|chordmode)\b"
[docs] def update_state(self, state): state.enter(ExpectChordMode())
[docs]class DrumMode(InputMode): rx = r"\\(drums|drummode)\b"
[docs] def update_state(self, state): state.enter(ExpectDrumMode())
[docs]class FigureMode(InputMode): rx = r"\\(figures|figuremode)\b"
[docs] def update_state(self, state): state.enter(ExpectFigureMode())
[docs]class UserCommand(IdentifierRef): pass
[docs]class SimultaneousOrSequentialCommand(Keyword): rx = r"\\(simultaneous|sequential)\b"
[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, )