Skip to content

Commit

Permalink
feat: wildcards (#1049)
Browse files Browse the repository at this point in the history
  • Loading branch information
dhess authored Jun 6, 2023
2 parents e64a289 + 2037646 commit a99aa5e
Show file tree
Hide file tree
Showing 48 changed files with 2,276 additions and 301 deletions.
18 changes: 16 additions & 2 deletions primer-service/test/outputs/OpenAPI/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,12 @@
"MakeLetRec",
"MakeLam",
"MakeLAM",
"AddBranch",
"AddBranchInt",
"AddBranchChar",
"DeleteBranch",
"DeleteBranchInt",
"DeleteBranchChar",
"RenamePattern",
"RenameLet",
"RenameLam",
Expand Down Expand Up @@ -401,13 +407,15 @@
"TEmptyHole",
"THole",
"TFun",
"TApp"
"TApp",
"PatternWildcard"
],
"type": "string"
},
"NodeFlavorPrimBody": {
"enum": [
"PrimCon"
"PrimCon",
"PrimPattern"
],
"type": "string"
},
Expand Down Expand Up @@ -1251,6 +1259,12 @@
"MakeLetRec",
"MakeLam",
"MakeLAM",
"AddBranch",
"AddBranchInt",
"AddBranchChar",
"DeleteBranch",
"DeleteBranchInt",
"DeleteBranchChar",
"RenamePattern",
"RenameLet",
"RenameLam",
Expand Down
24 changes: 16 additions & 8 deletions primer/gen/Primer/Gen/Core/Raw.hs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import Hedgehog.Range qualified as Range
import Primer.Core (
Bind' (Bind),
CaseBranch' (CaseBranch),
CaseFallback' (CaseExhaustive, CaseFallback),
Expr,
Expr' (..),
GVarName,
Expand All @@ -38,6 +39,7 @@ import Primer.Core (
LocalName (LocalName),
Meta (..),
ModuleName (ModuleName),
Pattern (PatCon, PatPrim),
PrimCon (..),
TmVarRef (..),
TyConName,
Expand Down Expand Up @@ -135,21 +137,27 @@ genLetrec :: ExprGen Expr
genLetrec = Letrec <$> genMeta <*> genLVarName <*> genExpr <*> genType <*> genExpr

genCase :: ExprGen Expr
genCase = Case <$> genMeta <*> genExpr <*> Gen.list (Range.linear 0 5) genBranch
genCase = Case <$> genMeta <*> genExpr <*> Gen.list (Range.linear 0 5) genBranch <*> Gen.choice [pure CaseExhaustive, CaseFallback <$> genExpr]
where
genBranch = CaseBranch <$> genValConName <*> Gen.list (Range.linear 0 5) genBind <*> genExpr
genBranch = CaseBranch <$> genScrut <*> Gen.list (Range.linear 0 5) genBind <*> genExpr
genScrut =
Gen.choice
[ PatCon <$> genValConName
, PatPrim <$> genPrimCon
]
genBind = Bind <$> genMeta <*> genLVarName

genPrim :: ExprGen Expr
genPrim = PrimCon <$> genMeta <*> genPrimCon

genPrimCon :: MonadGen m => m PrimCon
genPrimCon =
Gen.choice
[ PrimChar <$> Gen.unicodeAll
, PrimInt <$> Gen.integral (Range.linear (-intBound) intBound)
]
where
intBound = fromIntegral (maxBound :: Word64) -- arbitrary
genPrimCon :: (StateT ID Gen PrimCon)
genPrimCon =
Gen.choice
[ PrimChar <$> Gen.unicodeAll
, PrimInt <$> Gen.integral (Range.linear (-intBound) intBound)
]
-- This ensures that when we modify the constructors of `PrimCon` (i.e. we add/remove primitive types),
-- we are alerted that we need to update this generator.
_ = \case
Expand Down
54 changes: 46 additions & 8 deletions primer/gen/Primer/Gen/Core/Typed.hs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import Foreword hiding (mod)
import Control.Monad.Fresh (MonadFresh, fresh)
import Control.Monad.Morph (hoist)
import Control.Monad.Reader (mapReaderT)
import Data.List.Extra (nubSortOn)
import Data.Map qualified as M
import Hedgehog (
GenT,
Expand All @@ -47,6 +48,7 @@ import Hedgehog.Range qualified as Range
import Primer.Core (
Bind' (Bind),
CaseBranch' (CaseBranch),
CaseFallback' (..),
Expr' (..),
GVarName,
GlobalName (qualifiedModule),
Expand All @@ -55,6 +57,7 @@ import Primer.Core (
LVarName,
LocalName (LocalName, unLocalName),
ModuleName (),
Pattern (PatCon, PatPrim),
PrimCon (..),
TmVarRef (..),
TyConName,
Expand Down Expand Up @@ -436,24 +439,59 @@ genChk ty = do
-}
]
case_ :: WT (Maybe (GenT WT ExprG))
case_ =
case_ = (\ca cp -> Gen.frequency [(5, ca), (1, cp)]) <<$>> caseADT <<*>> casePrim
caseADT :: WT (Maybe (GenT WT ExprG))
caseADT =
asks (M.assocs . typeDefs) <&> \adts ->
if null adts
then Nothing
else Just $ do
(tc, td) <- Gen.element adts
let t = mkTAppCon tc (TEmptyHole () <$ typeDefParameters td)
(e, brs) <- Gen.justT $ do
(e, (brs, fb)) <- Gen.justT $ do
(e, eTy) <- genSyns t -- NB: this could return something only consistent with t, e.g. if t=List ?, could get eT=? Nat
vcs' <- instantiateValCons eTy
fmap (e,) <$> case vcs' of
Left TDIHoleType -> pure $ Just []
Left TDIHoleType -> pure $ Just ([], CaseExhaustive)
Left _err -> pure Nothing -- if we didn't get an instance of t, try again; TODO: this is rather inefficient, and discards a lot...
Right (_, _, vcs) -> fmap Just . for vcs $ \(c, params) -> do
ns <- for params $ \nt -> (,nt) <$> genLVarNameAvoiding [ty, nt]
let binds = map (Bind () . fst) ns
CaseBranch c binds <$> local (extendLocalCxts ns) (genChk ty)
pure $ Case () e brs
Right (_, _, allVcs) -> case nonEmpty allVcs of
Nothing -> pure $ Just ([], CaseExhaustive)
Just allVcs' -> do
(vcs, fb) <-
Gen.frequency
[ (3, pure (allVcs, CaseExhaustive))
, (1, (,) <$> genStrictSubsequence allVcs' <*> fmap CaseFallback (genChk ty))
]
fmap (Just . (,fb)) . for vcs $ \(c, params) -> do
ns <- for params $ \nt -> (,nt) <$> genLVarNameAvoiding [ty, nt]
let binds = map (Bind () . fst) ns
CaseBranch (PatCon c) binds <$> local (extendLocalCxts ns) (genChk ty)
pure $ Case () e brs fb
casePrim :: WT (Maybe (GenT WT ExprG))
casePrim = do
primGens <- genPrimCon
pure $
if null primGens
then Nothing
else Just $ do
(pg, scrutTy0) <- Gen.element primGens
let scrutTy = TCon () scrutTy0
(e0, scrutTy') <- genSyns scrutTy
let e = if scrutTy == scrutTy' then e0 else Ann () e0 scrutTy
brs0 <- Gen.list (Range.linear 0 5) $ do
p <- pg
(p,) . CaseBranch (PatPrim p) [] <$> genChk ty
let brs = nubSortOn ((\case PrimInt n -> Left n; PrimChar c -> Right c) . fst) brs0
fb <- genChk ty
pure $ Case () e (snd <$> brs) (CaseFallback fb)

genStrictSubsequence :: MonadGen m => NonEmpty a -> m [a]
genStrictSubsequence xs = Gen.justT $ do
s <- Gen.subsequence $ toList xs
pure $
if length s == length xs
then Nothing
else Just s

-- | Generates types which infer kinds consistent with the argument
-- I.e. @genWTType k@ will generate types @ty@ such that @synthKind ty = k'@
Expand Down
1 change: 1 addition & 0 deletions primer/primer.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ library primer-hedgehog
build-depends:
, base
, containers
, extra
, hedgehog ^>=1.1
, mmorph ^>=1.2.0
, mtl
Expand Down
39 changes: 34 additions & 5 deletions primer/src/Primer/API.hs
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ import Primer.App.Base (TypeDefNodeSelection (..))
import Primer.Core (
Bind' (..),
CaseBranch' (..),
CaseFallback' (CaseExhaustive, CaseFallback),
Expr,
Expr' (..),
GVarName,
Expand All @@ -167,7 +168,7 @@ import Primer.Core (
_typeMetaLens,
)
import Primer.Core.DSL qualified as DSL
import Primer.Core.Meta (LocalName)
import Primer.Core.Meta (LocalName, Pattern (PatCon, PatPrim))
import Primer.Core.Meta qualified as Core
import Primer.Database (
OffsetLimit,
Expand Down Expand Up @@ -822,7 +823,7 @@ viewTreeExpr e0 = case e0 of
, childTrees = [viewTreeExpr e1, viewTreeType t, viewTreeExpr e2]
, rightChild = Nothing
}
Case _ e bs ->
Case _ e bs fb ->
Tree
{ nodeId
, body = NoBody Flavor.Case
Expand All @@ -837,10 +838,10 @@ viewTreeExpr e0 = case e0 of
-- which should only happen when matching on `Void`
ifoldr
(\i b next -> Just $ (viewCaseBranch i b){rightChild = next})
Nothing
viewFallback
bs
)
viewCaseBranch i (CaseBranch con binds rhs) =
viewCaseBranch i (CaseBranch p binds rhs) =
let
-- these IDs will not clash with any others in the tree,
-- since node IDs in the input expression are unique,
Expand All @@ -854,7 +855,7 @@ viewTreeExpr e0 = case e0 of
BoxBody . RecordPair Flavor.Pattern $
( Tree
{ nodeId = patternRootId
, body = TextBody $ RecordPair Flavor.PatternCon $ globalName con
, body = pat p
, childTrees =
map
( \(Bind m v) ->
Expand All @@ -872,6 +873,34 @@ viewTreeExpr e0 = case e0 of
, childTrees = [viewTreeExpr rhs]
, rightChild = Nothing
}
viewFallback = case fb of
CaseExhaustive -> Nothing
CaseFallback rhs ->
let
-- these IDs will not clash with any others in the tree,
-- since node IDs in the input expression are unique,
-- and don't contain non-numerical characters
boxId = nodeId <> "Pwild"
patternRootId = boxId <> "B"
in
Just $
Tree
{ nodeId = boxId
, body =
BoxBody . RecordPair Flavor.Pattern $
( Tree
{ nodeId = patternRootId
, body = NoBody Flavor.PatternWildcard
, childTrees = []
, rightChild = Nothing
}
)
, childTrees = [viewTreeExpr rhs]
, rightChild = Nothing
}
pat = \case
PatCon n -> TextBody $ RecordPair Flavor.PatternCon $ globalName n
PatPrim pc -> PrimBody $ RecordPair Flavor.PrimPattern pc
PrimCon _ pc ->
Tree
{ nodeId
Expand Down
2 changes: 2 additions & 0 deletions primer/src/Primer/API/NodeFlavor.hs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ data NodeFlavorTextBody

data NodeFlavorPrimBody
= PrimCon
| PrimPattern
deriving stock (Show, Read, Eq, Generic, Enum, Bounded)
deriving (ToJSON, FromJSON) via CustomJSON '[TagSingleConstructors] NodeFlavorPrimBody
deriving anyclass (NFData)
Expand All @@ -62,6 +63,7 @@ data NodeFlavorNoBody
| THole
| TFun
| TApp
| PatternWildcard
deriving stock (Show, Read, Eq, Generic, Enum, Bounded)
deriving (ToJSON, FromJSON) via PrimerJSON NodeFlavorNoBody
deriving anyclass (NFData)
Loading

1 comment on commit a99aa5e

@github-actions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Performance Alert ⚠️

Possible performance regression was detected for benchmark 'Primer benchmarks'.
Benchmark result of this commit is worse than the previous benchmark result exceeding threshold 2.

Benchmark suite Current: a99aa5e Previous: e64a289 Ratio
typecheck/mapOddPrim 1: outlier variance 0.2781882841068313 outlier variance 0.012343749999999994 outlier variance 22.54
typecheck/mapOddPrim 100: outlier variance 0.6738812995417974 outlier variance 0.31999092083270114 outlier variance 2.11

This comment was automatically generated by workflow using github-action-benchmark.

CC: @dhess

Please sign in to comment.