{-# OPTIONS_HADDOCK prune #-} {-# OPTIONS_GHC -Werror -Wall #-} {-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE Safe #-} -- | Used to declare expressions module Elm.Expression ( Expr(..) ) where import Protolude import Control.Monad (mapM, when) import Control.Monad.Writer (tell) import Data.List hiding (map) import Data.String (IsString (..), String) import Elm.Classes (Generate (..)) import Elm.GenError (GenError (..)) import Text.PrettyPrint hiding (Str) -- | The expression type data Expr {- Constants -} -- | A boolean literal = Bool Bool -- | A string literal | Str String -- | An integer literal | Int Int -- | A float literal | Float Float -- | An underscore variable placeholder | Under {- Inline -} -- | A variable | Var String -- | Function application, the tail is applied to the head | App [Expr] -- | A list of expressions | List [Expr] -- | Apply an inline operator to two expressions | Op String Expr Expr -- | A tuple of expressions | Tuple [Expr] -- | A record, the first paramater is an optional record to update from | Record (Maybe Expr) [(String, Expr)] {- Multi Line -} -- | A let expression | Let Expr [(Expr, Expr)] -- | A case expression | Case Expr [(Expr, Expr)] {- Util -} -- | Wrap an expression in parens, should be mostly automatic | Parens Expr -- | Allows creating variables with overloaded strings instance IsString Expr where fromString = Var instance Generate Expr where generate expr = case expr of Var str -> do when (str == "") $ tell $ Error "An empty string is not a valid variable name" return $ text str App [] -- I don't think this has a valid meaning -> do tell $ Error "Invalid syntax, trying to apply nothing" return $ text "" App [expr'] -> generate expr' App exprs -- If only I could understand my own code :( -> do docs <- mapM vop exprs return . hsep $ docs Tuple [] -> return "()" Tuple items -> do when (length items > 9) $ tell $ Error "Length of tuple is too long" when (length items > 7) $ tell $ WarningList [ "Tuples of length longer than seven are not comparable" ] docs <- mapM generate items return $ lparen <+> (hsep . punctuate "," $ docs) <+> rparen Str str -> return . doubleQuotes . text $ str Op op expr1 expr2 -> do doc1 <- vop expr1 doc2 <- vop expr2 return $ doc1 <+> text op <+> doc2 Case _ [] -> do tell $ Error "Unable to create case expression with 0 cases" return "" Case value options -> do docValue <- generate value optionsList <- genCaseList options return $ "case" <+> docValue <+> "of" $+$ nest 4 optionsList List items -> do docs <- mapM generate items return . brackets . hsep . punctuate "," $ docs Let _ [] -> do tell $ Error "Unable to create let expression with 0 bindings" return "" Let value bindings -> do bindingsList <- genLetList bindings valueDoc <- generate value return $ "let" $+$ nest 4 bindingsList $+$ "in" $+$ nest 4 valueDoc Int val -> do when (val > 9007199254740991) $ -- I would love for someone, somewhere, to get this warning tell $ WarningList [ "The number " ++ show val ++ " is larger than the largest safe number in js" ] return . int $ val Float val -> do when (val > 9007199254740991) $ tell $ WarningList [ "The number " ++ show val ++ " is larger that the largest safe number in js" ] return . float $ val Under -> return . char $ '_' Bool bool' -> if bool' then return . text $ "True" else return . text $ "False" Record Nothing [] -> return "{}" Record (Just (Var str)) [] -- tbh, what would you even be trying to do? -> do tell $ WarningList [ "Trying to update record " ++ str ++ " with no changed fields" ] return . text $ str Record (Just (Var str)) updates -> do list' <- genRecordList updates return $ lbrace <+> text str <+> "|" <+> list' <+> rbrace Record (Just _) _ -- This seems to be how it is -> do tell $ Error "You are unable to update a record with a non constant" return "" Record Nothing updates -> do list' <- genRecordList updates return $ lbrace <+> list' <+> rbrace Parens expr' -> do doc <- generate expr' return . parens $ doc -- Generates the list of key value pairs in a record where genRecordList updates = do let (keys, values) = unzip updates let docKeys = map text keys docValues <- mapM generate values return . hsep . punctuate "," . map (\(a, b) -> a <+> "=" <+> b) $ zip docKeys docValues -- Generates the list of declerations in a let expression genLetList bindings = do let (keys, values) = unzip bindings docKeys <- mapM generate keys docValues <- mapM generate values return . vcat . map (\(a, b) -> a <+> "=" <+> b) $ zip docKeys docValues -- Generates the list of cases in a case statement genCaseList options = do let (keys, values) = unzip options docKeys <- mapM generate keys docValues <- sequence . map generate $ values return . vcat . punctuate "\n" . map (\(a, b) -> a <+> "->" $+$ nest 4 b) $ zip docKeys docValues -- takes an expression and wraps it in parens -- if required for nesting it in another expression vop expr' = case expr' of Var _ -> generate expr' Tuple _ -> generate expr' List _ -> generate expr' Int _ -> generate expr' Float _ -> generate expr' Under -> generate expr' Str _ -> generate expr' Record _ _ -> generate expr' _ -> do doc <- generate expr' return . parens $ doc