Compare commits

...

7 Commits

Author SHA1 Message Date
voidlizard e2215bd022 backported changes from hbs2 2025-01-22 14:18:54 +03:00
Dmitry Zuikov 09c70e6694 quasiquotes 2025-01-09 15:14:27 +03:00
voidlizard ca2e824cdf suckless-script extension 2024-11-24 12:22:32 +03:00
Sergey Ivanov ff6f1a2e05 Fix `Variable not in scope: replicateM_` 2024-09-24 21:59:58 +04:00
Sergey Ivanov be80eabc1d flake.lock: Update
Flake lock file updates:

• Updated input 'fuzzy':
    'git+http://git.hbs2.net/GmcLB9gEPT4tbx9eyQiECwsu8oPyEh6qKEpQDtyBWVPA?rev=831879978213a1aed15ac70aa116c33bcbe964b8&tag=0.1.3.1' (2024-05-17)
  → 'git+http://git.hbs2.net/GmcLB9gEPT4tbx9eyQiECwsu8oPyEh6qKEpQDtyBWVPA?ref=refs/heads/master&rev=a834b152e29d632c816eefe117036e5d9330bd03' (2024-09-24)
• Updated input 'fuzzy/nixpkgs':
    'github:NixOS/nixpkgs/442d407992384ed9c0e6d352de75b69079904e4e' (2024-02-09)
  → 'github:NixOS/nixpkgs/568bfef547c14ca438c56a0bece08b8bb2b71a9c' (2024-09-23)
• Updated input 'haskell-flake-utils':
    'github:ivanovs-4/haskell-flake-utils/896219e5bde6efac72198550454e9dd9b5ed9ac9' (2022-12-30)
  → 'github:ivanovs-4/haskell-flake-utils/3cbdc5d6093e8b4464ae64097e0c8c61e4414ff2' (2024-02-13)
• Updated input 'nixpkgs':
    'github:NixOS/nixpkgs/442d407992384ed9c0e6d352de75b69079904e4e' (2024-02-09)
  → 'github:NixOS/nixpkgs/568bfef547c14ca438c56a0bece08b8bb2b71a9c' (2024-09-23)
2024-09-24 21:58:34 +04:00
Dmitry Zuikov b6c5087312 version bump 2024-08-27 09:29:15 +03:00
Dmitry Zuikov c63f7c366f fixed dependency 2024-08-27 09:27:37 +03:00
8 changed files with 554 additions and 74 deletions

View File

@ -36,18 +36,18 @@
"nixpkgs": "nixpkgs" "nixpkgs": "nixpkgs"
}, },
"locked": { "locked": {
"lastModified": 1715918584, "lastModified": 1737544489,
"narHash": "sha256-moioa3ixAZb0y/xxyxUVjSvXoSiDGXy/vAx6B70d2yM=", "narHash": "sha256-prTXYnEgUIIe5nMaE3rC9g6Ej3qtAKudd8T/QA+eyW8=",
"ref": "refs/heads/master", "ref": "refs/heads/master",
"rev": "831879978213a1aed15ac70aa116c33bcbe964b8", "rev": "9e41a735a8bdb02b2b5c405341d8b3b98242f021",
"revCount": 63, "revCount": 46,
"type": "git", "type": "git",
"url": "http://git.hbs2/GmcLB9gEPT4tbx9eyQiECwsu8oPyEh6qKEpQDtyBWVPA?tag=0.1.3.1" "url": "http://git.hbs2.net/GmcLB9gEPT4tbx9eyQiECwsu8oPyEh6qKEpQDtyBWVPA"
}, },
"original": { "original": {
"rev": "831879978213a1aed15ac70aa116c33bcbe964b8", "rev": "9e41a735a8bdb02b2b5c405341d8b3b98242f021",
"type": "git", "type": "git",
"url": "http://git.hbs2/GmcLB9gEPT4tbx9eyQiECwsu8oPyEh6qKEpQDtyBWVPA?tag=0.1.3.1" "url": "http://git.hbs2.net/GmcLB9gEPT4tbx9eyQiECwsu8oPyEh6qKEpQDtyBWVPA"
} }
}, },
"haskell-flake-utils": { "haskell-flake-utils": {
@ -73,11 +73,11 @@
"flake-utils": "flake-utils_2" "flake-utils": "flake-utils_2"
}, },
"locked": { "locked": {
"lastModified": 1672412555, "lastModified": 1707809372,
"narHash": "sha256-Kaa8F7nQFR3KuS6Y9WRUxeJeZlp6CCubyrRfmiEsW4k=", "narHash": "sha256-wfTL9PlCSOqSSyU4eenFFI7pHrV21gba4GEILnI4nAU=",
"owner": "ivanovs-4", "owner": "ivanovs-4",
"repo": "haskell-flake-utils", "repo": "haskell-flake-utils",
"rev": "896219e5bde6efac72198550454e9dd9b5ed9ac9", "rev": "3cbdc5d6093e8b4464ae64097e0c8c61e4414ff2",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -88,33 +88,33 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1707451808, "lastModified": 1727089097,
"narHash": "sha256-UwDBUNHNRsYKFJzyTMVMTF5qS4xeJlWoeyJf+6vvamU=", "narHash": "sha256-ZMHMThPsthhUREwDebXw7GX45bJnBCVbfnH1g5iuSPc=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "442d407992384ed9c0e6d352de75b69079904e4e", "rev": "568bfef547c14ca438c56a0bece08b8bb2b71a9c",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "NixOS", "owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "442d407992384ed9c0e6d352de75b69079904e4e",
"type": "github" "type": "github"
} }
}, },
"nixpkgs_2": { "nixpkgs_2": {
"locked": { "locked": {
"lastModified": 1707451808, "lastModified": 1727089097,
"narHash": "sha256-UwDBUNHNRsYKFJzyTMVMTF5qS4xeJlWoeyJf+6vvamU=", "narHash": "sha256-ZMHMThPsthhUREwDebXw7GX45bJnBCVbfnH1g5iuSPc=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "442d407992384ed9c0e6d352de75b69079904e4e", "rev": "568bfef547c14ca438c56a0bece08b8bb2b71a9c",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "NixOS", "owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "442d407992384ed9c0e6d352de75b69079904e4e",
"type": "github" "type": "github"
} }
}, },

View File

@ -2,12 +2,12 @@
description = "suckless-cong: sexp based configs"; description = "suckless-cong: sexp based configs";
inputs = { inputs = {
nixpkgs.url = "github:NixOS/nixpkgs?rev=442d407992384ed9c0e6d352de75b69079904e4e"; nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
haskell-flake-utils.url = "github:ivanovs-4/haskell-flake-utils"; haskell-flake-utils.url = "github:ivanovs-4/haskell-flake-utils";
fuzzy.url = fuzzy.url =
# "git+http://git.hbs2/GmcLB9gEPT4tbx9eyQiECwsu8oPyEh6qKEpQDtyBWVPA?ref=sexp-parser&rev=bd3a38904864d5cc333974e7b029412607b46871"; # "git+http://git.hbs2/GmcLB9gEPT4tbx9eyQiECwsu8oPyEh6qKEpQDtyBWVPA?ref=sexp-parser&rev=bd3a38904864d5cc333974e7b029412607b46871";
"git+http://git.hbs2/GmcLB9gEPT4tbx9eyQiECwsu8oPyEh6qKEpQDtyBWVPA?tag=0.1.3.1&rev=831879978213a1aed15ac70aa116c33bcbe964b8"; "git+http://git.hbs2.net/GmcLB9gEPT4tbx9eyQiECwsu8oPyEh6qKEpQDtyBWVPA?rev=9e41a735a8bdb02b2b5c405341d8b3b98242f021";
}; };

View File

@ -0,0 +1,47 @@
{-# Language TypeOperators #-}
module Data.Config.Suckless.Almost.RPC where
import Data.Config.Suckless
import Control.Exception
import Control.Monad
import Control.Monad.IO.Class
import Data.ByteString.Lazy as LBS
import Data.ByteString.Lazy.Char8 as LBS8
import Data.Function
import Data.Text.Encoding.Error qualified as TE
import Data.Text.Encoding qualified as TE
import Data.Text qualified as T
import Data.Typeable
import Prettyprinter
import System.Process.Typed
data CallProcException =
CallProcException ExitCode
deriving (Show,Typeable)
instance Exception CallProcException
-- FIXME: to-suckless-script
callProc :: forall m . (MonadIO m)
=> FilePath
-> [String]
-> [Syntax C]
-> m [Syntax C]
callProc name params syn = do
let input = fmap (LBS.fromStrict . TE.encodeUtf8 . T.pack . show . pretty) syn
& LBS8.unlines
& byteStringInput
let what = proc name params & setStderr closed & setStdin input
(code, i, _) <- readProcess what
unless (code == ExitSuccess) do
liftIO $ throwIO (CallProcException code)
let s = TE.decodeUtf8With TE.lenientDecode (LBS.toStrict i)
parseTop s & either (liftIO . throwIO) pure

View File

@ -1,5 +1,6 @@
{-# Language UndecidableInstances #-} {-# Language UndecidableInstances #-}
{-# Language PatternSynonyms #-} {-# Language PatternSynonyms #-}
{-# Language RecordWildCards #-}
module Data.Config.Suckless.Script module Data.Config.Suckless.Script
( module Exported ( module Exported
, module Data.Config.Suckless.Script , module Data.Config.Suckless.Script
@ -8,12 +9,14 @@ module Data.Config.Suckless.Script
import Data.Config.Suckless as Exported import Data.Config.Suckless as Exported
import Data.Config.Suckless.Script.Internal as Exported import Data.Config.Suckless.Script.Internal as Exported
import Control.Monad
import Control.Monad.Reader import Control.Monad.Reader
import Data.HashMap.Strict qualified as HM import Data.HashMap.Strict qualified as HM
import Prettyprinter import Prettyprinter
import Prettyprinter.Render.Terminal import Prettyprinter.Render.Terminal
import Data.List qualified as List import Data.List qualified as List
import Data.Text qualified as Text import Data.Text qualified as Text
import Data.String
import UnliftIO import UnliftIO
@ -27,13 +30,13 @@ helpList hasDoc p = do
d <- ask >>= readTVarIO d <- ask >>= readTVarIO
let ks = [k | Id k <- List.sort (HM.keys d) let ks = [k | Id k <- List.sort (HM.keys d)
, match k , match k
, not hasDoc || docDefined (HM.lookup (Id k) d) , docDefined (HM.lookup (Id k) d) || not hasDoc
] ]
display_ $ vcat (fmap pretty ks) display_ $ vcat (fmap pretty ks)
where where
docDefined (Just (Bind (Just w) _)) = True docDefined (Just (Bind (Just Man{..}) _)) | not manHidden = True
docDefined _ = False docDefined _ = False
helpEntry :: MonadUnliftIO m => Id -> RunM c m () helpEntry :: MonadUnliftIO m => Id -> RunM c m ()
@ -47,3 +50,25 @@ helpEntry what = do
pattern HelpEntryBound :: forall {c}. Id -> [Syntax c] pattern HelpEntryBound :: forall {c}. Id -> [Syntax c]
pattern HelpEntryBound what <- [ListVal (SymbolVal "builtin:lambda" : SymbolVal what : _ )] pattern HelpEntryBound what <- [ListVal (SymbolVal "builtin:lambda" : SymbolVal what : _ )]
splitOpts :: [(Id,Int)]
-> [Syntax C]
-> ([Syntax C], [Syntax C])
splitOpts def opts' = flip fix (mempty, opts) $ \go -> \case
(acc, []) -> acc
( (o,a), r@(StringLike x) : rs ) -> do
case HM.lookup (fromString x) omap of
Nothing -> go ((o, a <> [r]), rs)
Just n -> do
let (w, rest) = List.splitAt n rs
let result = mkList @C ( r : w )
go ( (o <> [result], a), rest )
( (o,a), r : rs ) -> do
go ((o, a <> [r]), rs)
where
omap = HM.fromList [ (p, x) | (p,x) <- def ]
opts = opts'

View File

@ -15,6 +15,8 @@ import System.FilePath
import System.FilePattern import System.FilePattern
import Data.HashSet qualified as HS import Data.HashSet qualified as HS
import Prettyprinter
import Lens.Micro.Platform import Lens.Micro.Platform
import UnliftIO import UnliftIO
import Control.Concurrent.STM qualified as STM import Control.Concurrent.STM qualified as STM
@ -67,12 +69,12 @@ entries = do
entry $ bindMatch "glob" $ \syn -> do entry $ bindMatch "glob" $ \syn -> do
(p,i,d) <- case syn of (p,i,d) <- case syn of
[] -> pure (["*"], [], ".") [] -> pure (["**/*"], ["**/.*"], ".")
[StringLike d, StringLike i, StringLike e] -> do s@[StringLike d, ListVal (StringLikeList i) ] -> do
pure ([i], [e], d) pure (i, [], d)
[StringLike d, ListVal (StringLikeList i), ListVal (StringLikeList e)] -> do s@[StringLike d, ListVal (StringLikeList i), ListVal (StringLikeList e) ] -> do
pure (i, e, d) pure (i, e, d)
_ -> throwIO (BadFormException @c nil) _ -> throwIO (BadFormException @c nil)

View File

@ -2,6 +2,7 @@
{-# Language UndecidableInstances #-} {-# Language UndecidableInstances #-}
{-# Language PatternSynonyms #-} {-# Language PatternSynonyms #-}
{-# Language ViewPatterns #-} {-# Language ViewPatterns #-}
{-# Language RecordWildCards #-}
module Data.Config.Suckless.Script.Internal module Data.Config.Suckless.Script.Internal
( module Data.Config.Suckless.Script.Internal ( module Data.Config.Suckless.Script.Internal
, module Export , module Export
@ -10,15 +11,20 @@ module Data.Config.Suckless.Script.Internal
import Data.Config.Suckless import Data.Config.Suckless
import Control.Applicative import Control.Applicative
import Control.Monad
import Control.Monad.Identity import Control.Monad.Identity
import Control.Monad.Reader import Control.Monad.Reader
import Control.Monad.Writer import Control.Monad.Writer
import Data.ByteString (ByteString) import Data.ByteString (ByteString)
import Data.ByteString.Char8 qualified as BS8 import Data.ByteString.Char8 qualified as BS8
import Data.ByteString qualified as BS
import Data.ByteString.Lazy qualified as LBS
import Data.Data import Data.Data
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
import Data.HashSet (HashSet)
import Data.HashSet qualified as HS
import Data.HashMap.Strict (HashMap) import Data.HashMap.Strict (HashMap)
import Data.HashMap.Strict qualified as HM import Data.HashMap.Strict qualified as HM
import Data.Kind import Data.Kind
@ -29,6 +35,8 @@ import Data.String
import Data.Text.IO qualified as TIO import Data.Text.IO qualified as TIO
import Data.Text qualified as Text import Data.Text qualified as Text
import Data.Text (Text) import Data.Text (Text)
import Data.Text.Encoding (decodeUtf8With)
import Data.Text.Encoding.Error (ignore)
import Data.Time.Clock.POSIX import Data.Time.Clock.POSIX
import GHC.Generics hiding (C) import GHC.Generics hiding (C)
import Prettyprinter import Prettyprinter
@ -115,7 +123,7 @@ instance IsString ManDesc where
instance Pretty (Man a) where instance Pretty (Man a) where
pretty e = "NAME" pretty e = "NAME"
<> line <> line
<> indent 8 (pretty (manName e) <> fmtBrief e) <> indent 4 (pretty (manName e) <> fmtBrief e)
<> line <> line
<> fmtSynopsis <> fmtSynopsis
<> fmtDescription <> fmtDescription
@ -130,14 +138,14 @@ instance Pretty (Man a) where
Nothing -> mempty Nothing -> mempty
Just (ManReturns t s) -> Just (ManReturns t s) ->
line <> "RETURN VALUE" <> line line <> "RETURN VALUE" <> line
<> indent 8 ( <> indent 4 (
if not (Text.null s) then if not (Text.null s) then
(pretty t <> hsep ["","-",""] <> pretty s) <> line (pretty t <> hsep ["","-",""] <> pretty s) <> line
else pretty t ) else pretty t )
fmtDescription = line fmtDescription = line
<> "DESCRIPTION" <> line <> "DESCRIPTION" <> line
<> indent 8 ( case manDesc e of <> indent 4 ( case manDesc e of
Nothing -> pretty (manBrief e) Nothing -> pretty (manBrief e)
Just x -> pretty x) Just x -> pretty x)
<> line <> line
@ -156,13 +164,13 @@ instance Pretty (Man a) where
es -> line es -> line
<> "EXAMPLES" <> "EXAMPLES"
<> line <> line
<> indent 8 ( vcat (fmap pretty es) ) <> indent 4 ( vcat (fmap pretty es) )
synEntry (ManSynopsis (ManApply [])) = synEntry (ManSynopsis (ManApply [])) =
indent 8 ( parens (pretty (manName e)) ) <> line indent 4 ( parens (pretty (manName e)) ) <> line
synEntry (ManSynopsis (ManApply xs)) = do synEntry (ManSynopsis (ManApply xs)) = do
indent 8 do indent 4 do
parens (pretty (manName e) <+> parens (pretty (manName e) <+>
hsep [ pretty n | ManApplyArg t n <- xs ] ) hsep [ pretty n | ManApplyArg t n <- xs ] )
<> line <> line
@ -263,6 +271,12 @@ eatNil f = \case
class IsContext c => MkInt c s where class IsContext c => MkInt c s where
mkInt :: s -> Syntax c mkInt :: s -> Syntax c
class IsContext c => MkDouble c s where
mkDouble :: s -> Syntax c
instance (IsContext c, RealFrac s) => MkDouble c s where
mkDouble v = Literal noContext $ LitScientific (realToFrac v)
instance (Integral i, IsContext c) => MkInt c i where instance (Integral i, IsContext c) => MkInt c i where
mkInt n = Literal noContext $ LitInt (fromIntegral n) mkInt n = Literal noContext $ LitInt (fromIntegral n)
@ -317,6 +331,7 @@ isPair = \case
data BindAction c ( m :: Type -> Type) = data BindAction c ( m :: Type -> Type) =
BindLambda { fromLambda :: [Syntax c] -> RunM c m (Syntax c) } BindLambda { fromLambda :: [Syntax c] -> RunM c m (Syntax c) }
| BindMacro { fromMacro :: [Syntax c] -> RunM c m (Syntax c) }
| BindValue (Syntax c) | BindValue (Syntax c)
data Bind c ( m :: Type -> Type) = Bind data Bind c ( m :: Type -> Type) = Bind
@ -331,18 +346,11 @@ newtype NameNotBoundException =
deriving stock Show deriving stock Show
deriving newtype (Generic,Typeable) deriving newtype (Generic,Typeable)
newtype NotLambda = NotLambda Id
deriving stock Show
deriving newtype (Generic,Typeable)
instance Exception NotLambda
data BadFormException c = BadFormException (Syntax c) data BadFormException c = BadFormException (Syntax c)
| ArityMismatch (Syntax c) | ArityMismatch (Syntax c)
| NotLambda (Syntax c)
newtype TypeCheckError c = TypeCheckError (Syntax c) | TypeCheckError (Syntax c)
instance Exception (TypeCheckError C)
newtype BadValueException = BadValueException String newtype BadValueException = BadValueException String
deriving stock Show deriving stock Show
@ -353,8 +361,7 @@ instance Exception NameNotBoundException
instance IsContext c => Show (BadFormException c) where instance IsContext c => Show (BadFormException c) where
show (BadFormException sy) = show $ "BadFormException" <+> pretty sy show (BadFormException sy) = show $ "BadFormException" <+> pretty sy
show (ArityMismatch sy) = show $ "ArityMismatch" <+> pretty sy show (ArityMismatch sy) = show $ "ArityMismatch" <+> pretty sy
show (NotLambda sy) = show $ "NotLambda" <+> pretty sy
instance IsContext c => Show (TypeCheckError c) where
show (TypeCheckError sy) = show $ "TypeCheckError" <+> pretty sy show (TypeCheckError sy) = show $ "TypeCheckError" <+> pretty sy
instance Exception (BadFormException C) instance Exception (BadFormException C)
@ -388,8 +395,20 @@ makeDict w = execWriter ( fromMakeDict w )
entry :: Dict c m -> MakeDictM c m () entry :: Dict c m -> MakeDictM c m ()
entry = tell entry = tell
hide :: MakeDictM c m () hide :: Bind c m -> Bind c m
hide = pure () hide (Bind w x) = Bind (Just updatedMan) x
where
updatedMan = case w of
Nothing -> mempty { manHidden = True }
Just man -> man { manHidden = True }
hidden :: MakeDictM c m () -> MakeDictM c m ()
hidden = censor (HM.map hide)
hidePrefix :: Id -> MakeDictM c m () -> MakeDictM c m ()
hidePrefix (Id p) = censor (HM.filterWithKey exclude)
where
exclude (Id k) _ = not (Text.isPrefixOf p k)
desc :: Doc ann -> MakeDictM c m () -> MakeDictM c m () desc :: Doc ann -> MakeDictM c m () -> MakeDictM c m ()
desc txt = censor (HM.map setDesc) desc txt = censor (HM.map setDesc)
@ -409,7 +428,6 @@ returns tp txt = censor (HM.map setReturns)
w0 = mempty { manReturns = Just (ManReturns tp txt) } w0 = mempty { manReturns = Just (ManReturns tp txt) }
setReturns (Bind w x) = Bind (Just (maybe w0 (<>w0) w)) x setReturns (Bind w x) = Bind (Just (maybe w0 (<>w0) w)) x
addSynopsis :: ManSynopsis -> Bind c m -> Bind c m addSynopsis :: ManSynopsis -> Bind c m -> Bind c m
addSynopsis synopsis (Bind w x) = Bind (Just updatedMan) x addSynopsis synopsis (Bind w x) = Bind (Just updatedMan) x
where where
@ -433,7 +451,7 @@ opt n d = n <+> "-" <+> d
examples :: ManExamples -> MakeDictM c m () -> MakeDictM c m () examples :: ManExamples -> MakeDictM c m () -> MakeDictM c m ()
examples (ManExamples s) = censor (HM.map setExamples ) examples (ManExamples s) = censor (HM.map setExamples )
where where
ex = ManExamples (Text.unlines $ Text.strip <$> Text.lines (Text.strip s)) ex = ManExamples (Text.unlines $ Text.lines (Text.strip s))
ex0 = mempty { manExamples = [ex] } ex0 = mempty { manExamples = [ex] }
setExamples (Bind w x) = Bind (Just (maybe ex0 (<>ex0) w)) x setExamples (Bind w x) = Bind (Just (maybe ex0 (<>ex0) w)) x
@ -484,9 +502,13 @@ apply_ :: forall c m . ( IsContext c
apply_ s args = case s of apply_ s args = case s of
ListVal [SymbolVal "builtin:lambda", SymbolVal n] -> apply n args ListVal [SymbolVal "builtin:lambda", SymbolVal n] -> apply n args
SymbolVal "quot" -> pure $ mkList args
SymbolVal "quote" -> pure $ mkList args
SymbolVal "quasiquot" -> mkList <$> mapM (evalQQ mempty) args
SymbolVal "quasiquote" -> mkList <$> mapM (evalQQ mempty) args
SymbolVal what -> apply what args SymbolVal what -> apply what args
Lambda d body -> applyLambda d body args Lambda d body -> applyLambda d body args
e -> throwIO $ BadFormException @c s e -> throwIO $ NotLambda e
apply :: forall c m . ( IsContext c apply :: forall c m . ( IsContext c
, MonadUnliftIO m , MonadUnliftIO m
@ -495,6 +517,13 @@ apply :: forall c m . ( IsContext c
=> Id => Id
-> [Syntax c] -> [Syntax c]
-> RunM c m (Syntax c) -> RunM c m (Syntax c)
apply "quot" args = do
pure $ mkList args
apply "quasiquot" args = do
mkList <$> mapM (evalQQ mempty) args
apply name args' = do apply name args' = do
-- notice $ red "APPLY" <+> pretty name -- notice $ red "APPLY" <+> pretty name
what <- ask >>= readTVarIO <&> HM.lookup name what <- ask >>= readTVarIO <&> HM.lookup name
@ -505,8 +534,11 @@ apply name args' = do
Just (BindValue (Lambda argz body) ) -> do Just (BindValue (Lambda argz body) ) -> do
applyLambda argz body args' applyLambda argz body args'
Just (BindMacro macro) -> do
macro args'
Just (BindValue _) -> do Just (BindValue _) -> do
throwIO (NotLambda name) throwIO (NotLambda (mkSym @c name))
Nothing -> throwIO (NameNotBound name) Nothing -> throwIO (NameNotBound name)
@ -542,26 +574,99 @@ bindBuiltins dict = do
atomically do atomically do
modifyTVar t (<> dict) modifyTVar t (<> dict)
evalQQ :: forall c m . ( IsContext c
, MonadUnliftIO m
, Exception (BadFormException c)
) => Dict c m
-> Syntax c -> RunM c m (Syntax c)
evalQQ d0 = \case
-- SymbolVal (Id w) | Text.isPrefixOf "," w -> do
-- let what = Id (Text.drop 1 w)
-- lookupValue what >>= eval
ListVal [ SymbolVal ",", w ] -> eval' d0 w
List c es -> List c <$> mapM (evalQQ d0) es
other -> pure other
eval :: forall c m . ( IsContext c eval :: forall c m . ( IsContext c
, MonadUnliftIO m , MonadUnliftIO m
, Exception (BadFormException c) , Exception (BadFormException c)
) => Syntax c -> RunM c m (Syntax c) )
eval syn = handle (handleForm syn) $ do => Syntax c
-> RunM c m (Syntax c)
eval = eval' mempty
dict <- ask >>= readTVarIO eval' :: forall c m . ( IsContext c
, MonadUnliftIO m
, Exception (BadFormException c)
) => Dict c m
-> Syntax c
-> RunM c m (Syntax c)
eval' dict0 syn = handle (handleForm syn) $ do
dict1 <- ask >>= readTVarIO
let dict = dict0 <> dict1
-- liftIO $ print $ show $ "TRACE EXP" <+> pretty syn
case syn of case syn of
SymbolVal (Id s) | Text.isPrefixOf ":" s -> do
pure (mkSym @c (Text.drop 1 s))
ListVal [ w, SymbolVal ".", b] -> do ListVal [ w, SymbolVal ".", b] -> do
pure $ mkList [w, b] pure $ mkList [w, b]
ListVal [ SymbolVal ":", b] -> do
pure $ mkList [b]
ListVal [ SymbolVal "'", ListVal b] -> do
pure $ mkList b
ListVal [ SymbolVal "'", StringLike x] -> do
pure $ mkSym x
ListVal [ SymbolVal "'", x] -> do
pure x
ListVal [ SymbolVal ",", x] -> do
pure x
ListVal [ SymbolVal "`", ListVal b] -> do
mkList <$> mapM (evalQQ dict) b
ListVal [ SymbolVal "quasiquot", ListVal b] -> do
mkList <$> mapM (evalQQ dict) b
ListVal [ SymbolVal "quot", ListVal b] -> do ListVal [ SymbolVal "quot", ListVal b] -> do
pure $ mkList b pure $ mkList b
ListVal [ SymbolVal "eval", e ] -> eval e >>= eval
ListVal [SymbolVal "define", SymbolVal what, e] -> do ListVal [SymbolVal "define", SymbolVal what, e] -> do
ev <- eval e ev <- eval e
bind what ev>> pure nil bind what ev>> pure nil
ListVal [SymbolVal "define-macro", LambdaArgs (name:argz), e] -> do
t <- ask
let runMacro argvalz = do
de <- forM (zip argz argvalz) $ \(n,e) -> do
v <- eval e
pure (n, Bind mzero (BindValue v))
let d0 = HM.fromList de
eval' d0 e >>= eval' d0
let b = Bind mzero (BindMacro runMacro)
atomically $ modifyTVar t (HM.insert name b)
pure nil
ListVal [SymbolVal "lambda", arglist, body] -> do ListVal [SymbolVal "lambda", arglist, body] -> do
pure $ mkForm @c "lambda" [ arglist, body ] pure $ mkForm @c "lambda" [ arglist, body ]
@ -590,23 +695,29 @@ eval syn = handle (handleForm syn) $ do
ListVal (SymbolVal name : args') -> do ListVal (SymbolVal name : args') -> do
apply name args' apply name args'
SymbolVal (Id s) | Text.isPrefixOf ":" s -> do ListVal (e' : args') -> do
pure (mkSym @c (Text.drop 1 s)) -- e <- eval e'
apply_ e' args'
SymbolVal name | HM.member name dict -> do SymbolVal name | HM.member name dict -> do
let what = HM.lookup name dict
let what = HM.lookup name dict0 <|> HM.lookup name dict1
& maybe (BindValue (mkSym name)) bindAction & maybe (BindValue (mkSym name)) bindAction
-- liftIO $ print $ "LOOKUP" <+> pretty name <+> pretty what
case what of case what of
BindValue e -> pure e BindValue e -> pure e
BindLambda e -> pure $ mkForm "builtin:lambda" [mkSym name] BindLambda e -> pure $ mkForm "builtin:lambda" [mkSym name]
BindMacro _ -> pure nil
e@(SymbolVal name) | not (HM.member name dict) -> do e@(SymbolVal name) | not (HM.member name dict) -> do
pure e pure e
e@Literal{} -> pure e e@Literal{} -> pure e
e -> throwIO $ BadFormException @c e e -> throwIO $ NotLambda @c e
where where
handleForm syn = \case handleForm syn = \case
@ -614,6 +725,9 @@ eval syn = handle (handleForm syn) $ do
throwIO (BadFormException syn) throwIO (BadFormException syn)
(ArityMismatch s :: BadFormException c) -> do (ArityMismatch s :: BadFormException c) -> do
throwIO (ArityMismatch syn) throwIO (ArityMismatch syn)
(TypeCheckError s :: BadFormException c) -> do
throwIO (TypeCheckError syn)
other -> throwIO other
runM :: forall c m a. ( IsContext c runM :: forall c m a. ( IsContext c
, MonadUnliftIO m , MonadUnliftIO m
@ -631,6 +745,13 @@ run d sy = do
tvd <- newTVarIO d tvd <- newTVarIO d
lastDef nil <$> runReaderT (fromRunM (mapM eval sy)) tvd lastDef nil <$> runReaderT (fromRunM (mapM eval sy)) tvd
runEval :: forall c m . ( IsContext c
, MonadUnliftIO m
, Exception (BadFormException c)
) => TVar (Dict c m) -> [Syntax c] -> m (Syntax c)
runEval tvd sy = do
lastDef nil <$> runReaderT (fromRunM (mapM eval sy)) tvd
evalTop :: forall c m . ( IsContext c evalTop :: forall c m . ( IsContext c
, MonadUnliftIO m , MonadUnliftIO m
, Exception (BadFormException c)) , Exception (BadFormException c))
@ -643,23 +764,37 @@ bindMatch n fn = HM.singleton n (Bind man (BindLambda fn))
where where
man = Just $ mempty { manName = Just (manNameOf n) } man = Just $ mempty { manName = Just (manNameOf n) }
bindMacro :: Id -> ([Syntax c] -> RunM c m (Syntax c)) -> Dict c m
bindMacro n fn = HM.singleton n (Bind man (BindMacro fn))
where
man = Just $ mempty { manName = Just (manNameOf n) }
bindValue :: Id -> Syntax c -> Dict c m bindValue :: Id -> Syntax c -> Dict c m
bindValue n e = HM.singleton n (Bind mzero (BindValue e)) bindValue n e = HM.singleton n (Bind mzero (BindValue e))
lookupValue :: forall c m . (IsContext c, MonadUnliftIO m)
=> Id -> RunM c m (Syntax c)
lookupValue i = do
ask >>= readTVarIO
<&> (fmap bindAction . HM.lookup i)
>>= \case
Just (BindValue s) -> pure s
_ -> throwIO (NameNotBound i)
nil :: forall c . IsContext c => Syntax c nil :: forall c . IsContext c => Syntax c
nil = List noContext [] nil = List noContext []
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 [])
fixContext :: (IsContext c1, IsContext c2) => Syntax c1 -> Syntax c2 fixContext :: forall c1 c2 . (IsContext c1, IsContext c2) => Syntax c1 -> Syntax c2
fixContext = go fixContext = go
where where
go = \case go = \case
List _ xs -> List noContext (fmap go xs) List _ xs -> List noContext (fmap go xs)
Symbol _ w -> Symbol noContext w Symbol _ w -> Symbol noContext w
Literal _ l -> Literal noContext l Literal _ l -> Literal noContext l
OpaqueValue box -> OpaqueValue box
fmt :: Syntax c -> Doc ann fmt :: Syntax c -> Doc ann
fmt = \case fmt = \case
@ -769,6 +904,23 @@ internalEntries = do
z -> z ->
throwIO (BadFormException @C nil) throwIO (BadFormException @C nil)
entry $ bindMatch "eval" $ \syn -> do
r <- mapM eval syn
pure $ lastDef nil r
entry $ bindMatch "id" $ \case
[ e ] -> pure e
_ -> throwIO (BadFormException @C nil)
entry $ bindMatch "inc" $ \case
[ LitIntVal n ] -> pure (mkInt (succ n))
_ -> throwIO (TypeCheckError @C nil)
entry $ bindMatch "dec" $ \case
[ LitIntVal n ] -> pure (mkInt (succ n))
_ -> throwIO (TypeCheckError @C nil)
entry $ bindMatch "map" $ \syn -> do entry $ bindMatch "map" $ \syn -> do
case syn of case syn of
[ListVal (SymbolVal "builtin:lambda" : SymbolVal fn : _), ListVal rs] -> do [ListVal (SymbolVal "builtin:lambda" : SymbolVal fn : _), ListVal rs] -> do
@ -782,6 +934,16 @@ internalEntries = do
_ -> do _ -> do
throwIO (BadFormException @C nil) throwIO (BadFormException @C nil)
entry $ bindMatch "quot" $ \case
[ syn ] -> pure $ mkList [syn]
_ -> do
throwIO (BadFormException @C nil)
entry $ bindMatch "quasiquot" $ \case
[ syn ] -> mkList . List.singleton <$> (evalQQ mempty) syn
_ -> do
throwIO (BadFormException @C nil)
entry $ bindMatch "head" $ \case entry $ bindMatch "head" $ \case
[ ListVal es ] -> pure (head es) [ ListVal es ] -> pure (head es)
_ -> throwIO (TypeCheckError @C nil) _ -> throwIO (TypeCheckError @C nil)
@ -821,6 +983,61 @@ internalEntries = do
[ sy ] -> display sy [ sy ] -> display sy
ss -> display (mkList ss) ss -> display (mkList ss)
let colorz = HM.fromList
[ ("red", pure (Red, True))
, ("red~", pure (Red, False))
, ("green", pure (Green, True))
, ("green~", pure (Green, False))
, ("yellow", pure (Yellow, True))
, ("yellow~", pure (Yellow, False))
, ("blue", pure (Blue, True))
, ("blue~", pure (Blue, False))
, ("magenta", pure (Magenta, True))
, ("magenta~",pure (Magenta, False))
, ("cyan", pure (Cyan, True))
, ("cyan~", pure (Cyan, False))
, ("white", pure (White, True))
, ("white~", pure (White, False))
, ("black", pure (Black, True))
, ("black~", pure (Black, False))
, ("_", mzero)
]
let fgc fg = case join (HM.lookup fg colorz) of
Just (co, True) -> color co
Just (co, False) -> colorDull co
Nothing -> mempty
let niceTerm f = \case
LitStrVal x -> do
let s = renderStrict $ layoutPretty defaultLayoutOptions (annotate f $ pretty x)
mkStr s
other -> do
let s = renderStrict $ layoutPretty defaultLayoutOptions (annotate f $ pretty other)
mkStr s
entry $ bindMatch "ansi" $ \case
[ SymbolVal fg, SymbolVal bg, term ] | HM.member fg colorz && HM.member bg colorz -> do
let b = case join (HM.lookup bg colorz) of
Just (co, True) -> bgColor co
Just (co, False) -> bgColorDull co
Nothing -> mempty
let f = b <> fgc fg
pure $ niceTerm f term
[ SymbolVal fg, s] | HM.member fg colorz -> do
let f = fgc fg
pure $ niceTerm f s
-- let wtf = show $ pretty s
-- let x = Text.unpack $ renderStrict $ layoutPretty defaultLayoutOptions (annotate f $ pretty wtf)
-- -- error $ show x
-- pure $ mkStr x
_ -> throwIO (BadFormException @c nil)
brief "prints new line character to stdout" brief "prints new line character to stdout"
$ entry $ bindMatch "newline" $ nil_ $ \case $ entry $ bindMatch "newline" $ nil_ $ \case
[] -> liftIO (putStrLn "") [] -> liftIO (putStrLn "")
@ -835,7 +1052,7 @@ internalEntries = do
[ sy ] -> display sy >> liftIO (putStrLn "") [ sy ] -> display sy >> liftIO (putStrLn "")
ss -> mapM_ display ss >> liftIO (putStrLn "") ss -> mapM_ display ss >> liftIO (putStrLn "")
entry $ bindMatch "str:read-stdin" $ \case entry $ bindMatch "str:stdin" $ \case
[] -> liftIO getContents <&> mkStr @c [] -> liftIO getContents <&> mkStr @c
_ -> throwIO (BadFormException @c nil) _ -> throwIO (BadFormException @c nil)
@ -845,7 +1062,7 @@ internalEntries = do
_ -> throwIO (BadFormException @c nil) _ -> throwIO (BadFormException @c nil)
brief "reads file as a string" do brief "reads file as a string" do
entry $ bindMatch "str:read-file" $ \case entry $ bindMatch "str:file" $ \case
[StringLike fn] -> liftIO (TIO.readFile fn) <&> mkStr [StringLike fn] -> liftIO (TIO.readFile fn) <&> mkStr
_ -> throwIO (BadFormException @c nil) _ -> throwIO (BadFormException @c nil)
@ -858,15 +1075,41 @@ internalEntries = do
entry $ bindValue "space" $ mkStr " " entry $ bindValue "space" $ mkStr " "
entry $ bindMatch "parse-top" $ \case let doParseTop w l s =
parseTop s & either (const nil) (mkForm w . fmap ( l . fixContext) )
[SymbolVal w, LitStrVal s] -> do let wrapWith e = \case
pure $ parseTop s & either (const nil) (mkForm w . fmap fixContext) List c es -> List c (e : es)
other -> other
let lwrap = \case
e@(SymbolVal x) -> wrapWith e
_ -> id
[LitStrVal s] -> do brief "parses string as toplevel and produces a form"
pure $ parseTop s & either (const nil) (mkList . fmap fixContext) $ desc "parse:top:string SYMBOL STRING-LIKE"
$ entry $ bindMatch "parse:top:string" $ \case
_ -> throwIO (BadFormException @c nil) [SymbolVal w, LitStrVal s] -> do
pure $ doParseTop w id s
[SymbolVal w, e@(SymbolVal r), LitStrVal s] -> do
pure $ doParseTop w (lwrap e) s
_ -> throwIO (BadFormException @c nil)
brief "parses file as toplevel form and produces a form"
$ desc "parse:top:file SYMBOL <FILENAME>"
$ entry $ bindMatch "parse:top:file" $ \case
[SymbolVal w, StringLike fn] -> do
s <- liftIO $ TIO.readFile fn
pure $ doParseTop w id s
[SymbolVal w, e@(SymbolVal r), StringLike fn] -> do
s <- liftIO $ TIO.readFile fn
pure $ doParseTop w (lwrap e) s
_ -> throwIO (BadFormException @c nil)
let atomFrom = \case let atomFrom = \case
[StringLike s] -> pure (mkSym s) [StringLike s] -> pure (mkSym s)
@ -966,3 +1209,60 @@ internalEntries = do
_ -> throwIO (BadFormException @c nil) _ -> throwIO (BadFormException @c nil)
brief "decodes bytes as utf8 text"
$ desc "bytes:decode <BYTES>"
$ entry $ bindMatch "bytes:decode" $ \case
[ OpaqueVal box ] -> do
let lbs' = fromOpaque @LBS.ByteString box
<|>
(LBS.fromStrict <$> fromOpaque @BS.ByteString box)
lbs <- maybe (throwIO (UnexpectedType "unknown / ByteString")) pure lbs'
-- TODO: maybe-throw-on-invalid-encoding
let txt = decodeUtf8With ignore (LBS.toStrict lbs)
pure $ mkStr txt
_ -> throwIO (BadFormException @c nil)
brief "reads bytes from a file"
$ desc "bytes:file FILE"
$ entry $ bindMatch "bytes:file" $ \case
[ StringLike fn ] -> do
liftIO (LBS.readFile fn) >>= mkOpaque
_ -> throwIO (BadFormException @c nil)
brief "reads bytes from a STDIN"
$ desc "bytes:stdin"
$ entry $ bindMatch "bytes:stdin" $ \case
[] -> do
liftIO LBS.getContents >>= mkOpaque
_ -> throwIO (BadFormException @c nil)
brief "writes bytes to STDOUT"
$ desc "bytes:put <BYTES>"
$ entry $ bindMatch "bytes:put" $ nil_ $ \case
[isOpaqueOf @LBS.ByteString -> Just s ] -> do
liftIO $ LBS.putStr s
[isOpaqueOf @ByteString -> Just s ] -> do
liftIO $ BS.putStr s
_ -> throwIO (BadFormException @c nil)
brief "writes bytes to FILE"
$ desc "bytes:write <FILE> <BYTES>"
$ entry $ bindMatch "bytes:write" $ nil_ $ \case
[StringLike fn, isOpaqueOf @LBS.ByteString -> Just s ] -> do
liftIO $ LBS.writeFile fn s
[StringLike fn, isOpaqueOf @ByteString -> Just s ] -> do
liftIO $ BS.writeFile fn s
_ -> throwIO (BadFormException @c nil)

View File

@ -4,15 +4,24 @@
{-# LANGUAGE PatternSynonyms #-} {-# LANGUAGE PatternSynonyms #-}
{-# Language ViewPatterns #-} {-# Language ViewPatterns #-}
{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE RecordWildCards #-}
module Data.Config.Suckless.Syntax module Data.Config.Suckless.Syntax
( Syntax(..) ( Syntax(..)
, Id(..) , Id(..)
, Literal(..) , Literal(..)
, Opaque(..)
, HasContext , HasContext
, C(..) , C(..)
, Context(..) , Context(..)
, IsContext(..) , IsContext(..)
, IsLiteral(..) , IsLiteral(..)
, ByteStringSorts(..)
, mkOpaque
, isOpaqueOf
, fromOpaque
, fromOpaqueThrow
, isByteString
, SyntaxTypeError(..)
, pattern SymbolVal , pattern SymbolVal
, pattern ListVal , pattern ListVal
, pattern LitIntVal , pattern LitIntVal
@ -20,27 +29,38 @@ module Data.Config.Suckless.Syntax
, pattern LitBoolVal , pattern LitBoolVal
, pattern LitScientificVal , pattern LitScientificVal
, pattern StringLike , pattern StringLike
, pattern TextLike
, pattern StringLikeList , pattern StringLikeList
, pattern Nil , pattern Nil
, pattern OpaqueVal
) )
where where
import Data.Data import Data.Data
import Data.Dynamic
import Data.Kind import Data.Kind
import Data.String import Data.String
import Data.Text (Text) import Data.Text (Text)
import Data.Scientific import Data.Scientific
import GHC.Generics (Generic(..)) import GHC.Generics (Generic(..))
import Data.Maybe import Data.Maybe
-- import GHC.Generics( Fixity(..) )
-- import Data.Data as Data
import Data.Aeson import Data.Aeson
import Data.Aeson.Key import Data.Aeson.Key
import Data.Aeson.KeyMap qualified as Aeson import Data.Aeson.KeyMap qualified as Aeson
import Data.Vector qualified as V import Data.Vector qualified as V
import Data.Traversable (forM) import Data.Traversable (forM)
import Data.Text qualified as Text import Data.Text qualified as Text
import Data.ByteString (ByteString)
import Data.ByteString.Lazy qualified as LBS
import Data.Function import Data.Function
import Data.Functor
import Control.Applicative
import Control.Exception
import Type.Reflection
import Control.Monad.IO.Class
import System.IO.Unsafe (unsafePerformIO)
import Data.IORef
import Data.Word
import Prettyprinter import Prettyprinter
@ -63,20 +83,29 @@ pattern LitBoolVal v <- Literal _ (LitBool v)
pattern ListVal :: [Syntax c] -> Syntax c pattern ListVal :: [Syntax c] -> Syntax c
pattern ListVal v <- List _ v pattern ListVal v <- List _ v
stringLike :: Syntax c -> Maybe String stringLike :: Syntax c -> Maybe String
stringLike = \case stringLike = \case
LitStrVal s -> Just $ Text.unpack s LitStrVal s -> Just $ Text.unpack s
SymbolVal (Id s) -> Just $ Text.unpack s SymbolVal (Id s) -> Just $ Text.unpack s
_ -> Nothing _ -> Nothing
textLike :: Syntax c -> Maybe Text
textLike = \case
LitStrVal s -> Just s
SymbolVal (Id s) -> Just s
x -> Nothing
stringLikeList :: [Syntax c] -> [String] stringLikeList :: [Syntax c] -> [String]
stringLikeList syn = [ stringLike s | s <- syn ] & takeWhile isJust & catMaybes stringLikeList syn = [ stringLike s | s <- syn ] & takeWhile isJust & catMaybes
data ByteStringSorts = ByteStringLazy LBS.ByteString | ByteStringStrict ByteString
pattern StringLike :: forall {c} . String -> Syntax c pattern StringLike :: forall {c} . String -> Syntax c
pattern StringLike e <- (stringLike -> Just e) pattern StringLike e <- (stringLike -> Just e)
pattern TextLike :: forall {c} . Text -> Syntax c
pattern TextLike e <- (textLike -> Just e)
pattern StringLikeList :: forall {c} . [String] -> [Syntax c] pattern StringLikeList :: forall {c} . [String] -> [Syntax c]
pattern StringLikeList e <- (stringLikeList -> e) pattern StringLikeList e <- (stringLikeList -> e)
@ -84,10 +113,25 @@ pattern StringLikeList e <- (stringLikeList -> e)
pattern Nil :: forall {c} . Syntax c pattern Nil :: forall {c} . Syntax c
pattern Nil <- ListVal [] pattern Nil <- ListVal []
pattern OpaqueVal :: forall {c} . Opaque -> Syntax c
pattern OpaqueVal box <- OpaqueValue box
data family Context c :: Type data family Context c :: Type
isOpaqueOf :: forall a c . (Typeable a, IsContext c) => Syntax c -> Maybe a
isOpaqueOf = \case
OpaqueValue box -> fromOpaque @a box
_ -> Nothing
isByteString :: Syntax c -> Maybe ByteStringSorts
isByteString = \case
OpaqueValue box -> do
let lbs = fromOpaque @LBS.ByteString box <&> ByteStringLazy
let bs = fromOpaque @ByteString box <&> ByteStringStrict
lbs <|> bs
_ -> Nothing
class IsContext c where class IsContext c where
noContext :: Context c noContext :: Context c
@ -103,14 +147,71 @@ class IsLiteral a where
newtype Id = newtype Id =
Id Text Id Text
deriving newtype (IsString,Pretty) deriving newtype (IsString,Pretty,Semigroup,Monoid)
deriving stock (Data,Generic,Show,Eq,Ord) deriving stock (Data,Generic,Show,Eq,Ord)
type ForOpaque a = (Typeable a, Eq a)
data Opaque = forall a. ForOpaque a =>
Opaque
{ opaqueProxy :: !(Proxy a)
, opaqueId :: !Word64
, opaqueRep :: !SomeTypeRep
, opaqueDyn :: !Dynamic
}
opaqueIdIORef :: IORef Word64
opaqueIdIORef = unsafePerformIO (newIORef 1)
{-# NOINLINE opaqueIdIORef #-}
mkOpaque :: forall c a m . (MonadIO m, ForOpaque a) => a -> m (Syntax c)
mkOpaque x = do
n <- liftIO $ atomicModifyIORef opaqueIdIORef (\n -> (succ n,n))
pure $ OpaqueValue $ Opaque (Proxy :: Proxy a) n (someTypeRep (Proxy :: Proxy a)) (toDyn x)
data SyntaxTypeError =
UnexpectedType String
deriving stock (Show,Typeable)
instance Exception SyntaxTypeError
fromOpaque :: forall a. Typeable a => Opaque -> Maybe a
fromOpaque (Opaque{..}) = fromDynamic opaqueDyn
fromOpaqueThrow :: forall a m . (MonadIO m, Typeable a) => String -> Opaque -> m a
fromOpaqueThrow s (Opaque{..}) = do
let o = fromDynamic @a opaqueDyn
liftIO $ maybe (throwIO (UnexpectedType s)) pure o
instance Eq Opaque where
(Opaque p1 _ t1 d1) == (Opaque _ _ t2 d2) =
t1 == t2 && unpack p1 d1 == unpack p1 d2
where
unpack :: forall a . (Typeable a) => Proxy a -> Dynamic -> Maybe a
unpack _ = fromDynamic @a
-- Partial Data implementation for Opaque
instance Data Opaque where
gfoldl _ z (Opaque{..}) = z (Opaque{..})
-- Can not be unfolded
gunfold _ z _ = z (Opaque (Proxy :: Proxy ()) 0 (someTypeRep (Proxy :: Proxy ())) (toDyn ()))
toConstr _ = opaqueConstr
dataTypeOf _ = opaqueDataType
opaqueConstr :: Constr
opaqueConstr = mkConstr opaqueDataType "Opaque" [] Prefix
opaqueDataType :: DataType
opaqueDataType = mkDataType "Opaque" [opaqueConstr]
data Literal = data Literal =
LitStr Text LitStr Text
| LitInt Integer | LitInt Integer
| LitScientific Scientific | LitScientific Scientific
| LitBool Bool | LitBool Bool
deriving stock (Eq,Ord,Data,Generic,Show) deriving stock (Eq,Ord,Data,Generic,Show)
instance IsLiteral Text where instance IsLiteral Text where
@ -141,13 +242,14 @@ data Syntax c
= List (Context c) [Syntax c] = List (Context c) [Syntax c]
| Symbol (Context c) Id | Symbol (Context c) Id
| Literal (Context c) Literal | Literal (Context c) Literal
| OpaqueValue Opaque
deriving stock (Generic,Typeable) deriving stock (Generic,Typeable)
instance Eq (Syntax c) where instance Eq (Syntax c) where
(==) (Literal _ a) (Literal _ b) = a == b (==) (Literal _ a) (Literal _ b) = a == b
(==) (Symbol _ a) (Symbol _ b) = a == b (==) (Symbol _ a) (Symbol _ b) = a == b
(==) (List _ a) (List _ b) = a == b (==) (List _ a) (List _ b) = a == b
(==) (OpaqueValue a) (OpaqueValue b) = a == b
(==) _ _ = False (==) _ _ = False
deriving instance (Data c, Data (Context c)) => Data (Syntax c) deriving instance (Data c, Data (Context c)) => Data (Syntax c)
@ -157,6 +259,7 @@ instance Pretty (Syntax c) where
pretty (Symbol _ s) = pretty s pretty (Symbol _ s) = pretty s
pretty (List _ (x:xs)) = parens $ align $ sep ( fmap pretty (x:xs) ) pretty (List _ (x:xs)) = parens $ align $ sep ( fmap pretty (x:xs) )
pretty (List _ []) = parens mempty pretty (List _ []) = parens mempty
pretty (OpaqueValue v) = "#opaque:" <> viaShow (opaqueRep v) <> ":" <> pretty (opaqueId v)
instance Pretty Literal where instance Pretty Literal where
pretty = \case pretty = \case
@ -175,6 +278,7 @@ instance ToJSON Literal where
toJSON (LitBool b) = Bool b toJSON (LitBool b) = Bool b
instance ToJSON (Syntax c) where instance ToJSON (Syntax c) where
toJSON (OpaqueValue{}) = Null
toJSON (Symbol _ (Id "#nil")) = Null toJSON (Symbol _ (Id "#nil")) = Null
toJSON (Symbol _ (Id s)) = String s toJSON (Symbol _ (Id s)) = String s
toJSON (Literal _ l) = toJSON l toJSON (Literal _ l) = toJSON l

View File

@ -1,6 +1,6 @@
cabal-version: 3.0 cabal-version: 3.0
name: suckless-conf name: suckless-conf
version: 0.1.2.7 version: 0.1.2.9
-- synopsis: -- synopsis:
-- description: -- description:
license: BSD-3-Clause license: BSD-3-Clause
@ -67,6 +67,7 @@ library
, Data.Config.Suckless.KeyValue , Data.Config.Suckless.KeyValue
, Data.Config.Suckless.Script , Data.Config.Suckless.Script
, Data.Config.Suckless.Script.File , Data.Config.Suckless.Script.File
, Data.Config.Suckless.Almost.RPC
other-modules: other-modules:
Data.Config.Suckless.Types Data.Config.Suckless.Types
@ -95,6 +96,7 @@ library
, text , text
, time , time
, transformers , transformers
, typed-process
, unliftio , unliftio
, unordered-containers , unordered-containers
, vector , vector