1# This file is part of python-ly, https://pypi.python.org/pypi/python-ly 2# 3# Copyright (c) 2008 - 2015 by Wilbert Berendsen 4# 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. 9# 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. 14# 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. 19 20""" 21Parses and tokenizes LilyPond input. 22""" 23 24from __future__ import unicode_literals 25 26import itertools 27 28from . import _token 29from . import Parser, FallthroughParser 30 31 32re_articulation = r"[-_^][_.>|!+^-]" 33re_dynamic = ( 34 r"\\[<!>]|" 35 r"\\(f{1,5}|p{1,5}" 36 r"|mf|mp|fp|spp?|sff?|sfz|rfz" 37 r"|cresc|decresc|dim|cr|decr" 38 r")(?![A-Za-z])") 39 40re_duration = r"(\\(maxima|longa|breve)\b|(1|2|4|8|16|32|64|128|256|512|1024|2048)(?!\d))" 41re_dot = r"\." 42re_scaling = r"\*[\t ]*\d+(/\d+)?" 43 44# an identifier allowing letters and single hyphens in between 45re_identifier = r"[^\W\d_]+([_-][^\W\d_]+)*" 46 47# the lookahead pattern for the end of an identifier (ref) 48re_identifier_end = r"(?![_-]?[^\W\d])" 49 50 51class Identifier(_token.Token): 52 """A variable name, like ``some-variable``.""" 53 rx = r"(?<![^\W\d])" + re_identifier + re_identifier_end 54 55 56class IdentifierRef(_token.Token): 57 r"""A reference to an identifier, e.g. ``\some-variable``.""" 58 rx = r"\\" + re_identifier + re_identifier_end 59 60 61class Variable(Identifier): 62 pass 63 64 65class UserVariable(Identifier): 66 pass 67 68 69class Value(_token.Item, _token.Numeric): 70 pass 71 72 73class DecimalValue(Value): 74 rx = r"-?\d+(\.\d+)?" 75 76 77class IntegerValue(DecimalValue): 78 rx = r"\d+" 79 80 81class Fraction(Value): 82 rx = r"\d+/\d+" 83 84 85class Delimiter(_token.Token): 86 pass 87 88 89class DotPath(Delimiter): 90 """A dot in dotted path notation.""" 91 rx = r"\." 92 93 94class Error(_token.Error): 95 pass 96 97 98class Comment(_token.Comment): 99 pass 100 101 102class BlockCommentStart(Comment, _token.BlockCommentStart): 103 rx = r"%{" 104 def update_state(self, state): 105 state.enter(ParseBlockComment()) 106 107 108class BlockCommentEnd(Comment, _token.BlockCommentEnd, _token.Leaver): 109 rx = r"%}" 110 111 112class BlockComment(Comment, _token.BlockComment): 113 pass 114 115 116class LineComment(Comment, _token.LineComment): 117 rx = r"%.*$" 118 119 120class String(_token.String): 121 pass 122 123 124class StringQuotedStart(String, _token.StringStart): 125 rx = r'"' 126 def update_state(self, state): 127 state.enter(ParseString()) 128 129 130class StringQuotedEnd(String, _token.StringEnd): 131 rx = r'"' 132 def update_state(self, state): 133 state.leave() 134 state.endArgument() 135 136 137class StringQuoteEscape(_token.Character): 138 rx = r'\\[\\"]' 139 140 141class MusicItem(_token.Token): 142 r"""A note, rest, spacer, ``\skip`` or ``q``.""" 143 144 145class Skip(MusicItem): 146 rx = r"\\skip" + re_identifier_end 147 148 149class Spacer(MusicItem): 150 rx = r"s(?![A-Za-z])" 151 152 153class Rest(MusicItem): 154 rx = r"[Rr](?![A-Za-z])" 155 156 157class Note(MusicItem): 158 rx = r"[a-x]+(?![A-Za-z])" 159 160 161class Q(MusicItem): 162 rx = r"q(?![A-Za-z])" 163 164 165class DrumNote(MusicItem): 166 rx = r"[a-z]+(?![A-Za-z])" 167 168 169class Octave(_token.Token): 170 rx = r",+|'+" 171 172 173class OctaveCheck(_token.Token): 174 rx = r"=(,+|'+)?" 175 176 177class Accidental(_token.Token): 178 pass 179 180 181class AccidentalReminder(Accidental): 182 rx = r"!" 183 184 185class AccidentalCautionary(Accidental): 186 rx = r"\?" 187 188 189class Duration(_token.Token): 190 pass 191 192 193class Length(Duration): 194 rx = re_duration 195 def update_state(self, state): 196 state.enter(ParseDuration()) 197 198 199class Dot(Duration): 200 rx = re_dot 201 202 203class Scaling(Duration): 204 rx = re_scaling 205 206 207class OpenBracket(Delimiter, _token.MatchStart, _token.Indent): 208 """An open bracket, does not enter different parser, subclass or reimplement Parser.update_state().""" 209 rx = r"\{" 210 matchname = "bracket" 211 212 213class CloseBracket(Delimiter, _token.MatchEnd, _token.Dedent): 214 rx = r"\}" 215 matchname = "bracket" 216 def update_state(self, state): 217 state.leave() 218 state.endArgument() 219 220 221class OpenSimultaneous(Delimiter, _token.MatchStart, _token.Indent): 222 """An open double French quote, does not enter different parser, subclass or reimplement Parser.update_state().""" 223 rx = r"<<" 224 matchname = "simultaneous" 225 226 227class CloseSimultaneous(Delimiter, _token.MatchEnd, _token.Dedent): 228 rx = r">>" 229 matchname = "simultaneous" 230 def update_state(self, state): 231 state.leave() 232 state.endArgument() 233 234 235class SequentialStart(OpenBracket): 236 def update_state(self, state): 237 state.enter(ParseMusic()) 238 239 240class SequentialEnd(CloseBracket): 241 pass 242 243 244class SimultaneousStart(OpenSimultaneous): 245 def update_state(self, state): 246 state.enter(ParseMusic()) 247 248 249class SimultaneousEnd(CloseSimultaneous): 250 pass 251 252 253class PipeSymbol(Delimiter): 254 rx = r"\|" 255 256 257class Articulation(_token.Token): 258 """Base class for articulation things.""" 259 260 261class ArticulationCommand(Articulation, IdentifierRef): 262 @classmethod 263 def test_match(cls, match): 264 s = match.group()[1:] 265 if '-' not in s: 266 from .. import words 267 for l in ( 268 words.articulations, 269 words.ornaments, 270 words.fermatas, 271 words.instrument_scripts, 272 words.repeat_scripts, 273 words.ancient_scripts, 274 ): 275 if s in l: 276 return True 277 return False 278 279 280class Direction(_token.Token): 281 rx = r"[-_^]" 282 def update_state(self, state): 283 state.enter(ParseScriptAbbreviationOrFingering()) 284 285 286class ScriptAbbreviation(Articulation, _token.Leaver): 287 rx = r"[+|!>._^-]" 288 289 290class Fingering(Articulation, _token.Leaver): 291 rx = r"\d+" 292 293 294class StringNumber(Articulation): 295 rx = r"\\\d+" 296 297 298class Slur(_token.Token): 299 pass 300 301 302class SlurStart(Slur, _token.MatchStart): 303 rx = r"\(" 304 matchname = "slur" 305 306 307class SlurEnd(Slur, _token.MatchEnd): 308 rx = r"\)" 309 matchname = "slur" 310 311 312class PhrasingSlurStart(SlurStart): 313 rx = r"\\\(" 314 matchname = "phrasingslur" 315 316 317class PhrasingSlurEnd(SlurEnd): 318 rx = r"\\\)" 319 matchname = "phrasingslur" 320 321 322class Tie(Slur): 323 rx = r"~" 324 325 326class Beam(_token.Token): 327 pass 328 329 330class BeamStart(Beam, _token.MatchStart): 331 rx = r"\[" 332 matchname = "beam" 333 334 335class BeamEnd(Beam, _token.MatchEnd): 336 rx = r"\]" 337 matchname = "beam" 338 339 340class Ligature(_token.Token): 341 pass 342 343 344class LigatureStart(Ligature, _token.MatchStart): 345 rx = r"\\\[" 346 matchname = "ligature" 347 348 349class LigatureEnd(Ligature, _token.MatchEnd): 350 rx = r"\\\]" 351 matchname = "ligature" 352 353 354class Tremolo(_token.Token): 355 pass 356 357 358class TremoloColon(Tremolo): 359 rx = r":" 360 def update_state(self, state): 361 state.enter(ParseTremolo()) 362 363 364class TremoloDuration(Tremolo, _token.Leaver): 365 rx = r"\b(8|16|32|64|128|256|512|1024|2048)(?!\d)" 366 367 368class ChordItem(_token.Token): 369 """Base class for chordmode items.""" 370 371 372class ChordModifier(ChordItem): 373 rx = r"((?<![a-z])|^)(aug|dim|sus|min|maj|m)(?![a-z])" 374 375 376class ChordSeparator(ChordItem): 377 rx = r":|\^|/\+?" 378 379 380class ChordStepNumber(ChordItem): 381 rx = r"\d+[-+]?" 382 383 384class DotChord(ChordItem): 385 rx = r"\." 386 387 388class VoiceSeparator(Delimiter): 389 rx = r"\\\\" 390 391 392class Dynamic(_token.Token): 393 rx = re_dynamic 394 395 396class Command(_token.Item, IdentifierRef): 397 @classmethod 398 def test_match(cls, match): 399 s = match.group()[1:] 400 if '-' not in s: 401 from .. import words 402 return s in words.lilypond_music_commands 403 return False 404 405 406class Keyword(_token.Item, IdentifierRef): 407 @classmethod 408 def test_match(cls, match): 409 s = match.group()[1:] 410 if '-' not in s: 411 from .. import words 412 return s in words.lilypond_keywords 413 return False 414 415 416class Specifier(_token.Token): 417 # a specifier of a command e.g. the name of clef or repeat style. 418 pass 419 420 421class Score(Keyword): 422 rx = r"\\score\b" 423 def update_state(self, state): 424 state.enter(ExpectScore()) 425 426 427class Book(Keyword): 428 rx = r"\\book\b" 429 def update_state(self, state): 430 state.enter(ExpectBook()) 431 432 433class BookPart(Keyword): 434 rx = r"\\bookpart\b" 435 def update_state(self, state): 436 state.enter(ExpectBookPart()) 437 438 439class Paper(Keyword): 440 rx = r"\\paper\b" 441 def update_state(self, state): 442 state.enter(ExpectPaper()) 443 444 445class Header(Keyword): 446 rx = r"\\header\b" 447 def update_state(self, state): 448 state.enter(ExpectHeader()) 449 450 451class Layout(Keyword): 452 rx = r"\\layout\b" 453 def update_state(self, state): 454 state.enter(ExpectLayout()) 455 456 457class Midi(Keyword): 458 rx = r"\\midi\b" 459 def update_state(self, state): 460 state.enter(ExpectMidi()) 461 462 463class With(Keyword): 464 rx = r"\\with\b" 465 def update_state(self, state): 466 state.enter(ExpectWith()) 467 468 469class LayoutContext(Keyword): 470 rx = r"\\context\b" 471 def update_state(self, state): 472 state.enter(ExpectContext()) 473 474 475class Markup(_token.Item): 476 """Base class for all markup commands.""" 477 478 479class MarkupStart(Markup, Command): 480 rx = r"\\markup" + re_identifier_end 481 def update_state(self, state): 482 state.enter(ParseMarkup(1)) 483 484 485class MarkupLines(Markup): 486 rx = r"\\markuplines" + re_identifier_end 487 def update_state(self, state): 488 state.enter(ParseMarkup(1)) 489 490 491class MarkupList(Markup): 492 rx = r"\\markuplist" + re_identifier_end 493 def update_state(self, state): 494 state.enter(ParseMarkup(1)) 495 496 497class MarkupCommand(Markup, IdentifierRef): 498 """A markup command.""" 499 @classmethod 500 def test_match(cls, match): 501 from .. import words 502 return match.group()[1:] in words.markupcommands 503 504 def update_state(self, state): 505 from .. import words 506 command = self[1:] 507 if command in words.markupcommands_nargs[0]: 508 state.endArgument() 509 else: 510 for argcount in 2, 3, 4, 5: 511 if command in words.markupcommands_nargs[argcount]: 512 break 513 else: 514 argcount = 1 515 state.enter(ParseMarkup(argcount)) 516 517 518class MarkupScore(Markup): 519 rx = r"\\score\b" 520 def update_state(self, state): 521 state.enter(ExpectScore()) 522 523 524class MarkupUserCommand(Markup, IdentifierRef): 525 """A user-defined markup (i.e. not in the words markupcommands list).""" 526 def update_state(self, state): 527 state.endArgument() 528 529 530class MarkupWord(_token.Item): 531 rx = r'[^{}"\\\s#%]+' 532 533 534class OpenBracketMarkup(OpenBracket): 535 def update_state(self, state): 536 state.enter(ParseMarkup()) 537 538 539class CloseBracketMarkup(CloseBracket): 540 def update_state(self, state): 541 # go back to the opening bracket, this is the ParseMarkup 542 # parser with the 0 argcount 543 while state.parser().argcount > 0: 544 state.leave() 545 state.leave() 546 state.endArgument() 547 548 549class Repeat(Command): 550 rx = r"\\repeat(?![A-Za-z])" 551 def update_state(self, state): 552 state.enter(ParseRepeat()) 553 554 555class RepeatSpecifier(Specifier): 556 @_token.patternproperty 557 def rx(): 558 from .. import words 559 return r"\b({0})(?![A-Za-z])".format("|".join(words.repeat_types)) 560 561 562class RepeatCount(IntegerValue, _token.Leaver): 563 pass 564 565 566class Tempo(Command): 567 rx = r"\\tempo\b" 568 def update_state(self, state): 569 state.enter(ParseTempo()) 570 571 572class TempoSeparator(Delimiter): 573 rx = r"[-~](?=\s*\d)" 574 575 576class Partial(Command): 577 rx = r"\\partial\b" 578 579 580class Override(Keyword): 581 rx = r"\\override\b" 582 def update_state(self, state): 583 state.enter(ParseOverride()) 584 585 586class Set(Override): 587 rx = r"\\set\b" 588 def update_state(self, state): 589 state.enter(ParseSet()) 590 591 592class Revert(Override): 593 rx = r"\\revert\b" 594 def update_state(self, state): 595 state.enter(ParseRevert()) 596 597 598class Unset(Keyword): 599 rx = r"\\unset\b" 600 def update_state(self, state): 601 state.enter(ParseUnset()) 602 603 604class Tweak(Keyword): 605 rx = r"\\tweak\b" 606 def update_state(self, state): 607 state.enter(ParseTweak()) 608 609 610class Translator(Command): 611 def update_state(self, state): 612 state.enter(ParseTranslator()) 613 614 615class New(Translator): 616 rx = r"\\new\b" 617 618 619class Context(Translator): 620 rx = r"\\context\b" 621 622 623class Change(Translator): 624 rx = r"\\change\b" 625 626 627class AccidentalStyle(Command): 628 rx = r"\\accidentalStyle\b" 629 def update_state(self, state): 630 state.enter(ParseAccidentalStyle()) 631 632 633class AccidentalStyleSpecifier(Specifier): 634 @_token.patternproperty 635 def rx(): 636 from .. import words 637 return r"\b({0})(?!-?\w)".format("|".join(words.accidentalstyles)) 638 639 640class AlterBroken(Command): 641 rx = r"\\alterBroken\b" 642 def update_state(self, state): 643 state.enter(ParseAlterBroken()) 644 645 646class Clef(Command): 647 rx = r"\\clef\b" 648 def update_state(self, state): 649 state.enter(ParseClef()) 650 651 652class ClefSpecifier(Specifier): 653 @_token.patternproperty 654 def rx(): 655 from .. import words 656 return r"\b({0})\b".format("|".join(words.clefs_plain)) 657 658 def update_state(self, state): 659 state.leave() 660 661 662class PitchCommand(Command): 663 rx = r"\\(relative|transpose|transposition|key|octaveCheck)\b" 664 def update_state(self, state): 665 argcount = 2 if self == '\\transpose' else 1 666 state.enter(ParsePitchCommand(argcount)) 667 668 669class KeySignatureMode(Command): 670 @_token.patternproperty 671 def rx(): 672 from .. import words 673 return r"\\({0})(?![A-Za-z])".format("|".join(words.modes)) 674 675 676class Hide(Keyword): 677 rx = r"\\hide\b" 678 def update_state(self, state): 679 state.enter(ParseHideOmit()) 680 681 682class Omit(Keyword): 683 rx = r"\\omit\b" 684 def update_state(self, state): 685 state.enter(ParseHideOmit()) 686 687 688class Unit(Command): 689 rx = r"\\(mm|cm|in|pt)\b" 690 691 692class InputMode(Command): 693 pass 694 695 696class LyricMode(InputMode): 697 rx = r"\\(lyricmode|((old)?add)?lyrics|lyricsto)\b" 698 def update_state(self, state): 699 state.enter(ExpectLyricMode()) 700 701 702class Lyric(_token.Item): 703 """Base class for Lyric items.""" 704 705 706class LyricText(Lyric): 707 rx = r"[^\\\s\d\"]+" 708 709 710class LyricHyphen(Lyric): 711 rx = r"--(?=($|[\s\\]))" 712 713 714class LyricExtender(Lyric): 715 rx = r"__(?=($|[\s\\]))" 716 717 718class LyricSkip(Lyric): 719 rx = r"_(?=($|[\s\\]))" 720 721 722class Figure(_token.Token): 723 """Base class for Figure items.""" 724 725 726class FigureStart(Figure): 727 rx = r"<" 728 def update_state(self, state): 729 state.enter(ParseFigure()) 730 731 732class FigureEnd(Figure, _token.Leaver): 733 rx = r">" 734 735 736class FigureBracket(Figure): 737 rx = r"[][]" 738 739 740class FigureStep(Figure): 741 """A step figure number or the underscore.""" 742 rx = r"_|\d+" 743 744 745class FigureAccidental(Figure): 746 """A figure accidental.""" 747 rx = r"[-+!]+" 748 749 750class FigureModifier(Figure): 751 """A figure modifier.""" 752 rx = r"\\[\\!+]|/" 753 754 755class NoteMode(InputMode): 756 rx = r"\\(notes|notemode)\b" 757 def update_state(self, state): 758 state.enter(ExpectNoteMode()) 759 760 761class ChordMode(InputMode): 762 rx = r"\\(chords|chordmode)\b" 763 def update_state(self, state): 764 state.enter(ExpectChordMode()) 765 766 767class DrumMode(InputMode): 768 rx = r"\\(drums|drummode)\b" 769 def update_state(self, state): 770 state.enter(ExpectDrumMode()) 771 772 773class FigureMode(InputMode): 774 rx = r"\\(figures|figuremode)\b" 775 def update_state(self, state): 776 state.enter(ExpectFigureMode()) 777 778 779class UserCommand(IdentifierRef): 780 pass 781 782 783class SimultaneousOrSequentialCommand(Keyword): 784 rx = r"\\(simultaneous|sequential)\b" 785 786 787class SchemeStart(_token.Item): 788 rx = "[#$](?![{}])" 789 def update_state(self, state): 790 from . import scheme 791 state.enter(scheme.ParseScheme(1)) 792 793 794class ContextName(_token.Token): 795 @_token.patternproperty 796 def rx(): 797 from .. import words 798 return r"\b({0})\b".format("|".join(words.contexts)) 799 800 801class BackSlashedContextName(ContextName): 802 @_token.patternproperty 803 def rx(): 804 from .. import words 805 return r"\\({0})\b".format("|".join(words.contexts)) 806 807 808class GrobName(_token.Token): 809 @_token.patternproperty 810 def rx(): 811 from .. import data 812 return r"\b({0})\b".format("|".join(data.grobs())) 813 814 815class GrobProperty(Variable): 816 rx = r"\b([a-z]+|[XY])(-([a-z]+|[XY]))*(?![\w])" 817 818 819class ContextProperty(Variable): 820 @_token.patternproperty 821 def rx(): 822 from .. import data 823 return r"\b({0})\b".format("|".join(data.context_properties())) 824 825 826class PaperVariable(Variable): 827 """A variable inside Paper. Always follow this one by UserVariable.""" 828 @classmethod 829 def test_match(cls, match): 830 from .. import words 831 return match.group() in words.papervariables 832 833 834class HeaderVariable(Variable): 835 """A variable inside Header. Always follow this one by UserVariable.""" 836 @classmethod 837 def test_match(cls, match): 838 from .. import words 839 return match.group() in words.headervariables 840 841 842class LayoutVariable(Variable): 843 """A variable inside Header. Always follow this one by UserVariable.""" 844 @classmethod 845 def test_match(cls, match): 846 from .. import words 847 return match.group() in words.layoutvariables 848 849 850class Chord(_token.Token): 851 """Base class for Chord delimiters.""" 852 pass 853 854 855class ChordStart(Chord): 856 rx = r"<" 857 def update_state(self, state): 858 state.enter(ParseChord()) 859 860 861class ChordEnd(Chord, _token.Leaver): 862 rx = r">" 863 864 865class DrumChordStart(ChordStart): 866 def update_state(self, state): 867 state.enter(ParseDrumChord()) 868 869 870class DrumChordEnd(ChordEnd): 871 pass 872 873 874class ErrorInChord(Error): 875 rx = "|".join(( 876 re_articulation, # articulation 877 r"<<|>>", # double french quotes 878 r"\\[\\\]\[\(\)()]", # slurs beams 879 re_duration, # duration 880 re_scaling, # scaling 881 )) 882 883 884class Name(UserVariable): 885 r"""A variable name without \ prefix.""" 886 887 888class EqualSign(_token.Token): 889 rx = r"=" 890 891 892# Parsers 893class ParseLilyPond(Parser): 894 mode = 'lilypond' 895 896# basic stuff that can appear everywhere 897space_items = ( 898 _token.Space, 899 BlockCommentStart, 900 LineComment, 901) 902 903 904base_items = space_items + ( 905 SchemeStart, 906 StringQuotedStart, 907) 908 909 910# items that represent commands in both toplevel and music mode 911command_items = ( 912 Repeat, 913 PitchCommand, 914 Override, Revert, 915 Set, Unset, 916 Hide, Omit, 917 Tweak, 918 New, Context, Change, 919 With, 920 Clef, 921 Tempo, 922 Partial, 923 KeySignatureMode, 924 AccidentalStyle, 925 AlterBroken, 926 SimultaneousOrSequentialCommand, 927 ChordMode, DrumMode, FigureMode, LyricMode, NoteMode, 928 MarkupStart, MarkupLines, MarkupList, 929 ArticulationCommand, 930 Keyword, 931 Command, 932 SimultaneousOrSequentialCommand, 933 UserCommand, 934) 935 936 937# items that occur in toplevel, book, bookpart or score 938# no Leave-tokens! 939toplevel_base_items = base_items + ( 940 SequentialStart, 941 SimultaneousStart, 942) + command_items 943 944 945# items that occur in music expressions 946music_items = base_items + ( 947 Dynamic, 948 Skip, 949 Spacer, 950 Q, 951 Rest, 952 Note, 953 Fraction, 954 Length, 955 Octave, 956 OctaveCheck, 957 AccidentalCautionary, 958 AccidentalReminder, 959 PipeSymbol, 960 VoiceSeparator, 961 SequentialStart, SequentialEnd, 962 SimultaneousStart, SimultaneousEnd, 963 ChordStart, 964 ContextName, 965 GrobName, 966 SlurStart, SlurEnd, 967 PhrasingSlurStart, PhrasingSlurEnd, 968 Tie, 969 BeamStart, BeamEnd, 970 LigatureStart, LigatureEnd, 971 Direction, 972 StringNumber, 973 IntegerValue, 974) + command_items 975 976 977# items that occur inside chords 978music_chord_items = ( 979 ErrorInChord, 980 ChordEnd, 981) + music_items 982 983 984 985class ParseGlobal(ParseLilyPond): 986 """Parses LilyPond from the toplevel of a file.""" 987 items = ( 988 Book, 989 BookPart, 990 Score, 991 MarkupStart, MarkupLines, MarkupList, 992 Paper, Header, Layout, 993 ) + toplevel_base_items + ( 994 Name, 995 DotPath, 996 EqualSign, 997 Fraction, 998 DecimalValue, 999 ) 1000 def update_state(self, state, token): 1001 if isinstance(token, EqualSign): 1002 state.enter(ParseGlobalAssignment()) 1003 1004 1005class ParseGlobalAssignment(FallthroughParser, ParseLilyPond): 1006 items = space_items + ( 1007 Skip, 1008 Spacer, 1009 Q, 1010 Rest, 1011 Note, 1012 Length, 1013 Fraction, 1014 DecimalValue, 1015 Direction, 1016 StringNumber, 1017 Dynamic, 1018 ) 1019 1020 1021class ExpectOpenBracket(FallthroughParser, ParseLilyPond): 1022 """Waits for an OpenBracket and then replaces the parser with the class set in the replace attribute. 1023 1024 Subclass this to set the destination for the OpenBracket. 1025 1026 """ 1027 default = Error 1028 items = space_items + ( 1029 OpenBracket, 1030 ) 1031 def update_state(self, state, token): 1032 if isinstance(token, OpenBracket): 1033 state.replace(self.replace()) 1034 1035 1036class ExpectMusicList(FallthroughParser, ParseLilyPond): 1037 """Waits for an OpenBracket or << and then replaces the parser with the class set in the replace attribute. 1038 1039 Subclass this to set the destination for the OpenBracket. 1040 1041 """ 1042 items = space_items + ( 1043 OpenBracket, 1044 OpenSimultaneous, 1045 SimultaneousOrSequentialCommand, 1046 ) 1047 def update_state(self, state, token): 1048 if isinstance(token, (OpenBracket, OpenSimultaneous)): 1049 state.replace(self.replace()) 1050 1051 1052class ParseScore(ParseLilyPond): 1053 r"""Parses the expression after ``\score {``, leaving at ``}`` """ 1054 items = ( 1055 CloseBracket, 1056 Header, Layout, Midi, With, 1057 ) + toplevel_base_items 1058 1059 1060class ExpectScore(ExpectOpenBracket): 1061 replace = ParseScore 1062 1063 1064class ParseBook(ParseLilyPond): 1065 r"""Parses the expression after ``\book {``, leaving at ``}`` """ 1066 items = ( 1067 CloseBracket, 1068 MarkupStart, MarkupLines, MarkupList, 1069 BookPart, 1070 Score, 1071 Paper, Header, Layout, 1072 ) + toplevel_base_items 1073 1074 1075 1076class ExpectBook(ExpectOpenBracket): 1077 replace = ParseBook 1078 1079 1080class ParseBookPart(ParseLilyPond): 1081 r"""Parses the expression after ``\bookpart {``, leaving at ``}`` """ 1082 items = ( 1083 CloseBracket, 1084 MarkupStart, MarkupLines, MarkupList, 1085 Score, 1086 Paper, Header, Layout, 1087 ) + toplevel_base_items 1088 1089 1090class ExpectBookPart(ExpectOpenBracket): 1091 replace = ParseBookPart 1092 1093 1094class ParsePaper(ParseLilyPond): 1095 r"""Parses the expression after ``\paper {``, leaving at ``}`` """ 1096 items = base_items + ( 1097 CloseBracket, 1098 MarkupStart, MarkupLines, MarkupList, 1099 PaperVariable, 1100 UserVariable, 1101 EqualSign, 1102 DotPath, 1103 DecimalValue, 1104 Unit, 1105 ) 1106 1107 1108class ExpectPaper(ExpectOpenBracket): 1109 replace = ParsePaper 1110 1111 1112class ParseHeader(ParseLilyPond): 1113 r"""Parses the expression after ``\header {``, leaving at ``}`` """ 1114 items = ( 1115 CloseBracket, 1116 MarkupStart, MarkupLines, MarkupList, 1117 HeaderVariable, 1118 UserVariable, 1119 EqualSign, 1120 DotPath, 1121 ) + toplevel_base_items 1122 1123 1124class ExpectHeader(ExpectOpenBracket): 1125 replace = ParseHeader 1126 1127 1128class ParseLayout(ParseLilyPond): 1129 r"""Parses the expression after ``\layout {``, leaving at ``}`` """ 1130 items = base_items + ( 1131 CloseBracket, 1132 LayoutContext, 1133 LayoutVariable, 1134 UserVariable, 1135 EqualSign, 1136 DotPath, 1137 DecimalValue, 1138 Unit, 1139 ContextName, 1140 GrobName, 1141 ) + command_items 1142 1143 1144class ExpectLayout(ExpectOpenBracket): 1145 replace = ParseLayout 1146 1147 1148class ParseMidi(ParseLilyPond): 1149 r"""Parses the expression after ``\midi {``, leaving at ``}`` """ 1150 items = base_items + ( 1151 CloseBracket, 1152 LayoutContext, 1153 LayoutVariable, 1154 UserVariable, 1155 EqualSign, 1156 DotPath, 1157 DecimalValue, 1158 Unit, 1159 ContextName, 1160 GrobName, 1161 ) + command_items 1162 1163 1164class ExpectMidi(ExpectOpenBracket): 1165 replace = ParseMidi 1166 1167 1168class ParseWith(ParseLilyPond): 1169 r"""Parses the expression after ``\with {``, leaving at ``}`` """ 1170 items = ( 1171 CloseBracket, 1172 ContextName, 1173 GrobName, 1174 ContextProperty, 1175 EqualSign, 1176 DotPath, 1177 ) + toplevel_base_items 1178 1179 1180class ExpectWith(ExpectOpenBracket): 1181 replace = ParseWith 1182 1183 1184class ParseContext(ParseLilyPond): 1185 r"""Parses the expression after (``\layout {``) ``\context {``, leaving at ``}`` """ 1186 items = ( 1187 CloseBracket, 1188 BackSlashedContextName, 1189 ContextProperty, 1190 EqualSign, 1191 DotPath, 1192 ) + toplevel_base_items 1193 1194 1195class ExpectContext(ExpectOpenBracket): 1196 replace = ParseContext 1197 1198 1199class ParseMusic(ParseLilyPond): 1200 """Parses LilyPond music expressions.""" 1201 items = music_items + ( 1202 TremoloColon, 1203 ) 1204 1205 1206class ParseChord(ParseMusic): 1207 """LilyPond inside chords ``< >``""" 1208 items = music_chord_items 1209 1210 1211class ParseString(Parser): 1212 default = String 1213 items = ( 1214 StringQuotedEnd, 1215 StringQuoteEscape, 1216 ) 1217 1218 1219class ParseBlockComment(Parser): 1220 default = BlockComment 1221 items = ( 1222 BlockCommentEnd, 1223 ) 1224 1225 1226class ParseMarkup(Parser): 1227 items = ( 1228 MarkupScore, 1229 MarkupCommand, 1230 MarkupUserCommand, 1231 OpenBracketMarkup, 1232 CloseBracketMarkup, 1233 MarkupWord, 1234 ) + base_items 1235 1236 1237class ParseRepeat(FallthroughParser): 1238 items = space_items + ( 1239 RepeatSpecifier, 1240 StringQuotedStart, 1241 RepeatCount, 1242 ) 1243 1244 1245class ParseTempo(FallthroughParser): 1246 items = space_items + ( 1247 MarkupStart, 1248 StringQuotedStart, 1249 SchemeStart, 1250 Length, 1251 EqualSign, 1252 ) 1253 def update_state(self, state, token): 1254 if isinstance(token, EqualSign): 1255 state.replace(ParseTempoAfterEqualSign()) 1256 1257 1258class ParseTempoAfterEqualSign(FallthroughParser): 1259 items = space_items + ( 1260 IntegerValue, 1261 TempoSeparator, 1262 ) 1263 1264 1265class ParseDuration(FallthroughParser): 1266 items = space_items + ( 1267 Dot, 1268 ) 1269 def fallthrough(self, state): 1270 state.replace(ParseDurationScaling()) 1271 1272 1273class ParseDurationScaling(ParseDuration): 1274 items = space_items + ( 1275 Scaling, 1276 ) 1277 def fallthrough(self, state): 1278 state.leave() 1279 1280 1281class ParseOverride(ParseLilyPond): 1282 argcount = 0 1283 items = ( 1284 ContextName, 1285 DotPath, 1286 GrobName, 1287 GrobProperty, 1288 EqualSign, 1289 ) + base_items 1290 def update_state(self, state, token): 1291 if isinstance(token, EqualSign): 1292 state.replace(ParseDecimalValue()) 1293 1294 1295class ParseRevert(FallthroughParser): 1296 r"""parse the arguments of ``\revert``""" 1297 # allow both the old scheme syntax but also the dotted 2.18+ syntax 1298 # allow either a dot between the GrobName and the property path or not 1299 # correctly fall through when one property path has been parsed 1300 # (uses ParseGrobPropertyPath and ExpectGrobProperty) 1301 # (When the old scheme syntax is used this parser also falls through, 1302 # assuming that the previous parser will handle it) 1303 items = space_items + ( 1304 ContextName, 1305 DotPath, 1306 GrobName, 1307 GrobProperty, 1308 ) 1309 def update_state(self, state, token): 1310 if isinstance(token, GrobProperty): 1311 state.replace(ParseGrobPropertyPath()) 1312 1313 1314class ParseGrobPropertyPath(FallthroughParser): 1315 items = space_items + ( 1316 DotPath, 1317 ) 1318 def update_state(self, state, token): 1319 if isinstance(token, DotPath): 1320 state.enter(ExpectGrobProperty()) 1321 1322 1323class ExpectGrobProperty(FallthroughParser): 1324 items = space_items + ( 1325 GrobProperty, 1326 ) 1327 def update_state(self, state, token): 1328 if isinstance(token, GrobProperty): 1329 state.leave() 1330 1331 1332class ParseSet(ParseLilyPond): 1333 argcount = 0 1334 items = ( 1335 ContextName, 1336 DotPath, 1337 ContextProperty, 1338 EqualSign, 1339 Name, 1340 ) + base_items 1341 def update_state(self, state, token): 1342 if isinstance(token, EqualSign): 1343 state.replace(ParseDecimalValue()) 1344 1345 1346class ParseUnset(FallthroughParser): 1347 items = space_items + ( 1348 ContextName, 1349 DotPath, 1350 ContextProperty, 1351 Name, 1352 ) 1353 def update_state(self, state, token): 1354 if isinstance(token, ContextProperty) or token[:1].islower(): 1355 state.leave() 1356 1357 1358class ParseTweak(FallthroughParser): 1359 items = space_items + ( 1360 GrobName, 1361 DotPath, 1362 GrobProperty, 1363 ) 1364 def update_state(self, state, token): 1365 if isinstance(token, GrobProperty): 1366 state.replace(ParseTweakGrobProperty()) 1367 1368 1369class ParseTweakGrobProperty(FallthroughParser): 1370 items = space_items + ( 1371 DotPath, 1372 DecimalValue, 1373 ) 1374 def update_state(self, state, token): 1375 if isinstance(token, DotPath): 1376 state.enter(ExpectGrobProperty()) 1377 elif isinstance(token, DecimalValue): 1378 state.leave() 1379 1380 1381class ParseTranslator(FallthroughParser): 1382 items = space_items + ( 1383 ContextName, 1384 Name, 1385 ) 1386 1387 def update_state(self, state, token): 1388 if isinstance(token, (Name, ContextName)): 1389 state.replace(ExpectTranslatorId()) 1390 1391 1392class ExpectTranslatorId(FallthroughParser): 1393 items = space_items + ( 1394 EqualSign, 1395 ) 1396 1397 def update_state(self, state, token): 1398 if token == '=': 1399 state.replace(ParseTranslatorId()) 1400 1401 1402class ParseTranslatorId(FallthroughParser): 1403 argcount = 1 1404 items = space_items + ( 1405 Name, 1406 StringQuotedStart, 1407 ) 1408 1409 def update_state(self, state, token): 1410 if isinstance(token, Name): 1411 state.leave() 1412 1413 1414class ParseClef(FallthroughParser): 1415 argcount = 1 1416 items = space_items + ( 1417 ClefSpecifier, 1418 StringQuotedStart, 1419 ) 1420 1421 1422class ParseHideOmit(FallthroughParser): 1423 items = space_items + ( 1424 ContextName, 1425 DotPath, 1426 GrobName, 1427 ) 1428 def update_state(self, state, token): 1429 if isinstance(token, GrobName): 1430 state.leave() 1431 1432 1433class ParseAccidentalStyle(FallthroughParser): 1434 items = space_items + ( 1435 ContextName, 1436 DotPath, 1437 AccidentalStyleSpecifier, 1438 ) 1439 def update_state(self, state, token): 1440 if isinstance(token, AccidentalStyleSpecifier): 1441 state.leave() 1442 1443 1444class ParseAlterBroken(FallthroughParser): 1445 items = space_items + ( 1446 GrobProperty, 1447 ) 1448 def update_state(self, state, token): 1449 if isinstance(token, GrobProperty): 1450 state.replace(ParseGrobPropertyPath()) 1451 1452 1453class ParseScriptAbbreviationOrFingering(FallthroughParser): 1454 argcount = 1 1455 items = space_items + ( 1456 ScriptAbbreviation, 1457 Fingering, 1458 ) 1459 1460 1461class ParseInputMode(ParseLilyPond): 1462 """Base class for parser for mode-changing music commands.""" 1463 @classmethod 1464 def update_state(cls, state, token): 1465 if isinstance(token, (OpenSimultaneous, OpenBracket)): 1466 state.enter(cls()) 1467 1468 1469class ParseLyricMode(ParseInputMode): 1470 r"""Parser for ``\lyrics``, ``\lyricmode``, ``\addlyrics``, etc.""" 1471 items = base_items + ( 1472 CloseBracket, 1473 CloseSimultaneous, 1474 OpenBracket, 1475 OpenSimultaneous, 1476 PipeSymbol, 1477 LyricHyphen, 1478 LyricExtender, 1479 LyricSkip, 1480 LyricText, 1481 Dynamic, 1482 Skip, 1483 Length, 1484 MarkupStart, MarkupLines, MarkupList, 1485 ) + command_items 1486 1487 1488class ExpectLyricMode(ExpectMusicList): 1489 replace = ParseLyricMode 1490 items = space_items + ( 1491 OpenBracket, 1492 OpenSimultaneous, 1493 SchemeStart, 1494 StringQuotedStart, 1495 Name, 1496 SimultaneousOrSequentialCommand, 1497 ) 1498 1499 1500class ParseChordMode(ParseInputMode, ParseMusic): 1501 r"""Parser for ``\chords`` and ``\chordmode``.""" 1502 items = ( 1503 OpenBracket, 1504 OpenSimultaneous, 1505 ) + music_items + ( # TODO: specify items exactly, e.g. < > is not allowed 1506 ChordSeparator, 1507 ) 1508 def update_state(self, state, token): 1509 if isinstance(token, ChordSeparator): 1510 state.enter(ParseChordItems()) 1511 else: 1512 super(ParseChordMode, self).update_state(state, token) 1513 1514 1515class ExpectChordMode(ExpectMusicList): 1516 replace = ParseChordMode 1517 1518 1519class ParseNoteMode(ParseMusic): 1520 r"""Parser for ``\notes`` and ``\notemode``. Same as Music itself.""" 1521 1522 1523class ExpectNoteMode(ExpectMusicList): 1524 replace = ParseNoteMode 1525 1526 1527class ParseDrumChord(ParseMusic): 1528 """LilyPond inside chords in drummode ``< >``""" 1529 items = base_items + ( 1530 ErrorInChord, 1531 DrumChordEnd, 1532 Dynamic, 1533 Skip, 1534 Spacer, 1535 Q, 1536 Rest, 1537 DrumNote, 1538 Fraction, 1539 Length, 1540 PipeSymbol, 1541 VoiceSeparator, 1542 SequentialStart, SequentialEnd, 1543 SimultaneousStart, SimultaneousEnd, 1544 ChordStart, 1545 ContextName, 1546 GrobName, 1547 SlurStart, SlurEnd, 1548 PhrasingSlurStart, PhrasingSlurEnd, 1549 Tie, 1550 BeamStart, BeamEnd, 1551 LigatureStart, LigatureEnd, 1552 Direction, 1553 StringNumber, 1554 IntegerValue, 1555 ) + command_items 1556 1557 1558class ParseDrumMode(ParseInputMode, ParseMusic): 1559 r"""Parser for ``\drums`` and ``\drummode``.""" 1560 items = ( 1561 OpenBracket, 1562 OpenSimultaneous, 1563 ) + base_items + ( 1564 Dynamic, 1565 Skip, 1566 Spacer, 1567 Q, 1568 Rest, 1569 DrumNote, 1570 Fraction, 1571 Length, 1572 PipeSymbol, 1573 VoiceSeparator, 1574 SequentialStart, SequentialEnd, 1575 SimultaneousStart, SimultaneousEnd, 1576 DrumChordStart, 1577 ContextName, 1578 GrobName, 1579 SlurStart, SlurEnd, 1580 PhrasingSlurStart, PhrasingSlurEnd, 1581 Tie, 1582 BeamStart, BeamEnd, 1583 LigatureStart, LigatureEnd, 1584 Direction, 1585 StringNumber, 1586 IntegerValue, 1587 ) + command_items 1588 1589 1590class ExpectDrumMode(ExpectMusicList): 1591 replace = ParseDrumMode 1592 1593 1594class ParseFigureMode(ParseInputMode, ParseMusic): 1595 r"""Parser for ``\figures`` and ``\figuremode``.""" 1596 items = base_items + ( 1597 CloseBracket, 1598 CloseSimultaneous, 1599 OpenBracket, 1600 OpenSimultaneous, 1601 PipeSymbol, 1602 FigureStart, 1603 Skip, Spacer, Rest, 1604 Length, 1605 ) + command_items 1606 1607 1608class ParseFigure(Parser): 1609 """Parse inside ``< >`` in figure mode.""" 1610 items = base_items + ( 1611 FigureEnd, 1612 FigureBracket, 1613 FigureStep, 1614 FigureAccidental, 1615 FigureModifier, 1616 MarkupStart, MarkupLines, MarkupList, 1617 ) 1618 1619 1620class ExpectFigureMode(ExpectMusicList): 1621 replace = ParseFigureMode 1622 1623 1624class ParsePitchCommand(FallthroughParser): 1625 argcount = 1 1626 items = space_items + ( 1627 Note, 1628 Octave, 1629 ) 1630 def update_state(self, state, token): 1631 if isinstance(token, Note): 1632 self.argcount -= 1 1633 elif isinstance(token, _token.Space) and self.argcount <= 0: 1634 state.leave() 1635 1636 1637class ParseTremolo(FallthroughParser): 1638 items = (TremoloDuration,) 1639 1640 1641class ParseChordItems(FallthroughParser): 1642 items = ( 1643 ChordSeparator, 1644 ChordModifier, 1645 ChordStepNumber, 1646 DotChord, 1647 Note, 1648 ) 1649 1650 1651class ParseDecimalValue(FallthroughParser): 1652 """Parses a decimal value without a # before it (if present).""" 1653 items = space_items + ( 1654 Fraction, 1655 DecimalValue, 1656 ) 1657 1658 1659