1
# This file is part of the Frescobaldi project, http://www.frescobaldi.org/
3
# Copyright (c) 2008 - 2011 by Wilbert Berendsen
5
# This program is free software; you can redistribute it and/or
6
# modify it under the terms of the GNU General Public License
7
# as published by the Free Software Foundation; either version 2
8
# of the License, or (at your option) any later version.
10
# This program is distributed in the hope that it will be useful,
11
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
# GNU General Public License for more details.
15
# You should have received a copy of the GNU General Public License
16
# along with this program; if not, write to the Free Software
17
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18
# See http://www.gnu.org/licenses/ for more information.
21
Parses and tokenizes LilyPond input.
24
from __future__ import unicode_literals
29
from . import Parser, FallthroughParser
32
re_articulation = r"[-_^][_.>|+^-]"
36
r"|mf|mp|fp|spp?|sff?|sfz|rfz"
37
r"|cresc|decresc|dim|cr|decr"
40
re_duration = r"(\\(maxima|longa|breve)\b|(1|2|4|8|16|32|64|128|256|512|1024|2048)(?!\d))"
42
re_scaling = r"\*[\t ]*\d+(/\d+)?"
45
class Variable(_token.Token):
49
class UserVariable(_token.Token):
53
class Value(_token.Item, _token.Numeric):
57
class DecimalValue(Value):
61
class Fraction(Value):
65
class Error(_token.Error):
69
class Comment(_token.Comment):
73
class BlockCommentStart(Comment, _token.BlockCommentStart, _token.Indent):
75
def updateState(self, state):
76
state.enter(ParseBlockComment())
79
class BlockCommentEnd(Comment, _token.BlockCommentEnd, _token.Leaver, _token.Dedent):
83
class BlockCommentSpace(Comment, _token.BlockComment, _token.Space):
87
class LineComment(Comment, _token.LineComment):
91
class String(_token.String):
95
class StringQuotedStart(String, _token.StringStart):
97
def updateState(self, state):
98
state.enter(ParseString())
101
class StringQuotedEnd(String, _token.StringEnd):
103
def updateState(self, state):
108
class StringQuoteEscape(_token.Character):
112
class Skip(_token.Token):
113
rx = r"s(?![A-Za-z])"
116
class Rest(_token.Token):
117
rx = r"[Rr](?![A-Za-z])"
120
class Note(_token.Token):
121
rx = r"[a-x]+(?![A-Za-z])"
124
class Octave(_token.Token):
128
class OctaveCheck(_token.Token):
132
class Accidental(_token.Token):
136
class AccidentalReminder(Accidental):
140
class AccidentalCautionary(Accidental):
144
class Duration(_token.Token):
148
class Length(Duration):
150
def updateState(self, state):
151
state.enter(ParseDuration())
158
class Scaling(Duration):
162
class Delimiter(_token.Token):
166
class OpenBracket(Delimiter, _token.MatchStart, _token.Indent):
167
"""An open bracket, does not enter different parser, subclass or reimplement Parser.updateState()."""
169
matchname = "bracket"
172
class CloseBracket(Delimiter, _token.MatchEnd, _token.Dedent):
174
matchname = "bracket"
175
def updateState(self, state):
180
class OpenSimultaneous(Delimiter, _token.MatchStart, _token.Indent):
181
"""An open double French quote, does not enter different parser, subclass or reimplement Parser.updateState()."""
183
matchname = "simultaneous"
186
class CloseSimultaneous(Delimiter, _token.MatchEnd, _token.Dedent):
188
matchname = "simultaneous"
189
def updateState(self, state):
194
class SequentialStart(OpenBracket):
195
def updateState(self, state):
196
state.enter(ParseMusic())
199
class SequentialEnd(CloseBracket):
203
class SimultaneousStart(OpenSimultaneous):
204
def updateState(self, state):
205
state.enter(ParseMusic())
208
class SimultaneousEnd(CloseSimultaneous):
212
class PipeSymbol(Delimiter):
216
class IntegerValue(Value):
220
class Articulation(_token.Token):
221
@_token.patternproperty
224
return r"\\({0})(?![A-Za-z])".format("|".join(itertools.chain(
228
words.instrument_scripts,
229
words.repeat_scripts,
230
words.ancient_scripts,
234
class Direction(Articulation):
236
def updateState(self, state):
237
state.enter(ParseScriptAbbreviationOrFingering())
240
class ScriptAbbreviation(Articulation, _token.Leaver):
244
class Fingering(Articulation, _token.Leaver):
248
class StringNumber(Articulation):
252
class Slur(_token.Token):
256
class SlurStart(Slur, _token.MatchStart):
261
class SlurEnd(Slur, _token.MatchEnd):
266
class PhrasingSlurStart(SlurStart):
268
matchname = "phrasingslur"
271
class PhrasingSlurEnd(SlurEnd):
273
matchname = "phrasingslur"
280
class Beam(_token.Token):
284
class BeamStart(Beam, _token.MatchStart):
289
class BeamEnd(Beam, _token.MatchEnd):
294
class Ligature(_token.Token):
298
class LigatureStart(Ligature, _token.MatchStart):
300
matchname = "ligature"
303
class LigatureEnd(Ligature, _token.MatchEnd):
305
matchname = "ligature"
308
class Keyword(_token.Item):
309
@_token.patternproperty
312
return r"\\({0})(?![A-Za-z])".format("|".join(words.lilypond_keywords))
315
class VoiceSeparator(Delimiter):
319
class Dynamic(_token.Token):
323
class Command(_token.Item):
324
@_token.patternproperty
327
return r"\\({0})(?![A-Za-z])".format("|".join(words.lilypond_music_commands))
330
class Specifier(_token.Token):
331
# a specifier of a command e.g. the name of clef or repeat style.
335
class Score(Keyword):
337
def updateState(self, state):
338
state.enter(ExpectScore())
343
def updateState(self, state):
344
state.enter(ExpectBook())
347
class BookPart(Keyword):
349
def updateState(self, state):
350
state.enter(ExpectBookPart())
353
class Paper(Keyword):
355
def updateState(self, state):
356
state.enter(ExpectPaper())
359
class Header(Keyword):
361
def updateState(self, state):
362
state.enter(ExpectHeader())
365
class Layout(Keyword):
367
def updateState(self, state):
368
state.enter(ExpectLayout())
373
def updateState(self, state):
374
state.enter(ExpectMidi())
379
def updateState(self, state):
380
state.enter(ExpectWith())
383
class LayoutContext(Keyword):
385
def updateState(self, state):
386
state.enter(ExpectContext())
389
class Markup(Command):
390
rx = r"\\markup(?![A-Za-z])"
391
def updateState(self, state):
392
state.enter(ParseMarkup(1))
395
class MarkupLines(Markup):
396
rx = r"\\markuplines(?![A-Za-z])"
397
def updateState(self, state):
398
state.enter(ParseMarkup(1))
401
class MarkupList(Markup):
402
rx = r"\\markuplist(?![A-Za-z])"
403
def updateState(self, state):
404
state.enter(ParseMarkup(1))
407
class MarkupCommand(Markup):
408
rx = r"\\[A-Za-z]+(-[A-Za-z]+)*(?![A-Za-z])"
409
def updateState(self, state):
412
if command in ly.words.markupcommands_nargs[0]:
415
for argcount in 2, 3, 4, 5:
416
if command in ly.words.markupcommands_nargs[argcount]:
420
state.enter(ParseMarkup(argcount))
423
class MarkupScore(Markup):
425
def updateState(self, state):
426
state.enter(ExpectScore())
429
class MarkupWord(_token.Item):
430
rx = r'[^{}"\\\s#%]+'
433
class OpenBracketMarkup(OpenBracket):
434
def updateState(self, state):
435
state.enter(ParseMarkup())
438
class CloseBracketMarkup(CloseBracket):
439
def updateState(self, state):
445
class Repeat(Command):
446
rx = r"\\repeat(?![A-Za-z])"
447
def updateState(self, state):
448
state.enter(ParseRepeat())
451
class RepeatSpecifier(Specifier):
452
@_token.patternproperty
455
return r"\b({0})(?![A-Za-z])".format("|".join(words.repeat_types))
458
class RepeatStringSpecifier(String, Specifier):
459
@_token.patternproperty
462
return r'"({0})"'.format("|".join(words.repeat_types))
465
class RepeatCount(IntegerValue, _token.Leaver):
469
class Override(Keyword):
471
def updateState(self, state):
472
state.enter(ParseOverride())
477
def updateState(self, state):
478
state.enter(ParseSet())
481
class Revert(Override):
483
def updateState(self, state):
484
state.enter(ParseRevert())
487
class DotSetOverride(Delimiter):
491
class Unset(Keyword):
493
def updateState(self, state):
494
state.enter(ParseUnset())
499
def updateState(self, state):
500
state.enter(ParseNewContext())
513
def updateState(self, state):
514
state.enter(ParseClef())
517
class ClefSpecifier(Specifier):
518
@_token.patternproperty
521
return r"\b({0})\b".format("|".join(words.clefs_plain))
523
def updateState(self, state):
527
class PitchCommand(Command):
528
rx = r"\\(relative|transpose|transposition|key|octaveCheck)\b"
529
def updateState(self, state):
530
argcount = 2 if self == '\\transpose' else 1
531
state.enter(ParsePitchCommand(argcount))
535
rx = r"\\(mm|cm|in|pt)\b"
538
class InputMode(Command):
542
class LyricMode(InputMode):
543
rx = r"\\(lyricmode|((old)?add)?lyrics|lyricsto)\b"
544
def updateState(self, state):
545
state.enter(ExpectLyricMode())
548
class Lyric(_token.Item):
549
"""Base class for Lyric items."""
552
class LyricText(Lyric):
553
rx = r"[^\\\s\d~\"]+"
556
class LyricHyphen(Lyric):
560
class LyricExtender(Lyric):
564
class LyricSkip(Lyric):
568
class LyricTie(Lyric):
572
class NoteMode(InputMode):
573
rx = r"\\(notes|notemode)\b"
574
def updateState(self, state):
575
state.enter(ExpectNoteMode())
578
class ChordMode(InputMode):
579
rx = r"\\(chords|chordmode)\b"
580
def updateState(self, state):
581
state.enter(ExpectChordMode())
584
class DrumMode(InputMode):
585
rx = r"\\(drums|drummode)\b"
586
def updateState(self, state):
587
state.enter(ExpectDrumMode())
590
class FigureMode(InputMode):
591
rx = r"\\(figures|figuremode)\b"
592
def updateState(self, state):
593
state.enter(ExpectFigureMode())
596
class UserCommand(_token.Token):
597
rx = r"\\[A-Za-z]+(?![A-Za-z])"
600
class SchemeStart(_token.Item):
602
def updateState(self, state):
604
state.enter(scheme.ParseScheme(1))
607
class ContextName(_token.Token):
608
@_token.patternproperty
611
return r"\b({0})\b".format("|".join(words.contexts))
614
class BackSlashedContextName(ContextName):
615
@_token.patternproperty
618
return r"\\({0})\b".format("|".join(words.contexts))
621
class GrobName(_token.Token):
622
@_token.patternproperty
625
return r"\b({0})\b".format("|".join(data.grobs()))
628
class ContextProperty(_token.Token):
629
@_token.patternproperty
632
return r"\b({0})\b".format("|".join(data.context_properties()))
635
class PaperVariable(Variable):
636
@_token.patternproperty
639
return r"\b({0})\b".format("|".join(words.papervariables))
642
class HeaderVariable(Variable):
643
@_token.patternproperty
646
return r"\b({0})\b".format("|".join(words.headervariables))
649
class LayoutVariable(Variable):
650
@_token.patternproperty
653
return r"\b({0})\b".format("|".join(words.layoutvariables))
656
class Chord(_token.Token):
657
"""Base class for Chord delimiters."""
661
class ChordStart(Chord):
663
def updateState(self, state):
664
state.enter(ParseChord())
667
class ChordEnd(Chord, _token.Leaver):
671
class ErrorInChord(Error):
673
re_articulation, # articulation
674
r"<<|>>", # double french quotes
675
r"\\[\\\]\[\(\)()]", # slurs beams
676
re_duration, # duration
677
re_scaling, # scaling
681
class Name(UserVariable):
682
"""A variable name without \\ prefix."""
683
rx = r"[a-zA-Z]+(?![a-zA-Z])"
686
class NameLower(Name):
687
"""A lowercase name."""
688
rx = r"[a-z]+(?![a-zA-Z])"
691
class NameHyphenLower(Name):
692
"""A lowercase name that may contain hyphens."""
693
rx = r"[a-z]+(-[a-z]+)*(!?[-a-zA-Z])"
696
class EqualSign(_token.Token):
700
class EqualSignSetOverride(EqualSign):
701
"""An equal sign in a set/override construct."""
702
def updateState(self, state):
703
# wait for one more expression, then leave
704
state.parser().argcount = 1
709
class ParseLilyPond(Parser):
712
# basic stuff that can appear everywhere
720
base_items = space_items + (
726
# items that represent commands in both toplevel and music mode
732
New, Context, Change,
735
ChordMode, DrumMode, FigureMode, LyricMode, NoteMode,
736
Markup, MarkupLines, MarkupList,
743
# items that occur in toplevel, book, bookpart or score
745
toplevel_base_items = base_items + (
752
# items that occur in music expressions
753
music_items = base_items + (
762
AccidentalCautionary,
766
SequentialStart, SequentialEnd,
767
SimultaneousStart, SimultaneousEnd,
772
PhrasingSlurStart, PhrasingSlurEnd,
775
LigatureStart, LigatureEnd,
782
# items that occur inside chords
783
music_chord_items = (
791
class ParseGlobal(ParseLilyPond):
792
"""Parses LilyPond from the toplevel of a file."""
793
# TODO: implement assignments
798
Markup, MarkupLines, MarkupList,
799
Paper, Header, Layout,
800
) + toplevel_base_items + (
806
class ExpectOpenBracket(ParseLilyPond):
807
"""Waits for an OpenBracket and then replaces the parser with the class set in the replace attribute.
809
Subclass this to set the destination for the OpenBracket.
813
items = space_items + (
816
def updateState(self, state, token):
817
if isinstance(token, OpenBracket):
818
state.replace(self.replace())
821
class ExpectOpenBrackedOrSimultaneous(ParseLilyPond):
822
"""Waits for an OpenBracket or << and then replaces the parser with the class set in the replace attribute.
824
Subclass this to set the destination for the OpenBracket.
828
items = space_items + (
832
def updateState(self, state, token):
833
if isinstance(token, (OpenBracket, OpenSimultaneous)):
834
state.replace(self.replace())
837
class ParseScore(ParseLilyPond):
838
"""Parses the expression after \score {, leaving at } """
841
Header, Layout, Midi, With,
842
) + toplevel_base_items
845
class ExpectScore(ExpectOpenBracket):
849
class ParseBook(ParseLilyPond):
850
"""Parses the expression after \book {, leaving at } """
853
Markup, MarkupLines, MarkupList,
856
Paper, Header, Layout,
857
) + toplevel_base_items
861
class ExpectBook(ExpectOpenBracket):
865
class ParseBookPart(ParseLilyPond):
866
"""Parses the expression after \score {, leaving at } """
869
Markup, MarkupLines, MarkupList,
871
Paper, Header, Layout,
872
) + toplevel_base_items
875
class ExpectBookPart(ExpectOpenBracket):
876
replace = ParseBookPart
879
class ParsePaper(ParseLilyPond):
880
"""Parses the expression after \score {, leaving at } """
881
items = base_items + (
883
Markup, MarkupLines, MarkupList,
891
class ExpectPaper(ExpectOpenBracket):
895
class ParseHeader(ParseLilyPond):
896
"""Parses the expression after \score {, leaving at } """
899
Markup, MarkupLines, MarkupList,
902
) + toplevel_base_items
905
class ExpectHeader(ExpectOpenBracket):
906
replace = ParseHeader
909
class ParseLayout(ParseLilyPond):
910
"""Parses the expression after \score {, leaving at } """
911
items = base_items + (
921
class ExpectLayout(ExpectOpenBracket):
922
replace = ParseLayout
925
class ParseMidi(ParseLilyPond):
926
"""Parses the expression after \score {, leaving at } """
927
items = base_items + (
937
class ExpectMidi(ExpectOpenBracket):
941
class ParseWith(ParseLilyPond):
942
"""Parses the expression after \score {, leaving at } """
947
) + toplevel_base_items
950
class ExpectWith(ExpectOpenBracket):
954
class ParseContext(ParseLilyPond):
955
"""Parses the expression after \score {, leaving at } """
958
BackSlashedContextName,
961
) + toplevel_base_items
964
class ExpectContext(ExpectOpenBracket):
965
replace = ParseContext
968
class ParseMusic(ParseLilyPond):
969
"""Parses LilyPond music expressions."""
973
class ParseChord(ParseMusic):
974
"""LilyPond inside chords < >"""
975
items = music_chord_items
978
class ParseString(Parser):
986
class ParseBlockComment(Parser):
994
class ParseMarkup(Parser):
1004
class ParseRepeat(FallthroughParser):
1005
items = space_items + (
1007
RepeatStringSpecifier,
1012
class ParseDuration(FallthroughParser):
1013
items = space_items + (
1016
def fallthrough(self, state):
1017
state.replace(ParseDurationScaling())
1020
class ParseDurationScaling(ParseDuration):
1021
items = space_items + (
1024
def fallthrough(self, state):
1028
class ParseOverride(ParseLilyPond):
1034
EqualSignSetOverride,
1036
Markup, MarkupLines, MarkupList,
1040
class ParseRevert(FallthroughParser):
1041
items = space_items + (
1050
class ParseSet(ParseLilyPond):
1056
EqualSignSetOverride,
1058
Markup, MarkupLines, MarkupList,
1062
class ParseUnset(FallthroughParser):
1063
items = space_items + (
1071
class ParseNewContext(FallthroughParser):
1072
items = space_items + (
1080
class ParseClef(FallthroughParser):
1082
items = space_items + (
1088
class ParseScriptAbbreviationOrFingering(FallthroughParser):
1090
items = space_items + (
1096
class ParseInputMode(ParseLilyPond):
1097
"""Base class for parser for mode-changing music commands."""
1100
class ExpectLyricMode(FallthroughParser):
1101
items = space_items + (
1109
def updateState(self, state, token):
1110
if isinstance(token, (OpenBracket, OpenSimultaneous)):
1111
state.replace(ParseLyricMode())
1114
class ParseLyricMode(ParseInputMode):
1115
"""Parser for \\lyrics, \\lyricmode, \\addlyrics, etc."""
1116
items = base_items + (
1130
Markup, MarkupLines, MarkupList,
1133
def updateState(self, state, token):
1134
if isinstance(token, (OpenSimultaneous, OpenBracket)):
1135
state.replace(ParseLyricMode())
1138
class ExpectChordMode(FallthroughParser):
1139
items = space_items + (
1144
def updateState(self, state, token):
1145
if isinstance(token, (OpenBracket, OpenSimultaneous)):
1146
state.replace(ParseChordMode())
1149
class ParseChordMode(ParseInputMode, ParseMusic):
1150
"""Parser for \\chords and \\chordmode."""
1151
pass # TODO: implement
1154
class ExpectNoteMode(FallthroughParser):
1155
items = space_items + (
1160
def updateState(self, state, token):
1161
if isinstance(token, (OpenBracket, OpenSimultaneous)):
1162
state.replace(ParseNoteMode())
1165
class ParseNoteMode(ParseInputMode, ParseMusic):
1166
"""Parser for \\notes and \\notemode. Same as Music itself."""
1169
class ExpectDrumMode(FallthroughParser):
1170
items = space_items + (
1175
def updateState(self, state, token):
1176
if isinstance(token, (OpenBracket, OpenSimultaneous)):
1177
state.replace(ParseDrumMode())
1180
class ParseDrumMode(ParseInputMode, ParseMusic):
1181
"""Parser for \\drums and \\drummode."""
1182
pass # TODO: implement
1185
class ExpectFigureMode(FallthroughParser):
1186
items = space_items + (
1191
def updateState(self, state, token):
1192
if isinstance(token, (OpenBracket, OpenSimultaneous)):
1193
state.replace(ParseFigureMode())
1196
class ParseFigureMode(ParseInputMode, ParseMusic):
1197
"""Parser for \\figures and \\figuremode."""
1198
pass # TODO: implement
1201
class ParsePitchCommand(FallthroughParser):
1203
items = space_items + (
1207
def updateState(self, state, token):
1208
if isinstance(token, Note):
1210
elif isinstance(token, _token.Space) and self.argcount <= 0: