colorless 0.0.0 → 0.0.1
raw patch · 16 files changed
+1822/−232 lines, 16 filesdep +aesondep +bytestringdep +colorlessdep −megaparsecdep −pregamesetup-changed
Dependencies added: aeson, bytestring, colorless, containers, errors, exceptions, hspec, lifted-async, monad-control, monad-logger, mtl, random, scientific, text, unordered-containers, vector
Dependencies removed: megaparsec, pregame
Files
- LICENSE +19/−20
- README.md +1/−0
- Setup.hs +0/−2
- colorless.cabal +65/−55
- library/Colorless/Endpoint.hs +78/−0
- library/Colorless/Runtime/Ast.hs +226/−0
- library/Colorless/Runtime/Expr.hs +677/−0
- library/Colorless/Runtime/Types.hs +116/−0
- library/Colorless/Runtime/Val.hs +284/−0
- library/Colorless/Types.hs +123/−0
- package.yaml +64/−0
- src/Colorless/CodeGen/Types.hs +0/−155
- tests/AstSpec.hs +124/−0
- tests/ExprSpec.hs +13/−0
- tests/Spec.hs +1/−0
- tests/ValSpec.hs +31/−0
LICENSE view
@@ -1,30 +1,29 @@-Copyright Joe Vargas (c) 2016+BSD 3-Clause License +Copyright (c) 2017, Joe Vargas All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright- notice, this list of conditions and the following disclaimer.+* Redistributions of source code must retain the above copyright notice, this+ list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above- copyright notice, this list of conditions and the following- disclaimer in the documentation and/or other materials provided- with the distribution.+* Redistributions in binary form must reproduce the above copyright notice,+ this list of conditions and the following disclaimer in the documentation+ and/or other materials provided with the distribution. - * Neither the name of Joe Vargas nor the names of other- contributors may be used to endorse or promote products derived- from this software without specific prior written permission.+* Neither the name of the copyright holder nor the names of its+ contributors may be used to endorse or promote products derived from+ this software without specific prior written permission. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,1 @@+# colorless-haskell
− Setup.hs
@@ -1,2 +0,0 @@-import Distribution.Simple-main = defaultMain
colorless.cabal view
@@ -1,60 +1,70 @@-name:- colorless-version:- 0.0.0-synopsis:- Yet another IDL for RPC-description:- Please see README.md-homepage:- http://github.com/jxv/colorless#readme-license:- BSD3-license-file:- LICENSE-author:- Joe Vargas-maintainer:- http://github.com/jxv-copyright:- 2016 Joe Vargas-category:- Web-build-type:- Simple--- extra-source-files:-cabal-version:- >=1.10+-- This file has been generated from package.yaml by hpack version 0.17.0.+--+-- see: https://github.com/sol/hpack +name: colorless+version: 0.0.1+synopsis: Colorless+description: Colorless+category: Web+maintainer: Joe Vargas+license: BSD3+license-file: LICENSE+build-type: Simple+cabal-version: >= 1.10++extra-source-files:+ package.yaml+ README.md+ library hs-source-dirs:- src- exposed-modules:- Colorless.CodeGen.Types+ library+ default-extensions: ConstraintKinds DeriveGeneric DuplicateRecordFields FlexibleContexts FlexibleInstances GADTs GeneralizedNewtypeDeriving KindSignatures LambdaCase MultiParamTypeClasses NamedFieldPuns OverloadedStrings RankNTypes ScopedTypeVariables TupleSections TypeApplications TypeOperators+ ghc-options: -Wall build-depends:- base >= 4.7 && < 5- , pregame == 1.0.3.0- , megaparsec- default-language:- Haskell2010- default-extensions:- NoImplicitPrelude- OverloadedStrings- TypeSynonymInstances- FlexibleInstances- FlexibleContexts- GeneralizedNewtypeDeriving- LambdaCase- ScopedTypeVariables- NamedFieldPuns- MultiParamTypeClasses- TypeFamilies- TupleSections- DuplicateRecordFields- OverloadedLists+ aeson+ , base >= 4.7 && < 5+ , bytestring+ , containers+ , errors+ , exceptions+ , lifted-async+ , monad-control+ , monad-logger+ , mtl+ , random+ , scientific+ , text+ , unordered-containers+ , vector+ exposed-modules:+ Colorless.Endpoint+ Colorless.Runtime.Ast+ Colorless.Runtime.Expr+ Colorless.Runtime.Types+ Colorless.Runtime.Val+ Colorless.Types+ default-language: Haskell2010 -source-repository head- type:- git- location:- https://github.com/jxv/colorless+test-suite colorless-suite+ type: exitcode-stdio-1.0+ main-is: Spec.hs+ hs-source-dirs:+ tests+ default-extensions: ConstraintKinds DeriveGeneric DuplicateRecordFields FlexibleContexts FlexibleInstances GADTs GeneralizedNewtypeDeriving KindSignatures LambdaCase MultiParamTypeClasses NamedFieldPuns OverloadedStrings RankNTypes ScopedTypeVariables TupleSections TypeApplications TypeOperators+ ghc-options: -Wall -rtsopts -threaded -with-rtsopts=-N+ build-depends:+ aeson+ , base >= 4.7 && < 5+ , colorless+ , containers+ , hspec+ , scientific+ , text+ , vector+ other-modules:+ AstSpec+ ExprSpec+ ValSpec+ default-language: Haskell2010
+ library/Colorless/Endpoint.hs view
@@ -0,0 +1,78 @@+{-# LANGUAGE DataKinds #-}+module Colorless.Endpoint+ ( runColorless+ , runColorlessSingleton+ ) where++import qualified Data.Map as Map+import qualified Data.HashMap.Lazy as HML+import Control.Monad.IO.Class (MonadIO(..))+import Control.Concurrent.Async.Lifted ()+import Data.Aeson+import Data.Aeson.Types (parseMaybe)+import Data.Text (Text)+import Data.Map (Map)++import Colorless.Types++runColorlessSingleton+ :: (MonadIO m, RuntimeThrower m)+ => Version+ -> (Request -> m Response)+ -> Value+ -> m Value+runColorlessSingleton Version{major,minor} handleRequest = runColorless (Map.singleton major (minor, handleRequest))++runColorless+ :: (MonadIO m, RuntimeThrower m)+ => Map Major (Minor, Request -> m Response)+ -> Value+ -> m Value+runColorless handleRequestMap v = do+ colorlessVersion <- getColorlessVersion v+ assertColorlessVersionCompatiability colorlessVersion+ apiVersion <- getApiVersion v+ let apiMajor = major apiVersion+ case Map.lookup apiMajor handleRequestMap of+ Nothing -> case leastAndGreatest (Map.keys handleRequestMap) of+ Nothing -> runtimeThrow RuntimeError'NoImplementation+ Just (minMajor, maxMajor) ->+ if minMajor > apiMajor+ then runtimeThrow RuntimeError'ApiMajorVersionTooLow+ else if maxMajor < apiMajor+ then runtimeThrow RuntimeError'ApiMajorVersionTooHigh+ else runtimeThrow RuntimeError'NoImplementation+ Just (maxMinor, handleRequest) -> if minor apiVersion > maxMinor+ then runtimeThrow RuntimeError'ApiMinorVersionTooHigh+ else case parseRequest v of+ Nothing -> runtimeThrow RuntimeError'UnparsableFormat+ Just req -> toJSON <$> handleRequest req++leastAndGreatest :: Ord a => [a] -> Maybe (a,a)+leastAndGreatest [] = Nothing+leastAndGreatest xs = Just (minimum xs, maximum xs)++assertColorlessVersionCompatiability :: RuntimeThrower m => Version -> m ()+assertColorlessVersionCompatiability Version{major,minor}+ | major > mustMajor = runtimeThrow RuntimeError'ColorlessMajorVersionTooHigh+ | major < mustMajor = runtimeThrow RuntimeError'ColorlessMajorVersionTooLow+ | minor > maxMinor = runtimeThrow RuntimeError'ColorlessMinorVersionTooHigh+ | otherwise = return ()+ where+ mustMajor = 0+ maxMinor = 0++getApiVersion :: RuntimeThrower m => Value -> m Version+getApiVersion = getVersion "version" RuntimeError'NoApiVersion++getColorlessVersion :: RuntimeThrower m => Value -> m Version+getColorlessVersion = getVersion "colorless" RuntimeError'NoColorlessVersion++getVersion :: RuntimeThrower m => Text -> RuntimeError -> Value -> m Version+getVersion name err (Object o) = case HML.lookup name o of+ Just x -> maybe (runtimeThrow err) return (parseMaybe parseJSON x)+ Nothing -> runtimeThrow err+getVersion _ err _ = runtimeThrow err++parseRequest :: Value -> Maybe Request+parseRequest = parseMaybe parseJSON
+ library/Colorless/Runtime/Ast.hs view
@@ -0,0 +1,226 @@+{-# LANGUAGE DeriveGeneric #-}+module Colorless.Runtime.Ast+ ( Ast(..)+ , Ref(..)+ , If(..)+ , Set(..)+ , Get(..)+ , Define(..)+ , Lambda(..)+ , List(..)+ , Begin(..)+ , FnCall(..)+ , Enumerator(..)+ , Struct(..)+ , Wrap(..)+ , StructCall(..)+ , WrapCall(..)+ , EnumerationCall(..)+ , HollowCall(..)+ , Const(..)+ ) where++import qualified Data.HashMap.Lazy as HML+import qualified Data.Vector as V+import Control.Monad (mzero)+import Control.Applicative ((<|>))+import Data.Text (Text)+import Data.Aeson+import Data.Map (Map)+import GHC.Generics (Generic)++import Colorless.Runtime.Types++data Ast+ = Ast'Ref Ref+ | Ast'If If+ | Ast'Set Set+ | Ast'Get Get+ | Ast'Define Define+ | Ast'Lambda Lambda+ | Ast'List List+ | Ast'Begin Begin+ | Ast'FnCall FnCall+ | Ast'WrapCall WrapCall+ | Ast'StructCall StructCall+ | Ast'EnumerationCall EnumerationCall+ | Ast'HollowCall HollowCall+ | Ast'Enumerator Enumerator+ | Ast'Struct Struct+ | Ast'Wrap Wrap+ | Ast'Const Const+ deriving (Show, Eq)++instance FromJSON Ast where+ parseJSON v+ = (Ast'Ref <$> parseJSON v)+ <|> (Ast'If <$> parseJSON v)+ <|> (Ast'Set <$> parseJSON v)+ <|> (Ast'Get <$> parseJSON v)+ <|> (Ast'Define <$> parseJSON v)+ <|> (Ast'Lambda <$> parseJSON v)+ <|> (Ast'List <$> parseJSON v)+ <|> (Ast'Begin <$> parseJSON v)+ <|> (Ast'FnCall <$> parseJSON v)+ <|> (Ast'EnumerationCall <$> parseJSON v)+ <|> (Ast'WrapCall <$> parseJSON v)+ <|> (Ast'StructCall <$> parseJSON v)+ <|> (Ast'HollowCall <$> parseJSON v)+ <|> (Ast'Enumerator <$> parseJSON v)+ <|> (Ast'Struct <$> parseJSON v)+ <|> (Ast'Wrap <$> parseJSON v)+ <|> (Ast'Const <$> parseJSON v)++data Ref = Ref+ { symbol :: Symbol+ } deriving (Show, Eq)++instance FromJSON Ref where+ parseJSON (Object o) = Ref <$> o .: "@"+ parseJSON _ = mzero++data If = If+ { cond :: Ast+ , true :: Ast+ , false :: Ast+ } deriving (Show, Eq)++instance FromJSON If where+ parseJSON (Array arr) = case V.toList arr of+ ["if", cond, true, false] -> If <$> parseJSON cond <*> parseJSON true <*> parseJSON false+ _ -> mzero+ parseJSON _ = mzero++data Set = Set+ { var :: Symbol+ , expr :: Ast+ } deriving (Show, Eq)++instance FromJSON Set where+ parseJSON (Array arr) = case V.toList arr of+ ["set", var, expr] -> Set <$> parseJSON var <*> parseJSON expr+ _ -> mzero+ parseJSON _ = mzero++data Get = Get+ { path :: [Text]+ , val :: Ast+ } deriving (Show, Eq)++instance FromJSON Get where+ parseJSON (Array arr) = case V.toList arr of+ ["get", path, val] -> Get <$> parseJSON path <*> parseJSON val+ _ -> mzero+ parseJSON _ = mzero++data Define = Define+ { var :: Symbol+ , expr :: Ast+ } deriving (Show, Eq)++instance FromJSON Define where+ parseJSON (Array arr) = case V.toList arr of+ ["def", var, expr] -> Define <$> parseJSON var <*> parseJSON expr+ _ -> mzero+ parseJSON _ = mzero++data Lambda = Lambda+ { args :: [(Symbol, Type)]+ , expr :: Ast+ } deriving (Show, Eq)++instance FromJSON Lambda where+ parseJSON (Array arr) = case V.toList arr of+ ["fn", Array params, expr] -> Lambda <$> mapM parseParam (V.toList params) <*> parseJSON expr+ _ -> mzero+ where+ parseParam = \case+ Object o -> case HML.toList o of+ [(key,val)] -> (Symbol key,) <$> parseJSON val+ _ -> mzero+ _ -> mzero+ parseJSON _ = mzero++data List = List+ { list :: [Ast]+ } deriving (Show, Eq)++instance FromJSON List where+ parseJSON (Object o) = List <$> o .: "List"+ parseJSON _ = mzero++data Begin = Begin+ { vals :: [Ast]+ } deriving (Show, Eq)++instance FromJSON Begin where+ parseJSON (Array arr) = case V.toList arr of+ "begin":xs -> Begin <$> mapM parseJSON xs+ _ -> mzero+ parseJSON _ = mzero++data FnCall = FnCall+ { fn :: Ast+ , args :: [Ast]+ } deriving (Show, Eq)++instance FromJSON FnCall where+ parseJSON (Array arr) = case V.toList arr of+ ((String x):xs) -> FnCall <$> pure (Ast'Ref $ Ref $ Symbol x) <*> mapM parseJSON xs+ (x:xs) -> FnCall <$> parseJSON x <*> mapM parseJSON xs+ _ -> mzero+ parseJSON _ = mzero++data EnumerationCall = EnumerationCall+ { n :: TypeName+ , e :: Ast+ } deriving (Show, Eq, Generic)++instance FromJSON EnumerationCall++data WrapCall = WrapCall+ { n :: TypeName+ , w :: Ast+ } deriving (Show, Eq, Generic)++instance FromJSON WrapCall++data StructCall = StructCall+ { n :: TypeName+ , m :: Map MemberName Ast+ } deriving (Show, Eq, Generic)++instance FromJSON StructCall++data HollowCall = HollowCall+ { n :: TypeName+ } deriving (Show, Eq, Generic)++instance FromJSON HollowCall++data Enumerator = Enumerator+ { tag :: EnumeratorName+ , m :: Maybe (Map MemberName Ast)+ } deriving (Show, Eq, Generic)++instance FromJSON Enumerator where+ parseJSON (Object o) = do+ tag <- o .: "tag"+ let tagless = HML.delete "tag" o+ if HML.size o == 1+ then pure $ Enumerator tag Nothing+ else Enumerator tag <$> (Just <$> parseJSON (Object tagless))+ parseJSON _ = mzero++data Struct = Struct+ { m :: Map MemberName Ast+ } deriving (Show, Eq, Generic)++instance FromJSON Struct where+ parseJSON v = Struct <$> parseJSON v++data Wrap = Wrap+ { w :: Ast+ } deriving (Show, Eq, Generic)++instance FromJSON Wrap
+ library/Colorless/Runtime/Expr.hs view
@@ -0,0 +1,677 @@+{-# LANGUAGE RankNTypes #-}+module Colorless.Runtime.Expr+ ( Expr(..)+ , EvalConfig(..)+ --+ , Ref(..)+ , UnVal(..)+ , UnEnumerator(..)+ , UnWrap(..)+ , UnStruct(..)+ , If(..)+ , Set(..)+ , Get(..)+ , Define(..)+ , Lambda(..)+ , Fn(..)+ , List(..)+ , Begin(..)+ , FnCall(..)+ , ApiUnCall(..)+ , HollowUnCall(..)+ , WrapUnCall(..)+ , StructUnCall(..)+ , EnumerationUnCall(..)+ , Val(..)+ , ApiVal(..)+ , Wrap(..)+ , Struct(..)+ , Enumerator(..)+ , ApiCall(..)+ --+ , jsonToExpr+ , apiCallName+ , fromAst+ --+ , ApiParser(..)+ , parseApiCall+ --+ , eval+ , forceVal+ , runEval+ , emptyEnv+ ) where++import qualified Data.Map as Map+import Control.Monad (when, join)+import Control.Monad.IO.Class (MonadIO(..))+import Control.Monad.Reader (MonadReader(..), ReaderT(..), asks, lift)+import Data.Map (Map)+import Data.Int+import Data.Word+import Data.Aeson (parseJSON, Value)+import Data.Aeson.Types (parseMaybe)+import Data.IORef (IORef, readIORef, newIORef, writeIORef)+import Data.Scientific (toBoundedInteger, Scientific)+import Data.Text (Text)++import qualified Colorless.Runtime.Ast as Ast+import Colorless.Types+import Colorless.Runtime.Types+import Colorless.Runtime.Val+import Colorless.Runtime.Ast (Ast(..))++data EvalConfig m = EvalConfig+ { options :: Options+ , apiCall :: ApiCall -> m Val+ }++jsonToExpr :: (Monad m) => Value -> Maybe (Expr m)+jsonToExpr = fmap fromAst . parseMaybe parseJSON++newtype Eval m a = Eval (ReaderT (EvalConfig m) m a)+ deriving (Functor, Applicative, Monad, MonadReader (EvalConfig m), MonadIO)++instance RuntimeThrower m => RuntimeThrower (Eval m) where+ runtimeThrow err = Eval (lift $ runtimeThrow err)++getOptions :: Monad m => Eval m Options+getOptions = asks options++type Env m = Map Symbol (IORef (Expr m))++runEval :: MonadIO m => Eval m a -> EvalConfig m -> m a+runEval (Eval r) = runReaderT r++data Expr m+ = Expr'Ref Ref+ | Expr'UnVal (UnVal m)+ | Expr'Val Val+ | Expr'If (If m)+ | Expr'Set (Set m)+ | Expr'Get (Get m)+ | Expr'Define (Define m)+ | Expr'Lambda (Lambda m)+ | Expr'List (List m)+ | Expr'Fn (Fn m)+ | Expr'FnCall (FnCall m)+ | Expr'Begin (Begin m)+ | Expr'ApiUnCall (ApiUnCall m)+ deriving (Show, Eq)++data UnVal m+ = UnVal'Const Const+ | UnVal'UnWrap (UnWrap m)+ | UnVal'UnStruct (UnStruct m)+ | UnVal'UnEnumerator (UnEnumerator m)+ deriving (Show, Eq)++data UnWrap m = UnWrap+ { w :: Expr m+ } deriving (Show, Eq)++data UnStruct m = UnStruct+ { m :: Map MemberName (Expr m)+ } deriving (Show, Eq)++data UnEnumerator m = UnEnumerator+ { tag :: EnumeratorName+ , m :: Maybe (Map MemberName (Expr m))+ } deriving (Show, Eq)++data Ref = Ref+ { symbol :: Symbol+ } deriving (Show, Eq)++data If m = If+ { cond :: Expr m+ , true :: Expr m+ , false :: Expr m+ } deriving (Show, Eq)++data Set m = Set+ { var :: Symbol+ , expr :: Expr m+ } deriving (Show, Eq)++data Get m = Get+ { path :: [Text]+ , expr :: Expr m+ } deriving (Show, Eq)++data Define m = Define+ { var :: Symbol+ , expr :: Expr m+ } deriving (Show, Eq)++data Lambda m = Lambda+ { params :: [(Symbol, Type)]+ , expr :: Expr m+ } deriving (Show, Eq)++newtype Fn m = Fn ([Expr m] -> Eval m (Expr m))++instance Show (Fn m) where+ show _ = "<Fn>"++instance Eq (Fn m) where+ (==) _ _ = False++data List m = List+ { list :: [Expr m]+ } deriving (Show, Eq)++data Begin m = Begin+ { exprs :: [Expr m]+ } deriving (Show, Eq)++data FnCall m = FnCall+ { fn :: Expr m+ , args :: [Expr m]+ } deriving (Show, Eq)++data ApiUnCall m+ = ApiUnCall'HollowUnCall HollowUnCall+ | ApiUnCall'WrapUnCall (WrapUnCall m)+ | ApiUnCall'StructUnCall (StructUnCall m)+ | ApiUnCall'EnumerationUnCall (EnumerationUnCall m)+ deriving (Show, Eq)++data HollowUnCall = HollowUnCall+ { n :: TypeName+ } deriving (Show, Eq)++data WrapUnCall m = WrapUnCall+ { n :: TypeName+ , w :: Expr m+ } deriving (Show, Eq)++data StructUnCall m = StructUnCall+ { n :: TypeName+ , m :: Map MemberName (Expr m)+ } deriving (Show, Eq)++data EnumerationUnCall m = EnumerationUnCall+ { n :: TypeName+ , e :: Expr m+ } deriving (Show, Eq)++data ApiCall+ = ApiCall'Hollow TypeName+ | ApiCall'Struct TypeName Struct+ | ApiCall'Enumeration TypeName Enumerator+ | ApiCall'Wrap TypeName Wrap+ deriving (Show, Eq)++apiCallName :: ApiCall -> TypeName+apiCallName = \case+ ApiCall'Hollow n -> n+ ApiCall'Struct n _ -> n+ ApiCall'Enumeration n _ -> n+ ApiCall'Wrap n _ -> n++--++fromAst :: Monad m => Ast -> Expr m+fromAst = \case+ Ast'Ref Ast.Ref{symbol} -> Expr'Ref $ Ref symbol+ Ast'If Ast.If{cond,true,false} -> Expr'If $ If (fromAst cond) (fromAst true) (fromAst false)+ Ast'Set Ast.Set{var,expr} -> Expr'Set $ Set var (fromAst expr)+ Ast'Get Ast.Get{path,val} -> Expr'Get $ Get path (fromAst val)+ Ast'Define Ast.Define{var,expr} -> Expr'Define $ Define var (fromAst expr)+ Ast'Lambda Ast.Lambda{args,expr} -> Expr'Lambda $ Lambda args (fromAst expr)+ Ast'List Ast.List{list} -> Expr'List $ List $ map fromAst list+ Ast'Begin Ast.Begin{vals} -> Expr'Begin $ Begin $ map fromAst vals+ Ast'FnCall Ast.FnCall{fn,args} -> Expr'FnCall $ FnCall (fromAst fn) (map fromAst args)+ Ast'WrapCall Ast.WrapCall{n,w} -> Expr'ApiUnCall $ ApiUnCall'WrapUnCall $ WrapUnCall n (fromAst w)+ Ast'HollowCall Ast.HollowCall{n} -> Expr'ApiUnCall $ ApiUnCall'HollowUnCall $ HollowUnCall n+ Ast'StructCall Ast.StructCall{n,m} -> Expr'ApiUnCall $ ApiUnCall'StructUnCall $ StructUnCall n (fromAst <$> m)+ Ast'EnumerationCall Ast.EnumerationCall{n,e} -> Expr'ApiUnCall $ ApiUnCall'EnumerationUnCall $ EnumerationUnCall n (fromAst e)+ Ast'Enumerator Ast.Enumerator{tag,m} -> Expr'UnVal $ UnVal'UnEnumerator $ UnEnumerator tag (fmap fromAst <$> m)+ Ast'Struct Ast.Struct{m} -> Expr'UnVal $ UnVal'UnStruct $ UnStruct (fromAst <$> m)+ Ast'Wrap Ast.Wrap{w} -> Expr'UnVal $ UnVal'UnWrap $ UnWrap (fromAst w)+ Ast'Const c -> Expr'UnVal $ UnVal'Const c++--++addEnvToEnv :: (RuntimeThrower m, Ord k, MonadIO m) => Maybe Int -> Map k a -> IORef (Map k a) -> m (IORef (Map k a))+addEnvToEnv maybeVariableLimit vars envRef = do+ env <- liftIO $ readIORef envRef+ let env' = Map.union vars env+ case maybeVariableLimit of+ Nothing -> return ()+ Just limit -> when (Map.size env' > limit) $ runtimeThrow RuntimeError'VariableLimit+ liftIO $ newIORef env'++addVarToEnv :: (Ord k, MonadIO m, RuntimeThrower m) => Maybe Int -> IORef (Map k a) -> k -> a -> Map k a -> m ()+addVarToEnv maybeVariableLimit envRef var ref env = do+ let env' = Map.insert var ref env+ case maybeVariableLimit of+ Nothing -> return ()+ Just limit -> when (Map.size env' > limit) $ runtimeThrow RuntimeError'VariableLimit+ liftIO $ writeIORef envRef env'++varLookup :: (MonadIO m, RuntimeThrower m) => Map Symbol (IORef a) -> Symbol -> m a+varLookup env symbol@(Symbol s) = case Map.lookup symbol env of+ Nothing -> runtimeThrow $ RuntimeError'UnknownVariable s+ Just var -> liftIO $ readIORef $ var++--++eval :: (MonadIO m, RuntimeThrower m) => Expr m -> IORef (Env m) -> Eval m (Expr m)+eval expr envRef = case expr of+ Expr'Ref atom -> evalRef atom envRef+ Expr'If if' -> evalIf if' envRef+ Expr'UnVal unVal -> evalUnVal unVal envRef+ Expr'Val val -> return $ Expr'Val val+ Expr'Set set -> evalSet set envRef+ Expr'Get get -> evalGet get envRef+ Expr'Define define -> evalDefine define envRef+ Expr'Lambda lambda -> evalLambda lambda envRef+ Expr'Fn _ -> return expr -- throw error?+ Expr'List list -> evalList list envRef+ Expr'FnCall call -> evalFnCall call envRef+ Expr'Begin begin -> evalBegin begin envRef+ Expr'ApiUnCall apiUnCall -> evalApiUnCall apiUnCall envRef++forceVal :: (RuntimeThrower m) => Expr m -> Eval m Val+forceVal (Expr'Val v) = return v+forceVal _ = runtimeThrow RuntimeError'IncompatibleType++evalRef :: (MonadIO m, RuntimeThrower m) => Ref -> IORef (Env m) -> Eval m (Expr m)+evalRef Ref{symbol} envRef = do+ env <- liftIO $ readIORef envRef+ varLookup env symbol++evalUnVal :: (MonadIO m, RuntimeThrower m) => UnVal m -> IORef (Env m) -> Eval m (Expr m)+evalUnVal unVal envRef = case unVal of+ UnVal'Const c -> return $ Expr'Val $ Val'Const c++ UnVal'UnStruct UnStruct{m} -> do+ members <- mapM (\(name,expr) -> (name,) <$> (forceVal =<< eval expr envRef)) (Map.toList m)+ return $ Expr'Val $ Val'ApiVal $ ApiVal'Struct $ Struct (Map.fromList members)++ UnVal'UnWrap UnWrap{w} -> do+ w' <- eval w envRef+ case w' of+ Expr'Val (Val'Const c) -> return $ Expr'Val $ Val'ApiVal $ ApiVal'Wrap $ Wrap c+ _ -> runtimeThrow RuntimeError'IncompatibleType++ UnVal'UnEnumerator UnEnumerator{tag,m} -> do+ case m of+ Nothing -> return $ Expr'Val $ Val'ApiVal $ ApiVal'Enumerator $ Enumerator tag Nothing+ Just members' -> do+ members <- mapM (\(name,expr) -> (name,) <$> (forceVal =<< eval expr envRef)) (Map.toList members')+ return $ Expr'Val $ Val'ApiVal $ ApiVal'Enumerator $ Enumerator tag (Just $ Map.fromList members)++evalIf :: (MonadIO m, RuntimeThrower m) => If m -> IORef (Env m) -> Eval m (Expr m)+evalIf If{cond, true, false} envRef = do+ envRef' <- liftIO $ newIORef =<< readIORef envRef+ v <- eval cond envRef'+ case v of+ Expr'Val (Val'Const (Const'Bool cond')) -> do+ envRef'' <- liftIO $ newIORef =<< readIORef envRef+ eval (if cond' then true else false) envRef''+ _ -> runtimeThrow RuntimeError'IncompatibleType++evalSet :: (MonadIO m, RuntimeThrower m) => Set m -> IORef (Env m) -> Eval m (Expr m)+evalSet Set{var = var@(Symbol var'), expr} envRef = do+ expr' <- eval expr envRef+ env <- liftIO $ readIORef envRef+ case Map.lookup var env of+ Nothing -> runtimeThrow $ RuntimeError'UnknownVariable var'+ Just v -> do+ liftIO $ writeIORef v expr'+ return expr'++evalGet :: (MonadIO m, RuntimeThrower m) => Get m -> IORef (Env m) -> Eval m (Expr m)+evalGet Get{path,expr} envRef = getter path =<< eval expr envRef++getter :: (MonadIO m, RuntimeThrower m) => [Text] -> Expr m -> Eval m (Expr m)+getter [] expr = return expr+getter path expr =+ case expr of+ Expr'Val val -> case val of+ Val'ApiVal apiVal -> getterApiVal path apiVal+ _ -> runtimeThrow RuntimeError'IncompatibleType+ _ -> runtimeThrow RuntimeError'IncompatibleType++getterApiVal :: (MonadIO m, RuntimeThrower m) => [Text] -> ApiVal -> Eval m (Expr m)+getterApiVal ("w":path) (ApiVal'Wrap (Wrap w)) = getter path (Expr'Val (Val'Const w))+getterApiVal (mName:path) (ApiVal'Struct Struct{m}) =+ case Map.lookup (MemberName mName) m of+ Nothing -> runtimeThrow RuntimeError'IncompatibleType+ Just member -> getter path (Expr'Val member)+getterApiVal (mName:path) (ApiVal'Enumerator Enumerator{m})+ | mName == "tag" = runtimeThrow RuntimeError'IncompatibleType+ | otherwise = case m >>= Map.lookup (MemberName mName) of+ Nothing -> runtimeThrow RuntimeError'IncompatibleType+ Just member -> getter path (Expr'Val member)+getterApiVal _ _ = runtimeThrow RuntimeError'IncompatibleType++evalDefine :: (MonadIO m, RuntimeThrower m) => Define m -> IORef (Env m) -> Eval m (Expr m)+evalDefine Define{var, expr} envRef = do+ expr' <- eval expr envRef+ env <- liftIO $ readIORef envRef+ ref <- liftIO $ newIORef expr'+ limit <- variableLimit <$> getOptions+ addVarToEnv limit envRef var ref env+ return expr'++evalLambda :: (MonadIO m, RuntimeThrower m) => Lambda m -> IORef (Env m) -> Eval m (Expr m)+evalLambda Lambda{params, expr} envRef = do+ return . Expr'Fn . Fn $ \vals -> do+ let keys = map fst params+ let args = zip keys vals+ let keysLen = length keys+ let argsLen = length args+ if keysLen /= argsLen+ then runtimeThrow $ if keysLen < argsLen+ then RuntimeError'TooManyArguments+ else RuntimeError'TooFewArguments+ else do+ args' <- liftIO $ mapM newIORef (Map.fromList args)+ limit <- variableLimit <$> getOptions+ envRef' <- addEnvToEnv limit args' envRef+ eval expr envRef'++evalList :: (MonadIO m, RuntimeThrower m) => List m -> IORef (Env m)-> Eval m (Expr m)+evalList List{list} envRef = do+ list' <- mapM (\item -> eval item envRef) list+ return . Expr'List $ List list'++evalBegin :: (MonadIO m, RuntimeThrower m) => Begin m -> IORef (Env m) -> Eval m (Expr m)+evalBegin Begin{exprs} envRef = case exprs of+ [] -> return $ Expr'Val $ Val'Const $ Const'Null+ _ -> last <$> mapM (\expr -> eval expr envRef) exprs++evalFnCall :: (MonadIO m, RuntimeThrower m) => FnCall m -> IORef (Env m) -> Eval m (Expr m)+evalFnCall FnCall{fn, args} envRef = do+ val <- eval fn envRef+ case val of+ Expr'Fn (Fn fn') -> do+ args' <- mapM (\arg -> eval arg envRef) args+ fn' args'+ Expr'Ref Ref{symbol} -> do+ env <- liftIO $ readIORef envRef+ v <- varLookup env symbol+ case v of+ Expr'Fn (Fn fn') -> do+ args' <- mapM (\arg -> eval arg envRef) args+ fn' args'+ _ -> runtimeThrow $ RuntimeError'IncompatibleType+ _ -> runtimeThrow RuntimeError'IncompatibleType++evalApiUnCall :: (MonadIO m, RuntimeThrower m) => ApiUnCall m -> IORef (Env m) -> Eval m (Expr m)+evalApiUnCall apiUnCall envRef = Expr'Val <$> case apiUnCall of+ ApiUnCall'HollowUnCall c -> evalHollowUnCall c+ ApiUnCall'WrapUnCall c -> evalWrapUnCall c envRef+ ApiUnCall'StructUnCall c -> evalStructUnCall c envRef+ ApiUnCall'EnumerationUnCall c -> evalEnumerationUnCall c envRef++evalHollowUnCall :: (MonadIO m, RuntimeThrower m) => HollowUnCall -> Eval m Val+evalHollowUnCall HollowUnCall{n} =+ Eval . ReaderT $ \cfg ->+ apiCall cfg $ ApiCall'Hollow n++evalWrapUnCall :: (MonadIO m, RuntimeThrower m) => WrapUnCall m -> IORef (Env m) -> Eval m Val+evalWrapUnCall WrapUnCall{n,w} envRef = do+ expr <- eval w envRef+ case expr of+ Expr'Val (Val'Const c) -> Eval . ReaderT $ \cfg ->+ apiCall cfg $ ApiCall'Wrap n (Wrap c)+ _ -> runtimeThrow RuntimeError'IncompatibleType++evalStructUnCall :: (MonadIO m, RuntimeThrower m) => StructUnCall m -> IORef (Env m) -> Eval m Val+evalStructUnCall StructUnCall{n,m} envRef = do+ m' <- mapM eval' m+ Eval . ReaderT $ \cfg ->+ apiCall cfg $ ApiCall'Struct n (Struct m')+ where+ eval' expr = do+ expr' <- eval expr envRef+ case expr' of+ Expr'Val v -> return v+ _ -> runtimeThrow RuntimeError'IncompatibleType++evalEnumerationUnCall :: (MonadIO m, RuntimeThrower m) => EnumerationUnCall m -> IORef (Env m) -> Eval m Val+evalEnumerationUnCall EnumerationUnCall{n,e} envRef = do+ expr <- eval e envRef+ case expr of+ Expr'Val (Val'ApiVal (ApiVal'Enumerator e')) -> Eval . ReaderT $ \cfg ->+ apiCall cfg $ ApiCall'Enumeration n e'+ _ -> runtimeThrow RuntimeError'IncompatibleType++emptyEnv :: RuntimeThrower m => IO (IORef (Env m))+emptyEnv = do+ eq <- newIORef eqExpr+ neq <- newIORef neqExpr+ concat' <- newIORef concatExpr+ add <- newIORef addExpr+ sub <- newIORef subExpr+ mul <- newIORef mulExpr+ div' <- newIORef divExpr+ newIORef $ Map.fromList+ [ ("==", eq)+ , ("/=", neq)+ , ("+", add)+ , ("-", sub)+ , ("*", mul)+ , ("/", div')+ , ("concat", concat')+ ]++numExpr :: RuntimeThrower m+ => (Scientific -> Scientific -> Scientific)+ -> (Int8 -> Int8 -> Int8)+ -> (Int16 -> Int16 -> Int16)+ -> (Int32 -> Int32 -> Int32)+ -> (Int64 -> Int64 -> Int64)+ -> (Word8 -> Word8 -> Word8)+ -> (Word16 -> Word16 -> Word16)+ -> (Word32 -> Word32 -> Word32)+ -> (Word64 -> Word64 -> Word64)+ -> Expr m+numExpr num i8 i16 i32 i64 u8 u16 u32 u64 = Expr'Fn . Fn $ \args ->+ case args of+ (_:[]) -> runtimeThrow RuntimeError'TooFewArguments+ [Expr'Val (Val'Const (Const'Number x)), Expr'Val (Val'Const (Const'Number y))] -> return $+ Expr'Val $ Val'Const $ Const'Number $ x `num` y++ [Expr'Val (Val'Prim (Prim'I8 x)), Expr'Val (Val'Prim (Prim'I8 y))] -> toExpr $ x `i8` y+ [Expr'Val (Val'Const (Const'Number x)), Expr'Val (Val'Prim (Prim'I8 y))] -> case toBoundedInteger x of+ Just x' -> toExpr $ x' `i8` y+ Nothing -> runtimeThrow RuntimeError'IncompatibleType+ [Expr'Val (Val'Prim (Prim'I8 x)), Expr'Val (Val'Const (Const'Number y))] -> case toBoundedInteger y of+ Just y' -> toExpr $ x `i8` y'+ Nothing -> runtimeThrow RuntimeError'IncompatibleType++ [Expr'Val (Val'Prim (Prim'I16 x)), Expr'Val (Val'Prim (Prim'I16 y))] -> toExpr $ x `i16` y+ [Expr'Val (Val'Const (Const'Number x)), Expr'Val (Val'Prim (Prim'I16 y))] -> case toBoundedInteger x of+ Just x' -> toExpr $ x' `i16` y+ Nothing -> runtimeThrow RuntimeError'IncompatibleType+ [Expr'Val (Val'Prim (Prim'I16 x)), Expr'Val (Val'Const (Const'Number y))] -> case toBoundedInteger y of+ Just y' -> toExpr $ x `i16` y'+ Nothing -> runtimeThrow RuntimeError'IncompatibleType++ [Expr'Val (Val'Prim (Prim'I32 x)), Expr'Val (Val'Prim (Prim'I32 y))] -> toExpr $ x `i32` y+ [Expr'Val (Val'Const (Const'Number x)), Expr'Val (Val'Prim (Prim'I32 y))] -> case toBoundedInteger x of+ Just x' -> toExpr $ x' `i32` y+ Nothing -> runtimeThrow RuntimeError'IncompatibleType+ [Expr'Val (Val'Prim (Prim'I32 x)), Expr'Val (Val'Const (Const'Number y))] -> case toBoundedInteger y of+ Just y' -> toExpr $ x `i32` y'+ Nothing -> runtimeThrow RuntimeError'IncompatibleType++ [Expr'Val (Val'Prim (Prim'I64 x)), Expr'Val (Val'Prim (Prim'I64 y))] -> toExpr $ x `i64` y+ [Expr'Val (Val'Const (Const'Number x)), Expr'Val (Val'Prim (Prim'I64 y))] -> case toBoundedInteger x of+ Just x' -> toExpr $ x' `i64` y+ Nothing -> runtimeThrow RuntimeError'IncompatibleType+ [Expr'Val (Val'Prim (Prim'I64 x)), Expr'Val (Val'Const (Const'Number y))] -> case toBoundedInteger y of+ Just y' -> toExpr $ x `i64` y'+ Nothing -> runtimeThrow RuntimeError'IncompatibleType++ [Expr'Val (Val'Prim (Prim'U8 x)), Expr'Val (Val'Prim (Prim'U8 y))] -> toExpr $ x `u8` y+ [Expr'Val (Val'Const (Const'Number x)), Expr'Val (Val'Prim (Prim'U8 y))] -> case toBoundedInteger x of+ Just x' -> toExpr $ x' `u8` y+ Nothing -> runtimeThrow RuntimeError'IncompatibleType+ [Expr'Val (Val'Prim (Prim'U8 x)), Expr'Val (Val'Const (Const'Number y))] -> case toBoundedInteger y of+ Just y' -> toExpr $ x `u8` y'+ Nothing -> runtimeThrow RuntimeError'IncompatibleType++ [Expr'Val (Val'Prim (Prim'U16 x)), Expr'Val (Val'Prim (Prim'U16 y))] -> toExpr $ x `u16` y+ [Expr'Val (Val'Const (Const'Number x)), Expr'Val (Val'Prim (Prim'U16 y))] -> case toBoundedInteger x of+ Just x' -> toExpr $ x' `u16` y+ Nothing -> runtimeThrow RuntimeError'IncompatibleType+ [Expr'Val (Val'Prim (Prim'U16 x)), Expr'Val (Val'Const (Const'Number y))] -> case toBoundedInteger y of+ Just y' -> toExpr $ x `u16` y'+ Nothing -> runtimeThrow RuntimeError'IncompatibleType++ [Expr'Val (Val'Prim (Prim'U32 x)), Expr'Val (Val'Prim (Prim'U32 y))] -> toExpr $ x `u32` y+ [Expr'Val (Val'Const (Const'Number x)), Expr'Val (Val'Prim (Prim'U32 y))] -> case toBoundedInteger x of+ Just x' -> toExpr $ x' `u32` y+ Nothing -> runtimeThrow RuntimeError'IncompatibleType+ [Expr'Val (Val'Prim (Prim'U32 x)), Expr'Val (Val'Const (Const'Number y))] -> case toBoundedInteger y of+ Just y' -> toExpr $ x `u32` y'+ Nothing -> runtimeThrow RuntimeError'IncompatibleType++ [Expr'Val (Val'Prim (Prim'U64 x)), Expr'Val (Val'Prim (Prim'U64 y))] -> toExpr $ x `u64` y+ [Expr'Val (Val'Const (Const'Number x)), Expr'Val (Val'Prim (Prim'U64 y))] -> case toBoundedInteger x of+ Just x' -> toExpr $ x' `u64` y+ Nothing -> runtimeThrow RuntimeError'IncompatibleType+ [Expr'Val (Val'Prim (Prim'U64 x)), Expr'Val (Val'Const (Const'Number y))] -> case toBoundedInteger y of+ Just y' -> toExpr $ x `u64` y'+ Nothing -> runtimeThrow RuntimeError'IncompatibleType++ (_:_:[]) -> runtimeThrow RuntimeError'IncompatibleType+ _ -> runtimeThrow RuntimeError'TooManyArguments+ where+ toExpr v = return $ Expr'Val (toVal v)++boolExpr :: RuntimeThrower m+ => (Scientific -> Scientific -> Bool)+ -> (Int8 -> Int8 -> Bool)+ -> (Int16 -> Int16 -> Bool)+ -> (Int32 -> Int32 -> Bool)+ -> (Int64 -> Int64 -> Bool)+ -> (Word8 -> Word8 -> Bool)+ -> (Word16 -> Word16 -> Bool)+ -> (Word32 -> Word32 -> Bool)+ -> (Word64 -> Word64 -> Bool)+ -> Expr m+boolExpr num i8 i16 i32 i64 u8 u16 u32 u64 = Expr'Fn . Fn $ \args ->+ case args of+ (_:[]) -> runtimeThrow RuntimeError'TooManyArguments+ [Expr'Val (Val'Const (Const'Number x)), Expr'Val (Val'Const (Const'Number y))] -> toExpr $ x `num` y++ [Expr'Val (Val'Prim (Prim'I8 x)), Expr'Val (Val'Prim (Prim'I8 y))] -> toExpr $ x `i8` y+ [Expr'Val (Val'Const (Const'Number x)), Expr'Val (Val'Prim (Prim'I8 y))] -> case toBoundedInteger x of+ Just x' -> toExpr $ x' `i8` y+ Nothing -> runtimeThrow RuntimeError'IncompatibleType+ [Expr'Val (Val'Prim (Prim'I8 x)), Expr'Val (Val'Const (Const'Number y))] -> case toBoundedInteger y of+ Just y' -> toExpr $ x `i8` y'+ Nothing -> runtimeThrow RuntimeError'IncompatibleType++ [Expr'Val (Val'Prim (Prim'I16 x)), Expr'Val (Val'Prim (Prim'I16 y))] -> toExpr $ x `i16` y+ [Expr'Val (Val'Const (Const'Number x)), Expr'Val (Val'Prim (Prim'I16 y))] -> case toBoundedInteger x of+ Just x' -> toExpr $ x' `i16` y+ Nothing -> runtimeThrow RuntimeError'IncompatibleType+ [Expr'Val (Val'Prim (Prim'I16 x)), Expr'Val (Val'Const (Const'Number y))] -> case toBoundedInteger y of+ Just y' -> toExpr $ x `i16` y'+ Nothing -> runtimeThrow RuntimeError'IncompatibleType++ [Expr'Val (Val'Prim (Prim'I32 x)), Expr'Val (Val'Prim (Prim'I32 y))] -> toExpr $ x `i32` y+ [Expr'Val (Val'Const (Const'Number x)), Expr'Val (Val'Prim (Prim'I32 y))] -> case toBoundedInteger x of+ Just x' -> toExpr $ x' `i32` y+ Nothing -> runtimeThrow RuntimeError'IncompatibleType+ [Expr'Val (Val'Prim (Prim'I32 x)), Expr'Val (Val'Const (Const'Number y))] -> case toBoundedInteger y of+ Just y' -> toExpr $ x `i32` y'+ Nothing -> runtimeThrow RuntimeError'IncompatibleType++ [Expr'Val (Val'Prim (Prim'I64 x)), Expr'Val (Val'Prim (Prim'I64 y))] -> toExpr $ x `i64` y+ [Expr'Val (Val'Const (Const'Number x)), Expr'Val (Val'Prim (Prim'I64 y))] -> case toBoundedInteger x of+ Just x' -> toExpr $ x' `i64` y+ Nothing -> runtimeThrow RuntimeError'IncompatibleType+ [Expr'Val (Val'Prim (Prim'I64 x)), Expr'Val (Val'Const (Const'Number y))] -> case toBoundedInteger y of+ Just y' -> toExpr $ x `i64` y'+ Nothing -> runtimeThrow RuntimeError'IncompatibleType++ [Expr'Val (Val'Prim (Prim'U8 x)), Expr'Val (Val'Prim (Prim'U8 y))] -> toExpr $ x `u8` y+ [Expr'Val (Val'Const (Const'Number x)), Expr'Val (Val'Prim (Prim'U8 y))] -> case toBoundedInteger x of+ Just x' -> toExpr $ x' `u8` y+ Nothing -> runtimeThrow RuntimeError'IncompatibleType+ [Expr'Val (Val'Prim (Prim'U8 x)), Expr'Val (Val'Const (Const'Number y))] -> case toBoundedInteger y of+ Just y' -> toExpr $ x `u8` y'+ Nothing -> runtimeThrow RuntimeError'IncompatibleType++ [Expr'Val (Val'Prim (Prim'U16 x)), Expr'Val (Val'Prim (Prim'U16 y))] -> toExpr $ x `u16` y+ [Expr'Val (Val'Const (Const'Number x)), Expr'Val (Val'Prim (Prim'U16 y))] -> case toBoundedInteger x of+ Just x' -> toExpr $ x' `u16` y+ Nothing -> runtimeThrow RuntimeError'IncompatibleType+ [Expr'Val (Val'Prim (Prim'U16 x)), Expr'Val (Val'Const (Const'Number y))] -> case toBoundedInteger y of+ Just y' -> toExpr $ x `u16` y'+ Nothing -> runtimeThrow RuntimeError'IncompatibleType++ [Expr'Val (Val'Prim (Prim'U32 x)), Expr'Val (Val'Prim (Prim'U32 y))] -> toExpr $ x `u32` y+ [Expr'Val (Val'Const (Const'Number x)), Expr'Val (Val'Prim (Prim'U32 y))] -> case toBoundedInteger x of+ Just x' -> toExpr $ x' `u32` y+ Nothing -> runtimeThrow RuntimeError'IncompatibleType+ [Expr'Val (Val'Prim (Prim'U32 x)), Expr'Val (Val'Const (Const'Number y))] -> case toBoundedInteger y of+ Just y' -> toExpr $ x `u32` y'+ Nothing -> runtimeThrow RuntimeError'IncompatibleType++ [Expr'Val (Val'Prim (Prim'U64 x)), Expr'Val (Val'Prim (Prim'U64 y))] -> toExpr $ x `u64` y+ [Expr'Val (Val'Const (Const'Number x)), Expr'Val (Val'Prim (Prim'U64 y))] -> case toBoundedInteger x of+ Just x' -> toExpr $ x' `u64` y+ Nothing -> runtimeThrow RuntimeError'IncompatibleType+ [Expr'Val (Val'Prim (Prim'U64 x)), Expr'Val (Val'Const (Const'Number y))] -> case toBoundedInteger y of+ Just y' -> toExpr $ x `u64` y'+ Nothing -> runtimeThrow RuntimeError'IncompatibleType++ (_:_:[]) -> runtimeThrow RuntimeError'IncompatibleType+ _ -> runtimeThrow RuntimeError'TooFewArguments+ where+ toExpr v = return $ Expr'Val (toVal v)++eqExpr :: RuntimeThrower m => Expr m+eqExpr = boolExpr (==) (==) (==) (==) (==) (==) (==) (==) (==)++neqExpr :: RuntimeThrower m => Expr m+neqExpr = boolExpr (/=) (/=) (/=) (/=) (/=) (/=) (/=) (/=) (/=)++concatExpr :: RuntimeThrower m => Expr m+concatExpr = Expr'Fn . Fn $ \args ->+ case args of+ (_:[]) -> runtimeThrow RuntimeError'TooFewArguments+ [x, y] -> case (x,y) of+ (Expr'Val (Val'Const (Const'String x')), Expr'Val (Val'Const (Const'String y'))) -> return $+ Expr'Val . Val'Const . Const'String $ x' `mappend` y'+ _ -> runtimeThrow RuntimeError'IncompatibleType+ _ -> runtimeThrow RuntimeError'TooManyArguments++addExpr :: RuntimeThrower m => Expr m+addExpr = numExpr (+) (+) (+) (+) (+) (+) (+) (+) (+)++subExpr :: RuntimeThrower m => Expr m+subExpr = numExpr (-) (-) (-) (-) (-) (-) (-) (-) (-)++mulExpr :: RuntimeThrower m => Expr m+mulExpr = numExpr (*) (*) (*) (*) (*) (*) (*) (*) (*)++divExpr :: RuntimeThrower m => Expr m+divExpr = numExpr (/) div div div div div div div div++--++data ApiParser api = ApiParser+ { hollow :: Map TypeName api+ , struct :: Map TypeName (Val -> Maybe api)+ , enumeration :: Map TypeName (Val -> Maybe api)+ , wrap :: Map TypeName (Val -> Maybe api)+ }++parseApiCall :: ApiParser api -> ApiCall -> Maybe api+parseApiCall ApiParser{hollow, struct, enumeration, wrap} = \case+ ApiCall'Hollow n -> Map.lookup n hollow+ ApiCall'Struct n s -> join $ ($ Val'ApiVal (ApiVal'Struct s)) <$> Map.lookup n struct+ ApiCall'Enumeration n e -> join $ ($ Val'ApiVal (ApiVal'Enumerator e)) <$> Map.lookup n enumeration+ ApiCall'Wrap n w -> join $ ($ Val'ApiVal (ApiVal'Wrap w)) <$> Map.lookup n wrap
+ library/Colorless/Runtime/Types.hs view
@@ -0,0 +1,116 @@+module Colorless.Runtime.Types+ ( Symbol(..)+ , Type(..)+ , TypeName(..)+ , MemberName(..)+ , EnumeratorName(..)+ , EnumeratorType(..)+ , EnumerationType(..)+ , StructType(..)+ , HollowType(..)+ , Prim(..)+ , Const(..)+ ) where++import qualified Data.HashMap.Lazy as HML+import Control.Monad (mzero)+import Data.Aeson (FromJSON(..), FromJSONKey, ToJSONKey, Value(..), (.:), ToJSON(..))+import Data.Text (Text)+import Data.Map (Map)+import Data.Int+import Data.Word+import Data.Scientific+import Data.String (IsString)++newtype Symbol = Symbol Text+ deriving (Show, Eq, Ord, FromJSON, IsString)++data Type = Type+ { n :: TypeName+ , p :: Maybe Type+ } deriving (Show, Eq)++instance FromJSON Type where+ parseJSON = \case+ String s -> pure $ Type (TypeName s) Nothing+ Object o -> do+ n <- o .: "n"+ case HML.lookup "p" o of+ Nothing -> pure $ Type n Nothing+ Just p -> Type n <$> fmap Just (parseJSON p)+ _ -> mzero++newtype TypeName = TypeName Text+ deriving (Show, Eq, Ord, FromJSON, IsString)++newtype EnumeratorName = EnumeratorName Text+ deriving (Show, Eq, Ord, FromJSON, ToJSON, IsString)++newtype MemberName = MemberName Text+ deriving (Show, Eq, Ord, FromJSON, ToJSON, ToJSONKey, FromJSONKey, IsString)++data EnumeratorType = EnumeratorType+ { m :: Maybe (Map MemberName Type)+ } deriving (Show, Eq)++data EnumerationType = EnumerationType+ { e :: Map EnumeratorName EnumeratorType+ , o :: Type+ } deriving (Show, Eq)++data StructType = StructType+ { m :: Map MemberName Type+ , o :: Type+ } deriving (Show, Eq)++data HollowType = HollowType+ { o :: Type+ } deriving (Show, Eq)++data Prim+ = Prim'Bool Bool+ | Prim'I8 Int8+ | Prim'I16 Int16+ | Prim'I32 Int32+ | Prim'I64 Int64+ | Prim'U8 Word8+ | Prim'U16 Word16+ | Prim'U32 Word32+ | Prim'U64 Word64+ | Prim'String Text+ deriving (Show, Eq)++instance ToJSON Prim where+ toJSON = \case+ Prim'Bool b -> toJSON b+ Prim'I8 i -> toJSON i+ Prim'I16 i -> toJSON i+ Prim'I32 i -> toJSON i+ Prim'I64 i -> toJSON i+ Prim'U8 u -> toJSON u+ Prim'U16 u -> toJSON u+ Prim'U32 u -> toJSON u+ Prim'U64 u -> toJSON u+ Prim'String s -> toJSON s++data Const+ = Const'Null+ | Const'Bool Bool+ | Const'String Text+ | Const'Number Scientific+ deriving (Show, Eq)++instance ToJSON Const where+ toJSON = \case+ Const'Null -> Null+ Const'Bool b -> Bool b+ Const'String s -> String s+ Const'Number n -> Number n++instance FromJSON Const where+ parseJSON = \case+ Null -> pure $ Const'Null+ Bool b -> pure $ Const'Bool b+ String s -> pure $ Const'String s+ Number n -> pure $ Const'Number n+ _ -> mzero
+ library/Colorless/Runtime/Val.hs view
@@ -0,0 +1,284 @@+{-# LANGUAGE DeriveGeneric #-}+module Colorless.Runtime.Val+ ( Val(..)+ , ApiVal(..)+ , Wrap(..)+ , Struct(..)+ , Enumerator(..)+ --+ , FromVal(..)+ , ToVal(..)+ , getMember+ , fromValFromJson+ , combineObjects+ ) where++import qualified Data.HashMap.Lazy as HML+import qualified Data.Map as Map+import qualified Data.Vector as V+import Control.Monad (mzero)+import Control.Applicative ((<|>))+import Data.Aeson+import Data.Aeson.Types (parseMaybe)+import Data.Map (Map)+import Data.Text (Text)+import Data.Int+import Data.Word+import Data.Scientific+import GHC.Generics (Generic)++import Colorless.Runtime.Types++data Val+ = Val'Const Const+ | Val'Prim Prim+ | Val'ApiVal ApiVal+ | Val'List [Val]+ deriving (Show, Eq)++instance ToJSON Val where+ toJSON = \case+ Val'Const c -> toJSON c+ Val'ApiVal v -> toJSON v+ Val'List l -> toJSON l+ Val'Prim p -> toJSON p++instance FromJSON Val where+ parseJSON = \case+ Null -> return $ Val'Const Const'Null+ Number n -> return $ Val'Const $ Const'Number n+ String s -> return $ Val'Const $ Const'String s+ Bool b -> return $ Val'Const $ Const'Bool b+ Array arr -> Val'List <$> (mapM parseJSON $ V.toList arr)+ v@Object{} -> Val'ApiVal <$> parseJSON v++data ApiVal+ = ApiVal'Wrap Wrap+ | ApiVal'Struct Struct+ | ApiVal'Enumerator Enumerator+ deriving (Show, Eq)++instance ToJSON ApiVal where+ toJSON = \case+ ApiVal'Wrap w -> toJSON w+ ApiVal'Struct s -> toJSON s+ ApiVal'Enumerator e -> toJSON e++instance FromJSON ApiVal where+ parseJSON v =+ (ApiVal'Wrap <$> parseJSON v) <|>+ (ApiVal'Enumerator <$> parseJSON v) <|>+ (ApiVal'Struct <$> parseJSON v)++data Wrap = Wrap+ { w :: Const+ } deriving (Show, Eq, Generic)++instance ToJSON Wrap+instance FromJSON Wrap++data Struct = Struct+ { m :: Map MemberName Val+ } deriving (Show, Eq, Generic)++instance ToJSON Struct where+ toJSON Struct{m} = toJSON m++instance FromJSON Struct where+ parseJSON v = Struct <$> parseJSON v++data Enumerator = Enumerator+ { tag :: EnumeratorName+ , m :: Maybe (Map MemberName Val)+ } deriving (Show, Eq, Generic)++instance ToJSON Enumerator where+ toJSON Enumerator{tag,m} = object $ [ "tag" .= tag ] ++ case m of+ Nothing -> []+ Just m' -> concatMap (\(MemberName k,v) -> [ k .= v ]) (Map.toList m')++instance FromJSON Enumerator where+ parseJSON (Object o) = do+ tag <- o .: "tag"+ let tagless = HML.delete "tag" o+ if HML.size o == 1+ then pure $ Enumerator tag Nothing+ else Enumerator tag <$> (Just <$> parseJSON (Object tagless))+ parseJSON _ = mzero++--++class ToVal a where+ toVal :: a -> Val++instance ToVal () where+ toVal () = Val'Const Const'Null++instance ToVal Bool where+ toVal b = Val'Const $ Const'Bool b++instance ToVal Text where+ toVal s = Val'Const $ Const'String s++intToVal :: Integral a => a -> Val+intToVal n = Val'Const $ Const'Number (fromInteger $ toInteger n)++instance ToVal Int where+ toVal = intToVal++instance ToVal Int8 where+ toVal i = Val'Prim $ Prim'I8 i++instance ToVal Int16 where+ toVal i = Val'Prim $ Prim'I16 i++instance ToVal Int32 where+ toVal i = Val'Prim $ Prim'I32 i++instance ToVal Int64 where+ toVal i = Val'Prim $ Prim'I64 i++instance ToVal Word where+ toVal = intToVal++instance ToVal Word8 where+ toVal u = Val'Prim $ Prim'U8 u++instance ToVal Word16 where+ toVal u = Val'Prim $ Prim'U16 u++instance ToVal Word32 where+ toVal u = Val'Prim $ Prim'U32 u++instance ToVal Word64 where+ toVal u = Val'Prim $ Prim'U64 u++instance ToVal Float where+ toVal f = Val'Const $ Const'Number $ fromFloatDigits f++instance ToVal Double where+ toVal d = Val'Const $ Const'Number $ fromFloatDigits d++instance ToVal a => ToVal (Maybe a) where+ toVal m = Val'ApiVal $ ApiVal'Enumerator $ case m of+ Nothing -> Enumerator "None" Nothing+ Just s -> Enumerator "Some" (Just $ Map.singleton "some" (toVal s))++instance (ToVal a, ToVal b) => ToVal (Either a b) where+ toVal m = Val'ApiVal $ ApiVal'Enumerator $ case m of+ Left l -> Enumerator "Left" (Just $ Map.singleton "left" (toVal l))+ Right r -> Enumerator "Right" (Just $ Map.singleton "right" (toVal r))++instance ToVal a => ToVal [a] where+ toVal list = Val'List $ map toVal list++--++class FromVal a where+ fromVal :: Val -> Maybe a++instance FromVal () where+ fromVal (Val'Const Const'Null) = Just ()+ fromVal _ = Nothing++instance FromVal Bool where+ fromVal (Val'Const (Const'Bool b)) = Just b+ fromVal _ = Nothing++instance FromVal Text where+ fromVal (Val'Const (Const'String s)) = Just s+ fromVal _ = Nothing++intFromVal :: (Bounded i, Integral i) => Val -> Maybe i+intFromVal (Val'Const (Const'Number n)) = toBoundedInteger n+intFromVal _ = Nothing++instance FromVal Int where+ fromVal = intFromVal++instance FromVal Int8 where+ fromVal = \case+ Val'Const (Const'Number n) -> toBoundedInteger n+ Val'Prim (Prim'I8 i) -> Just i+ _ -> Nothing++instance FromVal Int16 where+ fromVal = \case+ Val'Const (Const'Number n) -> toBoundedInteger n+ Val'Prim (Prim'I16 i) -> Just i+ _ -> Nothing++instance FromVal Int32 where+ fromVal = \case+ Val'Const (Const'Number n) -> toBoundedInteger n+ Val'Prim (Prim'I32 i) -> Just i+ _ -> Nothing++instance FromVal Int64 where+ fromVal = \case+ Val'Const (Const'Number n) -> toBoundedInteger n+ Val'Prim (Prim'I64 i) -> Just i+ _ -> Nothing++instance FromVal Word where+ fromVal = intFromVal++instance FromVal Word8 where+ fromVal = \case+ Val'Const (Const'Number n) -> toBoundedInteger n+ Val'Prim (Prim'U8 u) -> Just u+ _ -> Nothing++instance FromVal Word16 where+ fromVal = \case+ Val'Const (Const'Number n) -> toBoundedInteger n+ Val'Prim (Prim'U16 u) -> Just u+ _ -> Nothing++instance FromVal Word32 where+ fromVal = \case+ Val'Const (Const'Number n) -> toBoundedInteger n+ Val'Prim (Prim'U32 u) -> Just u+ _ -> Nothing++instance FromVal Word64 where+ fromVal = \case+ Val'Const (Const'Number n) -> toBoundedInteger n+ Val'Prim (Prim'U64 u) -> Just u+ _ -> Nothing++instance FromVal Float where+ fromVal (Val'Const (Const'Number n)) = Just $ toRealFloat n+ fromVal _ = Nothing++instance FromVal Double where+ fromVal (Val'Const (Const'Number n)) = Just $ toRealFloat n+ fromVal _ = Nothing++instance FromVal a => FromVal (Maybe a) where+ fromVal (Val'ApiVal (ApiVal'Enumerator (Enumerator tag m))) = case (tag,m) of+ ("Some",Just m') -> fromVal <$> Map.lookup "some" m'+ ("None",Nothing) -> Just Nothing+ _ -> Nothing+ fromVal _ = Nothing++instance (FromVal a, FromVal b) => FromVal (Either a b) where+ fromVal (Val'ApiVal (ApiVal'Enumerator (Enumerator tag m))) = case (tag,m) of+ ("Left",Just m') -> Map.lookup "left" m' >>= \l -> Left <$> fromVal l+ ("Right",Just m') -> Map.lookup "right" m' >>= \r -> Right <$> fromVal r+ _ -> Nothing+ fromVal _ = Nothing++instance FromVal a => FromVal [a] where+ fromVal (Val'List list) = mapM fromVal list+ fromVal _ = Nothing++getMember :: FromVal a => Map MemberName Val -> MemberName -> Maybe a+getMember m n = fromVal =<< Map.lookup n m++fromValFromJson :: (FromVal b) => Value -> Maybe b+fromValFromJson x = fromVal =<< parseMaybe parseJSON x++combineObjects :: Value -> Value -> Value+combineObjects (Object x) (Object y) = Object $ HML.union x y+combineObjects _ _ = error "not objects"
+ library/Colorless/Types.hs view
@@ -0,0 +1,123 @@+{-# LANGUAGE DeriveGeneric #-}+module Colorless.Types+ ( Version(..)+ , Major(..)+ , Minor(..)+ , Request(..)+ , Response(..)+ , ResponseError(..)+ , RuntimeError(..)+ , RuntimeThrower(..)+ , Options(..)+ ) where++import Control.Monad (mzero)+import Data.Aeson+import Data.Text (Text)+import GHC.Generics++newtype Major = Major Int+ deriving (Show, Eq, Generic, Num, Ord, Real, Integral, Enum)++instance FromJSON Major+instance ToJSON Major++newtype Minor = Minor Int+ deriving (Show, Eq, Generic, Num, Ord, Real, Integral, Enum)++instance FromJSON Minor+instance ToJSON Minor++data Version = Version+ { major :: Major+ , minor :: Minor+ } deriving (Show, Eq, Generic)++instance ToJSON Version+instance FromJSON Version++data Request = Request+ { meta :: Value+ , calls :: [Value]+ } deriving (Show, Eq)++instance FromJSON Request where+ parseJSON (Object o) = Request+ <$> o .: "meta"+ <*> o .: "calls"+ parseJSON _ = mzero++data RuntimeError+ = RuntimeError'UnparsableFormat+ | RuntimeError'UnrecognizedCall+ | RuntimeError'VariableLimit+ | RuntimeError'UnknownVariable Text+ | RuntimeError'IncompatibleType+ | RuntimeError'TooFewArguments+ | RuntimeError'TooManyArguments+ | RuntimeError'NoApiVersion+ | RuntimeError'NoColorlessVersion+ | RuntimeError'ApiMajorVersionTooLow+ | RuntimeError'ApiMajorVersionTooHigh+ | RuntimeError'ApiMinorVersionTooHigh+ | RuntimeError'ColorlessMajorVersionTooLow+ | RuntimeError'ColorlessMajorVersionTooHigh+ | RuntimeError'ColorlessMinorVersionTooHigh+ | RuntimeError'UnparsableMeta+ | RuntimeError'UnparsableCalls+ | RuntimeError'NoImplementation+ | RuntimeError'NotMember+ deriving (Show, Eq)++instance ToJSON RuntimeError where+ toJSON = \case+ RuntimeError'UnparsableFormat -> e "UnparsableFormat"+ RuntimeError'UnrecognizedCall -> object [ "tag" .= String "UnrecognizedCall" ]+ RuntimeError'VariableLimit -> e "VariableLimit"+ RuntimeError'UnknownVariable m -> object [ "tag" .= String "UnknownVariable", "name" .= m ]+ RuntimeError'IncompatibleType -> e "IncompatibleType"+ RuntimeError'TooFewArguments -> e "TooFewArguments"+ RuntimeError'TooManyArguments -> e "TooManyArguments"+ RuntimeError'NoApiVersion -> e "NoApiVersion"+ RuntimeError'NoColorlessVersion -> e "NoColorlessVersion"+ RuntimeError'ApiMajorVersionTooHigh -> e "ApiMajorVersionTooHigh"+ RuntimeError'ApiMajorVersionTooLow -> e "ApiMajorVersionTooLow"+ RuntimeError'ApiMinorVersionTooHigh -> e "ApiMinorVersionTooHigh"+ RuntimeError'ColorlessMajorVersionTooHigh -> e "ColorlessMajorVersionTooHigh"+ RuntimeError'ColorlessMajorVersionTooLow -> e "ColorlessMajorVersionTooLow"+ RuntimeError'ColorlessMinorVersionTooHigh -> e "ColorlessMinorVersionTooHigh"+ RuntimeError'UnparsableMeta -> e "UnparsableMeta"+ RuntimeError'UnparsableCalls -> e "UnparsableCalls"+ RuntimeError'NoImplementation -> e "NoImplementation"+ RuntimeError'NotMember -> e "NotMember"+ where+ e s = object [ "tag" .= String s ]++data ResponseError+ = ResponseError'Service Value+ | ResponseError'Runtime RuntimeError+ deriving (Eq, Show)++instance ToJSON ResponseError where+ toJSON = \case+ ResponseError'Service m -> object [ "tag" .= String "Service", "service" .= m ]+ ResponseError'Runtime m -> object [ "tag" .= String "Runtime", "runtime" .= m ]++data Response+ = Response'Error ResponseError+ | Response'Success Value+ deriving (Show, Eq)++instance ToJSON Response where+ toJSON (Response'Error m) = object [ "tag" .= String "Error", "error" .= m ]+ toJSON (Response'Success m) = object [ "tag" .= String "Success", "success" .= m ]++class Monad m => RuntimeThrower m where+ runtimeThrow :: RuntimeError -> m a++instance RuntimeThrower IO where+ runtimeThrow err = error $ "Runtime error - " ++ show err++data Options = Options+ { variableLimit :: Maybe Int+ } deriving (Show, Eq)
+ package.yaml view
@@ -0,0 +1,64 @@+name: colorless+version: '0.0.1'+category: Web+synopsis: Colorless+description: Colorless+maintainer: Joe Vargas+license: BSD3+extra-source-files:+- package.yaml+- README.md+ghc-options: -Wall+default-extensions:+- ConstraintKinds+- DeriveGeneric+- DuplicateRecordFields+- FlexibleContexts+- FlexibleInstances+- GADTs+- GeneralizedNewtypeDeriving+- KindSignatures+- LambdaCase+- MultiParamTypeClasses+- NamedFieldPuns+- OverloadedStrings+- RankNTypes+- ScopedTypeVariables+- TupleSections+- TypeApplications+- TypeOperators+library:+ dependencies:+ - aeson+ - base >= 4.7 && < 5+ - bytestring+ - containers+ - errors+ - exceptions+ - lifted-async+ - monad-control+ - monad-logger+ - mtl+ - random+ - scientific+ - text+ - unordered-containers+ - vector+ source-dirs: library+tests:+ colorless-suite:+ dependencies:+ - aeson+ - base >= 4.7 && < 5+ - colorless+ - containers+ - hspec+ - scientific+ - text+ - vector+ ghc-options:+ - -rtsopts+ - -threaded+ - -with-rtsopts=-N+ main: Spec.hs+ source-dirs: tests
− src/Colorless/CodeGen/Types.hs
@@ -1,155 +0,0 @@-module Colorless.CodeGen.Types- (- ) where--import Pregame--newtype FuncName = FuncName Text deriving (Show, Eq, Ord, IsString)-newtype ArgName = ArgName Text deriving (Show, Eq, Ord, IsString)-newtype Tag = Tag Text deriving (Show, Eq, Ord, IsString)-newtype SumName = SumName Text deriving (Show, Eq, Ord, IsString)-newtype SubtypeName = SubtypeName Text deriving (Show, Eq, Ord, IsString)-newtype FieldName = FieldName Text deriving (Show, Eq, Ord, IsString)-newtype OpaqueName = OpaqueName Text deriving (Show, Eq, Ord, IsString)-newtype TypeParamName = TypeParamName Text deriving (Show, Eq, Ord, IsString)-newtype Negative = Negative Integer deriving (Show, Eq, Num)-newtype Positive = Positive Integer deriving (Show, Eq, Num)-newtype Natural = Natural Integer deriving (Show, Eq, Num)-newtype Rational = Rational (Integer, Integer) deriving (Show, Eq)-newtype Version = Version (Integer, Integer, Integer) deriving (Show, Eq, Ord)-newtype HttpDirectory = HttpDirectory [Text] deriving (Show, Eq, Monoid)-newtype HttpPort = HttpPort Int deriving (Show, Eq)-newtype SpecName = SpecName Text deriving (Show, Eq, IsString)-newtype DomainName = DomainName Text deriving (Show, Eq, IsString)--data PrimType- = PrimTypeUnit- | PrimTypeU8- | PrimTypeU16- | PrimTypeU32- | PrimTypeU64- | PrimTypeI8- | PrimTypeI16- | PrimTypeI32- | PrimTypeI64- | PrimTypeF32- | PrimTypeF64- | PrimTypeBool- | PrimTypeChar- | PrimTypeStr- | PrimTypeInt- | PrimTypeNeg- | PrimTypePos- | PrimTypeNat- | PrimTypeRat- deriving (Show, Eq)--data PrimRef- = PrimRefUnit- | PrimRefU8 Word8- | PrimRefU16 Word16- | PrimRefU32 Word32- | PrimRefU64 Word64- | PrimRefI8 Int8- | PrimRefI16 Int16- | PrimRefI32 Int32- | PrimRefI64 Int64- | PrimRefF32 Float- | PrimRefF64 Double- | PrimRefBool Bool- | PrimRefChar Char- | PrimRefStr Text- | PrimRefInt Integer- | PrimRefNeg Negative- | PrimRefPos Positive- | PrimRefNat Natural- | PrimRefRat Rational- deriving (Show, Eq)--data Type- = TypePrim PrimType- | TypeOpaque OpaqueName- deriving (Show, Eq)--data TypeRef- = TypeRefPrimType PrimType- | TypeRefOpaque OpaqueRef- deriving (Show, Eq)--data TypeParamRef- = TypeParamRefPrimRef PrimRef- | TypeParamRefPrimType PrimType- | TypeParamRefOpaqueRef OpaqueRef- deriving (Show, Eq)--data TypeParam = TypeParam- { _name :: TypeParamName- , _ref :: TypeParamRef- } deriving (Show, Eq)--data OpaqueRef = OpaqueRef- { _name :: OpaqueName- , _params :: [TypeParamRef]- } deriving (Show, Eq)--data FuncDef = FuncDef- { _args :: [(ArgName, TypeRef)]- , _tags :: Set Tag- } deriving (Show, Eq)--data SumDef = SumDef- { _params :: [TypeParamRef]- , _subtypes :: Map SubtypeName [TypeRef]- , _tags :: Set Tag- } deriving (Show, Eq)--data ProductDef = ProductDef- { _params :: [TypeParamRef]- , _fields :: Map FieldName TypeRef- , _tags :: Set Tag- } deriving (Show, Eq)--data OpaqueDef- = OpaqueDefSum SumDef- | OpaqueDefProduct ProductDef- deriving (Show, Eq)--data ServiceDef = ServiceDef- { _opaques :: Map OpaqueName OpaqueDef- , _funcs :: Map FuncName FuncDef- , _tags :: Set Tag- } deriving (Show, Eq)--data HttpFormat- = HttpFormatJson- deriving (Show, Eq)--data HttpMeta- = HttpMetaHeader- deriving (Show, Eq)--data HttpImpl = HttpImpl- { _directory :: HttpDirectory- , _port :: HttpPort- , _meta :: HttpMeta- , _format :: HttpFormat- } deriving (Show, Eq)--data Impl- = ImplHttp HttpImpl- deriving (Show, Eq)--data Domain = Domain- { _name :: DomainName- , _service :: ServiceDef- , _impl :: Impl- } deriving (Show, Eq)--data SpecDef = SpecDef- { _name :: SpecName- , _domains :: [Domain]- } deriving (Show, Eq)--data Specs = Specs- { _defs :: Map Version SpecDef- } deriving (Show, Eq)
+ tests/AstSpec.hs view
@@ -0,0 +1,124 @@+module AstSpec (spec) where++import qualified Data.Vector as V+import qualified Data.Map as Map+import Test.Hspec+import Data.Aeson+import Data.Aeson.Types++import Colorless.Runtime.Types+import Colorless.Runtime.Ast++parseAst :: Value -> Maybe Ast+parseAst = parseMaybe parseJSON++spec :: Spec+spec = do+ describe "parse basic types" $ do+ it "TypeName" $ shouldBe+ (parseMaybe parseJSON (String "MyName"))+ (Just $ TypeName "MyName")++ it "MemberName" $ shouldBe+ (parseMaybe parseJSON (String "MyName"))+ (Just $ MemberName "MyName")++ context "Type" $ do+ it "string only" $ shouldBe+ (parseMaybe parseJSON (String "MyType"))+ (Just $ Type "MyType" Nothing)++ it "object with no param" $ shouldBe+ (parseMaybe parseJSON $ object ["n" .= String "MyType"])+ (Just $ Type "MyType" Nothing)++ it "object with param" $ shouldBe+ (parseMaybe parseJSON $ object ["n" .= String "MyType", "p" .= String "MyParam"])+ (Just $ Type "MyType" (Just $ Type "MyParam" Nothing))++ describe "parse Ast" $ do+ it "Ref" $ shouldBe+ (parseAst $ object ["@" .= String "myRef"])+ (Just $ Ast'Ref $ Ref "myRef")++ context "If" $ do+ it "simple" $ shouldBe+ (parseAst $ Array $ V.fromList ["if", Bool True, "true", "false"])+ (Just $ Ast'If $ If+ (Ast'Const $ Const'Bool True)+ (Ast'Const $ Const'String "true")+ (Ast'Const $ Const'String "false"))++ context "Set" $ do+ it "simple" $ shouldBe+ (parseAst $ Array $ V.fromList ["set", "myVar", Bool False])+ (Just $ Ast'Set $ Set "myVar" (Ast'Const $ Const'Bool False))++ context "Get" $ do+ it "simple yet not semantically correct" $ shouldBe+ (parseAst $ Array $ V.fromList ["get", Array $ V.fromList ["a","b","c"], Bool False])+ (Just $ Ast'Get $ Get ["a","b","c"] (Ast'Const $ Const'Bool False))++ context "Define" $ do+ it "simple" $ shouldBe+ (parseAst $ Array $ V.fromList ["def", "myVar", Bool True])+ (Just $ Ast'Define $ Define "myVar" (Ast'Const $ Const'Bool True))++ context "Lambda" $ do+ it "simple" $ shouldBe+ (parseAst $ Array $ V.fromList ["fn", Array $ V.fromList [ object [ "myVar" .= String "Int64" ] ], Bool True])+ (Just $ Ast'Lambda $ Lambda [("myVar", Type "Int64" Nothing)] (Ast'Const $ Const'Bool True))++ context "List" $ do+ it "simple" $ shouldBe+ (parseAst $ object [ "List" .= [String "hello"] ])+ (Just $ Ast'List $ List [Ast'Const $ Const'String "hello"])++ context "Begin" $ do+ it "simple" $ shouldBe+ (parseAst $ Array $ V.fromList ["begin", Bool True])+ (Just $ Ast'Begin $ Begin [Ast'Const $ Const'Bool True])++ context "FnCall" $ do+ it "simple" $ shouldBe+ (parseAst $ Array $ V.fromList ["someFn", "one", Bool False])+ (Just $ Ast'FnCall $ FnCall (Ast'Ref $ Ref "someFn") [Ast'Const $ Const'String "one", Ast'Const $ Const'Bool False])++ context "EnumerationCall" $ do+ it "simple" $ shouldBe+ (parseAst $ object [ "n" .= String "MyEnum", "e" .= object [ "tag" .= String "MyTag" ] ])+ (Just $ Ast'EnumerationCall $ EnumerationCall "MyEnum" (Ast'Enumerator $ Enumerator "MyTag" Nothing))++ context "StructCall" $ do+ it "simple" $ shouldBe+ (parseAst $ object [ "n" .= String "MyStruct", "m" .= object [ "x" .= Bool True ] ])+ (Just $ Ast'StructCall $ StructCall "MyStruct" (Map.fromList [("x", Ast'Const $ Const'Bool True)]))++ context "HollowCall" $ do+ it "simple" $ shouldBe+ (parseAst $ object [ "n" .= String "MyCall" ])+ (Just $ Ast'HollowCall $ HollowCall "MyCall")++ context "Enumerator" $ do+ it "simple without members" $ shouldBe+ (parseAst $ object [ "tag" .= String "MyTag" ])+ (Just $ Ast'Enumerator $ Enumerator "MyTag" Nothing)+ it "simple with members" $ shouldBe+ (parseAst $ object [ "tag" .= String "MyTag", "x" .= Bool True ])+ (Just $ Ast'Enumerator $ Enumerator "MyTag" (Just $ Map.fromList [("x", Ast'Const $ Const'Bool True)]))++ context "Struct" $ do+ it "simple" $ shouldBe+ (parseAst $ object [ "key0" .= Bool True ])+ (Just $ Ast'Struct $ Struct $ Map.fromList [("key0", Ast'Const $ Const'Bool True)])++ context "Const" $ do+ it "Bool" $ shouldBe+ (parseAst $ Bool True)+ (Just $ Ast'Const $ Const'Bool True)+ it "String" $ shouldBe+ (parseAst $ String "Hello")+ (Just $ Ast'Const $ Const'String "Hello")+ it "Number" $ shouldBe+ (parseAst $ Number 12345)+ (Just $ Ast'Const $ Const'Number 12345)
+ tests/ExprSpec.hs view
@@ -0,0 +1,13 @@+module ExprSpec where++import Test.Hspec++-- import Colorless.Types+-- import Colorless.Runtime.Types+-- import Colorless.Runtime.Expr++spec :: Spec+spec = do+ describe "expr" $ do+ it "" $ do+ True `shouldBe` True
+ tests/Spec.hs view
@@ -0,0 +1,1 @@+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
+ tests/ValSpec.hs view
@@ -0,0 +1,31 @@+module ValSpec (spec) where++import qualified Data.Map as Map+import Test.Hspec+import Data.Aeson+import Data.Aeson.Types+import Data.Text (Text)++import Colorless.Runtime.Types+import Colorless.Runtime.Val++spec :: Spec+spec = do+ describe "parse basic types from json" $ do+ it "Enumerator" $ shouldBe+ (parseMaybe parseJSON $ object [ "tag" .= ("None" :: Text) ])+ (Just $ Val'ApiVal $ ApiVal'Enumerator $ Enumerator "None" Nothing)+ it "Struct" $ shouldBe+ (parseMaybe parseJSON $ object [ "x" .= True ])+ (Just $ Val'ApiVal $ ApiVal'Struct $ Struct $ Map.fromList [("x", Val'Const $ Const'Bool True)] )+ it "Struct" $ shouldBe+ (parseMaybe parseJSON $ object [ "x" .= object [ "tag" .= ("None" :: Text) ] ])+ (Just $ Val'ApiVal $ ApiVal'Struct $ Struct $ Map.fromList [("x", Val'ApiVal $ ApiVal'Enumerator $ Enumerator "None" Nothing)] )+ describe "parse types from val" $ do+ context "Option" $ do+ it "None" $ shouldBe+ (fromVal $ Val'ApiVal $ ApiVal'Enumerator $ Enumerator "None" Nothing)+ (Just Nothing :: Maybe (Maybe Bool))+ it "Some" $ shouldBe+ (fromVal $ Val'ApiVal $ ApiVal'Enumerator $ Enumerator "Some" $ Just $ Map.fromList [("some", Val'Const $ Const'Bool $ True)])+ (Just (Just True) :: Maybe (Maybe Bool))