Add syntactic holes

This commit is contained in:
Madeline Haraj 2018-11-17 18:20:59 -05:00 committed by John Wiegley
parent f3970d49b9
commit 28ab983408
No known key found for this signature in database
GPG key ID: C144D8F4F19FE630
10 changed files with 58 additions and 8 deletions

View file

@ -41,6 +41,7 @@ import Nix.Utils
class (Show v, Monad m) => MonadEval v m where
freeVariable :: Text -> m v
synHole :: Text -> m v
attrMissing :: NonEmpty Text -> Maybe v -> m v
evaledSym :: Text -> v -> m v
evalCurPos :: m v
@ -91,10 +92,18 @@ data EvalFrame m v
= EvaluatingExpr (Scopes m v) NExprLoc
| ForcingExpr (Scopes m v) NExprLoc
| Calling String SrcSpan
| SynHole (SynHoleInfo m v)
deriving (Show, Typeable)
instance (Typeable m, Typeable v) => Exception (EvalFrame m v)
data SynHoleInfo m t = SynHoleInfo
{ _synHoleInfo_expr :: NExprLoc
, _synHoleInfo_scope :: Scopes m t
} deriving (Show, Typeable)
instance (Typeable m, Typeable t) => Exception (SynHoleInfo m t)
eval :: forall v t m. MonadNixEval v t m => NExprF (m v) -> m v
eval (NSym "__curPos") = evalCurPos
@ -148,6 +157,8 @@ eval (NAbs params body) = do
args <- buildArgument params arg
pushScope args (k (M.map (`force` pure) args) body)
eval (NSynHole name) = synHole name
-- | If you know that the 'scope' action will result in an 'AttrSet t', then
-- this implementation may be used as an implementation for 'evalWith'.
evalWithAttrSet :: forall v t m. MonadNixEval v t m => m v -> m v -> m v

View file

@ -151,6 +151,14 @@ instance MonadNix e m => MonadEval (NValue m) m where
freeVariable var =
nverr $ ErrorCall $ "Undefined variable '" ++ Text.unpack var ++ "'"
synHole name = do
span <- currentPos
scope <- currentScopes @_ @(NThunk m)
evalError @(NValue m) $ SynHole $ SynHoleInfo
{ _synHoleInfo_expr = Fix $ NSynHole_ span name
, _synHoleInfo_scope = scope
}
attrMissing ks Nothing =
evalError @(NValue m) $ ErrorCall $
"Inheriting unknown attribute: "

View file

@ -70,6 +70,12 @@ mkSym = Fix . mkSymF
mkSymF :: Text -> NExprF a
mkSymF = NSym
mkSynHole :: Text -> NExpr
mkSynHole = Fix . mkSynHoleF
mkSynHoleF :: Text -> NExprF a
mkSynHoleF = NSynHole
mkSelector :: Text -> NAttrPath NExpr
mkSelector = (:| []) . StaticKey

View file

@ -134,6 +134,8 @@ data NExprF r
-- evaluate the second argument.
| NAssert !r !r
-- ^ Assert that the first returns true before evaluating the second.
| NSynHole !VarName
-- ^ Syntactic hole, e.g. @^foo@ , @^hole_name@
deriving (Ord, Eq, Generic, Generic1, Typeable, Data, Functor,
Foldable, Traversable, Show, NFData, Hashable)

View file

@ -190,6 +190,9 @@ nullSpan = SrcSpan nullPos nullPos
pattern NSym_ :: SrcSpan -> VarName -> NExprLocF r
pattern NSym_ ann x = Compose (Ann ann (NSym x))
pattern NSynHole_ :: SrcSpan -> Text -> NExprLocF r
pattern NSynHole_ ann x = Compose (Ann ann (NSynHole x))
pattern NConstant_ :: SrcSpan -> NAtom -> NExprLocF r
pattern NConstant_ ann x = Compose (Ann ann (NConstant x))

View file

@ -129,7 +129,8 @@ nixTerm = do
x == '<' ||
x == '/' ||
x == '"' ||
x == '\''
x == '\''||
x == '^'
case c of
'(' -> nixSelect nixParens
'{' -> nixSelect nixSet
@ -138,6 +139,7 @@ nixTerm = do
'/' -> nixPath
'"' -> nixString
'\'' -> nixString
'^' -> nixSynHole
_ -> msum $
[ nixSelect nixSet | c == 'r' ] ++
[ nixPath | pathChar c ] ++
@ -157,6 +159,9 @@ nixToplevelForm = keywords <+> nixLambda <+> nixExpr
nixSym :: Parser NExprLoc
nixSym = annotateLocation1 $ mkSymF <$> identifier
nixSynHole :: Parser NExprLoc
nixSynHole = annotateLocation1 $ mkSynHoleF <$> (char '^' >> identifier)
nixInt :: Parser NExprLoc
nixInt = annotateLocation1 (mkIntF <$> integer <?> "integer")

View file

@ -256,6 +256,7 @@ exprFNixDoc = \case
[ "assert" <+> withoutParens cond <> semi
, align $ withoutParens body
]
NSynHole name -> simpleExpr $ pretty ("^" <> unpack name)
where
recPrefix = "rec" <> space

View file

@ -75,12 +75,13 @@ renderFrame :: forall v e m ann.
MonadFile m, Typeable m, Typeable v)
=> NixFrame -> m [Doc ann]
renderFrame (NixFrame level f)
| Just (e :: EvalFrame m v) <- fromException f = renderEvalFrame level e
| Just (e :: ThunkLoop) <- fromException f = renderThunkLoop level e
| Just (e :: ValueFrame m) <- fromException f = renderValueFrame level e
| Just (e :: NormalLoop m) <- fromException f = renderNormalLoop level e
| Just (e :: ExecFrame m) <- fromException f = renderExecFrame level e
| Just (e :: ErrorCall) <- fromException f = pure [pretty (show e)]
| Just (e :: EvalFrame m v) <- fromException f = renderEvalFrame level e
| Just (e :: ThunkLoop) <- fromException f = renderThunkLoop level e
| Just (e :: ValueFrame m) <- fromException f = renderValueFrame level e
| Just (e :: NormalLoop m) <- fromException f = renderNormalLoop level e
| Just (e :: ExecFrame m) <- fromException f = renderExecFrame level e
| Just (e :: ErrorCall) <- fromException f = pure [pretty (show e)]
| Just (e :: SynHoleInfo m v) <- fromException f = pure [text (show e)]
| otherwise = error $ "Unrecognized frame: " ++ show f
wrapExpr :: NExprF r -> NExpr
@ -107,7 +108,15 @@ renderEvalFrame level f = do
fmap (:[]) $ renderLocation ann $
"While calling builtins." <> pretty name
_ -> pure []
SynHole synfo -> sequence $
let e@(Fix (Compose (Ann ann _))) = _synHoleInfo_expr synfo
in [ renderLocation ann =<<
renderExpr level "While evaluating" "Syntactic Hole" e
, pure $ text $ show (_synHoleInfo_scope synfo)
]
ForcingExpr _ _ -> pure []
renderExpr :: (MonadReader e m, Has e Options, MonadFile m)
=> NixLevel -> String -> String -> NExprLoc -> m (Doc ann)

View file

@ -61,6 +61,7 @@ freeVars e = case unFix e of
-- This also makes sense because its value can be overridden by `x: with y; x`
(NWith set expr) -> freeVars set `Set.union` freeVars expr
(NAssert assertion expr) -> freeVars assertion `Set.union` freeVars expr
(NSynHole _) -> Set.empty
where

View file

@ -369,6 +369,10 @@ instance ( MonadFreshId Int m
tv <- fresh
return $ Judgment (As.singleton var tv) [] tv
synHole var = do
tv <- fresh
return $ Judgment (As.singleton var tv) [] tv
-- If we fail to look up an attribute, we just don't know the type.
attrMissing _ _ = Judgment As.empty [] <$> fresh