packages feed

s-expression (empty) → 0.0.0

raw patch · 7 files changed

+477/−0 lines, 7 filesdep +basedep +derive-monoiddep +lenssetup-changed

Dependencies added: base, derive-monoid, lens, s-expression, semigroups

Files

+ LICENSE view
@@ -0,0 +1,22 @@+The MIT License (MIT)++Copyright (c) 2015 Sam Boosalis++Permission is hereby granted, free of charge, to any person obtaining a copy+of this software and associated documentation files (the "Software"), to deal+in the Software without restriction, including without limitation the rights+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+copies of the Software, and to permit persons to whom the Software is+furnished to do so, subject to the following conditions:++The above copyright notice and this permission notice shall be included in all+copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+SOFTWARE.+
+ Main.hs view
@@ -0,0 +1,1 @@+import Data.Sexp.Main
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ s-expression.cabal view
@@ -0,0 +1,88 @@+name: s-expression+version: 0.0.0+cabal-version: >=1.10+build-type: Simple+license: MIT+license-file: LICENSE+copyright: Copyright (C) 2015 Spiros M. Boosalis+maintainer: samboosalis@gmail.com+stability: experimental+homepage: https://github.com/sboosali/s-expression#readme+bug-reports: https://github.com/sboosali/s-expression/issues+synopsis: simple general-purpose s-expressions +description: + this package provides general-purpose functionality for manipulating s-expressions. like:+ . + * a @Functor@ instance that transforms the atoms+ . + * a @Monad@ instance that "expands" atoms into s-expressions+ . + * a @Foldable@ instance that enumerates the atoms (leaves)+ . + * @IsList@ and @IsString@ instances for literal syntax + . + * + . + * + . + * + . + the core type is:+ . + @+ data <https://hackage.haskell.org/package/s-expression/docs//Data-Sexp.html Sexp> f a+  = Atom a +  | List   [Sexp f a]+  | Sexp f [Sexp f a]+ @+ .+ which lets you provide your own custom function name that interprets its arguments.+ . + e.g. TODO  + . + for efficient parsing/printing, use:  + . + * https://hackage.haskell.org/package/sexp+ . + * https://hackage.haskell.org/package/atto-lisp+ . ++category: Data +author: Spiros Boosalis+tested-with: GHC ==7.10.1++source-repository head+    type: git+    location: https://github.com/sboosali/s-expression++library++ exposed-modules:+  Data.Sexp +  Data.Sexp.Example +  Data.Sexp.Main ++ build-depends:+    base >=4.8 && <5.0++  -- cabal sandbox add-source ... +  , derive-monoid >=0.0.1  ++  , lens +  , semigroups++ default-language: Haskell2010+ hs-source-dirs: sources+ ghc-options: -Wall+ default-extensions: AutoDeriveTypeable+++executable example-s-expressions+ main-is: Main.hs+ build-depends:+    base+  , s-expression+ default-language: Haskell2010+ hs-source-dirs: .+ ghc-options: -Wall+
+ sources/Data/Sexp.hs view
@@ -0,0 +1,330 @@+{-# LANGUAGE AutoDeriveTypeable, DeriveDataTypeable, DeriveGeneric, DeriveAnyClass, DeriveFunctor, DeriveFoldable, DeriveTraversable  #-}+{-# LANGUAGE TemplateHaskell, LambdaCase, TypeFamilies #-}+module Data.Sexp where++import Derive.List (deriveList) +import Control.Lens (Plated (..)) ++import Data.Foldable  (Foldable (..))+import Data.Data (Data) +import GHC.Generics (Generic) +import GHC.Exts       (IsString (..))+import Data.Void (Void) +++{- | a heterogenous list.++a <http://en.wikipedia.org/wiki/Common_Lisp#The_function_namespace Lisp-2> S-expression, where:++* @f@ is the function namespace+* @a@ is the atom namespace++you could define @type Lisp1 a = Sexp a a@. with some caveats: ++* @f@ is ignored by 'Monad'ic methods like 'joinSexp'+* @plate@ doesn't reach the @f@, even when @f ~ a@, as the 'Plated' instance is manual, not automatic via @Data@.++the 'List' case is just a specialized @'Sexp' ()@, but easier to work with than:++* @Sexp (Maybe f) [Sexp f a]@ (where Nothing would represent 'List')+* forcing each concrete @f@ to hold a unit case (which would represent 'List')++examples: ++>>> 'toList' (List [Atom "f",Atom "x",List [Atom "g",Atom "y"],Atom "z"])+["f","x","g","y","z"]++>>> :{+let doubleSexp e = do+                    x <- e+                    listSexp [x,x]+:} +>>> doubleSexp (List [Atom "f", Sexp () [Atom "a", Atom "b"]])+List [List [Atom "f",Atom "f"],Sexp () [List [Atom "a",Atom "a"],List [Atom "b",Atom "b"]]]++-}+data Sexp f a+ = Atom a + | List   [Sexp f a]+ | Sexp f [Sexp f a]+ deriving (Show,Read,Eq,Ord,Functor,Foldable,Traversable,Data,Generic)++{-| isomorphic to: ++@+data Sexp_ a+ = Atom_ a + | List_ [Sexp_ a]+@ ++when you only care about lists (e.g. to interface with other s-expression libraries).  ++-}+type Sexp_ = Sexp Void ++{-| ++@+data ByteSexp+ = Atom ByteString+ | List [ByteSexp]++bytesexp2sexp :: ByteSexp -> 'Sexp_' ByteString+bytesexp2sexp = 'toSexp' $ \case+ Atom s  -> Left  s + List es -> Right es +@++-}+toSexp :: (r -> Either a [r]) -> (r -> Sexp_ a)  +toSexp f = go+ where + go r = case f r of +  Left  a  -> Atom a +  Right rs -> List (map go rs) ++-- | default instance via the 'Monad' subclass.+instance Applicative (Sexp f) where+ pure = return + (<*>) f x = f >>= (\g -> x >>= (return.g))++{- |++definitions:++@+ 'return' = 'pureSexp'+ '(>>=)' = 'bindSexp'+@++proofs of laws:+++* left-inverse(1): @join . return = id@++    @+    join (return m)+    joinSexp (pureSexp m)+    joinSexp (Atom m)+    m+    @+++* left-inverse(2): @join . fmap return = id@++    (the Sexp case is elided, the steps being identical to the List case)++    @+    join (fmap return m)+    joinSexp (fmap pureSexp m)+    joinSexp (fmap Atom m)+    -- case analysis +    case m of+     Atom x ->+      joinSexp (Atom (Atom x)) +      -- by definition of joinSexp+      Atom x+     List ms ->+      joinSexp (List (fmap (fmap Atom) ms)+      -- by definition of joinSexp+      List (fmap joinSexp (fmap (fmap Atom) ms))+      -- functor composition +      List (fmap (joinSexp . fmap Atom) ms)+      List (fmap (join . fmap return) ms)+      -- by induction+      List (fmap id ms)+      -- functor identity +      List ms+     -- both cases are identity +    m+    @+    +    where: +    +    @+    fmap f = \case+     Atom x  -> f x +     List ms -> List (fmap (fmap f) ms)+    +    join = \case+     Atom x  -> x +     List ms -> List (fmap joinSexp ms)+    @+    +* associativity(3): @join . join = join . fmap join@++    @+    TODO+    @++-}+instance Monad (Sexp f) where+ return = pure+ (>>=)  = bindSexp+-- TODO laws, verified as @QuickCheck@ properties:++instance Plated (Sexp f a) where+ plate f = \case+  Atom a    -> Atom   <$> pure a+  List   ps -> List   <$> traverse f ps+  Sexp g ps -> Sexp g <$> traverse f ps++{-| ++>>> :set -XOverloadedStrings  +>>> "x" :: Sexp f String+Atom "x" ++-}+instance (IsString a) => IsString (Sexp f a) where+ fromString = Atom . fromString++{-| @pureSexp = 'Atom'@+-}+pureSexp :: a -> Sexp f a+pureSexp = Atom+{-# INLINE pureSexp #-}++bindSexp :: Sexp f a -> (a -> Sexp f b) -> Sexp f b+bindSexp s f = (joinSexp . fmap f) s+{-# INLINE bindSexp #-}++{-| ++-}+joinSexp :: Sexp f (Sexp f a) -> Sexp f a+joinSexp = \case+ Atom     e -> e+ List   ess -> List   (joinSexp <$> ess)+ Sexp f ess -> Sexp f (joinSexp <$> ess)+{-# INLINEABLE joinSexp #-} -- to hit the Atom I hope.++deriveList ''Sexp 'List ++{-| refines any Sexp to a list, which can be given to the 'List'. -}+toSexpList :: Sexp f a -> [Sexp f a]++{-| >>> appendSexp (Atom "f") (List [Atom "x"])+List [Atom "f",Atom "x"]+-}+appendSexp :: Sexp f a -> Sexp f a -> Sexp f a++{-| @emptySexp = 'List' []@ -}+emptySexp :: Sexp f a+++{-| fold over an sexp. ++i.e. strictly evaluate a sexp ("all the way") to an atom, within any monadic context.++-}+evalSexp :: (Monad m) => ([a] -> m a) -> ([a] -> g -> m a) -> Sexp g a -> m a+evalSexp list apply = \case+ Atom a    -> pure a+ List   es -> list           =<< traverse go es+ Sexp g es -> (flip apply) g =<< traverse go es+ where+ go = evalSexp list apply++{-| ++>>> data ArithFunc = Add | Multiply | Negate deriving Show +>>> let badArith  = Sexp Negate [Atom 1, Atom 2, Atom 3] :: Sexp ArithFunc Integer +>>> let goodArith = Sexp Add [Sexp Multiply [], Sexp Negate [Atom (10::Integer)], Sexp Multiply [Atom 2, Atom 3, Atom 4]]+>>> :set -XLambdaCase+>>> :{+let evalArith = \case+                 Add      -> \case+                              xs    -> Just [sum xs]+                 Multiply -> \case+                              xs    -> Just [product xs]+                 Negate   -> \case+                              [x]   -> Just [negate x] +                              _     -> Nothing +:}+>>> evalSplatSexp (flip evalArith) (fmap (:[]) badArith)  -- wrong arity +Nothing +>>> evalSplatSexp (flip evalArith) (fmap (:[]) goodArith) -- (+ (*) (- 10) (* 2 3 4))+Just [15] ++specializing, as above, @(m ~ Maybe)@, @(b ~ [Integer])@, @(g ~ ArithFunc)@:++@evalSplatSexp :: ([Integer] -> ArithFunc -> Maybe [Integer]) -> (Sexp ArithFunc [Integer] -> Maybe [Integer])@ ++@evalSplatSexp apply = 'evalSexp' ('pure'.'fold') (apply.'fold')@ ++-}+evalSplatSexp :: (Monad m, Monoid b) => (b -> g -> m b) -> (Sexp g b -> m b)+evalSplatSexp apply = evalSexp (pure.fold) (apply.fold)+{-# INLINE evalSplatSexp #-}++{-| ++when a Sexp\'s atoms are 'Monoid'al ("list-like"),+after evaluating some expressions into atoms,+we can "splat" them back together.++@splatList@ takes: ++* an evaluator @eval@++* and a list of s-expressions @es@ to evaluate in sequence.++-}+splatSexpList :: (Applicative m, Monoid b) => (Sexp g b -> m b) -> [Sexp g b] -> m b+splatSexpList eval = fmap fold . traverse eval +{-# INLINE splatSexpList #-}++{-| inject a list of atoms.  ++>>> listSexp [1,2,3]+List [Atom 1,Atom 2,Atom 3]++-}+listSexp :: [a] -> Sexp f a+listSexp = List . map Atom +{-# INLINE listSexp #-}++-- data SexpF f a r+--  = AtomF a+--  | FuncF (f r) +--  | ListF [r]+--  deriving (Show,Read,Eq,Ord,Functor,Data,Generic)++-- type Sexp' a = Fix (SexpF a)++{- helper when converting from other sexp types, like from a parsing library. ++@Either Bytestring [Bytestring]@ ++is isomorphic to: ++e.g. the @atto-lisp@ package defines: ++@+data Lisp+  = Symbol Text   -- ^ A symbol (including keyword)+  | String Text   -- ^ A string.+  | Number Number   -- ^ A number+  | List [Lisp]     -- ^ A proper list: @(foo x 42)@+  | DotList [Lisp] Lisp  -- ^ A list with a non-nil tail: @(foo x+                         -- . 42)@.  The list argument must be+                         -- non-empty and the tail must be non-'nil'.+@ ++we can define: ++@+type AttoLispSexp = Sexp AttoLispFunc AttoLispAtom+data AttoLispAtom = SymbolAtom Text | StringAtom Text | NumberAtom Number +TODO data AttoLispFunc r = DotListFunc [r] r +@+++-}+-- fromSexp ::  -> +-- fromSexp = ++-- toSexp ::  -> +-- toSexp = +
+ sources/Data/Sexp/Example.hs view
@@ -0,0 +1,19 @@+{-# LANGUAGE OverloadedLists, OverloadedStrings #-}+{-| (see source) ++-}+module Data.Sexp.Example where +import Data.Sexp +++exampleSexp :: Sexp () String+exampleSexp = ["f", "x", ["g", "y"], "z"]++-- type ElispSexp = Sexp ElispFunc ElispAtom++-- data ElispAtom+--  = ++-- data ElispFunc+--  = +
+ sources/Data/Sexp/Main.hs view
@@ -0,0 +1,15 @@+{-# LANGUAGE LambdaCase #-}+{-# OPTIONS_GHC -fno-warn-missing-signatures #-}+module Data.Sexp.Main where +import Data.Sexp.Example +import Data.Foldable  (Foldable (..))++import           System.Environment             (getArgs)+++main = mainWith =<< getArgs++mainWith = \case+ _ -> do +  print$ exampleSexp +  print$ toList exampleSexp