processing-1.2.0.0: Graphics/Web/Processing/Core/Primal.hs
{-# LANGUAGE OverloadedStrings, MultiParamTypeClasses, FunctionalDependencies,
DeriveGeneric, TypeOperators, DefaultSignatures, FlexibleContexts,
TemplateHaskell
#-}
{- | Internal core module.
The purpose of this module is to define the most basic types
and write the necessary instances for them.
-}
module Graphics.Web.Processing.Core.Primal (
-- * Types
-- ** Singleton types
-- | A list of singleton types, used to restrict the
-- use of certain commands to specific contexts.
Preamble (..), Setup (..), Draw (..)
, MouseClicked (..), MouseReleased (..)
, KeyPressed (..)
-- ** Recursive types
, Recursive (..)
-- ** @Proc_*@ types
-- *** Boolean
, Proc_Bool (..), fromBool
, true, false
, pnot, (#||), (#&&)
-- *** Int
, Proc_Int (..), fromInt
, pfloor, pround
-- *** Float
, Proc_Float (..), fromFloat
, intToFloat
, noisef
-- *** Image
, Proc_Image
-- *** Char
, Proc_Char , fromChar
-- *** Text
, Proc_Text, fromStText
, (+.+)
, Proc_Show (..)
-- *** Keys
, Proc_Key (..)
, Proc_KeyCode (..)
-- ** Type class of proc types
, ProcType (..)
, isVarInArg, isVarInAssign
, assignVarName
-- ** Conditionals
, Proc_Eq (..)
, Proc_Ord (..)
-- ** Reductions
, Reducible (..)
-- ** Variables
, Var, varName, varFromText
, ArrayVar, arrayVarName, arrayVarFromText
, arraySize
, arrayVarToVar
-- ** Script
, ProcCode (..), ProcArg (..), ProcAssign (..)
, ProcList (..)
, emptyCode
, (>>.)
, ProcScript (..)
, emptyScript
) where
import Prelude hiding (foldr)
import Data.Text (Text,lines,pack)
import Data.Text.Lazy (toStrict)
import qualified Data.Sequence as Seq
import Data.Monoid
import Data.String
import Data.Foldable (foldMap,foldr)
import Control.Applicative
-- Pretty
import Text.PrettyPrint.Mainland
-- QuickCheck
import Test.QuickCheck (Arbitrary (..), Gen, oneof, sized, resize, vectorOf)
import Test.QuickCheck.Instances()
-- Meta-programming
import GHC.Generics
import Graphics.Web.Processing.Core.TH
------------------------------------------------
-- QUICK CHECK DERIVING
{-
Some of the types defined in this module have a big
amount of data constructors. Creating Arbitrary instances
for each of them manually is a tedious and unnecessary work.
In order to be able to derive automatically instance for
the arbitrary typeclass, we create a new class, PArbitrary
(from Processing Arbitrary). Having access to the class
definition, we can provide a default instance based in our
generic deriving. Once an instance to PArbitrary is done,
the Arbitrary instance is trivial:
instance Arbitrary a where
arbitrary = parbitrary
Given the number of data constructors, and being most of them
recursive, if we create totally random values, the chances of
creating (insanely) huge values is very high. To avoid it, we
set a maximum number of random steps (see 'sizeLimit'). When
this number is reached, we "travel" to the left-most constructor,
which by definition will be finite (something like Proc_Float Float).
-}
class PArbitrary a where
parbitrary :: Gen a
default parbitrary :: (Generic a, GArbitrary (Rep a)) => Gen a
parbitrary = to <$> garbitrary
class GArbitrary f where
garbitrary :: Gen (f a)
instance GArbitrary U1 where
garbitrary = pure U1
instance (GArbitrary a, GArbitrary b) => GArbitrary (a :*: b) where
garbitrary = sized $
\n -> resize (n+1) $ (:*:) <$> garbitrary <*> garbitrary
sizeLimit :: Int
sizeLimit = 15
instance (GArbitrary a, GArbitrary b) => GArbitrary (a :+: b) where
garbitrary = sized $
\n -> if n > sizeLimit
then L1 <$> garbitrary
else oneof [L1 <$> garbitrary, R1 <$> garbitrary]
instance GArbitrary a => GArbitrary (M1 i c a) where
garbitrary = M1 <$> garbitrary
instance PArbitrary a => GArbitrary (K1 i a) where
garbitrary = K1 <$> parbitrary
------------------------------------------------
-- DEFAULT INSTANCES
instance Arbitrary a => PArbitrary (Maybe a) where
parbitrary = arbitrary
instance Arbitrary a => PArbitrary (Seq.Seq a) where
parbitrary = arbitrary
instance PArbitrary Int where
parbitrary = arbitrary
instance PArbitrary Float where
parbitrary = arbitrary
instance PArbitrary Text where
parbitrary = pack <$> vectorOf 4 parbitrary
instance PArbitrary Char where
parbitrary = oneof $ fmap pure [ 'a' .. 'z' ]
instance Arbitrary a => PArbitrary [a] where
parbitrary = arbitrary
------------------------------------------------
{-
Processing.js code is divided in different sections.
Each section handles a different event. Naturally,
there are commands that may be called inside of a
particular context, but not within another. Most of
these commands are runnable in different kind of
events. Writing variables should be possible from
any event. To handle this situation, we annotate
the AST with a /context/. This context indicates
which event that portion of code belongs to. For
example, @ProcCode Draw@ indicates that the code
belongs to the draw loop. Now we can restrict
functions to work only under certain contexts.
For example, variables should be created only once.
Since events may be called several times, we restrict
the type of any function that creates variables,
annotating the type with 'Preamble'.
-}
-- | The /preamble/ is the code that is executed
-- at the beginning of the script.
data Preamble = Preamble
-- | In the /setup/ part, settings like /size/ or
-- /frame rate/ are supplied.
data Setup = Setup
-- | The drawing loop.
data Draw = Draw
-- | Code that is executed when the mouse is clicked.
data MouseClicked = MouseClicked
-- | Code that is executed when the mouse is released.
data MouseReleased = MouseReleased
-- | Code executed when a key is pressed.
data KeyPressed = KeyPressed
-- PRETTY-HELPERS
pfunction :: Text -> [Doc] -> Doc
pfunction n as = fromText n <> parens (commasep as)
-- TYPES
{-
Some Proc_* types can be seen as extensions
of some Haskell types. For example Proc_Float
may contain a Float under the Proc_Float data
constructor. However, it has more data constructors
and, therefore, it may contain other different
values. The Extended class is created for
these types. It provides two methods that, once
defined, permit to extend functions and operators
from the type that has been extended to the extension.
For example, we can extend the sin function, which
is defined for Float values, to value of the type
Proc_Float.
The two methods required are extend and patmatch
(pattern match). The method extend should inject
a value from the extended type to the extension.
The method patmatch would do the opposite. However,
not every element in the extension belongs to the
extended type. We return Nothing in those cases.
Extended functions and operators behave the same
way as the originals for values in the extended
type. A supplied default function/operator
indicates what to do in the rest of cases.
-}
class Extended from to | to -> from where
extend :: from -> to
patmatch :: to -> Maybe from
-- | Function extension.
extendf :: (Extended from to, Extended from' to')
=> (from -> from') -> (to -> to') -> (to -> to')
extendf f g x =
case patmatch x of
Nothing -> g x
Just a -> extend $ f a
-- | Operator extension.
extendop :: (Extended from to
,Extended from' to'
,Extended from'' to'')
=> (from -> from' -> from'')
-> (to -> to' -> to'')
-> (to -> to' -> to'')
extendop f g x y =
case (patmatch x, patmatch y) of
(Just a, Just b) -> extend $ f a b
_ -> g x y
{- | Class of recursive types.
The 'recursor' function applies the
given function to every subexpression
of the same type. For example, this would
be the recursor over lists:
recursor f [] = []
recursor f (x:xs) = x : f xs
Instances of Recursive can be derived
using $(deriveRecursive ''Type).
-}
class Recursive a where
recursor :: (a -> a) -> a -> a
{- Proc_* types
Proc_* types are AST's for different kind
of expressions. For example, a value of type
Proc_Bool store an AST of a boolean expression.
The "Proc_" prefix indicates that the type
of the expression matches a type in Processing.
Proc_* types have a specialized version of
'extend'. This way, the Extended class can be
kept hidden to the user. If this is or not a
good idea is something to be discussed.
Note that the class use Functional Dependencies.
-}
-- | Boolean values.
data Proc_Bool =
Proc_True
| Proc_False
-- Operations
| Proc_Neg Proc_Bool
| Proc_Or Proc_Bool Proc_Bool
| Proc_And Proc_Bool Proc_Bool
-- Variables
| Bool_Var Text
-- Comparisons
-- Bool
| Bool_Eq Proc_Bool Proc_Bool
| Bool_NEq Proc_Bool Proc_Bool
-- Int (with order)
| Int_Eq Proc_Int Proc_Int
| Int_NEq Proc_Int Proc_Int
| Int_LE Proc_Int Proc_Int
| Int_L Proc_Int Proc_Int
| Int_GE Proc_Int Proc_Int
| Int_G Proc_Int Proc_Int
-- Float (with order)
| Float_Eq Proc_Float Proc_Float
| Float_NEq Proc_Float Proc_Float
| Float_LE Proc_Float Proc_Float
| Float_L Proc_Float Proc_Float
| Float_GE Proc_Float Proc_Float
| Float_G Proc_Float Proc_Float
-- Char
| Char_Eq Proc_Char Proc_Char
| Char_NEq Proc_Char Proc_Char
-- Text
| Text_Eq Proc_Text Proc_Text
-- Key
| Key_Eq Proc_Key Proc_Key
| KeyCode_Eq Proc_KeyCode Proc_KeyCode
-- Conditional
| Bool_Cond Proc_Bool Proc_Bool Proc_Bool
deriving (Eq,Ord,Generic)
instance PArbitrary Proc_Bool
instance Arbitrary Proc_Bool where
arbitrary = parbitrary
instance Extended Bool Proc_Bool where
extend True = Proc_True
extend False = Proc_False
patmatch Proc_True = Just True
patmatch Proc_False = Just False
patmatch _ = Nothing
-- Doc helpers
docEq :: Doc
docEq = fromText "=="
docNEq :: Doc
docNEq = fromText "!="
docLE :: Doc
docLE = fromText "<="
docL :: Doc
docL = fromText "<"
docGE :: Doc
docGE = fromText ">="
docG :: Doc
docG = fromText ">"
-- | Processing.js syntax for conditionals.
--
-- > <bool> ? <a> : <a>
--
docCond :: Doc -> Doc -> Doc -> Doc
docCond _if _then _else = _if <+> fromText "?" <+> _then <+> fromText ":" <+> _else
-- | This constant indicates how many spaces
-- are added in each indentation. Events
-- and conditionals add indentation.
indentLevel :: Int
indentLevel = 3
instance Pretty Proc_Bool where
ppr Proc_True = fromText "true"
ppr Proc_False = fromText "false"
ppr (Proc_Neg b) = fromText "!" <> ppr b
ppr (Proc_Or b1 b2) = parens $ ppr b1 <+> fromText "||" <+> ppr b2
ppr (Proc_And b1 b2) = parens $ ppr b1 <+> fromText "&&" <+> ppr b2
ppr (Bool_Var t) = fromText t
-- Comparisons
ppr (Bool_Eq x y) = parens $ ppr x <+> docEq <+> ppr y
ppr (Bool_NEq x y) = parens $ ppr x <+> docNEq <+> ppr y
ppr (Int_Eq x y) = parens $ ppr x <+> docEq <+> ppr y
ppr (Int_NEq x y) = parens $ ppr x <+> docNEq <+> ppr y
ppr (Int_LE x y) = parens $ ppr x <+> docLE <+> ppr y
ppr (Int_L x y) = parens $ ppr x <+> docL <+> ppr y
ppr (Int_GE x y) = parens $ ppr x <+> docGE <+> ppr y
ppr (Int_G x y) = parens $ ppr x <+> docG <+> ppr y
ppr (Float_Eq x y) = parens $ ppr x <+> docEq <+> ppr y
ppr (Float_NEq x y) = parens $ ppr x <+> docNEq <+> ppr y
ppr (Float_LE x y) = parens $ ppr x <+> docLE <+> ppr y
ppr (Float_L x y) = parens $ ppr x <+> docL <+> ppr y
ppr (Float_GE x y) = parens $ ppr x <+> docGE <+> ppr y
ppr (Float_G x y) = parens $ ppr x <+> docG <+> ppr y
ppr (Char_Eq x y) = parens $ ppr x <+> docEq <+> ppr y
ppr (Char_NEq x y) = parens $ ppr x <+> docNEq <+> ppr y
ppr (Text_Eq x y) = ppr x <> fromText "." <> pfunction "equals" [ppr y]
ppr (Key_Eq x y) = parens $ ppr x <+> docEq <+> ppr y
ppr (KeyCode_Eq x y) = parens $ ppr x <+> docEq <+> ppr y
-- Conditional
ppr (Bool_Cond b x y) = parens $ docCond (ppr b) (ppr x) (ppr y)
-- | Value of 'True'.
true :: Proc_Bool
true = Proc_True
-- | Value of 'False'.
false :: Proc_Bool
false = Proc_False
-- | Negation.
pnot :: Proc_Bool -> Proc_Bool
pnot = extendf not Proc_Neg
infixr 2 #||
-- | Disjunction.
(#||) :: Proc_Bool -> Proc_Bool -> Proc_Bool
(#||) = extendop (||) Proc_Or
infixr 3 #&&
-- | Conjunction.
(#&&) :: Proc_Bool -> Proc_Bool -> Proc_Bool
(#&&) = extendop (&&) Proc_And
-- | Cast a 'Bool' value.
fromBool :: Bool -> Proc_Bool
fromBool = extend
-- | Integer numbers.
data Proc_Int =
Proc_Int Int
-- Operations
| Int_Sum Proc_Int Proc_Int
| Int_Substract Proc_Int Proc_Int
| Int_Divide Proc_Int Proc_Int
| Int_Mult Proc_Int Proc_Int
| Int_Mod Proc_Int Proc_Int
-- Variables
| Int_Var Text
-- Functions
| Int_Abs Proc_Int
| Int_Floor Proc_Float
| Int_Round Proc_Float
-- Conditional
| Int_Cond Proc_Bool Proc_Int Proc_Int
deriving (Eq,Ord,Generic)
instance PArbitrary Proc_Int
instance Arbitrary Proc_Int where
arbitrary = parbitrary
instance Extended Int Proc_Int where
extend = Proc_Int
patmatch (Proc_Int a) = Just a
patmatch _ = Nothing
instance Pretty Proc_Int where
ppr (Proc_Int i) = ppr i
ppr (Int_Sum n m) = parens $ ppr n <> fromText "+" <> ppr m
ppr (Int_Substract n m) = parens $ ppr n <> fromText "-" <> ppr m
ppr (Int_Divide n m) = parens $ ppr n <> fromText "/" <> ppr m
ppr (Int_Mult n m) = parens $ ppr n <> fromText "*" <> ppr m
ppr (Int_Mod n m) = parens $ ppr n <> fromText "%" <> ppr m
ppr (Int_Var t) = fromText t
ppr (Int_Abs n) = pfunction "abs" [ppr n]
ppr (Int_Floor x) = pfunction "floor" [ppr x]
ppr (Int_Round x) = pfunction "round" [ppr x]
ppr (Int_Cond b x y) = parens $ docCond (ppr b) (ppr x) (ppr y)
-- | Cast an 'Int' value.
fromInt :: Int -> Proc_Int
fromInt = extend
-- | Calculate the 'floor' of a 'Proc_Float'.
pfloor :: Proc_Float -> Proc_Int
pfloor = extendf floor Int_Floor
-- | Round a number to the closest integer.
pround :: Proc_Float -> Proc_Int
pround = extendf round Int_Round
instance Enum Proc_Int where
toEnum = fromInt
fromEnum n = case patmatch n of
Nothing -> error "Proc_Int: fromEnum applied to a variable."
Just i -> i
succ n = n + 1
pred n = n - 1
-- | WARNING: 'signum' method is undefined.
instance Num Proc_Int where
fromInteger = fromInt . fromInteger
(+) = extendop (+) Int_Sum
(-) = extendop (-) Int_Substract
(*) = extendop (*) Int_Mult
abs = extendf abs Int_Abs
signum = error "Proc_Int: signum method is undefined."
instance Real Proc_Int where
toRational n = case patmatch n of
Nothing -> error "Proc_Int: toRational applied to a variable."
Just i -> toRational i
instance Integral Proc_Int where
div = extendop div Int_Divide
mod = extendop mod Int_Mod
quotRem n d = (div n d, mod n d)
divMod = quotRem
toInteger n = case patmatch n of
Nothing -> error "Proc_Int: toInteger applied to a variable."
Just i -> toInteger i
-- | Floating point numbers.
-- The provided 'Eq' instance checks the equality of the
-- internal expression, not the value.
data Proc_Float =
Proc_Float Float
-- Operations
| Float_Sum Proc_Float Proc_Float
| Float_Substract Proc_Float Proc_Float
| Float_Divide Proc_Float Proc_Float
| Float_Mult Proc_Float Proc_Float
| Float_Mod Proc_Float Proc_Float
| Float_Neg Proc_Float
-- Variables
| Float_Var Text
-- Functions
| Float_Abs Proc_Float
| Float_Exp Proc_Float
| Float_Sqrt Proc_Float
| Float_Log Proc_Float
| Float_Sine Proc_Float
| Float_Cosine Proc_Float
| Float_Arcsine Proc_Float
| Float_Arccosine Proc_Float
| Float_Arctangent Proc_Float
| Float_Floor Proc_Float -- Applies floor but it treats the result
-- as a float. Only internal.
| Float_Round Proc_Float -- Same observation for Float_Floor.
| Float_Noise Proc_Float Proc_Float
| Float_Random Proc_Float Proc_Float
-- Conditional
| Float_Cond Proc_Bool Proc_Float Proc_Float
deriving (Eq,Ord,Generic)
instance PArbitrary Proc_Float
instance Arbitrary Proc_Float where
arbitrary = parbitrary
instance Extended Float Proc_Float where
extend = Proc_Float
patmatch (Proc_Float x) = Just x
patmatch _ = Nothing
instance Pretty Proc_Float where
ppr (Proc_Float f) = ppr f
ppr (Float_Sum x y) = parens $ ppr x <> fromText "+" <> ppr y
ppr (Float_Substract x y) = parens $ ppr x <> fromText "-" <> ppr y
ppr (Float_Divide x y) = parens $ ppr x <> fromText "/" <> ppr y
ppr (Float_Mult x y) = parens $ ppr x <> fromText "*" <> ppr y
ppr (Float_Neg x) = fromText "-" <> ppr x
ppr (Float_Mod x y) = parens $ ppr x <> fromText "%" <> ppr y
ppr (Float_Var t) = fromText t
ppr (Float_Abs x) = pfunction "abs" [ppr x]
ppr (Float_Exp x) = pfunction "exp" [ppr x]
ppr (Float_Sqrt x) = pfunction "sqrt" [ppr x]
ppr (Float_Log x) = pfunction "log" [ppr x]
ppr (Float_Sine x) = pfunction "sin" [ppr x]
ppr (Float_Cosine x) = pfunction "cos" [ppr x]
ppr (Float_Arcsine x) = pfunction "asin" [ppr x]
ppr (Float_Arccosine x) = pfunction "acos" [ppr x]
ppr (Float_Arctangent x) = pfunction "atan" [ppr x]
ppr (Float_Floor x) = pfunction "floor" [ppr x]
ppr (Float_Round x) = pfunction "round" [ppr x]
ppr (Float_Noise x y) = pfunction "noise" [ppr x,ppr y]
ppr (Float_Cond b x y) = parens $ docCond (ppr b) (ppr x) (ppr y)
ppr (Float_Random x y) = pfunction "random" [ppr x,ppr y]
-- | Cast a 'Float' value.
fromFloat :: Float -> Proc_Float
fromFloat = extend
-- | Noise random function.
noisef :: Proc_Float -> Proc_Float -> Proc_Float
noisef = Float_Noise
-- | Cast a 'Proc_Int' to a 'Proc_Float'.
intToFloat :: Proc_Int -> Proc_Float
intToFloat (Proc_Int i) = Proc_Float $ fromIntegral i
intToFloat (Int_Sum n m) = Float_Sum (intToFloat n) (intToFloat m)
intToFloat (Int_Substract n m) = Float_Substract (intToFloat n) (intToFloat m)
intToFloat (Int_Divide n m) = Float_Divide (intToFloat n) (intToFloat m)
intToFloat (Int_Mult n m) = Float_Mult (intToFloat n) (intToFloat m)
intToFloat (Int_Mod n m) = Float_Mod (intToFloat n) (intToFloat m)
intToFloat (Int_Var t) = Float_Var t
intToFloat (Int_Abs n) = Float_Abs $ intToFloat n
intToFloat (Int_Floor x) = Float_Floor x
intToFloat (Int_Round x) = Float_Round x
intToFloat (Int_Cond b x y) = Float_Cond b (intToFloat x) (intToFloat y)
-- | WARNING: 'signum' method is undefined.
instance Num Proc_Float where
fromInteger = fromFloat . fromInteger
(+) = extendop (+) Float_Sum
(-) = extendop (-) Float_Substract
(*) = extendop (*) Float_Mult
abs = extendf abs Float_Abs
negate = extendf negate Float_Neg
signum = error "Proc_Float: signum method is undefined."
instance Fractional Proc_Float where
(/) = extendop (/) Float_Divide
fromRational = fromFloat . fromRational
-- | WARNING: 'sinh', 'cosh', 'asinh', 'acosh' and 'atanh'
-- methods are undefined. They are not present in
-- processing.js.
instance Floating Proc_Float where
pi = extend pi
exp = extendf exp Float_Exp
sqrt = extendf sqrt Float_Sqrt
log = extendf log Float_Log
sin = extendf sin Float_Sine
cos = extendf cos Float_Cosine
asin = extendf asin Float_Arcsine
acos = extendf acos Float_Arccosine
atan = extendf atan Float_Arctangent
-- UNDEFINED
sinh = error "Proc_Float: sinh method is undefined."
cosh = error "Proc_Float: cosh method is undefined."
asinh = error "Proc_Float: asinh method is undefined."
acosh = error "Proc_Float: acosh method is undefined."
atanh = error "Proc_Float: atanh method is undefined."
-- | Type of images.
data Proc_Image =
Image_Var Text
| Image_Cond Proc_Bool Proc_Image Proc_Image
deriving (Eq,Generic)
instance PArbitrary Proc_Image
instance Arbitrary Proc_Image where
arbitrary = parbitrary
instance Pretty Proc_Image where
ppr (Image_Var t) = fromText t
ppr (Image_Cond b x y) = parens $ docCond (ppr b) (ppr x) (ppr y)
-- | Type of characters.
data Proc_Char =
Proc_Char Char
| Char_Var Text
| Char_Cond Proc_Bool Proc_Char Proc_Char
deriving (Eq,Ord,Generic)
instance PArbitrary Proc_Char
instance Arbitrary Proc_Char where
arbitrary = parbitrary
instance Extended Char Proc_Char where
extend = Proc_Char
patmatch (Proc_Char c) = Just c
patmatch _ = Nothing
-- | Cast a 'Char' value.
fromChar :: Char -> Proc_Char
fromChar = extend
instance Pretty Proc_Char where
ppr (Proc_Char c) = enclose squote squote (char c)
ppr (Char_Var n) = fromText n
ppr (Char_Cond b x y) = parens $ docCond (ppr b) (ppr x) (ppr y)
-- | Type of textual values.
--
-- It is recommended to enable the @OverloadedStrings@ extension.
-- Note that 'Proc_Text' is an instance of the 'IsString' class.
data Proc_Text =
Proc_Text Text
| Text_Var Text
-- Proc_Show
-- See <http://processingjs.org/reference/str_> for avaiable
-- types.
| Show_Bool Proc_Bool
| Show_Char Proc_Char
| Show_Float Proc_Float
| Show_Int Proc_Int
-- String appending
| Text_Append Proc_Text Proc_Text
-- Conditional
| Text_Cond Proc_Bool Proc_Text Proc_Text
deriving (Eq,Ord,Generic)
infixr 5 +.+
-- | Append two text strings.
(+.+) :: Proc_Text -> Proc_Text -> Proc_Text
(+.+) = Text_Append
instance PArbitrary Proc_Text
instance Arbitrary Proc_Text
instance Extended Text Proc_Text where
extend = Proc_Text
patmatch (Proc_Text t) = Just t
patmatch _ = Nothing
instance Pretty Proc_Text where
-- Wrong pretty-printer for text values. Fix it to
-- escape characters.
ppr (Proc_Text t) = enclose dquote dquote (fromText t)
ppr (Text_Var n) = fromText n
--
ppr (Show_Bool x) = pfunction "str" [ppr x]
ppr (Show_Char x) = pfunction "str" [ppr x]
ppr (Show_Float x) = pfunction "str" [ppr x]
ppr (Show_Int x) = pfunction "str" [ppr x]
-- No parenthesis are included since appending is the
-- only operation over text.
ppr (Text_Append x y) = ppr x <+> fromText "+" <+> ppr y
--
ppr (Text_Cond b x y) = parens $ docCond (ppr b) (ppr x) (ppr y)
-- | Cast a strict 'Text' value.
fromStText :: Text -> Proc_Text
fromStText = extend
instance IsString Proc_Text where
fromString = fromStText . fromString
-- | Similar to the 'Show' class, but for @Proc_*@ types.
class Proc_Show a where
-- | Render a value as a 'Proc_Text'.
pshow :: a -> Proc_Text
instance Proc_Show Proc_Bool where
pshow = Show_Bool
instance Proc_Show Proc_Char where
pshow = Show_Char
instance Proc_Show Proc_Float where
pshow = Show_Float
instance Proc_Show Proc_Int where
pshow = Show_Int
-- | Type of keyboard keys.
data Proc_Key =
Key_Var
| Key_CODED
| Key_Char Char
deriving (Eq,Ord,Generic)
instance PArbitrary Proc_Key
instance Pretty Proc_Key where
ppr Key_Var = fromText "key"
ppr Key_CODED = fromText "CODED"
ppr (Key_Char c) = ppr $ fromChar c
-- | Type of keyboard key codes.
data Proc_KeyCode =
KeyCode_Var
| KeyCode_UP
| KeyCode_DOWN
| KeyCode_LEFT
| KeyCode_RIGHT
| KeyCode_ALT
| KeyCode_CONTROL
| KeyCode_SHIFT
| KeyCode_BACKSPACE
| KeyCode_TAB
| KeyCode_ENTER
| KeyCode_RETURN
| KeyCode_ESC
| KeyCode_DELETE
deriving (Eq,Ord,Generic)
instance PArbitrary Proc_KeyCode
instance Pretty Proc_KeyCode where
ppr KeyCode_Var = fromText "keyCode"
ppr KeyCode_UP = fromText "UP"
ppr KeyCode_DOWN = fromText "DOWN"
ppr KeyCode_LEFT = fromText "LEFT"
ppr KeyCode_RIGHT = fromText "RIGHT"
ppr KeyCode_ALT = fromText "ALT"
ppr KeyCode_CONTROL = fromText "CONTROL"
ppr KeyCode_SHIFT = fromText "SHIFT"
ppr KeyCode_BACKSPACE = fromText "BACKSPACE"
ppr KeyCode_TAB = fromText "TAB"
ppr KeyCode_ENTER = fromText "ENTER"
ppr KeyCode_RETURN = fromText "RETURN"
ppr KeyCode_ESC = fromText "ESC"
ppr KeyCode_DELETE = fromText "DELETE"
-- END OF PROC_* TYPES
----------------------------------------------
----------------------------------------------
{- Proc_* type mechanics
Three types are automatically generated for the
Proc_* types. These are ProcArg, ProcAssign and ProcList.
A processing command may receive several arguments
of the same or different Proc_* types.
We encode arguments under the ProcArg type. The
ProcArg type is the disjoint union of the different
Proc_* types. For a complete list, see the
Graphics.Web.Processing.Core.TH module.
The list is called procTypeNames.
In the other hand, variable assignments are also
encoded in a particular type, named ProcAssign.
The ProcAssign type is the disjoint union of the
product of each Proc_* type with Text. This means
that a value of this type contains a value of any
of the different Proc_* types together with a value
of type Text. This text represents the name of the
variable in the assignment.
ProcList is a type for lists of processing values,
used in the implementation of arrays. The ProcList
type is the disjoint union of each Proc_* type
embedded into a list.
All ProcArg, ProcAssign and ProcList are generated
automatically, together with instances of the
Pretty class, by procTypeMechs.
Compile with "-f info" to see the generated
code. The structure for each Proc_* type is as
follows:
data ProcArg = (union in *) *Arg Proc_*
instance Pretty ProcArg where
(for each *) ppr (*Arg x) = ppr x
data ProcAssign = (union in *) *Assign Text Proc_*
instance Pretty ProcAssign where
(for each *) ppr (*Assign t x) =
fromText t <+> fromText "=" <+> ppr x
data ProcList = (union in *) *List [Proc_*]
instance Pretty ProcList where
(for each *) ppr (*List xs) =
fromText "{" <> commasep (fmap ppr xs) <> fromText "}"
In addition, the following two functions are defined.
ptype :: ProcAssign -> Doc
(for each *) ptype (*Assign _ _) = fromText "Name of * in processing.js"
ltype :: ProcList -> Doc
(for each *) ltype (*List _) = fromText $ "Name of * in processing.js" ++ "[]"
-}
$(procTypeMechs)
instance PArbitrary ProcArg
instance Arbitrary ProcArg where
arbitrary = parbitrary
instance PArbitrary ProcAssign
instance PArbitrary ProcList
-- CODE
-- | A piece of Processing code.
-- The type parameter indicates what the
-- context of the code is.
-- This context will allow or disallow
-- the use of certain commands along
-- different events.
data ProcCode c =
Command Text [ProcArg]
| CreateVar ProcAssign
| CreateArrayVar Text ProcList
| Assignment ProcAssign
| Conditional Proc_Bool -- IF
(ProcCode c) -- THEN
(ProcCode c) -- ELSE
| Comment Text
| Sequence (Seq.Seq (ProcCode c))
deriving (Generic,Eq)
instance PArbitrary (ProcCode c)
instance Arbitrary (ProcCode c) where
arbitrary = parbitrary
instance Pretty (ProcCode c) where
ppr (Command n as) = pfunction n (fmap ppr as) <+> fromText ";"
-- ptype is defined by $(procTypeMechs).
ppr (CreateVar a) = ptype a <+> ppr a <+> fromText ";"
-- ltype is defined by $(procTypeMechs).
ppr (CreateArrayVar n xs) = ltype xs <+> fromText n <+> fromText "="
<+> ppr xs <+> fromText ";"
ppr (Assignment a) = ppr a <+> fromText ";"
ppr (Conditional b e1 e2) =
let c1 = indent indentLevel $ ppr e1
c2 = indent indentLevel $ ppr e2
in pfunction "if" [ppr b]
<+> enclose lbrace rbrace (line <> c1)
<+> if e2 == mempty then mempty
else fromText "else" <+> enclose lbrace rbrace (line <> c2)
ppr (Comment t) = stack $ fmap (fromText . ("// " <>)) $ Data.Text.lines t
ppr (Sequence sq) =
if Seq.null sq then Text.PrettyPrint.Mainland.empty
else foldMap ((<> line) . ppr) sq
-- | Sequence to pieces of code with the same
-- context type. This way, code that belongs
-- to different parts of the program will
-- never get mixed.
(>>.) :: ProcCode c -> ProcCode c -> ProcCode c
(Sequence xs) >>. (Sequence ys) = Sequence $ xs Seq.>< ys
(Sequence xs) >>. p = if Seq.null xs
then p
else Sequence $ xs Seq.|> p
p >>. (Sequence xs) = if Seq.null xs
then p
else Sequence $ p Seq.<| xs
p >>. q = Sequence $ Seq.fromList [p,q]
-- | An empty piece of code.
emptyCode :: ProcCode a
emptyCode = Sequence $ Seq.empty
instance Monoid (ProcCode a) where
mempty = emptyCode
mappend = (>>.)
---- ProcType class and instances
-- | Type of variables.
data Var a = Var { -- | Get the name of a variable.
varName :: Text }
-- | Internal function to create variables.
varFromText :: Text -> Var a
varFromText = Var
-- | Type of variables storing arrays.
data ArrayVar a =
ArrayVar { -- | Size of the array.
arraySize :: Int
, innerVar :: Var a }
-- | Get the name of a variable storing an array.
arrayVarName :: ArrayVar a -> Text
arrayVarName = varName . innerVar
-- | Internal function to create array variables.
arrayVarFromText :: Int -> Text -> ArrayVar a
arrayVarFromText n t = ArrayVar n (Var t)
-- | Translate an Array variable to the correspondent
-- component variable.
arrayVarToVar :: ArrayVar a -> Proc_Int -> Var a
arrayVarToVar v n = varFromText $ arrayVarName v <> "[" <> f n <> "]"
where
f = toStrict . prettyLazyText 80 . ppr
----- CLASSES
-- | Class of Processing value types (@Proc_*@ types).
--
-- @Proc_*@ types are types from the world of Processing.
-- Some of them are similar to Haskell types, like 'Proc_Bool'
-- and 'Bool'. However, they are not equal. @Proc_*@ types
-- are instance of 'Eq'. However, you should instead use methods from
-- the analog 'Proc_Eq' class. @Proc_*@ types contain expressions instead
-- of values. Think of @2+2@ instead of @4@. Under this situation,
-- @2+2 /= 3+1@, since they are different expressions, even if they
-- evaluate to the same value. Actually, you will get 'True'
-- from the evaluation of @2+2 == 3+1@, since the library is smart
-- enough to figure out they have the same value. But, please, don't
-- rely on this. Use the 'Proc_Eq' and 'Proc_Ord' classes instead.
-- They return Processing boolean expressions instead of 'Bool' values.
-- Anyway, the types of the library will try to force you to use @Proc_*@
-- types everywhere.
--
-- The reason this library stores expressions instead of values is that
-- it needs to handle things like @2+x@, where @x@ is an unknown value.
-- However, an effort is done to ensure that each expression is reduced
-- to its minimum extension.
class ProcType a where
-- | Create a variable assignment, provided
-- the name of the variable and the value to asign.
proc_assign :: Text -> a -> ProcAssign
-- | Create a list.
proc_list :: [a] -> ProcList
-- | Create an argument for a command.
proc_arg :: a -> ProcArg
-- | Variable reading.
proc_read :: Var a -> a
-- | Conditional value.
proc_cond :: Proc_Bool -> a -> a -> a
-- | Check if a variable is contained in an expression.
checkForVar :: Text -> a -> Bool
{- Template Haskell and Proc_* types.
Template Haskell is used in order to derive instances
of the ProcType class. These instances consist merely
in select the appropiate data constructor of the
appropiate datatype. Use "-f info" when compiling
with cabal to see the generated instances. This is
the general format for each Proc_* type:
instance ProcType Proc_* where
proc_assign = *Assign
proc_list = *List
proc_arg = *Arg
proc_read (Var v) = *_Var v
proc_cond = *_Cond
-}
$(deriveProcTypeInsts)
{- Eq and Ord classes for Proc_* types
Since Proc_* types represent expressions,
they cannot be compared in the usual way.
We cannot decide if two expressions will reduce
to the same value. In fact, sometimes they will,
and sometimes they will not. What we can do is
to, given two expressions, return a boolean
expression, which will evaluate to the correct
value in the appropiate context.
Therefore, we define the Proc_Eq and Proc_Ord
classes similarly to Eq and Ord, but returning
a Proc_Bool value instead of a Bool value.
Operators have the same name than their
analagous, but preceded with #.
-}
infix 4 #==, #/=
-- | 'Eq' class for @Proc_*@ values.
class Proc_Eq a where
(#==) :: a -> a -> Proc_Bool
(#/=) :: a -> a -> Proc_Bool
-- Minimal default instance: (#==) or (#/=).
x #== y = pnot $ x #/= y
x #/= y = pnot $ x #== y
instance Proc_Eq Proc_Bool where
(#==) = Bool_Eq
(#/=) = Bool_NEq
instance Proc_Eq Proc_Int where
(#==) = Int_Eq
(#/=) = Int_NEq
instance Proc_Eq Proc_Float where
(#==) = Float_Eq
(#/=) = Float_NEq
instance Proc_Eq Proc_Char where
(#==) = Char_Eq
(#/=) = Char_NEq
instance Proc_Eq Proc_Text where
(#==) = Text_Eq
instance Proc_Eq Proc_Key where
(#==) = Key_Eq
instance Proc_Eq Proc_KeyCode where
(#==) = KeyCode_Eq
infix 4 #<=, #<, #>=, #>
-- | 'Ord' class for @Proc_*@ values.
class Proc_Ord a where
(#<=) :: a -> a -> Proc_Bool
(#<) :: a -> a -> Proc_Bool
(#>=) :: a -> a -> Proc_Bool
(#>) :: a -> a -> Proc_Bool
instance Proc_Ord Proc_Int where
(#<=) = Int_LE
(#<) = Int_L
(#>=) = Int_GE
(#>) = Int_G
instance Proc_Ord Proc_Float where
(#<=) = Float_LE
(#<) = Float_L
(#>=) = Float_GE
(#>) = Float_G
{- Proc_* types and recursion
Since some of the Proc_* types are recursive,
we derive the correspondent instances using
Template Haskell. Otherwise, they would be
long and tedious.
-}
$(deriveRecursive ''Proc_Bool)
$(deriveRecursive ''Proc_Int)
$(deriveRecursive ''Proc_Float)
-- | Class of reducible types. Values of these
-- types contain expressions that can be
-- reducible.
class Eq a => Reducible a where
reduce :: a -> a
-- | Find a fix point of the 'reduce' function from
-- any value. If 'reduce' is well defined, this function
-- must end.
iteratedReduce :: Reducible a => a -> a
iteratedReduce = fst . firstWith (uncurry (==)) . pairing . iterate reduce
where
pairing (x:y:xs) = (x,y) : pairing (y:xs)
pairing _ = []
firstWith f (x:xs) = if f x then x else firstWith f xs
firstWith _ _ = error "Error in iterated reduction. Report this as a bug."
{-
FLOAT REDUCTION
Float is probably the most common argument type.
Below a case-by-case analysis tries to reduce a
float expression to its minimal extension.
The more we reduce, the more effective
will be the Processing output code, since we save
operations.
-}
instance Reducible Proc_Float where
-- Distribution
reduce f@(Float_Sum (Float_Mult x y) (Float_Mult x' y'))
| x == x' = reduce $ x * (y + y')
| y == y' = reduce $ y * (x + x')
| otherwise = recursor reduce f
-- x + (-y) = x - y
reduce (Float_Sum x (Float_Neg y)) = reduce $ Float_Substract x y
-- (-x) + y = y - x
reduce (Float_Sum (Float_Neg x) y) = reduce $ Float_Substract y x
--
reduce (Float_Sum x y)
| x == y = 2 * reduce x
| x == 0 = reduce y
| y == 0 = reduce x
| otherwise = reduce x + reduce y
-- x - (-y) = x + y
reduce (Float_Substract x (Float_Neg y)) = reduce $ Float_Sum x y
reduce (Float_Substract x y)
| x == y = 0
| x == 0 = reduce $ negate y
| y == 0 = reduce x
| otherwise = reduce x - reduce y
reduce (Float_Mult x y)
| x == y = reduce x ** 2
| x == 1 = reduce y
| y == 1 = reduce x
| otherwise = reduce x * reduce y
-- (x*y)/z = y*(x/z)
reduce (Float_Divide (Float_Mult (Proc_Float x) y) (Proc_Float z)) =
reduce $ (y*) $ Proc_Float $ x / z
-- (x*y)/z = x*(y/z)
reduce (Float_Divide (Float_Mult x (Proc_Float y)) (Proc_Float z)) =
reduce $ (x*) $ Proc_Float $ y / z
reduce x = recursor reduce x
instance Reducible ProcArg where
reduce (FloatArg x) = FloatArg $ iteratedReduce x
reduce x = x
instance Reducible (ProcCode c) where
reduce (Sequence sq) = Sequence $ fmap reduce $ foldr (
\x xs ->
case Seq.viewl xs of
y Seq.:< ys -> case reduceProcPair x y of
Nothing -> x Seq.<| xs
Just z -> z Seq.<| ys
Seq.EmptyL -> Seq.singleton x
) Seq.empty sq
reduce (Command n xs) = Command n $ fmap reduce xs
reduce x = x
reduceProcPair :: ProcCode c -> ProcCode c -> Maybe (ProcCode c)
-- Translations
reduceProcPair (Command "translate" [FloatArg 0,FloatArg 0]) x = Just x
reduceProcPair x (Command "translate" [FloatArg 0,FloatArg 0]) = Just x
reduceProcPair (Command "translate" [FloatArg x,FloatArg y])
(Command "translate" [FloatArg x',FloatArg y'])
= Just $ Command "translate" [ FloatArg $ x+x'
, FloatArg $ y+y']
-- Rotations
reduceProcPair (Command "rotate" [FloatArg 0]) x = Just x
reduceProcPair x (Command "rotate" [FloatArg 0]) = Just x
reduceProcPair (Command "rotate" [FloatArg x])
(Command "rotate" [FloatArg x'])
= Just $ Command "rotate" [FloatArg $ x+x']
reduceProcPair _ _ = Nothing
----
-- | A complete Processing script.
--
-- It consists in several parts, most of them optional.
--
-- To generate each part of the code, use the 'ProcM' monad
-- and the functions from the "Graphics.Web.Processing.Interface"
-- module. Then, run 'runProcM' or 'execProcM' to get the
-- code result.
--
-- More abstract functions generate 'ProcScript' values as well.
-- See modules "Graphics.Web.Processing.Mid" and "Graphics.Web.Processing.Simple"
-- for two alternative ways.
data ProcScript = ProcScript
{ proc_preamble :: ProcCode Preamble
, proc_setup :: ProcCode Setup
, proc_draw :: Maybe (ProcCode Draw)
, proc_mouseClicked :: Maybe (ProcCode MouseClicked)
, proc_mouseReleased :: Maybe (ProcCode MouseReleased)
, proc_keyPressed :: Maybe (ProcCode KeyPressed)
} deriving (Eq,Generic)
instance PArbitrary ProcScript
instance Arbitrary ProcScript where
arbitrary = parbitrary
-- | Empty script.
emptyScript :: ProcScript
emptyScript = ProcScript {
proc_preamble = emptyCode
, proc_setup = emptyCode
, proc_draw = Nothing
, proc_mouseClicked = Nothing
, proc_mouseReleased = Nothing
, proc_keyPressed = Nothing
}
pvoid :: Pretty a => Text -> Maybe a -> Doc
pvoid _ Nothing = Text.PrettyPrint.Mainland.empty
pvoid n (Just c) = fromText "void" <+> fromText n <> fromText "()" <+> enclose lbrace rbrace inside
where
inside = line <> indent indentLevel (ppr c) <> line
instance Pretty ProcScript where
ppr ps = stack [
ppr $ proc_preamble ps
, pvoid "setup" $ Just $ proc_setup ps
, pvoid "draw" $ proc_draw ps
, pvoid "mouseClicked" $ proc_mouseClicked ps
, pvoid "mouseReleased" $ proc_mouseReleased ps
, pvoid "keyPressed" $ proc_keyPressed ps
]