mirror of https://github.com/voidlizard/hbs2
suckless: recursive imports, primitive import loop detection
This commit is contained in:
parent
9c81855a13
commit
46b02d458f
|
@ -30,6 +30,7 @@ import Data.ByteString.Char8 qualified as BS8
|
||||||
import Data.ByteString qualified as BS
|
import Data.ByteString qualified as BS
|
||||||
import Data.ByteString.Lazy qualified as LBS
|
import Data.ByteString.Lazy qualified as LBS
|
||||||
import Data.Data
|
import Data.Data
|
||||||
|
import Data.Coerce
|
||||||
import Data.Function as Export
|
import Data.Function as Export
|
||||||
import Data.Functor as Export
|
import Data.Functor as Export
|
||||||
import Data.Hashable
|
import Data.Hashable
|
||||||
|
@ -336,6 +337,7 @@ data BadFormException c = BadFormException (Syntax c)
|
||||||
| ArityMismatch (Syntax c)
|
| ArityMismatch (Syntax c)
|
||||||
| NotLambda (Syntax c)
|
| NotLambda (Syntax c)
|
||||||
| NotBuiltinLambda Id
|
| NotBuiltinLambda Id
|
||||||
|
| RuntimeError (Syntax c)
|
||||||
| TypeCheckError (Syntax c)
|
| TypeCheckError (Syntax c)
|
||||||
|
|
||||||
newtype BadValueException = BadValueException String
|
newtype BadValueException = BadValueException String
|
||||||
|
@ -349,6 +351,8 @@ instance IsContext c => Show (BadFormException c) where
|
||||||
show (ArityMismatch sy) = show $ "ArityMismatch" <+> pretty sy
|
show (ArityMismatch sy) = show $ "ArityMismatch" <+> pretty sy
|
||||||
show (NotLambda sy) = show $ "NotLambda" <+> pretty sy
|
show (NotLambda sy) = show $ "NotLambda" <+> pretty sy
|
||||||
show (TypeCheckError sy) = show $ "TypeCheckError" <+> pretty sy
|
show (TypeCheckError sy) = show $ "TypeCheckError" <+> pretty sy
|
||||||
|
show (RuntimeError sy) = show $ "RuntimeError" <+> pretty sy
|
||||||
|
show (NotBuiltinLambda sy) = show $ "NotBuiltinLambda" <+> pretty sy
|
||||||
|
|
||||||
instance Exception (BadFormException C)
|
instance Exception (BadFormException C)
|
||||||
|
|
||||||
|
@ -618,7 +622,7 @@ eval' dict0 syn' = handle (handleForm syn') $ do
|
||||||
let dict = dict0 <> dict1
|
let dict = dict0 <> dict1
|
||||||
|
|
||||||
-- liiftIO $ print $ show $ "TRACE EXP" <+> pretty syn
|
-- liiftIO $ print $ show $ "TRACE EXP" <+> pretty syn
|
||||||
let importDecls = HS.fromList [ "define", "define-macro" :: Id ]
|
let importDecls = HS.fromList [ "import", "define", "define-macro" :: Id ]
|
||||||
|
|
||||||
case syn' of
|
case syn' of
|
||||||
|
|
||||||
|
@ -655,6 +659,29 @@ eval' dict0 syn' = handle (handleForm syn') $ do
|
||||||
ListVal [ SymbolVal "eval", e ] -> eval e >>= eval
|
ListVal [ SymbolVal "eval", e ] -> eval e >>= eval
|
||||||
|
|
||||||
ListVal [ SymbolVal "import", StringLike fn ] -> do
|
ListVal [ SymbolVal "import", StringLike fn ] -> do
|
||||||
|
|
||||||
|
let importsName = "*runtime-imports*"
|
||||||
|
let alreadyError = RuntimeError $ mkForm "runtime-error" [ mkStr @c ["already imported", pretty fn] ]
|
||||||
|
let disappearedMessage = [mkStr @c [coerce importsName, "misteriously disappeared" :: Text]]
|
||||||
|
let disappeared = RuntimeError $ mkForm "runtime-error" disappearedMessage
|
||||||
|
|
||||||
|
initial <- newTVarIO (mempty :: HashMap Id (HashSet Id)) >>= mkOpaque
|
||||||
|
|
||||||
|
imp_ <- lookupValueDef initial importsName >>= \case
|
||||||
|
OpaqueVal e -> fromOpaque @(TVar (HashMap Id (HashSet Id))) e & \case
|
||||||
|
Just x -> pure x
|
||||||
|
Nothing -> throwIO disappeared
|
||||||
|
|
||||||
|
_ -> throwIO (RuntimeError (mkStr @c $ show $ pretty importsName <> "misteriously disappeared"))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
seen <- atomically $ stateTVar imp_ (\e -> (HM.lookup (mkId fn) e, HM.insert (mkId fn) mempty e))
|
||||||
|
|
||||||
|
-- liftIO $ print $ pretty "import" <+> pretty fn
|
||||||
|
|
||||||
|
unless (isNothing seen) $ throwIO alreadyError
|
||||||
|
|
||||||
-- FIXME: fancy-error-handling
|
-- FIXME: fancy-error-handling
|
||||||
syn <- liftIO (TIO.readFile fn) <&> parseTop >>= either(error.show) pure
|
syn <- liftIO (TIO.readFile fn) <&> parseTop >>= either(error.show) pure
|
||||||
|
|
||||||
|
@ -665,7 +692,7 @@ eval' dict0 syn' = handle (handleForm syn') $ do
|
||||||
|
|
||||||
-- liftIO $ mapM_ (print . pretty) decls
|
-- liftIO $ mapM_ (print . pretty) decls
|
||||||
|
|
||||||
mapM_ eval decls
|
evalTop decls
|
||||||
|
|
||||||
pure nil
|
pure nil
|
||||||
|
|
||||||
|
@ -828,6 +855,19 @@ lookupValue i = do
|
||||||
Just (BindValue s) -> pure s
|
Just (BindValue s) -> pure s
|
||||||
_ -> throwIO (NameNotBound i)
|
_ -> throwIO (NameNotBound i)
|
||||||
|
|
||||||
|
lookupValueDef :: forall c m . (IsContext c, Exception (BadFormException c), MonadUnliftIO m)
|
||||||
|
=> Syntax c
|
||||||
|
-> Id
|
||||||
|
-> RunM c m (Syntax c)
|
||||||
|
lookupValueDef defVal i = do
|
||||||
|
ask >>= readTVarIO
|
||||||
|
<&> (fmap bindAction . HM.lookup i)
|
||||||
|
>>= \case
|
||||||
|
Just (BindValue s) -> pure s
|
||||||
|
_ -> do
|
||||||
|
bind i defVal
|
||||||
|
pure defVal
|
||||||
|
|
||||||
nil_ :: (IsContext c, MonadIO m) => (a -> RunM c m b) -> a -> RunM c m (Syntax c)
|
nil_ :: (IsContext c, MonadIO m) => (a -> RunM c m b) -> a -> RunM c m (Syntax c)
|
||||||
nil_ m w = m w >> pure (List noContext [])
|
nil_ m w = m w >> pure (List noContext [])
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@ module Data.Config.Suckless.Syntax
|
||||||
, nil
|
, nil
|
||||||
, mkList
|
, mkList
|
||||||
, mkBool
|
, mkBool
|
||||||
|
, MkId(..)
|
||||||
, MkForm(..)
|
, MkForm(..)
|
||||||
, MkSym(..)
|
, MkSym(..)
|
||||||
, MkInt(..)
|
, MkInt(..)
|
||||||
|
@ -331,6 +332,15 @@ instance IsContext c => FromJSON (Syntax c) where
|
||||||
pure $ List noContext (Symbol noContext (Id "object") : pairs)
|
pure $ List noContext (Symbol noContext (Id "object") : pairs)
|
||||||
parseJSON _ = fail "Cannot parse JSON to Syntax"
|
parseJSON _ = fail "Cannot parse JSON to Syntax"
|
||||||
|
|
||||||
|
class MkId a where
|
||||||
|
mkId :: a -> Id
|
||||||
|
|
||||||
|
instance MkId Text where
|
||||||
|
mkId = Id
|
||||||
|
|
||||||
|
instance MkId String where
|
||||||
|
mkId = Id . Text.pack
|
||||||
|
|
||||||
class IsContext c => MkSym c a where
|
class IsContext c => MkSym c a where
|
||||||
mkSym :: a -> Syntax c
|
mkSym :: a -> Syntax c
|
||||||
|
|
||||||
|
@ -352,6 +362,15 @@ instance IsContext c => MkStr c String where
|
||||||
instance IsContext c => MkStr c Text where
|
instance IsContext c => MkStr c Text where
|
||||||
mkStr s = Literal noContext $ LitStr s
|
mkStr s = Literal noContext $ LitStr s
|
||||||
|
|
||||||
|
instance IsContext c => MkStr c [Text] where
|
||||||
|
mkStr s = mkStr $ mconcat s
|
||||||
|
|
||||||
|
instance IsContext c => MkStr c [String] where
|
||||||
|
mkStr s = mkStr $ mconcat s
|
||||||
|
|
||||||
|
instance IsContext c => MkStr c [Doc ann] where
|
||||||
|
mkStr s = mkStr $ show (hsep s)
|
||||||
|
|
||||||
mkBool :: forall c . IsContext c => Bool -> Syntax c
|
mkBool :: forall c . IsContext c => Bool -> Syntax c
|
||||||
mkBool v = Literal noContext (LitBool v)
|
mkBool v = Literal noContext (LitBool v)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue