added: TypeShape

This commit is contained in:
klntsky 2019-06-28 14:58:15 +03:00
parent 4e5570df7d
commit f354ade0f7
No known key found for this signature in database
GPG Key ID: 612281040BC67F9E
8 changed files with 457 additions and 129 deletions

View File

@ -20,7 +20,7 @@
, "node-fs"
, "node-fs-aff"
, "node-process"
, "psci-support"
, "profunctor"
, "search-trie"
, "strings"
, "string-parsers"

View File

@ -1,13 +1,15 @@
module Spago.Search.App.SearchResults where
import Prelude
import Spago.Search.Index
import Spago.Search.TypeDecoder
import Spago.Search.TypeShape
import Spago.Search.TypeQuery
import CSS (textWhitespace, whitespacePreWrap)
import Data.Array as Array
import Data.Either (Either(..))
import Data.List (List(..), (:))
import Data.List as List
import Data.Maybe (Maybe(..), isNothing, isJust)
import Data.Newtype (unwrap, wrap)
@ -32,8 +34,12 @@ import Web.HTML as HTML
import Web.HTML.Location as Location
import Web.HTML.Window as Window
-- | Is it a search by type or by declaration?
data ResultsType = TypeResults TypeQuery | DeclResults
type State = { mbIndex :: Maybe SearchIndex
, results :: Array SearchResult
, resultsType :: ResultsType
, input :: String
, contents :: Element
, resultsCount :: Int
@ -51,6 +57,7 @@ mkComponent contents =
H.mkComponent
{ initialState: const { mbIndex: Nothing
, results: []
, resultsType: DeclResults
, input: ""
, contents
, resultsCount: 25
@ -87,11 +94,28 @@ handleQuery (SearchFieldMessage (InputUpdated input) next) = do
let path = List.fromFoldable $
String.toCharArray $
String.toLower input
H.modify_ (_ { results = Array.concat $
List.toUnfoldable $
map List.toUnfoldable $
Trie.queryValues path $
index
eiTypeQuery = parseTypeQuery input
resultsType =
case eiTypeQuery of
Left _ -> DeclResults
Right query
| isValuableQuery query -> TypeResults query
| otherwise -> DeclResults
results =
case resultsType of
DeclResults ->
Array.concat $
List.toUnfoldable $
map List.toUnfoldable $
Trie.queryValues path $
index.decls
TypeResults query ->
List.toUnfoldable $
Trie.queryValues shape index.types >>= identity
where
shape = shapeOfTypeQuery query
H.modify_ (_ { results = results
, resultsType = resultsType
, resultsCount = 25 })
pure Nothing
@ -149,10 +173,13 @@ render state =
]
else
let selectedResults = Array.take state.resultsCount state.results in
[ HH.div [ HP.classes [ wrap "result" ] ]
[ HH.div [ HP.classes [ wrap "result" ] ] $
[ HH.text "Found "
, HH.strong_ [ HH.text $ show $ Array.length state.results ]
, HH.text " definitions"
, HH.text $
case state.resultsType of
DeclResults -> " definitions."
TypeResults _ -> " definitions with similar types."
]
, HH.div [ HP.id_ "spage-search-results-container" ] $
@ -168,7 +195,6 @@ render state =
]
]
renderSummary :: forall a b. String -> HH.HTML b a
renderSummary text =
HH.div [ HP.id_ "spago-search-summary" ]
@ -259,7 +285,11 @@ renderType = case _ of
TypeApp (TypeConstructor (QualifiedName { moduleName: ["Prim"]
, name: "Record" }))
record -> renderRow record
record ->
HH.span_ [ HH.text "{ | "
, renderType record
, HH.text " }" ]
TypeApp t1 t2 ->
HH.span_ [ renderType t1
@ -268,10 +298,10 @@ renderType = case _ of
]
ty@(ForAll _ _ _) ->
let foralls = joinForalls ty in
let foralls = joinForAlls ty in
HH.span_ $
[ HH.text "forall" ] <>
( Array.fromFoldable foralls.vars <#>
( Array.fromFoldable foralls.binders <#>
\ { var, mbKind } ->
case mbKind of
Nothing -> HH.text (" " <> var)
@ -293,11 +323,11 @@ renderType = case _ of
REmpty -> HH.text "{}"
ty@(RCons _ _ _) -> renderRow ty
BinaryNoParensType t1 t2 t3 ->
BinaryNoParensType op t1 t2 ->
HH.span_
[ renderType t1
, renderType op
, renderType t2
, renderType t3
]
ParensInType ty ->
HH.span_
@ -306,14 +336,6 @@ renderType = case _ of
, HH.text ")"
]
joinForalls :: Type -> { vars :: List.List { var :: String, mbKind :: Maybe Kind }
, ty :: Type }
joinForalls = go Nil
where
go acc (ForAll var ty mbKind) =
go ({ var, mbKind } : acc) ty
go acc ty = { vars: acc, ty }
renderRow :: forall a. Type -> HH.HTML a Action
renderRow ty =
let rows = joinRows ty in
@ -326,16 +348,6 @@ renderRow ty =
) <>
[ HH.text " }" ]
joinRows :: Type -> List { row :: String
, ty :: Type
}
joinRows = go Nil
where
go acc (RCons row ty rest) =
go ({ row, ty } : acc) rest
go acc _ = List.reverse acc
htmlSingleton :: forall t406 t407. HH.HTML t407 t406 -> HH.HTML t407 t406
htmlSingleton x = HH.span_ [ x ]
@ -355,3 +367,8 @@ renderKind = case _ of
Row k1 -> HH.span_ [ HH.text "#", renderKind k1 ]
FunKind k1 k2 -> HH.span_ [ renderKind k1, renderKind k2 ]
NamedKind qname -> renderQualifiedName qname
isValuableQuery :: TypeQuery -> Boolean
isValuableQuery (QVar _) = false
isValuableQuery (QConst _) = false
isValuableQuery _ = true

View File

@ -1,9 +1,8 @@
module Spago.Search.Index where
import Data.Tuple
import Prelude
import Spago.Search.TypeDecoder (Constraint, FunDeps, Kind, Type, TypeArgument)
import Spago.Search.DocsJson (ChildDeclType(..), ChildIndexEntry(..), DeclType(..), Declarations(..), IndexEntry(..))
import Spago.Search.TypeShape
import Control.Alt ((<|>))
import Data.Array ((!!))
@ -11,14 +10,19 @@ import Data.Foldable (foldr)
import Data.List (List, (:))
import Data.List as List
import Data.Maybe (Maybe(..), fromMaybe)
import Data.Newtype (class Newtype)
import Data.Search.Trie (Trie, alter)
import Data.Newtype (class Newtype, unwrap)
import Data.Search.Trie (Trie, alter, entriesUnordered, fromList, insert)
import Data.String.CodeUnits (stripPrefix, stripSuffix, toCharArray)
import Data.String.Common (toLower)
import Data.String.Common as String
import Data.String.Pattern (Pattern(..))
import Spago.Search.DocsJson (ChildDeclType(..), ChildIndexEntry(..), DeclType(..), Declarations(..), IndexEntry(..))
import Spago.Search.TypeDecoder (Constraint, FunDeps, Kind, Type, TypeArgument)
newtype SearchIndex = SearchIndex (Trie Char (List SearchResult))
newtype SearchIndex
= SearchIndex { decls :: Trie Char (List SearchResult)
, types :: Trie ShapeChunk (List SearchResult)
}
derive instance newtypeSearchIndex :: Newtype SearchIndex _
@ -52,7 +56,42 @@ newtype SearchResult
derive instance newtypeSearchResult :: Newtype SearchResult _
mkSearchIndex :: Array Declarations -> SearchIndex
mkSearchIndex = SearchIndex <<< foldr insertDeclarations mempty
mkSearchIndex decls =
SearchIndex { decls: trie
, types
}
where
trie = foldr insertDeclarations mempty decls
types = foldr insertTypes mempty do
Tuple _ results <- entriesUnordered trie
result <- results
case (unwrap result).info of
ValueResult dict ->
insertTypeResultsFor dict.type result
TypeClassMemberResult dict ->
-- TODO: fix missing foralls for type class members
insertTypeResultsFor dict.type result
TypeSynonymResult dict ->
insertTypeResultsFor dict.type result
_ -> mempty
insertTypeResultsFor ty result =
let path = shapeOfType ty in
pure $ Tuple path result
insertTypes
:: Tuple (List ShapeChunk) SearchResult
-> Trie ShapeChunk (List SearchResult)
-> Trie ShapeChunk (List SearchResult)
insertTypes (Tuple path result) trie =
alter path updateResults trie
where
updateResults mbOldResults =
case mbOldResults of
Just oldResults ->
Just $ result : oldResults
Nothing ->
Just $ List.singleton result
insertDeclarations
:: Declarations

View File

@ -96,4 +96,4 @@ main = do
writeDeclarations declarations
patchDocs
let index = mkSearchIndex declarations
liftEffect $ log $ "Loaded " <> show (Trie.size $ unwrap index) <> " definitions"
liftEffect $ log $ "Loaded " <> show (Trie.size $ (unwrap index).decls) <> " definitions"

View File

@ -8,25 +8,26 @@ import Data.Either (Either)
import Data.Foldable (foldl)
import Data.Generic.Rep (class Generic)
import Data.Generic.Rep.Show (genericShow)
import Data.List (List, some, (:))
import Data.List (List(..), some, (:))
import Data.List as List
import Data.List.NonEmpty (NonEmptyList, cons', uncons)
import Data.List.NonEmpty as NonEmptyList
import Data.Maybe (Maybe(..))
import Data.Set as Set
import Data.String.CodeUnits (fromCharArray)
import Data.Tuple (Tuple(..))
import Data.Tuple (Tuple(..), snd)
import Text.Parsing.StringParser (ParseError, Parser, runParser, try)
import Text.Parsing.StringParser.CodePoints (alphaNum, anyLetter, char, eof, lowerCaseChar, skipSpaces, string, upperCaseChar)
import Text.Parsing.StringParser.Combinators (fix, sepBy, sepBy1, sepEndBy, sepEndBy1)
data TypeQuery
= QAny String
| QConcrete String
= QVar String
| QConst String
| QFun TypeQuery TypeQuery
| QApp TypeQuery TypeQuery
| QForAll (NonEmptyList String) TypeQuery
| QConstraint String (List String) TypeQuery
| QRow (List (Tuple String TypeQuery))
| QEmpty
derive instance eqTypeQuery :: Eq TypeQuery
derive instance genericTypeQuery :: Generic TypeQuery _
@ -70,8 +71,8 @@ typeQueryParser = fix \typeQuery ->
constrained =
QConstraint <$> (upperCaseIdent <* some space) <*>
(sepEndBy ident (some space) <* string "=>") <*>
funs
(sepEndBy ident (some space) <* string "=>" <* skipSpaces) <*>
typeQuery
in
try constrained <|> funs
@ -91,11 +92,11 @@ foldr1 f = go List.Nil
any :: Parser TypeQuery
any = do
QAny <$> lowerCaseIdent
QVar <$> lowerCaseIdent
concrete :: Parser TypeQuery
concrete =
QConcrete <$> upperCaseIdent
QConst <$> upperCaseIdent
ident :: Parser String
ident = do
@ -117,3 +118,41 @@ lowerCaseIdent = do
space :: Parser Char
space = char ' '
-- | Used only in `getFreeVariables`.
data FreeVarCounterQueueEntry = Unbind (Set.Set String) | Next TypeQuery
getFreeVariables :: TypeQuery -> Set.Set String
getFreeVariables query = go Set.empty Set.empty (List.singleton $ Next query)
where
insertIfUnbound bound var free =
if Set.member var bound
then free
else Set.insert var free
go bound free Nil = free
go bound free (Unbind vars : rest) =
go (Set.difference bound vars) free rest
go bound free (Next (QVar var) : rest) =
go bound (insertIfUnbound bound var free) rest
go bound free (Next (QConst str) : rest) =
go bound free rest
go bound free (Next (QFun q1 q2) : rest) =
go bound free (Next q1 : Next q2 : rest)
go bound free (Next (QApp q1 q2) : rest) =
go bound free (Next q1 : Next q2 : rest)
go bound free (Next (QForAll nl q) : rest) =
go (Set.union bound newBound) free queue
where
newBound = NonEmptyList.foldr Set.insert mempty nl
queue = (Next q : Unbind (Set.difference newBound bound) : rest)
go bound free (Next (QConstraint _ vars q) : rest) =
go bound (List.foldr (insertIfUnbound bound) free vars) (Next q : rest)
go bound free (Next (QRow lst) : rest) =
go bound free ((lst <#> snd >>> Next) <> rest)

View File

@ -0,0 +1,138 @@
module Spago.Search.TypeShape where
import Prelude
import Spago.Search.TypeDecoder
import Spago.Search.TypeQuery
import Spago.Search.TypeQuery
import Data.Generic.Rep (class Generic)
import Data.Generic.Rep.Show (genericShow)
import Data.List (List(..), (:))
import Data.List as List
import Data.List.NonEmpty as NonEmptyList
import Data.Maybe (Maybe)
import Data.Set as Set
import Data.Tuple (Tuple(..), snd)
type TypeShape = List ShapeChunk
data ShapeChunk
= PVar
| PFun
| PApp
| PForAll Int
| PRow Int
derive instance eqShapeChunk :: Eq ShapeChunk
derive instance ordShapeChunk :: Ord ShapeChunk
derive instance genericShapeChunk :: Generic ShapeChunk _
instance showShapeChunk :: Show ShapeChunk where
show x = genericShow x
shapeOfTypeQuery :: TypeQuery -> TypeShape
shapeOfTypeQuery query =
prependForAll $ List.reverse $ go (pure query) Nil
where
prependForAll (PForAll n : rest) =
PForAll (count + n) : rest
prependForAll shape =
if count == 0
then shape
else PForAll count : shape
count = Set.size $ getFreeVariables query
go Nil acc = acc
go (this:rest) acc =
case this of
QVar _ ->
go rest (PVar : acc)
QConst v ->
go rest (PVar : acc)
QFun q1 q2 ->
go (q1 : q2 : rest) (PFun : acc)
QApp q1 q2 ->
go (q1 : q2 : rest) (PApp : acc)
QForAll lst q ->
go (q : rest) (PForAll (NonEmptyList.length lst) : acc)
QConstraint str lst q ->
go (q : rest) acc
QRow lst ->
let lst' = List.sortBy (\(Tuple x _) (Tuple y _) -> compare x y) lst in
go (map snd lst' <> rest) (PRow (List.length lst) : acc)
shapeOfType :: Type -> TypeShape
shapeOfType ty = List.reverse $ go (pure ty) Nil
where
go Nil acc = acc
go (this:rest) acc =
case this of
TypeVar _ ->
go rest (PVar : acc)
TypeLevelString _ ->
go rest (PVar : acc)
TypeWildcard ->
go rest (PVar : acc)
TypeApp (TypeApp (TypeConstructor (QualifiedName { moduleName: ["Prim"]
, name: "Function" })) t1) t2 ->
go (t1 : t2 : rest) (PFun : acc)
TypeConstructor (QualifiedName { name }) ->
go rest (PVar : acc)
TypeOp _ ->
go rest (PVar : acc)
TypeApp child1 child2 ->
go (child1 : child2 : rest) (PApp : acc)
forallType@(ForAll _ _ _) ->
go (foralls.ty : rest) (PForAll (List.length foralls.binders) : acc)
where foralls = joinForAlls forallType
ParensInType child ->
go (child : rest) acc
ConstrainedType _ child ->
go (child : rest) acc
REmpty ->
-- TODO: reconsider
go rest (PVar : acc)
row@(RCons _ _ _) ->
go (typesInRow <> rest) (PRow (List.length joined) : acc)
where
joined = List.sortBy (\x y -> compare x.row y.row) $ joinRows row
typesInRow = joined <#> (_.ty)
BinaryNoParensType op l r ->
go (TypeApp (TypeApp op l) r : rest) acc
joinForAlls
:: Type
-> { binders :: List { var :: String
, mbKind :: Maybe Kind }
, ty :: Type
}
joinForAlls ty = go Nil ty
where
go acc (ForAll var ty' mbKind) =
go ({ var, mbKind } : acc) ty'
go acc ty' = { binders: acc, ty: ty' }
joinRows :: Type -> List { row :: String
, ty :: Type
}
joinRows = go Nil
where
go acc (RCons row ty rest) =
go ({ row, ty } : acc) rest
go acc _ = List.reverse acc

View File

@ -12,7 +12,7 @@ import Partial.Unsafe
import Prelude
import Spago.Search.DocsJson
import Spago.Search.TypeDecoder
import Test.Unit (suite, test, timeout)
import Test.Unit (suite, test)
import Test.Unit.Assert as Assert
import Test.Unit.Main (runTest)
import Test.TypeQuery as TypeQuery

View File

@ -1,136 +1,228 @@
module Test.TypeQuery where
import Data.Argonaut.Decode
import Data.Argonaut.Encode
import Data.Argonaut.Parser
import Data.Either
import Data.Maybe
import Effect (Effect)
import Effect.Aff
import Effect.Console (log)
import Partial.Unsafe
import Prelude
import Spago.Search.DocsJson
import Spago.Search.TypeDecoder
import Spago.Search.TypeQuery
import Test.Unit (suite, test, timeout)
import Test.Unit.Assert as Assert
import Test.Unit.Main (runTest)
import Data.List as List
import Spago.Search.TypeShape
import Data.Either (Either(..), isRight)
import Data.Foldable (class Foldable)
import Data.List (List(..), (:))
import Data.Tuple
import Data.Foldable
import Data.List as List
import Data.List.NonEmpty (NonEmptyList)
import Data.List.NonEmpty as NonEmptyList
import Data.Set as Set
import Data.Tuple (Tuple(..))
import Effect.Aff (Aff)
import Test.Unit (TestSuite, suite, test)
import Test.Unit.Assert as Assert
tests =
tests :: TestSuite
tests = do
suite "TypeQuery parser" do
test "test #0" do
let input = "a"
assertRight (parseTypeQuery input) (QAny "a")
assertRight (parseTypeQuery input) (QVar "a")
test "test #0.1" do
test "test #1" do
let input = "ab"
assertRight (parseTypeQuery input) (QAny "ab")
test "test #1.0" do
let input = "a b"
assertRight (parseTypeQuery input) (QApp (QAny "a") (QAny "b"))
test "test #1.1" do
let input = "a b c"
assertRight (parseTypeQuery input) (QApp (QApp (QAny "a") (QAny "b")) (QAny "c"))
assertRight (parseTypeQuery input) (QVar "ab")
test "test #2" do
let input = "a -> b"
assertRight (parseTypeQuery input) (QFun (QAny "a") (QAny "b"))
let input = "a b"
assertRight (parseTypeQuery input) (QApp (QVar "a") (QVar "b"))
test "test #3" do
let input = "a -> b c"
assertRight (parseTypeQuery input) (QFun (QAny "a") (QApp (QAny "b") (QAny "c")))
let input = "a b c"
assertRight (parseTypeQuery input) (QApp (QApp (QVar "a") (QVar "b")) (QVar "c"))
test "test #4" do
let input = "a b -> c"
assertRight (parseTypeQuery input) (QFun (QApp (QAny "a") (QAny "b")) (QAny "c"))
let input = "a -> b"
assertRight (parseTypeQuery input) (QFun (QVar "a") (QVar "b"))
test "test #5" do
let input = "a b"
assertRight (parseTypeQuery input) (QApp (QAny "a") (QAny "b"))
let input = "a -> b c"
assertRight (parseTypeQuery input) (QFun (QVar "a") (QApp (QVar "b") (QVar "c")))
test "test #6" do
let input = "a (b c)"
assertRight (parseTypeQuery input) (QApp (QAny "a") (QApp (QAny "b") (QAny "c")))
test "test #6.1" do
let input = "(a b) (c d)"
assertRight (parseTypeQuery input)
(QApp (QApp (QAny "a") (QAny "b"))
(QApp (QAny "c") (QAny "d")))
let input = "a b -> c"
assertRight (parseTypeQuery input) (QFun (QApp (QVar "a") (QVar "b")) (QVar "c"))
test "test #7" do
let input = "a ( b c )"
assertRight (parseTypeQuery input) (QApp (QAny "a") (QApp (QAny "b") (QAny "c")))
let input = "a b"
assertRight (parseTypeQuery input) (QApp (QVar "a") (QVar "b"))
test "test #8" do
let input = "aaa"
assertRight (parseTypeQuery input) (QAny "aaa")
let input = "a (b c)"
assertRight (parseTypeQuery input) (QApp (QVar "a") (QApp (QVar "b") (QVar "c")))
test "test #9" do
let input = "aaa ( bbb ccc )"
assertRight (parseTypeQuery input) (QApp (QAny "aaa") (QApp (QAny "bbb") (QAny "ccc")))
let input = "(a b) (c d)"
assertRight (parseTypeQuery input)
(QApp (QApp (QVar "a") (QVar "b"))
(QApp (QVar "c") (QVar "d")))
test "test #10" do
let input = "(a -> b) -> (c -> d)"
assertRight (parseTypeQuery input) (QFun (QFun (QAny "a") (QAny "b"))
(QFun (QAny "c") (QAny "d")))
let input = "a ( b c )"
assertRight (parseTypeQuery input) (QApp (QVar "a") (QApp (QVar "b") (QVar "c")))
test "test #11" do
let input = "a -> b -> c -> d"
assertRight (parseTypeQuery input) (QFun (QAny "a")
(QFun (QAny "b")
(QFun (QAny "c") (QAny "d"))))
test "test #11.1" do
let input = "a -> b -> c"
assertRight (parseTypeQuery input) (QFun (QAny "a")
(QFun (QAny "b")
(QAny "c")))
let input = "aaa"
assertRight (parseTypeQuery input) (QVar "aaa")
test "test #12" do
let input = "forall a b c. c"
assertRight (parseTypeQuery input) (QForAll (nl "a" ["b", "c"]) (QAny "c"))
let input = "aaa ( bbb ccc )"
assertRight (parseTypeQuery input) (QApp (QVar "aaa") (QApp (QVar "bbb") (QVar "ccc")))
test "test #13" do
let input = "forall a. Maybe a"
assertRight (parseTypeQuery input) (QForAll (nl "a" $ []) (QApp (QConcrete "Maybe") (QAny "a")))
let input = "(a -> b) -> (c -> d)"
assertRight (parseTypeQuery input) (QFun (QFun (QVar "a") (QVar "b"))
(QFun (QVar "c") (QVar "d")))
test "test #14" do
let input = "a -> b -> c -> d"
assertRight (parseTypeQuery input) (QFun (QVar "a")
(QFun (QVar "b")
(QFun (QVar "c") (QVar "d"))))
test "test #15" do
let input = "a -> b -> c"
assertRight (parseTypeQuery input) (QFun (QVar "a")
(QFun (QVar "b")
(QVar "c")))
test "test #16" do
let input = "forall a b c. c"
assertRight (parseTypeQuery input) (QForAll (nl "a" ["b", "c"]) (QVar "c"))
test "test #17" do
let input = "forall a. Maybe a"
assertRight (parseTypeQuery input) (QForAll (nl "a" $ []) (QApp (QConst "Maybe") (QVar "a")))
test "test #18" do
let input = "forall m a. Monad m => a -> m a"
assertRight (parseTypeQuery input)
(QForAll (nl "m" ["a"])
(QConstraint "Monad" (l ["m"])
(QFun (QAny "a")
(QApp (QAny "m") (QAny "a")))))
(QFun (QVar "a")
(QApp (QVar "m") (QVar "a")))))
test "test #15" do
test "test #19" do
let input = "{ a :: Int }"
assertRight (parseTypeQuery input)
(QRow (pure (Tuple "a" (QConcrete "Int"))))
(QRow (pure (Tuple "a" (QConst "Int"))))
test "test #16" do
test "test #20" do
let input = "{a::Int}"
assertRight (parseTypeQuery input)
(QRow (pure (Tuple "a" (QConcrete "Int"))))
(QRow (pure (Tuple "a" (QConst "Int"))))
test "test #17" do
test "test #21" do
let input = "Int"
assertRight (parseTypeQuery input) (QConcrete "Int")
assertRight (parseTypeQuery input) (QConst "Int")
test "test #18" do
test "test #22" do
let input = "a->b"
assertRight (parseTypeQuery input) (QFun (QAny "a") (QAny "b"))
assertRight (parseTypeQuery input) (QFun (QVar "a") (QVar "b"))
test "test #23" do
let input = "forall m a. MonadRec m => Process m a -> m a"
assertRight (parseTypeQuery input) (QForAll (nl "m" ("a" : Nil))
(QConstraint "MonadRec" ("m" : Nil)
(QFun (QApp (QApp (QConst "Process")
(QVar "m")) (QVar "a"))
(QApp (QVar "m") (QVar "a")))))
test "test #24" do
let input = "forall t f a. Foldable1 t => Apply f => f"
assertRight (parseTypeQuery input) (QForAll (nl "t" ["f", "a"])
(QConstraint "Foldable1" (l ["t"])
(QConstraint "Apply" ("f" : Nil) (QVar "f"))))
test "test #25" do
let input = "forall m a.MonadRec m=>Process m a->m a"
assertRight (parseTypeQuery input) ((QForAll (nl "m" ("a" : Nil))
(QConstraint "MonadRec" ("m" : Nil)
(QFun (QApp (QApp (QConst "Process")
(QVar "m")) (QVar "a"))
(QApp (QVar "m") (QVar "a"))))))
suite "polish notation" do
test "test #1" do
let input = "(a -> b) -> (b -> ((a -> b) -> c))"
assertRight (shapeOfTypeQuery <$> parseTypeQuery input)
(l [ PForAll 3, PFun, PFun, PVar, PVar, PFun, PVar, PFun, PFun
, PVar, PVar, PVar ])
test "test #2" do
let input = "forall a. (a -> b) -> (b -> ((a -> b) -> c))"
assertRight (shapeOfTypeQuery <$> parseTypeQuery input)
(l [ PForAll 3, PFun, PFun, PVar, PVar, PFun, PVar, PFun, PFun
, PVar, PVar, PVar ])
test "test #3" do
let input = "forall a. (a -> b) -> (b -> ((a -> b) -> c))"
assertRight (shapeOfTypeQuery <$> parseTypeQuery input)
(l [ PForAll 3, PFun, PFun, PVar, PVar, PFun, PVar, PFun, PFun
, PVar, PVar, PVar ])
test "test #4" do
let input = "forall a. (forall h. ST h (STArray h a)) -> Array a"
assertRight (shapeOfTypeQuery <$> parseTypeQuery input)
(l [ PForAll 1, PFun, PForAll 1, PApp, PApp, PVar, PVar, PApp, PApp, PVar, PVar, PVar, PApp, PVar, PVar ])
suite "free variable counting" do
test "test #1" do
let input = "forall a. (a -> b) -> (b -> ((a -> b) -> c))"
assertRight (countFreeVars <$> parseTypeQuery input) 2
test "test #2" do
-- `b` is not bound on the left, `a` is not bound on the right
let input = "(forall a. (a -> b)) -> forall b. (b -> a)"
assertRight (countFreeVars <$> parseTypeQuery input) 2
test "test #3" do
let input = "a -> forall a. a"
assertRight (countFreeVars <$> parseTypeQuery input) 1
test "test #4" do
let input = "(forall a. a) -> a"
assertRight (countFreeVars <$> parseTypeQuery input) 1
test "test #5" do
let input = "forall a. a -> a"
assertRight (countFreeVars <$> parseTypeQuery input) 0
test "test #6" do
let input = "a -> b -> c"
assertRight (countFreeVars <$> parseTypeQuery input) 3
test "test #7" do
let input = "forall m a. Monad m => a -> m a"
assertRight (countFreeVars <$> parseTypeQuery input) 0
test "test #8" do
let input = "Monad m => a -> m a"
assertRight (countFreeVars <$> parseTypeQuery input) 2
test "test #9" do
let input = "Monad m => a -> a"
assertRight (countFreeVars <$> parseTypeQuery input) 2
test "test #10" do
let input = "forall a. (forall a. a) a"
assertRight (countFreeVars <$> parseTypeQuery input) 0
test "test #11" do
let input = "forall a. (forall b. a) a"
assertRight (countFreeVars <$> parseTypeQuery input) 0
test "test #12" do
let input = "forall a. (forall b. a) a b"
assertRight (countFreeVars <$> parseTypeQuery input) 1
l :: forall f. Foldable f => (forall a. f a -> List a)
l = List.fromFoldable
@ -153,3 +245,6 @@ assertRight eiActual expected =
Assert.equal (Right expected) eiActual
Right actual -> do
Assert.equal (Right expected) eiActual
countFreeVars :: TypeQuery -> Int
countFreeVars = getFreeVariables >>> Set.size