-- | Plat is a template engine, and, as such, it is designed to help you present your data
-- as simple text with the markup of your choice.
--
-- In order to use it, you'll have to do three things.
--
-- * Prepare your data;
--
-- * Create a template to insert this data into; and
--
-- * Render the template with this data.
--
-- See the following sections for explanations and examples.

module Plat
(

-- * Context

-- $contextDoc

 Context,
 (=:),

-- ** Context elements

 ContextElement,
 ElementC(..),
 listValues,

-- * Template

-- $templateDoc

 Template,
 templateE,
 template,

-- ** Template errors

 TemplateErr(..),
 TemplateErrLeft(..),
 TemplateErrRight(..),
 templateErr,

-- * Rendering

-- $renderDoc

 render,

-- ** Rendering errors

 RenderErr(..),
 ContextType(..),
 renderErr,

-- * Position

 CharNumber,
 LineNumber,
 Pos,
 posContext
 ) where
import Plat.Context
import Plat.Template
import Plat.Rendering

-- $contextDoc
-- Let's repeat: Plat is a template engine. Therefore, it's impossible to do calculations
-- in the template, even small ones, like counting the lines. You need to have your data
-- prepared before you render the template, and that includes sorting, indexing, etc.
--
-- There are different types of data, which are used differently. As for now, there are
-- four types, although in the future there could be more. They are:
--
-- * Strings, that are inserted into the template;
--
-- * Booleans, that are checked during rendering;
--
-- * Arrays, that are iterated over; and
--
-- * Records, that keep other values of different types.
--
-- Using one type instead of the other results in a run-time error.
--
-- Records and arrays form a hierarchy, the root of which should always be a record.
-- This root becomes a context which the template is rendered with. Again: it can't be
-- a string, or a boolean, or even an array.
--
-- Defining strings and booleans is pretty straightforward, usual Haskell 'String's and
-- 'Boolean's will do, and Haskell lists are a way to define arrays. As for records,
-- we provide a handy 'Context' monad, which allows us to define records with just one
-- '=:' operator.
--
-- Below is an example of data, prepared to be inserted into a template. It's a simple
-- list of employees in a fictional company named "Mac's tools". For each employee we
-- provide his name, numerical index (as we know, it's impossible to implement this
-- numbering in Plat itself), and, optionaly a boolean "bad", which, probably, is a Mac's
-- evaluation of this particular employee's performance.
--
-- >ctx =
-- >    do "name" =: "Mac's tools"
-- >       "staff" =: [
-- >        do "index" =: "1"
-- >           "name" =: "Alice"
-- >        ,
-- >        do "name" =: "Bob"
-- >           "index" =: "2"
-- >           "bad" =: True
-- >        ,
-- >        do "index" =: "3"
-- >           "name" =: "Nicolás"
-- >        ]
-- 
-- Of course, as this is nothing but a simple Haskell value, one can use all power of
-- Haskell to create such data automatically.

-- $templateDoc
-- Unlike contexts, templates are not created in Haskell. Instead, they are typically
-- held in separate files and written in a special markup language. Of course, there isn't
-- anything to prevent them from being held in strings inside Haskell source code.
-- However, they are compiled into a special opaque data type 'Template' before they are
-- used. This compilation needs to happen just once, unless the template is changed.
--
-- All template commands start from the letter \'\@\'. Therefore, all \'\@\'s that should
-- be in the rendering result need to be escaped. In fact, that is one of the commands:
--
-- [@\@\@@] Represent the string \"\@\".
--
-- There is a way to insert comments in the template. For now, we only support one-line
-- comments. They won't be present in the result of rendering.
--
-- [@\@#@] Starts a one-line comment, continued up to the end of the current line.
--
-- There is also a command that does nothing.
--
-- [@\@.@] Does nothing.
--
-- It can be used as a separator between a command and some other text. For example,
-- if you want the letter \'a\' to follow the output of the command \'\@foo\'
-- without any space between them, you can write \'\@foo\@.a\'.
--
-- There is one command to insert the string from the context into the template.
--
-- [@\@/expr/@] Insert the value of the expression @\'/expr/\'@, which should be a string.
--
-- For now, the expression syntax is very simple. Any expression is just a sequence
-- of names separated by dots (\'.\'). Expressions are evaluated from left to right,
-- starting with the context, used to render the template. Each name is just a name
-- of a field in the current record, and the value of this field replaces that record.
-- For example, if the expression is @\'foo.bar.baz\'@, then it's value is the value
-- of the field @\'baz\'@ of the record, which is the value of the field @\'bar\'@ of
-- another record, which, in turn, is the value if the field @\'foo\'@ of the context.
--
-- It's a runtime error if the required field doesn't exist or if the current value is
-- not a record. There are two exceptions. First, if the value of @\'/expr/\'@ is boolean,
-- then the expression @\'/expr/.not\'@ is valid and it's value is also boolean,
-- complement to the value of @\'/expr/\'@. Second, if the value of @\'/expr/\'@ is
-- an array, then the expression @\'/expr/.empty\'@ is, again, boolean, and it is true
-- if and only if the value of @\'/expr/\'@ is an empty array. Of course, neither of this
-- two expressions can be inserted into the template as a string.
--
-- There is a command to check for the value of a boolean expression.
--
-- [@\@!/expr/@] Checks the value of the expression @\'/expr/\'@, which must be true.
--
-- It's a runtime error if the value of @\'/expr/\'@ is not boolean, or if it's false.
-- However, there is a difference between this two variants. If the expression
-- @\'/expr/\'@ is not valid, then the expression @\'/expr/.not\'@ is also not valid,
-- but if @\'/expr/\'@ is false, then @\'/expr/.not\'@ is valid and true.
--
-- Of course, checking the boolean value is not very useful, unless you do it between
-- the branching commands.
--
-- [@\@{@] Starts the list of branches.
--
-- [@\@|@] Separates branches in the list.
--
-- [@\@}@] Ends the list of branches.
--
-- Each branch is a separate template, which is rendered with the same context as the
-- list of branches itself. Plat chooses the first branch that doesn't fail and uses it
-- instead of the whole list as if there was no failure. It's a runtime error
-- if all branches fail. It's a compile-time error to have unbalanced starting or ending
-- branching command, or to have the separating command not surrounded by them. Here,
-- \'compile-time\' means \'when the template is compiled\', not when the Haskell source
-- is compiled.
--
-- At last, there are two looping commands.
--
-- [@\@/expr/\[/name/@] Starts the loop.
--
-- [@\@\]@] Ends the loop.
--
-- Here, the value of @\'/expr/\'@ should be an array. Each of it's items, in turn, is
-- added to the context under the name @\'/name/\'@, and the template between these
-- commands is rendered with this new context.
--
-- An example:
--
-- >List of employees at @name:
-- >@staff[person
-- >@person.index@.. @person.name@{@!person.bad (going to be fired)@|@}@
-- >@]@# There won't be an empty line in the result
--
-- This is a simple template, which can be used with the data example above. Note that
-- if the employee is marked as \'bad\', then there would be a note that he is going to be
-- fired; otherwise, there won't be anything, as the second branch is empty. Of course,
-- you can use several branches, not just two of them.
--
-- There is just one quirk here. If the line in the template contains only some control
-- commands (i.e. branching, looping, or checking commands), padded with whitespaces
-- at the beginning and the end, then this line won't be present in the result of
-- rendering. All the whitespace, together with the line break, would be removed.
-- So it's safe, for example, to end the loop on a separate line. On the other hand,
-- if the line contains nothing but whitespace, then it won't be removed.
--
-- If you don't want a line to be removed, you can just insert the @\'\@.\'@ command.

-- $renderDoc
-- Now, when you have a 'Context' and a 'Template', it's time to bring them together.
--
-- With the examples given above, the result of rendering would be the following:
--
-- >List of employees at Mac's tools:
-- >1. Alice
-- >2. Bob (going to be fired)
-- >3. Nicolás