Skip to content

Instantly share code, notes, and snippets.

@TravisCardwell
Last active July 23, 2020 10:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save TravisCardwell/49904adeaa269c7851a3b38b91e3839f to your computer and use it in GitHub Desktop.
Save TravisCardwell/49904adeaa269c7851a3b38b91e3839f to your computer and use it in GitHub Desktop.
demonstration of defining a custom function in ginger
#!/usr/bin/env stack
{- stack
script
--resolver lts-16.6
--package ginger
--package text
-}
-- This is a demonstration of defining a custom function in ginger.
--
-- * https://old.reddit.com/r/haskell/comments/hw3u84/which_template_library_for_source_code_generation/
-- * https://hackage.haskell.org/package/ginger
--
-- This file is a Stack script. With Stack installed, make this file
-- executable and execute it to run the script.
--
-- $ chmod 0755 GingerFunDemo.hs
-- $ ./GingerFunDemo.hs
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
module Main (main) where
-- https://hackage.haskell.org/package/base
import Data.Bifunctor (first)
import Data.Functor.Identity (Identity(runIdentity))
import Data.Maybe (fromMaybe)
-- https://hackage.haskell.org/package/ginger
import qualified Text.Ginger as G
-- https://hackage.haskell.org/package/text
import qualified Data.Text as T
import qualified Data.Text.IO as TIO
------------------------------------------------------------------------------
-- Parse a template.
--
-- This is a pure template parser for parsing templates from strings, using
-- custom syntax.
parseTemplate
:: String
-> Either String (G.Template G.SourcePos)
parseTemplate = first G.peErrorMessage . runIdentity . G.parseGinger' options
where
options :: G.ParserOptions Identity
options = G.ParserOptions
{ G.poIncludeResolver = const $ pure Nothing
, G.poSourceName = Nothing
, G.poKeepTrailingNewline = False
, G.poLStripBlocks = False
, G.poTrimBlocks = False
, G.poDelimiters = G.Delimiters
{ G.delimOpenInterpolation = "<<"
, G.delimCloseInterpolation = ">>"
, G.delimOpenTag = "<!"
, G.delimCloseTag = "!>"
, G.delimOpenComment = "<#"
, G.delimCloseComment = "#>"
}
}
------------------------------------------------------------------------------
-- Format a list of strings as a comma-separated list string.
gfnCommas
:: Monad m
=> G.Function m
gfnCommas [(_, arg)] = pure . fromMaybe "" $
G.toGVal . T.intercalate ", " . map G.asText <$> G.asList arg
gfnCommas _ = pure ""
------------------------------------------------------------------------------
main :: IO ()
main = do
template <- either fail pure . parseTemplate $ unlines
[ "function foo(<< args|commas >>) {"
, "<! for arg in args !>"
, " print(<< arg >>);"
, "<! endfor !>"
, "}"
]
let context = G.makeContextText $ \case
"args" -> G.toGVal ["hoge", "piyo" :: T.Text]
"commas" -> G.fromFunction gfnCommas
_ -> G.toGVal ()
TIO.putStr $ G.runGinger context template
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment