lue-bird/elm-syntax-type-infer/1.0.3

NOTE: the latest version is 2.0.8

ElmSyntaxTypeInfer

Add type information to the nodes of an elm-syntax tree.

{ importedTypes : Dict ModuleName ModuleTypes
, moduleOriginLookup : ModuleOriginLookup
, otherModuleDeclaredTypes : ModuleTypes
}
-> List Function
-> Result
String
(Dict
String
{ parameters :
List (TypedNode (Pattern (Type String)) (Type String))
, result : TypedNode (Expression (Type String)) (Type String)
, type_ : Type String
, nameRange : Range
, documentation :
Maybe
{ content : String
, range : Range
}
, signature :
Maybe
{ range : Range
, nameRange : Range
, annotationTypeRange : Range
, annotationType : TypeAnnotation
}
}
)

Infer types of value/Elm.Syntax.Expression.Function declarations in a module.

import Elm.Syntax.Node
import Elm.Syntax.Expression
import ElmSyntaxTypeInfer




[ { declaration =
        Elm.Syntax.Node.empty
            { name = Elm.Syntax.Node.empty "hello"
            , expression =
                Elm.Syntax.Node.empty
                    (Elm.Syntax.Expression.Literal "world")
            , arguments = []
            }
  , signature = Nothing
  , documentation = Nothing
  }
]
    |> ElmSyntaxTypeInfer.valueAndFunctionDeclarations
        { importedTypes = ElmSyntaxTypeInfer.elmCoreTypes
        , moduleOriginLookup = exampleModuleOriginLookup
        , otherModuleDeclaredTypes =
            []
                |> ElmSyntaxTypeInfer.moduleDeclarationsToTypes
                    exampleModuleOriginLookup
                |> .types
        }
-->
Ok
    (FastDict.singleton "hello"
        { type_ =
            ElmSyntaxTypeInfer.TypeNotVariable
                (ElmSyntaxTypeInfer.TypeConstruct
                    { moduleOrigin = [ "String" ], name = "String", arguments = [] }
                )
        ...
        }
    )




exampleModuleOriginLookup : ElmSyntaxTypeInfer.ModuleOriginLookup
exampleModuleOriginLookup =
    []
        |> ElmSyntaxTypeInfer.importsToModuleOriginLookup
            ElmSyntaxTypeInfer.elmCoreTypes

See ModuleTypes and ModuleOriginLookup

Warning! Always first verify that provided type alias declarations aren't (mutually) recursive. Otherwise this function might never terminate.

context

type alias ModuleTypes =
{ signatures : Dict String (Type String)
, typeAliases :
Dict
String
{ parameters : List String
, type_ : Type String
, recordFieldOrder : Maybe (List String)
}
, choiceTypes :
Dict
String
{ parameters : List String
, variants : Dict String (List (Type String))
}
}

Known types of members in a module. Create with moduleDeclarationsToTypes and moduleInterfaceToTypes

elmCoreTypes : Dict ModuleName ModuleTypes

ModuleTypes exposed in elm/core.

Please always start with elmCoreTypes and add further module info with FastDict.union using moduleDeclarationsToTypes and moduleInterfaceToTypes

And if for some reason you already know used dependencies at compile time, you can re-use the code generator used for these elm/core types

ModuleOriginLookup
-> List Declaration
-> { types : ModuleTypes
, errors : List String
}

Extract all known types from declarations within a module. Un-annotated value/function declarations are ignored.

For dependency modules, use moduleInterfaceToTypes

Module
-> { types : ModuleTypes
, errors : List String
}

Extract all known types from module metadata, usually from parsing the docs.json of a dependency.

For elm-syntax modules, use moduleDeclarationsToTypes

type alias ModuleOriginLookup =
{ references : Dict ( ModuleName, String ) ModuleName
, typeConstructs : Dict ( ModuleName, String ) ModuleName
, keepOperatorIsExposedFromParserAdvanced : Bool
, ignoreOperatorIsExposedFromParserAdvanced : Bool
}

How to map names and symbols used in a module to their origin module. Create with importsToModuleOriginLookup.

Contains variants, type alias names, choice type names, port names, value/function declaration names and whether (|.) and or (|=) are imported from Parser.Advanced.

Also contains locally declared names when available.

Type- and expression/pattern-level members are separated since you can for example have a local List variant while also using an (implicitly) imported List type.

Dict ModuleName ModuleTypes
-> List (Node Import)
-> ModuleOriginLookup

Calculate valid mappings of qualifications + name to origin module based on a module's imports.

Requires all exposed ModuleTypes so we can resolve exposing (..) and ChoiceType(..).

syntax

type alias TypedNode value type_ =
{ range : Range
, value : value
, type_ : type_
}

A part in the syntax tree with an attached

  • range in the source
  • global Type: concretely inferred, influenced by other branches and value/function (let) declaration type annotations

Since type is a parameter, you can fill it with any representation like Elm.Syntax.TypeAnnotation.TypeAnnotation

type Expression type_
= ExpressionUnit
| ExpressionInteger
{ base : Base10Or16
, value : Int
}
| ExpressionFloat Float
| ExpressionString String
| ExpressionChar Char
| ExpressionReference
{ moduleOrigin : ModuleName
, qualification : ModuleName
, name : String
}
| ExpressionOperatorFunction
{ moduleOrigin : ModuleName
, symbol : String
}
| ExpressionRecordAccessFunction String
| ExpressionNegation (TypedNode (Expression type_) type_)
| ExpressionParenthesized (TypedNode (Expression type_) type_)
| ExpressionTuple
{ part0 : TypedNode (Expression type_) type_
, part1 : TypedNode (Expression type_) type_
}
| ExpressionTriple
{ part0 : TypedNode (Expression type_) type_
, part1 : TypedNode (Expression type_) type_
, part2 : TypedNode (Expression type_) type_
}
| ExpressionRecordAccess
{ record : TypedNode (Expression type_) type_
, fieldNameRange : Range
, fieldName : String
}
| ExpressionInfixOperation
{ operator :
{ moduleOrigin : ModuleName
, symbol : String
, type_ : type_
}
, left : TypedNode (Expression type_) type_
, right : TypedNode (Expression type_) type_
}
| ExpressionIfThenElse
{ condition : TypedNode (Expression type_) type_
, onTrue : TypedNode (Expression type_) type_
, onFalse : TypedNode (Expression type_) type_
}
| ExpressionList (List (TypedNode (Expression type_) type_))
| ExpressionCall
{ called : TypedNode (Expression type_) type_
, argument0 : TypedNode (Expression type_) type_
, argument1Up : List (TypedNode (Expression type_) type_)
}
| ExpressionRecord
(List
{ range : Range
, name : String
, nameRange : Range
, value : TypedNode (Expression type_) type_
}
)
| ExpressionRecordUpdate
{ recordVariable :
TypedNode
{ moduleOrigin : ModuleName
, name : String
}
type_
, field0 :
{ range : Range
, name : String
, nameRange : Range
, value : TypedNode (Expression type_) type_
}
, field1Up :
List
{ range : Range
, name : String
, nameRange : Range
, value : TypedNode (Expression type_) type_
}
}
| ExpressionLambda
{ parameter0 : TypedNode (Pattern type_) type_
, parameter1Up : List (TypedNode (Pattern type_) type_)
, result : TypedNode (Expression type_) type_
}
| ExpressionCaseOf
{ matchedExpression : TypedNode (Expression type_) type_
, case0 :
{ pattern : TypedNode (Pattern type_) type_
, result : TypedNode (Expression type_) type_
}
, case1Up :
List
{ pattern : TypedNode (Pattern type_) type_
, result : TypedNode (Expression type_) type_
}
}
| ExpressionLetIn
{ declaration0 :
{ range : Range
, declaration : LetDeclaration type_
}
, declaration1Up :
List
{ range : Range
, declaration : LetDeclaration type_
}
, result : TypedNode (Expression type_) type_
}

Like Elm.Syntax.Expression.Expression but its sub-nodes are TypedNodes

type LetDeclaration type_
= LetDestructuring
{ pattern : TypedNode (Pattern type_) type_
, expression : TypedNode (Expression type_) type_
}
| LetValueOrFunctionDeclaration
{ signature :
Maybe
{ range : Range
, nameRange : Range
, annotationType : TypeAnnotation
, annotationTypeRange : Range
}
, nameRange : Range
, name : String
, parameters : List (TypedNode (Pattern type_) type_)
, result : TypedNode (Expression type_) type_
, type_ : type_
}
type Pattern type_
= PatternIgnored
| PatternVariable String
| PatternParenthesized (TypedNode (Pattern type_) type_)
| PatternAs
{ pattern : TypedNode (Pattern type_) type_
, variable : TypedNode String type_
}
| PatternUnit
| PatternChar Char
| PatternString String
| PatternInt
{ base : Base10Or16
, value : Int
}
| PatternTuple
{ part0 : TypedNode (Pattern type_) type_
, part1 : TypedNode (Pattern type_) type_
}
| PatternTriple
{ part0 : TypedNode (Pattern type_) type_
, part1 : TypedNode (Pattern type_) type_
, part2 : TypedNode (Pattern type_) type_
}
| PatternRecord (List (TypedNode String type_))
| PatternListCons
{ head : TypedNode (Pattern type_) type_
, tail : TypedNode (Pattern type_) type_
}
| PatternListExact (List (TypedNode (Pattern type_) type_))
| PatternVariant
{ moduleOrigin : ModuleName
, qualification : ModuleName
, name : String
, values : List (TypedNode (Pattern type_) type_)
}

Like Elm.Syntax.Pattern.Pattern but all its sub-nodes are TypedNodes

= Base10
| Base16

Either decimal or hexadecimal. Used by PatternInt and ExpressionInteger

type Type variable
= TypeVariable variable
| TypeNotVariable (TypeNotVariable variable)

Type information attached to expressions and patterns, see TypedNode.

This is different from Elm.Syntax.TypeAnnotation.TypeAnnotation in that it doesn't contain information unrelated to type inference like ranges, qualification levels or parens.

type TypeNotVariable variable
= TypeUnit
| TypeConstruct
{ moduleOrigin : ModuleName
, name : String
, arguments : List (Type variable)
}
| TypeTuple
{ part0 : Type variable
, part1 : Type variable
}
| TypeTriple
{ part0 : Type variable
, part1 : Type variable
, part2 : Type variable
}
| TypeRecord (Dict String (Type variable))
| TypeRecordExtension
{ recordVariable : variable
, fields : Dict String (Type variable)
}
| TypeFunction
{ input : Type variable
, output : Type variable
}

Type except the variable case

If you are interested in exposing helpers like expressionMapType, open an issue