<commit_before>module AST.Decoding exposing (decode, decodeInfix)

import AST.Ranges as Ranges exposing (Range)
import AST.Types exposing (..)
import Json.Decode exposing (Decoder, field, list, string, map, map2, map3, map4, succeed, maybe, lazy, int, bool, andThen, float, fail, at)
import Json.Decode.Extra exposing ((|:))
import Util.Json exposing (decodeTyped)


decodeTypedWithRange : List ( String, Decoder (Range -> a) ) -> Decoder a
decodeTypedWithRange opts =
    lazy
        (\() ->
            field "type" string
                |> andThen
                    (\t ->
                        case List.filter (Tuple.first >> (==) t) opts |> List.head of
                            Just m ->
                                (field (Tuple.first m) <| Tuple.second m)
                                    |: at [ Tuple.first m, "range" ] Ranges.decode

                            Nothing ->
                                fail ("No decoder for type: " ++ t)
                    )
        )


rangeField : Decoder Range
rangeField =
    field "range" Ranges.decode


nameField : Decoder String
nameField =
    field "name" string


decode : Decoder File
decode =
    succeed File
        |: field "moduleDefinition" decodeModule
        |: field "imports" (list decodeImport)
        |: field "declarations" (list decodeDeclaration)
        |: field "comments" (list decodeComment)


decodeComment : Decoder ( String, Range )
decodeComment =
    succeed (,)
        |: field "text" string
        |: field "range" Ranges.decode


decodeModule : Decoder Module
decodeModule =
    decodeTyped
        [ ( "normal", decodeDefaultModuleData |> map NormalModule )
        , ( "port", decodeDefaultModuleData |> map PortModule )
        , ( "effect", decodeEffectModuleData |> map EffectModule )
        , ( "nomodule", succeed NoModule )
        ]


decodeDefaultModuleData : Decoder DefaultModuleData
decodeDefaultModuleData =
    succeed DefaultModuleData
        |: field "moduleName" decodeModuleName
        |: field "exposingList" (decodeExposingList decodeExpose)


decodeEffectModuleData : Decoder EffectModuleData
decodeEffectModuleData =
    succeed EffectModuleData
        |: field "moduleName" decodeModuleName
        |: field "exposingList" (decodeExposingList decodeExpose)
        |: field "command" (maybe string)
        |: field "subscription" (maybe string)


decodeModuleName : Decoder ModuleName
decodeModuleName =
    list string


decodeExpose : Decoder Expose
decodeExpose =
    decodeTyped
        [ ( "infix", map2 InfixExpose nameField rangeField )
        , ( "function", map2 FunctionExpose nameField rangeField )
        , ( "typeOrAlias", map2 TypeOrAliasExpose nameField rangeField )
        , ( "typeexpose", map TypeExpose decodeExposedType )
        ]


decodeExposedType : Decoder ExposedType
decodeExposedType =
    succeed ExposedType
        |: nameField
        |: field "inner" (decodeExposingList decodeValueConstructorExpose)
        |: rangeField


decodeValueConstructorExpose : Decoder ValueConstructorExpose
decodeValueConstructorExpose =
    succeed (,)
        |: nameField
        |: rangeField


decodeExposingList : Decoder a -> Decoder (Exposure a)
decodeExposingList x =
    lazy
        (\() ->
            decodeTyped
                [ ( "none", succeed None )
                , ( "all", Ranges.decode |> map All )
                , ( "explicit", list x |> map Explicit )
                ]
        )


decodeImport : Decoder Import
decodeImport =
    succeed Import
        |: field "moduleName" decodeModuleName
        |: field "moduleAlias" (maybe decodeModuleName)
        |: field "exposingList" (decodeExposingList decodeExpose)
        |: rangeField


decodeDeclaration : Decoder Declaration
decodeDeclaration =
    lazy
        (\() ->
            decodeTyped
                [ ( "function", decodeFunction |> map FuncDecl )
                , ( "typeAlias", decodeTypeAlias |> map AliasDecl )
                , ( "typedecl", decodeType |> map TypeDecl )
                , ( "port", decodeSignature |> map PortDeclaration )
                , ( "infix", decodeInfix |> map InfixDeclaration )
                , ( "destrucutring", decodeDestructuring |> map DestructuringDeclaration )
                ]
        )


decodeInfix : Decoder Infix
decodeInfix =
    succeed Infix
        |: field "direction" decodeInfixDirection
        |: field "precedence" int
        |: field "operator" string


decodeDestructuring : Decoder Destructuring
decodeDestructuring =
    lazy
        (\() ->
            succeed Destructuring
                |: field "pattern" decodePattern
                |: field "expression" decodeExpression
        )


decodeType : Decoder Type
decodeType =
    succeed Type
        |: nameField
        |: field "generics" (list string)
        |: field "constructors" (list decodeValueConstructor)


decodeValueConstructor : Decoder ValueConstructor
decodeValueConstructor =
    succeed ValueConstructor
        |: nameField
        |: field "arguments" (list decodeTypeReference)
        |: rangeField


decodeTypeAlias : Decoder TypeAlias
decodeTypeAlias =
    succeed TypeAlias
        |: nameField
        |: field "generics" (list string)
        |: field "typeReference" decodeTypeReference
        |: rangeField


decodeFunction : Decoder Function
decodeFunction =
    lazy
        (\() ->
            succeed Function
                |: field "documentation" (maybe string)
                |: field "signature" (maybe decodeSignature)
                |: field "declaration" decodeFunctionDeclaration
        )


decodeSignature : Decoder FunctionSignature
decodeSignature =
    succeed FunctionSignature
        |: field "operatorDefinition" bool
        |: nameField
        |: field "typeReference" decodeTypeReference

<commit_msg>Issue #26: Stricter decoding of cached files<commit_after>module AST.Decoding exposing (decode, decodeInfix)

import AST.Ranges as Ranges exposing (Range)
import AST.Types exposing (..)
import Json.Decode exposing (Decoder, field, list, string, map, map2, map3, map4, succeed, lazy, int, bool, andThen, float, fail, at, nullable)
import Json.Decode.Extra exposing ((|:))
import Util.Json exposing (decodeTyped)


decodeTypedWithRange : List ( String, Decoder (Range -> a) ) -> Decoder a
decodeTypedWithRange opts =
    lazy
        (\() ->
            field "type" string
                |> andThen
                    (\t ->
                        case List.filter (Tuple.first >> (==) t) opts |> List.head of
                            Just m ->
                                (field (Tuple.first m) <| Tuple.second m)
                                    |: at [ Tuple.first m, "range" ] Ranges.decode

                            Nothing ->
                                fail ("No decoder for type: " ++ t)
                    )
        )


rangeField : Decoder Range
rangeField =
    field "range" Ranges.decode


nameField : Decoder String
nameField =
    field "name" string


decode : Decoder File
decode =
    succeed File
        |: field "moduleDefinition" decodeModule
        |: field "imports" (list decodeImport)
        |: field "declarations" (list decodeDeclaration)
        |: field "comments" (list decodeComment)


decodeComment : Decoder ( String, Range )
decodeComment =
    succeed (,)
        |: field "text" string
        |: field "range" Ranges.decode


decodeModule : Decoder Module
decodeModule =
    decodeTyped
        [ ( "normal", decodeDefaultModuleData |> map NormalModule )
        , ( "port", decodeDefaultModuleData |> map PortModule )
        , ( "effect", decodeEffectModuleData |> map EffectModule )
        , ( "nomodule", succeed NoModule )
        ]


decodeDefaultModuleData : Decoder DefaultModuleData
decodeDefaultModuleData =
    succeed DefaultModuleData
        |: field "moduleName" decodeModuleName
        |: field "exposingList" (decodeExposingList decodeExpose)


decodeEffectModuleData : Decoder EffectModuleData
decodeEffectModuleData =
    succeed EffectModuleData
        |: field "moduleName" decodeModuleName
        |: field "exposingList" (decodeExposingList decodeExpose)
        |: field "command" (nullable string)
        |: field "subscription" (nullable string)


decodeModuleName : Decoder ModuleName
decodeModuleName =
    list string


decodeExpose : Decoder Expose
decodeExpose =
    decodeTyped
        [ ( "infix", map2 InfixExpose nameField rangeField )
        , ( "function", map2 FunctionExpose nameField rangeField )
        , ( "typeOrAlias", map2 TypeOrAliasExpose nameField rangeField )
        , ( "typeexpose", map TypeExpose decodeExposedType )
        ]


decodeExposedType : Decoder ExposedType
decodeExposedType =
    succeed ExposedType
        |: nameField
        |: field "inner" (decodeExposingList decodeValueConstructorExpose)
        |: rangeField


decodeValueConstructorExpose : Decoder ValueConstructorExpose
decodeValueConstructorExpose =
    succeed (,)
        |: nameField
        |: rangeField


decodeExposingList : Decoder a -> Decoder (Exposure a)
decodeExposingList x =
    lazy
        (\() ->
            decodeTyped
                [ ( "none", succeed None )
                , ( "all", Ranges.decode |> map All )
                , ( "explicit", list x |> map Explicit )
                ]
        )


decodeImport : Decoder Import
decodeImport =
    succeed Import
        |: field "moduleName" decodeModuleName
        |: field "moduleAlias" (nullable decodeModuleName)
        |: field "exposingList" (decodeExposingList decodeExpose)
        |: rangeField


decodeDeclaration : Decoder Declaration
decodeDeclaration =
    lazy
        (\() ->
            decodeTyped
                [ ( "function", decodeFunction |> map FuncDecl )
                , ( "typeAlias", decodeTypeAlias |> map AliasDecl )
                , ( "typedecl", decodeType |> map TypeDecl )
                , ( "port", decodeSignature |> map PortDeclaration )
                , ( "infix", decodeInfix |> map InfixDeclaration )
                , ( "destrucutring", decodeDestructuring |> map DestructuringDeclaration )
                ]
        )


decodeInfix : Decoder Infix
decodeInfix =
    succeed Infix
        |: field "direction" decodeInfixDirection
        |: field "precedence" int
        |: field "operator" string


decodeDestructuring : Decoder Destructuring
decodeDestructuring =
    lazy
        (\() ->
            succeed Destructuring
                |: field "pattern" decodePattern
                |: field "expression" decodeExpression
        )


decodeType : Decoder Type
decodeType =
    succeed Type
        |: nameField
        |: field "generics" (list string)
        |: field "constructors" (list decodeValueConstructor)


decodeValueConstructor : Decoder ValueConstructor
decodeValueConstructor =
    succeed ValueConstructor
        |: nameField
        |: field "arguments" (list decodeTypeReference)
        |: rangeField


decodeTypeAlias : Decoder TypeAlias
decodeTypeAlias =
    succeed TypeAlias
        |: nameField
        |: field "generics" (list string)
        |: field "typeReference" decodeTypeReference
        |: rangeField


decodeFunction : Decoder Function
decodeFunction =
    lazy
        (\() ->
            succeed Function
                |: field "documentation" (nullable string)
                |: field "signature" (nullable decodeSignature)
                |: field "declaration" decodeFunctionDeclaration
        )


decodeSignature : Decoder FunctionSignature
decodeSignature =
    succeed FunctionSignature
        |: field "operatorDefinition" bool
        |: nameField
        |: field "typeReference" decodeTypeReference


{'fraction_non_alphanumeric': 0.0871273596993252, 'fraction_numerical': 0.001195865721363287, 'mean_word_length': 2.291537812763565, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 55, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5791169', 'n_tokens_mistral': 3135, 'n_tokens_neox': 3061, 'n_words': 814}