Implement functions

This commit is contained in:
Niels G. W. Serup 2024-09-28 13:57:53 +02:00
parent 67447ac23b
commit da7ea65cf5
No known key found for this signature in database
GPG Key ID: 38EEEBCE67324F19
12 changed files with 106 additions and 54 deletions

View File

@ -11,7 +11,10 @@ extra-doc-files: README.md
common common common common
ghc-options: -Wall ghc-options: -Wall
default-language: GHC2024 default-language: GHC2021
default-extensions:
OverloadedStrings
LambdaCase
library library
import: common import: common
@ -30,18 +33,22 @@ library
DependencyRunner DependencyRunner
SiteGenerator SiteGenerator
build-depends: build-depends:
base base >=4.14 && <4.20
, mtl , mtl
, containers , containers
, bytestring
, text , text
, template-haskell , directory
, blaze-html
, pandoc
, JuicyPixels
, JuicyPixels-stbir
, template-haskell >=2.16 && <2.22
executable byg executable byg
import: common import: common
main-is: src/Main.hs main-is: src/Main.hs
build-depends: build-depends:
base base >=4.14 && <4.20
, text , text
, template-haskell , template-haskell
, byg , byg

View File

@ -6,4 +6,5 @@ pkgs.haskell.lib.overrideCabal
"--ghc-option=-Werror" "--ghc-option=-Werror"
"--ghc-option=-O2" "--ghc-option=-O2"
]; ];
doHaddock = false;
}) })

View File

@ -1,7 +1,7 @@
let let
sources = import ./sources.nix; sources = import ./sources.nix;
pkgs = import sources.nixpkgs {}; pkgs = import sources.nixpkgs {};
haskell = pkgs.haskell.packages.ghc910; haskell = pkgs.haskell.packages.ghc98;
in in
{ {
pkgs = pkgs; pkgs = pkgs;

View File

@ -3,5 +3,10 @@ pkgs.mkShell {
buildInputs = [ buildInputs = [
haskell.ghc haskell.ghc
pkgs.cabal-install pkgs.cabal-install
pkgs.zlib.dev
]; ];
shellHook = ''
export LD_LIBRARY_PATH="${pkgs.zlib}/lib";
'';
} }

View File

@ -1,3 +1,4 @@
{-# LANGUAGE GADTs #-}
{-# LANGUAGE FunctionalDependencies #-} {-# LANGUAGE FunctionalDependencies #-}
module DependencyGenerator module DependencyGenerator
( DepGenM ( DepGenM
@ -23,7 +24,7 @@ module DependencyGenerator
, concatTexts , concatTexts
, joinPaths , joinPaths
, fileComponents , fileComponents
, isImageExtension , hasImageExtension
, applyTemplate , applyTemplate
, toText , toText
, listDirectory , listDirectory
@ -198,8 +199,8 @@ joinPaths a b = do
fileComponents :: TokenableTo FilePath a => a -> DepGenM' (String, String) fileComponents :: TokenableTo FilePath a => a -> DepGenM' (String, String)
fileComponents a = runFunction FileComponents =<< toToken a fileComponents a = runFunction FileComponents =<< toToken a
isImageExtension :: TokenableTo FilePath a => a -> DepGenM' Bool hasImageExtension :: TokenableTo FilePath a => a -> DepGenM' Bool
isImageExtension a = runFunction IsImageExtension =<< toToken a hasImageExtension a = runFunction HasImageExtension =<< toToken a
applyTemplate :: (TokenableTo Template a, TokenableTo Text b) => a -> b -> DepGenM' Text applyTemplate :: (TokenableTo Template a, TokenableTo Text b) => a -> b -> DepGenM' Text
applyTemplate a b = do applyTemplate a b = do

View File

@ -4,7 +4,7 @@ module DependencyRunner
, runDepRunMIO , runDepRunMIO
) where ) where
import Types (Value(..), Valuable(..)) import Types (Value(..), fromValue)
import Types.Dependency import Types.Dependency
import Evaluation.Function import Evaluation.Function
import Evaluation.FunctionIO import Evaluation.FunctionIO

View File

@ -2,36 +2,51 @@ module Evaluation.Function
( evalFunction ( evalFunction
) where ) where
import Types (Function(..), Value(..)) import Types (Function(..), Value(..), Template(..), fromValue)
import Data.Char (toLower)
import qualified Data.Text as T
fileComponents :: String -> (String, String)
fileComponents s =
let (lastRev, firstRev) = span (/= '.') $ reverse s
in case firstRev of
_ : firstRev' -> (reverse firstRev', reverse lastRev)
[] -> (reverse lastRev, "")
isImageExtension :: String -> Bool
isImageExtension = (`elem` ["jpg"]) . map toLower
evalFunction :: Function -> Value -> Value evalFunction :: Function -> Value -> Value
evalFunction f x = case (f, x) of evalFunction f x = case (f, x) of
(AppendStrings, Tuple (String _, String _)) -> (AppendStrings, Tuple (String s0, String s1)) ->
String undefined String (s0 ++ s1)
(ConcatStrings, List _) -> (ConcatStrings, List vs) ->
String undefined String $ concatMap fromValue vs
(AppendTexts, Tuple (Text _, Text _)) -> (AppendTexts, Tuple (Text t0, Text t1)) ->
Text undefined Text $ T.append t0 t1
(ConcatTexts, List _) -> (ConcatTexts, List vs) ->
Text undefined Text $ T.concat $ map fromValue vs
(JoinPaths, Tuple (String _, String _)) -> (JoinPaths, Tuple (String s0, String s1)) ->
String undefined String (s0 ++ "/" ++ s1)
(FileComponents, String _) -> (FileComponents, String s) ->
Tuple (String undefined, String undefined) let (base, ext) = fileComponents s
in Tuple (String base, String ext)
(IsImageExtension, String _) -> (HasImageExtension, String s) ->
Bool undefined let (_, ext) = fileComponents s
in Bool $ isImageExtension ext
(ApplyTemplate, Tuple (Template _, Text _)) -> (ApplyTemplate, Tuple (Template (TemplateParts beforeContent afterContent), Text t)) ->
Text undefined Text $ T.concat [beforeContent, t, afterContent]
(ToText, String _) -> (ToText, String s) ->
Text undefined Text $ T.pack s
_ -> _ ->
error "unexpected combination of function and argument type" error "unexpected combination of function and argument type"

View File

@ -2,30 +2,59 @@ module Evaluation.FunctionIO
( evalFunctionIO ( evalFunctionIO
) where ) where
import Types (FunctionIO(..), Value(..)) import Types.Values
import Types (FunctionIO(..), Value(..), toValue)
import qualified Data.Text as T
import qualified Data.Text.Lazy as TL
import qualified Data.Text.IO as T
import qualified Text.Pandoc as P
import qualified Text.Blaze.Html.Renderer.Text as B
import qualified Codec.Picture as CP
import qualified Codec.Picture.STBIR as CPS
import System.Directory (listDirectory, createDirectory, copyFile)
evalFunctionIO :: FunctionIO -> Value -> IO Value evalFunctionIO :: FunctionIO -> Value -> IO Value
evalFunctionIO f x = case (f, x) of evalFunctionIO f x = case (f, x) of
(ListDirectory, String _) -> (ListDirectory, String s) ->
pure $ List undefined (List . map toValue) <$> listDirectory s
(ReadTemplate, String _) -> (ReadTemplate, String s) -> do
pure $ Template undefined t <- T.readFile s
let c = "CONTENT"
(beforeContent, after) = T.breakOn c t
afterContent = T.drop (T.length c) after
pure $ Template $ TemplateParts beforeContent afterContent
(ConvertImage, Tuple (Tuple (String _, String _), ImageConversionSettings _)) -> (ConvertImage, Tuple (Tuple (String source, String target),
pure $ Empty ImageConversionSettings (ResizeToWidth widthResized))) -> do
imageOrig <- CP.readImage source
let imageOrig' = case imageOrig of
Left s -> error ("unexpected error: " ++ s)
Right image -> CP.convertRGB8 image
sizeFactor :: Double
sizeFactor = fromIntegral (CP.imageWidth imageOrig') / fromIntegral widthResized
heightResized = round (fromIntegral (CP.imageHeight imageOrig') / sizeFactor)
imageResized = CPS.resize CPS.defaultOptions widthResized heightResized imageOrig'
CP.saveJpgImage 90 target $ CP.ImageRGB8 imageResized
pure Empty
(SaveFile, Tuple (Text _, String _)) -> (SaveFile, Tuple (Text t, String s)) -> do
pure $ Empty T.writeFile s t
pure Empty
(CopyFile, Tuple (String _, String _)) -> (CopyFile, Tuple (String source, String target)) -> do
pure $ Empty copyFile source target
pure Empty
(MakeDir, String _) -> (MakeDir, String s) -> do
pure $ Empty createDirectory s
pure Empty
(RunPandoc, String _) -> (RunPandoc, String s) -> do
pure $ Text undefined contents <- T.readFile s
html <- P.runIOorExplode (P.writeHtml5 P.def =<< P.readMarkdown P.def contents)
pure $ Text $ TL.toStrict $ B.renderHtml html
_ -> _ ->
error "unexpected combination of function and argument type" error "unexpected combination of function and argument type"

View File

@ -1,4 +1,3 @@
{-# LANGUAGE OverloadedStrings #-}
module SiteGenerator (generateSite) where module SiteGenerator (generateSite) where
import Types import Types
@ -30,7 +29,7 @@ handleRecipeDir outputDir template indexName dir = do
recipeDirOut <- joinPaths outputDir dir recipeDirOut <- joinPaths outputDir dir
makeDir recipeDirOut makeDir recipeDirOut
dirContents <- listDirectory dir dirContents <- listDirectory dir
areImageFilenames <- mapDepGenM isImageExtension areImageFilenames <- mapDepGenM hasImageExtension
$ unzipSndDepGenM $ mapDepGenM fileComponents dirContents $ unzipSndDepGenM $ mapDepGenM fileComponents dirContents
imageFilenames <- filterDepGenM areImageFilenames dirContents imageFilenames <- filterDepGenM areImageFilenames dirContents
imagePaths <- mapDepGenM (joinPaths dir) imageFilenames imagePaths <- mapDepGenM (joinPaths dir) imageFilenames

View File

@ -1,4 +1,4 @@
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE GADTs #-}
module Types.Dependency module Types.Dependency
( Action(..) ( Action(..)
, UToken(..) , UToken(..)

View File

@ -10,7 +10,7 @@ data Function = AppendStrings
| ConcatTexts | ConcatTexts
| JoinPaths | JoinPaths
| FileComponents | FileComponents
| IsImageExtension | HasImageExtension
| ApplyTemplate | ApplyTemplate
| ToText | ToText
deriving (Show, Lift) deriving (Show, Lift)

View File

@ -1,6 +1,5 @@
module Types.Values module Types.Values
( ImageConversionSettings(..) ( ImageConversionSettings(..)
, TemplatePart(..)
, Template(..) , Template(..)
) where ) where
@ -10,9 +9,5 @@ import Language.Haskell.TH.Syntax (Lift)
data ImageConversionSettings = ResizeToWidth Int data ImageConversionSettings = ResizeToWidth Int
deriving (Show, Lift) deriving (Show, Lift)
data TemplatePart = Literal Text data Template = TemplateParts Text Text
| KeyValue Text
deriving (Show, Lift)
data Template = TemplateParts [TemplatePart]
deriving (Show, Lift) deriving (Show, Lift)