<commit_before>module Music.Chart.Parsers exposing (..)

import Parser exposing (..)
import Music.Chart exposing (..)
import Music.Chord as Chord exposing (..)
import Music.Note as Note exposing (..)


newLine : Parser ()
newLine =
    symbol "\n"


spaces : Parser ()
spaces =
    ignore zeroOrMore (\c -> c == ' ')


spacesAndNewlines : Parser ()
spacesAndNewlines =
    ignore zeroOrMore (\c -> c == ' ' || c == '\n')


keepUntilEndOfLine : Parser String
keepUntilEndOfLine =
    keep oneOrMore (\c -> c /= '\n')


chart : Parser Chart
chart =
    let
        dashes =
            "---"
    in
        inContext "chart" <|
            succeed Chart
                |. spacesAndNewlines
                |. symbol dashes
                |. newLine
                |. symbol "title:"
                |. spaces
                |= keepUntilEndOfLine
                |. newLine
                |. symbol "key:"
                |. spaces
                |= note
                |. newLine
                |. symbol dashes
                |. spacesAndNewlines
                |= repeat oneOrMore (part |. spacesAndNewlines)
                |. end


part : Parser Part
part =
    inContext "part" <|
        succeed (\partName toPart -> toPart partName)
            |. symbol "="
            |. spaces
            |= keepUntilEndOfLine
            |. spacesAndNewlines
            |= oneOf
                [ succeed (\bars partName -> Part partName bars)
                    |= repeat oneOrMore (bar |. spacesAndNewlines)
                , succeed PartRepeat
                ]


bar : Parser Bar
bar =
    inContext "bar" <|
        oneOf
            [ succeed BarRepeat
                |. symbol "-"
            , succeed Bar
                |= chords
            ]


chords : Parser (List Chord)
chords =
    inContext "chords" <|
        succeed (::)
            |= chord
            |= repeat zeroOrMore
                (succeed identity
                    |. symbol "/"
                    |= chord
                )


chord : Parser Chord
chord =
    inContext "chord" <|
        succeed (,)
            |= note
            |= quality


quality : Parser Quality
quality =
    inContext "quality" <|
        oneOfTuples Chord.qualitiesAndStrings


note : Parser Note
note =
    inContext "note" <|
        oneOfTuples Note.notesAndStrings


oneOfTuples : List ( a, String ) -> Parser a
oneOfTuples tuples =
    let
        -- Sort tuples to avoid having empty strings first, which would match in every cases.
        sortTuples =
            List.sortBy (Tuple.second >> String.length)
                >> List.reverse
    in
        oneOf
            (tuples
                |> sortTuples
                |> List.map
                    (\( x, name ) ->
                        succeed x
                            |. symbol name
                    )
            )
<commit_msg>Patch 1: Clearer part construction in parser<commit_after>module Music.Chart.Parsers exposing (..)

import Parser exposing (..)
import Music.Chart exposing (..)
import Music.Chord as Chord exposing (..)
import Music.Note as Note exposing (..)


newLine : Parser ()
newLine =
    symbol "\n"


spaces : Parser ()
spaces =
    ignore zeroOrMore (\c -> c == ' ')


spacesAndNewlines : Parser ()
spacesAndNewlines =
    ignore zeroOrMore (\c -> c == ' ' || c == '\n')


keepUntilEndOfLine : Parser String
keepUntilEndOfLine =
    keep oneOrMore (\c -> c /= '\n')


chart : Parser Chart
chart =
    let
        dashes =
            "---"
    in
        inContext "chart" <|
            succeed Chart
                |. spacesAndNewlines
                |. symbol dashes
                |. newLine
                |. symbol "title:"
                |. spaces
                |= keepUntilEndOfLine
                |. newLine
                |. symbol "key:"
                |. spaces
                |= note
                |. newLine
                |. symbol dashes
                |. spacesAndNewlines
                |= repeat oneOrMore (part |. spacesAndNewlines)
                |. end


part : Parser Part
part =
    inContext "part" <|
        succeed asPart
            |. symbol "="
            |. spaces
            |= keepUntilEndOfLine
            |. spacesAndNewlines
            |= oneOf
                [ succeed Just
                    |= repeat oneOrMore (bar |. spacesAndNewlines)
                , succeed Nothing
                ]


asPart : String -> Maybe (List Bar) -> Part
asPart name bars =
    case bars of
        Nothing ->
            PartRepeat name

        Just b ->
            Part name b


bar : Parser Bar
bar =
    inContext "bar" <|
        oneOf
            [ succeed BarRepeat
                |. symbol "-"
            , succeed Bar
                |= chords
            ]


chords : Parser (List Chord)
chords =
    inContext "chords" <|
        succeed (::)
            |= chord
            |= repeat zeroOrMore
                (succeed identity
                    |. symbol "/"
                    |= chord
                )


chord : Parser Chord
chord =
    inContext "chord" <|
        succeed (,)
            |= note
            |= quality


quality : Parser Quality
quality =
    inContext "quality" <|
        oneOfTuples Chord.qualitiesAndStrings


note : Parser Note
note =
    inContext "note" <|
        oneOfTuples Note.notesAndStrings


oneOfTuples : List ( a, String ) -> Parser a
oneOfTuples tuples =
    let
        -- Sort tuples to avoid having empty strings first, which would match in every cases.
        sortTuples =
            List.sortBy (Tuple.second >> String.length)
                >> List.reverse
    in
        oneOf
            (tuples
                |> sortTuples
                |> List.map
                    (\( x, name ) ->
                        succeed x
                            |. symbol name
                    )
            )

{'fraction_non_alphanumeric': 0.09451892075343628, 'fraction_numerical': 0.00016969285593076533, 'mean_word_length': 1.1951582867783985, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 31, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7121051', 'n_tokens_mistral': 1683, 'n_tokens_neox': 1561, 'n_words': 423}