| Safe Haskell | Safe |
|---|---|
| Language | Haskell2010 |
Zinza
Contents
Description
SPDX-Identifier-Id: GPL-2.0-or-later AND BSD-3-Clause
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
instanceZinzaLicenses wheretoType=genericToTypeidtoValue=genericToValueid instanceZinzaLicense wheretoType=genericToTypeSFPtoValue=genericToValueSFP
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"] ::ModuleConfigLicenses) "fixtures/licenses.zinza" >>= putStr
prints a Haskell module source code:
module DemoLicenses (render) where
import Prelude (String, fst, snd, ($))
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 two structures
- negated:
!foo - field access
foo.bar
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.
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
- 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
- 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
- stripFieldPrefix :: forall a. (Generic a, GFieldNames (Rep a)) => Proxy a -> String -> String
- class GZinzaType (f :: Type -> Type)
- class GZinzaValue (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>>>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 #
Instances
| Zinza Bool Source # | |
| Zinza Char Source # | |
| Zinza () Source # | |
| Zinza Text Source # | |
| Zinza Text Source # | |
| Zinza a => Zinza [a] Source # | |
| Zinza a => Zinza (NonEmpty a) Source # | |
| Zinza a => Zinza (Set a) Source # | |
| (Zinza a, Zinza b) => Zinza (a, b) Source # | |
| (Zinza k, Zinza v) => Zinza (Map k v) Source # | Pairs are encoded as |
Generic deriving
Generically derive toType function.
Generically derive toValue function.
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 #
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 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, |
| 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 |
| ENot (LExpr a) | negation |
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 selectors 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 |
Values
Values are passed at run-time, when the template is interpreted.
When compiled to the Haskell module, Values 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 | |
| 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 |
Instances
| 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 RuntimeErrors.
Without bugs, compiled template should not throw any RuntimeErrors,
as they are prevented statically, i.e. reported already as CompileErrors.
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 # | |
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 # | |