License | GPL-2.0-or-later AND BSD-3-Clause |
---|---|
Safe Haskell | Safe |
Language | Haskell2010 |
Zinza
Contents
Description
Zinza - a small jinja-syntax-inspired typed-template compiler.
Zinza typechecks and compiles a template. We can compile either to Haskell function, or to verbatim Haskell module (planned).
Zinza is very minimalistic. Features are added when needed.
Example usage
Given a template
{% for license in licenses %} licenseName {{license.con}} = {{license.name}} {% endfor %}
and data definitions like:
newtype Licenses = Licenses { licenses :: [License] } deriving (Generic
) data License = License { licenseCon :: String , licenseName :: String } deriving (Generic
)
We can (generically) derive Zinza
instances for Licenses
and License
instanceZinza
Licenses wheretoType
=genericToType
idtoValue
=genericToValue
idfromValue
=genericFromValue
id instanceZinza
License wheretoType
=genericToTypeSFP
toValue
=genericToValueSFP
fromValue
=genericFromValueSFP
Then the example of run-time usage is
example :: IO String
example = do
-- this might fail, type errors!
run <- parseAndCompileTemplateIO
"fixtures/licenses.zinza"
-- this shouldn't fail (run-time errors are due bugs in zinza)
run $ Licenses
[ License "Foo" (show "foo-1.0")
, License "Bar" (show "bar-1.2")
]
The result of running an example
is:
licenseName Foo = "foo-1.0" licenseName Bar = "bar-1.2"
Module generation
Zinza also supports standalone module generation.
parseAndCompileModuleIO
(simpleConfig
"DemoLicenses" ["Licenses"] ::ModuleConfig
Licenses) "fixtures/licenses.zinza" >>= putStr
prints a Haskell module source code:
{--} module DemoLicenses (render) where import Prelude (String, fst, snd, ($), not, return) import Control.Monad (forM_) import Licenses type Writer a = (String, a) tell :: String -> Writer (); tell x = (x, ()) execWriter :: Writer a -> String; execWriter = fst render :: Licenses -> String render (z_root) = execWriter $ do forM_ (licenses $ z_root) $ z_var0_license -> do tell "licenseName " tell (licenseCon $ z_var0_license) tell " = " tell (licenseName $ z_var0_license) tell "n"
which is not dependent on Zinza. You are free to use more efficient writer as well.
Expressions
{{ expression }}
Expression syntax has only few constructions:
- field access
foo.bar
- function application
fun bar
(though function can only benot
)
Note: you can provide your own Prelude of functions. See Bools.hs
and Bools.zinza
in tests for an example.
You cannot define new functions in templates, but you can pass
them as template arguments.
Control structures
The for and if statements are supported:
{% for value in values %} ... {% endfor %}
{% if boolExpression %} ... {% elif anotherBoolExpression %} ... {% else %} ... {% endif %}
If a control structure tag starts at the first column, the possible trailing new line feed is stripped. This way full-line control tags don't introduce new lines in the output.
Blocks
It's possible to define blocks to be used (possibly multiple times) later:
{% defblock blockname %} ... {% endblock %}
And the block can be used later with:
{% useblock blockname %}
Blocks follow scopes of if
and for
control structures
Comments
{# Comments are omitted from the output #}
Synopsis
- parseAndCompileTemplate :: (Zinza a, ThrowRuntime m) => FilePath -> String -> Either CompileOrParseError (a -> m String)
- parseAndCompileTemplateIO :: (Zinza a, ThrowRuntime m) => FilePath -> IO (a -> m String)
- parseAndCompileModule :: Zinza a => ModuleConfig a -> FilePath -> String -> Either CompileOrParseError String
- parseAndCompileModuleIO :: Zinza a => ModuleConfig a -> FilePath -> IO String
- data ModuleConfig a = ModuleConfig {}
- simpleConfig :: forall a. Typeable a => String -> [String] -> ModuleConfig a
- class Zinza a where
- toType :: Proxy a -> Ty
- toTypeList :: Proxy a -> Ty
- toValue :: a -> Value
- toValueList :: [a] -> Value
- fromValue :: Loc -> Value -> Either RuntimeError a
- fromValueList :: Loc -> Value -> Either RuntimeError [a]
- genericToType :: forall a. (Generic a, GZinzaType (Rep a)) => (String -> String) -> Proxy a -> Ty
- genericToValue :: forall a. (Generic a, GZinzaValue (Rep a)) => (String -> String) -> a -> Value
- genericFromValue :: forall a. (Generic a, GZinzaFrom (Rep a)) => (String -> String) -> Loc -> Value -> Either RuntimeError a
- genericToTypeSFP :: forall a. (Generic a, GZinzaType (Rep a), GFieldNames (Rep a)) => Proxy a -> Ty
- genericToValueSFP :: forall a. (Generic a, GZinzaValue (Rep a), GFieldNames (Rep a)) => a -> Value
- genericFromValueSFP :: forall a. (Generic a, GZinzaFrom (Rep a), GFieldNames (Rep a)) => Loc -> Value -> Either RuntimeError a
- stripFieldPrefix :: forall a. (Generic a, GFieldNames (Rep a)) => Proxy a -> String -> String
- class GZinzaType (f :: Type -> Type)
- class GZinzaValue (f :: Type -> Type)
- class GZinzaFrom (f :: Type -> Type)
- class GFieldNames (f :: Type -> Type)
- data Node a
- type Nodes a = [Node a]
- data Expr a
- type LExpr a = Located (Expr a)
- data Ty
- displayTy :: Ty -> String
- data Value
- newtype ParseError = ParseError String
- data CompileError
- data CompileOrParseError
- data RuntimeError
- class AsRuntimeError e where
- asRuntimeError :: RuntimeError -> e
- class Monad m => ThrowRuntime m where
- throwRuntime :: RuntimeError -> m a
- data Loc = Loc !Int !Int
- data Located a = L !Loc a
- zeroLoc :: Loc
- displayLoc :: Loc -> String
- class Traversable t => TraversableWithLoc t where
- traverseWithLoc :: Applicative f => (Loc -> a -> f b) -> t a -> f (t b)
- type Var = String
- type Selector = String
Documentation
parseAndCompileTemplate Source #
Arguments
:: (Zinza a, ThrowRuntime m) | |
=> FilePath | name of the template |
-> String | contents of the template |
-> Either CompileOrParseError (a -> m String) |
Parse and compile the template into Haskell function.
parseAndCompileTemplateIO :: (Zinza a, ThrowRuntime m) => FilePath -> IO (a -> m String) Source #
Like parseAndCompileTemplate
but reads file and (possibly)
throws CompileOrParseError
.
Compilation to Haskell module
parseAndCompileModule :: Zinza a => ModuleConfig a -> FilePath -> String -> Either CompileOrParseError String Source #
Parse and compile the template into String
representing a Haskell module.
parseAndCompileModuleIO :: Zinza a => ModuleConfig a -> FilePath -> IO String Source #
Like parseAndCompileModule
but reads file and (possibly)
throws CompileOrParseError
.
data ModuleConfig a Source #
Configuration for module rendering
Constructors
ModuleConfig | |
Instances
Show (ModuleConfig a) Source # | |
Defined in Zinza.Module Methods showsPrec :: Int -> ModuleConfig a -> ShowS # show :: ModuleConfig a -> String # showList :: [ModuleConfig a] -> ShowS # |
Arguments
:: Typeable a | |
=> String | module name |
-> [String] | imports |
-> ModuleConfig a |
Simple configuration to use with parseAndCompileModule
or
parseAndCompileModuleIO
.
Input class
Zinza
class tells how to convert the type into template parameters,
and their types.
Class can be auto-derived for product types.
>>>
data R = R { recFoo :: String, recBar :: Char } deriving Generic
>>>
instance Zinza R where toType = genericToTypeSFP; toValue = genericToValueSFP; fromValue = genericFromValueSFP
>>>
displayTy $ toType (Proxy :: Proxy R)
"{bar: String, foo: String}"
Methods
toType :: Proxy a -> Ty Source #
toTypeList :: Proxy a -> Ty Source #
toValue :: a -> Value Source #
toValueList :: [a] -> Value Source #
fromValue :: Loc -> Value -> Either RuntimeError a Source #
fromValueList :: Loc -> Value -> Either RuntimeError [a] Source #
Instances
Generic deriving
Generically derive toType
function.
Generically derive toValue
function.
Arguments
:: (Generic a, GZinzaFrom (Rep a)) | |
=> (String -> String) | field renamer |
-> Loc | |
-> Value | |
-> Either RuntimeError a |
genericToTypeSFP :: forall a. (Generic a, GZinzaType (Rep a), GFieldNames (Rep a)) => Proxy a -> Ty Source #
genericToValueSFP :: forall a. (Generic a, GZinzaValue (Rep a), GFieldNames (Rep a)) => a -> Value Source #
genericFromValueSFP :: forall a. (Generic a, GZinzaFrom (Rep a), GFieldNames (Rep a)) => Loc -> Value -> Either RuntimeError a Source #
stripFieldPrefix :: forall a. (Generic a, GFieldNames (Rep a)) => Proxy a -> String -> String Source #
Field renamer which will automatically strip lowercase prefix from field names.
>>>
data R = R { recFoo :: Int, recBar :: Char } deriving Generic
>>>
stripFieldPrefix (Proxy :: Proxy R) "recFoo"
"foo"
If whole field is lower case, it's left intact
>>>
newtype Wrapped = Wrap { unwrap :: String } deriving Generic
>>>
stripFieldPrefix (Proxy :: Proxy Wrapped) "unwrap"
"unwrap"
class GZinzaType (f :: Type -> Type) Source #
Minimal complete definition
gtoType
class GZinzaValue (f :: Type -> Type) Source #
Minimal complete definition
gtoValue
Instances
(i ~ D, GZinzaValueSum f) => GZinzaValue (M1 i c f) Source # | |
Defined in Zinza.Generic |
class GZinzaFrom (f :: Type -> Type) Source #
Minimal complete definition
gfromValue
Instances
(i ~ D, GZinzaFromSum f) => GZinzaFrom (M1 i c f) Source # | |
Defined in Zinza.Generic Methods gfromValue :: Loc -> Ty -> (Var -> Maybe Value) -> Either RuntimeError (M1 i c f ()) |
class GFieldNames (f :: Type -> Type) Source #
Minimal complete definition
fieldNames
Instances
(i ~ D, GFieldNamesSum f) => GFieldNames (M1 i c f) Source # | |
Defined in Zinza.Generic Methods fieldNames :: Proxy (M1 i c f) -> [String] |
Templates
Template parts.
We use polymorphic recursion for de Bruijn indices.
See materials on bound
library.
Constructors
NRaw String | raw text block |
NExpr (LExpr a) | expression |
NIf (LExpr a) (Nodes a) (Nodes a) | conditional block, |
NFor Var (LExpr a) (Nodes (Maybe a)) | for loop, |
NDefBlock Loc Var (Nodes a) | define block |
NUseBlock Loc Var | use block |
NComment | comments |
Instances
Functor Node Source # | |
Foldable Node Source # | |
Defined in Zinza.Node Methods fold :: Monoid m => Node m -> m # foldMap :: Monoid m => (a -> m) -> Node a -> m # foldr :: (a -> b -> b) -> b -> Node a -> b # foldr' :: (a -> b -> b) -> b -> Node a -> b # foldl :: (b -> a -> b) -> b -> Node a -> b # foldl' :: (b -> a -> b) -> b -> Node a -> b # foldr1 :: (a -> a -> a) -> Node a -> a # foldl1 :: (a -> a -> a) -> Node a -> a # elem :: Eq a => a -> Node a -> Bool # maximum :: Ord a => Node a -> a # | |
Traversable Node Source # | |
TraversableWithLoc Node Source # | |
Defined in Zinza.Node Methods traverseWithLoc :: Applicative f => (Loc -> a -> f b) -> Node a -> f (Node b) Source # | |
Show a => Show (Node a) Source # | |
Expressions in templates.
Note: there are only eliminators; we cannot construct "bigger" expressions.
Constructors
EVar (Located a) | variable |
EField (LExpr a) (Located Var) | field accessor |
EApp (LExpr a) (LExpr a) | function application |
Instances
Monad Expr Source # |
|
Functor Expr Source # | |
Applicative Expr Source # | |
Foldable Expr Source # | |
Defined in Zinza.Expr Methods fold :: Monoid m => Expr m -> m # foldMap :: Monoid m => (a -> m) -> Expr a -> m # foldr :: (a -> b -> b) -> b -> Expr a -> b # foldr' :: (a -> b -> b) -> b -> Expr a -> b # foldl :: (b -> a -> b) -> b -> Expr a -> b # foldl' :: (b -> a -> b) -> b -> Expr a -> b # foldr1 :: (a -> a -> a) -> Expr a -> a # foldl1 :: (a -> a -> a) -> Expr a -> a # elem :: Eq a => a -> Expr a -> Bool # maximum :: Ord a => Expr a -> a # | |
Traversable Expr Source # | |
TraversableWithLoc Expr Source # | |
Defined in Zinza.Expr Methods traverseWithLoc :: Applicative f => (Loc -> a -> f b) -> Expr a -> f (Expr b) Source # | |
Show a => Show (Expr a) Source # | |
Types
Zinza's type-system is delibarately extremely simple.
Zinza types.
The selector
s tell how the Haskell value can be
converted to primitive value. E.g.
>>>
toType (Proxy :: Proxy Char)
TyString (Just "return")
Constructors
TyBool | boolean |
TyString (Maybe Selector) | string |
TyList (Maybe Selector) Ty | lists |
TyRecord (Map Var (Selector, Ty)) | records |
TyFun Ty Ty | functions |
Values
Value
s are passed at run-time, when the template is interpreted.
When compiled to the Haskell module, Value
s aren't used.
Template values.
Errors
newtype ParseError Source #
Constructors
ParseError String |
Instances
Show ParseError Source # | |
Defined in Zinza.Errors Methods showsPrec :: Int -> ParseError -> ShowS # show :: ParseError -> String # showList :: [ParseError] -> ShowS # | |
Exception ParseError Source # | |
Defined in Zinza.Errors Methods toException :: ParseError -> SomeException # fromException :: SomeException -> Maybe ParseError # displayException :: ParseError -> String # |
data CompileError Source #
Constructors
UnboundTopLevelVar Loc Var | |
ShadowingBlock Loc Var | |
UnboundUseBlock Loc Var | |
ARuntimeError RuntimeError |
Instances
Show CompileError Source # | |
Defined in Zinza.Errors Methods showsPrec :: Int -> CompileError -> ShowS # show :: CompileError -> String # showList :: [CompileError] -> ShowS # | |
Exception CompileError Source # | |
Defined in Zinza.Errors Methods toException :: CompileError -> SomeException # fromException :: SomeException -> Maybe CompileError # displayException :: CompileError -> String # | |
AsRuntimeError CompileError Source # | |
Defined in Zinza.Errors Methods |
data CompileOrParseError Source #
Constructors
ACompileError CompileError | |
AParseError ParseError |
Instances
Show CompileOrParseError Source # | |
Defined in Zinza.Errors Methods showsPrec :: Int -> CompileOrParseError -> ShowS # show :: CompileOrParseError -> String # showList :: [CompileOrParseError] -> ShowS # | |
Exception CompileOrParseError Source # | |
Defined in Zinza.Errors Methods toException :: CompileOrParseError -> SomeException # fromException :: SomeException -> Maybe CompileOrParseError # |
data RuntimeError Source #
Constructors
NotBool Loc Ty | |
NotString Loc Ty | |
NotRecord Loc Ty | |
NotList Loc Ty | |
FieldNotInRecord Loc Var Ty | |
NotFunction Loc Ty | |
FunArgDontMatch Loc Ty Ty | |
CustomError Loc String Ty |
Instances
Eq RuntimeError Source # | |
Defined in Zinza.Errors | |
Show RuntimeError Source # | |
Defined in Zinza.Errors Methods showsPrec :: Int -> RuntimeError -> ShowS # show :: RuntimeError -> String # showList :: [RuntimeError] -> ShowS # | |
Exception RuntimeError Source # | |
Defined in Zinza.Errors Methods toException :: RuntimeError -> SomeException # fromException :: SomeException -> Maybe RuntimeError # displayException :: RuntimeError -> String # | |
AsRuntimeError RuntimeError Source # | |
Defined in Zinza.Errors Methods |
class AsRuntimeError e where Source #
Class representing errors containing RuntimeError
s.
Without bugs, compiled template should not throw any RuntimeError
s,
as they are prevented statically, i.e. reported already as CompileError
s.
Methods
asRuntimeError :: RuntimeError -> e Source #
Instances
AsRuntimeError RuntimeError Source # | |
Defined in Zinza.Errors Methods | |
AsRuntimeError CompileError Source # | |
Defined in Zinza.Errors Methods |
class Monad m => ThrowRuntime m where Source #
Methods
throwRuntime :: RuntimeError -> m a Source #
Instances
ThrowRuntime IO Source # | |
Defined in Zinza.Errors Methods throwRuntime :: RuntimeError -> IO a Source # | |
AsRuntimeError e => ThrowRuntime (Either e) Source # | |
Defined in Zinza.Errors Methods throwRuntime :: RuntimeError -> Either e a Source # | |
ThrowRuntime m => ThrowRuntime (StateT s m) Source # | |
Defined in Zinza.Errors Methods throwRuntime :: RuntimeError -> StateT s m a Source # |
Location
Location, line and column.
Located element.
Instances
Functor Located Source # | |
Foldable Located Source # | |
Defined in Zinza.Pos Methods fold :: Monoid m => Located m -> m # foldMap :: Monoid m => (a -> m) -> Located a -> m # foldr :: (a -> b -> b) -> b -> Located a -> b # foldr' :: (a -> b -> b) -> b -> Located a -> b # foldl :: (b -> a -> b) -> b -> Located a -> b # foldl' :: (b -> a -> b) -> b -> Located a -> b # foldr1 :: (a -> a -> a) -> Located a -> a # foldl1 :: (a -> a -> a) -> Located a -> a # elem :: Eq a => a -> Located a -> Bool # maximum :: Ord a => Located a -> a # minimum :: Ord a => Located a -> a # | |
Traversable Located Source # | |
Eq a => Eq (Located a) Source # | |
Show a => Show (Located a) Source # | |
displayLoc :: Loc -> String Source #
Pretty-print location.
class Traversable t => TraversableWithLoc t where Source #
Some containers have location for each element.
Methods
traverseWithLoc :: Applicative f => (Loc -> a -> f b) -> t a -> f (t b) Source #
Instances
TraversableWithLoc Expr Source # | |
Defined in Zinza.Expr Methods traverseWithLoc :: Applicative f => (Loc -> a -> f b) -> Expr a -> f (Expr b) Source # | |
TraversableWithLoc Node Source # | |
Defined in Zinza.Node Methods traverseWithLoc :: Applicative f => (Loc -> a -> f b) -> Node a -> f (Node b) Source # |