1
Fork 0

Proof of concept around copy symantics.

main
Joshua Potter 2021-12-26 10:14:04 -05:00
parent 6eab07e732
commit 8cf7bcd222
1 changed files with 159 additions and 41 deletions

View File

@ -6,6 +6,7 @@
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RankNTypes #-} {-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeOperators #-} {-# LANGUAGE TypeOperators #-}
{-# LANGUAGE ViewPatterns #-} {-# LANGUAGE ViewPatterns #-}
@ -15,11 +16,13 @@ module Parser.Final
, SQ(..) , SQ(..)
, Symantics(..) , Symantics(..)
, TQ(..) , TQ(..)
, TextSymantics(..)
, Typeable(..) , Typeable(..)
, fromDyn , fromDyn
, parseSingle , parseSingle
, parseStrict , parseStrict
, toDyn , toDyn
, runBoth'
) where ) where
import qualified Control.Monad.Combinators.Expr as E import qualified Control.Monad.Combinators.Expr as E
@ -27,12 +30,12 @@ import qualified Data.Eq.Type as EQ
import qualified Text.Megaparsec as M import qualified Text.Megaparsec as M
import Control.Applicative ((<|>)) import Control.Applicative ((<|>))
import Control.DeepSeq (NFData(..), deepseq)
import Data.Eq.Type ((:=)) import Data.Eq.Type ((:=))
import Data.Functor (void) import Data.Functor (void)
import Data.Maybe (isJust) import Data.Maybe (isJust)
import Data.Text (Text, pack, unpack) import Data.Text (Text, drop, dropEnd, pack, unpack)
import Parser.Utils import Parser.Utils
import Prelude hiding (drop)
-- ======================================== -- ========================================
-- Symantics -- Symantics
@ -46,6 +49,10 @@ class Symantics repr where
eAnd :: repr Bool -> repr Bool -> repr Bool eAnd :: repr Bool -> repr Bool -> repr Bool
eOr :: repr Bool -> repr Bool -> repr Bool eOr :: repr Bool -> repr Bool -> repr Bool
class (Symantics repr) => TextSymantics repr where
eText :: Text -> repr Text
eAppend :: repr Text -> repr Text -> repr Text
newtype Eval a = Eval {runEval :: a} deriving (Eq, Show) newtype Eval a = Eval {runEval :: a} deriving (Eq, Show)
instance Symantics Eval where instance Symantics Eval where
@ -56,6 +63,10 @@ instance Symantics Eval where
eAnd (Eval lhs) (Eval rhs) = Eval (lhs && rhs) eAnd (Eval lhs) (Eval rhs) = Eval (lhs && rhs)
eOr (Eval lhs) (Eval rhs) = Eval (lhs || rhs) eOr (Eval lhs) (Eval rhs) = Eval (lhs || rhs)
instance TextSymantics Eval where
eText = Eval
eAppend (Eval lhs) (Eval rhs) = Eval $ lhs <> rhs
-- ======================================== -- ========================================
-- Typeable -- Typeable
-- ======================================== -- ========================================
@ -63,24 +74,35 @@ instance Symantics Eval where
class Typeable repr where class Typeable repr where
pInt :: repr Integer pInt :: repr Integer
pBool :: repr Bool pBool :: repr Bool
pText :: repr Text
newtype TQ t = TQ {runTQ :: forall repr. Typeable repr => repr t} newtype TQ t = TQ {runTQ :: forall repr. Typeable repr => repr t}
instance Typeable TQ where instance Typeable TQ where
pInt = TQ pInt pInt = TQ pInt
pBool = TQ pBool pBool = TQ pBool
pText = TQ pText
newtype AsInt a = AsInt (Maybe (a := Integer)) newtype AsInt a = AsInt (Maybe (a := Integer))
instance Typeable AsInt where instance Typeable AsInt where
pInt = AsInt (Just EQ.refl) pInt = AsInt (Just EQ.refl)
pBool = AsInt Nothing pBool = AsInt Nothing
pText = AsInt Nothing
newtype AsBool a = AsBool (Maybe (a := Bool)) newtype AsBool a = AsBool (Maybe (a := Bool))
instance Typeable AsBool where instance Typeable AsBool where
pInt = AsBool Nothing pInt = AsBool Nothing
pBool = AsBool (Just EQ.refl) pBool = AsBool (Just EQ.refl)
pText = AsBool Nothing
newtype AsText a = AsText (Maybe (a := Text))
instance Typeable AsText where
pInt = AsText Nothing
pBool = AsText Nothing
pText = AsText (Just EQ.refl)
-- ======================================== -- ========================================
-- Dynamic -- Dynamic
@ -90,7 +112,7 @@ data Dynamic repr = forall t. Dynamic (TQ t) (repr t)
class IsDynamic a where class IsDynamic a where
type' :: forall repr. Typeable repr => repr a type' :: forall repr. Typeable repr => repr a
lift' :: forall repr. Symantics repr => a -> repr a lift' :: forall repr. TextSymantics repr => a -> repr a
cast' :: forall repr t. TQ t -> Maybe (t := a) cast' :: forall repr t. TQ t -> Maybe (t := a)
instance IsDynamic Integer where instance IsDynamic Integer where
@ -103,7 +125,12 @@ instance IsDynamic Bool where
lift' = eBool lift' = eBool
cast' (TQ t) = case t of AsBool a -> a cast' (TQ t) = case t of AsBool a -> a
toDyn :: forall repr a. IsDynamic a => Symantics repr => a -> Dynamic repr instance IsDynamic Text where
type' = pText
lift' = eText
cast' (TQ t) = case t of AsText a -> a
toDyn :: forall repr a. IsDynamic a => TextSymantics repr => a -> Dynamic repr
toDyn = Dynamic type' . lift' toDyn = Dynamic type' . lift'
fromDyn :: forall repr a. IsDynamic a => Dynamic repr -> Maybe (repr a) fromDyn :: forall repr a. IsDynamic a => Dynamic repr -> Maybe (repr a)
@ -112,28 +139,28 @@ fromDyn (Dynamic t e) = case t of
r' <- r r' <- r
pure $ EQ.coerce (EQ.lift r') e pure $ EQ.coerce (EQ.lift r') e
-- ======================================== asDyn
-- Single pass
-- ========================================
binDyn
:: forall repr a :: forall repr a
. Symantics repr . TextSymantics repr
=> IsDynamic a => IsDynamic a
=> (repr a -> repr a -> repr a) => (repr a -> repr a -> repr a)
-> Dynamic repr -> Dynamic repr
-> Dynamic repr -> Dynamic repr
-> Maybe (Dynamic repr) -> Maybe (Dynamic repr)
binDyn bin lhs rhs = do asDyn bin lhs rhs = do
lhs' <- fromDyn lhs lhs' <- fromDyn lhs
rhs' <- fromDyn rhs rhs' <- fromDyn rhs
pure . Dynamic type' $ bin lhs' rhs' pure . Dynamic type' $ bin lhs' rhs'
parseSingle :: forall repr. Symantics repr => Parser (Dynamic repr) -- ========================================
parseSingle = -- Single pass
let ferr (offset, msg) = M.setOffset offset >> fail msg -- ========================================
in expr >>= either ferr pure
parseSingle :: forall repr. TextSymantics repr => Parser (Dynamic repr)
parseSingle = expr >>= either offsetFail pure
where where
offsetFail (offset, msg) = M.setOffset offset >> fail msg
expr = E.makeExprParser term expr = E.makeExprParser term
[ [binary "+" eAdd, binary "-" eSub] [ [binary "+" eAdd, binary "-" eSub]
, [binary "&&" eAnd, binary "||" eOr] , [binary "&&" eAnd, binary "||" eOr]
@ -145,29 +172,17 @@ parseSingle =
pure $ \lhs rhs -> do pure $ \lhs rhs -> do
lhs' <- lhs lhs' <- lhs
rhs' <- rhs rhs' <- rhs
case binDyn bin lhs' rhs' of case asDyn bin lhs' rhs' of
Nothing -> Left (offset, "Invalid operands for `" <> unpack name <> "`") Nothing -> Left (offset, "Invalid operands for `" <> unpack name <> "`")
Just a -> pure a Just a -> pure a
term = parens expr <|> term = parens expr <|> Right . toDyn <$> integer <|> Right . toDyn <$> boolean
Right . toDyn <$> integer <|>
Right . toDyn <$> boolean
-- ======================================== -- ========================================
-- Strict -- Strict
-- ======================================== -- ========================================
instance (NFData t) => NFData (Eval t) where parseStrict :: forall repr. TextSymantics repr => Parser (Dynamic repr)
rnf (Eval t) = t `seq` ()
instance NFData (Dynamic Eval) where
rnf (Dynamic t e) = e `seq` ()
parseStrict
:: forall repr
. Symantics repr
=> NFData (Dynamic repr)
=> Parser (Dynamic repr)
parseStrict = term >>= expr parseStrict = term >>= expr
where where
expr :: Dynamic repr -> Parser (Dynamic repr) expr :: Dynamic repr -> Parser (Dynamic repr)
@ -189,9 +204,9 @@ parseStrict = term >>= expr
-> Parser (Dynamic repr) -> Parser (Dynamic repr)
nest t bin op = do nest t bin op = do
t' <- term t' <- term
case binDyn bin t t' of case asDyn bin t t' of
Nothing -> fail $ "Invalid operands for `" <> show op <> "`" Nothing -> fail $ "Invalid operands for `" <> show op <> "`"
Just a -> a `deepseq` expr a Just a -> a `seq` expr a
term :: Parser (Dynamic repr) term :: Parser (Dynamic repr)
term = do term = do
@ -200,18 +215,36 @@ parseStrict = term >>= expr
toDyn <$> integer <|> toDyn <$> boolean toDyn <$> integer <|> toDyn <$> boolean
-- ======================================== -- ========================================
-- Printer -- Pretty print
-- ======================================== -- ========================================
newtype Print a = Print {runPrint :: Text} deriving (Eq, Show) newtype PPrint a = PPrint {runPPrint :: Text} deriving (Eq, Show)
instance Symantics Print where instance Symantics PPrint where
eInt = Print . pack . show eInt = PPrint . pack . show
eBool = Print . pack . show eBool = PPrint . pack . show
eAdd (Print lhs) (Print rhs) = Print ("(" <> lhs <> " + " <> rhs <> ")") eAdd (PPrint lhs) (PPrint rhs) = PPrint $ "(" <> lhs <> " + " <> rhs <> ")"
eSub (Print lhs) (Print rhs) = Print ("(" <> lhs <> " - " <> rhs <> ")") eSub (PPrint lhs) (PPrint rhs) = PPrint $ "(" <> lhs <> " - " <> rhs <> ")"
eAnd (Print lhs) (Print rhs) = Print ("(" <> lhs <> " && " <> rhs <> ")") eAnd (PPrint lhs) (PPrint rhs) = PPrint $ "(" <> lhs <> " && " <> rhs <> ")"
eOr (Print lhs) (Print rhs) = Print ("(" <> lhs <> " || " <> rhs <> ")") eOr (PPrint lhs) (PPrint rhs) = PPrint $ "(" <> lhs <> " || " <> rhs <> ")"
instance TextSymantics PPrint where
eText = PPrint
eAppend (PPrint lhs) (PPrint rhs) =
PPrint $ "(" <> lhs <> " <> " <> rhs <> ")"
-- ========================================
-- Multiplication
-- ========================================
class (Symantics repr) => MulSymantics repr where
eMul :: repr Integer -> repr Integer -> repr Integer
instance MulSymantics Eval where
eMul (Eval lhs) (Eval rhs) = Eval (lhs * rhs)
instance MulSymantics PPrint where
eMul (PPrint lhs) (PPrint rhs) = PPrint $ "(" <> lhs <> " * " <> rhs <> ")"
-- ======================================== -- ========================================
-- Closed -- Closed
@ -226,3 +259,88 @@ instance Symantics SQ where
eSub (SQ lhs) (SQ rhs) = SQ (eSub lhs rhs) eSub (SQ lhs) (SQ rhs) = SQ (eSub lhs rhs)
eAnd (SQ lhs) (SQ rhs) = SQ (eAnd lhs rhs) eAnd (SQ lhs) (SQ rhs) = SQ (eAnd lhs rhs)
eOr (SQ lhs) (SQ rhs) = SQ (eOr lhs rhs) eOr (SQ lhs) (SQ rhs) = SQ (eOr lhs rhs)
newtype MSQ a = MSQ {runMSQ :: forall repr. MulSymantics repr => repr a}
instance Symantics MSQ where
eInt e = MSQ (eInt e)
eBool e = MSQ (eBool e)
eAdd (MSQ lhs) (MSQ rhs) = MSQ (eAdd lhs rhs)
eSub (MSQ lhs) (MSQ rhs) = MSQ (eSub lhs rhs)
eAnd (MSQ lhs) (MSQ rhs) = MSQ (eAnd lhs rhs)
eOr (MSQ lhs) (MSQ rhs) = MSQ (eOr lhs rhs)
instance MulSymantics MSQ where
eMul (MSQ lhs) (MSQ rhs) = MSQ (eMul lhs rhs)
data Result = RInt Integer | RBool Bool
runBoth :: Dynamic SQ -> Maybe (Result, Text)
runBoth d = case fromDyn @SQ @Integer d of
Just (SQ q) -> pure (case q of Eval a -> RInt a, case q of PPrint a -> a)
Nothing -> case fromDyn @SQ @Bool d of
Just (SQ q) -> pure (case q of Eval a -> RBool a, case q of PPrint a -> a)
Nothing -> Nothing
pPrint :: Dynamic SQ -> Maybe Text
pPrint d = case fromDyn @SQ @Integer d of
Just (SQ q) -> pure case q of PPrint a -> a
Nothing -> case fromDyn @SQ @Bool d of
Just (SQ q) -> pure case q of PPrint a -> a
Nothing -> Nothing
-- Have to define a new function to work on `MulSymantics`.
pPrint' :: Dynamic MSQ -> Maybe Text
pPrint' d = case fromDyn @MSQ @Integer d of
Just (MSQ q) -> pure case q of PPrint a -> a
Nothing -> case fromDyn @MSQ @Bool d of
Just (MSQ q) -> pure case q of PPrint a -> a
Nothing -> case fromDyn @MSQ @Text d of
Just (MSQ q) -> pure case q of PPrint a -> a
Nothing -> Nothing
-- ========================================
-- Open
-- ========================================
data SCopy repr1 repr2 a = SCopy (repr1 a) (repr2 a)
instance (Symantics repr1, Symantics repr2)
=> Symantics (SCopy repr1 repr2) where
eInt e = SCopy (eInt e) (eInt e)
eBool e = SCopy (eBool e) (eBool e)
eAdd (SCopy a1 a2) (SCopy b1 b2) = SCopy (eAdd a1 b1) (eAdd a2 b2)
eSub (SCopy a1 a2) (SCopy b1 b2) = SCopy (eSub a1 b1) (eSub a2 b2)
eAnd (SCopy a1 a2) (SCopy b1 b2) = SCopy (eAnd a1 b1) (eAnd a2 b2)
eOr (SCopy a1 a2) (SCopy b1 b2) = SCopy (eOr a1 b1) (eOr a2 b2)
instance (MulSymantics repr1, MulSymantics repr2)
=> MulSymantics (SCopy repr1 repr2) where
eMul (SCopy a1 a2) (SCopy b1 b2) = SCopy (eMul a1 b1) (eMul a2 b2)
runEval'
:: forall repr
. Dynamic (SCopy Eval repr)
-> Maybe (Result, Dynamic repr)
runEval' d = case fromDyn d :: Maybe (SCopy Eval repr Integer) of
Just (SCopy (Eval a) r) -> pure (RInt a, Dynamic pInt r)
Nothing -> case fromDyn d :: Maybe (SCopy Eval repr Bool) of
Just (SCopy (Eval a) r) -> pure (RBool a, Dynamic pBool r)
Nothing -> Nothing
runPPrint'
:: forall repr
. Dynamic (SCopy PPrint repr)
-> Maybe (Text, Dynamic repr)
runPPrint' d = case fromDyn d :: Maybe (SCopy PPrint repr Text) of
Just (SCopy (PPrint a) r) -> pure (a, Dynamic pText r)
Nothing -> Nothing
runBoth'
:: forall repr
. Dynamic (SCopy Eval (SCopy PPrint repr))
-> Maybe (Result, Text, Dynamic repr)
runBoth' d = do
(r, d') <- runEval' d
(p, d'') <- runPPrint' d'
pure (r, p, d'')