Skip to content

Commit d7b12be

Browse files
authored
Merge pull request #55 from purescript/phil/type-search
Type search
2 parents 652faf3 + a99d0f3 commit d7b12be

File tree

3 files changed

+67
-7
lines changed

3 files changed

+67
-7
lines changed

server/Main.hs

+62-3
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,27 @@
11
{-# LANGUAGE DataKinds #-}
22
{-# LANGUAGE DeriveAnyClass #-}
33
{-# LANGUAGE DeriveGeneric #-}
4+
{-# LANGUAGE LambdaCase #-}
45
{-# LANGUAGE OverloadedStrings #-}
56
{-# LANGUAGE TupleSections #-}
67

78
module Main (main) where
89

9-
import Control.Monad (unless)
10+
import Control.Monad (unless, (>=>))
11+
import Control.Monad.Error.Class (throwError)
1012
import Control.Monad.IO.Class (liftIO)
1113
import Control.Monad.Logger (runLogger')
14+
import Control.Monad.State (State)
15+
import qualified Control.Monad.State as State
1216
import Control.Monad.Trans (lift)
13-
import Control.Monad.Error.Class (throwError)
1417
import Control.Monad.Trans.Except (ExceptT(..), runExceptT)
1518
import Control.Monad.Trans.Reader (runReaderT)
1619
import qualified Data.Aeson as A
1720
import Data.Aeson ((.=))
1821
import qualified Data.ByteString.Lazy as BL
19-
import Data.List (foldl')
22+
import Data.Function (on)
23+
import Data.List (foldl', nubBy)
24+
import qualified Data.Map as M
2025
import Data.String (fromString)
2126
import Data.Text (Text)
2227
import qualified Data.Text as T
@@ -30,12 +35,14 @@ import qualified Language.PureScript.CodeGen.JS as J
3035
import qualified Language.PureScript.CoreFn as CF
3136
import qualified Language.PureScript.Errors.JSON as P
3237
import qualified Language.PureScript.Interactive as I
38+
import qualified Language.PureScript.TypeChecker.TypeSearch as TS
3339
import System.Environment (getArgs)
3440
import System.Exit (exitFailure)
3541
import System.FilePath ((</>))
3642
import System.FilePath.Glob (glob)
3743
import qualified System.IO as IO
3844
import System.IO.UTF8 (readUTF8File)
45+
import qualified Text.Parsec.Combinator as Parsec
3946
import Web.Scotty
4047
import qualified Web.Scotty as Scotty
4148

@@ -90,6 +97,58 @@ server bundled externs initEnv port = do
9097
Scotty.json $ A.object [ "error" .= err ]
9198
Right comp ->
9299
Scotty.json $ A.object [ "js" .= comp ]
100+
get "/search" $ do
101+
query <- param "q"
102+
Scotty.setHeader "Access-Control-Allow-Origin" "*"
103+
Scotty.setHeader "Content-Type" "application/json"
104+
case tryParseType query of
105+
Nothing -> Scotty.json $ A.object [ "error" .= ("Cannot parse type" :: Text) ]
106+
Just ty -> do
107+
let elabs = lookupAllConstructors initEnv ty
108+
search = M.toList . TS.typeSearch (Just []) initEnv (P.emptyCheckState initEnv)
109+
results = nubBy ((==) `on` fst) $ do
110+
elab <- elabs
111+
let strictMatches = search (replaceTypeVariablesAndDesugar (\nm s -> P.Skolem nm s (P.SkolemScope 0) Nothing) elab)
112+
flexMatches = search (replaceTypeVariablesAndDesugar (const P.TUnknown) elab)
113+
take 50 (strictMatches ++ flexMatches)
114+
Scotty.json $ A.object [ "results" .= [ P.showQualified P.runIdent k
115+
| (k, _) <- take 50 results
116+
]
117+
]
118+
119+
lookupAllConstructors :: P.Environment -> P.Type -> [P.Type]
120+
lookupAllConstructors env = P.everywhereOnTypesM $ \case
121+
P.TypeConstructor (P.Qualified Nothing tyCon) -> P.TypeConstructor <$> lookupConstructor env tyCon
122+
other -> pure other
123+
where
124+
lookupConstructor :: P.Environment -> P.ProperName 'P.TypeName -> [P.Qualified (P.ProperName 'P.TypeName)]
125+
lookupConstructor env nm =
126+
[ q
127+
| (q@(P.Qualified (Just mn) thisNm), _) <- M.toList (P.types env)
128+
, thisNm == nm
129+
]
130+
131+
-- | (Consistently) replace unqualified type constructors and type variables with unknowns.
132+
--
133+
-- Also remove the @ParensInType@ Constructor (we need to deal with type operators later at some point).
134+
replaceTypeVariablesAndDesugar :: (Text -> Int -> P.Type) -> P.Type -> P.Type
135+
replaceTypeVariablesAndDesugar f ty = State.evalState (P.everywhereOnTypesM go ty) (0, M.empty) where
136+
go = \case
137+
P.ParensInType ty -> pure ty
138+
P.TypeVar s -> do
139+
(next, m) <- State.get
140+
case M.lookup s m of
141+
Nothing -> do
142+
let ty = f s next
143+
State.put (next + 1, M.insert s ty m)
144+
pure ty
145+
Just ty -> pure ty
146+
other -> pure other
147+
148+
tryParseType :: Text -> Maybe P.Type
149+
tryParseType = hush (P.lex "") >=> hush (P.runTokenParser "" (P.parsePolyType <* Parsec.eof))
150+
where
151+
hush f = either (const Nothing) Just . f
93152

94153
bundle :: IO (Either Bundle.ErrorMessage String)
95154
bundle = runExceptT $ do

stack.yaml

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ flags: {}
33
packages:
44
- '.'
55
extra-deps:
6-
- purescript-0.10.4
7-
- bower-json-0.8.0
6+
- purescript-0.10.5
7+
- bower-json-1.0.0.1
88
- language-javascript-0.6.0.9
99
- parsec-3.1.11

trypurescript.cabal

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: trypurescript
2-
version: 0.10.4
2+
version: 0.10.5
33
cabal-version: >=1.8
44
build-type: Simple
55
license: BSD3
@@ -20,11 +20,12 @@ executable trypurescript
2020
filepath -any,
2121
Glob -any,
2222
scotty -any,
23-
purescript ==0.10.4,
23+
purescript ==0.10.5,
2424
containers -any,
2525
http-types >= 0.8.5,
2626
transformers ==0.4.*,
2727
mtl ==2.2.1,
28+
parsec,
2829
text -any,
2930
time -any
3031
hs-source-dirs: server

0 commit comments

Comments
 (0)