§
    ƒØj;  ã                  óh  — d Z ddlmZ g d¢ZddlZddlZddlZddlZddlZddl	Z	ddl
Z
 ej        d¦  «        Z ej        d¦  «        Zd)d„Zd*d+d„Zd,d-d„Zd,d-d„Zd.d/d„Zd0d1d„Zd1d„Zd2d„Zd3d„Zd4d„Zej         G d „ d!¦  «        ¦   «         Z	 	 d5d6d'„Zed(k    rddlZ ej        ¦   «          dS dS )7z 
Tools for working with strings
é    )Úannotations)ÚwhitespaceEqualÚgetNumFromStrÚhyphenToCamelCaseÚcamelCaseToHyphenÚspaceCamelCaseÚgetMd5Ú	formatStrÚstripAccentsÚnormalizeFilenameÚremovePunctuationÚparenthesesMatchÚParenthesesMatchNz\s+z
+ÚaÚstrÚbÚreturnÚboolc                óî   — t                                d| ¦  «        } t                                d|¦  «        }t                               d| ¦  «        } t                               d|¦  «        }| |k    rdS dS )a  
    returns True if a and b are equal except for whitespace differences

    >>> a = '    hello \n there '
    >>> b = 'hello there'
    >>> c = ' bye there '
    >>> common.whitespaceEqual(a, b)
    True
    >>> common.whitespaceEqual(a, c)
    False
    Ú TF)Ú
WHITESPACEÚsubÚLINEFEED)r   r   s     ú�/Users/tedpeters/Documents/Projects/Claude Projects/Misc Tools/notename-sheets/.venv/lib/python3.11/site-packages/music21/common/stringTools.pyr   r   ,   sc   € õ 	�Š�r˜1ÑÔ€AÝ�Š�r˜1ÑÔ€AÝ�Š�R˜ÑÔ€AÝ�Š�R˜ÑÔ€AØˆA‚v€vØˆtàˆuó    Ú
0123456789ÚusrStrÚnumbersútuple[str, str]c                óÆ   — g }g }| D ]1}||v r|                      |¦  «         Œ|                      |¦  «         Œ2d                     |¦  «        d                     |¦  «        fS )a$  
    Given a string, extract any numbers.
    Return two strings, the numbers (as strings) and the remaining characters.

    >>> common.getNumFromStr('23a')
    ('23', 'a')
    >>> common.getNumFromStr('23a954Hello')
    ('23954', 'aHello')
    >>> common.getNumFromStr('')
    ('', '')
    r   )ÚappendÚjoin)r   r   ÚfoundÚremainÚchars        r   r   r   C   so   € ð €EØ€FØð  ð  ˆØ�7ˆ?ˆ?Ø�LŠL˜ÑÔÐÐà�MŠM˜$ÑÔÐÐà�7Š7�5‰>Œ>˜2Ÿ7š7 6™?œ?Ð*Ð*r   ú-Úreplacementc                óš   — d}t          |                      |¦  «        ¦  «        D ]%\  }}|dk    r|}Œ||                     ¦   «         z  }Œ&|S )a  
    Given a hyphen-connected-string, change it to
    a camelCaseConnectedString.

    The replacement can be specified to be something besides a hyphen.

    >>> common.hyphenToCamelCase('movement-name')
    'movementName'

    >>> common.hyphenToCamelCase('movement_name', replacement='_')
    'movementName'

    Safe to call on a string lacking the replacement character:

    >>> common.hyphenToCamelCase('voice')
    'voice'

    And on "words" beginning with numbers:

    >>> common.hyphenToCamelCase('music-21')
    'music21'
    r   r   )Ú	enumerateÚsplitÚ
capitalize)r   r'   ÚpostÚiÚwords        r   r   r   Z   s[   € ð. €DÝ˜VŸ\š\¨+Ñ6Ô6Ñ7Ô7ð &ð &‰ˆˆ4Ø�Š6ˆ6ØˆDˆDà�D—O’OÑ%Ô%Ñ%ˆDˆDØ€Kr   c                ó(  — t          |¦  «        dk    rt          d¦  «        ‚|                     ¦   «         |k    rt          d¦  «        ‚t          j        dd|z   dz   | ¦  «        }t          j        dd|z   dz   |¦  «                             ¦   «         S )aF  
    Given a camel-cased string, or a mixture of numbers and characters,
    create a space separated string.

    The replacement can be specified to be something besides a hyphen, but only
    a single character and not (for internal reasons) an uppercase character.

    code from https://stackoverflow.com/questions/1175208/elegant-python-function-to-convert-camelcase-to-snake-case

    >>> common.camelCaseToHyphen('movementName')
    'movement-name'

    First letter can be uppercase as well:

    >>> common.camelCaseToHyphen('MovementName')
    'movement-name'

    >>> common.camelCaseToHyphen('movementNameName')
    'movement-name-name'

    >>> common.camelCaseToHyphen('fileName', replacement='_')
    'file_name'

    Some things you cannot do:

    >>> common.camelCaseToHyphen('fileName', replacement='NotFound')
    Traceback (most recent call last):
    ValueError: Replacement must be a single character.

    >>> common.camelCaseToHyphen('fileName', replacement='A')
    Traceback (most recent call last):
    ValueError: Replacement cannot be an uppercase character.
    é   z'Replacement must be a single character.z-Replacement cannot be an uppercase character.z(.)([A-Z][a-z]+)z\1z\2z([a-z0-9])([A-Z]))ÚlenÚ
ValueErrorÚlowerÚrer   )r   r'   Ús1s      r   r   r   z   s–   € õF ˆ;ÑÔ˜1ÒÐÝÐBÑCÔCÐCØ×ÒÑÔ˜kÒ)Ð)ÝÐHÑIÔIÐIÝ	ŒÐ" E¨KÑ$7¸%Ñ$?ÀÑ	HÔ	H€BÝŒ6Ð% u¨{Ñ':¸UÑ'BÀBÑGÔG×MÒMÑOÔOÐOr   Tc                óð  — d}d}d}d}d}g }|€d}	n|}	| D ]‚}
|
|v rd}nd}|r|s|sd}nd}|s|s|rd}nd}|rC|
                      ¦   «         s|s|r|                     d¦  «         |                     |
¦  «         n|                     |
¦  «         |rd}Œ€d}Œƒd                     |¦  «        }|	D ]-}d                     |¦  «        }|                     ||¦  «        }Œ.|r|                     dd¦  «        }|S )	a‡  
    Given a camel-cased string, or a mixture of numbers and characters,
    create a space separated string.

    If replaceUnderscore is True (default) then underscores also become spaces (but without the _)

    >>> common.spaceCamelCase('thisIsATest')
    'this Is A Test'
    >>> common.spaceCamelCase('ThisIsATest')
    'This Is A Test'
    >>> common.spaceCamelCase('movement3')
    'movement 3'
    >>> common.spaceCamelCase('opus41no1')
    'opus 41 no 1'
    >>> common.spaceCamelCase('opus23402no219235')
    'opus 23402 no 219235'
    >>> common.spaceCamelCase('opus23402no219235').title()
    'Opus 23402 No 219235'

    There is a small list called fixMeList that can fix mistakes.

    >>> common.spaceCamelCase('PMFC22')
    'PMFC 22'

    >>> common.spaceCamelCase('hello_myke')
    'hello myke'
    >>> common.spaceCamelCase('hello_myke', replaceUnderscore=False)
    'hello_myke'
    z0123456789.FN)ÚPMFCTú r   Ú_)Úisupperr!   r"   Úreplace)r   ÚreplaceUnderscoreÚ	fixMeListr   ÚfirstNumÚ	firstCharÚisNumberÚ	lastIsNumr,   Ú	fixupListr%   ÚpostStrÚfixMeÚfixMeSpaceds                 r   r   r   ¥   sw  € ð< €GØ€HØ€IØ€HØ€IØ€Dð ÐØˆ	ˆ	àˆ	àð ð ˆØ�7ˆ?ˆ?ØˆHˆHàˆHàð 	˜Hð 	¨Yð 	ØˆHˆHàˆHð ð 	 	ð 	¨ið 	ØˆIˆIàˆIàð 	Ø�|Š|‰~Œ~ð ! ð !¨Yð !Ø—’˜CÑ Ô Ð Ø�KŠK˜ÑÔÐÐà�KŠK˜ÑÔÐàð 	ØˆIˆIàˆIˆIØ�gŠg�d‰mŒm€GØð 6ð 6ˆØ—h’h˜u‘o”oˆØ—/’/ +¨uÑ5Ô5ˆˆàð ,Ø—/’/ # sÑ+Ô+ˆØ€Nr   c                ót  — | €At          t          j        ¦   «         ¦  «        t          t          j        ¦   «         ¦  «        z   } t          j        ¦   «         }	 |                     | ¦  «         n8# t          $ r+ |                     |                      d¦  «        ¦  «         Y nw xY w|                     ¦   «         S )zÂ
    Return an md5 hash from a string.  If no value is given then
    the current time plus a random number is encoded.

    >>> common.getMd5('test')
    '098f6bcd4621d373cade4e832627b4f6'
    NúUTF-8)	r   ÚtimeÚrandomÚhashlibÚmd5ÚupdateÚ	TypeErrorÚencodeÚ	hexdigest)ÚvalueÚms     r   r	   r	   ö   sŸ   € ð €}Ý•D”I‘K”KÑ Ô ¥3¥v¤}¡¤Ñ#7Ô#7Ñ7ˆÝŒ‰Œ€Að(Ø	�Š�‰ŒˆˆøÝð (ð (ð (Ø	�Š�—’˜gÑ&Ô&Ñ'Ô'Ð'Ð'Ð'ð(øøøð �;Š;‰=Œ=Ðs   ÁA. Á.2B#Â"B#c                ó´  — | g|¢} t          t          | ¦  «        ¦  «        D ]Ÿ}| |         }t          |t          ¦  «        r|                     d¦  «        | |<   t          |t
          ¦  «        sS	 t          |¦  «        | |<   Œ`# t          $ r2 	 |                     d¦  «        | |<   n# t          $ r d| |<   Y nw xY wY Œ›w xY wŒ d 	                    | ¦  «        dz   S )a  
    DEPRECATED: do not use.  May be removed at any time.

    Format one or more data elements into string suitable for printing
    straight to stderr or other outputs

    >>> a = common.formatStr('test', '1', 2, 3)
    >>> print(a)
    test 1 2 3
    <BLANKLINE>
    zutf-8r   r8   ú
)
Úranger1   Ú
isinstanceÚbytesÚdecoder   ÚreprrM   ÚAttributeErrorr"   )ÚmsgÚrest_of_messageÚkeywordsr-   Úxs        r   r
   r
   
  s  € ð Ð
!�Ð
!€CÝ•3�s‘8”8‰_Œ_ð  ð  ˆØ�ŒFˆÝ�a�ÑÔð 	'Ø—X’X˜gÑ&Ô&ˆC�‰FÝ˜!�SÑ!Ô!ð 	 ð Ý˜a™œ��A‘�øÝð  ð  ð  ð ØŸXšX gÑ.Ô.�C˜‘F�FøÝ%ð  ð  ð  Ø�C˜‘F�F�Fð øøøøøð øøøð	 ð �8Š8�C‰=Œ=˜4ÑÐs6   Á/BÂ
B>ÂB&Â%B>Â&B8Â5B>Â7B8Â8B>Â=B>ÚinputStringc                ó2  — t          j        d| ¦  «                             dd¦  «                             dd¦  «                             dd¦  «                             dd¦  «                             d	d¦  «        }d
                     d„ |D ¦   «         ¦  «        S )uí  
    removes accents from unicode strings.

    >>> s = 'trÃ©s vite'
    >>> 'Ã©' in s
    True
    >>> common.stripAccents(s)
    'tres vite'

    Also handles the German Eszett and smart quotes

    >>> common.stripAccents('MuÃŸ')
    'Muss'
    >>> common.stripAccents('SÃ¼ss, â€œÃªtrÃ©â€�')
    'Suss, "etre"'

    Note -- it is still possible to have non-Ascii characters after this,
    like in this Japanese expression for music:

    >>> common.stripAccents('éŸ³æ¥½')
    'éŸ³æ¥½'
    ÚNFKDõ   ÃŸÚssu   â€œú"u   â€�u   â€˜ú'u   â€™r   c                ó:   — g | ]}t          j        |¦  «        °|‘ŒS © )ÚunicodedataÚ	combining)Ú.0Úcs     r   ú
<listcomp>z stripAccents.<locals>.<listcomp>G  s(   € ÐIÐIÐI˜!µÔ0EÀaÑ0HÔ0HÐI�AÐIÐIÐIr   )rg   Ú	normalizer;   r"   )r^   Ú	nfkd_forms     r   r   r   (  s‡   € õ0 	Ô˜f kÑ2Ô2ß	Š��tÑ	Ô	ß	Š�˜Ñ	Ô	ß	Š�˜Ñ	Ô	ß	Š�˜Ñ	Ô	ß	Š�˜Ñ	Ô	ð ð �7Š7ÐIÐI˜yÐIÑIÔIÑJÔJÐJr   Únamec                óh  — d}t          | ¦  «        }|dk    r3| d         dk    r't          | |dz
  d…         ¦  «        }| d|dz
  …         } t          | ¦  «        } |                      dd¦  «                             d¦  «        } t          j        d	d
| ¦  «                             ¦   «         } |�| |z  } | S )u¢  
    take a name that might contain unicode characters, punctuation,
    or spaces and
    normalize it so that it is POSIX compliant (except for the limit
    on length).

    Takes in a string or unicode string and returns a string (unicode in Py3)
    without any accented characters.

    >>> common.normalizeFilename('03-NiccolÃ² allâ€™lessandra.not really.xml')
    '03-Niccolo_all_lessandra_not_really.xml'
    Né   éüÿÿÿú.é   ÚasciiÚignorerG   z[^\w-]r9   )r1   r   r   rN   rW   r4   r   Ústrip)rn   Ú	extensionÚlenNames      r   r   r   J  s·   € ð €IÝ�$‰iŒi€Gà�‚{€{�t˜B”x 3’�Ý˜˜W q™[˜\˜\Ô*Ñ+Ô+ˆ	Ø�L�W˜q‘[�LÔ!ˆå˜ÑÔ€DØ�;Š;�w Ñ)Ô)×0Ò0°Ñ9Ô9€DÝŒ6�)˜S $Ñ'Ô'×-Ò-Ñ/Ô/€DØÐØ�	ÑˆØ€Kr   Úsc                ó|   — t                                ddt          j        ¦  «        }|                      |¦  «        }|S )z
    Remove all punctuation from a string.

    >>> common.removePunctuation('This, is! my (face).')
    'This is my face'
    r   )r   Ú	maketransÚstringÚpunctuationÚ	translate)ry   r{   Úouts      r   r   r   f  s2   € õ —’˜b "¥fÔ&8Ñ9Ô9€IØ
�+Š+�iÑ
 Ô
 €CØ€Jr   c                  ó8   — e Zd ZU ded<   ded<   ded<   ded<   dS )	r   ÚintÚstartÚendr   Útextúlist[ParenthesesMatch]ÚnestedN)Ú__name__Ú
__module__Ú__qualname__Ú__annotations__rf   r   r   r   r   q  s9   € € € € € € à€J€J�JØ€H€H�HØ€I€I�IØ"Ð"Ð"Ñ"Ð"Ð"r   r   ú(ú)ÚopenÚcloser…   c           	     ó†  — |r|st          d¦  «        ‚t          dddg ¦  «        }|g}d}d}|t          | ¦  «        k     �rD|sh| ||t          |¦  «        z   …         |k    rJt          |t          |¦  «        z   ddg ¦  «        }|                     |¦  «         |t          |¦  «        z  }Œ~|s­| ||t          |¦  «        z   …         |k    r�t          |¦  «        dk    rt          d|›d|›d	|› d
�¦  «        ‚|                     ¦   «         }||_        | |j        |…         |_        |d         j                             |¦  «         |t          |¦  «        z  }�Œ-| |         dk    r| }nd}|dz  }|t          | ¦  «        k     �°Dt          |¦  «        dk    r$t          d|›d	|d         j        dz
  › d�¦  «        ‚|j        S )ap  
    Utility tool to return a list of parentheses matches for a string using a dataclass
    called `ParenthesesMatch` which has indices of the `start` and `end`
    of the match, and the `text` of the match, and a set of `nested`
    ParenthesesMatch objects (which may have their own nested objects).

    >>> st = r'Bologne wrote (a (whole) (lot) \(of\)) sym\(ph(on)ies\) concertantes.'
    >>> common.stringTools.parenthesesMatch(st)
    [ParenthesesMatch(start=15, end=37, text='a (whole) (lot) \\(of\\)',
                      nested=[ParenthesesMatch(start=18, end=23, text='whole', nested=[]),
                              ParenthesesMatch(start=26, end=29, text='lot', nested=[])]),
     ParenthesesMatch(start=47, end=49, text='on', nested=[])]

    Other brackets can be used:

    >>> st = r'[Whammy bars] and [oboes] do [not [mix] very] [well.]'
    >>> common.stringTools.parenthesesMatch(st, open='[', close=']')
    [ParenthesesMatch(start=1, end=12, text='Whammy bars', nested=[]),
     ParenthesesMatch(start=19, end=24, text='oboes', nested=[]),
     ParenthesesMatch(start=30, end=44, text='not [mix] very',
                      nested=[ParenthesesMatch(start=35, end=38, text='mix', nested=[])]),
     ParenthesesMatch(start=47, end=52, text='well.', nested=[])]

    The `open` and `close` parameters can be multiple characters:

    >>> st = r'Did you eat <<beans>> today <<Pythagoreas<<?>>>>'
    >>> common.stringTools.parenthesesMatch(st, open='<<', close='>>')
    [ParenthesesMatch(start=14, end=19, text='beans', nested=[]),
     ParenthesesMatch(start=30, end=46, text='Pythagoreas<<?>>',
                      nested=[ParenthesesMatch(start=43, end=44, text='?', nested=[])])]

    They cannot, however, be empty:

    >>> common.stringTools.parenthesesMatch(st, open='', close='')
    Traceback (most recent call last):
    ValueError: Neither open nor close can be empty.

    Unmatched opening or closing parentheses will raise a ValueError:

    >>> common.stringTools.parenthesesMatch('My (parentheses (sometimes (continue',)
    Traceback (most recent call last):
    ValueError:  Opening '(' at index 3 was never closed

    >>> common.stringTools.parenthesesMatch('This is a <bad> example>', open='<', close='>')
    Traceback (most recent call last):
    ValueError: Closing '>' without '<' at index 23.

    Note that using multiple characters like a prefix can have unintended consequences:

    >>> st = r'[Pitch("C4"), [Pitch("D5"), Pitch("E6")], Pitch("Pity("Z9")")]'
    >>> common.stringTools.parenthesesMatch(st, open='Pitch("', close='")')
    Traceback (most recent call last):
    ValueError: Closing '")' without 'Pitch("' at index 59.

    So to do something like this, you might need to get creative:

    >>> out = common.stringTools.parenthesesMatch(st, open='("', close='")')
    >>> out
    [ParenthesesMatch(start=8, end=10, text='C4', nested=[]),
     ParenthesesMatch(start=22, end=24, text='D5', nested=[]),
     ParenthesesMatch(start=35, end=37, text='E6', nested=[]),
     ParenthesesMatch(start=49, end=59, text='Pity("Z9")',
                      nested=[ParenthesesMatch(start=55, end=57, text='Z9', nested=[])])]
    >>> extractedPitches = []
    >>> for match in out:
    ...     if st[match.start - 7:match.start] == 'Pitch("':
    ...          extractedPitches.append(match.text)
    >>> extractedPitches
    ['C4', 'D5', 'E6', 'Pity("Z9")']

    * New in v9.3.
    z$Neither open nor close can be empty.éÿÿÿÿr   Fr   r0   zClosing z	 without z
 at index rr   ú\zOpening z was never closed)	r2   r   r1   r!   Úpoprƒ   r‚   r„   r†   )ry   r�   rŽ   Ú	mainMatchÚstackÚlastCharWasBackslashr-   ÚcurPMs           r   r   r   x  sô  € ðZ ð A�uð AÝÐ?Ñ@Ô@Ð@å   R¨¨RÑ0Ô0€IØ%. K€Eà Ðà	€AØ
�c�!‰fŒfŠ*‰*Ø$ð 	Ø�a˜�C ™IœI™�oÔ&¨$Ò.Ð.Ý$ Q­¨T©¬¡]°B¸¸BÑ?Ô?ˆEØ�LŠL˜ÑÔÐØ•�T‘”‰NˆAØØ&ð 		Ø�A�a�#˜e™*œ*‘nÐ$Ô%¨Ò.Ð.Ý�5‰zŒz˜QŠˆÝ Ð!T¨EÐ!TÐ!T¸dÐ!TÐ!TÐPQÐ!TÐ!TÐ!TÑUÔUÐUØ—I’I‘K”KˆEØˆEŒIØ˜5œ; q˜=Ô)ˆEŒJØ�"ŒIÔ×#Ò# EÑ*Ô*Ð*Ø•�U‘”‰OˆAÙàˆQŒ4�4Š<ˆ<Ø';Ð#;Ð Ð à#(Ð Ø	ˆQ‰ˆð- �c�!‰fŒfŠ*‰*õ0 ˆ5�z„z�A‚~€~ÝÐ[ DÐ[Ð[°e¸A´h´nÀqÑ6HÐ[Ð[Ð[Ñ\Ô\Ð\àÔÐr   Ú__main__)r   r   r   r   r   r   )r   )r   r   r   r   r   r   )r&   )r   r   r'   r   r   r   )TN)r   r   r   r   )N)r   r   )r^   r   r   r   )rn   r   r   r   )ry   r   r   r   )r‹   rŒ   )ry   r   r�   r   rŽ   r   r   r…   )Ú__doc__Ú
__future__r   Ú__all__ÚdataclassesrJ   rI   r4   rH   r|   rg   Úcompiler   r   r   r   r   r   r   r	   r
   r   r   r   Ú	dataclassr   r   r‡   Úmusic21ÚmainTestrf   r   r   ú<module>r       s&  ððð ð #Ð "Ð "Ð "Ð "Ð "ðð ð €ð Ð Ð Ð Ø €€€Ø €€€Ø 	€	€	€	Ø €€€Ø €€€Ø Ð Ð Ð ð ˆRŒZ˜ÑÔ€
Øˆ2Œ:�eÑÔ€ðð ð ð ð.+ð +ð +ð +ð +ð.ð ð ð ð ð@(Pð (Pð (Pð (Pð (PðVNð Nð Nð Nð Nðbð ð ð ð ð( ð  ð  ð  ð<Kð Kð Kð KðDð ð ð ð8	ð 	ð 	ð 	ð Ôð#ð #ð #ð #ð #ñ #ô #ñ Ôð#ð Øðqð qð qð qð qðj ˆzÒÐØ€N€N€NØ€GÔÑÔÐÐÐð Ðr   